chiark / gitweb /
eca03c85f5177e39a37033162958d612595f2a4a
[chiark-utils.git] / cprogs / prefork.c
1 /* common stuff for cgi-fcgi-interp and prefork-interp */
2
3 #include "prefork.h"
4
5 const char *interp, *ident, *script, *socket_path, *lock_path;
6 bool logging;
7 struct sha256_ctx identsc;
8 const char *run_base;
9
10 static uid_t us;
11 static const char *run_base_mkdir_p;
12
13 void common_diee(const char *m) { diee("%s", m); }
14 void common_die (const char *m) { die ("%s", m); }
15
16 void vmsgcore(int estatus, int errnoval, const char *fmt, va_list al) {
17   int r;
18
19   if (logging) {
20     const char *fmt_use = fmt;
21     char *fmt_free = 0;
22     if (errnoval!=-1) {
23       r = asprintf(&fmt_free, "%s: %%m", fmt);
24       if (r) {
25         fmt_free = 0;
26       } else {
27         fmt_use = fmt_free;
28       }
29     }
30     vsyslog(LOG_ERR, fmt_use, al);
31     free(fmt_free);
32   } else {
33     fprintf(stderr, "%s: ", our_name);
34     vfprintf(stderr,fmt,al);
35     if (errnoval!=-1) fprintf(stderr,": %s",strerror(errnoval));
36     fputc('\n',stderr);
37   }
38   if (estatus) exit(estatus);
39 }
40
41 void usagemessage(void) { fusagemessage(stderr); }
42
43 void of_help(const struct cmdinfo *ci, const char *val) {
44   fusagemessage(stdout);
45   if (ferror(stdout)) diee("write usage message to stdout");
46   exit(0);
47 }
48
49 void of_iassign(const struct cmdinfo *ci, const char *val) {
50   long v;
51   char *ep;
52   errno= 0; v= strtol(val,&ep,10);
53   if (!*val || *ep || errno || v<INT_MIN || v>INT_MAX)
54     badusage("bad integer argument `%s' for --%s",val,ci->olong);
55   *ci->iassignto = v;
56 }
57
58 static void ident_addbyte(char c) {
59   IDENT_ADD_OBJ(c);
60 }
61
62 void ident_addstring(const char *string) {
63   sha256_update(&identsc,strlen(string)+1,string);
64 }
65
66 void off_ident_addstring(const struct cmdinfo *ci, const char *string) {
67   ident_addbyte('S');
68   ident_addstring(string);
69 }
70
71 void off_ident_addenv(const struct cmdinfo *ci, const char *name) {
72   const char *val = getenv(name);
73   ident_addbyte('E');
74   if (val) {
75     sha256_update(&identsc,strlen(name),name); /* no nul */
76     sha256_update(&identsc,1,"=");
77     ident_addstring(val);
78   } else {
79     ident_addstring(name);
80   }
81 }
82
83 bool stabs_same_inode(struct stat *a, struct stat *b) {
84   return (a->st_dev == b->st_dev &&
85           a->st_ino == b->st_ino);
86 }
87
88 bool find_run_base_var_run(void) {
89   struct stat stab;
90   char *try;
91   int r;
92
93   try = m_asprintf("%s/%lu", "/var/run/user", us);
94   r = lstat(try, &stab);
95   if (r<0) {
96     if (errno == ENOENT ||
97         errno == ENOTDIR ||
98         errno == EACCES ||
99         errno == EPERM)
100       return 0; /* oh well */
101     diee("stat /var/run/user/UID");
102   }
103   if (!S_ISDIR(stab.st_mode)) {
104     warning("%s not a directory, falling back to ~\n", try);
105     return 0;
106   }
107   if (stab.st_uid != us) {
108     warning("%s not owned by uid %lu, falling back to ~\n", try,
109             (unsigned long)us);
110     return 0;
111   }
112   if (stab.st_mode & 0077) {
113     warning("%s writeable by group or other, falling back to ~\n", try);
114     return 0;
115   }
116   run_base = m_asprintf("%s/%s", try, our_name);
117   return 1;
118 }
119
120 static bool find_run_base_home(void) {
121   struct passwd *pw;
122   struct utsname ut;
123   char *dot, *try;
124   int r;
125
126   pw = getpwuid(us);  if (!pw) diee("getpwent(uid)");
127
128   r = uname(&ut);   if (r) diee("uname(2)");
129   dot = strchr(ut.nodename, '.');
130   if (dot) *dot = 0;
131   if (sizeof(ut.nodename) > 32)
132     ut.nodename[32] = 0;
133
134   run_base_mkdir_p = m_asprintf("%s/.%s", pw->pw_dir, our_name);
135   try = m_asprintf("%s/%s", run_base_mkdir_p, ut.nodename);
136   run_base = try;
137   return 1;
138 }
139
140 void find_socket_path(void) {
141   struct sockaddr_un sun;
142   int r;
143
144   us = getuid();  if (us==(uid_t)-1) diee("getuid");
145
146   find_run_base_var_run() ||
147     find_run_base_home() ||
148     (abort(),0);
149
150   int maxidentlen = sizeof(sun.sun_path) - strlen(run_base) - 10 - 2;
151
152   if (!ident) {
153     if (maxidentlen < MINHEXHASH)
154       die("base directory `%s'"
155           " leaves only %d characters for id hash"
156           " which is too little (<%d)",
157           run_base, maxidentlen, MINHEXHASH);
158
159     int identlen = maxidentlen > 64 ? 64 : maxidentlen;
160     char *hexident = xmalloc(identlen + 2);
161     unsigned char bbuf[32];
162     int i;
163
164     ident_addstring(interp);
165     if (script)
166       ident_addstring(script);
167     sha256_digest(&identsc,sizeof(bbuf),bbuf);
168
169     for (i=0; i<identlen; i += 2)
170       sprintf(hexident+i, "%02x", bbuf[i/2]);
171
172     hexident[identlen] = 0;
173     ident = hexident;
174   }
175
176   if (strlen(ident) > maxidentlen)
177     die("base directory `%s' plus ident `%s' too long"
178         " (with spare) for socket (max ident %d)\n",
179         run_base, ident, maxidentlen);
180
181   r = mkdir(run_base, 0700);
182   if (r && errno==ENOENT && run_base_mkdir_p) {
183     r = mkdir(run_base_mkdir_p, 0700);
184     if (r) diee("mkdir %s (since %s was ENOENT)",run_base_mkdir_p,run_base);
185     r = mkdir(run_base, 0700);
186   }
187   if (r) {
188     if (!(errno == EEXIST))
189       diee("mkdir %s",run_base);
190   }
191
192   socket_path = m_asprintf("%s/s%s",run_base,ident);
193 }  
194
195 // Returns fd
196 int flock_file(const char *lock_path) {
197   int r;
198   int lockfd = -1;
199   struct stat stab_fd;
200   struct stat stab_path;
201
202   for (;;) {
203     if (lockfd >= 0) { close(lockfd); lockfd = -1; }
204
205     lockfd = open(lock_path, O_CREAT|O_RDWR, 0600);
206     if (lockfd<0) diee("create lock (%s)", lock_path);
207
208     r = flock(lockfd, LOCK_EX);
209     if (r && errno == EINTR) continue;
210     if (r) diee("lock lock (%s)", lock_path);
211
212     r = fstat(lockfd, &stab_fd);
213     if (r) diee("fstat locked lock");
214
215     r = stat(lock_path, &stab_path);
216     if (!r) {
217       if (stabs_same_inode(&stab_path, &stab_fd)) break;
218     } else {
219       if (!(errno == ENOENT)) diee("re-stat locked lock (%s)", lock_path);
220     }
221   }
222
223   return lockfd;
224 }
225
226 // Returns fd
227 int acquire_lock(void) {
228   lock_path = m_asprintf("%s/l%s",run_base,ident);
229   return flock_file(lock_path);
230 }
231
232 static void shbang_opts(const char *const **argv_io,
233                         const struct cmdinfo *cmdinfos) {
234   myopt(argv_io, cmdinfos);
235
236   interp = *(*argv_io)++;
237   if (!interp) badusage("need interpreter argument");
238 }
239
240 void process_opts(const char *const **argv_io) {
241   const char *smashedopt;
242
243   sha256_init(&identsc);
244   ident_addinit();
245
246   if ((*argv_io)[0] &&
247       (smashedopt = (*argv_io)[1]) &&
248       smashedopt[0]=='-' &&
249       (strchr(smashedopt,' ') || strchr(smashedopt,','))) {
250     /* single argument containg all the options and <interp> */
251     *argv_io += 2; /* eat argv[0] and smashedopt */
252     const char *split_args[MAX_OPTS+1];
253     int split_argc = 0;
254     split_args[split_argc++] = (*argv_io)[0];
255     for (;;) {
256       if (split_argc >= MAX_OPTS) die("too many options in combined arg");
257       split_args[split_argc++] = smashedopt;
258       if (smashedopt[0] != '-') /* never true on first iteration */
259         break;
260       char *delim = strchr(smashedopt,' ');
261       if (!delim) delim = strchr(smashedopt,',');
262       if (!delim) badusage("combined arg lacks <interpreter>");
263       *delim = 0;
264       smashedopt = delim+1;
265     }
266     assert(split_argc <= MAX_OPTS);
267     split_args[split_argc++] = 0;
268
269     const char *const *split_argv = split_args;
270
271     shbang_opts(&split_argv, cmdinfos);
272     /* sets interp */
273
274     if (!**argv_io)
275       badusage("no script argument (expected after combined #! options)");
276   } else {
277     shbang_opts(argv_io, cmdinfos);
278   }
279
280   if (**argv_io)
281     script = *(*argv_io)++;
282 }