etc: Added an explicit .wsgi2 extension in the Python configuration.
[ashd.git] / lib / cf.c
CommitLineData
06c1a718
FT
1/*
2 ashd - A Sane HTTP Daemon
3 Copyright (C) 2008 Fredrik Tolf <fredrik@dolda2000.com>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#include <stdlib.h>
20#include <stdio.h>
21#include <unistd.h>
22#include <string.h>
23#include <ctype.h>
24#include <glob.h>
dd281054 25#include <libgen.h>
3d74fc07 26#include <sys/socket.h>
06c1a718
FT
27#include <errno.h>
28
29#ifdef HAVE_CONFIG_H
30#include <config.h>
31#endif
32#include <utils.h>
33#include <cf.h>
34#include <mt.h>
35#include <proc.h>
36#include <log.h>
37
38#define CH_SOCKET 0
39#define CH_FORK 1
40
1924fe8c
FT
41struct stdchild {
42 int type;
43 char **argv;
3b77f136 44 char **envp;
1924fe8c
FT
45 int fd;
46};
47
dd281054 48static int parsefile(struct cfstate *s, FILE *in);
1924fe8c
FT
49static void stdmerge(struct child *old, struct child *new);
50static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *idata);
51static void stddestroy(struct child *ch);
dd281054
FT
52
53static int doinclude(struct cfstate *s, char *spec)
06c1a718 54{
dd281054
FT
55 int rv, i;
56 FILE *inc;
06c1a718 57 glob_t globm;
dd281054
FT
58 char *fbk, *dir, *fspec;
59
60 rv = 0;
61 fbk = s->file;
62 if(spec[0] == '/') {
63 fspec = spec;
64 } else {
65 dir = sstrdup(fbk);
66 fspec = sprintf3("%s/%s", dirname(dir), spec);
67 free(dir);
68 }
69 if(glob(fspec, 0, NULL, &globm))
70 return(0);
71 for(i = 0; i < globm.gl_pathc; i++) {
72 if((inc = fopen(globm.gl_pathv[i], "r")) != NULL) {
73 s->file = globm.gl_pathv[i];
74 if(parsefile(s, inc)) {
75 fclose(inc);
76 rv = 1;
77 goto out;
78 }
79 fclose(inc);
80 inc = NULL;
81 }
82 }
83
84out:
85 globfree(&globm);
86 s->file = fbk;
87 return(rv);
88}
89
90static int parsefile(struct cfstate *s, FILE *in)
91{
92 int i;
06c1a718
FT
93 char line[1024];
94 int eof, argc;
95 int ind, indst[80], indl;
dd281054 96 char *p, **w;
06c1a718
FT
97
98 s->lno = 0;
99 indst[indl = 0] = 0;
100 eof = 0;
101 while(1) {
102 if(fgets(line, sizeof(line), in) == NULL) {
103 eof = 1;
104 line[0] = 0;
105 }
106 s->lno++;
c3b91092
FT
107 if(line[0]) {
108 for(p = line + strlen(line) - 1; p >= line; p--) {
109 if(isspace(*p))
110 *p = 0;
111 else
112 break;
113 }
06c1a718
FT
114 }
115 for(ind = 0, p = line; *p; p++) {
116 if(*p == ' ') {
117 ind++;
118 } else if(*p == '\t') {
119 ind = ind - (ind % 8) + 8;
120 } else {
121 break;
122 }
123 }
124 if(!eof && (!*p || (*p == '#')))
125 continue;
126
127 reindent:
128 if(ind > indst[indl]) {
129 indst[++indl] = ind;
130 if(!s->expstart) {
131 s->res = tokenize("start");
132 if(yield())
133 return(1);
134 } else {
135 s->expstart = 0;
136 }
137 } else {
138 if(s->expstart) {
139 s->res = tokenize("end");
140 if(yield())
141 return(1);
142 s->expstart = 0;
143 }
144 while(ind < indst[indl]) {
145 indl--;
146 s->res = tokenize("end");
147 if(yield())
148 return(1);
149 }
150 if(ind > indst[indl]) {
151 flog(LOG_WARNING, "%s:%i: unexpected indentation level", s->file, s->lno);
152 goto reindent;
153 }
154 }
155
156 if(eof)
157 return(0);
158
159 argc = calen(w = tokenize(line));
160 if(argc < 1) {
161 /* Shouldn't happen, but... */
162 freeca(w);
163 continue;
164 }
165
166 if(indl == 0) {
167 if(!strcmp(w[0], "include")) {
06c1a718 168 for(i = 1; i < argc; i++) {
dd281054
FT
169 if(doinclude(s, w[i])) {
170 freeca(w);
171 return(1);
06c1a718
FT
172 }
173 }
174 freeca(w);
06c1a718
FT
175 continue;
176 }
177 }
178
179 if(!strcmp(w[0], "start") ||
180 !strcmp(w[0], "end") ||
181 !strcmp(w[0], "eof")) {
182 flog(LOG_WARNING, "%s:%i: illegal directive: %s", s->file, s->lno, w[0]);
183 } else {
184 s->res = w;
185 if(yield())
186 return(1);
187 }
188 }
189}
190
191static void parsefn(struct muth *mt, va_list args)
192{
193 vavar(struct cfstate *, s);
194 vavar(FILE *, in);
195 vavar(char *, file);
196
197 s->file = sstrdup(file);
198 if(parsefile(s, in))
199 goto out;
200 do {
201 s->res = tokenize("eof");
202 } while(!yield());
203
204out:
205 free(s->file);
206}
207
208char **getcfline(struct cfstate *s)
209{
210 freeca(s->argv);
211 if(s->res == NULL)
212 resume(s->pf, 0);
213 s->argc = calen(s->argv = s->res);
214 s->res = NULL;
215 return(s->argv);
216}
217
218struct cfstate *mkcfparser(FILE *in, char *name)
219{
220 struct cfstate *s;
221
222 omalloc(s);
223 s->pf = mustart(parsefn, s, in, name);
224 return(s);
225}
226
227void freecfparser(struct cfstate *s)
228{
229 resume(s->pf, -1);
230 freeca(s->argv);
231 freeca(s->res);
232 free(s);
233}
234
0fc6fd13
FT
235char *findstdconf(char *name)
236{
f9a65eb2 237 char *home, *path, *p, *p2, *t;
0fc6fd13 238
f9a65eb2
FT
239 if((home = getenv("HOME")) != NULL) {
240 if(!access(t = sprintf2("%s/.ashd/etc/%s", home, name), R_OK))
13975be5 241 return(t);
13975be5 242 free(t);
0fc6fd13 243 }
f9a65eb2
FT
244 if((path = getenv("PATH")) != NULL) {
245 path = sstrdup(path);
246 for(p = strtok(path, ":"); p != NULL; p = strtok(NULL, ":")) {
247 if((p2 = strrchr(p, '/')) == NULL)
248 continue;
249 *p2 = 0;
250 if(!access(t = sprintf2("%s/etc/%s", p, name), R_OK)) {
251 free(path);
252 return(t);
253 }
254 free(t);
255 }
256 free(path);
257 }
0fc6fd13
FT
258 return(NULL);
259}
260
1924fe8c 261struct child *newchild(char *name, struct chandler *iface, void *pdata)
06c1a718
FT
262{
263 struct child *ch;
264
265 omalloc(ch);
266 ch->name = sstrdup(name);
1924fe8c
FT
267 ch->iface = iface;
268 ch->pdata = pdata;
06c1a718
FT
269 return(ch);
270}
271
272void freechild(struct child *ch)
273{
1924fe8c
FT
274 if(ch->iface->destroy != NULL)
275 ch->iface->destroy(ch);
06c1a718
FT
276 if(ch->name != NULL)
277 free(ch->name);
06c1a718
FT
278 free(ch);
279}
280
1924fe8c
FT
281void mergechildren(struct child *dst, struct child *src)
282{
283 struct child *ch1, *ch2;
284
285 for(ch1 = dst; ch1 != NULL; ch1 = ch1->next) {
286 for(ch2 = src; ch2 != NULL; ch2 = ch2->next) {
287 if(ch1->iface->merge && !strcmp(ch1->name, ch2->name)) {
288 ch1->iface->merge(ch1, ch2);
289 break;
290 }
291 }
292 }
293}
294
06c1a718
FT
295void skipcfblock(struct cfstate *s)
296{
297 char **w;
298
299 while(1) {
300 w = getcfline(s);
301 if(!strcmp(w[0], "end") || !strcmp(w[0], "eof"))
302 return;
303 }
304}
305
1924fe8c
FT
306static struct chandler stdhandler = {
307 .handle = stdhandle,
308 .merge = stdmerge,
309 .destroy = stddestroy,
310};
311
b3eb750f
FT
312static char **expandargs(struct stdchild *sd)
313{
314 int i;
315 char **ret, *p, *p2, *p3, *np, *env;
316 struct charbuf exp;
317
318 ret = szmalloc(sizeof(*ret) * (calen(sd->argv) + 1));
319 bufinit(exp);
320 for(i = 0; sd->argv[i] != NULL; i++) {
321 if((p = strchr(sd->argv[i], '$')) == NULL) {
322 ret[i] = sstrdup(sd->argv[i]);
323 } else {
324 exp.d = 0;
325 for(p2 = sd->argv[i]; p != NULL; p2 = np, p = strchr(np, '$')) {
326 bufcat(exp, p2, p - p2);
327 if(p[1] == '{') {
328 if((p3 = strchr((p += 2), '}')) == NULL)
329 break;
330 np = p3 + 1;
331 } else {
332 for(p3 = ++p; *p3; p3++) {
333 if(!(((*p3 >= 'a') && (*p3 <= 'z')) ||
334 ((*p3 >= 'A') && (*p3 <= 'Z')) ||
335 ((*p3 >= '0') && (*p3 <= '9')) ||
336 (*p3 == '_'))) {
337 break;
338 }
339 }
340 np = p3;
341 }
342 char temp[(p3 - p) + 1];
343 memcpy(temp, p, p3 - p);
344 temp[p3 - p] = 0;
345 if((env = getenv(temp)) != NULL)
346 bufcatstr(exp, env);
347 }
348 bufcatstr2(exp, np);
349 ret[i] = sstrdup(exp.b);
350 }
351 }
352 ret[i] = NULL;
353 buffree(exp);
354 return(ret);
355}
356
1924fe8c
FT
357static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *idata)
358{
3b77f136 359 struct stdchild *sd = ch->pdata;
45b19d96 360 int serr;
b3eb750f 361 char **args;
1924fe8c 362
3b77f136
FT
363 void stdinit(void *data)
364 {
365 int i;
366
367 for(i = 0; sd->envp[i]; i += 2)
368 putenv(sprintf2("%s=%s", sd->envp[i], sd->envp[i + 1]));
209dfd8b
FT
369 if(chinit != NULL)
370 chinit(data);
3b77f136
FT
371 }
372
373 if(sd->type == CH_SOCKET) {
b3eb750f
FT
374 if(sd->fd < 0) {
375 args = expandargs(sd);
376 sd->fd = stdmkchild(args, stdinit, idata);
377 freeca(args);
378 }
3b77f136 379 if(sendreq2(sd->fd, req, fd, MSG_NOSIGNAL | MSG_DONTWAIT)) {
45b19d96
FT
380 serr = errno;
381 if((serr == EPIPE) || (serr == ECONNRESET)) {
1924fe8c 382 /* Assume that the child has crashed and restart it. */
3b77f136 383 close(sd->fd);
b3eb750f
FT
384 args = expandargs(sd);
385 sd->fd = stdmkchild(args, stdinit, idata);
386 freeca(args);
3b77f136 387 if(!sendreq2(sd->fd, req, fd, MSG_NOSIGNAL | MSG_DONTWAIT))
1924fe8c
FT
388 return(0);
389 }
45b19d96
FT
390 flog(LOG_ERR, "could not pass on request to child %s: %s", ch->name, strerror(serr));
391 if(serr != EAGAIN) {
3b77f136
FT
392 close(sd->fd);
393 sd->fd = -1;
fc35a3ef 394 }
1924fe8c
FT
395 return(-1);
396 }
3b77f136 397 } else if(sd->type == CH_FORK) {
b3eb750f
FT
398 args = expandargs(sd);
399 if(stdforkserve(args, req, fd, chinit, idata) < 0) {
400 freeca(args);
1924fe8c 401 return(-1);
b3eb750f
FT
402 }
403 freeca(args);
1924fe8c
FT
404 }
405 return(0);
406}
407
408static void stdmerge(struct child *dst, struct child *src)
409{
410 struct stdchild *od, *nd;
411
412 if(src->iface == &stdhandler) {
413 nd = dst->pdata;
414 od = src->pdata;
415 nd->fd = od->fd;
416 od->fd = -1;
417 }
418}
419
420static void stddestroy(struct child *ch)
421{
422 struct stdchild *d = ch->pdata;
423
424 if(d->fd >= 0)
425 close(d->fd);
426 if(d->argv)
427 freeca(d->argv);
3b77f136
FT
428 if(d->envp)
429 freeca(d->envp);
1924fe8c
FT
430 free(d);
431}
432
06c1a718
FT
433struct child *parsechild(struct cfstate *s)
434{
435 struct child *ch;
1924fe8c 436 struct stdchild *d;
3b77f136 437 struct charvbuf envbuf;
06c1a718
FT
438 int i;
439 int sl;
440
441 sl = s->lno;
442 if(!strcmp(s->argv[0], "child")) {
443 s->expstart = 1;
444 if(s->argc < 2) {
445 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
446 skipcfblock(s);
447 return(NULL);
448 }
1924fe8c
FT
449 ch = newchild(s->argv[1], &stdhandler, omalloc(d));
450 d->type = CH_SOCKET;
06c1a718
FT
451 } else if(!strcmp(s->argv[0], "fchild")) {
452 s->expstart = 1;
453 if(s->argc < 2) {
454 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
455 skipcfblock(s);
456 return(NULL);
457 }
1924fe8c
FT
458 ch = newchild(s->argv[1], &stdhandler, omalloc(d));
459 d->type = CH_FORK;
06c1a718
FT
460 } else {
461 return(NULL);
462 }
1924fe8c 463 d->fd = -1;
06c1a718 464
3b77f136 465 bufinit(envbuf);
06c1a718
FT
466 while(1) {
467 getcfline(s);
468 if(!strcmp(s->argv[0], "exec")) {
469 if(s->argc < 2) {
470 flog(LOG_WARNING, "%s:%i: too few parameters to `exec'", s->file, s->lno);
471 continue;
472 }
1924fe8c 473 d->argv = szmalloc(sizeof(*d->argv) * s->argc);
06c1a718 474 for(i = 0; i < s->argc - 1; i++)
1924fe8c 475 d->argv[i] = sstrdup(s->argv[i + 1]);
3b77f136
FT
476 } else if(!strcmp(s->argv[0], "env")) {
477 if(s->argc < 3) {
478 flog(LOG_WARNING, "%s:%i: too few parameters to `env'", s->file, s->lno);
479 continue;
480 }
481 bufadd(envbuf, sstrdup(s->argv[1]));
482 bufadd(envbuf, sstrdup(s->argv[2]));
06c1a718
FT
483 } else if(!strcmp(s->argv[0], "end") || !strcmp(s->argv[0], "eof")) {
484 break;
485 } else {
486 flog(LOG_WARNING, "%s:%i: unknown directive `%s' in child declaration", s->file, s->lno, s->argv[0]);
487 }
488 }
3b77f136
FT
489 bufadd(envbuf, NULL);
490 d->envp = envbuf.b;
1924fe8c 491 if(d->argv == NULL) {
06c1a718
FT
492 flog(LOG_WARNING, "%s:%i: missing `exec' in child declaration %s", s->file, sl, ch->name);
493 freechild(ch);
494 return(NULL);
495 }
496 return(ch);
497}
498
6a7a868e 499int childhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *idata)
06c1a718 500{
1924fe8c 501 return(ch->iface->handle(ch, req, fd, chinit, idata));
06c1a718 502}