+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)
+ 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);
+ } 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) {
+ 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);
+}
+