* prefork-interp [<option>,..],<interpreter> <script> [<args> ...]
* prefork-interp '[<option> ..] <interpreter>' <script> [<args> ...]
*
- * Options must specify argument laundering mode.
- * Currently the only mode supported is:
+ * Options must specify argument mediation approach.
+ * Currently the only argument mediation supported is:
+ *
* -U unlaundered: setup and executor both get all arguments and env vars
* ident covers only env vars specified with -E
* ident covers only arguments interpreter and (if present) script
+ *
+ * Options for setting the operation mode:
+ *
+ * (none) Default: start new server if needed, then run service
+ * -f Force a fresh service (old one is terminated)
+ * --kill Kill any existing service; do not actually run anything
+ *
+ * Options for controlling whether different invocations share a server:
+ *
+ * -E VAR ident includes env var VAR (or its absence)
+ * -G STRING ident includes string STRING
+ * -g IDENT use IDENT rather than hex(SHA256(... identity things ...))
+ *
+ * (Ordering of -E and -G options is relevant; invocations with different
+ * -E -G options are different even if the env var settings are the same)
*/
/*
2. Env var PREFORK_INTERP contains:
- v1,SECS.NSECS[,...] LISTEN,CALL,WATCHE,WATCHI[,...][ ...]
+ 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:
+ To parse it: treat as bytes and split on ASCII space, taking
+ the first two words. (There may or may not be
+ further "words"; and if there are they might be binary data.)
+ Then split each of the first two words (which will contain only
+ ASCII printing characters) on comma. Take the first two items:
v1 Protocol version indicator - literal. If something else,
- fail (installation is incompatible somehow).
+ fail (means 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.
+ timestamp just before script started running, as a
+ decimal time_t. NSECS is exactly 9 digits.
+ To be used for auto reloading (see below).
- These items are file descriptors:
+ The 2nd word's items are file descriptors:
LISTEN listening socket nonblocking
CALL call socket for initial call blocking
WATCHE liveness watcher stderr nonblocking
WATCHI liveness sentinel unspecified
+ (any further descriptors should be ignored, not closed)
+
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.
+ (This allows simple synchronous operation.)
2. Open syslog
3. fork/exit (fork and have parent exit) (to make server)
and make a note to send all error messages to syslog
7. Enter select loop, looking for the following:
- * accept on LISTEN:
+ A. 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
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)
+ Report child exit status if not zero or SIGPIPE.
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
+ B. WATCHE is readable:
+ * EOF: log at LOG_INFO, and exit
+ * data to read: read what is available immediately;
+ it will be an error message: log it at LOG_ERR, and exit
4. service (monitor) child does the following:
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)
+ 15. write the wait status, in 32-bit big-endian, to CAL
16. exit 0
+ Errors detected in the service monitor should be sent to
+ syslog, or stderr, depending on whether this is the initial
+ service monitor (from part 3 step 5) or an accepted socket
+ service monitor (from part 4 step 9); this can be achieved
+ easily by having a global flag (set at part 3 step 6),
+ or perhaps using logger(8) and redirecting stderr (but
+ then be careful to ensure everyone gets only the necessary fds).
+
+ EOF on CALL, or EPIPE/SIGPIPE writing to it, are not errors.
+ In this case, exit zero or die with SIGPIPE, so parent
+ won't report error either (part 3 step 7(A)(ii)).
+
***************************************************************************
\f
*/
#include <arpa/inet.h>
+#include <sys/utsname.h>
#include <uv.h>
{ 0 }
};
+static void ident_add_stat(const char *path) {
+ struct stat stab;
+ int r = stat(path, &stab);
+ if (r) diee("failed to stat %s", path);
+
+ IDENT_ADD_OBJ(stab.st_dev);
+ IDENT_ADD_OBJ(stab.st_ino);
+}
+
void ident_addinit(void) {
- char ident_magic[1] = { 0 };
- sha256_update(&identsc, sizeof(ident_magic), ident_magic);
+ char magic = 1;
+
+ IDENT_ADD_OBJ(magic);
+
+ struct utsname uts = { };
+ size_t utslen = sizeof(uts);
+ int r = uname(&uts);
+ if (r) diee("uname failed!");
+ IDENT_ADD_OBJ(utslen);
+ IDENT_ADD_OBJ(uts);
+
+ ident_add_stat(".");
+ ident_add_stat("/");
}
static void propagate_exit_status(int status, const char *what) {