X-Git-Url: http://www.dolda2000.com/gitweb/?a=blobdiff_plain;f=lib%2Fcf.c;h=b809d331e051db3255c43b666997052743b2d8cc;hb=832efc978604c4779db85540e3c9c8ee8898d8c9;hp=82e5fba56d14e931563743f7534ee25bcbaf196f;hpb=b390f906b00859da9cf7c0ec7e0ca034010e5fce;p=ashd.git diff --git a/lib/cf.c b/lib/cf.c index 82e5fba..b809d33 100644 --- a/lib/cf.c +++ b/lib/cf.c @@ -22,6 +22,9 @@ #include #include #include +#include +#include +#include #include #ifdef HAVE_CONFIG_H @@ -36,15 +39,64 @@ #define CH_SOCKET 0 #define CH_FORK 1 -static int parsefile(struct cfstate *s, FILE *in) +struct stdchild { + int type; + char **argv; + char **envp; + int fd; + int agains; + time_t lastrep; +}; + +static int parsefile(struct cfstate *s, FILE *in); +static void stdmerge(struct child *old, struct child *new); +static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *idata); +static void stddestroy(struct child *ch); + +static int doinclude(struct cfstate *s, char *spec) { - int i, o, ret; + int rv, i; + FILE *inc; glob_t globm; + char *fbk, *dir, *fspec; + + rv = 0; + fbk = s->file; + if(spec[0] == '/') { + fspec = spec; + } else { + dir = sstrdup(fbk); + fspec = sprintf3("%s/%s", dirname(dir), spec); + free(dir); + } + if(glob(fspec, 0, NULL, &globm)) + return(0); + for(i = 0; i < globm.gl_pathc; i++) { + if((inc = fopen(globm.gl_pathv[i], "r")) != NULL) { + s->file = globm.gl_pathv[i]; + if(parsefile(s, inc)) { + fclose(inc); + rv = 1; + goto out; + } + fclose(inc); + inc = NULL; + } + } + +out: + globfree(&globm); + s->file = fbk; + return(rv); +} + +static int parsefile(struct cfstate *s, FILE *in) +{ + int i; char line[1024]; int eof, argc; int ind, indst[80], indl; - char *p, **w, *fbk; - FILE *inc; + char *p, **w; s->lno = 0; indst[indl = 0] = 0; @@ -55,11 +107,13 @@ static int parsefile(struct cfstate *s, FILE *in) line[0] = 0; } s->lno++; - for(p = line + strlen(line) - 1; p >= line; p--) { - if(isspace(*p)) - *p = 0; - else - break; + if(line[0]) { + for(p = line + strlen(line) - 1; p >= line; p--) { + if(isspace(*p)) + *p = 0; + else + break; + } } for(ind = 0, p = line; *p; p++) { if(*p == ' ') { @@ -114,26 +168,13 @@ static int parsefile(struct cfstate *s, FILE *in) if(indl == 0) { if(!strcmp(w[0], "include")) { - fbk = s->file; for(i = 1; i < argc; i++) { - if((ret = glob(w[i], 0, NULL, &globm)) == 0) { - for(o = 0; o < globm.gl_pathc; o++) { - if((inc = fopen(globm.gl_pathv[o], "r")) != NULL) { - s->file = globm.gl_pathv[o]; - if(parsefile(s, inc)) { - fclose(inc); - globfree(&globm); - freeca(w); - return(1); - } - fclose(inc); - } - } - globfree(&globm); + if(doinclude(s, w[i])) { + freeca(w); + return(1); } } freeca(w); - s->file = fbk; continue; } } @@ -194,28 +235,66 @@ void freecfparser(struct cfstate *s) free(s); } -static struct child *newchild(char *name, int type) +char *findstdconf(char *name) +{ + char *home, *path, *p, *p2, *t; + + if((home = getenv("HOME")) != NULL) { + if(!access(t = sprintf2("%s/.ashd/etc/%s", home, name), R_OK)) + return(t); + free(t); + } + if((path = getenv("PATH")) != NULL) { + path = sstrdup(path); + for(p = strtok(path, ":"); p != NULL; p = strtok(NULL, ":")) { + if((p2 = strrchr(p, '/')) == NULL) + continue; + *p2 = 0; + if(!access(t = sprintf2("%s/etc/%s", p, name), R_OK)) { + free(path); + return(t); + } + free(t); + } + free(path); + } + return(NULL); +} + +struct child *newchild(char *name, struct chandler *iface, void *pdata) { struct child *ch; omalloc(ch); ch->name = sstrdup(name); - ch->type = type; - ch->fd = -1; + ch->iface = iface; + ch->pdata = pdata; return(ch); } void freechild(struct child *ch) { - if(ch->fd != -1) - close(ch->fd); + if(ch->iface->destroy != NULL) + ch->iface->destroy(ch); if(ch->name != NULL) free(ch->name); - if(ch->argv != NULL) - freeca(ch->argv); free(ch); } +void mergechildren(struct child *dst, struct child *src) +{ + struct child *ch1, *ch2; + + for(ch1 = dst; ch1 != NULL; ch1 = ch1->next) { + for(ch2 = src; ch2 != NULL; ch2 = ch2->next) { + if(ch1->iface->merge && !strcmp(ch1->name, ch2->name)) { + ch1->iface->merge(ch1, ch2); + break; + } + } + } +} + void skipcfblock(struct cfstate *s) { char **w; @@ -227,9 +306,160 @@ void skipcfblock(struct cfstate *s) } } +static struct chandler stdhandler = { + .handle = stdhandle, + .merge = stdmerge, + .destroy = stddestroy, +}; + +static char **expandargs(struct stdchild *sd) +{ + int i; + char **ret, *p, *p2, *p3, *np, *env; + struct charbuf exp; + + ret = szmalloc(sizeof(*ret) * (calen(sd->argv) + 1)); + bufinit(exp); + for(i = 0; sd->argv[i] != NULL; i++) { + if((p = strchr(sd->argv[i], '$')) == NULL) { + ret[i] = sstrdup(sd->argv[i]); + } else { + exp.d = 0; + for(p2 = sd->argv[i]; p != NULL; p2 = np, p = strchr(np, '$')) { + bufcat(exp, p2, p - p2); + if(p[1] == '{') { + if((p3 = strchr((p += 2), '}')) == NULL) { + np = p; + break; + } + np = p3 + 1; + } else { + for(p3 = ++p; *p3; p3++) { + if(!(((*p3 >= 'a') && (*p3 <= 'z')) || + ((*p3 >= 'A') && (*p3 <= 'Z')) || + ((*p3 >= '0') && (*p3 <= '9')) || + (*p3 == '_'))) { + break; + } + } + np = p3; + } + char temp[(p3 - p) + 1]; + memcpy(temp, p, p3 - p); + temp[p3 - p] = 0; + if((env = getenv(temp)) != NULL) + bufcatstr(exp, env); + } + bufcatstr2(exp, np); + ret[i] = sstrdup(exp.b); + } + } + ret[i] = NULL; + buffree(exp); + return(ret); +} + +struct sidata { + struct stdchild *sd; + void (*sinit)(void *); + void *sdata; +}; + +static void stdinit(void *data) +{ + struct sidata *d = data; + int i; + + for(i = 0; d->sd->envp[i]; i += 2) + putenv(sprintf2("%s=%s", d->sd->envp[i], d->sd->envp[i + 1])); + if(d->sinit != NULL) + d->sinit(d->sdata); +} + +static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *sdata) +{ + struct stdchild *sd = ch->pdata; + int serr; + char **args; + struct sidata idat; + + if(sd->type == CH_SOCKET) { + idat = (struct sidata) {.sd = sd, .sinit = chinit, .sdata = sdata}; + if(sd->fd < 0) { + args = expandargs(sd); + sd->fd = stdmkchild(args, stdinit, &idat); + freeca(args); + } + if(sendreq2(sd->fd, req, fd, MSG_NOSIGNAL | MSG_DONTWAIT)) { + serr = errno; + if((serr == EPIPE) || (serr == ECONNRESET)) { + /* Assume that the child has crashed and restart it. */ + close(sd->fd); + args = expandargs(sd); + sd->fd = stdmkchild(args, stdinit, &idat); + freeca(args); + if(!sendreq2(sd->fd, req, fd, MSG_NOSIGNAL | MSG_DONTWAIT)) + goto ok; + serr = errno; + } + if(serr == EAGAIN) { + if(sd->agains++ == 0) { + flog(LOG_WARNING, "request to child %s denied due to buffer overload", ch->name); + sd->lastrep = time(NULL); + } + } else { + flog(LOG_ERR, "could not pass on request to child %s: %s", ch->name, strerror(serr)); + close(sd->fd); + sd->fd = -1; + } + return(-1); + } + ok: + if((sd->agains > 0) && ((time(NULL) - sd->lastrep) > 10)) { + flog(LOG_WARNING, "%i requests to child %s were denied due to buffer overload", sd->agains, ch->name); + sd->agains = 0; + } + } else if(sd->type == CH_FORK) { + args = expandargs(sd); + if(stdforkserve(args, req, fd, chinit, sdata) < 0) { + freeca(args); + return(-1); + } + freeca(args); + } + return(0); +} + +static void stdmerge(struct child *dst, struct child *src) +{ + struct stdchild *od, *nd; + + if(src->iface == &stdhandler) { + nd = dst->pdata; + od = src->pdata; + nd->fd = od->fd; + od->fd = -1; + } +} + +static void stddestroy(struct child *ch) +{ + struct stdchild *d = ch->pdata; + + if(d->fd >= 0) + close(d->fd); + if(d->argv) + freeca(d->argv); + if(d->envp) + freeca(d->envp); + free(d); +} + struct child *parsechild(struct cfstate *s) { struct child *ch; + struct stdchild *d; + struct charvbuf envbuf; int i; int sl; @@ -241,7 +471,8 @@ struct child *parsechild(struct cfstate *s) skipcfblock(s); return(NULL); } - ch = newchild(s->argv[1], CH_SOCKET); + ch = newchild(s->argv[1], &stdhandler, omalloc(d)); + d->type = CH_SOCKET; } else if(!strcmp(s->argv[0], "fchild")) { s->expstart = 1; if(s->argc < 2) { @@ -249,11 +480,14 @@ struct child *parsechild(struct cfstate *s) skipcfblock(s); return(NULL); } - ch = newchild(s->argv[1], CH_FORK); + ch = newchild(s->argv[1], &stdhandler, omalloc(d)); + d->type = CH_FORK; } else { return(NULL); } + d->fd = -1; + bufinit(envbuf); while(1) { getcfline(s); if(!strcmp(s->argv[0], "exec")) { @@ -261,16 +495,25 @@ struct child *parsechild(struct cfstate *s) flog(LOG_WARNING, "%s:%i: too few parameters to `exec'", s->file, s->lno); continue; } - ch->argv = szmalloc(sizeof(*ch->argv) * s->argc); + d->argv = szmalloc(sizeof(*d->argv) * s->argc); for(i = 0; i < s->argc - 1; i++) - ch->argv[i] = sstrdup(s->argv[i + 1]); + d->argv[i] = sstrdup(s->argv[i + 1]); + } else if(!strcmp(s->argv[0], "env")) { + if(s->argc < 3) { + flog(LOG_WARNING, "%s:%i: too few parameters to `env'", s->file, s->lno); + continue; + } + bufadd(envbuf, sstrdup(s->argv[1])); + bufadd(envbuf, sstrdup(s->argv[2])); } else if(!strcmp(s->argv[0], "end") || !strcmp(s->argv[0], "eof")) { break; } else { flog(LOG_WARNING, "%s:%i: unknown directive `%s' in child declaration", s->file, s->lno, s->argv[0]); } } - if(ch->argv == NULL) { + bufadd(envbuf, NULL); + d->envp = envbuf.b; + if(d->argv == NULL) { flog(LOG_WARNING, "%s:%i: missing `exec' in child declaration %s", s->file, sl, ch->name); freechild(ch); return(NULL); @@ -278,27 +521,7 @@ struct child *parsechild(struct cfstate *s) return(ch); } -int childhandle(struct child *ch, struct hthead *req, int fd) +int childhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *idata) { - if(ch->type == CH_SOCKET) { - if(ch->fd < 0) - ch->fd = stdmkchild(ch->argv); - if(sendreq(ch->fd, req, fd)) { - if(errno == EPIPE) { - /* Assume that the child has crashed and restart it. */ - close(ch->fd); - ch->fd = stdmkchild(ch->argv); - if(!sendreq(ch->fd, req, fd)) - return(0); - } - flog(LOG_ERR, "could not pass on request to child %s: %s", ch->name, strerror(errno)); - close(ch->fd); - ch->fd = -1; - return(-1); - } - } else if(ch->type == CH_FORK) { - if(stdforkserve(ch->argv, req, fd) < 0) - return(-1); - } - return(0); + return(ch->iface->handle(ch, req, fd, chinit, idata)); }