chiark / gitweb /
prefork-interp: kill and fresh modes
[chiark-utils.git] / cprogs / prefork-interp.c
1 /*
2  * "Interpreter" that you can put in #! like this
3  *   #!/usr/bin/prefork-interp [<options>] <interpreter>
4  *
5  * Usages:
6  *   prefork-interp  [<option> ..] <interpreter>  [<script> [<args> ...]]
7  *   prefork-interp  [<option>,..],<interpreter>   <script> [<args> ...]
8  *   prefork-interp '[<option> ..] <interpreter>'  <script> [<args> ...]
9  *
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
15  */
16
17 /*
18 ***************************************************************************
19 \f
20   State during service execution, process parentage and key fds
21
22       CALLER
23         ||
24         ||
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
31                                    \ \ \     \   ||
32                                    | | |\     |  ||
33                                    | | | \    |  ||
34                                    | \ |  \   \  ||
35                                     \ \ \  \   MONITOR &
36                                      \ \ \  `12  ||  |
37                                       \ \ \      ||  |
38                                        \ \ \     ||  |execterm
39                                         \ \ \    ||  |
40                                          \ \ \   ||  |
41                                           \ \ 2  ||  |
42                                            \ 1 EXECUTOR
43                                             0
44     ----      pipes, sockets
45     012       descriptors
46     -==-      fds shared
47     ||        process parentage
48     &&        session leader (daemon)
49     &         process group leader
50
51 ***************************************************************************
52 \f
53  Control flow and causality
54
55       CALLER
56          |
57          |fork/exec
58          |
59       CLIENT
60          |
61       attempt to connect, and read greeting
62          |failure?                \success?
63          |                         \
64       tidy up stale /run entries    *1 (continue from send_fds, below)
65       acquire lock
66          |
67       retry attempt to connect, and read greeting
68          |failure?                \success?
69          |                         \
70       create listening socket     release lock
71          |                           \
72       fork/daemonise                  *1
73          |    `------------------.
74          |                      WATCHER(C) &&
75          |
76        make "fake" initial call socketpair                               (C)
77          |                                                    prefork-interp
78        fork/exec   #########################################################
79          |      `-------------.                                  application
80          |         #        SCRIPT (setup)
81          |         #          |
82          |         #       script initialisation
83          |         #          |                                  application
84          |         #  ########|#############################################
85          |         #          |                               prefork-interp
86          |         #       identify fds from envirnment               (Perl)
87          |         #       open syslog
88          |         #          |
89          |         #       dzemonize
90          |   ,.....<....../   |
91       waitpid      #        fork for initial service
92          |         #          |child?       |parent?
93          |         #          |             |
94          |         #          |         SCRIPT [server] &&
95          |         #          |             |
96          |         #          |         ** accept / event loop **
97          |         #          |        accepted?    \      \ \
98          |         #          |            /         \ watch\ \idle
99          |         #          |        fork child     \stderr\ \timeout?
100          |         #          | _________/            |       | |
101          |         #          |/                      |read?  | |
102          |         #     SCRIPT [monitor]             |   eof?| |
103          |         #       setpgrpt &                 |       | |
104          |         #          |                     log msg   | |
105        read   ,....<.....send greeting                |       | |
106       greeting     #          |                    ___________________
107          |         #          |
108       release      #          |
109       lock    *1   #          |
110          |   /     #          |
111       send fds.....>....      |
112          |         #    \receive fds
113          |         #             |
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
121          |         #        |   |        #       |
122          |         #            |        #    kernel closes execterm
123          |         #            | ,......<....../|
124          |         #      execterm?      #       |
125          |         #            |        #     zombie
126          |         #        |   | ,......<...../
127          |         #       waitpid       #  _______________
128          |         #          |          #
129          |    ,....<....,..send status   #
130     read status    #  ________________   #
131    _____________   #
132
133
134   ********** Or, if client is killed **********
135
136          |         #          |          #  execute service
137      terminates    #    wait for read    #       |
138          |         #      (select)       #       |
139       kernel       #        |   |        #       |
140      closes call   #        |            #       |
141                 \..>......_ |            #       |
142    _____________   #       \|call?       #       |
143                    #        |            #       |
144                    #  kill whole pgrp... #    killled
145                    #        |            #     zombie
146                    #        |   | ,......<....../
147                    #       waitpid       #  _______________
148                    #          |          #
149                    #   send exit status  #
150                    #  _____SIGPIPE______ #
151
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
158     event?     condition
159     ______     process termination (after reaping, if shown)
160
161 ***************************************************************************
162 \f
163   Sequence of events and fd pluming.
164   NB INCOMPLETE - does not cover execterm, cleanup
165  
166    client (C wrapper)        connects to server
167                                (including reading ack byte)
168                              if fails or garbage
169                              === acquires lock ===
170                              makes new listening socket
171                              makes watcher pipes
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)
179  
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
186  
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
191                              close fd: lockfile
192                              possibly clean env, argv
193  
194       setup (script)         runs initialisation parts of the script
195                              at prefork establishment point:
196       setup (pm) [1]         opens syslog
197                              forks for server
198                  [2]         exits
199  
200          server (pm) [1]     [fd0: null],
201                              [fd[12]: fd2-from-outer]
202                              setsid
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
210  
211    [client (C wrapper)]      if client connect succeeds:
212                              now fd: call(client-end)
213                                 sends message with: cmdline, env
214                                 sends fds
215  
216          [server (script)]   accepts, forks subseq monitor
217  
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)
222                              sends ack byte
223                              receives args, env, fds
224                              forks executor
225  
226              executor        sorts out fds:
227                              fd0, fd1, fd2: from-outer
228                              close fds: call(server-end)
229                              retained fds: syslog
230  
231                              sets cmdline, env
232                              runs main part of script
233                              exits normally
234  
235            [monitor]         [fd[012]: null]
236                              [fd[12]: init: fd2-from-outer; subseq: null]
237                              [errors: init: fd2; subseq: syslog]
238                              reaps executor
239                              reports status via socket
240  
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)
245
246 ***************************************************************************
247 \f
248 */
249
250 #include <arpa/inet.h>
251
252 #include <uv.h>
253
254 #include "prefork.h"
255
256 const char our_name[] = "prefork-interp";
257
258 static struct sockaddr_un sockaddr_sun;
259 static FILE *call_sock;
260
261 #define ACK_BYTE '\n'
262
263 static const char *const *executor_argv;
264
265 static const char header_magic[4] = "PFI\n";
266
267 void fusagemessage(FILE *f) {
268   fprintf(f, "usage: #!/usr/bin/prefork-interp [<options>]\n");
269 }
270
271 static int laundering;
272 static int mode;
273 static int max_sockets = 100; // maximum entries in the run dir is 2x this
274
275 static struct stat initial_stab;
276
277 #define MODE_NORMAL 0
278 #define MODE_KILL   'k'
279 #define MODE_FRESH  'f'
280
281 const struct cmdinfo cmdinfos[]= {
282   PREFORK_CMDINFOS
283   { 0,         'U',   0,    .iassignto= &laundering,    .arg= 'U'         },
284   { "kill",     0,    0,    .iassignto= &mode,          .arg= MODE_KILL   },
285   { 0,         'f',   0,    .iassignto= &mode,          .arg= MODE_FRESH  },
286   { 0 }
287 };
288
289 void ident_addinit(void) {
290   char ident_magic[1] = { 0 };
291   sha256_update(&identsc, sizeof(ident_magic), ident_magic);
292 }
293
294 static void propagate_exit_status(int status, const char *what) {
295   int r;
296
297   if (WIFEXITED(status)) {
298     _exit(status);
299   }
300
301   if (WIFSIGNALED(status)) {
302     int sig = WTERMSIG(status);
303     const char *signame = strsignal(sig);
304     if (signame == 0) signame = "unknown signal";
305
306     if (! WCOREDUMP(status) &&
307         (sig == SIGINT ||
308          sig == SIGHUP ||
309          sig == SIGPIPE ||
310          sig == SIGKILL)) {
311       struct sigaction sa;
312       FILLZERO(sa);
313       sa.sa_handler = SIG_DFL;
314       r = sigaction(sig, &sa, 0);
315       if (r) diee("failed to reset signal handler while propagating %s",
316                   signame);
317
318       sigset_t sset;
319       sigemptyset(&sset);
320       sigaddset(&sset, sig);
321       r = sigprocmask(SIG_UNBLOCK, &sset, 0);
322       if (r) diee("failed to reset signal block while propagating %s",
323                   signame);
324
325       raise(sig);
326       die("unexpectedly kept running after raising (to propagate) %s",
327           signame);
328     }
329
330     die("%s failed due to signal %d %s%s", what, sig, signame,
331         WCOREDUMP(status) ? " (core dumped)" : "");
332   }
333
334   die("%s failed with weird wait status %d 0x%x", what, status, status);
335 }
336
337 typedef struct {
338   char *name_hash;
339   time_t atime;
340 } PrecleanEntry;
341
342 static int preclean_entry_compar_name(const void *av, const void *bv) {
343   const PrecleanEntry *a = av;
344   const PrecleanEntry *b = bv;
345   return strcmp(a->name_hash, b->name_hash);
346 }
347
348 static int preclean_entry_compar_atime(const void *av, const void *bv) {
349   const PrecleanEntry *ae = av;  time_t a = ae->atime;
350   const PrecleanEntry *be = bv;  time_t b = be->atime;
351   return (a > b ? +1 :
352           a < b ? -1 : 0);
353 }
354
355 static time_t preclean_stat_atime(const char *s_path) {
356   struct stat stab;
357   int r= lstat(s_path, &stab);
358   if (r) {
359     if (errno!=ENOENT) diee("pre-cleanup: stat socket (%s)", s_path);
360     return 0;
361   }
362   return stab.st_atime;
363 }
364
365 static void preclean(void) {
366   DIR *dir = opendir(run_base);
367   if (!dir) {
368     if (errno == ENOENT) return;
369     diee("pre-cleanup: open run dir (%s)", run_base);
370   }
371
372   PrecleanEntry *entries=0;
373   size_t avail_entries=0;
374   size_t used_entries=0;
375
376   struct dirent *de;
377   while ((errno = 0, de = readdir(dir))) {
378     char c0 = de->d_name[0];
379     if (!(c0 == 'l' || c0 == 's')) continue;
380     char *name_hash = m_asprintf("%s", de->d_name+1);
381     char *s_path = m_asprintf("%s/s%s", run_base, name_hash);
382     time_t atime = preclean_stat_atime(s_path);
383
384     if (avail_entries == used_entries) {
385       assert(avail_entries < INT_MAX / 4 / sizeof(PrecleanEntry));
386       avail_entries <<= 1;
387       avail_entries += 10;
388       entries = realloc(entries, avail_entries * sizeof(PrecleanEntry));
389     }
390     entries[used_entries].name_hash = name_hash;
391     entries[used_entries].atime = atime;
392     used_entries++;
393   }
394   if (errno) diee("pre-cleanup: read run dir (%s)", run_base);
395
396   // First we dedupe (after sorting by path)
397   qsort(entries, used_entries, sizeof(PrecleanEntry),
398         preclean_entry_compar_name);
399   PrecleanEntry *p, *q;
400   for (p=entries, q=entries; p < entries + used_entries; p++) {
401     if (q > entries && !strcmp(p->name_hash, (q-1)->name_hash))
402       continue;
403     *q++ = *p;
404   }
405   used_entries = q - entries;
406
407   // Now maybe delete some things
408   //
409   // Actually this has an off-by-one error since we are about
410   // to create a socket, so the actual number of sockets is one more.
411   // But, *actually*, since there might be multiple of us running at once,
412   // we might have even more than that.  This doesn't really matter.
413   if (used_entries > max_sockets) {
414     qsort(entries, used_entries, sizeof(PrecleanEntry),
415           preclean_entry_compar_atime);
416     for (p=entries; p < entries + max_sockets; p++) {
417       char *l_path = m_asprintf("%s/l%s", run_base, p->name_hash);
418       char *s_path = m_asprintf("%s/s%s", run_base, p->name_hash);
419       int lock_fd = flock_file(l_path);
420       // Recheck atime - we might have raced!
421       time_t atime = preclean_stat_atime(s_path);
422       if (atime != p->atime) {
423         // Raced.  This will leave use deleting too few things.  Whatever.
424       } else {
425         int r= unlink(s_path);
426         if (r && errno!=ENOENT) diee("preclean: delete stale (%s)", s_path);
427         r= unlink(l_path);
428         if (r) diee("preclean: delete stale lock (%s)", s_path);
429         // NB we don't hold the lock any more now.
430       }
431       close(lock_fd);
432       free(l_path);
433       free(s_path);
434     }
435   }
436
437   for (p=entries; p < entries + used_entries; p++)
438     free(p->name_hash);
439   free(entries);
440 }
441
442 static __attribute((noreturn)) void die_data_overflow(void) {
443   die("cannot handle data with length >2^32");
444 }
445
446 static void prepare_data(size_t *len, char **buf,
447                          const void *data, size_t dl) {
448   if (len) {
449     if (dl >= SIZE_MAX - *len)
450       die_data_overflow();
451     *len += dl;
452   }
453   if (buf) {
454     memcpy(*buf, data, dl);
455     *buf += dl;
456   }
457 }
458
459 static void prepare_length(size_t *len, char **buf, size_t dl_sz) {
460   if (dl_sz > UINT32_MAX) die_data_overflow();
461   uint32_t dl = htonl(dl_sz);
462   prepare_data(len, buf, &dl, sizeof(dl));
463 }
464
465 static void prepare_string(size_t *len, char **buf, const char *s) {
466   size_t sl = strlen(s);
467   prepare_data(len, buf, s, sl+1);
468 }
469
470 static void prepare_message(size_t *len, char **buf) {
471   const char *s;
472
473   const char *const *p = (void*)environ;
474   while ((s = *p++)) {
475     if (strchr(s, '='))
476       prepare_string(len, buf, s);
477   }
478
479   prepare_string(len, buf, "");
480
481   p = executor_argv;
482   while ((s = *p++))
483     prepare_string(len, buf, s);
484 }
485
486 static void send_fd(int payload_fd) {
487   int via_fd = fileno(call_sock);
488
489   union {
490     struct cmsghdr align;
491     char buf[CMSG_SPACE(sizeof(payload_fd))];
492   } cmsg_buf;
493
494   struct msghdr msg;
495   FILLZERO(msg);
496   FILLZERO(cmsg_buf);
497
498   char dummy_byte = 0;
499   struct iovec iov;
500   FILLZERO(iov);
501   iov.iov_base = &dummy_byte;
502   iov.iov_len = 1;
503
504   msg.msg_name = 0;
505   msg.msg_iov = &iov;
506   msg.msg_iovlen = 1;
507   msg.msg_control = cmsg_buf.buf;
508   msg.msg_controllen = sizeof(cmsg_buf.buf);
509
510   struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
511   cmsg->cmsg_level = SOL_SOCKET;
512   cmsg->cmsg_type = SCM_RIGHTS;
513   cmsg->cmsg_len = CMSG_LEN(sizeof(payload_fd));
514   *(int*)CMSG_DATA(cmsg) = payload_fd;
515
516   msg.msg_controllen = sizeof(cmsg_buf.buf);
517
518   for (;;) {
519     ssize_t r = sendmsg(via_fd, &msg, 0);
520     if (r == -1) {
521       if (errno == EINTR) continue;
522       diee("send fd");
523     }
524     assert(r == 1);
525     break;
526   }
527 }
528
529 static void send_request(void) {
530   // Sending these first makes it easier for the script to
531   // use buffered IO for the message.
532   send_fd(0);
533   send_fd(1);
534   send_fd(2);
535
536   size_t len = 0;
537   prepare_message(&len, 0);
538
539   size_t tlen = len + 4;
540   char *m = xmalloc(tlen);
541   char *p = m;
542   prepare_length(0, &p, len);
543   prepare_message(0, &p);
544   assert(p == m + tlen);
545
546   ssize_t sr = fwrite(m, tlen, 1, call_sock);
547   if (sr != 1) diee("write request (buffer)");
548
549   if (fflush(call_sock)) diee("write request");
550 }
551
552 static FILE *call_sock_from_fd(int fd) {
553   int r;
554
555   FILE *call_sock = fdopen(fd, "r+");
556   if (!call_sock) diee("fdopen socket");
557
558   r = setvbuf(call_sock, 0, _IONBF, 0);
559   if (r) die("setvbuf socket");
560
561   return call_sock;
562 }
563
564 static bool was_eof(FILE *call_sock) {
565   return feof(call_sock) || errno==ECONNRESET;
566 }
567
568 // Returns -1 on EOF
569 static int protocol_read_maybe(void *data, size_t sz) {
570   if (!sz) return 0;
571   size_t sr = fread(data, sz, 1, call_sock);
572   if (sr != 1) {
573     if (was_eof(call_sock)) return -1;
574     diee("read() on monitor call socket (%zd)", sz);
575   }
576   return 0;
577 }
578
579 static void protocol_read(void *data, size_t sz) {
580   if (protocol_read_maybe(data, sz) < 0)
581     die("monitor process quit unexpectedly");
582 }
583
584 // Returns 0 if OK, error msg if peer was garbage.
585 static const char *read_greeting(void) {
586   char got_magic[sizeof(header_magic)];
587
588   if (protocol_read_maybe(&got_magic, sizeof(got_magic)) < 0)
589     return "initial monitor process quit";
590
591   if (memcmp(got_magic, header_magic, sizeof(header_magic)))
592     die("got unexpected protocol magic 0x%02x%02x%02x%02x",
593         got_magic[0], got_magic[1], got_magic[2], got_magic[3]);
594
595   uint32_t xdata_len;
596   protocol_read(&xdata_len, sizeof(xdata_len));
597   void *xdata = xmalloc(xdata_len);
598   protocol_read(xdata, xdata_len);
599
600   return 0;
601 }
602
603 // Returns: call(client-end), or 0 to mean "is garbage"
604 // find_socket_path must have been called
605 static FILE *connect_existing(void) {
606   int r;
607   int fd = -1;
608
609   if (mode != MODE_NORMAL) return 0;
610
611   fd = socket(AF_UNIX, SOCK_STREAM, 0);
612   if (fd==-1) diee("socket() for client");
613
614   socklen_t salen = sizeof(sockaddr_sun);
615   r = connect(fd, (const struct sockaddr*)&sockaddr_sun, salen);
616   if (r==-1) {
617     if (errno==ECONNREFUSED || errno==ENOENT) goto x_garbage;
618     diee("connect() %s", socket_path);
619   }
620
621   call_sock = call_sock_from_fd(fd);
622   fd = -1;
623
624   if (read_greeting())
625     goto x_garbage;
626
627   return call_sock;
628
629  x_garbage:
630   if (call_sock) { fclose(call_sock); call_sock=0; }
631   if (fd >= 0) close(fd);
632   return 0;
633 }
634
635 static void watcher_cb_stdin(uv_poll_t *handle, int status, int events) {
636   char c;
637   int r;
638
639   if ((errno = -status)) diee("watcher: poll stdin");
640   for (;;) {
641     r= read(0, &c, 1);
642     if (r!=-1) _exit(0);
643     if (!(errno==EINTR || errno==EWOULDBLOCK || errno==EAGAIN))
644       diee("watcher: read sentinel stdin");
645   }
646 }
647
648 static void watcher_cb_sockpath(uv_fs_event_t *handle, const char *filename,
649                                 int events, int status) {
650   int r;
651   struct stat now_stab;
652
653   if ((errno = -status)) diee("watcher: poll stdin");
654   for (;;) {
655     r= stat(socket_path, &now_stab);
656     if (r==-1) {
657       if (errno==ENOENT) _exit(0);
658       if (errno==EINTR) continue;
659       diee("stat socket: %s", socket_path);
660     }
661     if (!stabs_same_inode(&now_stab, &initial_stab))
662       _exit(0);
663   }
664 }
665
666 // On entry, stderr is still inherited, but 0 and 1 are the pipes
667 static __attribute__((noreturn))
668 void become_watcher(void) {
669   uv_loop_t loop;
670   uv_poll_t uvhandle_stdin;
671   uv_fs_event_t uvhandle_sockpath;
672   int r;
673
674   nonblock(0);
675
676   errno= -uv_loop_init(&loop);
677   if (errno) diee("watcher: uv_loop_init");
678
679   errno= -uv_poll_init(&loop, &uvhandle_stdin, 0);
680   if (errno) diee("watcher: uv_poll_init");
681   errno= -uv_poll_start(&uvhandle_stdin,
682                         UV_READABLE | UV_WRITABLE | UV_DISCONNECT,
683                         watcher_cb_stdin);
684   if (errno) diee("watcher: uv_poll_start");
685
686   errno= -uv_fs_event_init(&loop, &uvhandle_sockpath);
687   if (errno) diee("watcher: uv_fs_event_init");
688
689   errno= -uv_fs_event_start(&uvhandle_sockpath, watcher_cb_sockpath,
690                             socket_path, 0);
691   if (errno) diee("watcher: uv_fs_event_start");
692
693   // OK everything is set up, let us daemonise
694   if (dup2(1,2) != 2) diee("watcher: set daemonised stderr");
695   r= setvbuf(stderr, 0, _IOLBF, BUFSIZ);
696   if (r) diee("watcher: setvbuf stderr");
697
698   pid_t child = fork();
699   if (child == (pid_t)-1) diee("watcher: fork");
700   if (child) _exit(0);
701
702   if (setsid() == (pid_t)-1) diee("watcher: setsid");
703
704   r= uv_run(&loop, UV_RUN_DEFAULT);
705   die("uv_run returned (%d)", r);
706 }
707
708 static __attribute__((noreturn))
709 void become_setup(int sfd, int lockfd, int fake_pair[2],
710                   int watcher_stdin, int watcher_stderr) {
711   close(lockfd);
712   close(fake_pair[0]);
713   int call_fd = fake_pair[1];
714
715   int null_0 = open("/dev/null", O_RDONLY);  if (null_0 < 0) diee("open null");
716   if (dup2(null_0, 0)) diee("dup2 /dev/null onto stdin");
717   close(null_0);
718   if (dup2(2, 1) != 1) die("dup2 stderr onto stdout");
719
720   nonblock(sfd);
721
722   // Extension could work like this:
723   //
724   // We advertise a new protocol (perhaps one which is nearly entirely
725   // different after the connect) by putting a name for it comma-separated
726   // next to "v1".  Simple extension can be done by having the script
727   // side say something about it in the ack xdata, which we currently ignore.
728   putenv(m_asprintf("PREFORK_INTERP=v1 %d,%d,%d,%d",
729                     sfd, call_fd, watcher_stdin, watcher_stderr));
730
731   execvp(executor_argv[0], (char**)executor_argv);
732   diee("execute %s", executor_argv[0]);
733 }
734
735 static void connect_or_spawn(void) {
736   int r;
737
738   call_sock = connect_existing();
739   if (call_sock) return;
740
741   // We're going to make a new one, so clean out old ones
742   preclean();
743
744   int lockfd = acquire_lock();
745
746   if (mode == MODE_KILL) {
747     r= unlink(socket_path);
748     if (r && errno != ENOENT) diee("remove socket %s", socket_path);
749
750     r= unlink(lock_path);
751     if (r) diee("rmeove lock %s", lock_path);
752     _exit(0);
753   }
754
755   call_sock = connect_existing();
756   if (call_sock) { close(lockfd); return; }
757
758   // We must start a fresh one, and we hold the lock
759
760   r = unlink(socket_path);
761   if (r<0 && errno!=ENOENT)
762     diee("failed to remove stale socket %s", socket_path);
763
764   int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
765   if (sfd<0) diee("socket() for new listener");
766
767   socklen_t salen = sizeof(sockaddr_sun);
768   r= bind(sfd, (const struct sockaddr*)&sockaddr_sun, salen);
769   if (r<0) diee("bind() on new listener");
770
771   r= stat(socket_path, &initial_stab);
772   if (r<0) diee("stat() fresh socket");
773
774   // We never want callers to get ECONNREFUSED.  But:
775   // There is a race here: from my RTFM they may get ECONNREFUSED
776   // if they try between our bind() and listen().  But if they do, they'll
777   // acquire the lock (serialising with us) and retry, and then it will work.
778   r = listen(sfd, INT_MAX);
779   if (r<0) diee("listen() for new listener");
780
781   // Fork watcher
782
783   int watcher_stdin[2];
784   int watcher_stderr[2];
785   if (pipe(watcher_stdin) || pipe(watcher_stderr))
786     diee("pipe() for socket inode watcher");
787
788   pid_t watcher = fork();
789   if (watcher == (pid_t)-1) diee("fork for watcher");
790   if (!watcher) {
791     close(sfd);
792     close(lockfd);
793     close(watcher_stdin[1]);
794     close(watcher_stderr[0]);
795     if (dup2(watcher_stdin[0], 0) != 0 ||
796         dup2(watcher_stderr[1], 1) != 1)
797       diee("initial dup2() for watcher");
798     close(watcher_stdin[0]);
799     close(watcher_stderr[1]);
800     become_watcher();
801   }
802
803   close(watcher_stdin[0]);
804   close(watcher_stderr[1]);
805   nonblock(watcher_stderr[0]);
806
807   // Fork setup
808
809   int fake_pair[2];
810   r = socketpair(AF_UNIX, SOCK_STREAM, 0, fake_pair);
811   if (r<0) diee("socketpair() for fake initial connection");
812
813   pid_t setup_pid = fork();
814   if (setup_pid == (pid_t)-1) diee("fork for spawn setup");
815   if (!setup_pid) become_setup(sfd, lockfd, fake_pair,
816                                watcher_stdin[1], watcher_stderr[0]);
817   close(fake_pair[1]);
818   close(sfd);
819
820   call_sock = call_sock_from_fd(fake_pair[0]);
821
822   int status;
823   pid_t got = waitpid(setup_pid, &status, 0);
824   if (got == (pid_t)-1) diee("waitpid setup [%ld]", (long)setup_pid);
825   if (got != setup_pid) diee("waitpid setup [%ld] gave [%ld]!",
826                              (long)setup_pid, (long)got);
827   if (status != 0) propagate_exit_status(status, "setup");
828
829   const char *emsg = read_greeting();
830   if (emsg) die("setup failed: %s", emsg);
831
832   close(lockfd);
833   return;
834 }
835
836 static void make_executor_argv(const char *const *argv) {
837   switch (laundering) {
838   case 'U': break;
839   default: die("need -U (specifying unlaundered argument handling)");
840   }
841
842   const char *arg;
843   #define EACH_NEW_ARG(EACH) {                  \
844     arg = interp; { EACH }                      \
845     if ((arg = script)) { EACH }                \
846     const char *const *walk = argv;             \
847     while ((arg = *walk++)) { EACH }            \
848   }
849
850   size_t count = 1;
851   EACH_NEW_ARG( (void)arg; count++; );
852
853   const char **out = calloc(count, sizeof(char*));
854   executor_argv = (const char* const*)out;
855   if (!executor_argv) diee("allocate for arguments");
856
857   EACH_NEW_ARG( *out++ = arg; );
858   *out++ = 0;
859 }
860
861 int main(int argc_unused, const char *const *argv) {
862   process_opts(&argv);
863
864   // Now we have
865   //  - possibly interp
866   //  - possibly script
867   //  - remaining args
868   // which ought to be passed on to the actual executor.
869   make_executor_argv(argv);
870
871   find_socket_path();
872   FILLZERO(sockaddr_sun);
873   sockaddr_sun.sun_family = AF_UNIX;
874   assert(strlen(socket_path) <= sizeof(sockaddr_sun.sun_path));
875   strncpy(sockaddr_sun.sun_path, socket_path, sizeof(sockaddr_sun.sun_path));
876
877   connect_or_spawn();
878
879   // We're committed now, send the request (or bail out)
880   send_request();
881
882   uint32_t status;
883   protocol_read(&status, sizeof(status));
884
885   status = ntohl(status);
886   if (status > INT_MAX) die("status 0x%lx does not fit in an int",
887                             (unsigned long)status);
888
889   propagate_exit_status(status, "invocation");
890 }