1 /* common stuff for cgi-fcgi-interp and prefork-interp */
5 const char *interp, *ident;
6 int numservers=4, debugmode;
7 int check_interval=300;
9 struct sha256_ctx identsc;
12 const char *run_base, *script, *socket_path, *lock_path;
13 const char *run_base_mkdir_p;
16 void common_diee(const char *m) { diee("%s", m); }
17 void common_die (const char *m) { die ("%s", m); }
19 void vmsgcore(int estatus, int errnoval, const char *fmt, va_list al) {
23 const char *fmt_use = fmt;
26 r = asprintf(&fmt_free, "%s: %%m", fmt);
33 vsyslog(LOG_ERR, fmt_use, al);
36 fprintf(stderr, "%s: ", our_name);
37 vfprintf(stderr,fmt,al);
38 if (errnoval!=-1) fprintf(stderr,": %s",strerror(errnoval));
41 if (estatus) exit(estatus);
44 void usagemessage(void) { fusagemessage(stderr); }
46 void of_help(const struct cmdinfo *ci, const char *val) {
47 fusagemessage(stdout);
48 if (ferror(stdout)) diee("write usage message to stdout");
52 void of_iassign(const struct cmdinfo *ci, const char *val) {
55 errno= 0; v= strtol(val,&ep,10);
56 if (!*val || *ep || errno || v<INT_MIN || v>INT_MAX)
57 badusage("bad integer argument `%s' for --%s",val,ci->olong);
61 void ident_addstring(const struct cmdinfo *ci, const char *string) {
62 /* ci may be 0 and is provided so this can be .call */
63 sha256_update(&identsc,strlen(string)+1,string);
66 void off_ident_addenv(const struct cmdinfo *ci, const char *name) {
67 const char *val = getenv(name);
69 sha256_update(&identsc,strlen(name),name); /* no nul */
70 sha256_update(&identsc,1,"=");
71 ident_addstring(0,val);
73 ident_addstring(0,name);
77 bool find_run_base_var_run(void) {
82 try = m_asprintf("%s/%lu", "/var/run/user", us);
83 r = lstat(try, &stab);
85 if (errno == ENOENT ||
89 return 0; /* oh well */
90 diee("stat /var/run/user/UID");
92 if (!S_ISDIR(stab.st_mode)) {
93 warning("%s not a directory, falling back to ~\n", try);
96 if (stab.st_uid != us) {
97 warning("%s not owned by uid %lu, falling back to ~\n", try,
101 if (stab.st_mode & 0077) {
102 warning("%s writeable by group or other, falling back to ~\n", try);
105 run_base = m_asprintf("%s/%s", try, our_name);
109 static bool find_run_base_home(void) {
115 pw = getpwuid(us); if (!pw) diee("getpwent(uid)");
117 r = uname(&ut); if (r) diee("uname(2)");
118 dot = strchr(ut.nodename, '.');
120 if (sizeof(ut.nodename) > 32)
123 run_base_mkdir_p = m_asprintf("%s/.%s", pw->pw_dir, our_name);
124 try = m_asprintf("%s/%s", run_base_mkdir_p, ut.nodename);
129 void find_socket_path(void) {
130 struct sockaddr_un sun;
133 us = getuid(); if (us==(uid_t)-1) diee("getuid");
135 find_run_base_var_run() ||
136 find_run_base_home() ||
139 int maxidentlen = sizeof(sun.sun_path) - strlen(run_base) - 10 - 2;
142 if (maxidentlen < MINHEXHASH)
143 die("base directory `%s'"
144 " leaves only %d characters for id hash"
145 " which is too little (<%d)",
146 run_base, maxidentlen, MINHEXHASH);
148 int identlen = maxidentlen > 64 ? 64 : maxidentlen;
149 char *hexident = xmalloc(identlen + 2);
150 unsigned char bbuf[32];
153 ident_addstring(0,interp);
154 ident_addstring(0,script);
155 sha256_digest(&identsc,sizeof(bbuf),bbuf);
157 for (i=0; i<identlen; i += 2)
158 sprintf(hexident+i, "%02x", bbuf[i/2]);
160 hexident[identlen] = 0;
164 if (strlen(ident) > maxidentlen)
165 die("base directory `%s' plus ident `%s' too long"
166 " (with spare) for socket (max ident %d)\n",
167 run_base, ident, maxidentlen);
169 r = mkdir(run_base, 0700);
170 if (r && errno==ENOENT && run_base_mkdir_p) {
171 r = mkdir(run_base_mkdir_p, 0700);
172 if (r) diee("mkdir %s (since %s was ENOENT)",run_base_mkdir_p,run_base);
173 r = mkdir(run_base, 0700);
176 if (!(errno == EEXIST))
177 diee("mkdir %s",run_base);
180 socket_path = m_asprintf("%s/s%s",run_base,ident);
185 bool stab_isnewer(const struct stat *a, const struct stat *b) {
187 fprintf(stderr,"stab_isnewer mtim %lu.%06lu %lu.06%lu\n",
188 (unsigned long)a->st_mtim.tv_sec,
189 (unsigned long)a->st_mtim.tv_nsec,
190 (unsigned long)b->st_mtim.tv_sec,
191 (unsigned long)b->st_mtim.tv_nsec);
192 return timespeccmp(&a->st_mtim, &b->st_mtim, >);
195 void stab_mtimenow(struct stat *out) {
196 int r = clock_gettime(CLOCK_REALTIME, &out->st_mtim);
197 if (r) diee("(stage2) clock_gettime");
199 fprintf(stderr,"stab_mtimenow mtim %lu.%06lu\n",
200 (unsigned long)out->st_mtim.tv_sec,
201 (unsigned long)out->st_mtim.tv_nsec);
204 #else /* !defined(st_mtime) */
206 bool stab_isnewer(const struct stat *a, const struct stat *b) {
208 fprintf(stderr,"stab_isnewer mtime %lu %lu\n",
209 (unsigned long)a->st_mtime,
210 (unsigned long)b->st_mtime);
211 return a->st_mtime > b->st_mtime;
214 void stab_mtimenow(struct stat *out) {
215 out->st_mtime = time(NULL);
216 if (out->st_mtime == (time_t)-1) diee("(stage2) time()");
218 fprintf(stderr,"stab_mtimenow mtime %lu\n",
219 (unsigned long)out->st_mtime);
222 #endif /* !defined(st_mtime) */
224 bool check_garbage_vs(const struct stat *started) {
225 struct stat script_stab;
228 r = lstat(script, &script_stab);
229 if (r) diee("lstat script (%s)",script);
231 if (stab_isnewer(&script_stab, started))
234 if (S_ISLNK(script_stab.st_mode)) {
235 r = stat(script, &script_stab);
236 if (r) diee("stat script (%s0",script);
238 if (stab_isnewer(&script_stab, started))
245 bool check_garbage(void) {
246 struct stat sock_stab;
249 r = lstat(socket_path, &sock_stab);
251 if ((errno == ENOENT))
252 return 0; /* well, no garbage then */
253 diee("stat socket (%s)",socket_path);
256 return check_garbage_vs(&sock_stab);
260 int acquire_lock(void) {
264 lock_path = m_asprintf("%s/l%s",run_base,ident);
266 lockfd = open(lock_path, O_CREAT|O_RDWR, 0600);
267 if (lockfd<0) diee("create lock (%s)", lock_path);
269 r = flock(lockfd, LOCK_EX);
270 if (r) diee("lock lock (%s)", lock_path);
275 void tidy_garbage(void) {
276 /* We lock l<ident> and re-check. The effect of this is that each
277 * stale socket is removed only once. So unless multiple updates to
278 * the script happen rapidly, we can't be racing with the cgi-fcgi
279 * (which is recreating the socket */
283 lockfd = acquire_lock();
285 if (check_garbage()) {
286 r = unlink(socket_path);
288 if (!(errno == ENOENT))
289 diee("remove out-of-date socket (%s)", socket_path);
294 if (r) diee("close lock (%s)", lock_path);
297 static void shbang_opts(const char *const **argv_io,
298 const struct cmdinfo *cmdinfos) {
299 myopt(argv_io, cmdinfos);
301 interp = *(*argv_io)++;
302 if (!interp) badusage("need interpreter argument");
305 void process_opts(const char *const **argv_io) {
306 const char *smashedopt;
308 sha256_init(&identsc);
311 (smashedopt = (*argv_io)[1]) &&
312 smashedopt[0]=='-' &&
313 (strchr(smashedopt,' ') || strchr(smashedopt,','))) {
314 /* single argument containg all the options and <interp> */
315 *argv_io += 2; /* eat argv[0] and smashedopt */
316 const char *split_args[MAX_OPTS+1];
318 split_args[split_argc++] = (*argv_io)[0];
320 if (split_argc >= MAX_OPTS) die("too many options in combined arg");
321 split_args[split_argc++] = smashedopt;
322 if (smashedopt[0] != '-') /* never true on first iteration */
324 char *delim = strchr(smashedopt,' ');
325 if (!delim) delim = strchr(smashedopt,',');
326 if (!delim) badusage("combined arg lacks <interpreter>");
328 smashedopt = delim+1;
330 assert(split_argc <= MAX_OPTS);
331 split_args[split_argc++] = 0;
333 const char *const *split_argv = split_args;
335 shbang_opts(&split_argv, cmdinfos);
338 shbang_opts(argv_io, cmdinfos);
342 script = *(*argv_io)++;