From: Ian Jackson Date: Sun, 21 Aug 2022 12:42:11 +0000 (+0100) Subject: prefork-interp: protocol documentation X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=5d37fa16ab6a024c1bdc343492ae0d37543f5639;p=chiark-utils.git prefork-interp: protocol documentation Signed-off-by: Ian Jackson --- diff --git a/cprogs/prefork-interp.c b/cprogs/prefork-interp.c index 62e5ff0..8987dcf 100644 --- a/cprogs/prefork-interp.c +++ b/cprogs/prefork-interp.c @@ -81,7 +81,7 @@ | # | | # script initialisation | # | application - | # ########|############################################# + | ###########|############################################# | # | prefork-interp | # identify fds from envirnment (Perl) | # open syslog @@ -243,6 +243,107 @@ receives status, exits appropriately (if was bad signal, reports to stderr, exits 127) +*************************************************************************** + + Protocol, and functions of the script + + 1. Script interpreter will be spawned apparently as normal; + should run synchronously in the normal way until + "initialisation complete" point. At initialisation complete: + + 2. Env var PREFORK_INTERP contains: + + v1,SECS.NSECS[,...] LISTEN,CALL,WATCHE,WATCHI[,...][ ...] + + To parse it: split on ASCII space (or any whitespace), taking + first two words. There may or may not be further "words". + Then split each of the first two words on comma, + again taking the initial items as specified. The items are: + + v1 Protocol version indicator - literal. If something else, + fail (installation is incompatible somehow). + + SECS.NSECS + timestamp before script started running, as a decimal + time_t. NSECS is exactly 9 digits. + To be used for auto reloading. + + These items are file descriptors: + + LISTEN listening socket nonblocking + CALL call socket for initial call blocking + WATCHE liveness watcher stderr nonblocking + WATCHI liveness sentinel unspecified + + 3. Library should do the following: + + 1. Read and understand the PREFORK_INTERP env var. + If it is not set, initialisation complete should simply return. + This allows simple synchronous operation. + + 2. Open syslog + 3. fork/exit (fork and have parent exit) (to make server) + 4. setsid (to become session leader) + 5. fork initial service (monitor) child, using CALL (see below) + 6. Replace stdin/stdout/stderr with /dev/null, + and make a note to send all error messages to syslog + 7. Enter select loop, looking for the following: + + * accept on LISTEN: + i. see if we need to reload: is any file forming part + of the program newer than the SECS.NSECS ? + If so, log at LOG_INFO, and exit immediately + (dropping CALL, LISTEN, WATCHI, etc.) + ii. see if we can reap any children, possibly waiting + for children if we are at our concurrency limit + (limit should be configured through library, default 4) + iii. fork service (monitor) child, using accepted fd + + * WATCHE is readable: + * EOF:: log at LOG_INFO, and exit + * data to read: read what is available immediately, + log it as a message at LOG_ERR, and exit + + 4. service (monitor) child does the following: + + 1. close all of LISTEN, WATCHI, WATCHE + 2. setpgrp + 3. send a greeting (on CALL) "PFI\n\0\0\0\0" (8 bytes) + 4. read a single byte, fail if it's not zero + 5. three times, receive a single byte with a file descriptor + attached as ancillary data. (These descriptors will be + service stdin, stdout, stderr.) + 6. read a 4-byte big-endian length + 7. read that many bytes, the initial service request message, + which contains the following nul-terminated strings: + * environment variable settings in the format NAME=value + * an empty string + * arguments NOT INCLUDING argv[0] or script filename + (not that this means the service request must end in a nul) + 8. make a new pipe EXECTERM + 9. fork for the service executor; in the child + i. redirect stdin/stdout/stderr to the recevied fds + ii. replace environment and arguments with those received, + iii. close descriptors: close the original received descriptors; + close CALL; keep only the writing end of EXECTERM + iv. if the script programming language does things with SIGINT, + set it set back to default handling (immediate termination). + v. return back to script, now in the grandchild + + 10. in the parent, close EXECTERM writing end, and + 11. select, looking for one of the following: + * CALL is readable + * EXECTERM reading end is readable + No need to actually read, since these shouldn't produce + spurious wakeups (but do loop on EINTR). + 12. set SIGINT to ignored + 13. send SIGINT to the entire process group + 14. wait, blocking, for the executor child + 15. write the wait status, in 32-bit big-endian, to CALL + (this may generate SIGPIPE/EPIPE; + if so, die with SIGPIPE or exit 0; do treat that as failure) + 16. exit 0 + *************************************************************************** */