2 * "Interpreter" that you can put in #! like this
3 * #!/usr/bin/prefork-interp [<options>] <interpreter>
8 * client (C wrapper) connects to server
9 * (including reading ack byte)
11 * === acquire lock ===
12 * makes new listening socket
13 * makes first-instance socketpair
14 * forks setup (script, sock fds indicated in env)
15 * fd0, fd1, fd2: from-outer-caller
16 * other fd: call(client-end)(fake)
17 * reaps setup (and reports error)
18 * (implicitly releases lock)
20 * setup (pre-exec) fd0: null,
21 * fd[12: fd2-from-outer-caller
22 * env fds: orig-fd[01], listener,
23 * env fds: call(server-end)(fake)
26 * setup (script) runs initialisation parts of the script
27 * at prefork establishment point:
29 * forks for server, now becomes like monitor below
32 * server (script) fd0: null, fd[12]: syslog
33 * other fds: orig-fd[01], listener,
34 * other fds: call(server-end)(fake)
36 * right away, forks one fake-accepted monitor:
37 * f-a monitor [fd0: null, fd[12]: syslgo]
38 * other fds: call(server-end)(fake)
39 * runs as monitor, below
41 * [server (script)] fd0: null, fd[12]: syslog
43 * closes fds: orig-fd[01], call(server-end)fake)
44 * runs in loop accepting and forking,
45 * reaping and limiting children
46 * reports failures of monitors to syslog
48 * [client (C wrapper)] if client connect succeeds:
49 * now fd: call(client-end)
50 * sends message with: cmdline, env
53 * [server (script)] accepts, forks monitor
55 * monitor [fd0: null, fd[12]: syslgo]
56 * other fds: call(server-end)
58 * receives args, env, fds
61 * executor sorts out fds:
62 * fd0, fd1, fd2: from-outer-caller
63 * close fds: call(server-end)
64 * implicitly closed fds: syslog
67 * runs main part of script
70 * [monitor] [fd0: null, fd[12]: syslgo]
71 * [other fds: call(server-end)]
73 * reports status via socket
75 * [client (C wrapper)] [fd0, fd1, fd2: from-outer-caller]
76 * [other fd: call(client-end)]
77 * receives status, exits appropriately
78 * (if was bad signal, reports to stderr, exits 127)
81 struct sockaddr_un sun;
85 // Returns: call(client-end) fd, or -1 to mean "is garbage"
86 // find_socket_path must have been called
87 static int attempt_connect_existing(void) {
92 sun.sun_family = AF_UNIX;
93 assert(strlen(socket_path) <= sizeof(sun.sun_path));
94 strncpy(sun.sun_path, socket_path, sizeof(sun.sun_path));
96 bool isgarbage = check_garbage();
97 if (isgarbage) goto x_garbage;
99 fd = socket(AF_UNIX, SOCK_STREAM, 0);
100 if (fd==-1) diee("socket() for client");
102 salen_t salen = sizeof(sun);
103 r = connect(client, (const struct sockaddr*)sun, salen);
105 if (errno==ECONNREFUSED || errno==ENOENT) goto x_garbgae;
106 diee("connect() %s", socket_path);
111 sr = read(fd, &ack, 1);
113 if (errno==ECONNRESET) goto x_garbage;
114 if (errno==EINTR) continue;
115 diee("read() ack byte");
117 if (sr == 0) { goto x_garbage; }
118 if (ack != '\n') die("got ack byte 0x%02x, not '\n'", ack);
123 if (fd >= 0) close(fd);
127 static int connect_or_spawn(void) {
128 int fd = connect_existing();
129 if (fd >= 0) return fd;
132 fd = connect_existing();
134 int main(int argc, const char *const *argv) {
135 script = process_opts(argc, argv);
138 int fd = connect_or_spawn();