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/>.
46 static int parsefile(struct cfstate *s, FILE *in);
47 static void stdmerge(struct child *old, struct child *new);
48 static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *idata);
49 static void stddestroy(struct child *ch);
51 static int doinclude(struct cfstate *s, char *spec)
56 char *fbk, *dir, *fspec;
64 fspec = sprintf3("%s/%s", dirname(dir), spec);
67 if(glob(fspec, 0, NULL, &globm))
69 for(i = 0; i < globm.gl_pathc; i++) {
70 if((inc = fopen(globm.gl_pathv[i], "r")) != NULL) {
71 s->file = globm.gl_pathv[i];
72 if(parsefile(s, inc)) {
88 static int parsefile(struct cfstate *s, FILE *in)
93 int ind, indst[80], indl;
100 if(fgets(line, sizeof(line), in) == NULL) {
106 for(p = line + strlen(line) - 1; p >= line; p--) {
113 for(ind = 0, p = line; *p; p++) {
116 } else if(*p == '\t') {
117 ind = ind - (ind % 8) + 8;
122 if(!eof && (!*p || (*p == '#')))
126 if(ind > indst[indl]) {
129 s->res = tokenize("start");
137 s->res = tokenize("end");
142 while(ind < indst[indl]) {
144 s->res = tokenize("end");
148 if(ind > indst[indl]) {
149 flog(LOG_WARNING, "%s:%i: unexpected indentation level", s->file, s->lno);
157 argc = calen(w = tokenize(line));
159 /* Shouldn't happen, but... */
165 if(!strcmp(w[0], "include")) {
166 for(i = 1; i < argc; i++) {
167 if(doinclude(s, w[i])) {
177 if(!strcmp(w[0], "start") ||
178 !strcmp(w[0], "end") ||
179 !strcmp(w[0], "eof")) {
180 flog(LOG_WARNING, "%s:%i: illegal directive: %s", s->file, s->lno, w[0]);
189 static void parsefn(struct muth *mt, va_list args)
191 vavar(struct cfstate *, s);
195 s->file = sstrdup(file);
199 s->res = tokenize("eof");
206 char **getcfline(struct cfstate *s)
211 s->argc = calen(s->argv = s->res);
216 struct cfstate *mkcfparser(FILE *in, char *name)
221 s->pf = mustart(parsefn, s, in, name);
225 void freecfparser(struct cfstate *s)
233 char *findstdconf(char *name)
235 char *path, *p, *p2, *t;
237 if((path = getenv("PATH")) == NULL)
239 path = sstrdup(path);
240 for(p = strtok(path, ":"); p != NULL; p = strtok(NULL, ":")) {
241 if((p2 = strrchr(p, '/')) == NULL)
244 if(!access(t = sprintf2("%s/etc/%s", p, name), R_OK)) {
254 struct child *newchild(char *name, struct chandler *iface, void *pdata)
259 ch->name = sstrdup(name);
265 void freechild(struct child *ch)
267 if(ch->iface->destroy != NULL)
268 ch->iface->destroy(ch);
274 void mergechildren(struct child *dst, struct child *src)
276 struct child *ch1, *ch2;
278 for(ch1 = dst; ch1 != NULL; ch1 = ch1->next) {
279 for(ch2 = src; ch2 != NULL; ch2 = ch2->next) {
280 if(ch1->iface->merge && !strcmp(ch1->name, ch2->name)) {
281 ch1->iface->merge(ch1, ch2);
288 void skipcfblock(struct cfstate *s)
294 if(!strcmp(w[0], "end") || !strcmp(w[0], "eof"))
299 static struct chandler stdhandler = {
302 .destroy = stddestroy,
305 static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *idata)
307 struct stdchild *i = ch->pdata;
309 if(i->type == CH_SOCKET) {
311 i->fd = stdmkchild(i->argv, chinit, idata);
312 if(sendreq(i->fd, req, fd)) {
313 if((errno == EPIPE) || (errno == ECONNRESET)) {
314 /* Assume that the child has crashed and restart it. */
316 i->fd = stdmkchild(i->argv, chinit, idata);
317 if(!sendreq(i->fd, req, fd))
320 flog(LOG_ERR, "could not pass on request to child %s: %s", ch->name, strerror(errno));
325 } else if(i->type == CH_FORK) {
326 if(stdforkserve(i->argv, req, fd, chinit, idata) < 0)
332 static void stdmerge(struct child *dst, struct child *src)
334 struct stdchild *od, *nd;
336 if(src->iface == &stdhandler) {
344 static void stddestroy(struct child *ch)
346 struct stdchild *d = ch->pdata;
355 struct child *parsechild(struct cfstate *s)
363 if(!strcmp(s->argv[0], "child")) {
366 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
370 ch = newchild(s->argv[1], &stdhandler, omalloc(d));
372 } else if(!strcmp(s->argv[0], "fchild")) {
375 flog(LOG_WARNING, "%s:%i: missing name in child declaration", s->file, s->lno);
379 ch = newchild(s->argv[1], &stdhandler, omalloc(d));
388 if(!strcmp(s->argv[0], "exec")) {
390 flog(LOG_WARNING, "%s:%i: too few parameters to `exec'", s->file, s->lno);
393 d->argv = szmalloc(sizeof(*d->argv) * s->argc);
394 for(i = 0; i < s->argc - 1; i++)
395 d->argv[i] = sstrdup(s->argv[i + 1]);
396 } else if(!strcmp(s->argv[0], "end") || !strcmp(s->argv[0], "eof")) {
399 flog(LOG_WARNING, "%s:%i: unknown directive `%s' in child declaration", s->file, s->lno, s->argv[0]);
402 if(d->argv == NULL) {
403 flog(LOG_WARNING, "%s:%i: missing `exec' in child declaration %s", s->file, sl, ch->name);
410 int childhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *idata)
412 return(ch->iface->handle(ch, req, fd, chinit, idata));