2 * "Interpreter" that you can put in #! like this
3 * #!/usr/bin/prefork-interp [<options>] <interpreter>
4 * to amortise the startup time of a script.
7 * prefork-interp [<option> ..] <interpreter> [<script> [<args> ...]]
8 * prefork-interp [<option>,..],<interpreter> <script> [<args> ...]
9 * prefork-interp '[<option> ..] <interpreter>' <script> [<args> ...]
11 * The script must load a corresponding library (eg Proc::Prefork::Interp
12 * for Perl) and call its preform_initialisation_complete routine.
14 * Options must specify argument/environment mediation approach.
15 * Currently the only args/env mediation supported is:
17 * -U unlaundered: setup and executor both get all arguments and env vars
18 * ident covers only env vars specified with -E
19 * ident covers only two arguments: interpreter and (if present) script
21 * Options for setting the operation mode:
23 * (none) Default: start new server if needed, then run service
24 * -f Force a fresh service (old one is terminated)
25 * --kill Kill any existing service; do not actually run anything
27 * Options for controlling whether different invocations share a server:
29 * -E VAR ident includes env var VAR (or its absence)
30 * -G STRING ident includes string STRING
31 * -g IDENT use IDENT rather than hex(SHA256(... identity things ...))
33 * (Ordering of -E and -G options is relevant; invocations with different
34 * -E -G options are different even if the env var settings are the same)
38 ***************************************************************************
40 State during service execution, process parentage and key fds
45 || listen watch-err/in
46 || call (accept) \ ,------2
47 || ,-----------------------------. SERVER -----0 WATCHER(C)
48 CLIENT 2--=fdpassed>=---------. \ || && | &&
49 (C) 1--=fdpassed>=---------. \ \ || inotify
50 0--=fdpassed>=---------. \ \ \ || sockpath
68 && session leader (daemon)
69 & process group leader
71 ***************************************************************************
73 Control flow and causality
81 attempt to connect, and read greeting
84 tidy up stale /run entries *1 (continue from send_fds, below)
87 retry attempt to connect, and read greeting
90 create listening socket release lock
93 | `------------------.
96 make "fake" initial call socketpair (C)
98 fork/exec #########################################################
99 | `-------------. application
102 | # script initialisation
104 | ###########|#############################################
106 | # identify fds from envirnment (Perl)
111 waitpid # fork for initial service
114 | # | SCRIPT [server] &&
116 | # | ** accept / event loop **
117 | # | accepted? \ \ \
118 | # | / \ watch\ \idle
119 | # | fork child \stderr\ \timeout?
120 | # | _________/ | | |
122 | # SCRIPT [monitor] | eof?| |
125 read ,....<.....send greeting | | |
126 greeting # | ___________________
134 | # fork for executor (Perl)
135 | # |parent? \child? prefork-interp
136 | # | ######\############################
137 | # | # SCRIPT (executor) application
138 | # | # execute service
139 | # wait for read # |
140 | # (select) # terminates
142 | # | # kernel closes execterm
143 | # | ,......<....../|
146 | # | | ,......<...../
147 | # waitpid # _______________
149 | ,....<....,..send status #
150 read status # ________________ #
154 ********** Or, if client is killed **********
156 | # | # execute service
157 terminates # wait for read # |
162 _____________ # \|call? # |
164 # kill whole pgrp... # killled
166 # | | ,......<....../
167 # waitpid # _______________
170 # _____SIGPIPE______ #
172 | - \ / process control flow
173 ... < > causes mediated by fds or other IPC etc.
174 && session leader (daemon)
175 & process group leader
176 # language/implementation boundary
177 *1 line continued elsewhere
179 ______ process termination (after reaping, if shown)
181 ***************************************************************************
183 Sequence of events and fd pluming.
184 NB INCOMPLETE - does not cover execterm, cleanup
186 client (C wrapper) connects to server
187 (including reading ack byte)
189 === acquires lock ===
190 makes new listening socket
192 forks watcher and awaits
193 makes first-instance socketpair
194 forks setup (script, sock fds indicated in env)
195 fd0, fd1, fd2: from-outer
196 other fd: call(client-end)(fake)
197 reaps setup (and reports error)
198 (implicitly releases lock)
200 watcher fd[012]: watcher pipes
201 starts watch on socket path
202 sets stderr to line buffered
203 sets stdin to nonblocking
204 daemonises (one fork, becomes session leader)
205 when socket stat changes, quit
207 setup (pre-exec) fd0: null,
208 fd[12]: fd2-from-outer
209 env fds: listener, call(server-end)(fake),
210 watcher read, watcher write
212 possibly clean env, argv
214 setup (script) runs initialisation parts of the script
215 at prefork establishment point:
216 setup (pm) [1] opens syslog
220 server (pm) [1] [fd0: null],
221 [fd[12]: fd2-from-outer]
223 right away, forks init monitor
224 [2] closes outer caller fds and call(fake)
225 [server (pm)] fd[012]: null
226 other fds: listener, syslog
227 runs in loop accepting and forking,
228 reaping and limiting children (incl init monitor)
229 reports failures of monitors to syslog
231 [client (C wrapper)] if client connect succeeds:
232 now fd: call(client-end)
233 sends message with: cmdline, env
236 [server (script)] accepts, forks subseq monitor
238 monitor [1] [fd0: null]
239 (init [fd[12]: init: fd2-from-outer; subseq: null]
240 or errors: init: fd2; subseq: syslog
241 subseq) other fds: syslog, call(server-end)
243 receives args, env, fds
246 executor sorts out fds:
247 fd0, fd1, fd2: from-outer
248 close fds: call(server-end)
252 runs main part of script
255 [monitor] [fd[012]: null]
256 [fd[12]: init: fd2-from-outer; subseq: null]
257 [errors: init: fd2; subseq: syslog]
259 reports status via socket
261 [client (C wrapper)] [fd0, fd1, fd2: from-outer]
262 [other fd: call(client-end)]
263 receives status, exits appropriately
264 (if was bad signal, reports to stderr, exits 127)
266 ***************************************************************************
268 Protocol, and functions of the script
270 1. Script interpreter will be spawned apparently as normal;
271 should run synchronously in the normal way until
272 "initialisation complete" point. At initialisation complete:
274 2. Env var PREFORK_INTERP contains:
276 v1,SECS.NSECS[,...] LISTEN,CALL,WATCHE,WATCHI[,...][ ???]
278 To parse it: treat as bytes and split on ASCII space, taking
279 the first two words. (There may or may not be
280 further "words"; and if there are they might be binary data.)
281 Then split each of the first two words (which will contain only
282 ASCII printing characters) on comma. Take the first two items:
284 v1 Protocol version indicator - literal. If something else,
285 fail (means installation is incompatible somehow).
288 timestamp just before script started running, as a
289 decimal time_t. NSECS is exactly 9 digits.
290 To be used for auto reloading (see below).
292 The 2nd word's items are file descriptors:
294 LISTEN listening socket nonblocking
295 CALL call socket for initial call blocking
296 WATCHE liveness watcher stderr nonblocking
297 WATCHI liveness sentinel unspecified
299 (any further descriptors should be ignored, not closed)
301 3. Library should do the following:
303 1. Read and understand the PREFORK_INTERP env var.
304 If it is not set, initialisation complete should simply return.
305 (This allows simple synchronous operation.)
308 3. fork/exit (fork and have parent exit) (to make server)
309 4. setsid (to become session leader)
310 5. fork initial service (monitor) child, using CALL (see below)
311 6. Replace stdin/stdout/stderr with /dev/null,
312 and make a note to send all error messages to syslog
313 7. Enter select loop, looking for the following:
316 i. see if we need to reload: is any file forming part
317 of the program newer than the SECS.NSECS ?
318 If so, log at LOG_INFO, and exit immediately
319 (dropping CALL, LISTEN, WATCHI, etc.)
320 ii. see if we can reap any children, possibly waiting
321 for children if we are at our concurrency limit
322 (limit should be configured through library, default 4)
323 Report child exit status if not zero or SIGPIPE.
324 iii. fork service (monitor) child, using accepted fd
326 B. WATCHE is readable:
327 * EOF: log at LOG_INFO, and exit
328 * data to read: read what is available immediately;
329 it will be an error message: log it at LOG_ERR, and exit
331 4. service (monitor) child does the following:
333 1. close all of LISTEN, WATCHI, WATCHE
335 3. send a greeting (on CALL) "PFI\n\0\0\0\0" (8 bytes)
336 4. read a single byte, fail if it's not zero
337 5. three times, receive a single byte with a file descriptor
338 attached as ancillary data. (These descriptors will be
339 service stdin, stdout, stderr.)
340 6. read a 4-byte big-endian length
341 7. read that many bytes, the initial service request message,
342 which contains the following nul-terminated strings:
343 * environment variable settings in the format NAME=value
345 * arguments NOT INCLUDING argv[0] or script filename
346 (not that this means the service request must end in a nul)
347 8. make a new pipe EXECTERM
348 9. fork for the service executor; in the child
349 i. redirect stdin/stdout/stderr to the recevied fds
350 ii. replace environment and arguments with those received,
351 iii. close descriptors: close the original received descriptors;
352 close CALL; keep only the writing end of EXECTERM
353 iv. if the script programming language does things with SIGINT,
354 set it set back to default handling (immediate termination).
355 v. return back to script, now in the grandchild
357 10. in the parent, close EXECTERM writing end, and
358 11. select, looking for one of the following:
360 * EXECTERM reading end is readable
361 No need to actually read, since these shouldn't produce
362 spurious wakeups (but do loop on EINTR).
363 12. set SIGINT to ignored
364 13. send SIGINT to the entire process group
365 14. wait, blocking, for the executor child
366 15. write the wait status, in 32-bit big-endian, to CAL
369 Errors detected in the service monitor should be sent to
370 syslog, or stderr, depending on whether this is the initial
371 service monitor (from part 3 step 5) or an accepted socket
372 service monitor (from part 4 step 9); this can be achieved
373 easily by having a global flag (set at part 3 step 6),
374 or perhaps using logger(8) and redirecting stderr (but
375 then be careful to ensure everyone gets only the necessary fds).
377 EOF on CALL, or EPIPE/SIGPIPE writing to it, are not errors.
378 In this case, exit zero or die with SIGPIPE, so parent
379 won't report error either (part 3 step 7(A)(ii)).
381 ***************************************************************************
385 #include <arpa/inet.h>
386 #include <sys/utsname.h>
392 const char our_name[] = "prefork-interp";
394 static struct sockaddr_un sockaddr_sun;
395 static FILE *call_sock;
397 #define ACK_BYTE '\n'
399 static const char *const *executor_argv;
401 static const char header_magic[4] = "PFI\n";
403 void fusagemessage(FILE *f) {
404 fprintf(f, "usage: #!/usr/bin/prefork-interp [<options>]\n");
407 #define MODE_NORMAL 0
408 #define MODE_KILL 'k'
409 #define MODE_FRESH 'f'
411 #define MEDIATION_UNSPECIFIED 0
412 #define MEDIATION_UNLAUNDERED 'U'
414 static int mediation = MEDIATION_UNSPECIFIED;
415 static int mode = MODE_NORMAL;
416 static int max_sockets = 100; // maximum entries in the run dir is 2x this
418 static struct stat initial_stab;
420 const struct cmdinfo cmdinfos[]= {
422 { 0, 'U', 0, .iassignto= &mediation, .arg= MEDIATION_UNLAUNDERED },
423 { "kill", 0, 0, .iassignto= &mode, .arg= MODE_KILL },
424 { 0, 'f', 0, .iassignto= &mode, .arg= MODE_FRESH },
428 static void ident_add_stat(const char *path) {
430 int r = stat(path, &stab);
431 if (r) diee("failed to stat %s", path);
433 IDENT_ADD_OBJ(path[0], stab.st_dev);
434 IDENT_ADD_OBJ('i', stab.st_ino);
437 void ident_addinit(void) {
438 ident_add_key_byte(1);
440 struct utsname uts = { };
441 size_t utslen = sizeof(uts);
443 if (r) diee("uname failed!");
444 IDENT_ADD_OBJ('u', utslen);
445 IDENT_ADD_OBJ('u', uts);
451 static void propagate_exit_status(int status, const char *what) {
454 if (WIFEXITED(status)) {
455 _exit(WEXITSTATUS(status));
458 if (WIFSIGNALED(status)) {
459 int sig = WTERMSIG(status);
460 const char *signame = strsignal(sig);
461 if (signame == 0) signame = "unknown signal";
463 if (! WCOREDUMP(status) &&
471 sa.sa_handler = SIG_DFL;
472 if (sig != SIGKILL) {
473 r = sigaction(sig, &sa, 0);
474 if (r) diee("failed to reset signal handler while propagating %s",
479 sigaddset(&sset, sig);
480 r = sigprocmask(SIG_UNBLOCK, &sset, 0);
481 if (r) diee("failed to reset signal block while propagating %s",
486 die("unexpectedly kept running after raising (to propagate) %s",
490 die("%s failed due to signal %d %s%s", what, sig, signame,
491 WCOREDUMP(status) ? " (core dumped)" : "");
494 die("%s failed with weird wait status %d 0x%x", what, status, status);
502 static int preclean_entry_compar_name(const void *av, const void *bv) {
503 const PrecleanEntry *a = av;
504 const PrecleanEntry *b = bv;
505 return strcmp(a->name_hash, b->name_hash);
508 static int preclean_entry_compar_atime(const void *av, const void *bv) {
509 const PrecleanEntry *ae = av; time_t a = ae->atime;
510 const PrecleanEntry *be = bv; time_t b = be->atime;
515 static time_t preclean_stat_atime(const char *s_path) {
517 int r= lstat(s_path, &stab);
519 if (errno!=ENOENT) diee("pre-cleanup: stat socket (%s)", s_path);
522 return stab.st_atime;
525 static void preclean(void) {
526 DIR *dir = opendir(run_base);
528 if (errno == ENOENT) return;
529 diee("pre-cleanup: open run dir (%s)", run_base);
532 PrecleanEntry *entries=0;
533 size_t avail_entries=0;
534 size_t used_entries=0;
537 while ((errno = 0, de = readdir(dir))) {
538 char c0 = de->d_name[0];
539 if (!(c0 == 'l' || c0 == 's')) continue;
540 char *name_hash = m_asprintf("%s", de->d_name+1);
541 char *s_path = m_asprintf("%s/s%s", run_base, name_hash);
542 time_t atime = preclean_stat_atime(s_path);
544 if (avail_entries == used_entries) {
545 assert(avail_entries < INT_MAX / 4 / sizeof(PrecleanEntry));
548 entries = realloc(entries, avail_entries * sizeof(PrecleanEntry));
550 entries[used_entries].name_hash = name_hash;
551 entries[used_entries].atime = atime;
554 if (errno) diee("pre-cleanup: read run dir (%s)", run_base);
556 // First we dedupe (after sorting by path)
557 qsort(entries, used_entries, sizeof(PrecleanEntry),
558 preclean_entry_compar_name);
559 PrecleanEntry *p, *q;
560 for (p=entries, q=entries; p < entries + used_entries; p++) {
561 if (q > entries && !strcmp(p->name_hash, (q-1)->name_hash))
565 used_entries = q - entries;
567 // Now maybe delete some things
569 // Actually this has an off-by-one error since we are about
570 // to create a socket, so the actual number of sockets is one more.
571 // But, *actually*, since there might be multiple of us running at once,
572 // we might have even more than that. This doesn't really matter.
573 if (used_entries > max_sockets) {
574 qsort(entries, used_entries, sizeof(PrecleanEntry),
575 preclean_entry_compar_atime);
576 for (p=entries; p < entries + max_sockets; p++) {
577 char *l_path = m_asprintf("%s/l%s", run_base, p->name_hash);
578 char *s_path = m_asprintf("%s/s%s", run_base, p->name_hash);
579 int lock_fd = flock_file(l_path);
580 // Recheck atime - we might have raced!
581 time_t atime = preclean_stat_atime(s_path);
582 if (atime != p->atime) {
583 // Raced. This will leave use deleting too few things. Whatever.
585 int r= unlink(s_path);
586 if (r && errno!=ENOENT) diee("preclean: delete stale (%s)", s_path);
588 if (r) diee("preclean: delete stale lock (%s)", s_path);
589 // NB we don't hold the lock any more now.
597 for (p=entries; p < entries + used_entries; p++)
602 static __attribute((noreturn)) void die_data_overflow(void) {
603 die("cannot handle data with length >2^32");
606 static void prepare_data(size_t *len, char **buf,
607 const void *data, size_t dl) {
609 if (dl >= SIZE_MAX - *len)
614 memcpy(*buf, data, dl);
619 static void prepare_length(size_t *len, char **buf, size_t dl_sz) {
620 if (dl_sz > UINT32_MAX) die_data_overflow();
621 uint32_t dl = htonl(dl_sz);
622 prepare_data(len, buf, &dl, sizeof(dl));
625 static void prepare_string(size_t *len, char **buf, const char *s) {
626 size_t sl = strlen(s);
627 prepare_data(len, buf, s, sl+1);
630 static void prepare_message(size_t *len, char **buf) {
633 const char *const *p = (void*)environ;
636 prepare_string(len, buf, s);
639 prepare_string(len, buf, "");
643 prepare_string(len, buf, s);
646 static void send_fd(int payload_fd) {
647 int via_fd = fileno(call_sock);
650 struct cmsghdr align;
651 char buf[CMSG_SPACE(sizeof(payload_fd))];
661 iov.iov_base = &dummy_byte;
667 msg.msg_control = cmsg_buf.buf;
668 msg.msg_controllen = sizeof(cmsg_buf.buf);
670 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
671 cmsg->cmsg_level = SOL_SOCKET;
672 cmsg->cmsg_type = SCM_RIGHTS;
673 cmsg->cmsg_len = CMSG_LEN(sizeof(payload_fd));
674 *(int*)CMSG_DATA(cmsg) = payload_fd;
676 msg.msg_controllen = sizeof(cmsg_buf.buf);
679 ssize_t r = sendmsg(via_fd, &msg, 0);
681 if (errno == EINTR) continue;
689 static void send_request(void) {
691 ssize_t sr = fwrite(&ibyte, 1, 1, call_sock);
692 if (sr != 1) diee("write signalling byte");
694 // Sending these before the big message makes it easier for the script to
695 // use buffered IO for the message.
701 prepare_message(&len, 0);
703 size_t tlen = len + 4;
704 char *m = xmalloc(tlen);
706 prepare_length(0, &p, len);
707 prepare_message(0, &p);
708 assert(p == m + tlen);
710 sr = fwrite(m, tlen, 1, call_sock);
711 if (sr != 1) diee("write request (buffer)");
713 if (fflush(call_sock)) diee("write request");
716 static FILE *call_sock_from_fd(int fd) {
719 FILE *call_sock = fdopen(fd, "r+");
720 if (!call_sock) diee("fdopen socket");
722 r = setvbuf(call_sock, 0, _IONBF, 0);
723 if (r) die("setvbuf socket");
728 static bool was_eof(FILE *call_sock) {
729 return feof(call_sock) || errno==ECONNRESET;
733 static int protocol_read_maybe(void *data, size_t sz) {
735 size_t sr = fread(data, sz, 1, call_sock);
737 if (was_eof(call_sock)) return -1;
738 diee("read() on monitor call socket (%zd)", sz);
743 static void protocol_read(void *data, size_t sz) {
744 if (protocol_read_maybe(data, sz) < 0)
745 die("monitor process quit unexpectedly");
748 // Returns 0 if OK, error msg if peer was garbage.
749 static const char *read_greeting(void) {
750 char got_magic[sizeof(header_magic)];
752 if (protocol_read_maybe(&got_magic, sizeof(got_magic)) < 0)
753 return "initial monitor process quit"
754 " (maybe script didn't call preform_initialisation_complete?)";
756 if (memcmp(got_magic, header_magic, sizeof(header_magic)))
757 die("got unexpected protocol magic 0x%02x%02x%02x%02x",
758 got_magic[0], got_magic[1], got_magic[2], got_magic[3]);
761 protocol_read(&xdata_len, sizeof(xdata_len));
762 void *xdata = xmalloc(xdata_len);
763 protocol_read(xdata, xdata_len);
768 // Returns: call(client-end), or 0 to mean "is garbage"
769 // find_socket_path must have been called
770 static FILE *connect_existing(void) {
774 if (mode != MODE_NORMAL) return 0;
776 fd = socket(AF_UNIX, SOCK_STREAM, 0);
777 if (fd==-1) diee("socket() for client");
779 socklen_t salen = sizeof(sockaddr_sun);
780 r = connect(fd, (const struct sockaddr*)&sockaddr_sun, salen);
782 if (errno==ECONNREFUSED || errno==ENOENT) goto x_garbage;
783 diee("connect() %s", socket_path);
786 call_sock = call_sock_from_fd(fd);
795 if (call_sock) { fclose(call_sock); call_sock=0; }
796 if (fd >= 0) close(fd);
800 static void watcher_cb_stdin(uv_poll_t *handle, int status, int events) {
804 if ((errno = -status)) diee("watcher: poll stdin");
808 if (!(errno==EINTR || errno==EWOULDBLOCK || errno==EAGAIN))
809 diee("watcher: read sentinel stdin");
813 static void watcher_cb_sockpath(uv_fs_event_t *handle, const char *filename,
814 int events, int status) {
816 struct stat now_stab;
818 if ((errno = -status)) diee("watcher: poll stdin");
820 r= stat(socket_path, &now_stab);
822 if (errno==ENOENT) _exit(0);
823 if (errno==EINTR) continue;
824 diee("stat socket: %s", socket_path);
826 if (!stabs_same_inode(&now_stab, &initial_stab))
831 // On entry, stderr is still inherited, but 0 and 1 are the pipes
832 static __attribute__((noreturn))
833 void become_watcher(void) {
835 uv_poll_t uvhandle_stdin;
836 uv_fs_event_t uvhandle_sockpath;
841 errno= -uv_loop_init(&loop);
842 if (errno) diee("watcher: uv_loop_init");
844 errno= -uv_poll_init(&loop, &uvhandle_stdin, 0);
845 if (errno) diee("watcher: uv_poll_init");
846 errno= -uv_poll_start(&uvhandle_stdin,
847 UV_READABLE | UV_WRITABLE | UV_DISCONNECT,
849 if (errno) diee("watcher: uv_poll_start");
851 errno= -uv_fs_event_init(&loop, &uvhandle_sockpath);
852 if (errno) diee("watcher: uv_fs_event_init");
854 errno= -uv_fs_event_start(&uvhandle_sockpath, watcher_cb_sockpath,
856 if (errno) diee("watcher: uv_fs_event_start");
858 // OK everything is set up, let us daemonise
859 if (dup2(1,2) != 2) diee("watcher: set daemonised stderr");
860 r= setvbuf(stderr, 0, _IOLBF, BUFSIZ);
861 if (r) diee("watcher: setvbuf stderr");
863 pid_t child = fork();
864 if (child == (pid_t)-1) diee("watcher: fork");
867 if (setsid() == (pid_t)-1) diee("watcher: setsid");
869 r= uv_run(&loop, UV_RUN_DEFAULT);
870 die("uv_run returned (%d)", r);
873 static __attribute__((noreturn))
874 void become_setup(int sfd, int lockfd, int fake_pair[2],
875 int watcher_stdin, int watcher_stderr) {
878 int call_fd = fake_pair[1];
880 int null_0 = open("/dev/null", O_RDONLY); if (null_0 < 0) diee("open null");
881 if (dup2(null_0, 0)) diee("dup2 /dev/null onto stdin");
883 if (dup2(2, 1) != 1) die("dup2 stderr onto stdout");
887 // Extension could work like this:
889 // We could advertise a new protocol (perhaps one which is nearly entirely
890 // different after the connect) by putting a name for it comma-separated
891 // next to "v1". Simple extension can be done by having the script
892 // side say something about it in the ack xdata, which we currently ignore.
893 // Or we could add other extra data after v1.
894 putenv(m_asprintf("PREFORK_INTERP=v1,%jd.%09ld %d,%d,%d,%d",
895 (intmax_t)initial_stab.st_mtim.tv_sec,
896 (long)initial_stab.st_mtim.tv_nsec,
897 sfd, call_fd, watcher_stdin, watcher_stderr));
899 execvp(executor_argv[0], (char**)executor_argv);
900 diee("execute %s", executor_argv[0]);
903 static void connect_or_spawn(void) {
906 call_sock = connect_existing();
907 if (call_sock) return;
909 // We're going to make a new one, so clean out old ones
912 int lockfd = acquire_lock();
914 if (mode == MODE_KILL) {
915 r= unlink(socket_path);
916 if (r && errno != ENOENT) diee("remove socket %s", socket_path);
918 r= unlink(lock_path);
919 if (r) diee("rmeove lock %s", lock_path);
923 call_sock = connect_existing();
924 if (call_sock) { close(lockfd); return; }
926 // We must start a fresh one, and we hold the lock
928 r = unlink(socket_path);
929 if (r<0 && errno!=ENOENT)
930 diee("failed to remove stale socket %s", socket_path);
932 int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
933 if (sfd<0) diee("socket() for new listener");
935 socklen_t salen = sizeof(sockaddr_sun);
936 r= bind(sfd, (const struct sockaddr*)&sockaddr_sun, salen);
937 if (r<0) diee("bind() on new listener");
939 r= stat(socket_path, &initial_stab);
940 if (r<0) diee("stat() fresh socket");
942 // We never want callers to get ECONNREFUSED. But:
943 // There is a race here: from my RTFM they may get ECONNREFUSED
944 // if they try between our bind() and listen(). But if they do, they'll
945 // acquire the lock (serialising with us) and retry, and then it will work.
946 r = listen(sfd, INT_MAX);
947 if (r<0) diee("listen() for new listener");
951 int watcher_stdin[2];
952 int watcher_stderr[2];
953 if (pipe(watcher_stdin) || pipe(watcher_stderr))
954 diee("pipe() for socket inode watcher");
956 pid_t watcher = fork();
957 if (watcher == (pid_t)-1) diee("fork for watcher");
961 close(watcher_stdin[1]);
962 close(watcher_stderr[0]);
963 if (dup2(watcher_stdin[0], 0) != 0 ||
964 dup2(watcher_stderr[1], 1) != 1)
965 diee("initial dup2() for watcher");
966 close(watcher_stdin[0]);
967 close(watcher_stderr[1]);
971 close(watcher_stdin[0]);
972 close(watcher_stderr[1]);
973 nonblock(watcher_stderr[0]);
978 r = socketpair(AF_UNIX, SOCK_STREAM, 0, fake_pair);
979 if (r<0) diee("socketpair() for fake initial connection");
981 pid_t setup_pid = fork();
982 if (setup_pid == (pid_t)-1) diee("fork for spawn setup");
983 if (!setup_pid) become_setup(sfd, lockfd, fake_pair,
984 watcher_stdin[1], watcher_stderr[0]);
988 call_sock = call_sock_from_fd(fake_pair[0]);
991 pid_t got = waitpid(setup_pid, &status, 0);
992 if (got == (pid_t)-1) diee("waitpid setup [%ld]", (long)setup_pid);
993 if (got != setup_pid) diee("waitpid setup [%ld] gave [%ld]!",
994 (long)setup_pid, (long)got);
995 if (status != 0) propagate_exit_status(status, "setup");
997 const char *emsg = read_greeting();
998 if (emsg) die("setup failed: %s", emsg);
1004 static void make_executor_argv(const char *const *argv) {
1005 switch (mediation) {
1006 case MEDIATION_UNLAUNDERED: break;
1007 default: die("need -U (specifying unlaundered argument handling)");
1011 #define EACH_NEW_ARG(EACH) { \
1012 arg = interp; { EACH } \
1013 if ((arg = script)) { EACH } \
1014 const char *const *walk = argv; \
1015 while ((arg = *walk++)) { EACH } \
1019 EACH_NEW_ARG( (void)arg; count++; );
1021 const char **out = calloc(count, sizeof(char*));
1022 executor_argv = (const char* const*)out;
1023 if (!executor_argv) diee("allocate for arguments");
1025 EACH_NEW_ARG( *out++ = arg; );
1029 int main(int argc_unused, const char *const *argv) {
1030 process_opts(&argv);
1033 // - possibly interp
1034 // - possibly script
1036 // which ought to be passed on to the actual executor.
1037 make_executor_argv(argv);
1040 FILLZERO(sockaddr_sun);
1041 sockaddr_sun.sun_family = AF_UNIX;
1042 assert(strlen(socket_path) <= sizeof(sockaddr_sun.sun_path));
1043 strncpy(sockaddr_sun.sun_path, socket_path, sizeof(sockaddr_sun.sun_path));
1047 // We're committed now, send the request (or bail out)
1051 protocol_read(&status, sizeof(status));
1053 status = ntohl(status);
1054 if (status > INT_MAX) die("status 0x%lx does not fit in an int",
1055 (unsigned long)status);
1057 propagate_exit_status(status, "invocation");