/* common stuff for cgi-fcgi-interp and prefork-interp */
+/*
+ * Copyright 2016-2022 Ian Jackson and contributors to chiark-utils
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ * There is NO WARRANTY.
+ */
#include "prefork.h"
-const char *interp, *ident;
-int numservers=4, debugmode;
-int check_interval=300;
-
+const char *interp, *ident, *script, *socket_path, *lock_path;
+bool logging;
struct sha256_ctx identsc;
+const char *run_base;
-uid_t us;
-const char *run_base, *script, *socket_path;
-const char *run_base_mkdir_p;
-bool logging;
+static uid_t us;
+static const char *run_base_mkdir_p;
void common_diee(const char *m) { diee("%s", m); }
void common_die (const char *m) { die ("%s", m); }
if (logging) {
const char *fmt_use = fmt;
char *fmt_free = 0;
- if (errnoval) {
+ if (errnoval!=-1) {
r = asprintf(&fmt_free, "%s: %%m", fmt);
if (r) {
fmt_free = 0;
vsyslog(LOG_ERR, fmt_use, al);
free(fmt_free);
} else {
- fputs("cgi-fcgi-interp: ",stderr);
+ fprintf(stderr, "%s: ", our_name);
vfprintf(stderr,fmt,al);
if (errnoval!=-1) fprintf(stderr,": %s",strerror(errnoval));
fputc('\n',stderr);
*ci->iassignto = v;
}
-void ident_addstring(const struct cmdinfo *ci, const char *string) {
- /* ci may be 0 and is provided so this can be .call */
+void ident_add_key_byte(char key) {
+ sha256_update(&identsc,1,&key);
+}
+
+void ident_addstring(char key, const char *string) {
+ ident_add_key_byte(key);
sha256_update(&identsc,strlen(string)+1,string);
}
+void off_ident_addstring(const struct cmdinfo *ci, const char *string) {
+ ident_addstring('G', string);
+}
+
void off_ident_addenv(const struct cmdinfo *ci, const char *name) {
+ ident_addstring('E', name);
const char *val = getenv(name);
if (val) {
- sha256_update(&identsc,strlen(name),name); /* no nul */
- sha256_update(&identsc,1,"=");
- ident_addstring(0,val);
+ ident_addstring('v', val);
} else {
- ident_addstring(0,name);
+ ident_add_key_byte(0);
}
}
+bool stabs_same_inode(struct stat *a, struct stat *b) {
+ return (a->st_dev == b->st_dev &&
+ a->st_ino == b->st_ino);
+}
+
bool find_run_base_var_run(void) {
struct stat stab;
char *try;
warning("%s writeable by group or other, falling back to ~\n", try);
return 0;
}
- run_base = m_asprintf("%s/%s", try, "cgi-fcgi-interp");
+ run_base = m_asprintf("%s/%s", try, our_name);
return 1;
}
if (sizeof(ut.nodename) > 32)
ut.nodename[32] = 0;
- run_base_mkdir_p = m_asprintf("%s/%s", pw->pw_dir, ".cgi-fcgi-interp");
+ run_base_mkdir_p = m_asprintf("%s/.%s", pw->pw_dir, our_name);
try = m_asprintf("%s/%s", run_base_mkdir_p, ut.nodename);
run_base = try;
return 1;
unsigned char bbuf[32];
int i;
- ident_addstring(0,interp);
- ident_addstring(0,script);
+ ident_addstring('i', interp);
+ if (script)
+ ident_addstring('s', script);
sha256_digest(&identsc,sizeof(bbuf),bbuf);
for (i=0; i<identlen; i += 2)
socket_path = m_asprintf("%s/s%s",run_base,ident);
}
-#ifdef st_mtime
-
-bool stab_isnewer(const struct stat *a, const struct stat *b) {
- if (debugmode)
- fprintf(stderr,"stab_isnewer mtim %lu.%06lu %lu.06%lu\n",
- (unsigned long)a->st_mtim.tv_sec,
- (unsigned long)a->st_mtim.tv_nsec,
- (unsigned long)b->st_mtim.tv_sec,
- (unsigned long)b->st_mtim.tv_nsec);
- return timespeccmp(&a->st_mtim, &b->st_mtim, >);
-}
-
-void stab_mtimenow(struct stat *out) {
- int r = clock_gettime(CLOCK_REALTIME, &out->st_mtim);
- if (r) diee("(stage2) clock_gettime");
- if (debugmode)
- fprintf(stderr,"stab_mtimenow mtim %lu.%06lu\n",
- (unsigned long)out->st_mtim.tv_sec,
- (unsigned long)out->st_mtim.tv_nsec);
-}
-
-#else /* !defined(st_mtime) */
-
-bool stab_isnewer(const struct stat *a, const struct stat *b) {
- if (debugmode)
- fprintf(stderr,"stab_isnewer mtime %lu %lu\n",
- (unsigned long)a->st_mtime,
- (unsigned long)b->st_mtime);
- return a->st_mtime > b->st_mtime;
-}
-
-void stab_mtimenow(struct stat *out) {
- out->st_mtime = time(NULL);
- if (out->st_mtime == (time_t)-1) diee("(stage2) time()");
- if (debugmode)
- fprintf(stderr,"stab_mtimenow mtime %lu\n",
- (unsigned long)out->st_mtime);
-}
-
-#endif /* !defined(st_mtime) */
-
-bool check_garbage_vs(const struct stat *started) {
- struct stat script_stab;
+// Returns fd
+int flock_file(const char *lock_path) {
int r;
+ int lockfd = -1;
+ struct stat stab_fd;
+ struct stat stab_path;
- r = lstat(script, &script_stab);
- if (r) diee("lstat script (%s)",script);
-
- if (stab_isnewer(&script_stab, started))
- return 1;
+ for (;;) {
+ if (lockfd >= 0) { close(lockfd); lockfd = -1; }
- if (S_ISLNK(script_stab.st_mode)) {
- r = stat(script, &script_stab);
- if (r) diee("stat script (%s0",script);
+ lockfd = open(lock_path, O_CREAT|O_RDWR, 0600);
+ if (lockfd<0) diee("create lock (%s)", lock_path);
- if (stab_isnewer(&script_stab, started))
- return 1;
- }
-
- return 0;
-}
+ r = flock(lockfd, LOCK_EX);
+ if (r && errno == EINTR) continue;
+ if (r) diee("lock lock (%s)", lock_path);
-bool check_garbage(void) {
- struct stat sock_stab;
- int r;
+ r = fstat(lockfd, &stab_fd);
+ if (r) diee("fstat locked lock");
- r = lstat(socket_path, &sock_stab);
- if (r) {
- if ((errno == ENOENT))
- return 0; /* well, no garbage then */
- diee("stat socket (%s)",socket_path);
+ r = stat(lock_path, &stab_path);
+ if (!r) {
+ if (stabs_same_inode(&stab_path, &stab_fd)) break;
+ } else {
+ if (!(errno == ENOENT)) diee("re-stat locked lock (%s)", lock_path);
+ }
}
- return check_garbage_vs(&sock_stab);
+ return lockfd;
}
-void tidy_garbage(void) {
- /* We lock l<ident> and re-check. The effect of this is that each
- * stale socket is removed only once. So unless multiple updates to
- * the script happen rapidly, we can't be racing with the cgi-fcgi
- * (which is recreating the socket */
- int lockfd = -1;
- int r;
-
- const char *lock_path = m_asprintf("%s/l%s",run_base,ident);
-
- lockfd = open(lock_path, O_CREAT|O_RDWR, 0600);
- if (lockfd<0) diee("create lock (%s)", lock_path);
-
- r = flock(lockfd, LOCK_EX);
- if (r) diee("lock lock (%s)", lock_path);
-
- if (check_garbage()) {
- r = unlink(socket_path);
- if (r) {
- if (!(errno == ENOENT))
- diee("remove out-of-date socket (%s)", socket_path);
- }
- }
-
- r = close(lockfd);
- if (r) diee("close lock (%s)", lock_path);
+// Returns fd
+int acquire_lock(void) {
+ lock_path = m_asprintf("%s/l%s",run_base,ident);
+ return flock_file(lock_path);
}
static void shbang_opts(const char *const **argv_io,
if (!interp) badusage("need interpreter argument");
}
-const char *process_opts(int argc, const char *const *argv) {
+void process_opts(const char *const **argv_io) {
const char *smashedopt;
sha256_init(&identsc);
+ ident_addinit();
- if (argc>=2 &&
- (smashedopt = argv[1]) &&
+ if ((*argv_io)[0] &&
+ (smashedopt = (*argv_io)[1]) &&
smashedopt[0]=='-' &&
(strchr(smashedopt,' ') || strchr(smashedopt,','))) {
/* single argument containg all the options and <interp> */
- argv += 2; /* eat argv[0] and smashedopt */
+ *argv_io += 2; /* eat argv[0] and smashedopt */
const char *split_args[MAX_OPTS+1];
int split_argc = 0;
- split_args[split_argc++] = argv[0];
+ split_args[split_argc++] = (*argv_io)[0];
for (;;) {
if (split_argc >= MAX_OPTS) die("too many options in combined arg");
split_args[split_argc++] = smashedopt;
shbang_opts(&split_argv, cmdinfos);
/* sets interp */
- if (!split_argv) badusage("combined arg too many non-option arguments");
+
+ if (!**argv_io)
+ badusage("no script argument (expected after combined #! options)");
} else {
- shbang_opts(&argv, cmdinfos);
+ shbang_opts(argv_io, cmdinfos);
}
- script = *argv++;
- if (!script) badusage("need script argument");
-
- return script;
+ if (**argv_io)
+ script = *(*argv_io)++;
}