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/>.
53 struct child *children;
54 struct pattern *patterns;
71 struct headmod *headers;
75 int handler, prio, disable;
78 static struct config *gconfig, *lconfig;
79 static volatile int reload = 0;
81 static void freepattern(struct pattern *pat)
86 for(rule = pat->rules; *rule; rule++) {
87 if((*rule)->header != NULL)
88 free((*rule)->header);
89 if((*rule)->pattern != NULL) {
90 regfree((*rule)->pattern);
91 free((*rule)->pattern);
95 while((head = pat->headers) != NULL) {
96 pat->headers = head->next;
101 if(pat->childnm != NULL)
106 static void freeconfig(struct config *cf)
108 struct child *ch, *nch;
109 struct pattern *pat, *npat;
111 for(ch = cf->children; ch != NULL; ch = nch) {
115 for(pat = cf->patterns; pat != NULL; pat = npat) {
122 static struct child *getchild(struct config *cf, char *name)
126 for(ch = cf->children; ch; ch = ch->next) {
127 if(!strcmp(ch->name, name))
133 static struct rule *newrule(struct pattern *pat)
138 for(i = 0; pat->rules[i]; i++);
139 pat->rules = srealloc(pat->rules, sizeof(*pat->rules) * (i + 2));
140 rule = pat->rules[i] = szmalloc(sizeof(*rule));
141 pat->rules[i + 1] = NULL;
145 static struct pattern *newpattern(void)
150 pat->rules = szmalloc(sizeof(*pat->rules));
154 static regex_t *regalloc(char *regex, int flags)
161 if((res = regcomp(ret, regex, flags | REG_EXTENDED)) != 0) {
162 regerror(res, ret, errbuf, sizeof(errbuf));
163 flog(LOG_WARNING, "%s: %s", regex, errbuf);
170 static struct pattern *parsepattern(struct cfstate *s)
175 struct headmod *head;
179 if(!strcmp(s->argv[0], "match")) {
189 if(!strcmp(s->argv[0], "point") ||
190 !strcmp(s->argv[0], "url") ||
191 !strcmp(s->argv[0], "method")) {
193 flog(LOG_WARNING, "%s:%i: missing pattern for `%s' match", s->file, s->lno, s->argv[0]);
198 if(strchr(s->argv[2], 'i'))
201 if((regex = regalloc(s->argv[1], rxfl)) == NULL) {
202 flog(LOG_WARNING, "%s:%i: invalid regex for `%s' match", s->file, s->lno, s->argv[0]);
206 if(!strcmp(s->argv[0], "point"))
207 rule->type = PAT_REST;
208 else if(!strcmp(s->argv[0], "url"))
209 rule->type = PAT_URL;
210 else if(!strcmp(s->argv[0], "method"))
211 rule->type = PAT_METHOD;
212 rule->pattern = regex;
214 if(strchr(s->argv[2], 's'))
215 rule->fl |= PATFL_MSS;
216 if(strchr(s->argv[2], 'q'))
217 rule->fl |= PATFL_UNQ;
219 } else if(!strcmp(s->argv[0], "header")) {
221 flog(LOG_WARNING, "%s:%i: missing header name or pattern for `header' match", s->file, s->lno);
226 if(strchr(s->argv[3], 'i'))
229 if((regex = regalloc(s->argv[2], rxfl)) == NULL) {
230 flog(LOG_WARNING, "%s:%i: invalid regex for `header' match", s->file, s->lno);
234 rule->type = PAT_HEADER;
235 rule->header = sstrdup(s->argv[1]);
236 rule->pattern = regex;
238 if(strchr(s->argv[3], 's'))
239 rule->fl |= PATFL_MSS;
241 } else if(!strcmp(s->argv[0], "all")) {
242 newrule(pat)->type = PAT_ALL;
243 } else if(!strcmp(s->argv[0], "default")) {
244 newrule(pat)->type = PAT_ALL;
246 } else if(!strcmp(s->argv[0], "order") || !strcmp(s->argv[0], "priority")) {
248 flog(LOG_WARNING, "%s:%i: missing specification for `%s' directive", s->file, s->lno, s->argv[0]);
251 pat->prio = atoi(s->argv[1]);
252 } else if(!strcmp(s->argv[0], "handler")) {
254 flog(LOG_WARNING, "%s:%i: missing child name for `handler' directive", s->file, s->lno);
257 if(pat->childnm != NULL)
259 pat->childnm = sstrdup(s->argv[1]);
260 pat->handler = HND_CHILD;
261 } else if(!strcmp(s->argv[0], "reparse")) {
262 pat->handler = HND_REPARSE;
263 } else if(!strcmp(s->argv[0], "restpat")) {
265 flog(LOG_WARNING, "%s:%i: missing pattern for `restpat' directive", s->file, s->lno);
268 if(pat->restpat != NULL)
270 pat->restpat = sstrdup(s->argv[1]);
271 } else if(!strcmp(s->argv[0], "set") || !strcmp(s->argv[0], "xset")) {
273 flog(LOG_WARNING, "%s:%i: missing header name or pattern for `%s' directive", s->file, s->lno, s->argv[0]);
277 if(!strcmp(s->argv[0], "xset"))
278 head->name = sprintf2("X-Ash-%s", s->argv[1]);
280 head->name = sstrdup(s->argv[1]);
281 head->value = sstrdup(s->argv[2]);
282 head->next = pat->headers;
284 } else if(!strcmp(s->argv[0], "end") || !strcmp(s->argv[0], "eof")) {
287 flog(LOG_WARNING, "%s:%i: unknown directive `%s' in pattern declaration", s->file, s->lno, s->argv[0]);
291 if(pat->rules[0] == NULL) {
292 flog(LOG_WARNING, "%s:%i: missing rules in match declaration", s->file, sl);
296 if(pat->handler == 0) {
297 flog(LOG_WARNING, "%s:%i: missing handler in match declaration", s->file, sl);
304 static struct config *readconfig(char *filename)
312 if((in = fopen(filename, "r")) == NULL) {
313 flog(LOG_WARNING, "%s: %s", filename, strerror(errno));
316 s = mkcfparser(in, filename);
321 if((ch = parsechild(s)) != NULL) {
322 ch->next = cf->children;
324 } else if((pat = parsepattern(s)) != NULL) {
325 pat->next = cf->patterns;
327 } else if(!strcmp(s->argv[0], "eof")) {
330 flog(LOG_WARNING, "%s:%i: unknown directive `%s'", s->file, s->lno, s->argv[0]);
339 static void exprestpat(struct hthead *req, struct pattern *pat, char **mstr)
348 for(mc = 0; mstr[mc]; mc++);
350 for(p = pat->restpat; *p; ) {
353 if((*p >= '0') && (*p <= '9')) {
355 bufcatstr(buf, mstr[*p - '0']);
357 } else if(*p == '_') {
358 bufcatstr(buf, req->rest);
360 } else if(*p == '$') {
363 } else if(*p == '{') {
364 if((p2 = strchr(p, '}')) == NULL) {
367 hdr = getheader(req, sprintf3("%.*s", p2 - p - 1, p + 1));
378 replrest(req, buf.b);
382 static void qoffsets(char *buf, int *obuf, char *pstr, int unquote)
390 if((pstr[i] == '%') && ((d1 = hexdigit(pstr[i + 1])) >= 0) && ((d2 = hexdigit(pstr[i + 2])) >= 0)) {
391 buf[o] = (d1 << 4) | d2;
402 for(i = 0; pstr[i]; i++) {
417 static void freematch(struct match *match)
423 static struct match *findmatch(struct config *cf, struct hthead *req, struct match *match)
435 for(pat = cf->patterns; pat != NULL; pat = pat->next) {
436 if(pat->disable || (match && (pat->prio <= match->pat->prio)))
439 for(i = 0; (rule = pat->rules[i]) != NULL; i++) {
441 if(rule->type == PAT_REST) {
444 } else if(rule->type == PAT_URL) {
447 } else if(rule->type == PAT_METHOD) {
450 } else if(rule->type == PAT_HEADER) {
452 if(!(pstr = getheader(req, rule->header)))
456 char pbuf[strlen(pstr) + 1];
457 int obuf[strlen(pstr) + 1];
458 qoffsets(pbuf, obuf, pstr, !!(rule->fl & PATFL_UNQ));
459 if(regexec(rx, pbuf, 10, gr, 0))
461 else if(rule->type == PAT_REST)
462 rmo = obuf[gr[0].rm_eo];
463 if(rule->fl & PATFL_MSS) {
465 flog(LOG_WARNING, "two pattern rules marked with `s' flag found (for handler %s)", pat->childnm);
468 for(o = 0; o < 10; o++) {
472 mstr = szmalloc((o + 1) * sizeof(*mstr));
473 for(o = 0; o < 10; o++) {
476 mstr[o] = smalloc(obuf[gr[o].rm_eo] - obuf[gr[o].rm_so] + 1);
477 memcpy(mstr[o], pstr + obuf[gr[o].rm_so], obuf[gr[o].rm_eo] - obuf[gr[o].rm_so]);
478 mstr[o][obuf[gr[o].rm_eo] - obuf[gr[o].rm_so]] = 0;
481 } else if(rule->type == PAT_ALL) {
496 static void execmatch(struct hthead *req, struct match *match)
498 struct headmod *head;
500 if(match->pat->restpat)
501 exprestpat(req, match->pat, match->mstr);
502 else if(match->rmo != -1)
503 replrest(req, req->rest + match->rmo);
504 for(head = match->pat->headers; head != NULL; head = head->next) {
505 headrmheader(req, head->name);
506 headappheader(req, head->name, head->value);
510 static void childerror(struct hthead *req, int fd)
513 simpleerror(fd, 500, "Server Error", "The request handler is overloaded.");
515 simpleerror(fd, 500, "Server Error", "The request handler crashed.");
518 static void serve(struct hthead *req, int fd)
524 match = findmatch(lconfig, req, match);
526 match = findmatch(gconfig, req, match);
528 simpleerror(fd, 404, "Not Found", "The requested resource could not be found on this server.");
531 execmatch(req, match);
532 switch(match->pat->handler) {
536 ch = getchild(lconfig, match->pat->childnm);
537 if((ch == NULL) && (gconfig != NULL))
538 ch = getchild(gconfig, match->pat->childnm);
540 flog(LOG_ERR, "child %s requested, but was not declared", match->pat->childnm);
541 simpleerror(fd, 500, "Configuration Error", "The server is erroneously configured. Handler %s was requested, but not declared.", match->pat->childnm);
544 if(childhandle(ch, req, fd, NULL, NULL))
548 match->pat->disable = 1;
550 match->pat->disable = 0;
558 static void reloadconf(char *nm)
562 if((cf = readconfig(nm)) == NULL) {
563 flog(LOG_WARNING, "could not reload configuration file `%s'", nm);
566 mergechildren(cf->children, lconfig->children);
571 static void chldhandler(int sig)
576 while((pid = waitpid(-1, &st, WNOHANG)) > 0) {
578 flog(LOG_WARNING, "child process %i dumped core", pid);
582 static void sighandler(int sig)
588 static void usage(FILE *out)
590 fprintf(out, "usage: patplex [-hN] CONFIGFILE\n");
593 int main(int argc, char **argv)
602 while((c = getopt(argc, argv, "hN")) >= 0) {
615 if(argc - optind < 1) {
620 if((gcf = findstdconf("ashd/patplex.rc")) != NULL) {
621 gconfig = readconfig(gcf);
625 if((strchr(lcf = argv[optind], '/')) == NULL) {
626 if((lcf = findstdconf(sprintf3("ashd/%s", lcf))) == NULL) {
627 flog(LOG_ERR, "could not find requested configuration file `%s'", argv[optind]);
631 if((lconfig = readconfig(lcf)) == NULL) {
632 flog(LOG_ERR, "could not read `%s'", lcf);
635 signal(SIGCHLD, chldhandler);
636 signal(SIGHUP, sighandler);
637 signal(SIGPIPE, sighandler);
643 if((fd = recvreq(0, &req)) < 0) {
647 flog(LOG_ERR, "recvreq: %s", strerror(errno));