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>
51 static int parsefile(struct cfstate *s, FILE *in);
52 static void stdmerge(struct child *old, struct child *new);
53 static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *idata);
54 static void stddestroy(struct child *ch);
56 static int doinclude(struct cfstate *s, char *spec)
61 char *fbk, *dir, *fspec;
69 fspec = sprintf3("%s/%s", dirname(dir), spec);
72 if(glob(fspec, 0, NULL, &globm))
74 for(i = 0; i < globm.gl_pathc; i++) {
75 if((inc = fopen(globm.gl_pathv[i], "r")) != NULL) {
76 s->file = globm.gl_pathv[i];
77 if(parsefile(s, inc)) {
93 static int parsefile(struct cfstate *s, FILE *in)
98 int ind, indst[80], indl;
105 if(fgets(line, sizeof(line), in) == NULL) {
111 for(p = line + strlen(line) - 1; p >= line; p--) {
118 for(ind = 0, p = line; *p; p++) {
121 } else if(*p == '\t') {
122 ind = ind - (ind % 8) + 8;
127 if(!eof && (!*p || (*p == '#')))
131 if(ind > indst[indl]) {
134 s->res = tokenize("start");
142 s->res = tokenize("end");
147 while(ind < indst[indl]) {
149 s->res = tokenize("end");
153 if(ind > indst[indl]) {
154 flog(LOG_WARNING, "%s:%i: unexpected indentation level", s->file, s->lno);
162 argc = calen(w = tokenize(line));
164 /* Shouldn't happen, but... */
170 if(!strcmp(w[0], "include")) {
171 for(i = 1; i < argc; i++) {
172 if(doinclude(s, w[i])) {
182 if(!strcmp(w[0], "start") ||
183 !strcmp(w[0], "end") ||
184 !strcmp(w[0], "eof")) {
185 flog(LOG_WARNING, "%s:%i: illegal directive: %s", s->file, s->lno, w[0]);
194 static void parsefn(struct muth *mt, va_list args)
196 vavar(struct cfstate *, s);
200 s->file = sstrdup(file);
204 s->res = tokenize("eof");
211 char **getcfline(struct cfstate *s)
216 s->argc = calen(s->argv = s->res);
221 struct cfstate *mkcfparser(FILE *in, char *name)
226 s->pf = mustart(parsefn, s, in, name);
230 void freecfparser(struct cfstate *s)
238 char *findstdconf(char *name)
240 char *home, *path, *p, *p2, *t;
242 if((home = getenv("HOME")) != NULL) {
243 if(!access(t = sprintf2("%s/.ashd/etc/%s", home, name), R_OK))
247 if((path = getenv("PATH")) != NULL) {
248 path = sstrdup(path);
249 for(p = strtok(path, ":"); p != NULL; p = strtok(NULL, ":")) {
250 if((p2 = strrchr(p, '/')) == NULL)
253 if(!access(t = sprintf2("%s/etc/%s", p, name), R_OK)) {
264 struct child *newchild(char *name, struct chandler *iface, void *pdata)
269 ch->name = sstrdup(name);
275 void freechild(struct child *ch)
277 if(ch->iface->destroy != NULL)
278 ch->iface->destroy(ch);
284 void mergechildren(struct child *dst, struct child *src)
286 struct child *ch1, *ch2;
288 for(ch1 = dst; ch1 != NULL; ch1 = ch1->next) {
289 for(ch2 = src; ch2 != NULL; ch2 = ch2->next) {
290 if(ch1->iface->merge && !strcmp(ch1->name, ch2->name)) {
291 ch1->iface->merge(ch1, ch2);
298 void skipcfblock(struct cfstate *s)
304 if(!strcmp(w[0], "end") || !strcmp(w[0], "eof"))
309 static struct chandler stdhandler = {
312 .destroy = stddestroy,
315 static char **expandargs(struct stdchild *sd)
318 char **ret, *p, *p2, *p3, *np, *env;
321 ret = szmalloc(sizeof(*ret) * (calen(sd->argv) + 1));
323 for(i = 0; sd->argv[i] != NULL; i++) {
324 if((p = strchr(sd->argv[i], '$')) == NULL) {
325 ret[i] = sstrdup(sd->argv[i]);
328 for(p2 = sd->argv[i]; p != NULL; p2 = np, p = strchr(np, '$')) {
329 bufcat(exp, p2, p - p2);
331 if((p3 = strchr((p += 2), '}')) == NULL) {
337 for(p3 = ++p; *p3; p3++) {
338 if(!(((*p3 >= 'a') && (*p3 <= 'z')) ||
339 ((*p3 >= 'A') && (*p3 <= 'Z')) ||
340 ((*p3 >= '0') && (*p3 <= '9')) ||
347 char temp[(p3 - p) + 1];
348 memcpy(temp, p, p3 - p);
350 if((env = getenv(temp)) != NULL)
354 ret[i] = sstrdup(exp.b);
364 void (*sinit)(void *);
368 static void stdinit(void *data)
370 struct sidata *d = data;
373 for(i = 0; d->sd->envp[i]; i += 2)
374 putenv(sprintf2("%s=%s", d->sd->envp[i], d->sd->envp[i + 1]));
379 static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *sdata)
381 struct stdchild *sd = ch->pdata;
386 if(sd->type == CH_SOCKET) {
387 idat = (struct sidata) {.sd = sd, .sinit = chinit, .sdata = sdata};
389 args = expandargs(sd);
390 sd->fd = stdmkchild(args, stdinit, &idat);
393 if(sendreq2(sd->fd, req, fd, MSG_NOSIGNAL | MSG_DONTWAIT)) {
395 if((serr == EPIPE) || (serr == ECONNRESET)) {
396 /* Assume that the child has crashed and restart it. */
398 args = expandargs(sd);
399 sd->fd = stdmkchild(args, stdinit, &idat);
401 if(!sendreq2(sd->fd, req, fd, MSG_NOSIGNAL | MSG_DONTWAIT))
406 if(sd->agains++ == 0) {
407 flog(LOG_WARNING, "request to child %s denied due to buffer overload", ch->name);
408 sd->lastrep = time(NULL);
411 flog(LOG_ERR, "could not pass on request to child %s: %s", ch->name, strerror(serr));
418 if((sd->agains > 0) && ((time(NULL) - sd->lastrep) > 10)) {
419 flog(LOG_WARNING, "%i requests to child %s were denied due to buffer overload", sd->agains, ch->name);
422 } else if(sd->type == CH_FORK) {
423 args = expandargs(sd);
424 if(stdforkserve(args, req, fd, chinit, sdata) < 0) {
433 static void stdmerge(struct child *dst, struct child *src)
435 struct stdchild *od, *nd;
437 if(src->iface == &stdhandler) {
445 static void stddestroy(struct child *ch)
447 struct stdchild *d = ch->pdata;
458 struct child *parsechild(struct cfstate *s)
462 struct charvbuf envbuf;
467 if(!strcmp(s->argv[0], "child")) {
470 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
474 ch = newchild(s->argv[1], &stdhandler, omalloc(d));
476 } else if(!strcmp(s->argv[0], "fchild")) {
479 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
483 ch = newchild(s->argv[1], &stdhandler, omalloc(d));
493 if(!strcmp(s->argv[0], "exec")) {
495 flog(LOG_WARNING, "%s:%i: too few parameters to `exec'", s->file, s->lno);
498 d->argv = szmalloc(sizeof(*d->argv) * s->argc);
499 for(i = 0; i < s->argc - 1; i++)
500 d->argv[i] = sstrdup(s->argv[i + 1]);
501 } else if(!strcmp(s->argv[0], "env")) {
503 flog(LOG_WARNING, "%s:%i: too few parameters to `env'", s->file, s->lno);
506 bufadd(envbuf, sstrdup(s->argv[1]));
507 bufadd(envbuf, sstrdup(s->argv[2]));
508 } else if(!strcmp(s->argv[0], "end") || !strcmp(s->argv[0], "eof")) {
511 flog(LOG_WARNING, "%s:%i: unknown directive `%s' in child declaration", s->file, s->lno, s->argv[0]);
514 bufadd(envbuf, NULL);
516 if(d->argv == NULL) {
517 flog(LOG_WARNING, "%s:%i: missing `exec' in child declaration %s", s->file, sl, ch->name);
524 int childhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *idata)
526 return(ch->iface->handle(ch, req, fd, chinit, idata));