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: split on ASCII space (or any whitespace), taking
259 first two words. There may or may not be further "words".
260 Then split each of the first two words on comma,
261 again taking the initial items as specified. The items are:
263 v1 Protocol version indicator - literal. If something else,
264 fail (installation is incompatible somehow).
267 timestamp before script started running, as a decimal
268 time_t. NSECS is exactly 9 digits.
269 To be used for auto reloading.
271 These items are file descriptors:
273 LISTEN listening socket nonblocking
274 CALL call socket for initial call blocking
275 WATCHE liveness watcher stderr nonblocking
276 WATCHI liveness sentinel unspecified
278 3. Library should do the following:
280 1. Read and understand the PREFORK_INTERP env var.
281 If it is not set, initialisation complete should simply return.
282 This allows simple synchronous operation.
285 3. fork/exit (fork and have parent exit) (to make server)
286 4. setsid (to become session leader)
287 5. fork initial service (monitor) child, using CALL (see below)
288 6. Replace stdin/stdout/stderr with /dev/null,
289 and make a note to send all error messages to syslog
290 7. Enter select loop, looking for the following:
293 i. see if we need to reload: is any file forming part
294 of the program newer than the SECS.NSECS ?
295 If so, log at LOG_INFO, and exit immediately
296 (dropping CALL, LISTEN, WATCHI, etc.)
297 ii. see if we can reap any children, possibly waiting
298 for children if we are at our concurrency limit
299 (limit should be configured through library, default 4)
300 iii. fork service (monitor) child, using accepted fd
302 * WATCHE is readable:
303 * EOF:: log at LOG_INFO, and exit
304 * data to read: read what is available immediately,
305 log it as a message at LOG_ERR, and exit
307 4. service (monitor) child does the following:
309 1. close all of LISTEN, WATCHI, WATCHE
311 3. send a greeting (on CALL) "PFI\n\0\0\0\0" (8 bytes)
312 4. read a single byte, fail if it's not zero
313 5. three times, receive a single byte with a file descriptor
314 attached as ancillary data. (These descriptors will be
315 service stdin, stdout, stderr.)
316 6. read a 4-byte big-endian length
317 7. read that many bytes, the initial service request message,
318 which contains the following nul-terminated strings:
319 * environment variable settings in the format NAME=value
321 * arguments NOT INCLUDING argv[0] or script filename
322 (not that this means the service request must end in a nul)
323 8. make a new pipe EXECTERM
324 9. fork for the service executor; in the child
325 i. redirect stdin/stdout/stderr to the recevied fds
326 ii. replace environment and arguments with those received,
327 iii. close descriptors: close the original received descriptors;
328 close CALL; keep only the writing end of EXECTERM
329 iv. if the script programming language does things with SIGINT,
330 set it set back to default handling (immediate termination).
331 v. return back to script, now in the grandchild
333 10. in the parent, close EXECTERM writing end, and
334 11. select, looking for one of the following:
336 * EXECTERM reading end is readable
337 No need to actually read, since these shouldn't produce
338 spurious wakeups (but do loop on EINTR).
339 12. set SIGINT to ignored
340 13. send SIGINT to the entire process group
341 14. wait, blocking, for the executor child
342 15. write the wait status, in 32-bit big-endian, to CALL
343 (this may generate SIGPIPE/EPIPE;
344 if so, die with SIGPIPE or exit 0; do treat that as failure)
347 ***************************************************************************
351 #include <arpa/inet.h>
357 const char our_name[] = "prefork-interp";
359 static struct sockaddr_un sockaddr_sun;
360 static FILE *call_sock;
362 #define ACK_BYTE '\n'
364 static const char *const *executor_argv;
366 static const char header_magic[4] = "PFI\n";
368 void fusagemessage(FILE *f) {
369 fprintf(f, "usage: #!/usr/bin/prefork-interp [<options>]\n");
372 #define MODE_NORMAL 0
373 #define MODE_KILL 'k'
374 #define MODE_FRESH 'f'
376 #define MEDIATION_UNSPECIFIED 0
377 #define MEDIATION_UNLAUNDERED 'U'
379 static int mediation = MEDIATION_UNSPECIFIED;
380 static int mode = MODE_NORMAL;
381 static int max_sockets = 100; // maximum entries in the run dir is 2x this
383 static struct stat initial_stab;
385 const struct cmdinfo cmdinfos[]= {
387 { 0, 'U', 0, .iassignto= &mediation, .arg= MEDIATION_UNLAUNDERED },
388 { "kill", 0, 0, .iassignto= &mode, .arg= MODE_KILL },
389 { 0, 'f', 0, .iassignto= &mode, .arg= MODE_FRESH },
393 void ident_addinit(void) {
394 char ident_magic[1] = { 0 };
395 sha256_update(&identsc, sizeof(ident_magic), ident_magic);
398 static void propagate_exit_status(int status, const char *what) {
401 if (WIFEXITED(status)) {
405 if (WIFSIGNALED(status)) {
406 int sig = WTERMSIG(status);
407 const char *signame = strsignal(sig);
408 if (signame == 0) signame = "unknown signal";
410 if (! WCOREDUMP(status) &&
417 sa.sa_handler = SIG_DFL;
418 r = sigaction(sig, &sa, 0);
419 if (r) diee("failed to reset signal handler while propagating %s",
424 sigaddset(&sset, sig);
425 r = sigprocmask(SIG_UNBLOCK, &sset, 0);
426 if (r) diee("failed to reset signal block while propagating %s",
430 die("unexpectedly kept running after raising (to propagate) %s",
434 die("%s failed due to signal %d %s%s", what, sig, signame,
435 WCOREDUMP(status) ? " (core dumped)" : "");
438 die("%s failed with weird wait status %d 0x%x", what, status, status);
446 static int preclean_entry_compar_name(const void *av, const void *bv) {
447 const PrecleanEntry *a = av;
448 const PrecleanEntry *b = bv;
449 return strcmp(a->name_hash, b->name_hash);
452 static int preclean_entry_compar_atime(const void *av, const void *bv) {
453 const PrecleanEntry *ae = av; time_t a = ae->atime;
454 const PrecleanEntry *be = bv; time_t b = be->atime;
459 static time_t preclean_stat_atime(const char *s_path) {
461 int r= lstat(s_path, &stab);
463 if (errno!=ENOENT) diee("pre-cleanup: stat socket (%s)", s_path);
466 return stab.st_atime;
469 static void preclean(void) {
470 DIR *dir = opendir(run_base);
472 if (errno == ENOENT) return;
473 diee("pre-cleanup: open run dir (%s)", run_base);
476 PrecleanEntry *entries=0;
477 size_t avail_entries=0;
478 size_t used_entries=0;
481 while ((errno = 0, de = readdir(dir))) {
482 char c0 = de->d_name[0];
483 if (!(c0 == 'l' || c0 == 's')) continue;
484 char *name_hash = m_asprintf("%s", de->d_name+1);
485 char *s_path = m_asprintf("%s/s%s", run_base, name_hash);
486 time_t atime = preclean_stat_atime(s_path);
488 if (avail_entries == used_entries) {
489 assert(avail_entries < INT_MAX / 4 / sizeof(PrecleanEntry));
492 entries = realloc(entries, avail_entries * sizeof(PrecleanEntry));
494 entries[used_entries].name_hash = name_hash;
495 entries[used_entries].atime = atime;
498 if (errno) diee("pre-cleanup: read run dir (%s)", run_base);
500 // First we dedupe (after sorting by path)
501 qsort(entries, used_entries, sizeof(PrecleanEntry),
502 preclean_entry_compar_name);
503 PrecleanEntry *p, *q;
504 for (p=entries, q=entries; p < entries + used_entries; p++) {
505 if (q > entries && !strcmp(p->name_hash, (q-1)->name_hash))
509 used_entries = q - entries;
511 // Now maybe delete some things
513 // Actually this has an off-by-one error since we are about
514 // to create a socket, so the actual number of sockets is one more.
515 // But, *actually*, since there might be multiple of us running at once,
516 // we might have even more than that. This doesn't really matter.
517 if (used_entries > max_sockets) {
518 qsort(entries, used_entries, sizeof(PrecleanEntry),
519 preclean_entry_compar_atime);
520 for (p=entries; p < entries + max_sockets; p++) {
521 char *l_path = m_asprintf("%s/l%s", run_base, p->name_hash);
522 char *s_path = m_asprintf("%s/s%s", run_base, p->name_hash);
523 int lock_fd = flock_file(l_path);
524 // Recheck atime - we might have raced!
525 time_t atime = preclean_stat_atime(s_path);
526 if (atime != p->atime) {
527 // Raced. This will leave use deleting too few things. Whatever.
529 int r= unlink(s_path);
530 if (r && errno!=ENOENT) diee("preclean: delete stale (%s)", s_path);
532 if (r) diee("preclean: delete stale lock (%s)", s_path);
533 // NB we don't hold the lock any more now.
541 for (p=entries; p < entries + used_entries; p++)
546 static __attribute((noreturn)) void die_data_overflow(void) {
547 die("cannot handle data with length >2^32");
550 static void prepare_data(size_t *len, char **buf,
551 const void *data, size_t dl) {
553 if (dl >= SIZE_MAX - *len)
558 memcpy(*buf, data, dl);
563 static void prepare_length(size_t *len, char **buf, size_t dl_sz) {
564 if (dl_sz > UINT32_MAX) die_data_overflow();
565 uint32_t dl = htonl(dl_sz);
566 prepare_data(len, buf, &dl, sizeof(dl));
569 static void prepare_string(size_t *len, char **buf, const char *s) {
570 size_t sl = strlen(s);
571 prepare_data(len, buf, s, sl+1);
574 static void prepare_message(size_t *len, char **buf) {
577 const char *const *p = (void*)environ;
580 prepare_string(len, buf, s);
583 prepare_string(len, buf, "");
587 prepare_string(len, buf, s);
590 static void send_fd(int payload_fd) {
591 int via_fd = fileno(call_sock);
594 struct cmsghdr align;
595 char buf[CMSG_SPACE(sizeof(payload_fd))];
605 iov.iov_base = &dummy_byte;
611 msg.msg_control = cmsg_buf.buf;
612 msg.msg_controllen = sizeof(cmsg_buf.buf);
614 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
615 cmsg->cmsg_level = SOL_SOCKET;
616 cmsg->cmsg_type = SCM_RIGHTS;
617 cmsg->cmsg_len = CMSG_LEN(sizeof(payload_fd));
618 *(int*)CMSG_DATA(cmsg) = payload_fd;
620 msg.msg_controllen = sizeof(cmsg_buf.buf);
623 ssize_t r = sendmsg(via_fd, &msg, 0);
625 if (errno == EINTR) continue;
633 static void send_request(void) {
635 ssize_t sr = fwrite(&ibyte, 1, 1, call_sock);
636 if (sr != 1) diee("write signalling byte");
638 // Sending these before the big message makes it easier for the script to
639 // use buffered IO for the message.
645 prepare_message(&len, 0);
647 size_t tlen = len + 4;
648 char *m = xmalloc(tlen);
650 prepare_length(0, &p, len);
651 prepare_message(0, &p);
652 assert(p == m + tlen);
654 sr = fwrite(m, tlen, 1, call_sock);
655 if (sr != 1) diee("write request (buffer)");
657 if (fflush(call_sock)) diee("write request");
660 static FILE *call_sock_from_fd(int fd) {
663 FILE *call_sock = fdopen(fd, "r+");
664 if (!call_sock) diee("fdopen socket");
666 r = setvbuf(call_sock, 0, _IONBF, 0);
667 if (r) die("setvbuf socket");
672 static bool was_eof(FILE *call_sock) {
673 return feof(call_sock) || errno==ECONNRESET;
677 static int protocol_read_maybe(void *data, size_t sz) {
679 size_t sr = fread(data, sz, 1, call_sock);
681 if (was_eof(call_sock)) return -1;
682 diee("read() on monitor call socket (%zd)", sz);
687 static void protocol_read(void *data, size_t sz) {
688 if (protocol_read_maybe(data, sz) < 0)
689 die("monitor process quit unexpectedly");
692 // Returns 0 if OK, error msg if peer was garbage.
693 static const char *read_greeting(void) {
694 char got_magic[sizeof(header_magic)];
696 if (protocol_read_maybe(&got_magic, sizeof(got_magic)) < 0)
697 return "initial monitor process quit";
699 if (memcmp(got_magic, header_magic, sizeof(header_magic)))
700 die("got unexpected protocol magic 0x%02x%02x%02x%02x",
701 got_magic[0], got_magic[1], got_magic[2], got_magic[3]);
704 protocol_read(&xdata_len, sizeof(xdata_len));
705 void *xdata = xmalloc(xdata_len);
706 protocol_read(xdata, xdata_len);
711 // Returns: call(client-end), or 0 to mean "is garbage"
712 // find_socket_path must have been called
713 static FILE *connect_existing(void) {
717 if (mode != MODE_NORMAL) return 0;
719 fd = socket(AF_UNIX, SOCK_STREAM, 0);
720 if (fd==-1) diee("socket() for client");
722 socklen_t salen = sizeof(sockaddr_sun);
723 r = connect(fd, (const struct sockaddr*)&sockaddr_sun, salen);
725 if (errno==ECONNREFUSED || errno==ENOENT) goto x_garbage;
726 diee("connect() %s", socket_path);
729 call_sock = call_sock_from_fd(fd);
738 if (call_sock) { fclose(call_sock); call_sock=0; }
739 if (fd >= 0) close(fd);
743 static void watcher_cb_stdin(uv_poll_t *handle, int status, int events) {
747 if ((errno = -status)) diee("watcher: poll stdin");
751 if (!(errno==EINTR || errno==EWOULDBLOCK || errno==EAGAIN))
752 diee("watcher: read sentinel stdin");
756 static void watcher_cb_sockpath(uv_fs_event_t *handle, const char *filename,
757 int events, int status) {
759 struct stat now_stab;
761 if ((errno = -status)) diee("watcher: poll stdin");
763 r= stat(socket_path, &now_stab);
765 if (errno==ENOENT) _exit(0);
766 if (errno==EINTR) continue;
767 diee("stat socket: %s", socket_path);
769 if (!stabs_same_inode(&now_stab, &initial_stab))
774 // On entry, stderr is still inherited, but 0 and 1 are the pipes
775 static __attribute__((noreturn))
776 void become_watcher(void) {
778 uv_poll_t uvhandle_stdin;
779 uv_fs_event_t uvhandle_sockpath;
784 errno= -uv_loop_init(&loop);
785 if (errno) diee("watcher: uv_loop_init");
787 errno= -uv_poll_init(&loop, &uvhandle_stdin, 0);
788 if (errno) diee("watcher: uv_poll_init");
789 errno= -uv_poll_start(&uvhandle_stdin,
790 UV_READABLE | UV_WRITABLE | UV_DISCONNECT,
792 if (errno) diee("watcher: uv_poll_start");
794 errno= -uv_fs_event_init(&loop, &uvhandle_sockpath);
795 if (errno) diee("watcher: uv_fs_event_init");
797 errno= -uv_fs_event_start(&uvhandle_sockpath, watcher_cb_sockpath,
799 if (errno) diee("watcher: uv_fs_event_start");
801 // OK everything is set up, let us daemonise
802 if (dup2(1,2) != 2) diee("watcher: set daemonised stderr");
803 r= setvbuf(stderr, 0, _IOLBF, BUFSIZ);
804 if (r) diee("watcher: setvbuf stderr");
806 pid_t child = fork();
807 if (child == (pid_t)-1) diee("watcher: fork");
810 if (setsid() == (pid_t)-1) diee("watcher: setsid");
812 r= uv_run(&loop, UV_RUN_DEFAULT);
813 die("uv_run returned (%d)", r);
816 static __attribute__((noreturn))
817 void become_setup(int sfd, int lockfd, int fake_pair[2],
818 int watcher_stdin, int watcher_stderr) {
821 int call_fd = fake_pair[1];
823 int null_0 = open("/dev/null", O_RDONLY); if (null_0 < 0) diee("open null");
824 if (dup2(null_0, 0)) diee("dup2 /dev/null onto stdin");
826 if (dup2(2, 1) != 1) die("dup2 stderr onto stdout");
830 // Extension could work like this:
832 // We could advertise a new protocol (perhaps one which is nearly entirely
833 // different after the connect) by putting a name for it comma-separated
834 // next to "v1". Simple extension can be done by having the script
835 // side say something about it in the ack xdata, which we currently ignore.
836 // Or we could add other extra data after v1.
837 putenv(m_asprintf("PREFORK_INTERP=v1,%jd.%09ld %d,%d,%d,%d",
838 (intmax_t)initial_stab.st_mtim.tv_sec,
839 (long)initial_stab.st_mtim.tv_nsec,
840 sfd, call_fd, watcher_stdin, watcher_stderr));
842 execvp(executor_argv[0], (char**)executor_argv);
843 diee("execute %s", executor_argv[0]);
846 static void connect_or_spawn(void) {
849 call_sock = connect_existing();
850 if (call_sock) return;
852 // We're going to make a new one, so clean out old ones
855 int lockfd = acquire_lock();
857 if (mode == MODE_KILL) {
858 r= unlink(socket_path);
859 if (r && errno != ENOENT) diee("remove socket %s", socket_path);
861 r= unlink(lock_path);
862 if (r) diee("rmeove lock %s", lock_path);
866 call_sock = connect_existing();
867 if (call_sock) { close(lockfd); return; }
869 // We must start a fresh one, and we hold the lock
871 r = unlink(socket_path);
872 if (r<0 && errno!=ENOENT)
873 diee("failed to remove stale socket %s", socket_path);
875 int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
876 if (sfd<0) diee("socket() for new listener");
878 socklen_t salen = sizeof(sockaddr_sun);
879 r= bind(sfd, (const struct sockaddr*)&sockaddr_sun, salen);
880 if (r<0) diee("bind() on new listener");
882 r= stat(socket_path, &initial_stab);
883 if (r<0) diee("stat() fresh socket");
885 // We never want callers to get ECONNREFUSED. But:
886 // There is a race here: from my RTFM they may get ECONNREFUSED
887 // if they try between our bind() and listen(). But if they do, they'll
888 // acquire the lock (serialising with us) and retry, and then it will work.
889 r = listen(sfd, INT_MAX);
890 if (r<0) diee("listen() for new listener");
894 int watcher_stdin[2];
895 int watcher_stderr[2];
896 if (pipe(watcher_stdin) || pipe(watcher_stderr))
897 diee("pipe() for socket inode watcher");
899 pid_t watcher = fork();
900 if (watcher == (pid_t)-1) diee("fork for watcher");
904 close(watcher_stdin[1]);
905 close(watcher_stderr[0]);
906 if (dup2(watcher_stdin[0], 0) != 0 ||
907 dup2(watcher_stderr[1], 1) != 1)
908 diee("initial dup2() for watcher");
909 close(watcher_stdin[0]);
910 close(watcher_stderr[1]);
914 close(watcher_stdin[0]);
915 close(watcher_stderr[1]);
916 nonblock(watcher_stderr[0]);
921 r = socketpair(AF_UNIX, SOCK_STREAM, 0, fake_pair);
922 if (r<0) diee("socketpair() for fake initial connection");
924 pid_t setup_pid = fork();
925 if (setup_pid == (pid_t)-1) diee("fork for spawn setup");
926 if (!setup_pid) become_setup(sfd, lockfd, fake_pair,
927 watcher_stdin[1], watcher_stderr[0]);
931 call_sock = call_sock_from_fd(fake_pair[0]);
934 pid_t got = waitpid(setup_pid, &status, 0);
935 if (got == (pid_t)-1) diee("waitpid setup [%ld]", (long)setup_pid);
936 if (got != setup_pid) diee("waitpid setup [%ld] gave [%ld]!",
937 (long)setup_pid, (long)got);
938 if (status != 0) propagate_exit_status(status, "setup");
940 const char *emsg = read_greeting();
941 if (emsg) die("setup failed: %s", emsg);
947 static void make_executor_argv(const char *const *argv) {
949 case MEDIATION_UNLAUNDERED: break;
950 default: die("need -U (specifying unlaundered argument handling)");
954 #define EACH_NEW_ARG(EACH) { \
955 arg = interp; { EACH } \
956 if ((arg = script)) { EACH } \
957 const char *const *walk = argv; \
958 while ((arg = *walk++)) { EACH } \
962 EACH_NEW_ARG( (void)arg; count++; );
964 const char **out = calloc(count, sizeof(char*));
965 executor_argv = (const char* const*)out;
966 if (!executor_argv) diee("allocate for arguments");
968 EACH_NEW_ARG( *out++ = arg; );
972 int main(int argc_unused, const char *const *argv) {
979 // which ought to be passed on to the actual executor.
980 make_executor_argv(argv);
983 FILLZERO(sockaddr_sun);
984 sockaddr_sun.sun_family = AF_UNIX;
985 assert(strlen(socket_path) <= sizeof(sockaddr_sun.sun_path));
986 strncpy(sockaddr_sun.sun_path, socket_path, sizeof(sockaddr_sun.sun_path));
990 // We're committed now, send the request (or bail out)
994 protocol_read(&status, sizeof(status));
996 status = ntohl(status);
997 if (status > INT_MAX) die("status 0x%lx does not fit in an int",
998 (unsigned long)status);
1000 propagate_exit_status(status, "invocation");