X-Git-Url: http://www.dolda2000.com/gitweb/?p=ashd.git;a=blobdiff_plain;f=src%2Fpatplex.c;h=13fa062e4aaaeb2486082fe69cd0d874340871b3;hp=6e6a78f723aaacad4aadd539a90b2c9d7fff8984;hb=9e70ef793ddcf51edc0f6489eb6f70762380afc0;hpb=326e08fce22a36b2dd14ce0abed18fbcb9e19882 diff --git a/src/patplex.c b/src/patplex.c index 6e6a78f..13fa062 100644 --- a/src/patplex.c +++ b/src/patplex.c @@ -24,6 +24,7 @@ #include #include #include +#include #ifdef HAVE_CONFIG_H #include @@ -33,9 +34,7 @@ #include #include #include - -#define CH_SOCKET 0 -#define CH_FORK 1 +#include #define PAT_REST 0 #define PAT_URL 1 @@ -51,14 +50,6 @@ struct config { struct pattern *patterns; }; -struct child { - struct child *next; - char *name; - int type; - char **argv; - int fd; -}; - struct rule { int type; int fl; @@ -66,18 +57,26 @@ struct rule { regex_t *pattern; }; +struct headmod { + struct headmod *next; + char *name, *value; +}; + struct pattern { struct pattern *next; + struct headmod *headers; char *childnm; struct rule **rules; char *restpat; }; -static struct config *config; +static struct config *gconfig, *lconfig; +static volatile int reload = 0; static void freepattern(struct pattern *pat) { struct rule **rule; + struct headmod *head; for(rule = pat->rules; *rule; rule++) { if((*rule)->header != NULL) @@ -88,31 +87,31 @@ static void freepattern(struct pattern *pat) } free(*rule); } + while((head = pat->headers) != NULL) { + pat->headers = head->next; + free(head->name); + free(head->value); + free(head); + } if(pat->childnm != NULL) free(pat->childnm); free(pat); } -static void freechild(struct child *ch) +static void freeconfig(struct config *cf) { - if(ch->fd != -1) - close(ch->fd); - if(ch->name != NULL) - free(ch->name); - if(ch->argv != NULL) - freeca(ch->argv); - free(ch); -} - -static struct child *newchild(char *name, int type) -{ - struct child *ch; + struct child *ch, *nch; + struct pattern *pat, *npat; - omalloc(ch); - ch->name = sstrdup(name); - ch->type = type; - ch->fd = -1; - return(ch); + for(ch = cf->children; ch != NULL; ch = nch) { + nch = ch->next; + freechild(ch); + } + for(pat = cf->patterns; pat != NULL; pat = npat) { + npat = pat->next; + freepattern(pat); + } + free(cf); } static struct child *getchild(struct config *cf, char *name) @@ -163,202 +162,158 @@ static regex_t *regalloc(char *regex, int flags) return(ret); } -static struct config *readconfig(char *filename) +static struct pattern *parsepattern(struct cfstate *s) { - int i; - struct config *cf; - FILE *s; - char line[1024]; - char *p, **w; - int ind, eof; - int lno; - int state; - int rv; - int argc; - struct child *child; struct pattern *pat; + int sl; struct rule *rule; + struct headmod *head; regex_t *regex; int rxfl; - if((s = fopen(filename, "r")) == NULL) + if(!strcmp(s->argv[0], "match")) { + s->expstart = 1; + pat = newpattern(); + } else { return(NULL); - omalloc(cf); - eof = 0; - state = 0; - w = NULL; - lno = 0; - do { - if(fgets(line, sizeof(line), s) == NULL) { - eof = 1; - line[0] = 0; - } - lno++; - for(p = line; *p; p++) { - if(*p == '#') + } + + sl = s->lno; + while(1) { + getcfline(s); + if(!strcmp(s->argv[0], "point") || + !strcmp(s->argv[0], "url") || + !strcmp(s->argv[0], "method")) { + if(s->argc < 2) { + flog(LOG_WARNING, "%s:%i: missing pattern for `%s' match", s->file, s->lno, s->argv[0]); continue; - if(!isspace(*p)) - break; - } - ind = isspace(line[0]); - w = tokenize(line); - argc = calen(w); - - retry: - if(state == 0) { - if(ind) { - flog(LOG_WARNING, "%s%i: unexpected line indentation in global scope", filename, lno); - goto next; - } else { - if(!w[0]) { - } else if(!strcmp(w[0], "child")) { - if(argc < 2) { - flog(LOG_WARNING, "%s:%i: missing name in child declaration", filename, lno); - goto next; - } - child = newchild(w[1], CH_SOCKET); - state = 1; - } else if(!strcmp(w[0], "fchild")) { - if(argc < 2) { - flog(LOG_WARNING, "%s:%i: missing name in child declaration", filename, lno); - goto next; - } - child = newchild(w[1], CH_FORK); - state = 1; - } else if(!strcmp(w[0], "match")) { - pat = newpattern(); - state = 2; - } else { - flog(LOG_WARNING, "%s:%i: unknown directive %s", filename, lno, w[0]); - } } - } else if(state == 1) { - if(ind) { - if(!w[0]) { - } else if(!strcmp(w[0], "exec")) { - if(argc < 2) { - flog(LOG_WARNING, "%s:%i: too few parameters to `exec'", filename, lno); - goto next; - } - child->argv = szmalloc(sizeof(*child->argv) * argc); - for(i = 0; i < argc - 1; i++) - child->argv[i] = sstrdup(w[i + 1]); - } else { - flog(LOG_WARNING, "%s:%i: unknown directive %s", filename, lno, w[0]); - } - } else { - state = 0; - if(child->argv == NULL) { - flog(LOG_WARNING, "%s:%i: missing `exec' in child declaration %s", filename, lno, child->name); - freechild(child); - goto retry; - } - child->next = cf->children; - cf->children = child; - goto retry; + if(s->argc >= 3) { + if(strchr(s->argv[2], 'i')) + rxfl |= REG_ICASE; } - } else if(state == 2) { - if(ind) { - rxfl = 0; - if(!w[0]) { - } else if(!strcmp(w[0], "point") || - !strcmp(w[0], "url") || - !strcmp(w[0], "method")) { - if(argc < 2) { - flog(LOG_WARNING, "%s:%i: missing pattern for `%s' match", w[0], filename, lno); - goto next; - } - if(argc >= 3) { - if(strchr(w[2], 'i')) - rxfl |= REG_ICASE; - } - if((regex = regalloc(w[1], rxfl)) == NULL) { - flog(LOG_WARNING, "%s:%i: invalid regex for `%s' match", w[0], filename, lno); - goto next; - } - rule = newrule(pat); - if(!strcmp(w[0], "point")) - rule->type = PAT_REST; - else if(!strcmp(w[0], "url")) - rule->type = PAT_URL; - else if(!strcmp(w[0], "method")) - rule->type = PAT_METHOD; - rule->pattern = regex; - if(argc >= 3) { - if(strchr(w[2], 's')) - rule->fl |= PATFL_MSS; - } - } else if(!strcmp(w[0], "header")) { - if(argc < 3) { - flog(LOG_WARNING, "%s:%i: missing header name or pattern for `header' match", filename, lno); - goto next; - } - if(argc >= 4) { - if(strchr(w[3], 'i')) - rxfl |= REG_ICASE; - } - if((regex = regalloc(w[2], rxfl)) == NULL) { - flog(LOG_WARNING, "%s:%i: invalid regex for `header' match", filename, lno); - goto next; - } - rule = newrule(pat); - rule->type = PAT_HEADER; - rule->header = sstrdup(w[1]); - rule->pattern = regex; - if(argc >= 4) { - if(strchr(w[3], 's')) - rule->fl |= PATFL_MSS; - } - } else if(!strcmp(w[0], "all")) { - newrule(pat)->type = PAT_ALL; - } else if(!strcmp(w[0], "default")) { - newrule(pat)->type = PAT_DEFAULT; - } else if(!strcmp(w[0], "handler")) { - if(argc < 2) { - flog(LOG_WARNING, "%s:%i: missing child name for `handler' directive", filename, lno); - goto next; - } - if(pat->childnm != NULL) - free(pat->childnm); - pat->childnm = sstrdup(w[1]); - } else if(!strcmp(w[0], "restpat")) { - if(argc < 2) { - flog(LOG_WARNING, "%s:%i: missing pattern for `restpat' directive", filename, lno); - goto next; - } - if(pat->restpat != NULL) - free(pat->restpat); - pat->restpat = sstrdup(w[1]); - } else { - flog(LOG_WARNING, "%s:%i: unknown directive %s", filename, lno, w[0]); - } - } else { - state = 0; - if(pat->rules[0] == NULL) { - flog(LOG_WARNING, "%s:%i: missing rules in match declaration", filename, lno); - freepattern(pat); - goto retry; - } - if(pat->childnm == NULL) { - flog(LOG_WARNING, "%s:%i: missing handler in match declaration", filename, lno); - freepattern(pat); - goto retry; - } - pat->next = cf->patterns; - cf->patterns = pat; - goto retry; + if((regex = regalloc(s->argv[1], rxfl)) == NULL) { + flog(LOG_WARNING, "%s:%i: invalid regex for `%s' match", s->file, s->lno, s->argv[0]); + continue; + } + rule = newrule(pat); + if(!strcmp(s->argv[0], "point")) + rule->type = PAT_REST; + else if(!strcmp(s->argv[0], "url")) + rule->type = PAT_URL; + else if(!strcmp(s->argv[0], "method")) + rule->type = PAT_METHOD; + rule->pattern = regex; + if(s->argc >= 3) { + if(strchr(s->argv[2], 's')) + rule->fl |= PATFL_MSS; + } + } else if(!strcmp(s->argv[0], "header")) { + if(s->argc < 3) { + flog(LOG_WARNING, "%s:%i: missing header name or pattern for `header' match", s->file, s->lno); + continue; + } + if(s->argc >= 4) { + if(strchr(s->argv[3], 'i')) + rxfl |= REG_ICASE; + } + if((regex = regalloc(s->argv[2], rxfl)) == NULL) { + flog(LOG_WARNING, "%s:%i: invalid regex for `header' match", s->file, s->lno); + continue; + } + rule = newrule(pat); + rule->type = PAT_HEADER; + rule->header = sstrdup(s->argv[1]); + rule->pattern = regex; + if(s->argc >= 4) { + if(strchr(s->argv[3], 's')) + rule->fl |= PATFL_MSS; + } + } else if(!strcmp(s->argv[0], "all")) { + newrule(pat)->type = PAT_ALL; + } else if(!strcmp(s->argv[0], "default")) { + newrule(pat)->type = PAT_DEFAULT; + } else if(!strcmp(s->argv[0], "handler")) { + if(s->argc < 2) { + flog(LOG_WARNING, "%s:%i: missing child name for `handler' directive", s->file, s->lno); + continue; } + if(pat->childnm != NULL) + free(pat->childnm); + pat->childnm = sstrdup(s->argv[1]); + } else if(!strcmp(s->argv[0], "restpat")) { + if(s->argc < 2) { + flog(LOG_WARNING, "%s:%i: missing pattern for `restpat' directive", s->file, s->lno); + continue; + } + if(pat->restpat != NULL) + free(pat->restpat); + pat->restpat = sstrdup(s->argv[1]); + } else if(!strcmp(s->argv[0], "set") || !strcmp(s->argv[0], "xset")) { + if(s->argc < 3) { + flog(LOG_WARNING, "%s:%i: missing header name or pattern for `%s' directive", s->file, s->lno, s->argv[0]); + continue; + } + omalloc(head); + if(!strcmp(s->argv[0], "xset")) + head->name = sprintf2("X-Ash-%s", s->argv[1]); + else + head->name = sstrdup(s->argv[1]); + head->value = sstrdup(s->argv[2]); + head->next = pat->headers; + pat->headers = head; + } else if(!strcmp(s->argv[0], "end") || !strcmp(s->argv[0], "eof")) { + break; + } else { + flog(LOG_WARNING, "%s:%i: unknown directive `%s' in pattern declaration", s->file, s->lno, s->argv[0]); } - - next: - freeca(w); - w = NULL; - } while(!eof); - rv = 0; + } - if(w != NULL) - freeca(w); - fclose(s); + if(pat->rules[0] == NULL) { + flog(LOG_WARNING, "%s:%i: missing rules in match declaration", s->file, sl); + freepattern(pat); + return(NULL); + } + if(pat->childnm == NULL) { + flog(LOG_WARNING, "%s:%i: missing handler in match declaration", s->file, sl); + freepattern(pat); + return(NULL); + } + return(pat); +} + +static struct config *readconfig(char *filename) +{ + struct cfstate *s; + struct config *cf; + struct child *ch; + struct pattern *pat; + FILE *in; + + if((in = fopen(filename, "r")) == NULL) { + flog(LOG_WARNING, "%s: %s", filename, strerror(errno)); + return(NULL); + } + s = mkcfparser(in, filename); + omalloc(cf); + + while(1) { + getcfline(s); + if((ch = parsechild(s)) != NULL) { + ch->next = cf->children; + cf->children = ch; + } else if((pat = parsepattern(s)) != NULL) { + pat->next = cf->patterns; + cf->patterns = pat; + } else if(!strcmp(s->argv[0], "eof")) { + break; + } else { + flog(LOG_WARNING, "%s:%i: unknown directive `%s'", s->file, s->lno, s->argv[0]); + } + } + + freecfparser(s); + fclose(in); return(cf); } @@ -387,10 +342,10 @@ static void exprestpat(struct hthead *req, struct pattern *pat, char **mstr) bufadd(buf, '$'); p++; } else if(*p == '{') { - if((p2 = strchr(p, '{')) == NULL) { + if((p2 = strchr(p, '}')) == NULL) { p++; } else { - hdr = getheader(req, sprintf3("$.*s", p2 - p - 1, p + 1)); + hdr = getheader(req, sprintf3("%.*s", p2 - p - 1, p + 1)); if(hdr) bufcatstr(buf, hdr); } @@ -405,7 +360,7 @@ static void exprestpat(struct hthead *req, struct pattern *pat, char **mstr) buffree(buf); } -static char *findmatch(struct config *cf, struct hthead *req, int trydefault) +static struct pattern *findmatch(struct config *cf, struct hthead *req, int trydefault) { int i, o; struct pattern *pat; @@ -468,7 +423,7 @@ static char *findmatch(struct config *cf, struct hthead *req, int trydefault) } if(mstr) freeca(mstr); - return(pat->childnm); + return(pat); } if(mstr) { freeca(mstr); @@ -478,63 +433,130 @@ static char *findmatch(struct config *cf, struct hthead *req, int trydefault) return(NULL); } -static void forkchild(struct child *ch) -{ - ch->fd = stdmkchild(ch->argv); -} - -static void passreq(struct child *ch, struct hthead *req, int fd) -{ - if(ch->fd < 0) - forkchild(ch); - if(sendreq(ch->fd, req, fd)) { - if(errno == EPIPE) { - /* Assume that the child has crashed and restart it. */ - forkchild(ch); - if(!sendreq(ch->fd, req, fd)) - return; - } - flog(LOG_ERR, "could not pass on request to child %s: %s", ch->name, strerror(errno)); - close(ch->fd); - ch->fd = -1; - } -} - static void serve(struct hthead *req, int fd) { - char *chnm; + struct pattern *pat; + struct headmod *head; struct child *ch; - if(((chnm = findmatch(config, req, 0)) == NULL) && ((chnm = findmatch(config, req, 1)) == NULL)) { + pat = NULL; + if(pat == NULL) + pat = findmatch(lconfig, req, 0); + if(pat == NULL) + pat = findmatch(lconfig, req, 1); + if(gconfig != NULL) { + if(pat == NULL) + pat = findmatch(gconfig, req, 0); + if(pat == NULL) + pat = findmatch(gconfig, req, 1); + } + if(pat == NULL) { simpleerror(fd, 404, "Not Found", "The requested resource could not be found on this server."); return; } - if((ch = getchild(config, chnm)) == NULL) { - flog(LOG_ERR, "child %s requested, but was not declared", chnm); - simpleerror(fd, 500, "Configuration Error", "The server is erroneously configured. Handler %s was requested, but not declared.", chnm); + ch = NULL; + if(ch == NULL) + ch = getchild(lconfig, pat->childnm); + if(gconfig != NULL) { + if(ch == NULL) + ch = getchild(gconfig, pat->childnm); + } + if(ch == NULL) { + flog(LOG_ERR, "child %s requested, but was not declared", pat->childnm); + simpleerror(fd, 500, "Configuration Error", "The server is erroneously configured. Handler %s was requested, but not declared.", pat->childnm); + return; + } + + for(head = pat->headers; head != NULL; head = head->next) { + headrmheader(req, head->name); + headappheader(req, head->name, head->value); + } + if(childhandle(ch, req, fd, NULL, NULL)) + simpleerror(fd, 500, "Server Error", "The request handler crashed."); +} + +static void reloadconf(char *nm) +{ + struct config *cf; + + if((cf = readconfig(nm)) == NULL) { + flog(LOG_WARNING, "could not reload configuration file `%s'", nm); return; } + mergechildren(cf->children, lconfig->children); + freeconfig(lconfig); + lconfig = cf; +} + +static void chldhandler(int sig) +{ + pid_t pid; + int st; - if(ch->type == CH_SOCKET) { - passreq(ch, req, fd); - } else if(ch->type == CH_FORK) { - stdforkserve(ch->argv, req, fd); + while((pid = waitpid(-1, &st, WNOHANG)) > 0) { + if(WCOREDUMP(st)) + flog(LOG_WARNING, "child process %i dumped core", pid); } } +static void sighandler(int sig) +{ + if(sig == SIGHUP) + reload = 1; +} + +static void usage(FILE *out) +{ + fprintf(out, "usage: patplex [-hN] CONFIGFILE\n"); +} + int main(int argc, char **argv) { + int c; + int nodef; + char *gcf; struct hthead *req; int fd; - - if(argc < 2) { - flog(LOG_ERR, "usage: patplex CONFIGFILE"); + + nodef = 0; + while((c = getopt(argc, argv, "hN")) >= 0) { + switch(c) { + case 'h': + usage(stdout); + exit(0); + case 'N': + nodef = 1; + break; + default: + usage(stderr); + exit(1); + } + } + if(argc - optind < 1) { + usage(stderr); exit(1); } - config = readconfig(argv[1]); - signal(SIGCHLD, SIG_IGN); + if(!nodef) { + if((gcf = findstdconf("ashd/patplex.rc")) != NULL) { + gconfig = readconfig(gcf); + free(gcf); + } + } + if((lconfig = readconfig(argv[optind])) == NULL) { + flog(LOG_ERR, "could not read `%s'", argv[optind]); + exit(1); + } + signal(SIGCHLD, chldhandler); + signal(SIGHUP, sighandler); + signal(SIGPIPE, sighandler); while(1) { + if(reload) { + reloadconf(argv[optind]); + reload = 0; + } if((fd = recvreq(0, &req)) < 0) { + if(errno == EINTR) + continue; if(errno != 0) flog(LOG_ERR, "recvreq: %s", strerror(errno)); break;