2 * "Interpreter" that you can put in #! like this
3 * #!/usr/bin/prefork-interp [<options>] <interpreter>
6 * prefork-interp [<option> ..] <interpreter> [<script> [<args> ...]]
7 * prefork-interp [<option>,..],<interpreter> <script> [<args> ...]
8 * prefork-interp '[<option> ..] <interpreter>' <script> [<args> ...]
10 * Options must specify argument laundering mode.
11 * Currently the only mode supported is:
12 * -U unlaundered: setup and executor both get all arguments and env vars
13 * ident covers only env vars specified with -E
14 * ident covers only arguments interpreter and (if present) script
18 ***************************************************************************
20 State during service execution, process parentage and key fds
25 || listen watch-err/in
26 || call (accept) \ ,------2
27 || ,-----------------------------. SERVER -----0 WATCHER(C)
28 CLIENT 2--=fdpassed>=---------. \ || && | &&
29 (C) 1--=fdpassed>=---------. \ \ || inotify
30 0--=fdpassed>=---------. \ \ \ || sockpath
48 && session leader (daemon)
49 & process group leader
51 ***************************************************************************
53 Control flow and causality
61 attempt to connect, and read greeting
64 tidy up stale /run entries *1 (continue from send_fds, below)
67 retry attempt to connect, and read greeting
70 create listening socket release lock
73 | `------------------.
76 make "fake" initial call socketpair (C)
78 fork/exec #########################################################
79 | `-------------. application
82 | # script initialisation
84 | ###########|#############################################
86 | # identify fds from envirnment (Perl)
91 waitpid # fork for initial service
94 | # | SCRIPT [server] &&
96 | # | ** accept / event loop **
98 | # | / \ watch\ \idle
99 | # | fork child \stderr\ \timeout?
100 | # | _________/ | | |
102 | # SCRIPT [monitor] | eof?| |
105 read ,....<.....send greeting | | |
106 greeting # | ___________________
114 | # fork for executor (Perl)
115 | # |parent? \child? prefork-interp
116 | # | ######\############################
117 | # | # SCRIPT (executor) application
118 | # | # execute service
119 | # wait for read # |
120 | # (select) # terminates
122 | # | # kernel closes execterm
123 | # | ,......<....../|
126 | # | | ,......<...../
127 | # waitpid # _______________
129 | ,....<....,..send status #
130 read status # ________________ #
134 ********** Or, if client is killed **********
136 | # | # execute service
137 terminates # wait for read # |
142 _____________ # \|call? # |
144 # kill whole pgrp... # killled
146 # | | ,......<....../
147 # waitpid # _______________
150 # _____SIGPIPE______ #
152 | - \ / process control flow
153 ... < > causes mediated by fds or other IPC etc.
154 && session leader (daemon)
155 & process group leader
156 # language/implementation boundary
157 *1 line continued elsewhere
159 ______ process termination (after reaping, if shown)
161 ***************************************************************************
163 Sequence of events and fd pluming.
164 NB INCOMPLETE - does not cover execterm, cleanup
166 client (C wrapper) connects to server
167 (including reading ack byte)
169 === acquires lock ===
170 makes new listening socket
172 forks watcher and awaits
173 makes first-instance socketpair
174 forks setup (script, sock fds indicated in env)
175 fd0, fd1, fd2: from-outer
176 other fd: call(client-end)(fake)
177 reaps setup (and reports error)
178 (implicitly releases lock)
180 watcher fd[012]: watcher pipes
181 starts watch on socket path
182 sets stderr to line buffered
183 sets stdin to nonblocking
184 daemonises (one fork, becomes session leader)
185 when socket stat changes, quit
187 setup (pre-exec) fd0: null,
188 fd[12]: fd2-from-outer
189 env fds: listener, call(server-end)(fake),
190 watcher read, watcher write
192 possibly clean env, argv
194 setup (script) runs initialisation parts of the script
195 at prefork establishment point:
196 setup (pm) [1] opens syslog
200 server (pm) [1] [fd0: null],
201 [fd[12]: fd2-from-outer]
203 right away, forks init monitor
204 [2] closes outer caller fds and call(fake)
205 [server (pm)] fd[012]: null
206 other fds: listener, syslog
207 runs in loop accepting and forking,
208 reaping and limiting children (incl init monitor)
209 reports failures of monitors to syslog
211 [client (C wrapper)] if client connect succeeds:
212 now fd: call(client-end)
213 sends message with: cmdline, env
216 [server (script)] accepts, forks subseq monitor
218 monitor [1] [fd0: null]
219 (init [fd[12]: init: fd2-from-outer; subseq: null]
220 or errors: init: fd2; subseq: syslog
221 subseq) other fds: syslog, call(server-end)
223 receives args, env, fds
226 executor sorts out fds:
227 fd0, fd1, fd2: from-outer
228 close fds: call(server-end)
232 runs main part of script
235 [monitor] [fd[012]: null]
236 [fd[12]: init: fd2-from-outer; subseq: null]
237 [errors: init: fd2; subseq: syslog]
239 reports status via socket
241 [client (C wrapper)] [fd0, fd1, fd2: from-outer]
242 [other fd: call(client-end)]
243 receives status, exits appropriately
244 (if was bad signal, reports to stderr, exits 127)
246 ***************************************************************************
248 Protocol, and functions of the script
250 1. Script interpreter will be spawned apparently as normal;
251 should run synchronously in the normal way until
252 "initialisation complete" point. At initialisation complete:
254 2. Env var PREFORK_INTERP contains:
256 v1,SECS.NSECS[,...] LISTEN,CALL,WATCHE,WATCHI[,...][ ???]
258 To parse it: treat as bytes and split on ASCII space, taking
259 the first two words. (There may or may not be
260 further "words"; and if there are they might be binary data.)
261 Then split each of the first two words (which will contain only
262 ASCII printing characters) on comma. Take the initial items:
264 v1 Protocol version indicator - literal. If something else,
265 fail (means installation is incompatible somehow).
268 timestamp just before script started running, as a
269 decimal time_t. NSECS is exactly 9 digits.
270 To be used for auto reloading (see below).
272 The 2nd word's items are file descriptors:
274 LISTEN listening socket nonblocking
275 CALL call socket for initial call blocking
276 WATCHE liveness watcher stderr nonblocking
277 WATCHI liveness sentinel unspecified
279 3. Library should do the following:
281 1. Read and understand the PREFORK_INTERP env var.
282 If it is not set, initialisation complete should simply return.
283 (This allows simple synchronous operation.)
286 3. fork/exit (fork and have parent exit) (to make server)
287 4. setsid (to become session leader)
288 5. fork initial service (monitor) child, using CALL (see below)
289 6. Replace stdin/stdout/stderr with /dev/null,
290 and make a note to send all error messages to syslog
291 7. Enter select loop, looking for the following:
294 i. see if we need to reload: is any file forming part
295 of the program newer than the SECS.NSECS ?
296 If so, log at LOG_INFO, and exit immediately
297 (dropping CALL, LISTEN, WATCHI, etc.)
298 ii. see if we can reap any children, possibly waiting
299 for children if we are at our concurrency limit
300 (limit should be configured through library, default 4)
301 iii. fork service (monitor) child, using accepted fd
303 * WATCHE is readable:
304 * EOF:: log at LOG_INFO, and exit
305 * data to read: read what is available immediately,
306 log it as a message at LOG_ERR, and exit
308 4. service (monitor) child does the following:
310 1. close all of LISTEN, WATCHI, WATCHE
312 3. send a greeting (on CALL) "PFI\n\0\0\0\0" (8 bytes)
313 4. read a single byte, fail if it's not zero
314 5. three times, receive a single byte with a file descriptor
315 attached as ancillary data. (These descriptors will be
316 service stdin, stdout, stderr.)
317 6. read a 4-byte big-endian length
318 7. read that many bytes, the initial service request message,
319 which contains the following nul-terminated strings:
320 * environment variable settings in the format NAME=value
322 * arguments NOT INCLUDING argv[0] or script filename
323 (not that this means the service request must end in a nul)
324 8. make a new pipe EXECTERM
325 9. fork for the service executor; in the child
326 i. redirect stdin/stdout/stderr to the recevied fds
327 ii. replace environment and arguments with those received,
328 iii. close descriptors: close the original received descriptors;
329 close CALL; keep only the writing end of EXECTERM
330 iv. if the script programming language does things with SIGINT,
331 set it set back to default handling (immediate termination).
332 v. return back to script, now in the grandchild
334 10. in the parent, close EXECTERM writing end, and
335 11. select, looking for one of the following:
337 * EXECTERM reading end is readable
338 No need to actually read, since these shouldn't produce
339 spurious wakeups (but do loop on EINTR).
340 12. set SIGINT to ignored
341 13. send SIGINT to the entire process group
342 14. wait, blocking, for the executor child
343 15. write the wait status, in 32-bit big-endian, to CALL
344 (this may generate SIGPIPE/EPIPE; if so, die with SIGPIPE
345 or exit 0; do treat SIGPIPE/EPIPE as failure)
348 Errors detected in the service monitor should be sent to
349 syslog, or stderr, depending on whether this is the initial
350 service monitor (from part 3 step 5) or an accepted socket
351 service monitor (from part 4 step 9); this can be achieved
352 easily by having a global flag (set at part 3 step 6),
353 or perhaps using logger(8) and redirecting stderr (but
354 then be careful to ensure everyone gets only the necessary fds).
356 ***************************************************************************
360 #include <arpa/inet.h>
366 const char our_name[] = "prefork-interp";
368 static struct sockaddr_un sockaddr_sun;
369 static FILE *call_sock;
371 #define ACK_BYTE '\n'
373 static const char *const *executor_argv;
375 static const char header_magic[4] = "PFI\n";
377 void fusagemessage(FILE *f) {
378 fprintf(f, "usage: #!/usr/bin/prefork-interp [<options>]\n");
381 #define MODE_NORMAL 0
382 #define MODE_KILL 'k'
383 #define MODE_FRESH 'f'
385 #define MEDIATION_UNSPECIFIED 0
386 #define MEDIATION_UNLAUNDERED 'U'
388 static int mediation = MEDIATION_UNSPECIFIED;
389 static int mode = MODE_NORMAL;
390 static int max_sockets = 100; // maximum entries in the run dir is 2x this
392 static struct stat initial_stab;
394 const struct cmdinfo cmdinfos[]= {
396 { 0, 'U', 0, .iassignto= &mediation, .arg= MEDIATION_UNLAUNDERED },
397 { "kill", 0, 0, .iassignto= &mode, .arg= MODE_KILL },
398 { 0, 'f', 0, .iassignto= &mode, .arg= MODE_FRESH },
402 void ident_addinit(void) {
403 char ident_magic[1] = { 0 };
404 sha256_update(&identsc, sizeof(ident_magic), ident_magic);
407 static void propagate_exit_status(int status, const char *what) {
410 if (WIFEXITED(status)) {
414 if (WIFSIGNALED(status)) {
415 int sig = WTERMSIG(status);
416 const char *signame = strsignal(sig);
417 if (signame == 0) signame = "unknown signal";
419 if (! WCOREDUMP(status) &&
426 sa.sa_handler = SIG_DFL;
427 r = sigaction(sig, &sa, 0);
428 if (r) diee("failed to reset signal handler while propagating %s",
433 sigaddset(&sset, sig);
434 r = sigprocmask(SIG_UNBLOCK, &sset, 0);
435 if (r) diee("failed to reset signal block while propagating %s",
439 die("unexpectedly kept running after raising (to propagate) %s",
443 die("%s failed due to signal %d %s%s", what, sig, signame,
444 WCOREDUMP(status) ? " (core dumped)" : "");
447 die("%s failed with weird wait status %d 0x%x", what, status, status);
455 static int preclean_entry_compar_name(const void *av, const void *bv) {
456 const PrecleanEntry *a = av;
457 const PrecleanEntry *b = bv;
458 return strcmp(a->name_hash, b->name_hash);
461 static int preclean_entry_compar_atime(const void *av, const void *bv) {
462 const PrecleanEntry *ae = av; time_t a = ae->atime;
463 const PrecleanEntry *be = bv; time_t b = be->atime;
468 static time_t preclean_stat_atime(const char *s_path) {
470 int r= lstat(s_path, &stab);
472 if (errno!=ENOENT) diee("pre-cleanup: stat socket (%s)", s_path);
475 return stab.st_atime;
478 static void preclean(void) {
479 DIR *dir = opendir(run_base);
481 if (errno == ENOENT) return;
482 diee("pre-cleanup: open run dir (%s)", run_base);
485 PrecleanEntry *entries=0;
486 size_t avail_entries=0;
487 size_t used_entries=0;
490 while ((errno = 0, de = readdir(dir))) {
491 char c0 = de->d_name[0];
492 if (!(c0 == 'l' || c0 == 's')) continue;
493 char *name_hash = m_asprintf("%s", de->d_name+1);
494 char *s_path = m_asprintf("%s/s%s", run_base, name_hash);
495 time_t atime = preclean_stat_atime(s_path);
497 if (avail_entries == used_entries) {
498 assert(avail_entries < INT_MAX / 4 / sizeof(PrecleanEntry));
501 entries = realloc(entries, avail_entries * sizeof(PrecleanEntry));
503 entries[used_entries].name_hash = name_hash;
504 entries[used_entries].atime = atime;
507 if (errno) diee("pre-cleanup: read run dir (%s)", run_base);
509 // First we dedupe (after sorting by path)
510 qsort(entries, used_entries, sizeof(PrecleanEntry),
511 preclean_entry_compar_name);
512 PrecleanEntry *p, *q;
513 for (p=entries, q=entries; p < entries + used_entries; p++) {
514 if (q > entries && !strcmp(p->name_hash, (q-1)->name_hash))
518 used_entries = q - entries;
520 // Now maybe delete some things
522 // Actually this has an off-by-one error since we are about
523 // to create a socket, so the actual number of sockets is one more.
524 // But, *actually*, since there might be multiple of us running at once,
525 // we might have even more than that. This doesn't really matter.
526 if (used_entries > max_sockets) {
527 qsort(entries, used_entries, sizeof(PrecleanEntry),
528 preclean_entry_compar_atime);
529 for (p=entries; p < entries + max_sockets; p++) {
530 char *l_path = m_asprintf("%s/l%s", run_base, p->name_hash);
531 char *s_path = m_asprintf("%s/s%s", run_base, p->name_hash);
532 int lock_fd = flock_file(l_path);
533 // Recheck atime - we might have raced!
534 time_t atime = preclean_stat_atime(s_path);
535 if (atime != p->atime) {
536 // Raced. This will leave use deleting too few things. Whatever.
538 int r= unlink(s_path);
539 if (r && errno!=ENOENT) diee("preclean: delete stale (%s)", s_path);
541 if (r) diee("preclean: delete stale lock (%s)", s_path);
542 // NB we don't hold the lock any more now.
550 for (p=entries; p < entries + used_entries; p++)
555 static __attribute((noreturn)) void die_data_overflow(void) {
556 die("cannot handle data with length >2^32");
559 static void prepare_data(size_t *len, char **buf,
560 const void *data, size_t dl) {
562 if (dl >= SIZE_MAX - *len)
567 memcpy(*buf, data, dl);
572 static void prepare_length(size_t *len, char **buf, size_t dl_sz) {
573 if (dl_sz > UINT32_MAX) die_data_overflow();
574 uint32_t dl = htonl(dl_sz);
575 prepare_data(len, buf, &dl, sizeof(dl));
578 static void prepare_string(size_t *len, char **buf, const char *s) {
579 size_t sl = strlen(s);
580 prepare_data(len, buf, s, sl+1);
583 static void prepare_message(size_t *len, char **buf) {
586 const char *const *p = (void*)environ;
589 prepare_string(len, buf, s);
592 prepare_string(len, buf, "");
596 prepare_string(len, buf, s);
599 static void send_fd(int payload_fd) {
600 int via_fd = fileno(call_sock);
603 struct cmsghdr align;
604 char buf[CMSG_SPACE(sizeof(payload_fd))];
614 iov.iov_base = &dummy_byte;
620 msg.msg_control = cmsg_buf.buf;
621 msg.msg_controllen = sizeof(cmsg_buf.buf);
623 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
624 cmsg->cmsg_level = SOL_SOCKET;
625 cmsg->cmsg_type = SCM_RIGHTS;
626 cmsg->cmsg_len = CMSG_LEN(sizeof(payload_fd));
627 *(int*)CMSG_DATA(cmsg) = payload_fd;
629 msg.msg_controllen = sizeof(cmsg_buf.buf);
632 ssize_t r = sendmsg(via_fd, &msg, 0);
634 if (errno == EINTR) continue;
642 static void send_request(void) {
644 ssize_t sr = fwrite(&ibyte, 1, 1, call_sock);
645 if (sr != 1) diee("write signalling byte");
647 // Sending these before the big message makes it easier for the script to
648 // use buffered IO for the message.
654 prepare_message(&len, 0);
656 size_t tlen = len + 4;
657 char *m = xmalloc(tlen);
659 prepare_length(0, &p, len);
660 prepare_message(0, &p);
661 assert(p == m + tlen);
663 sr = fwrite(m, tlen, 1, call_sock);
664 if (sr != 1) diee("write request (buffer)");
666 if (fflush(call_sock)) diee("write request");
669 static FILE *call_sock_from_fd(int fd) {
672 FILE *call_sock = fdopen(fd, "r+");
673 if (!call_sock) diee("fdopen socket");
675 r = setvbuf(call_sock, 0, _IONBF, 0);
676 if (r) die("setvbuf socket");
681 static bool was_eof(FILE *call_sock) {
682 return feof(call_sock) || errno==ECONNRESET;
686 static int protocol_read_maybe(void *data, size_t sz) {
688 size_t sr = fread(data, sz, 1, call_sock);
690 if (was_eof(call_sock)) return -1;
691 diee("read() on monitor call socket (%zd)", sz);
696 static void protocol_read(void *data, size_t sz) {
697 if (protocol_read_maybe(data, sz) < 0)
698 die("monitor process quit unexpectedly");
701 // Returns 0 if OK, error msg if peer was garbage.
702 static const char *read_greeting(void) {
703 char got_magic[sizeof(header_magic)];
705 if (protocol_read_maybe(&got_magic, sizeof(got_magic)) < 0)
706 return "initial monitor process quit";
708 if (memcmp(got_magic, header_magic, sizeof(header_magic)))
709 die("got unexpected protocol magic 0x%02x%02x%02x%02x",
710 got_magic[0], got_magic[1], got_magic[2], got_magic[3]);
713 protocol_read(&xdata_len, sizeof(xdata_len));
714 void *xdata = xmalloc(xdata_len);
715 protocol_read(xdata, xdata_len);
720 // Returns: call(client-end), or 0 to mean "is garbage"
721 // find_socket_path must have been called
722 static FILE *connect_existing(void) {
726 if (mode != MODE_NORMAL) return 0;
728 fd = socket(AF_UNIX, SOCK_STREAM, 0);
729 if (fd==-1) diee("socket() for client");
731 socklen_t salen = sizeof(sockaddr_sun);
732 r = connect(fd, (const struct sockaddr*)&sockaddr_sun, salen);
734 if (errno==ECONNREFUSED || errno==ENOENT) goto x_garbage;
735 diee("connect() %s", socket_path);
738 call_sock = call_sock_from_fd(fd);
747 if (call_sock) { fclose(call_sock); call_sock=0; }
748 if (fd >= 0) close(fd);
752 static void watcher_cb_stdin(uv_poll_t *handle, int status, int events) {
756 if ((errno = -status)) diee("watcher: poll stdin");
760 if (!(errno==EINTR || errno==EWOULDBLOCK || errno==EAGAIN))
761 diee("watcher: read sentinel stdin");
765 static void watcher_cb_sockpath(uv_fs_event_t *handle, const char *filename,
766 int events, int status) {
768 struct stat now_stab;
770 if ((errno = -status)) diee("watcher: poll stdin");
772 r= stat(socket_path, &now_stab);
774 if (errno==ENOENT) _exit(0);
775 if (errno==EINTR) continue;
776 diee("stat socket: %s", socket_path);
778 if (!stabs_same_inode(&now_stab, &initial_stab))
783 // On entry, stderr is still inherited, but 0 and 1 are the pipes
784 static __attribute__((noreturn))
785 void become_watcher(void) {
787 uv_poll_t uvhandle_stdin;
788 uv_fs_event_t uvhandle_sockpath;
793 errno= -uv_loop_init(&loop);
794 if (errno) diee("watcher: uv_loop_init");
796 errno= -uv_poll_init(&loop, &uvhandle_stdin, 0);
797 if (errno) diee("watcher: uv_poll_init");
798 errno= -uv_poll_start(&uvhandle_stdin,
799 UV_READABLE | UV_WRITABLE | UV_DISCONNECT,
801 if (errno) diee("watcher: uv_poll_start");
803 errno= -uv_fs_event_init(&loop, &uvhandle_sockpath);
804 if (errno) diee("watcher: uv_fs_event_init");
806 errno= -uv_fs_event_start(&uvhandle_sockpath, watcher_cb_sockpath,
808 if (errno) diee("watcher: uv_fs_event_start");
810 // OK everything is set up, let us daemonise
811 if (dup2(1,2) != 2) diee("watcher: set daemonised stderr");
812 r= setvbuf(stderr, 0, _IOLBF, BUFSIZ);
813 if (r) diee("watcher: setvbuf stderr");
815 pid_t child = fork();
816 if (child == (pid_t)-1) diee("watcher: fork");
819 if (setsid() == (pid_t)-1) diee("watcher: setsid");
821 r= uv_run(&loop, UV_RUN_DEFAULT);
822 die("uv_run returned (%d)", r);
825 static __attribute__((noreturn))
826 void become_setup(int sfd, int lockfd, int fake_pair[2],
827 int watcher_stdin, int watcher_stderr) {
830 int call_fd = fake_pair[1];
832 int null_0 = open("/dev/null", O_RDONLY); if (null_0 < 0) diee("open null");
833 if (dup2(null_0, 0)) diee("dup2 /dev/null onto stdin");
835 if (dup2(2, 1) != 1) die("dup2 stderr onto stdout");
839 // Extension could work like this:
841 // We could advertise a new protocol (perhaps one which is nearly entirely
842 // different after the connect) by putting a name for it comma-separated
843 // next to "v1". Simple extension can be done by having the script
844 // side say something about it in the ack xdata, which we currently ignore.
845 // Or we could add other extra data after v1.
846 putenv(m_asprintf("PREFORK_INTERP=v1,%jd.%09ld %d,%d,%d,%d",
847 (intmax_t)initial_stab.st_mtim.tv_sec,
848 (long)initial_stab.st_mtim.tv_nsec,
849 sfd, call_fd, watcher_stdin, watcher_stderr));
851 execvp(executor_argv[0], (char**)executor_argv);
852 diee("execute %s", executor_argv[0]);
855 static void connect_or_spawn(void) {
858 call_sock = connect_existing();
859 if (call_sock) return;
861 // We're going to make a new one, so clean out old ones
864 int lockfd = acquire_lock();
866 if (mode == MODE_KILL) {
867 r= unlink(socket_path);
868 if (r && errno != ENOENT) diee("remove socket %s", socket_path);
870 r= unlink(lock_path);
871 if (r) diee("rmeove lock %s", lock_path);
875 call_sock = connect_existing();
876 if (call_sock) { close(lockfd); return; }
878 // We must start a fresh one, and we hold the lock
880 r = unlink(socket_path);
881 if (r<0 && errno!=ENOENT)
882 diee("failed to remove stale socket %s", socket_path);
884 int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
885 if (sfd<0) diee("socket() for new listener");
887 socklen_t salen = sizeof(sockaddr_sun);
888 r= bind(sfd, (const struct sockaddr*)&sockaddr_sun, salen);
889 if (r<0) diee("bind() on new listener");
891 r= stat(socket_path, &initial_stab);
892 if (r<0) diee("stat() fresh socket");
894 // We never want callers to get ECONNREFUSED. But:
895 // There is a race here: from my RTFM they may get ECONNREFUSED
896 // if they try between our bind() and listen(). But if they do, they'll
897 // acquire the lock (serialising with us) and retry, and then it will work.
898 r = listen(sfd, INT_MAX);
899 if (r<0) diee("listen() for new listener");
903 int watcher_stdin[2];
904 int watcher_stderr[2];
905 if (pipe(watcher_stdin) || pipe(watcher_stderr))
906 diee("pipe() for socket inode watcher");
908 pid_t watcher = fork();
909 if (watcher == (pid_t)-1) diee("fork for watcher");
913 close(watcher_stdin[1]);
914 close(watcher_stderr[0]);
915 if (dup2(watcher_stdin[0], 0) != 0 ||
916 dup2(watcher_stderr[1], 1) != 1)
917 diee("initial dup2() for watcher");
918 close(watcher_stdin[0]);
919 close(watcher_stderr[1]);
923 close(watcher_stdin[0]);
924 close(watcher_stderr[1]);
925 nonblock(watcher_stderr[0]);
930 r = socketpair(AF_UNIX, SOCK_STREAM, 0, fake_pair);
931 if (r<0) diee("socketpair() for fake initial connection");
933 pid_t setup_pid = fork();
934 if (setup_pid == (pid_t)-1) diee("fork for spawn setup");
935 if (!setup_pid) become_setup(sfd, lockfd, fake_pair,
936 watcher_stdin[1], watcher_stderr[0]);
940 call_sock = call_sock_from_fd(fake_pair[0]);
943 pid_t got = waitpid(setup_pid, &status, 0);
944 if (got == (pid_t)-1) diee("waitpid setup [%ld]", (long)setup_pid);
945 if (got != setup_pid) diee("waitpid setup [%ld] gave [%ld]!",
946 (long)setup_pid, (long)got);
947 if (status != 0) propagate_exit_status(status, "setup");
949 const char *emsg = read_greeting();
950 if (emsg) die("setup failed: %s", emsg);
956 static void make_executor_argv(const char *const *argv) {
958 case MEDIATION_UNLAUNDERED: break;
959 default: die("need -U (specifying unlaundered argument handling)");
963 #define EACH_NEW_ARG(EACH) { \
964 arg = interp; { EACH } \
965 if ((arg = script)) { EACH } \
966 const char *const *walk = argv; \
967 while ((arg = *walk++)) { EACH } \
971 EACH_NEW_ARG( (void)arg; count++; );
973 const char **out = calloc(count, sizeof(char*));
974 executor_argv = (const char* const*)out;
975 if (!executor_argv) diee("allocate for arguments");
977 EACH_NEW_ARG( *out++ = arg; );
981 int main(int argc_unused, const char *const *argv) {
988 // which ought to be passed on to the actual executor.
989 make_executor_argv(argv);
992 FILLZERO(sockaddr_sun);
993 sockaddr_sun.sun_family = AF_UNIX;
994 assert(strlen(socket_path) <= sizeof(sockaddr_sun.sun_path));
995 strncpy(sockaddr_sun.sun_path, socket_path, sizeof(sockaddr_sun.sun_path));
999 // We're committed now, send the request (or bail out)
1003 protocol_read(&status, sizeof(status));
1005 status = ntohl(status);
1006 if (status > INT_MAX) die("status 0x%lx does not fit in an int",
1007 (unsigned long)status);
1009 propagate_exit_status(status, "invocation");