2 ashd - A Sane HTTP Daemon
3 Copyright (C) 2008 Fredrik Tolf <fredrik@dolda2000.com>
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.
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.
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/>.
26 #include <sys/socket.h>
49 static int parsefile(struct cfstate *s, FILE *in);
50 static void stdmerge(struct child *old, struct child *new);
51 static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *idata);
52 static void stddestroy(struct child *ch);
54 static int doinclude(struct cfstate *s, char *spec)
59 char *fbk, *dir, *fspec;
67 fspec = sprintf3("%s/%s", dirname(dir), spec);
70 if(glob(fspec, 0, NULL, &globm))
72 for(i = 0; i < globm.gl_pathc; i++) {
73 if((inc = fopen(globm.gl_pathv[i], "r")) != NULL) {
74 s->file = globm.gl_pathv[i];
75 if(parsefile(s, inc)) {
91 static int parsefile(struct cfstate *s, FILE *in)
96 int ind, indst[80], indl;
103 if(fgets(line, sizeof(line), in) == NULL) {
109 for(p = line + strlen(line) - 1; p >= line; p--) {
116 for(ind = 0, p = line; *p; p++) {
119 } else if(*p == '\t') {
120 ind = ind - (ind % 8) + 8;
125 if(!eof && (!*p || (*p == '#')))
129 if(ind > indst[indl]) {
132 s->res = tokenize("start");
140 s->res = tokenize("end");
145 while(ind < indst[indl]) {
147 s->res = tokenize("end");
151 if(ind > indst[indl]) {
152 flog(LOG_WARNING, "%s:%i: unexpected indentation level", s->file, s->lno);
160 argc = calen(w = tokenize(line));
162 /* Shouldn't happen, but... */
168 if(!strcmp(w[0], "include")) {
169 for(i = 1; i < argc; i++) {
170 if(doinclude(s, w[i])) {
180 if(!strcmp(w[0], "start") ||
181 !strcmp(w[0], "end") ||
182 !strcmp(w[0], "eof")) {
183 flog(LOG_WARNING, "%s:%i: illegal directive: %s", s->file, s->lno, w[0]);
192 static void parsefn(struct muth *mt, va_list args)
194 vavar(struct cfstate *, s);
198 s->file = sstrdup(file);
202 s->res = tokenize("eof");
209 char **getcfline(struct cfstate *s)
214 s->argc = calen(s->argv = s->res);
219 struct cfstate *mkcfparser(FILE *in, char *name)
224 s->pf = mustart(parsefn, s, in, name);
228 void freecfparser(struct cfstate *s)
236 char *findstdconf(char *name)
238 char *home, *path, *p, *p2, *t;
240 if((home = getenv("HOME")) != NULL) {
241 if(!access(t = sprintf2("%s/.ashd/etc/%s", home, name), R_OK))
245 if((path = getenv("PATH")) != NULL) {
246 path = sstrdup(path);
247 for(p = strtok(path, ":"); p != NULL; p = strtok(NULL, ":")) {
248 if((p2 = strrchr(p, '/')) == NULL)
251 if(!access(t = sprintf2("%s/etc/%s", p, name), R_OK)) {
262 struct child *newchild(char *name, struct chandler *iface, void *pdata)
267 ch->name = sstrdup(name);
273 void freechild(struct child *ch)
275 if(ch->iface->destroy != NULL)
276 ch->iface->destroy(ch);
282 void mergechildren(struct child *dst, struct child *src)
284 struct child *ch1, *ch2;
286 for(ch1 = dst; ch1 != NULL; ch1 = ch1->next) {
287 for(ch2 = src; ch2 != NULL; ch2 = ch2->next) {
288 if(ch1->iface->merge && !strcmp(ch1->name, ch2->name)) {
289 ch1->iface->merge(ch1, ch2);
296 void skipcfblock(struct cfstate *s)
302 if(!strcmp(w[0], "end") || !strcmp(w[0], "eof"))
307 static struct chandler stdhandler = {
310 .destroy = stddestroy,
313 static char **expandargs(struct stdchild *sd)
316 char **ret, *p, *p2, *p3, *np, *env;
319 ret = szmalloc(sizeof(*ret) * (calen(sd->argv) + 1));
321 for(i = 0; sd->argv[i] != NULL; i++) {
322 if((p = strchr(sd->argv[i], '$')) == NULL) {
323 ret[i] = sstrdup(sd->argv[i]);
326 for(p2 = sd->argv[i]; p != NULL; p2 = np, p = strchr(np, '$')) {
327 bufcat(exp, p2, p - p2);
329 if((p3 = strchr((p += 2), '}')) == NULL)
333 for(p3 = ++p; *p3; p3++) {
334 if(!(((*p3 >= 'a') && (*p3 <= 'z')) ||
335 ((*p3 >= 'A') && (*p3 <= 'Z')) ||
336 ((*p3 >= '0') && (*p3 <= '9')) ||
343 char temp[(p3 - p) + 1];
344 memcpy(temp, p, p3 - p);
346 if((env = getenv(temp)) != NULL)
350 ret[i] = sstrdup(exp.b);
360 void (*sinit)(void *);
364 static void stdinit(void *data)
366 struct sidata *d = data;
369 for(i = 0; d->sd->envp[i]; i += 2)
370 putenv(sprintf2("%s=%s", d->sd->envp[i], d->sd->envp[i + 1]));
375 static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *sdata)
377 struct stdchild *sd = ch->pdata;
382 if(sd->type == CH_SOCKET) {
383 idat = (struct sidata) {.sd = sd, .sinit = chinit, .sdata = sdata};
385 args = expandargs(sd);
386 sd->fd = stdmkchild(args, stdinit, &idat);
389 if(sendreq2(sd->fd, req, fd, MSG_NOSIGNAL | MSG_DONTWAIT)) {
391 if((serr == EPIPE) || (serr == ECONNRESET)) {
392 /* Assume that the child has crashed and restart it. */
394 args = expandargs(sd);
395 sd->fd = stdmkchild(args, stdinit, &idat);
397 if(!sendreq2(sd->fd, req, fd, MSG_NOSIGNAL | MSG_DONTWAIT))
402 if(sd->agains++ == 0)
403 flog(LOG_WARNING, "request to child %s denied due to buffer overload", ch->name);
405 flog(LOG_ERR, "could not pass on request to child %s: %s", ch->name, strerror(serr));
413 flog(LOG_WARNING, "%i requests to child %s were denied due to buffer overload", sd->agains, ch->name);
416 } else if(sd->type == CH_FORK) {
417 args = expandargs(sd);
418 if(stdforkserve(args, req, fd, chinit, sdata) < 0) {
427 static void stdmerge(struct child *dst, struct child *src)
429 struct stdchild *od, *nd;
431 if(src->iface == &stdhandler) {
439 static void stddestroy(struct child *ch)
441 struct stdchild *d = ch->pdata;
452 struct child *parsechild(struct cfstate *s)
456 struct charvbuf envbuf;
461 if(!strcmp(s->argv[0], "child")) {
464 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
468 ch = newchild(s->argv[1], &stdhandler, omalloc(d));
470 } else if(!strcmp(s->argv[0], "fchild")) {
473 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
477 ch = newchild(s->argv[1], &stdhandler, omalloc(d));
487 if(!strcmp(s->argv[0], "exec")) {
489 flog(LOG_WARNING, "%s:%i: too few parameters to `exec'", s->file, s->lno);
492 d->argv = szmalloc(sizeof(*d->argv) * s->argc);
493 for(i = 0; i < s->argc - 1; i++)
494 d->argv[i] = sstrdup(s->argv[i + 1]);
495 } else if(!strcmp(s->argv[0], "env")) {
497 flog(LOG_WARNING, "%s:%i: too few parameters to `env'", s->file, s->lno);
500 bufadd(envbuf, sstrdup(s->argv[1]));
501 bufadd(envbuf, sstrdup(s->argv[2]));
502 } else if(!strcmp(s->argv[0], "end") || !strcmp(s->argv[0], "eof")) {
505 flog(LOG_WARNING, "%s:%i: unknown directive `%s' in child declaration", s->file, s->lno, s->argv[0]);
508 bufadd(envbuf, NULL);
510 if(d->argv == NULL) {
511 flog(LOG_WARNING, "%s:%i: missing `exec' in child declaration %s", s->file, sl, ch->name);
518 int childhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *idata)
520 return(ch->iface->handle(ch, req, fd, chinit, idata));