1 /* common stuff for cgi-fcgi-interp and prefork-interp */
3 * Copyright 2016-2022 Ian Jackson and contributors to chiark-utils
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 * There is NO WARRANTY.
10 const char *interp, *ident, *script, *socket_path, *lock_path;
12 struct sha256_ctx identsc;
16 static const char *run_base_mkdir_p;
18 void common_diee(const char *m) { diee("%s", m); }
19 void common_die (const char *m) { die ("%s", m); }
21 void vmsgcore(int estatus, int errnoval, const char *fmt, va_list al) {
25 const char *fmt_use = fmt;
28 r = asprintf(&fmt_free, "%s: %%m", fmt);
35 vsyslog(LOG_ERR, fmt_use, al);
38 fprintf(stderr, "%s: ", our_name);
39 vfprintf(stderr,fmt,al);
40 if (errnoval!=-1) fprintf(stderr,": %s",strerror(errnoval));
43 if (estatus) exit(estatus);
46 void usagemessage(void) { fusagemessage(stderr); }
48 void of_help(const struct cmdinfo *ci, const char *val) {
49 fusagemessage(stdout);
50 if (ferror(stdout)) diee("write usage message to stdout");
54 void of_iassign(const struct cmdinfo *ci, const char *val) {
57 errno= 0; v= strtol(val,&ep,10);
58 if (!*val || *ep || errno || v<INT_MIN || v>INT_MAX)
59 badusage("bad integer argument `%s' for --%s",val,ci->olong);
63 void ident_add_key_byte(char key) {
64 sha256_update(&identsc,1,&key);
67 void ident_addstring(char key, const char *string) {
68 ident_add_key_byte(key);
69 sha256_update(&identsc,strlen(string)+1,string);
72 void off_ident_addstring(const struct cmdinfo *ci, const char *string) {
73 ident_addstring('G', string);
76 void off_ident_addenv(const struct cmdinfo *ci, const char *name) {
77 ident_addstring('E', name);
78 const char *val = getenv(name);
80 ident_addstring('v', val);
82 ident_add_key_byte(0);
86 bool stabs_same_inode(struct stat *a, struct stat *b) {
87 return (a->st_dev == b->st_dev &&
88 a->st_ino == b->st_ino);
91 bool find_run_base_var_run(void) {
96 try = m_asprintf("%s/%lu", "/var/run/user", us);
97 r = lstat(try, &stab);
99 if (errno == ENOENT ||
103 return 0; /* oh well */
104 diee("stat /var/run/user/UID");
106 if (!S_ISDIR(stab.st_mode)) {
107 warning("%s not a directory, falling back to ~\n", try);
110 if (stab.st_uid != us) {
111 warning("%s not owned by uid %lu, falling back to ~\n", try,
115 if (stab.st_mode & 0077) {
116 warning("%s writeable by group or other, falling back to ~\n", try);
119 run_base = m_asprintf("%s/%s", try, our_name);
123 static bool find_run_base_home(void) {
129 pw = getpwuid(us); if (!pw) diee("getpwent(uid)");
131 r = uname(&ut); if (r) diee("uname(2)");
132 dot = strchr(ut.nodename, '.');
134 if (sizeof(ut.nodename) > 32)
137 run_base_mkdir_p = m_asprintf("%s/.%s", pw->pw_dir, our_name);
138 try = m_asprintf("%s/%s", run_base_mkdir_p, ut.nodename);
143 void find_socket_path(void) {
144 struct sockaddr_un sun;
147 us = getuid(); if (us==(uid_t)-1) diee("getuid");
149 find_run_base_var_run() ||
150 find_run_base_home() ||
153 int maxidentlen = sizeof(sun.sun_path) - strlen(run_base) - 10 - 2;
156 if (maxidentlen < MINHEXHASH)
157 die("base directory `%s'"
158 " leaves only %d characters for id hash"
159 " which is too little (<%d)",
160 run_base, maxidentlen, MINHEXHASH);
162 int identlen = maxidentlen > 64 ? 64 : maxidentlen;
163 char *hexident = xmalloc(identlen + 2);
164 unsigned char bbuf[32];
167 ident_addstring('i', interp);
169 ident_addstring('s', script);
170 sha256_digest(&identsc,sizeof(bbuf),bbuf);
172 for (i=0; i<identlen; i += 2)
173 sprintf(hexident+i, "%02x", bbuf[i/2]);
175 hexident[identlen] = 0;
179 if (strlen(ident) > maxidentlen)
180 die("base directory `%s' plus ident `%s' too long"
181 " (with spare) for socket (max ident %d)\n",
182 run_base, ident, maxidentlen);
184 r = mkdir(run_base, 0700);
185 if (r && errno==ENOENT && run_base_mkdir_p) {
186 r = mkdir(run_base_mkdir_p, 0700);
187 if (r) diee("mkdir %s (since %s was ENOENT)",run_base_mkdir_p,run_base);
188 r = mkdir(run_base, 0700);
191 if (!(errno == EEXIST))
192 diee("mkdir %s",run_base);
195 socket_path = m_asprintf("%s/s%s",run_base,ident);
199 int flock_file(const char *lock_path) {
203 struct stat stab_path;
206 if (lockfd >= 0) { close(lockfd); lockfd = -1; }
208 lockfd = open(lock_path, O_CREAT|O_RDWR, 0600);
209 if (lockfd<0) diee("create lock (%s)", lock_path);
211 r = flock(lockfd, LOCK_EX);
212 if (r && errno == EINTR) continue;
213 if (r) diee("lock lock (%s)", lock_path);
215 r = fstat(lockfd, &stab_fd);
216 if (r) diee("fstat locked lock");
218 r = stat(lock_path, &stab_path);
220 if (stabs_same_inode(&stab_path, &stab_fd)) break;
222 if (!(errno == ENOENT)) diee("re-stat locked lock (%s)", lock_path);
230 int acquire_lock(void) {
231 lock_path = m_asprintf("%s/l%s",run_base,ident);
232 return flock_file(lock_path);
235 static void shbang_opts(const char *const **argv_io,
236 const struct cmdinfo *cmdinfos) {
237 myopt(argv_io, cmdinfos);
239 interp = *(*argv_io)++;
240 if (!interp) badusage("need interpreter argument");
243 void process_opts(const char *const **argv_io) {
244 const char *smashedopt;
246 sha256_init(&identsc);
250 (smashedopt = (*argv_io)[1]) &&
251 smashedopt[0]=='-' &&
252 (strchr(smashedopt,' ') || strchr(smashedopt,','))) {
253 /* single argument containg all the options and <interp> */
254 *argv_io += 2; /* eat argv[0] and smashedopt */
255 const char *split_args[MAX_OPTS+1];
257 split_args[split_argc++] = (*argv_io)[0];
259 if (split_argc >= MAX_OPTS) die("too many options in combined arg");
260 split_args[split_argc++] = smashedopt;
261 if (smashedopt[0] != '-') /* never true on first iteration */
263 char *delim = strchr(smashedopt,' ');
264 if (!delim) delim = strchr(smashedopt,',');
265 if (!delim) badusage("combined arg lacks <interpreter>");
267 smashedopt = delim+1;
269 assert(split_argc <= MAX_OPTS);
270 split_args[split_argc++] = 0;
272 const char *const *split_argv = split_args;
274 shbang_opts(&split_argv, cmdinfos);
278 badusage("no script argument (expected after combined #! options)");
280 shbang_opts(argv_io, cmdinfos);
284 script = *(*argv_io)++;