commit 37f7dbf5f09ff1eb1e37d380b32533e876325ef2 from: Stefan Sperling date: Sat Sep 06 13:34:00 2025 UTC move gotwebd fcgi record parsing into a dedicated process under pledge "stdio" ok op@ commit - 6f4eae69642d0a78da926cf32b3639e883f5fe09 commit + 37f7dbf5f09ff1eb1e37d380b32533e876325ef2 blob - 58a88e7f43f9c77ff11bef8420d7ec926c4ffb56 blob + 2446e2dde7afc86c2e450ef5c9845b2bd569261f --- gotwebd/fcgi.c +++ gotwebd/fcgi.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -41,10 +42,14 @@ #include "log.h" #include "tmpl.h" -size_t fcgi_parse_record(uint8_t *, size_t, struct request *); -void fcgi_parse_begin_request(uint8_t *, uint16_t, struct request *, +static void fcgi_sighdlr(int, short, void *); +static void fcgi_shutdown(void); +static void fcgi_launch(struct gotwebd *); + +void fcgi_parse_record(struct gotwebd_fcgi_record *); +int fcgi_parse_begin_request(uint8_t *, uint16_t, struct request *, uint16_t); -void fcgi_parse_params(uint8_t *, uint16_t, struct request *, uint16_t); +int fcgi_parse_params(uint8_t *, uint16_t, struct gotwebd_fcgi_params *); int fcgi_send_response(struct request *, int, const void *, size_t); void dump_fcgi_request_body(const char *, struct fcgi_record_header *); @@ -54,205 +59,127 @@ void dump_fcgi_begin_request_body(const char *, void dump_fcgi_end_request_body(const char *, struct fcgi_end_request_body *); -extern int cgi_inflight; extern struct requestlist requests; -void -fcgi_request(int fd, short events, void *arg) +static void +fcgi_shutdown(void) { - struct request *c = arg; - ssize_t n; - size_t parsed = 0; - - n = read(fd, c->buf + c->buf_pos + c->buf_len, - FCGI_RECORD_SIZE - c->buf_pos - c->buf_len); - - switch (n) { - case -1: - switch (errno) { - case EINTR: - case EAGAIN: - event_add(&c->ev, NULL); - return; - default: - goto fail; - } - break; - case 0: - if (c->client_status == CLIENT_CONNECT) { - log_warnx("client %u closed connection too early", - c->request_id); - goto fail; - } - return; - default: - break; + imsgbuf_clear(&gotwebd_env->iev_parent->ibuf); + free(gotwebd_env->iev_parent); + if (gotwebd_env->iev_server) { + imsgbuf_clear(&gotwebd_env->iev_server->ibuf); + free(gotwebd_env->iev_server); } - c->buf_len += n; + free(gotwebd_env); - /* - * Parse the records as they are received. Per the FastCGI - * specification, the server need only receive the FastCGI - * parameter records in full; it is free to begin execution - * at that point, which is what happens here. - */ - do { - parsed = fcgi_parse_record(c->buf + c->buf_pos, c->buf_len, c); - - /* - * When we start to actually process the entry, we - * send the request to the gotweb process, so we're - * done. - */ - if (c->client_status == CLIENT_REQUEST) { - fcgi_cleanup_request(c); - return; - } - - if (parsed != 0) { - c->buf_pos += parsed; - c->buf_len -= parsed; - } - - /* drop the parsed record */ - if (parsed != 0 && c->buf_len > 0) { - memmove(c->buf, c->buf + c->buf_pos, c->buf_len); - c->buf_pos = 0; - } - } while (parsed > 0 && c->buf_len > 0); - - event_add(&c->ev, NULL); - return; -fail: - fcgi_cleanup_request(c); + exit(0); } -size_t -fcgi_parse_record(uint8_t *buf, size_t n, struct request *c) +static void +fcgi_sighdlr(int sig, short event, void *arg) { - struct fcgi_record_header *h; - - if (n < sizeof(struct fcgi_record_header)) - return 0; - - h = (struct fcgi_record_header*) buf; - - dump_fcgi_record_header("", h); - - if (n < sizeof(struct fcgi_record_header) + ntohs(h->content_len) - + h->padding_len) - return 0; - - dump_fcgi_request_body("", h); - - if (h->version != 1) - log_warn("wrong version"); - - switch (h->type) { - case FCGI_BEGIN_REQUEST: - fcgi_parse_begin_request(buf + - sizeof(struct fcgi_record_header), - ntohs(h->content_len), c, ntohs(h->id)); + switch (sig) { + case SIGHUP: + log_info("%s: ignoring SIGHUP", __func__); break; - case FCGI_PARAMS: - fcgi_parse_params(buf + sizeof(struct fcgi_record_header), - ntohs(h->content_len), c, ntohs(h->id)); + case SIGPIPE: + log_info("%s: ignoring SIGPIPE", __func__); break; - case FCGI_STDIN: - return 0; - case FCGI_ABORT_REQUEST: - fcgi_create_end_record(c); - fcgi_cleanup_request(c); - return 0; + case SIGUSR1: + log_info("%s: ignoring SIGUSR1", __func__); + break; + case SIGCHLD: + break; + case SIGINT: + case SIGTERM: + fcgi_shutdown(); + break; default: - log_warn("unimplemented type %d", h->type); + log_warn("unexpected signal %d", sig); break; } - - return (sizeof(struct fcgi_record_header) + ntohs(h->content_len) - + h->padding_len); } -void -fcgi_parse_begin_request(uint8_t *buf, uint16_t n, - struct request *c, uint16_t id) +static void +send_parsed_params(struct gotwebd_fcgi_params *params) { - /* XXX -- FCGI_CANT_MPX_CONN */ - if (c->request_started) { - log_warn("unexpected FCGI_BEGIN_REQUEST, ignoring"); - return; - } + struct gotwebd *env = gotwebd_env; - if (n != sizeof(struct fcgi_begin_request_body)) { - log_warn("wrong size %d != %lu", n, - sizeof(struct fcgi_begin_request_body)); - return; - } - - c->request_started = 1; - c->id = id; + if (imsg_compose_event(env->iev_server, GOTWEBD_IMSG_FCGI_PARAMS, + GOTWEBD_PROC_SERVER, -1, -1, params, sizeof(*params)) == -1) + log_warn("imsg_compose_event"); } static void -process_request(struct request *c) +abort_request(uint32_t request_id) { struct gotwebd *env = gotwebd_env; - struct imsgev *iev_gotweb; - int ret, i; - struct request ic; - memcpy(&ic, c, sizeof(ic)); - - /* Don't leak pointers from our address space to another process. */ - ic.sock = NULL; - ic.srv = NULL; - ic.t = NULL; - ic.tp = NULL; - ic.buf = NULL; - ic.outbuf = NULL; - - /* Other process will use its own set of temp files. */ - for (i = 0; i < nitems(c->priv_fd); i++) - ic.priv_fd[i] = -1; - ic.fd = -1; - - /* Round-robin requests across gotweb processes. */ - iev_gotweb = &env->iev_gotweb[env->gotweb_cur]; - env->gotweb_cur = (env->gotweb_cur + 1) % env->prefork; - - ret = imsg_compose_event(iev_gotweb, GOTWEBD_IMSG_REQ_PROCESS, - GOTWEBD_PROC_SERVER, -1, c->fd, &ic, sizeof(ic)); - if (ret == -1) { + if (imsg_compose_event(env->iev_server, GOTWEBD_IMSG_REQ_ABORT, + GOTWEBD_PROC_SERVER, -1, -1, &request_id, sizeof(request_id)) == -1) log_warn("imsg_compose_event"); - return; - } - c->fd = -1; - - c->client_status = CLIENT_REQUEST; } void -fcgi_parse_params(uint8_t *buf, uint16_t n, struct request *c, uint16_t id) +fcgi_parse_record(struct gotwebd_fcgi_record *rec) { - uint32_t name_len, val_len; - uint8_t *val; + struct fcgi_record_header *h; + uint8_t *record_body; + struct gotwebd_fcgi_params params = { 0 }; - if (!c->request_started) { - log_warn("FCGI_PARAMS without FCGI_BEGIN_REQUEST, ignoring"); + if (rec->record_len < sizeof(struct fcgi_record_header) || + rec->record_len > sizeof(rec->record)) { + log_warnx("invalid fcgi record size"); + abort_request(rec->request_id); return; } - if (c->id != id) { - log_warn("unexpected id, ignoring"); + h = (struct fcgi_record_header *)&rec->record[0]; + + dump_fcgi_record_header("", h); + + if (rec->record_len != sizeof(*h) + ntohs(h->content_len) + + h->padding_len) { + abort_request(rec->request_id); return; } - if (n == 0) { - process_request(c); + dump_fcgi_request_body("", h); + + if (h->version != 1) { + log_warn("wrong fcgi header version: %u", h->version); + abort_request(rec->request_id); return; } + record_body = &rec->record[sizeof(*h)]; + switch (h->type) { + case FCGI_PARAMS: + if (fcgi_parse_params(record_body, + ntohs(h->content_len), ¶ms) == -1) { + abort_request(rec->request_id); + break; + } + params.request_id = rec->request_id; + send_parsed_params(¶ms); + break; + default: + log_warn("unexpected fcgi type %d", h->type); + abort_request(rec->request_id); + break; + } +} + +int +fcgi_parse_params(uint8_t *buf, uint16_t n, struct gotwebd_fcgi_params *params) +{ + uint32_t name_len, val_len; + uint8_t *val; + + if (n == 0) + return 0; + while (n > 0) { if (buf[0] >> 7 == 0) { name_len = buf[0]; @@ -265,11 +192,11 @@ fcgi_parse_params(uint8_t *buf, uint16_t n, struct req n -= 4; buf += 4; } else - return; + return -1; } if (n == 0) - return; + return -1; if (buf[0] >> 7 == 0) { val_len = buf[0]; @@ -283,50 +210,45 @@ fcgi_parse_params(uint8_t *buf, uint16_t n, struct req n -= 4; buf += 4; } else - return; + return -1; } if (n < name_len + val_len) - return; + return -1; val = buf + name_len; if (val_len < MAX_QUERYSTRING && name_len == 12 && strncmp(buf, "QUERY_STRING", 12) == 0) { - memcpy(c->querystring, val, val_len); - c->querystring[val_len] = '\0'; + /* TODO: parse querystring here */ + memcpy(params->querystring, val, val_len); + params->querystring[val_len] = '\0'; } if (val_len < MAX_DOCUMENT_URI && name_len == 12 && strncmp(buf, "DOCUMENT_URI", 12) == 0) { - memcpy(c->document_uri, val, val_len); - c->document_uri[val_len] = '\0'; + memcpy(params->document_uri, val, val_len); + params->document_uri[val_len] = '\0'; } if (val_len < MAX_SERVER_NAME && name_len == 11 && strncmp(buf, "SERVER_NAME", 11) == 0) { - memcpy(c->server_name, val, val_len); - c->server_name[val_len] = '\0'; + memcpy(params->server_name, val, val_len); + params->server_name[val_len] = '\0'; } if (name_len == 5 && strncmp(buf, "HTTPS", 5) == 0) - c->https = 1; + params->https = 1; buf += name_len + val_len; n -= name_len - val_len; } -} -void -fcgi_timeout(int fd, short events, void *arg) -{ - struct request *c = arg; - log_warnx("request %u has timed out", c->request_id); - fcgi_cleanup_request((struct request*) arg); + return 0; } static int @@ -458,8 +380,6 @@ fcgi_create_end_record(struct request *c) void fcgi_cleanup_request(struct request *c) { - cgi_inflight--; - if (evtimer_initialized(&c->tmo)) evtimer_del(&c->tmo); if (event_initialized(&c->ev)) @@ -514,4 +434,221 @@ dump_fcgi_end_request_body(const char *p, struct fcgi_ { log_debug("%sappStatus: %d", p, ntohl(b->app_status)); log_debug("%sprotocolStatus: %d", p, b->protocol_status); +} + +static void +fcgi_launch(struct gotwebd *env) +{ + if (env->iev_server == NULL) + fatalx("server process not connected"); +#ifndef PROFILE + if (pledge("stdio", NULL) == -1) + fatal("pledge"); +#endif + event_add(&env->iev_server->ev, NULL); +} + +static struct gotwebd_fcgi_record * +recv_record(struct imsg *imsg) +{ + struct gotwebd_fcgi_record *record; + + record = calloc(1, sizeof(*record)); + if (record == NULL) { + log_warn("calloc"); + return NULL; + } + + if (imsg_get_data(imsg, record, sizeof(*record)) == -1) { + log_warn("imsg_get_data"); + free(record); + return NULL; + } + + return record; +} + +static void +fcgi_dispatch_server(int fd, short event, void *arg) +{ + struct imsgev *iev = arg; + struct imsgbuf *ibuf; + struct imsg imsg; + ssize_t n; + int shut = 0; + + ibuf = &iev->ibuf; + + if (event & EV_READ) { + if ((n = imsgbuf_read(ibuf)) == -1) + fatal("imsgbuf_read error"); + if (n == 0) /* Connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if (imsgbuf_write(ibuf) == -1) + fatal("imsgbuf_write"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + if (n == 0) /* No more messages. */ + break; + + switch (imsg.hdr.type) { + case GOTWEBD_IMSG_FCGI_PARSE_PARAMS: { + struct gotwebd_fcgi_record *rec; + + rec = recv_record(&imsg); + if (rec) { + fcgi_parse_record(rec); + free(rec); + } + break; + } + default: + fatalx("%s: unknown imsg type %d", __func__, + imsg.hdr.type); + } + + imsg_free(&imsg); + } + + if (!shut) + imsg_event_add(iev); + else { + /* This pipe is dead. Remove its event handler */ + event_del(&iev->ev); + event_loopexit(NULL); + } +} + +static void +recv_server_pipe(struct gotwebd *env, struct imsg *imsg) +{ + struct imsgev *iev; + int fd; + + if (env->iev_server != NULL) { + log_warn("server pipe already received"); + return; + } + + fd = imsg_get_fd(imsg); + if (fd == -1) + fatalx("invalid server pipe fd"); + + iev = calloc(1, sizeof(*iev)); + if (iev == NULL) + fatal("calloc"); + + if (imsgbuf_init(&iev->ibuf, fd) == -1) + fatal("imsgbuf_init"); + imsgbuf_allow_fdpass(&iev->ibuf); + imsgbuf_set_maxsize(&iev->ibuf, sizeof(struct gotwebd_fcgi_record)); + + iev->handler = fcgi_dispatch_server; + iev->data = iev; + event_set(&iev->ev, fd, EV_READ, fcgi_dispatch_server, iev); + imsg_event_add(iev); + + env->iev_server = iev; } + +static void +fcgi_dispatch_main(int fd, short event, void *arg) +{ + struct imsgev *iev = arg; + struct imsgbuf *ibuf; + struct imsg imsg; + struct gotwebd *env = gotwebd_env; + ssize_t n; + int shut = 0; + + ibuf = &iev->ibuf; + + if (event & EV_READ) { + if ((n = imsgbuf_read(ibuf)) == -1) + fatal("imsgbuf_read error"); + if (n == 0) /* Connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if (imsgbuf_write(ibuf) == -1) + fatal("imsgbuf_write"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + if (n == 0) /* No more messages. */ + break; + + switch (imsg.hdr.type) { + case GOTWEBD_IMSG_CFG_DONE: + config_getcfg(env, &imsg); + break; + case GOTWEBD_IMSG_CTL_PIPE: + recv_server_pipe(env, &imsg); + break; + case GOTWEBD_IMSG_CTL_START: + fcgi_launch(env); + break; + default: + fatalx("%s: unknown imsg type %d", __func__, + imsg.hdr.type); + } + + imsg_free(&imsg); + } + + if (!shut) + imsg_event_add(iev); + else { + /* This pipe is dead. Remove its event handler */ + event_del(&iev->ev); + event_loopexit(NULL); + } +} + +void +gotwebd_fcgi(struct gotwebd *env, int fd) +{ + struct event sighup, sigint, sigusr1, sigchld, sigterm; + struct event_base *evb; + + evb = event_init(); + + if ((env->iev_parent = malloc(sizeof(*env->iev_parent))) == NULL) + fatal("malloc"); + if (imsgbuf_init(&env->iev_parent->ibuf, fd) == -1) + fatal("imsgbuf_init"); + imsgbuf_allow_fdpass(&env->iev_parent->ibuf); + env->iev_parent->handler = fcgi_dispatch_main; + env->iev_parent->data = env->iev_parent; + event_set(&env->iev_parent->ev, fd, EV_READ, fcgi_dispatch_main, + env->iev_parent); + event_add(&env->iev_parent->ev, NULL); + + signal(SIGPIPE, SIG_IGN); + + signal_set(&sighup, SIGHUP, fcgi_sighdlr, env); + signal_add(&sighup, NULL); + signal_set(&sigint, SIGINT, fcgi_sighdlr, env); + signal_add(&sigint, NULL); + signal_set(&sigusr1, SIGUSR1, fcgi_sighdlr, env); + signal_add(&sigusr1, NULL); + signal_set(&sigchld, SIGCHLD, fcgi_sighdlr, env); + signal_add(&sigchld, NULL); + signal_set(&sigterm, SIGTERM, fcgi_sighdlr, env); + signal_add(&sigterm, NULL); + +#ifndef PROFILE + if (pledge("stdio recvfd", NULL) == -1) + fatal("pledge"); +#endif + event_dispatch(); + event_base_free(evb); + fcgi_shutdown(); +} blob - cc891f8eb78a1444fed0684e79e52a496a2008c7 blob + 5f2b1c6836e35e7f9ed602b0e1425472f331e026 --- gotwebd/gotweb.c +++ gotwebd/gotweb.c @@ -156,6 +156,18 @@ gotweb_get_socket(int sock_id) return NULL; } +static void +cleanup_request(struct request *c) +{ + uint32_t request_id = c->request_id; + + fcgi_cleanup_request(c); + + if (imsg_compose_event(gotwebd_env->iev_server, GOTWEBD_IMSG_REQ_ABORT, + GOTWEBD_PROC_GOTWEB, -1, -1, &request_id, sizeof(request_id)) == -1) + log_warn("imsg_compose_event"); +} + static struct request * recv_request(struct imsg *imsg) { @@ -211,14 +223,14 @@ recv_request(struct imsg *imsg) c->tp = template(c, fcgi_write, c->outbuf, GOTWEBD_CACHESIZE); if (c->tp == NULL) { log_warn("gotweb init template"); - fcgi_cleanup_request(c); + cleanup_request(c); return NULL; } c->sock = gotweb_get_socket(c->sock_id); if (c->sock == NULL) { log_warn("socket id '%d' not found", c->sock_id); - fcgi_cleanup_request(c); + cleanup_request(c); return NULL; } @@ -226,15 +238,15 @@ recv_request(struct imsg *imsg) error = gotweb_init_transport(&c->t); if (error) { log_warnx("gotweb init transport: %s", error->msg); - fcgi_cleanup_request(c); + cleanup_request(c); return NULL; } /* get the gotwebd server */ - srv = gotweb_get_server(c->server_name); + srv = gotweb_get_server(c->fcgi_params.server_name); if (srv == NULL) { - log_warnx("server '%s' not found", c->server_name); - fcgi_cleanup_request(c); + log_warnx("server '%s' not found", c->fcgi_params.server_name); + cleanup_request(c); return NULL; } c->srv = srv; @@ -262,7 +274,7 @@ gotweb_process_request(struct request *c) goto err; } c->t->qs = qs; - error = gotweb_parse_querystring(qs, c->querystring); + error = gotweb_parse_querystring(qs, c->fcgi_params.querystring); if (error) { log_warnx("%s: %s", __func__, error->msg); goto err; @@ -270,22 +282,23 @@ gotweb_process_request(struct request *c) /* Log the request. */ if (gotwebd_env->gotwebd_verbose > 0) { + struct gotwebd_fcgi_params *p = &c->fcgi_params; char *server_name = NULL; char *querystring = NULL; char *document_uri = NULL; - if (c->server_name[0] && - stravis(&server_name, c->server_name, VIS_SAFE) == -1) { + if (p->server_name[0] && + stravis(&server_name, p->server_name, VIS_SAFE) == -1) { log_warn("stravis"); server_name = NULL; } - if (c->querystring[0] && - stravis(&querystring, c->querystring, VIS_SAFE) == -1) { + if (p->querystring[0] && + stravis(&querystring, p->querystring, VIS_SAFE) == -1) { log_warn("stravis"); querystring = NULL; } - if (c->document_uri[0] && - stravis(&document_uri, c->document_uri, VIS_SAFE) == -1) { + if (p->document_uri[0] && + stravis(&document_uri, p->document_uri, VIS_SAFE) == -1) { log_warn("stravis"); document_uri = NULL; } @@ -1160,12 +1173,13 @@ int gotweb_render_absolute_url(struct request *c, struct gotweb_url *url) { struct template *tp = c->tp; - const char *proto = c->https ? "https" : "http"; + struct gotwebd_fcgi_params *p = &c->fcgi_params; + const char *proto = p->https ? "https" : "http"; if (tp_writes(tp, proto) == -1 || tp_writes(tp, "://") == -1 || - tp_htmlescape(tp, c->server_name) == -1 || - tp_htmlescape(tp, c->document_uri) == -1) + tp_htmlescape(tp, p->server_name) == -1 || + tp_htmlescape(tp, p->document_uri) == -1) return -1; return gotweb_render_url(c, url); @@ -1495,6 +1509,7 @@ gotweb_dispatch_server(int fd, short event, void *arg) c = recv_request(&imsg); if (c) { int request_id = c->request_id; + log_info("%u: %s: request %u", getpid(), __func__, c->request_id); if (gotweb_process_request(c) == -1) { log_warnx("request %u failed", request_id); @@ -1506,7 +1521,7 @@ gotweb_dispatch_server(int fd, short event, void *arg) } fcgi_create_end_record(c); - fcgi_cleanup_request(c); + cleanup_request(c); } break; default: blob - 68ffcc1638c3db490e1b14e4ab1194f8dc7776db blob + 361e94d9fc08626854f1c0e4d31af3be36bec935 --- gotwebd/gotwebd.c +++ gotwebd/gotwebd.c @@ -53,6 +53,7 @@ void gotwebd_configure_done(struct gotwebd *); void gotwebd_sighdlr(int sig, short event, void *arg); void gotwebd_shutdown(void); void gotwebd_dispatch_server(int, short, void *); +void gotwebd_dispatch_fcgi(int, short, void *); void gotwebd_dispatch_gotweb(int, short, void *); struct gotwebd *gotwebd_env; @@ -156,6 +157,56 @@ sockets_compose_main(struct gotwebd *env, uint32_t typ void gotwebd_dispatch_server(int fd, short event, void *arg) +{ + struct imsgev *iev = arg; + struct imsgbuf *ibuf; + struct imsg imsg; + struct gotwebd *env = gotwebd_env; + ssize_t n; + int shut = 0; + + ibuf = &iev->ibuf; + + if (event & EV_READ) { + if ((n = imsgbuf_read(ibuf)) == -1) + fatal("imsgbuf_read error"); + if (n == 0) /* Connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if (imsgbuf_write(ibuf) == -1) + fatal("imsgbuf_write"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + if (n == 0) /* No more messages. */ + break; + + switch (imsg.hdr.type) { + case GOTWEBD_IMSG_CFG_DONE: + gotwebd_configure_done(env); + break; + default: + fatalx("%s: unknown imsg type %d", __func__, + imsg.hdr.type); + } + + imsg_free(&imsg); + } + + if (!shut) + imsg_event_add(iev); + else { + /* This pipe is dead. Remove its event handler */ + event_del(&iev->ev); + event_loopexit(NULL); + } +} + +void +gotwebd_dispatch_fcgi(int fd, short event, void *arg) { struct imsgev *iev = arg; struct imsgbuf *ibuf; @@ -320,6 +371,9 @@ spawn_process(struct gotwebd *env, const char *argv0, if (asprintf(&s, "-S%d", env->prefork) == -1) fatal("asprintf"); argv[argc++] = s; + } else if (proc_type == GOTWEBD_PROC_FCGI) { + argv[argc++] = "-F"; + argv[argc++] = username; } else if (proc_type == GOTWEBD_PROC_GOTWEB) { char *s; @@ -388,7 +442,7 @@ main(int argc, char **argv) fatal("%s: calloc", __func__); config_init(env); - while ((ch = getopt(argc, argv, "D:dG:f:nS:vW:")) != -1) { + while ((ch = getopt(argc, argv, "D:dG:f:F:nS:vW:")) != -1) { switch (ch) { case 'D': if (cmdline_symset(optarg) < 0) @@ -409,6 +463,10 @@ main(int argc, char **argv) case 'f': conffile = optarg; break; + case 'F': + proc_type = GOTWEBD_PROC_FCGI; + gotwebd_username = optarg; + break; case 'n': no_action = 1; break; @@ -487,6 +545,22 @@ main(int argc, char **argv) sockets(env, GOTWEBD_SOCK_FILENO); return 1; + case GOTWEBD_PROC_FCGI: + setproctitle("fcgi"); + log_procinit("fcgi"); + + if (chroot(env->httpd_chroot) == -1) + fatal("chroot %s", env->httpd_chroot); + if (chdir("/") == -1) + fatal("chdir /"); + + if (setgroups(gotwebd_ngroups, gotwebd_groups) == -1 || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) + fatal("failed to drop privileges"); + + gotwebd_fcgi(env, GOTWEBD_SOCK_FILENO); + return 1; case GOTWEBD_PROC_GOTWEB: setproctitle("gotweb"); log_procinit("gotweb"); @@ -510,6 +584,11 @@ main(int argc, char **argv) env->iev_server = calloc(env->server_cnt, sizeof(*env->iev_server)); if (env->iev_server == NULL) fatal("calloc"); + + env->iev_fcgi = calloc(env->server_cnt, sizeof(*env->iev_fcgi)); + if (env->iev_fcgi == NULL) + fatal("calloc"); + env->iev_gotweb = calloc(env->prefork, sizeof(*env->iev_gotweb)); if (env->iev_gotweb == NULL) fatal("calloc"); @@ -518,12 +597,18 @@ main(int argc, char **argv) spawn_process(env, argv0, &env->iev_server[i], GOTWEBD_PROC_SERVER, gotwebd_username, gotwebd_dispatch_server); + + spawn_process(env, argv0, &env->iev_fcgi[i], + GOTWEBD_PROC_FCGI, gotwebd_username, + gotwebd_dispatch_fcgi); } + for (i = 0; i < env->prefork; ++i) { spawn_process(env, argv0, &env->iev_gotweb[i], GOTWEBD_PROC_GOTWEB, gotwebd_username, gotwebd_dispatch_gotweb); } + if (chdir("/") == -1) fatal("chdir /"); @@ -578,26 +663,38 @@ main(int argc, char **argv) static void connect_children(struct gotwebd *env) { - struct imsgev *iev1, *iev2; + struct imsgev *iev_server, *iev_fcgi, *iev_gotweb; int pipe[2]; int i, j; for (i = 0; i < env->server_cnt; i++) { - iev1 = &env->iev_server[i]; + iev_server = &env->iev_server[i]; + iev_fcgi = &env->iev_fcgi[i]; + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe) == -1) + fatal("socketpair"); + + if (send_imsg(iev_server, GOTWEBD_IMSG_CTL_PIPE, pipe[0], + NULL, 0)) + fatal("send_imsg"); + + if (send_imsg(iev_fcgi, GOTWEBD_IMSG_CTL_PIPE, pipe[1], + NULL, 0)) + fatal("send_imsg"); + for (j = 0; j < env->prefork; j++) { - iev2 = &env->iev_gotweb[j]; + iev_gotweb = &env->iev_gotweb[j]; if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe) == -1) fatal("socketpair"); - if (send_imsg(iev1, GOTWEBD_IMSG_CTL_PIPE, pipe[0], - NULL, 0)) + if (send_imsg(iev_server, GOTWEBD_IMSG_CTL_PIPE, + pipe[0], NULL, 0)) fatal("send_imsg"); - if (send_imsg(iev2, GOTWEBD_IMSG_CTL_PIPE, pipe[1], - NULL, 0)) + if (send_imsg(iev_gotweb, GOTWEBD_IMSG_CTL_PIPE, + pipe[1], NULL, 0)) fatal("send_imsg"); } } blob - b6e69183db3f973c8495e874e281a672de6f3f4c blob + 18fe22bae49e8cf885500524c79e5780dd102d28 --- gotwebd/gotwebd.h +++ gotwebd/gotwebd.h @@ -124,6 +124,7 @@ struct got_reflist_head; enum gotwebd_proc_type { GOTWEBD_PROC_PARENT, GOTWEBD_PROC_SERVER, + GOTWEBD_PROC_FCGI, GOTWEBD_PROC_GOTWEB, }; @@ -134,6 +135,9 @@ enum imsg_type { GOTWEBD_IMSG_CFG_DONE, GOTWEBD_IMSG_CTL_PIPE, GOTWEBD_IMSG_CTL_START, + GOTWEBD_IMSG_FCGI_PARSE_PARAMS, + GOTWEBD_IMSG_FCGI_PARAMS, + GOTWEBD_IMSG_REQ_ABORT, GOTWEBD_IMSG_REQ_PROCESS, }; @@ -237,6 +241,20 @@ enum socket_priv_fds { BLOB_FD_1, BLOB_FD_2, PRIV_FDS__MAX, +}; + +struct gotwebd_fcgi_record { + uint32_t request_id; + uint8_t record[FCGI_RECORD_SIZE]; + size_t record_len; +}; + +struct gotwebd_fcgi_params { + uint32_t request_id; + char querystring[MAX_QUERYSTRING]; + char document_uri[MAX_DOCUMENT_URI]; + char server_name[MAX_SERVER_NAME]; + int https; }; struct template; @@ -257,17 +275,14 @@ struct request { uint32_t request_id; uint8_t *buf; - size_t buf_pos; size_t buf_len; uint8_t *outbuf; - char querystring[MAX_QUERYSTRING]; - char document_uri[MAX_DOCUMENT_URI]; - char server_name[MAX_SERVER_NAME]; - int https; + struct gotwebd_fcgi_params fcgi_params; + int nparams; + int nparams_parsed; - uint8_t request_started; int client_status; }; TAILQ_HEAD(requestlist, request); @@ -325,6 +340,9 @@ TAILQ_HEAD(serverlist, server); enum client_action { CLIENT_CONNECT, + CLIENT_FCGI_BEGIN, + CLIENT_FCGI_PARAMS, + CLIENT_FCGI_STDIN, CLIENT_REQUEST, CLIENT_DISCONNECT, }; @@ -367,6 +385,7 @@ struct gotwebd { struct imsgev *iev_parent; struct imsgev *iev_server; + struct imsgev *iev_fcgi; struct imsgev *iev_gotweb; uint16_t prefork; @@ -501,11 +520,10 @@ int parse_config(const char *, struct gotwebd *); int cmdline_symset(char *); /* fcgi.c */ -void fcgi_request(int, short, void *); -void fcgi_timeout(int, short, void *); void fcgi_cleanup_request(struct request *); void fcgi_create_end_record(struct request *); int fcgi_write(void *, const void *, size_t); +void gotwebd_fcgi(struct gotwebd *, int); /* got_operations.c */ const struct got_error *got_gotweb_closefile(FILE *); blob - d5af2ffcb28eb41174cdfe8a016103fbb42d50da blob + c7e1ac5b684f3f72cb60e8a5260089c8262b08a9 --- gotwebd/pages.tmpl +++ gotwebd/pages.tmpl @@ -167,7 +167,7 @@ nextsep(char *s, char **t) struct server *srv = c->srv; struct querystring *qs = c->t->qs; struct gotweb_url u_path; - const char *prfx = c->document_uri; + const char *prfx = c->fcgi_params.document_uri; const char *css = srv->custom_css; memset(&u_path, 0, sizeof(u_path)); blob - a06e82cff88e1f0b56f21f10175579e66d777ca1 blob + 14e2eca5e33aa5640070e3a4b9ef8634b2c05f64 --- gotwebd/sockets.c +++ gotwebd/sockets.c @@ -79,12 +79,136 @@ static struct socket *sockets_conf_new_socket(struct g int cgi_inflight = 0; +/* Request hash table needs some spare room to avoid collisions. */ +struct requestlist requests[GOTWEBD_MAXCLIENTS * 4]; +static SIPHASH_KEY requests_hash_key; + +static void +requests_init(void) +{ + int i; + + arc4random_buf(&requests_hash_key, sizeof(requests_hash_key)); + + for (i = 0; i < nitems(requests); i++) + TAILQ_INIT(&requests[i]); +} + +static uint64_t +request_hash(uint32_t request_id) +{ + return SipHash24(&requests_hash_key, &request_id, sizeof(request_id)); +} + +static void +add_request(struct request *c) +{ + uint64_t slot = request_hash(c->request_id) % nitems(requests); + TAILQ_INSERT_HEAD(&requests[slot], c, entry); + client_cnt++; +} + +static void +del_request(struct request *c) +{ + uint64_t slot = request_hash(c->request_id) % nitems(requests); + TAILQ_REMOVE(&requests[slot], c, entry); + client_cnt--; +} + +static struct request * +find_request(uint32_t request_id) +{ + uint64_t slot; + struct request *c; + + slot = request_hash(request_id) % nitems(requests); + TAILQ_FOREACH(c, &requests[slot], entry) { + if (c->request_id == request_id) + return c; + } + + return NULL; +} + +static void +cleanup_request(struct request *c) +{ + cgi_inflight--; + + del_request(c); + + event_add(&c->sock->ev, NULL); + + if (evtimer_initialized(&c->tmo)) + evtimer_del(&c->tmo); + if (event_initialized(&c->ev)) + event_del(&c->ev); + if (c->fd != -1) + close(c->fd); + free(c->buf); + free(c); +} + +static void +request_timeout(int fd, short events, void *arg) +{ + struct request *c = arg; + + log_warnx("request %u has timed out", c->request_id); + cleanup_request(c); +} + +static void +requests_purge(void) +{ + uint64_t slot; + struct request *c; + + for (slot = 0; slot < nitems(requests); slot++) { + while (!TAILQ_EMPTY(&requests[slot])) { + c = TAILQ_FIRST(&requests[slot]); + cleanup_request(c); + } + } +} + +static uint32_t +get_request_id(void) +{ + int duplicate = 0; + uint32_t id; + + do { + id = arc4random(); + duplicate = (find_request(id) != NULL); + } while (duplicate || id == 0); + + return id; +} + +static void +request_done(struct request *c) +{ + /* + * If we have not yet handed the client off to gotweb.c we + * must send an FCGI end record ourselves. + */ + if (c->client_status > CLIENT_CONNECT && + c->client_status < CLIENT_REQUEST) + fcgi_create_end_record(c); + + cleanup_request(c); +} + void sockets(struct gotwebd *env, int fd) { struct event sighup, sigint, sigusr1, sigchld, sigterm; struct event_base *evb; + requests_init(); + evb = event_init(); sockets_rlimit(-1); @@ -198,6 +322,8 @@ sockets_launch(struct gotwebd *env) struct socket *sock; int i, have_unix = 0, have_inet = 0; + if (env->iev_fcgi == NULL) + fatalx("fcgi process not connected"); if (env->gotweb_pending != 0) fatal("gotweb process not connected"); @@ -250,9 +376,27 @@ sockets_launch(struct gotwebd *env) fatal("pledge"); } #endif + event_add(&env->iev_fcgi->ev, NULL); for (i = 0; i < env->prefork; i++) event_add(&env->iev_gotweb[i].ev, NULL); +} +static void +abort_request(struct imsg *imsg) +{ + struct request *c; + uint32_t request_id; + + if (imsg_get_data(imsg, &request_id, sizeof(request_id)) == -1) { + log_warn("imsg_get_data"); + return; + } + + c = find_request(request_id); + if (c == NULL) + return; + + request_done(c); } static void @@ -284,6 +428,9 @@ server_dispatch_gotweb(int fd, short event, void *arg) break; switch (imsg.hdr.type) { + case GOTWEBD_IMSG_REQ_ABORT: + abort_request(&imsg); + break; default: fatalx("%s: unknown imsg type %d", __func__, imsg.hdr.type); @@ -320,6 +467,7 @@ recv_gotweb_pipe(struct gotwebd *env, struct imsg *ims if (imsgbuf_init(&iev->ibuf, fd) == -1) fatal("imsgbuf_init"); imsgbuf_allow_fdpass(&iev->ibuf); + imsgbuf_set_maxsize(&iev->ibuf, sizeof(struct gotwebd_fcgi_record)); iev->handler = server_dispatch_gotweb; iev->data = iev; @@ -327,9 +475,192 @@ recv_gotweb_pipe(struct gotwebd *env, struct imsg *ims imsg_event_add(iev); env->gotweb_pending--; +} + +static int +process_request(struct request *c) +{ + struct gotwebd *env = gotwebd_env; + struct imsgev *iev_gotweb; + int ret, i; + struct request ic; + + memcpy(&ic, c, sizeof(ic)); + + /* Don't leak pointers from our address space to another process. */ + ic.sock = NULL; + ic.srv = NULL; + ic.t = NULL; + ic.tp = NULL; + ic.buf = NULL; + ic.outbuf = NULL; + + /* Other process will use its own set of temp files. */ + for (i = 0; i < nitems(c->priv_fd); i++) + ic.priv_fd[i] = -1; + ic.fd = -1; + + /* Round-robin requests across gotweb processes. */ + iev_gotweb = &env->iev_gotweb[env->gotweb_cur]; + env->gotweb_cur = (env->gotweb_cur + 1) % env->prefork; + + ret = imsg_compose_event(iev_gotweb, GOTWEBD_IMSG_REQ_PROCESS, + GOTWEBD_PROC_SERVER, -1, c->fd, &ic, sizeof(ic)); + if (ret == -1) { + log_warn("imsg_compose_event"); + return -1; + } + + c->fd = -1; + c->client_status = CLIENT_REQUEST; + return 0; +} + +static void +recv_parsed_params(struct imsg *imsg) +{ + struct gotwebd_fcgi_params params, *p; + struct request *c; + + if (imsg_get_data(imsg, ¶ms, sizeof(params)) == -1) { + log_warn("imsg_get_data"); + return; + } + + c = find_request(params.request_id); + if (c == NULL) + return; + + if (c->client_status > CLIENT_FCGI_STDIN) + return; + + if (c->client_status < CLIENT_FCGI_PARAMS) + goto fail; + + p = &c->fcgi_params; + + if (params.querystring[0] != '\0' && + strlcpy(p->querystring, params.querystring, + sizeof(p->querystring)) >= sizeof(p->querystring)) { + log_warnx("querystring too long"); + goto fail; + } + + if (params.document_uri[0] != '\0' && + strlcpy(p->document_uri, params.document_uri, + sizeof(p->document_uri)) >= sizeof(p->document_uri)) { + log_warnx("document uri too long"); + goto fail; + } + + if (params.server_name[0] != '\0' && + strlcpy(p->server_name, params.server_name, + sizeof(p->server_name)) >= sizeof(p->server_name)) { + log_warnx("server name too long"); + goto fail; + } + + if (params.https && !p->https) + p->https = 1; + + c->nparams_parsed++; + + if (c->client_status == CLIENT_FCGI_STDIN && + c->nparams_parsed >= c->nparams) { + if (process_request(c) == -1) + goto fail; + } + + return; +fail: + request_done(c); +} + +static void +server_dispatch_fcgi(int fd, short event, void *arg) +{ + struct imsgev *iev = arg; + struct imsgbuf *ibuf; + struct imsg imsg; + ssize_t n; + int shut = 0; + + ibuf = &iev->ibuf; + + if (event & EV_READ) { + if ((n = imsgbuf_read(ibuf)) == -1) + fatal("imsgbuf_read error"); + if (n == 0) /* Connection closed */ + shut = 1; + } + if (event & EV_WRITE) { + if (imsgbuf_write(ibuf) == -1) + fatal("imsgbuf_write"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + if (n == 0) /* No more messages. */ + break; + + switch (imsg.hdr.type) { + case GOTWEBD_IMSG_FCGI_PARAMS: + recv_parsed_params(&imsg); + break; + case GOTWEBD_IMSG_REQ_ABORT: + abort_request(&imsg); + break; + default: + fatalx("%s: unknown imsg type %d", __func__, + imsg.hdr.type); + } + + imsg_free(&imsg); + } + + if (!shut) + imsg_event_add(iev); + else { + /* This pipe is dead. Remove its event handler */ + event_del(&iev->ev); + event_loopexit(NULL); + } } static void +recv_fcgi_pipe(struct gotwebd *env, struct imsg *imsg) +{ + struct imsgev *iev; + int fd; + + if (env->iev_fcgi != NULL) { + log_warn("fcgi pipe already received"); + return; + } + + fd = imsg_get_fd(imsg); + if (fd == -1) + fatalx("invalid gotweb pipe fd"); + + iev = calloc(1, sizeof(*iev)); + if (iev == NULL) + fatal("calloc"); + + if (imsgbuf_init(&iev->ibuf, fd) == -1) + fatal("imsgbuf_init"); + imsgbuf_allow_fdpass(&iev->ibuf); + imsgbuf_set_maxsize(&iev->ibuf, sizeof(struct gotwebd_fcgi_record)); + + iev->handler = server_dispatch_fcgi; + iev->data = iev; + event_set(&iev->ev, fd, EV_READ, server_dispatch_fcgi, iev); + imsg_event_add(iev); + + env->iev_fcgi = iev; +} + +static void sockets_dispatch_main(int fd, short event, void *arg) { struct imsgev *iev = arg; @@ -369,7 +700,10 @@ sockets_dispatch_main(int fd, short event, void *arg) config_getcfg(env, &imsg); break; case GOTWEBD_IMSG_CTL_PIPE: - recv_gotweb_pipe(env, &imsg); + if (env->iev_fcgi == NULL) + recv_fcgi_pipe(env, &imsg); + else + recv_gotweb_pipe(env, &imsg); break; case GOTWEBD_IMSG_CTL_START: sockets_launch(env); @@ -446,11 +780,18 @@ sockets_shutdown(void) free(sock); } + requests_purge(); + imsgbuf_clear(&gotwebd_env->iev_parent->ibuf); free(gotwebd_env->iev_parent); + + imsgbuf_clear(&gotwebd_env->iev_fcgi->ibuf); + free(gotwebd_env->iev_fcgi); + for (i = 0; i < gotwebd_env->prefork; i++) imsgbuf_clear(&gotwebd_env->iev_gotweb[i].ibuf); free(gotwebd_env->iev_gotweb); + free(gotwebd_env); exit(0); @@ -596,6 +937,158 @@ sockets_accept_paused(int fd, short events, void *arg) struct socket *sock = (struct socket *)arg; event_add(&sock->ev, NULL); +} + +static int +parse_params(struct request *c, uint8_t *record, size_t record_len) +{ + struct gotwebd *env = gotwebd_env; + struct gotwebd_fcgi_record rec; + int ret; + + memset(&rec, 0, sizeof(rec)); + + memcpy(rec.record, record, record_len); + rec.record_len = record_len; + rec.request_id = c->request_id; + + ret = imsg_compose_event(env->iev_fcgi, + GOTWEBD_IMSG_FCGI_PARSE_PARAMS, + GOTWEBD_PROC_SERVER, -1, -1, &rec, sizeof(rec)); + if (ret == -1) + log_warn("imsg_compose_event"); + + return ret; +} + +static void +read_fcgi_records(int fd, short events, void *arg) +{ + struct request *c = arg; + ssize_t n; + struct fcgi_record_header h; + size_t record_len; + + n = read(fd, c->buf + c->buf_len, FCGI_RECORD_SIZE - c->buf_len); + log_info("%u: %s: request %u, read=%zd", getpid(), __func__, c->request_id, n); + + switch (n) { + case -1: + switch (errno) { + case EINTR: + case EAGAIN: + goto more; + default: + goto fail; + } + break; + case 0: + if (c->client_status < CLIENT_FCGI_STDIN) { + log_warnx("client %u closed connection too early", + c->request_id); + goto fail; + } + return; + default: + break; + } + + c->buf_len += n; + + while (c->buf_len >= sizeof(h)) { + memcpy(&h, c->buf, sizeof(h)); + + record_len = sizeof(h) + ntohs(h.content_len) + h.padding_len; + if (record_len > FCGI_RECORD_SIZE) { + log_warnx("FGI record length too large"); + goto fail; + } + + if (c->buf_len < record_len) + goto more; + + switch (h.type) { + case FCGI_BEGIN_REQUEST: + if (c->client_status >= CLIENT_FCGI_BEGIN) { + log_warnx("unexpected FCGI_BEGIN_REQUEST"); + goto fail; + } + + if (ntohs(h.content_len) != + sizeof(struct fcgi_begin_request_body)) { + log_warnx("wrong begin request size %u != %zu", + ntohs(h.content_len), + sizeof(struct fcgi_begin_request_body)); + goto fail; + } + + /* XXX -- FCGI_CANT_MPX_CONN */ + c->client_status = CLIENT_FCGI_BEGIN; + c->id = ntohs(h.id); + break; + case FCGI_PARAMS: + if (c->client_status < CLIENT_FCGI_BEGIN) { + log_warnx("FCGI_PARAMS without " + "FCGI_BEGIN_REQUEST"); + goto fail; + } + if (c->client_status > CLIENT_FCGI_PARAMS) { + log_warnx("FCGI_PARAMS after FCGI_STDIN"); + goto fail; + } + + if (c->id != ntohs(h.id)) { + log_warnx("unexpected ID in FCGI header"); + goto fail; + } + + c->client_status = CLIENT_FCGI_PARAMS; + c->nparams++; + + if (parse_params(c, c->buf, record_len) == -1) + goto fail; + break; + case FCGI_ABORT_REQUEST: + log_warnx("received FCGI_ABORT_REQUEST from client"); + request_done(c); + return; + case FCGI_STDIN: + if (c->client_status < CLIENT_FCGI_BEGIN) { + log_warnx("FCGI_STDIN without " + "FCGI_BEGIN_REQUEST"); + goto fail; + } + + if (c->client_status < CLIENT_FCGI_PARAMS) { + log_warnx("FCGI_STDIN without FCGI_PARAMS"); + goto fail; + } + + if (c->id != ntohs(h.id)) { + log_warnx("unexpected ID in FCGI header"); + goto fail; + } + + c->client_status = CLIENT_FCGI_STDIN; + if (c->nparams_parsed >= c->nparams) { + if (process_request(c) == -1) + goto fail; + } + break; + default: + log_warn("unexpected FCGI type %u", h.type); + goto fail; + } + + /* drop the parsed record */ + c->buf_len -= record_len; + memmove(c->buf, c->buf + record_len, c->buf_len); + } +more: + event_add(&c->ev, NULL); + return; +fail: + request_done(c); } void @@ -609,12 +1102,16 @@ sockets_socket_accept(int fd, short event, void *arg) socklen_t len; int s; + log_info("%u: %s: %d clients", getpid(), __func__, client_cnt); + backoff.tv_sec = 1; backoff.tv_usec = 0; - event_add(&sock->ev, NULL); - if (event & EV_TIMEOUT) + if (event & EV_TIMEOUT) { + event_add(&sock->ev, NULL); + log_info("%u: %s: timeout", getpid(), __func__); return; + } len = sizeof(ss); @@ -626,6 +1123,8 @@ sockets_socket_accept(int fd, short event, void *arg) case EINTR: case EWOULDBLOCK: case ECONNABORTED: + log_info("%u: %s: errno %d", getpid(), __func__, errno); + event_add(&sock->ev, NULL); return; case EMFILE: case ENFILE: @@ -639,14 +1138,21 @@ sockets_socket_accept(int fd, short event, void *arg) } } - if (client_cnt > GOTWEBD_MAXCLIENTS) - goto err; + if (client_cnt > GOTWEBD_MAXCLIENTS) { + cgi_inflight--; + close(s); + if (c != NULL) + free(c); + event_add(&sock->ev, NULL); + return; + } c = calloc(1, sizeof(struct request)); if (c == NULL) { log_warn("%s: calloc", __func__); close(s); cgi_inflight--; + event_add(&sock->ev, NULL); return; } @@ -656,6 +1162,7 @@ sockets_socket_accept(int fd, short event, void *arg) close(s); cgi_inflight--; free(c); + event_add(&sock->ev, NULL); return; } @@ -664,23 +1171,18 @@ sockets_socket_accept(int fd, short event, void *arg) c->sock = sock; memcpy(c->priv_fd, gotwebd_env->priv_fd, sizeof(c->priv_fd)); c->sock_id = sock->conf.id; - c->buf_pos = 0; c->buf_len = 0; - c->request_started = 0; c->client_status = CLIENT_CONNECT; + c->request_id = get_request_id(); - event_set(&c->ev, s, EV_READ, fcgi_request, c); + event_set(&c->ev, s, EV_READ, read_fcgi_records, c); event_add(&c->ev, NULL); - evtimer_set(&c->tmo, fcgi_timeout, c); + evtimer_set(&c->tmo, request_timeout, c); evtimer_add(&c->tmo, &timeout); - return; -err: - cgi_inflight--; - close(s); - if (c != NULL) - free(c); + log_info("%u: %s: add_request %d", getpid(), __func__, c->request_id); + add_request(c); } void