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 first two 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 (any further descriptors should be ignored, not closed)
281 3. Library should do the following:
283 1. Read and understand the PREFORK_INTERP env var.
284 If it is not set, initialisation complete should simply return.
285 (This allows simple synchronous operation.)
288 3. fork/exit (fork and have parent exit) (to make server)
289 4. setsid (to become session leader)
290 5. fork initial service (monitor) child, using CALL (see below)
291 6. Replace stdin/stdout/stderr with /dev/null,
292 and make a note to send all error messages to syslog
293 7. Enter select loop, looking for the following:
296 i. see if we need to reload: is any file forming part
297 of the program newer than the SECS.NSECS ?
298 If so, log at LOG_INFO, and exit immediately
299 (dropping CALL, LISTEN, WATCHI, etc.)
300 ii. see if we can reap any children, possibly waiting
301 for children if we are at our concurrency limit
302 (limit should be configured through library, default 4)
303 Report child exit status if not zero or SIGPIPE.
304 iii. fork service (monitor) child, using accepted fd
306 B. WATCHE is readable:
307 * EOF: log at LOG_INFO, and exit
308 * data to read: read what is available immediately;
309 it will be an error message: log it at LOG_ERR, and exit
311 4. service (monitor) child does the following:
313 1. close all of LISTEN, WATCHI, WATCHE
315 3. send a greeting (on CALL) "PFI\n\0\0\0\0" (8 bytes)
316 4. read a single byte, fail if it's not zero
317 5. three times, receive a single byte with a file descriptor
318 attached as ancillary data. (These descriptors will be
319 service stdin, stdout, stderr.)
320 6. read a 4-byte big-endian length
321 7. read that many bytes, the initial service request message,
322 which contains the following nul-terminated strings:
323 * environment variable settings in the format NAME=value
325 * arguments NOT INCLUDING argv[0] or script filename
326 (not that this means the service request must end in a nul)
327 8. make a new pipe EXECTERM
328 9. fork for the service executor; in the child
329 i. redirect stdin/stdout/stderr to the recevied fds
330 ii. replace environment and arguments with those received,
331 iii. close descriptors: close the original received descriptors;
332 close CALL; keep only the writing end of EXECTERM
333 iv. if the script programming language does things with SIGINT,
334 set it set back to default handling (immediate termination).
335 v. return back to script, now in the grandchild
337 10. in the parent, close EXECTERM writing end, and
338 11. select, looking for one of the following:
340 * EXECTERM reading end is readable
341 No need to actually read, since these shouldn't produce
342 spurious wakeups (but do loop on EINTR).
343 12. set SIGINT to ignored
344 13. send SIGINT to the entire process group
345 14. wait, blocking, for the executor child
346 15. write the wait status, in 32-bit big-endian, to CAL
349 Errors detected in the service monitor should be sent to
350 syslog, or stderr, depending on whether this is the initial
351 service monitor (from part 3 step 5) or an accepted socket
352 service monitor (from part 4 step 9); this can be achieved
353 easily by having a global flag (set at part 3 step 6),
354 or perhaps using logger(8) and redirecting stderr (but
355 then be careful to ensure everyone gets only the necessary fds).
357 EOF on CALL, or EPIPE/SIGPIPE writing to it, are not errors.
358 In this case, exit zero or die with SIGPIPE, so parent
359 won't report error either (part 3 step 7(A)(ii)).
361 ***************************************************************************
365 #include <arpa/inet.h>
366 #include <sys/utsname.h>
372 const char our_name[] = "prefork-interp";
374 static struct sockaddr_un sockaddr_sun;
375 static FILE *call_sock;
377 #define ACK_BYTE '\n'
379 static const char *const *executor_argv;
381 static const char header_magic[4] = "PFI\n";
383 void fusagemessage(FILE *f) {
384 fprintf(f, "usage: #!/usr/bin/prefork-interp [<options>]\n");
387 #define MODE_NORMAL 0
388 #define MODE_KILL 'k'
389 #define MODE_FRESH 'f'
391 #define MEDIATION_UNSPECIFIED 0
392 #define MEDIATION_UNLAUNDERED 'U'
394 static int mediation = MEDIATION_UNSPECIFIED;
395 static int mode = MODE_NORMAL;
396 static int max_sockets = 100; // maximum entries in the run dir is 2x this
398 static struct stat initial_stab;
400 const struct cmdinfo cmdinfos[]= {
402 { 0, 'U', 0, .iassignto= &mediation, .arg= MEDIATION_UNLAUNDERED },
403 { "kill", 0, 0, .iassignto= &mode, .arg= MODE_KILL },
404 { 0, 'f', 0, .iassignto= &mode, .arg= MODE_FRESH },
408 static void ident_add_stat(const char *path) {
410 int r = stat(path, &stab);
411 if (r) diee("failed to stat %s", path);
413 IDENT_ADD_OBJ(stab.st_dev);
414 IDENT_ADD_OBJ(stab.st_ino);
417 void ident_addinit(void) {
420 IDENT_ADD_OBJ(magic);
422 struct utsname uts = { };
423 size_t utslen = sizeof(uts);
425 if (r) diee("uname failed!");
426 IDENT_ADD_OBJ(utslen);
433 static void propagate_exit_status(int status, const char *what) {
436 if (WIFEXITED(status)) {
440 if (WIFSIGNALED(status)) {
441 int sig = WTERMSIG(status);
442 const char *signame = strsignal(sig);
443 if (signame == 0) signame = "unknown signal";
445 if (! WCOREDUMP(status) &&
452 sa.sa_handler = SIG_DFL;
453 r = sigaction(sig, &sa, 0);
454 if (r) diee("failed to reset signal handler while propagating %s",
459 sigaddset(&sset, sig);
460 r = sigprocmask(SIG_UNBLOCK, &sset, 0);
461 if (r) diee("failed to reset signal block while propagating %s",
465 die("unexpectedly kept running after raising (to propagate) %s",
469 die("%s failed due to signal %d %s%s", what, sig, signame,
470 WCOREDUMP(status) ? " (core dumped)" : "");
473 die("%s failed with weird wait status %d 0x%x", what, status, status);
481 static int preclean_entry_compar_name(const void *av, const void *bv) {
482 const PrecleanEntry *a = av;
483 const PrecleanEntry *b = bv;
484 return strcmp(a->name_hash, b->name_hash);
487 static int preclean_entry_compar_atime(const void *av, const void *bv) {
488 const PrecleanEntry *ae = av; time_t a = ae->atime;
489 const PrecleanEntry *be = bv; time_t b = be->atime;
494 static time_t preclean_stat_atime(const char *s_path) {
496 int r= lstat(s_path, &stab);
498 if (errno!=ENOENT) diee("pre-cleanup: stat socket (%s)", s_path);
501 return stab.st_atime;
504 static void preclean(void) {
505 DIR *dir = opendir(run_base);
507 if (errno == ENOENT) return;
508 diee("pre-cleanup: open run dir (%s)", run_base);
511 PrecleanEntry *entries=0;
512 size_t avail_entries=0;
513 size_t used_entries=0;
516 while ((errno = 0, de = readdir(dir))) {
517 char c0 = de->d_name[0];
518 if (!(c0 == 'l' || c0 == 's')) continue;
519 char *name_hash = m_asprintf("%s", de->d_name+1);
520 char *s_path = m_asprintf("%s/s%s", run_base, name_hash);
521 time_t atime = preclean_stat_atime(s_path);
523 if (avail_entries == used_entries) {
524 assert(avail_entries < INT_MAX / 4 / sizeof(PrecleanEntry));
527 entries = realloc(entries, avail_entries * sizeof(PrecleanEntry));
529 entries[used_entries].name_hash = name_hash;
530 entries[used_entries].atime = atime;
533 if (errno) diee("pre-cleanup: read run dir (%s)", run_base);
535 // First we dedupe (after sorting by path)
536 qsort(entries, used_entries, sizeof(PrecleanEntry),
537 preclean_entry_compar_name);
538 PrecleanEntry *p, *q;
539 for (p=entries, q=entries; p < entries + used_entries; p++) {
540 if (q > entries && !strcmp(p->name_hash, (q-1)->name_hash))
544 used_entries = q - entries;
546 // Now maybe delete some things
548 // Actually this has an off-by-one error since we are about
549 // to create a socket, so the actual number of sockets is one more.
550 // But, *actually*, since there might be multiple of us running at once,
551 // we might have even more than that. This doesn't really matter.
552 if (used_entries > max_sockets) {
553 qsort(entries, used_entries, sizeof(PrecleanEntry),
554 preclean_entry_compar_atime);
555 for (p=entries; p < entries + max_sockets; p++) {
556 char *l_path = m_asprintf("%s/l%s", run_base, p->name_hash);
557 char *s_path = m_asprintf("%s/s%s", run_base, p->name_hash);
558 int lock_fd = flock_file(l_path);
559 // Recheck atime - we might have raced!
560 time_t atime = preclean_stat_atime(s_path);
561 if (atime != p->atime) {
562 // Raced. This will leave use deleting too few things. Whatever.
564 int r= unlink(s_path);
565 if (r && errno!=ENOENT) diee("preclean: delete stale (%s)", s_path);
567 if (r) diee("preclean: delete stale lock (%s)", s_path);
568 // NB we don't hold the lock any more now.
576 for (p=entries; p < entries + used_entries; p++)
581 static __attribute((noreturn)) void die_data_overflow(void) {
582 die("cannot handle data with length >2^32");
585 static void prepare_data(size_t *len, char **buf,
586 const void *data, size_t dl) {
588 if (dl >= SIZE_MAX - *len)
593 memcpy(*buf, data, dl);
598 static void prepare_length(size_t *len, char **buf, size_t dl_sz) {
599 if (dl_sz > UINT32_MAX) die_data_overflow();
600 uint32_t dl = htonl(dl_sz);
601 prepare_data(len, buf, &dl, sizeof(dl));
604 static void prepare_string(size_t *len, char **buf, const char *s) {
605 size_t sl = strlen(s);
606 prepare_data(len, buf, s, sl+1);
609 static void prepare_message(size_t *len, char **buf) {
612 const char *const *p = (void*)environ;
615 prepare_string(len, buf, s);
618 prepare_string(len, buf, "");
622 prepare_string(len, buf, s);
625 static void send_fd(int payload_fd) {
626 int via_fd = fileno(call_sock);
629 struct cmsghdr align;
630 char buf[CMSG_SPACE(sizeof(payload_fd))];
640 iov.iov_base = &dummy_byte;
646 msg.msg_control = cmsg_buf.buf;
647 msg.msg_controllen = sizeof(cmsg_buf.buf);
649 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
650 cmsg->cmsg_level = SOL_SOCKET;
651 cmsg->cmsg_type = SCM_RIGHTS;
652 cmsg->cmsg_len = CMSG_LEN(sizeof(payload_fd));
653 *(int*)CMSG_DATA(cmsg) = payload_fd;
655 msg.msg_controllen = sizeof(cmsg_buf.buf);
658 ssize_t r = sendmsg(via_fd, &msg, 0);
660 if (errno == EINTR) continue;
668 static void send_request(void) {
670 ssize_t sr = fwrite(&ibyte, 1, 1, call_sock);
671 if (sr != 1) diee("write signalling byte");
673 // Sending these before the big message makes it easier for the script to
674 // use buffered IO for the message.
680 prepare_message(&len, 0);
682 size_t tlen = len + 4;
683 char *m = xmalloc(tlen);
685 prepare_length(0, &p, len);
686 prepare_message(0, &p);
687 assert(p == m + tlen);
689 sr = fwrite(m, tlen, 1, call_sock);
690 if (sr != 1) diee("write request (buffer)");
692 if (fflush(call_sock)) diee("write request");
695 static FILE *call_sock_from_fd(int fd) {
698 FILE *call_sock = fdopen(fd, "r+");
699 if (!call_sock) diee("fdopen socket");
701 r = setvbuf(call_sock, 0, _IONBF, 0);
702 if (r) die("setvbuf socket");
707 static bool was_eof(FILE *call_sock) {
708 return feof(call_sock) || errno==ECONNRESET;
712 static int protocol_read_maybe(void *data, size_t sz) {
714 size_t sr = fread(data, sz, 1, call_sock);
716 if (was_eof(call_sock)) return -1;
717 diee("read() on monitor call socket (%zd)", sz);
722 static void protocol_read(void *data, size_t sz) {
723 if (protocol_read_maybe(data, sz) < 0)
724 die("monitor process quit unexpectedly");
727 // Returns 0 if OK, error msg if peer was garbage.
728 static const char *read_greeting(void) {
729 char got_magic[sizeof(header_magic)];
731 if (protocol_read_maybe(&got_magic, sizeof(got_magic)) < 0)
732 return "initial monitor process quit";
734 if (memcmp(got_magic, header_magic, sizeof(header_magic)))
735 die("got unexpected protocol magic 0x%02x%02x%02x%02x",
736 got_magic[0], got_magic[1], got_magic[2], got_magic[3]);
739 protocol_read(&xdata_len, sizeof(xdata_len));
740 void *xdata = xmalloc(xdata_len);
741 protocol_read(xdata, xdata_len);
746 // Returns: call(client-end), or 0 to mean "is garbage"
747 // find_socket_path must have been called
748 static FILE *connect_existing(void) {
752 if (mode != MODE_NORMAL) return 0;
754 fd = socket(AF_UNIX, SOCK_STREAM, 0);
755 if (fd==-1) diee("socket() for client");
757 socklen_t salen = sizeof(sockaddr_sun);
758 r = connect(fd, (const struct sockaddr*)&sockaddr_sun, salen);
760 if (errno==ECONNREFUSED || errno==ENOENT) goto x_garbage;
761 diee("connect() %s", socket_path);
764 call_sock = call_sock_from_fd(fd);
773 if (call_sock) { fclose(call_sock); call_sock=0; }
774 if (fd >= 0) close(fd);
778 static void watcher_cb_stdin(uv_poll_t *handle, int status, int events) {
782 if ((errno = -status)) diee("watcher: poll stdin");
786 if (!(errno==EINTR || errno==EWOULDBLOCK || errno==EAGAIN))
787 diee("watcher: read sentinel stdin");
791 static void watcher_cb_sockpath(uv_fs_event_t *handle, const char *filename,
792 int events, int status) {
794 struct stat now_stab;
796 if ((errno = -status)) diee("watcher: poll stdin");
798 r= stat(socket_path, &now_stab);
800 if (errno==ENOENT) _exit(0);
801 if (errno==EINTR) continue;
802 diee("stat socket: %s", socket_path);
804 if (!stabs_same_inode(&now_stab, &initial_stab))
809 // On entry, stderr is still inherited, but 0 and 1 are the pipes
810 static __attribute__((noreturn))
811 void become_watcher(void) {
813 uv_poll_t uvhandle_stdin;
814 uv_fs_event_t uvhandle_sockpath;
819 errno= -uv_loop_init(&loop);
820 if (errno) diee("watcher: uv_loop_init");
822 errno= -uv_poll_init(&loop, &uvhandle_stdin, 0);
823 if (errno) diee("watcher: uv_poll_init");
824 errno= -uv_poll_start(&uvhandle_stdin,
825 UV_READABLE | UV_WRITABLE | UV_DISCONNECT,
827 if (errno) diee("watcher: uv_poll_start");
829 errno= -uv_fs_event_init(&loop, &uvhandle_sockpath);
830 if (errno) diee("watcher: uv_fs_event_init");
832 errno= -uv_fs_event_start(&uvhandle_sockpath, watcher_cb_sockpath,
834 if (errno) diee("watcher: uv_fs_event_start");
836 // OK everything is set up, let us daemonise
837 if (dup2(1,2) != 2) diee("watcher: set daemonised stderr");
838 r= setvbuf(stderr, 0, _IOLBF, BUFSIZ);
839 if (r) diee("watcher: setvbuf stderr");
841 pid_t child = fork();
842 if (child == (pid_t)-1) diee("watcher: fork");
845 if (setsid() == (pid_t)-1) diee("watcher: setsid");
847 r= uv_run(&loop, UV_RUN_DEFAULT);
848 die("uv_run returned (%d)", r);
851 static __attribute__((noreturn))
852 void become_setup(int sfd, int lockfd, int fake_pair[2],
853 int watcher_stdin, int watcher_stderr) {
856 int call_fd = fake_pair[1];
858 int null_0 = open("/dev/null", O_RDONLY); if (null_0 < 0) diee("open null");
859 if (dup2(null_0, 0)) diee("dup2 /dev/null onto stdin");
861 if (dup2(2, 1) != 1) die("dup2 stderr onto stdout");
865 // Extension could work like this:
867 // We could advertise a new protocol (perhaps one which is nearly entirely
868 // different after the connect) by putting a name for it comma-separated
869 // next to "v1". Simple extension can be done by having the script
870 // side say something about it in the ack xdata, which we currently ignore.
871 // Or we could add other extra data after v1.
872 putenv(m_asprintf("PREFORK_INTERP=v1,%jd.%09ld %d,%d,%d,%d",
873 (intmax_t)initial_stab.st_mtim.tv_sec,
874 (long)initial_stab.st_mtim.tv_nsec,
875 sfd, call_fd, watcher_stdin, watcher_stderr));
877 execvp(executor_argv[0], (char**)executor_argv);
878 diee("execute %s", executor_argv[0]);
881 static void connect_or_spawn(void) {
884 call_sock = connect_existing();
885 if (call_sock) return;
887 // We're going to make a new one, so clean out old ones
890 int lockfd = acquire_lock();
892 if (mode == MODE_KILL) {
893 r= unlink(socket_path);
894 if (r && errno != ENOENT) diee("remove socket %s", socket_path);
896 r= unlink(lock_path);
897 if (r) diee("rmeove lock %s", lock_path);
901 call_sock = connect_existing();
902 if (call_sock) { close(lockfd); return; }
904 // We must start a fresh one, and we hold the lock
906 r = unlink(socket_path);
907 if (r<0 && errno!=ENOENT)
908 diee("failed to remove stale socket %s", socket_path);
910 int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
911 if (sfd<0) diee("socket() for new listener");
913 socklen_t salen = sizeof(sockaddr_sun);
914 r= bind(sfd, (const struct sockaddr*)&sockaddr_sun, salen);
915 if (r<0) diee("bind() on new listener");
917 r= stat(socket_path, &initial_stab);
918 if (r<0) diee("stat() fresh socket");
920 // We never want callers to get ECONNREFUSED. But:
921 // There is a race here: from my RTFM they may get ECONNREFUSED
922 // if they try between our bind() and listen(). But if they do, they'll
923 // acquire the lock (serialising with us) and retry, and then it will work.
924 r = listen(sfd, INT_MAX);
925 if (r<0) diee("listen() for new listener");
929 int watcher_stdin[2];
930 int watcher_stderr[2];
931 if (pipe(watcher_stdin) || pipe(watcher_stderr))
932 diee("pipe() for socket inode watcher");
934 pid_t watcher = fork();
935 if (watcher == (pid_t)-1) diee("fork for watcher");
939 close(watcher_stdin[1]);
940 close(watcher_stderr[0]);
941 if (dup2(watcher_stdin[0], 0) != 0 ||
942 dup2(watcher_stderr[1], 1) != 1)
943 diee("initial dup2() for watcher");
944 close(watcher_stdin[0]);
945 close(watcher_stderr[1]);
949 close(watcher_stdin[0]);
950 close(watcher_stderr[1]);
951 nonblock(watcher_stderr[0]);
956 r = socketpair(AF_UNIX, SOCK_STREAM, 0, fake_pair);
957 if (r<0) diee("socketpair() for fake initial connection");
959 pid_t setup_pid = fork();
960 if (setup_pid == (pid_t)-1) diee("fork for spawn setup");
961 if (!setup_pid) become_setup(sfd, lockfd, fake_pair,
962 watcher_stdin[1], watcher_stderr[0]);
966 call_sock = call_sock_from_fd(fake_pair[0]);
969 pid_t got = waitpid(setup_pid, &status, 0);
970 if (got == (pid_t)-1) diee("waitpid setup [%ld]", (long)setup_pid);
971 if (got != setup_pid) diee("waitpid setup [%ld] gave [%ld]!",
972 (long)setup_pid, (long)got);
973 if (status != 0) propagate_exit_status(status, "setup");
975 const char *emsg = read_greeting();
976 if (emsg) die("setup failed: %s", emsg);
982 static void make_executor_argv(const char *const *argv) {
984 case MEDIATION_UNLAUNDERED: break;
985 default: die("need -U (specifying unlaundered argument handling)");
989 #define EACH_NEW_ARG(EACH) { \
990 arg = interp; { EACH } \
991 if ((arg = script)) { EACH } \
992 const char *const *walk = argv; \
993 while ((arg = *walk++)) { EACH } \
997 EACH_NEW_ARG( (void)arg; count++; );
999 const char **out = calloc(count, sizeof(char*));
1000 executor_argv = (const char* const*)out;
1001 if (!executor_argv) diee("allocate for arguments");
1003 EACH_NEW_ARG( *out++ = arg; );
1007 int main(int argc_unused, const char *const *argv) {
1008 process_opts(&argv);
1011 // - possibly interp
1012 // - possibly script
1014 // which ought to be passed on to the actual executor.
1015 make_executor_argv(argv);
1018 FILLZERO(sockaddr_sun);
1019 sockaddr_sun.sun_family = AF_UNIX;
1020 assert(strlen(socket_path) <= sizeof(sockaddr_sun.sun_path));
1021 strncpy(sockaddr_sun.sun_path, socket_path, sizeof(sockaddr_sun.sun_path));
1025 // We're committed now, send the request (or bail out)
1029 protocol_read(&status, sizeof(status));
1031 status = ntohl(status);
1032 if (status > INT_MAX) die("status 0x%lx does not fit in an int",
1033 (unsigned long)status);
1035 propagate_exit_status(status, "invocation");