1*02ad6491Sblambert /* $OpenBSD: slowcgi.c,v 1.10 2013/09/11 09:31:22 blambert Exp $ */ 27ac15270Sflorian /* 37ac15270Sflorian * Copyright (c) 2013 David Gwynne <dlg@openbsd.org> 47ac15270Sflorian * Copyright (c) 2013 Florian Obser <florian@openbsd.org> 57ac15270Sflorian * 67ac15270Sflorian * Permission to use, copy, modify, and distribute this software for any 77ac15270Sflorian * purpose with or without fee is hereby granted, provided that the above 87ac15270Sflorian * copyright notice and this permission notice appear in all copies. 97ac15270Sflorian * 107ac15270Sflorian * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 117ac15270Sflorian * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 127ac15270Sflorian * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 137ac15270Sflorian * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 147ac15270Sflorian * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 157ac15270Sflorian * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 167ac15270Sflorian * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 177ac15270Sflorian */ 187ac15270Sflorian 197ac15270Sflorian #include <sys/types.h> 207ac15270Sflorian #include <sys/ioctl.h> 217ac15270Sflorian #include <sys/queue.h> 227ac15270Sflorian #include <sys/socket.h> 237ac15270Sflorian #include <sys/socketvar.h> 247ac15270Sflorian #include <sys/stat.h> 257ac15270Sflorian #include <sys/un.h> 267ac15270Sflorian #include <sys/wait.h> 277ac15270Sflorian #include <err.h> 287ac15270Sflorian #include <ctype.h> 297ac15270Sflorian #include <errno.h> 307ac15270Sflorian #include <event.h> 317ac15270Sflorian #include <netdb.h> 327ac15270Sflorian #include <pwd.h> 337ac15270Sflorian #include <signal.h> 347ac15270Sflorian #include <stdio.h> 357ac15270Sflorian #include <stdlib.h> 367ac15270Sflorian #include <string.h> 377ac15270Sflorian #include <syslog.h> 387ac15270Sflorian #include <unistd.h> 397ac15270Sflorian 407ac15270Sflorian #define TIMEOUT_DEFAULT 120 417ac15270Sflorian #define SLOWCGI_USER "www" 420ee0284dSblambert 430ee0284dSblambert #define FCGI_CONTENT_SIZE 65535 440ee0284dSblambert #define FCGI_PADDING_SIZE 255 450ee0284dSblambert #define FCGI_RECORD_SIZE \ 460ee0284dSblambert (sizeof(struct fcgi_record_header) + FCGI_CONTENT_SIZE + FCGI_PADDING_SIZE) 470ee0284dSblambert 487ac15270Sflorian #define STDOUT_DONE 1 497ac15270Sflorian #define STDERR_DONE 2 507ac15270Sflorian #define SCRIPT_DONE 4 517ac15270Sflorian 527ac15270Sflorian #define FCGI_BEGIN_REQUEST 1 537ac15270Sflorian #define FCGI_ABORT_REQUEST 2 547ac15270Sflorian #define FCGI_END_REQUEST 3 557ac15270Sflorian #define FCGI_PARAMS 4 567ac15270Sflorian #define FCGI_STDIN 5 577ac15270Sflorian #define FCGI_STDOUT 6 587ac15270Sflorian #define FCGI_STDERR 7 597ac15270Sflorian #define FCGI_DATA 8 607ac15270Sflorian #define FCGI_GET_VALUES 9 617ac15270Sflorian #define FCGI_GET_VALUES_RESULT 10 627ac15270Sflorian #define FCGI_UNKNOWN_TYPE 11 637ac15270Sflorian #define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE) 647ac15270Sflorian 657ac15270Sflorian #define FCGI_REQUEST_COMPLETE 0 667ac15270Sflorian #define FCGI_CANT_MPX_CONN 1 677ac15270Sflorian #define FCGI_OVERLOADED 2 687ac15270Sflorian #define FCGI_UNKNOWN_ROLE 3 697ac15270Sflorian 707ac15270Sflorian 717ac15270Sflorian struct listener { 727ac15270Sflorian struct event ev, pause; 737ac15270Sflorian }; 747ac15270Sflorian 757ac15270Sflorian struct env_val { 767ac15270Sflorian SLIST_ENTRY(env_val) entry; 777ac15270Sflorian char *val; 787ac15270Sflorian }; 797ac15270Sflorian SLIST_HEAD(env_head, env_val); 807ac15270Sflorian 817ac15270Sflorian struct fcgi_record_header { 827ac15270Sflorian uint8_t version; 837ac15270Sflorian uint8_t type; 847ac15270Sflorian uint16_t id; 857ac15270Sflorian uint16_t content_len; 867ac15270Sflorian uint8_t padding_len; 877ac15270Sflorian uint8_t reserved; 887ac15270Sflorian }__packed; 897ac15270Sflorian 907ac15270Sflorian struct fcgi_response { 917ac15270Sflorian TAILQ_ENTRY(fcgi_response) entry; 920ee0284dSblambert uint8_t data[FCGI_RECORD_SIZE]; 937ac15270Sflorian size_t data_pos; 947ac15270Sflorian size_t data_len; 957ac15270Sflorian }; 967ac15270Sflorian TAILQ_HEAD(fcgi_response_head, fcgi_response); 977ac15270Sflorian 987ac15270Sflorian struct fcgi_stdin { 997ac15270Sflorian TAILQ_ENTRY(fcgi_stdin) entry; 1007ac15270Sflorian uint8_t data[FCGI_RECORD_SIZE]; 1017ac15270Sflorian size_t data_pos; 1027ac15270Sflorian size_t data_len; 1037ac15270Sflorian }; 1047ac15270Sflorian TAILQ_HEAD(fcgi_stdin_head, fcgi_stdin); 1057ac15270Sflorian 1067ac15270Sflorian struct client { 1077ac15270Sflorian struct event ev; 1087ac15270Sflorian struct event resp_ev; 1097ac15270Sflorian struct event tmo; 1107ac15270Sflorian int fd; 1117ac15270Sflorian uint8_t buf[FCGI_RECORD_SIZE]; 1127ac15270Sflorian size_t buf_pos; 1137ac15270Sflorian size_t buf_len; 1147ac15270Sflorian struct fcgi_response_head response_head; 1157ac15270Sflorian struct fcgi_stdin_head stdin_head; 1167ac15270Sflorian uint16_t id; 1177ac15270Sflorian char script_name[MAXPATHLEN]; 1187ac15270Sflorian struct env_head env; 1197ac15270Sflorian int env_count; 1207ac15270Sflorian pid_t script_pid; 1217ac15270Sflorian int script_status; 1227ac15270Sflorian struct event script_ev; 1237ac15270Sflorian struct event script_err_ev; 1247ac15270Sflorian struct event script_stdin_ev; 1257ac15270Sflorian uint8_t script_flags; 1267ac15270Sflorian uint8_t request_started; 1277ac15270Sflorian }; 1287ac15270Sflorian 1297ac15270Sflorian struct clients { 1307ac15270Sflorian SLIST_ENTRY(clients) entry; 1317ac15270Sflorian struct client *client; 1327ac15270Sflorian }; 1337ac15270Sflorian SLIST_HEAD(clients_head, clients); 1347ac15270Sflorian 1357ac15270Sflorian struct slowcgi_proc { 1367ac15270Sflorian struct clients_head clients; 1377ac15270Sflorian struct event ev_sigchld; 1387ac15270Sflorian struct event ev_sigpipe; 1397ac15270Sflorian }; 1407ac15270Sflorian 1417ac15270Sflorian struct fcgi_begin_request_body { 1427ac15270Sflorian uint16_t role; 1437ac15270Sflorian uint8_t flags; 1447ac15270Sflorian uint8_t reserved[5]; 1457ac15270Sflorian }__packed; 1467ac15270Sflorian 14703e61f17Sblambert struct fcgi_end_request_body { 1487ac15270Sflorian uint32_t app_status; 1497ac15270Sflorian uint8_t protocol_status; 1507ac15270Sflorian uint8_t reserved[3]; 1517ac15270Sflorian }__packed; 15203e61f17Sblambert 1537ac15270Sflorian __dead void usage(void); 1540b5ee19eSblambert void slowcgi_listen(char *, gid_t); 1557ac15270Sflorian void slowcgi_paused(int, short, void*); 1567ac15270Sflorian void slowcgi_accept(int, short, void*); 1577ac15270Sflorian void slowcgi_request(int, short, void*); 1587ac15270Sflorian void slowcgi_response(int, short, void*); 1597ac15270Sflorian void slowcgi_timeout(int, short, void*); 1607ac15270Sflorian void slowcgi_sig_handler(int, short, void*); 1617ac15270Sflorian size_t parse_request(uint8_t* , size_t, struct client*); 1627ac15270Sflorian void parse_begin_request(uint8_t*, uint16_t, struct client*, 1637ac15270Sflorian uint16_t); 1647ac15270Sflorian void parse_params(uint8_t*, uint16_t, struct client*, uint16_t); 1657ac15270Sflorian void parse_stdin(uint8_t*, uint16_t, struct client*, uint16_t); 1667ac15270Sflorian void exec_cgi(struct client*); 1677ac15270Sflorian void script_in(int, struct event*, struct client*, uint8_t); 1687ac15270Sflorian void script_std_in(int, short, void*); 1697ac15270Sflorian void script_err_in(int, short, void*); 1707ac15270Sflorian void script_out(int, short, void*); 1717ac15270Sflorian void create_end_request(struct client*); 17203e61f17Sblambert void dump_fcgi_record(const char *, 17303e61f17Sblambert struct fcgi_record_header *); 1747ac15270Sflorian void dump_fcgi_record_header(const char*, 1757ac15270Sflorian struct fcgi_record_header*); 17603e61f17Sblambert void dump_fcgi_begin_request_body(const char *, 17703e61f17Sblambert struct fcgi_begin_request_body *); 17803e61f17Sblambert void dump_fcgi_end_request_body(const char *, 17903e61f17Sblambert struct fcgi_end_request_body *); 1807ac15270Sflorian void cleanup_client(struct client*); 18103e61f17Sblambert 1827ac15270Sflorian struct loggers { 1837ac15270Sflorian void (*err)(int, const char *, ...); 1847ac15270Sflorian void (*errx)(int, const char *, ...); 1857ac15270Sflorian void (*warn)(const char *, ...); 1867ac15270Sflorian void (*warnx)(const char *, ...); 1877ac15270Sflorian void (*info)(const char *, ...); 1887ac15270Sflorian void (*debug)(const char *, ...); 1897ac15270Sflorian }; 1907ac15270Sflorian 1917ac15270Sflorian const struct loggers conslogger = { 1927ac15270Sflorian err, 1937ac15270Sflorian errx, 1947ac15270Sflorian warn, 1957ac15270Sflorian warnx, 1967ac15270Sflorian warnx, /* info */ 1977ac15270Sflorian warnx /* debug */ 1987ac15270Sflorian }; 1997ac15270Sflorian 2007ac15270Sflorian void syslog_err(int, const char *, ...); 2017ac15270Sflorian void syslog_errx(int, const char *, ...); 2027ac15270Sflorian void syslog_warn(const char *, ...); 2037ac15270Sflorian void syslog_warnx(const char *, ...); 2047ac15270Sflorian void syslog_info(const char *, ...); 2057ac15270Sflorian void syslog_debug(const char *, ...); 2067ac15270Sflorian void syslog_vstrerror(int, int, const char *, va_list); 2077ac15270Sflorian 2087ac15270Sflorian const struct loggers syslogger = { 2097ac15270Sflorian syslog_err, 2107ac15270Sflorian syslog_errx, 2117ac15270Sflorian syslog_warn, 2127ac15270Sflorian syslog_warnx, 2137ac15270Sflorian syslog_info, 2147ac15270Sflorian syslog_debug 2157ac15270Sflorian }; 2167ac15270Sflorian 2177ac15270Sflorian const struct loggers *logger = &conslogger; 2187ac15270Sflorian 2197ac15270Sflorian #define lerr(_e, _f...) logger->err((_e), _f) 2207ac15270Sflorian #define lerrx(_e, _f...) logger->errx((_e), _f) 2217ac15270Sflorian #define lwarn(_f...) logger->warn(_f) 2227ac15270Sflorian #define lwarnx(_f...) logger->warnx(_f) 2237ac15270Sflorian #define linfo(_f...) logger->info(_f) 2247ac15270Sflorian #define ldebug(_f...) logger->debug(_f) 2257ac15270Sflorian 2267ac15270Sflorian __dead void 2277ac15270Sflorian usage(void) 2287ac15270Sflorian { 2297ac15270Sflorian extern char *__progname; 2300b5ee19eSblambert fprintf(stderr, "usage: %s [-d] [-s socket]\n", __progname); 2317ac15270Sflorian exit(1); 2327ac15270Sflorian } 2337ac15270Sflorian 2347ac15270Sflorian struct timeval timeout = { TIMEOUT_DEFAULT, 0 }; 2357ac15270Sflorian struct slowcgi_proc slowcgi_proc; 2367ac15270Sflorian int debug = 0; 2377ac15270Sflorian int on = 1; 2380b5ee19eSblambert char *fcgi_socket = "/var/www/run/slowcgi.sock"; 2397ac15270Sflorian 2407ac15270Sflorian int 2417ac15270Sflorian main(int argc, char *argv[]) 2427ac15270Sflorian { 2437ac15270Sflorian struct passwd *pw; 2447ac15270Sflorian int c; 2457ac15270Sflorian 2460b5ee19eSblambert while ((c = getopt(argc, argv, "ds:")) != -1) { 2477ac15270Sflorian switch (c) { 2487ac15270Sflorian case 'd': 2497ac15270Sflorian debug = 1; 2507ac15270Sflorian break; 2510b5ee19eSblambert case 's': 2520b5ee19eSblambert fcgi_socket = optarg; 2530b5ee19eSblambert break; 2547ac15270Sflorian default: 2557ac15270Sflorian usage(); 2567ac15270Sflorian /* NOTREACHED */ 2577ac15270Sflorian } 2587ac15270Sflorian } 2597ac15270Sflorian 2607ac15270Sflorian if (geteuid() != 0) 2617ac15270Sflorian errx(1, "need root privileges"); 2627ac15270Sflorian 2637ac15270Sflorian pw = getpwnam(SLOWCGI_USER); 2647ac15270Sflorian if (pw == NULL) 2657ac15270Sflorian err(1, "no %s user", SLOWCGI_USER); 2667ac15270Sflorian 2677ac15270Sflorian if (!debug && daemon(1, 0) == -1) 2687ac15270Sflorian err(1, "daemon"); 2697ac15270Sflorian 2707ac15270Sflorian event_init(); 2717ac15270Sflorian 2720b5ee19eSblambert slowcgi_listen(fcgi_socket, pw->pw_gid); 2730b5ee19eSblambert 2747ac15270Sflorian if (chroot(pw->pw_dir) == -1) 2757ac15270Sflorian lerr(1, "chroot(%s)", pw->pw_dir); 2767ac15270Sflorian 2777ac15270Sflorian if (chdir("/") == -1) 2787ac15270Sflorian lerr(1, "chdir(%s)", pw->pw_dir); 2797ac15270Sflorian 280bde99fd8Sblambert ldebug("chroot: %s", pw->pw_dir); 281bde99fd8Sblambert 2827ac15270Sflorian if (setgroups(1, &pw->pw_gid) || 2837ac15270Sflorian setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 2847ac15270Sflorian setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 2857ac15270Sflorian lerr(1, "unable to revoke privs"); 2867ac15270Sflorian 2877ac15270Sflorian SLIST_INIT(&slowcgi_proc.clients); 2887ac15270Sflorian 2897ac15270Sflorian signal_set(&slowcgi_proc.ev_sigchld, SIGCHLD, slowcgi_sig_handler, 2907ac15270Sflorian &slowcgi_proc); 2917ac15270Sflorian signal_set(&slowcgi_proc.ev_sigpipe, SIGPIPE, slowcgi_sig_handler, 2927ac15270Sflorian &slowcgi_proc); 2937ac15270Sflorian 2947ac15270Sflorian signal_add(&slowcgi_proc.ev_sigchld, NULL); 2957ac15270Sflorian signal_add(&slowcgi_proc.ev_sigpipe, NULL); 2967ac15270Sflorian 2977ac15270Sflorian event_dispatch(); 2987ac15270Sflorian return (0); 2997ac15270Sflorian } 3007ac15270Sflorian void 3010b5ee19eSblambert slowcgi_listen(char *path, gid_t gid) 3027ac15270Sflorian { 3037ac15270Sflorian struct listener *l = NULL; 3047ac15270Sflorian struct sockaddr_un sun; 3057ac15270Sflorian mode_t old_umask, mode; 3067ac15270Sflorian int fd; 3077ac15270Sflorian 3087ac15270Sflorian if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 3097ac15270Sflorian lerr(1, "slowcgi_listen: socket"); 3107ac15270Sflorian 3117ac15270Sflorian bzero(&sun, sizeof(sun)); 3127ac15270Sflorian sun.sun_family = AF_UNIX; 3137ac15270Sflorian strlcpy(sun.sun_path, path, sizeof(sun.sun_path)); 3147ac15270Sflorian 3157ac15270Sflorian if (unlink(path) == -1) 3167ac15270Sflorian if (errno != ENOENT) 3177ac15270Sflorian lerr(1, "slowcgi_listen: unlink %s", path); 3187ac15270Sflorian 3197ac15270Sflorian old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); 3207ac15270Sflorian mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP; 3217ac15270Sflorian 3227ac15270Sflorian if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) 3237ac15270Sflorian lerr(1,"slowcgi_listen: bind: %s", path); 3247ac15270Sflorian 3257ac15270Sflorian umask(old_umask); 3267ac15270Sflorian 3277ac15270Sflorian if (chmod(path, mode) == -1) 3287ac15270Sflorian lerr(1, "slowcgi_listen: chmod: %s", path); 3297ac15270Sflorian 3307ac15270Sflorian if (chown(path, 0, gid) == -1) 3317ac15270Sflorian lerr(1, "slowcgi_listen: chown: %s", path); 3327ac15270Sflorian 3337ac15270Sflorian if (ioctl(fd, FIONBIO, &on) == -1) 3347ac15270Sflorian lerr(1, "listener ioctl(FIONBIO)"); 3357ac15270Sflorian 3367ac15270Sflorian if (listen(fd, 5) == -1) 3377ac15270Sflorian lerr(1, "listen"); 3387ac15270Sflorian 3397ac15270Sflorian l = calloc(1, sizeof(*l)); 3407ac15270Sflorian if (l == NULL) 3417ac15270Sflorian lerr(1, "listener ev alloc"); 3427ac15270Sflorian 3437ac15270Sflorian event_set(&l->ev, fd, EV_READ | EV_PERSIST, slowcgi_accept, l); 3447ac15270Sflorian event_add(&l->ev, NULL); 3457ac15270Sflorian evtimer_set(&l->pause, slowcgi_paused, l); 346bde99fd8Sblambert 347bde99fd8Sblambert ldebug("socket: %s", path); 3487ac15270Sflorian } 3497ac15270Sflorian 3507ac15270Sflorian void 3517ac15270Sflorian slowcgi_paused(int fd, short events, void *arg) 3527ac15270Sflorian { 3537ac15270Sflorian struct listener *l = arg; 3547ac15270Sflorian event_add(&l->ev, NULL); 3557ac15270Sflorian } 3567ac15270Sflorian 3577ac15270Sflorian void 3587ac15270Sflorian slowcgi_accept(int fd, short events, void *arg) 3597ac15270Sflorian { 3607ac15270Sflorian struct listener *l; 3617ac15270Sflorian struct sockaddr_storage ss; 3627ac15270Sflorian struct timeval pause; 3637ac15270Sflorian struct client *c; 3647ac15270Sflorian struct clients *clients; 3657ac15270Sflorian socklen_t len; 3667ac15270Sflorian int s; 3677ac15270Sflorian 3687ac15270Sflorian l = arg; 369965b2218Sblambert pause.tv_sec = 1; 370965b2218Sblambert pause.tv_usec = 0; 3717ac15270Sflorian c = NULL; 3727ac15270Sflorian 3737ac15270Sflorian len = sizeof(ss); 3747ac15270Sflorian s = accept(fd, (struct sockaddr *)&ss, &len); 3757ac15270Sflorian if (s == -1) { 3767ac15270Sflorian switch (errno) { 3777ac15270Sflorian case EINTR: 3787ac15270Sflorian case EWOULDBLOCK: 3797ac15270Sflorian case ECONNABORTED: 3807ac15270Sflorian return; 3817ac15270Sflorian case EMFILE: 3827ac15270Sflorian case ENFILE: 3837ac15270Sflorian event_del(&l->ev); 3847ac15270Sflorian evtimer_add(&l->pause, &pause); 3857ac15270Sflorian return; 3867ac15270Sflorian default: 3877ac15270Sflorian lerr(1, "accept"); 3887ac15270Sflorian } 3897ac15270Sflorian } 3907ac15270Sflorian 3917ac15270Sflorian if (ioctl(s, FIONBIO, &on) == -1) 3927ac15270Sflorian lerr(1, "client ioctl(FIONBIO)"); 3937ac15270Sflorian 3947ac15270Sflorian c = calloc(1, sizeof(*c)); 3957ac15270Sflorian if (c == NULL) { 3967ac15270Sflorian lwarn("cannot calloc client"); 3977ac15270Sflorian close(s); 3987ac15270Sflorian return; 3997ac15270Sflorian } 4007ac15270Sflorian clients = calloc(1, sizeof(*clients)); 401aa48dbd7Sjasper if (clients == NULL) { 4027ac15270Sflorian lwarn("cannot calloc clients"); 4037ac15270Sflorian close(s); 4047ac15270Sflorian free(c); 4057ac15270Sflorian return; 4067ac15270Sflorian } 4077ac15270Sflorian c->fd = s; 4087ac15270Sflorian c->buf_pos = 0; 4097ac15270Sflorian c->buf_len = 0; 4107ac15270Sflorian c->request_started = 0; 4117ac15270Sflorian TAILQ_INIT(&c->response_head); 4127ac15270Sflorian TAILQ_INIT(&c->stdin_head); 4137ac15270Sflorian 4147ac15270Sflorian event_set(&c->ev, s, EV_READ | EV_PERSIST, slowcgi_request, c); 4157ac15270Sflorian event_add(&c->ev, NULL); 4167ac15270Sflorian 4177ac15270Sflorian event_set(&c->tmo, s, 0, slowcgi_timeout, c); 4187ac15270Sflorian event_add(&c->tmo, &timeout); 4197ac15270Sflorian clients->client = c; 4207ac15270Sflorian SLIST_INSERT_HEAD(&slowcgi_proc.clients, clients, entry); 4217ac15270Sflorian } 4227ac15270Sflorian 4237ac15270Sflorian void 4247ac15270Sflorian slowcgi_timeout(int fd, short events, void *arg) 4257ac15270Sflorian { 4267ac15270Sflorian cleanup_client((struct client*) arg); 4277ac15270Sflorian } 4287ac15270Sflorian 4297ac15270Sflorian void 4307ac15270Sflorian slowcgi_sig_handler(int sig, short event, void *arg) 4317ac15270Sflorian { 4327ac15270Sflorian struct client *c; 4337ac15270Sflorian struct clients *ncs; 4347ac15270Sflorian struct slowcgi_proc *p; 4357ac15270Sflorian pid_t pid; 4367ac15270Sflorian int status; 4377ac15270Sflorian 4387ac15270Sflorian p = arg; 4397ac15270Sflorian c = NULL; 4407ac15270Sflorian 4417ac15270Sflorian switch (sig) { 4427ac15270Sflorian case SIGCHLD: 4437ac15270Sflorian pid = wait(&status); 4447ac15270Sflorian SLIST_FOREACH(ncs, &p->clients, entry) 4457ac15270Sflorian if (ncs->client->script_pid == pid) { 4467ac15270Sflorian c = ncs->client; 4477ac15270Sflorian break; 4487ac15270Sflorian } 449bde99fd8Sblambert if (c == NULL) { 450bde99fd8Sblambert lwarnx("caught exit of unknown child %i", pid); 451bde99fd8Sblambert break; 452bde99fd8Sblambert } 453bde99fd8Sblambert 454bde99fd8Sblambert if (WIFSIGNALED(status)) 455bde99fd8Sblambert c->script_status = WTERMSIG(status); 456bde99fd8Sblambert else 4577ac15270Sflorian c->script_status = WEXITSTATUS(status); 458bde99fd8Sblambert 4597ac15270Sflorian if (c->script_flags == (STDOUT_DONE | STDERR_DONE)) 4607ac15270Sflorian create_end_request(c); 4617ac15270Sflorian c->script_flags |= SCRIPT_DONE; 462bde99fd8Sblambert 463bde99fd8Sblambert ldebug("wait: %s", c->script_name); 464bde99fd8Sblambert break; 4657ac15270Sflorian case SIGPIPE: 4667ac15270Sflorian /* ignore */ 4677ac15270Sflorian break; 4687ac15270Sflorian default: 4697ac15270Sflorian lerr(1, "unexpected signal: %d", sig); 4707ac15270Sflorian 4717ac15270Sflorian } 4727ac15270Sflorian } 4737ac15270Sflorian 4747ac15270Sflorian void 4757ac15270Sflorian slowcgi_response(int fd, short events, void *arg) 4767ac15270Sflorian { 4777ac15270Sflorian struct client *c; 4787ac15270Sflorian struct fcgi_record_header *header; 4797ac15270Sflorian struct fcgi_response *resp; 4807ac15270Sflorian ssize_t n; 4817ac15270Sflorian 4827ac15270Sflorian c = arg; 4837ac15270Sflorian 4847ac15270Sflorian while ((resp = TAILQ_FIRST(&c->response_head))) { 4857ac15270Sflorian header = (struct fcgi_record_header*) resp->data; 4867ac15270Sflorian if (debug) 48703e61f17Sblambert dump_fcgi_record("resp ", header); 4887ac15270Sflorian 4897ac15270Sflorian n = write(fd, resp->data + resp->data_pos, resp->data_len); 4907ac15270Sflorian if (n == -1) { 4917ac15270Sflorian if (errno == EAGAIN) 4927ac15270Sflorian return; 4937ac15270Sflorian cleanup_client(c); 4947ac15270Sflorian return; 4957ac15270Sflorian } 4967ac15270Sflorian resp->data_pos += n; 4977ac15270Sflorian resp->data_len -= n; 4987ac15270Sflorian if (resp->data_len == 0) { 4997ac15270Sflorian TAILQ_REMOVE(&c->response_head, resp, entry); 5007ac15270Sflorian free(resp); 5017ac15270Sflorian } 5027ac15270Sflorian } 5037ac15270Sflorian 5047ac15270Sflorian if (TAILQ_EMPTY(&c->response_head)) { 5057ac15270Sflorian if (c->script_flags == (STDOUT_DONE | STDERR_DONE | 5067ac15270Sflorian SCRIPT_DONE)) 5077ac15270Sflorian cleanup_client(c); 5087ac15270Sflorian else 5097ac15270Sflorian event_del(&c->resp_ev); 5107ac15270Sflorian } 5117ac15270Sflorian } 5127ac15270Sflorian 5137ac15270Sflorian void 5147ac15270Sflorian slowcgi_request(int fd, short events, void *arg) 5157ac15270Sflorian { 5167ac15270Sflorian struct client *c; 5177ac15270Sflorian size_t n, parsed; 5187ac15270Sflorian 5197ac15270Sflorian c = arg; 5207ac15270Sflorian parsed = 0; 5217ac15270Sflorian 5227ac15270Sflorian n = read(fd, c->buf + c->buf_pos + c->buf_len, 5237ac15270Sflorian FCGI_RECORD_SIZE - c->buf_pos-c->buf_len); 5247ac15270Sflorian 5257ac15270Sflorian switch (n) { 5267ac15270Sflorian case -1: 5277ac15270Sflorian switch (errno) { 5287ac15270Sflorian case EINTR: 5297ac15270Sflorian case EAGAIN: 5307ac15270Sflorian return; 5317ac15270Sflorian default: 5327ac15270Sflorian goto fail; 5337ac15270Sflorian } 5347ac15270Sflorian break; 5357ac15270Sflorian 5367ac15270Sflorian case 0: 5377ac15270Sflorian ldebug("closed connection"); 5387ac15270Sflorian goto fail; 5397ac15270Sflorian default: 5407ac15270Sflorian break; 5417ac15270Sflorian } 5427ac15270Sflorian 5437ac15270Sflorian c->buf_len += n; 5447ac15270Sflorian 545*02ad6491Sblambert /* 546*02ad6491Sblambert * Parse the records as they are received. Per the FastCGI 547*02ad6491Sblambert * specification, the server need only receive the FastCGI 548*02ad6491Sblambert * parameter records in full; it is free to begin execution 549*02ad6491Sblambert * at that point, which is what happens here. 550*02ad6491Sblambert */ 5517ac15270Sflorian do { 5527ac15270Sflorian parsed = parse_request(c->buf + c->buf_pos, c->buf_len, c); 5537ac15270Sflorian c->buf_pos += parsed; 5547ac15270Sflorian c->buf_len -= parsed; 5557ac15270Sflorian } while (parsed > 0 && c->buf_len > 0); 5567ac15270Sflorian 557965b2218Sblambert /* Make space for further reads */ 5587ac15270Sflorian if (c->buf_len > 0) { 5597ac15270Sflorian bcopy(c->buf + c->buf_pos, c->buf, c->buf_len); 5607ac15270Sflorian c->buf_pos = 0; 5617ac15270Sflorian } 5627ac15270Sflorian return; 5637ac15270Sflorian fail: 5647ac15270Sflorian cleanup_client(c); 5657ac15270Sflorian } 5667ac15270Sflorian 5677ac15270Sflorian void 5687ac15270Sflorian parse_begin_request(uint8_t *buf, uint16_t n, struct client *c, uint16_t id) 5697ac15270Sflorian { 5707ac15270Sflorian struct fcgi_begin_request_body *b; 5717ac15270Sflorian 572965b2218Sblambert /* XXX -- FCGI_CANT_MPX_CONN */ 5737ac15270Sflorian if (c->request_started) { 5747ac15270Sflorian lwarnx("unexpected FCGI_BEGIN_REQUEST, ignoring"); 5757ac15270Sflorian return; 5767ac15270Sflorian } 5777ac15270Sflorian 5787ac15270Sflorian if (n != sizeof(struct fcgi_begin_request_body)) { 5797ac15270Sflorian lwarnx("wrong size %d != %d", n, 5807ac15270Sflorian sizeof(struct fcgi_begin_request_body)); 5817ac15270Sflorian return; 5827ac15270Sflorian } 5837ac15270Sflorian 5847ac15270Sflorian c->request_started = 1; 5857ac15270Sflorian b = (struct fcgi_begin_request_body*) buf; 5867ac15270Sflorian 5877ac15270Sflorian c->id = id; 5887ac15270Sflorian SLIST_INIT(&c->env); 5897ac15270Sflorian c->env_count = 0; 5907ac15270Sflorian } 5917ac15270Sflorian void 5927ac15270Sflorian parse_params(uint8_t *buf, uint16_t n, struct client *c, uint16_t id) 5937ac15270Sflorian { 5947ac15270Sflorian struct env_val *env_entry; 5957ac15270Sflorian uint32_t name_len, val_len; 5967ac15270Sflorian 5977ac15270Sflorian if (!c->request_started) { 5987ac15270Sflorian lwarnx("FCGI_PARAMS without FCGI_BEGIN_REQUEST, ignoring"); 5997ac15270Sflorian return; 6007ac15270Sflorian } 6017ac15270Sflorian 6027ac15270Sflorian if (c->id != id) { 6037ac15270Sflorian lwarnx("unexpected id, ignoring"); 6047ac15270Sflorian return; 6057ac15270Sflorian } 6067ac15270Sflorian 6077ac15270Sflorian name_len = val_len = 0; 6087ac15270Sflorian 609*02ad6491Sblambert /* 610*02ad6491Sblambert * If this is the last FastCGI parameter record, 611*02ad6491Sblambert * begin execution of the CGI script. 612*02ad6491Sblambert */ 6137ac15270Sflorian if (n == 0) { 6147ac15270Sflorian exec_cgi(c); 6157ac15270Sflorian return; 6167ac15270Sflorian } 617*02ad6491Sblambert 6187ac15270Sflorian while (n > 0) { 6197ac15270Sflorian if (buf[0] >> 7 == 0) { 6207ac15270Sflorian name_len = buf[0]; 6217ac15270Sflorian n--; 6227ac15270Sflorian buf++; 6237ac15270Sflorian } else { 6247ac15270Sflorian if (n > 3) { 6257ac15270Sflorian name_len = ((buf[3] & 0x7f) << 24) + 6267ac15270Sflorian (buf[2] << 16) + (buf[1] << 8) + buf[0]; 6277ac15270Sflorian n -= 4; 6287ac15270Sflorian buf += 4; 6297ac15270Sflorian } else 6307ac15270Sflorian return; 6317ac15270Sflorian } 6327ac15270Sflorian 6337ac15270Sflorian if (n > 0) { 6347ac15270Sflorian if (buf[0] >> 7 == 0) { 6357ac15270Sflorian val_len = buf[0]; 6367ac15270Sflorian n--; 6377ac15270Sflorian buf++; 6387ac15270Sflorian } else { 6397ac15270Sflorian if (n > 3) { 6407ac15270Sflorian val_len = ((buf[3] & 0x7f) << 24) + 6417ac15270Sflorian (buf[2] << 16) + (buf[1] << 8) + 6427ac15270Sflorian buf[0]; 6437ac15270Sflorian n -= 4; 6447ac15270Sflorian buf += 4; 6457ac15270Sflorian } else 6467ac15270Sflorian return; 6477ac15270Sflorian } 6487ac15270Sflorian } 6497ac15270Sflorian if (n < name_len + val_len) 6507ac15270Sflorian return; 6517ac15270Sflorian 6527ac15270Sflorian if ((env_entry = malloc(sizeof(struct env_val))) == NULL) { 6537ac15270Sflorian lwarnx("cannot allocate env_entry"); 6547ac15270Sflorian return; 6557ac15270Sflorian } 6567ac15270Sflorian 6577ac15270Sflorian if ((env_entry->val = calloc(sizeof(char), name_len + val_len + 6587ac15270Sflorian 2)) == NULL) { 6597ac15270Sflorian lwarnx("cannot allocate env_entry->val"); 6607ac15270Sflorian free(env_entry); 6617ac15270Sflorian return; 6627ac15270Sflorian } 6637ac15270Sflorian 6647ac15270Sflorian bcopy(buf, env_entry->val, name_len); 665965b2218Sblambert buf += name_len; 666965b2218Sblambert n -= name_len; 6677ac15270Sflorian 6687ac15270Sflorian env_entry->val[name_len] = '\0'; 6697ac15270Sflorian if (val_len < MAXPATHLEN && strcmp(env_entry->val, 6707ac15270Sflorian "SCRIPT_NAME") == 0) { 6717ac15270Sflorian bcopy(buf, c->script_name, val_len); 67253847fa2Sflorian c->script_name[val_len] = '\0'; 6737ac15270Sflorian } 6747ac15270Sflorian env_entry->val[name_len] = '='; 6757ac15270Sflorian 6767ac15270Sflorian bcopy(buf, (env_entry->val) + name_len + 1, val_len); 677965b2218Sblambert buf += val_len; 678965b2218Sblambert n -= val_len; 6797ac15270Sflorian 6807ac15270Sflorian SLIST_INSERT_HEAD(&c->env, env_entry, entry); 6817ac15270Sflorian c->env_count++; 6827ac15270Sflorian } 6837ac15270Sflorian } 6847ac15270Sflorian 6857ac15270Sflorian void 6867ac15270Sflorian parse_stdin(uint8_t *buf, uint16_t n, struct client *c, uint16_t id) 6877ac15270Sflorian { 6887ac15270Sflorian struct fcgi_stdin *node; 6897ac15270Sflorian 6907ac15270Sflorian if (c->id != id) { 6917ac15270Sflorian lwarnx("unexpected id, ignoring"); 6927ac15270Sflorian return; 6937ac15270Sflorian } 6947ac15270Sflorian 6957ac15270Sflorian if ((node = calloc(1, sizeof(struct fcgi_stdin))) == NULL) { 6967ac15270Sflorian lwarnx("cannot calloc stdin node"); 6977ac15270Sflorian return; 6987ac15270Sflorian } 6997ac15270Sflorian 7007ac15270Sflorian bcopy(buf, node->data, n); 7017ac15270Sflorian node->data_pos = 0; 7027ac15270Sflorian node->data_len = n; 7037ac15270Sflorian 7047ac15270Sflorian TAILQ_INSERT_TAIL(&c->stdin_head, node, entry); 7057ac15270Sflorian 7067ac15270Sflorian event_del(&c->script_stdin_ev); 7077ac15270Sflorian event_set(&c->script_stdin_ev, EVENT_FD(&c->script_ev), EV_WRITE | 7087ac15270Sflorian EV_PERSIST, script_out, c); 7097ac15270Sflorian event_add(&c->script_stdin_ev, NULL); 7107ac15270Sflorian } 7117ac15270Sflorian 7127ac15270Sflorian size_t 7137ac15270Sflorian parse_request(uint8_t *buf, size_t n, struct client *c) 7147ac15270Sflorian { 7157ac15270Sflorian struct fcgi_record_header *h; 7167ac15270Sflorian 7177ac15270Sflorian if (n < sizeof(struct fcgi_record_header)) 7187ac15270Sflorian return (0); 7197ac15270Sflorian 7207ac15270Sflorian h = (struct fcgi_record_header*) buf; 7217ac15270Sflorian 7227ac15270Sflorian if (debug) 72303e61f17Sblambert dump_fcgi_record("", h); 7247ac15270Sflorian 7257ac15270Sflorian if (n < sizeof(struct fcgi_record_header) + ntohs(h->content_len) 7267ac15270Sflorian + h->padding_len) 7277ac15270Sflorian return (0); 7287ac15270Sflorian 7297ac15270Sflorian if (h->version != 1) 7307ac15270Sflorian lerrx(1, "wrong version"); 7317ac15270Sflorian 7327ac15270Sflorian switch (h->type) { 7337ac15270Sflorian case FCGI_BEGIN_REQUEST: 7347ac15270Sflorian parse_begin_request(buf + sizeof(struct fcgi_record_header), 7357ac15270Sflorian ntohs(h->content_len), c, ntohs(h->id)); 7367ac15270Sflorian break; 7377ac15270Sflorian case FCGI_PARAMS: 7387ac15270Sflorian parse_params(buf + sizeof(struct fcgi_record_header), 7397ac15270Sflorian ntohs(h->content_len), c, ntohs(h->id)); 7407ac15270Sflorian break; 7417ac15270Sflorian case FCGI_STDIN: 7427ac15270Sflorian parse_stdin(buf + sizeof(struct fcgi_record_header), 7437ac15270Sflorian ntohs(h->content_len), c, ntohs(h->id)); 7447ac15270Sflorian break; 7457ac15270Sflorian default: 7467ac15270Sflorian lwarnx("unimplemented type %d", h->type); 7477ac15270Sflorian } 7487ac15270Sflorian 7497ac15270Sflorian return (sizeof(struct fcgi_record_header) + ntohs(h->content_len) 7507ac15270Sflorian + h->padding_len); 7517ac15270Sflorian } 7527ac15270Sflorian 753*02ad6491Sblambert /* 754*02ad6491Sblambert * Fork a new CGI process to handle the request, translating 755*02ad6491Sblambert * between FastCGI parameter records and CGI's environment variables, 756*02ad6491Sblambert * as well as between the CGI process' stdin/stdout and the 757*02ad6491Sblambert * corresponding FastCGI records. 758*02ad6491Sblambert */ 7597ac15270Sflorian void 7607ac15270Sflorian exec_cgi(struct client *c) 7617ac15270Sflorian { 7627ac15270Sflorian struct env_val *env_entry; 7637ac15270Sflorian int s[2], s_err[2], i; 7647ac15270Sflorian pid_t pid; 7657ac15270Sflorian char *argv[2]; 7667ac15270Sflorian char **env; 7677ac15270Sflorian 7687ac15270Sflorian i = 0; 7697ac15270Sflorian 7707ac15270Sflorian if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s) == -1) 7717ac15270Sflorian lerr(1, "socketpair"); 7727ac15270Sflorian if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s_err) == -1) 7737ac15270Sflorian lerr(1, "socketpair"); 7747ac15270Sflorian ldebug("fork: %s", c->script_name); 775965b2218Sblambert 7767ac15270Sflorian switch (pid = fork()) { 7777ac15270Sflorian case -1: 7787ac15270Sflorian lwarn("fork"); 7797ac15270Sflorian return; 7807ac15270Sflorian case 0: 7817ac15270Sflorian /* Child process */ 7827ac15270Sflorian close(s[0]); 7837ac15270Sflorian close(s_err[0]); 7847ac15270Sflorian if (dup2(s[1], STDIN_FILENO) == -1) 7857ac15270Sflorian _exit(1); 7867ac15270Sflorian if (dup2(s[1], STDOUT_FILENO) == -1) 7877ac15270Sflorian _exit(1); 7887ac15270Sflorian if (dup2(s_err[1], STDERR_FILENO) == -1) 7897ac15270Sflorian _exit(1); 7907ac15270Sflorian argv[0] = c->script_name; 7917ac15270Sflorian argv[1] = NULL; 7927ac15270Sflorian if ((env = calloc(c->env_count + 1, sizeof(char*))) == NULL) 7937ac15270Sflorian _exit(1); 7947ac15270Sflorian SLIST_FOREACH(env_entry, &c->env, entry) 7957ac15270Sflorian env[i++] = env_entry->val; 7967ac15270Sflorian env[i++] = NULL; 7977ac15270Sflorian execve(c->script_name, argv, env); 7987ac15270Sflorian _exit(1); 7997ac15270Sflorian 8007ac15270Sflorian } 8017ac15270Sflorian /* Parent process*/ 8027ac15270Sflorian close(s[1]); 8037ac15270Sflorian close(s_err[1]); 8047ac15270Sflorian if (ioctl(s[0], FIONBIO, &on) == -1) 8057ac15270Sflorian lerr(1, "script ioctl(FIONBIO)"); 8067ac15270Sflorian if (ioctl(s_err[0], FIONBIO, &on) == -1) 8077ac15270Sflorian lerr(1, "script ioctl(FIONBIO)"); 8087ac15270Sflorian 8097ac15270Sflorian c->script_pid = pid; 8107ac15270Sflorian event_set(&c->script_ev, s[0], EV_READ | EV_PERSIST, script_std_in, c); 8117ac15270Sflorian event_add(&c->script_ev, NULL); 8127ac15270Sflorian event_set(&c->script_err_ev, s_err[0], EV_READ | EV_PERSIST, 8137ac15270Sflorian script_err_in, c); 8147ac15270Sflorian event_add(&c->script_err_ev, NULL); 8157ac15270Sflorian } 8167ac15270Sflorian 8177ac15270Sflorian void 8187ac15270Sflorian create_end_request(struct client *c) 8197ac15270Sflorian { 8207ac15270Sflorian struct fcgi_response *resp; 8217ac15270Sflorian struct fcgi_record_header *header; 82203e61f17Sblambert struct fcgi_end_request_body *end_request; 8237ac15270Sflorian 8247ac15270Sflorian if ((resp = malloc(sizeof(struct fcgi_response))) == NULL) { 8257ac15270Sflorian lwarnx("cannot malloc fcgi_response"); 8267ac15270Sflorian return; 8277ac15270Sflorian } 8287ac15270Sflorian header = (struct fcgi_record_header*) resp->data; 8297ac15270Sflorian header->version = 1; 8307ac15270Sflorian header->type = FCGI_END_REQUEST; 8317ac15270Sflorian header->id = htons(c->id); 8327ac15270Sflorian header->content_len = htons(sizeof(struct 83303e61f17Sblambert fcgi_end_request_body)); 8347ac15270Sflorian header->padding_len = 0; 8357ac15270Sflorian header->reserved = 0; 83603e61f17Sblambert end_request = (struct fcgi_end_request_body *) resp->data + 8377ac15270Sflorian sizeof(struct fcgi_record_header); 8387ac15270Sflorian end_request->app_status = htonl(c->script_status); 8397ac15270Sflorian end_request->protocol_status = FCGI_REQUEST_COMPLETE; 8407ac15270Sflorian resp->data_pos = 0; 84103e61f17Sblambert resp->data_len = sizeof(struct fcgi_end_request_body) + 8427ac15270Sflorian sizeof(struct fcgi_record_header); 8437ac15270Sflorian TAILQ_INSERT_TAIL(&c->response_head, resp, entry); 8447ac15270Sflorian } 8457ac15270Sflorian 8467ac15270Sflorian void 8477ac15270Sflorian script_in(int fd, struct event *ev, struct client *c, uint8_t type) 8487ac15270Sflorian { 8497ac15270Sflorian struct fcgi_response *resp; 8507ac15270Sflorian struct fcgi_record_header *header; 8517ac15270Sflorian ssize_t n; 8527ac15270Sflorian 8537ac15270Sflorian if ((resp = malloc(sizeof(struct fcgi_response))) == NULL) { 8547ac15270Sflorian lwarnx("cannot malloc fcgi_response"); 8557ac15270Sflorian return; 8567ac15270Sflorian } 8577ac15270Sflorian header = (struct fcgi_record_header*) resp->data; 8587ac15270Sflorian header->version = 1; 8597ac15270Sflorian header->type = type; 8607ac15270Sflorian header->id = htons(c->id); 8617ac15270Sflorian header->padding_len = 0; 8627ac15270Sflorian header->reserved = 0; 8637ac15270Sflorian 8647ac15270Sflorian n = read(fd, resp->data + sizeof(struct fcgi_record_header), 8650ee0284dSblambert FCGI_CONTENT_SIZE); 8667ac15270Sflorian 8677ac15270Sflorian if (n == -1) { 8687ac15270Sflorian switch (errno) { 8697ac15270Sflorian case EINTR: 8707ac15270Sflorian case EAGAIN: 871aa48dbd7Sjasper free(resp); 8727ac15270Sflorian return; 8737ac15270Sflorian default: 8747ac15270Sflorian n = 0; /* fake empty FCGI_STD{OUT,ERR} response */ 8757ac15270Sflorian } 8767ac15270Sflorian } 8777ac15270Sflorian header->content_len = htons(n); 8787ac15270Sflorian resp->data_pos = 0; 8797ac15270Sflorian resp->data_len = n + sizeof(struct fcgi_record_header); 8807ac15270Sflorian TAILQ_INSERT_TAIL(&c->response_head, resp, entry); 8817ac15270Sflorian 8827ac15270Sflorian event_del(&c->resp_ev); 8837ac15270Sflorian event_set(&c->resp_ev, EVENT_FD(&c->ev), EV_WRITE | EV_PERSIST, 8847ac15270Sflorian slowcgi_response, c); 8857ac15270Sflorian event_add(&c->resp_ev, NULL); 8867ac15270Sflorian 8877ac15270Sflorian if (n == 0) { 8887ac15270Sflorian if (type == FCGI_STDOUT) 8897ac15270Sflorian c->script_flags |= STDOUT_DONE; 8907ac15270Sflorian else 8917ac15270Sflorian c->script_flags |= STDERR_DONE; 8927ac15270Sflorian 8937ac15270Sflorian if (c->script_flags == (STDOUT_DONE | STDERR_DONE | 8947ac15270Sflorian SCRIPT_DONE)) { 8957ac15270Sflorian create_end_request(c); 8967ac15270Sflorian } 8977ac15270Sflorian event_del(ev); 8987ac15270Sflorian close(fd); 8997ac15270Sflorian } 9007ac15270Sflorian } 9017ac15270Sflorian 9027ac15270Sflorian void 9037ac15270Sflorian script_std_in(int fd, short events, void *arg) 9047ac15270Sflorian { 9057ac15270Sflorian struct client *c = arg; 9067ac15270Sflorian script_in(fd, &c->script_ev, c, FCGI_STDOUT); 9077ac15270Sflorian } 9087ac15270Sflorian 9097ac15270Sflorian void 9107ac15270Sflorian script_err_in(int fd, short events, void *arg) 9117ac15270Sflorian { 9127ac15270Sflorian struct client *c = arg; 9137ac15270Sflorian script_in(fd, &c->script_err_ev, c, FCGI_STDERR); 9147ac15270Sflorian } 9157ac15270Sflorian 9167ac15270Sflorian void 9177ac15270Sflorian script_out(int fd, short events, void *arg) 9187ac15270Sflorian { 9197ac15270Sflorian struct client *c; 9207ac15270Sflorian struct fcgi_stdin *node; 9217ac15270Sflorian ssize_t n; 9227ac15270Sflorian 9237ac15270Sflorian c = arg; 9247ac15270Sflorian 9257ac15270Sflorian while ((node = TAILQ_FIRST(&c->stdin_head))) { 9267ac15270Sflorian if (node->data_len == 0) { /* end of stdin marker */ 9277ac15270Sflorian shutdown(fd, SHUT_WR); 9287ac15270Sflorian break; 9297ac15270Sflorian } 9307ac15270Sflorian n = write(fd, node->data + node->data_pos, node->data_len); 9317ac15270Sflorian if (n == -1) { 9327ac15270Sflorian if (errno == EAGAIN) 9337ac15270Sflorian return; 9347ac15270Sflorian event_del(&c->script_stdin_ev); 9357ac15270Sflorian return; 9367ac15270Sflorian } 9377ac15270Sflorian node->data_pos += n; 9387ac15270Sflorian node->data_len -= n; 9397ac15270Sflorian if (node->data_len == 0) { 9407ac15270Sflorian TAILQ_REMOVE(&c->stdin_head, node, entry); 9417ac15270Sflorian free(node); 9427ac15270Sflorian } 9437ac15270Sflorian } 9447ac15270Sflorian } 9457ac15270Sflorian 9467ac15270Sflorian void 9477ac15270Sflorian cleanup_client(struct client *c) 9487ac15270Sflorian { 9497ac15270Sflorian struct fcgi_response *resp; 9507ac15270Sflorian struct fcgi_stdin *stdin_node; 9517ac15270Sflorian struct env_val *env_entry; 9527ac15270Sflorian struct clients *ncs, *tcs; 9537ac15270Sflorian 9547ac15270Sflorian evtimer_del(&c->tmo); 9557ac15270Sflorian if (event_initialized(&c->ev)) 9567ac15270Sflorian event_del(&c->ev); 9577ac15270Sflorian if (event_initialized(&c->resp_ev)) 9587ac15270Sflorian event_del(&c->resp_ev); 9597ac15270Sflorian if (event_initialized(&c->script_ev)) { 9607ac15270Sflorian close(EVENT_FD(&c->script_ev)); 9617ac15270Sflorian event_del(&c->script_ev); 9627ac15270Sflorian } 9637ac15270Sflorian if (event_initialized(&c->script_err_ev)) { 9647ac15270Sflorian close(EVENT_FD(&c->script_err_ev)); 9657ac15270Sflorian event_del(&c->script_err_ev); 9667ac15270Sflorian } 9677ac15270Sflorian if (event_initialized(&c->script_stdin_ev)) { 9687ac15270Sflorian close(EVENT_FD(&c->script_stdin_ev)); 9697ac15270Sflorian event_del(&c->script_stdin_ev); 9707ac15270Sflorian } 9717ac15270Sflorian close(c->fd); 9727ac15270Sflorian while (!SLIST_EMPTY(&c->env)) { 9737ac15270Sflorian env_entry = SLIST_FIRST(&c->env); 9747ac15270Sflorian SLIST_REMOVE_HEAD(&c->env, entry); 9757ac15270Sflorian free(env_entry->val); 9767ac15270Sflorian free(env_entry); 9777ac15270Sflorian } 9787ac15270Sflorian 9797ac15270Sflorian while ((resp = TAILQ_FIRST(&c->response_head))) { 9807ac15270Sflorian TAILQ_REMOVE(&c->response_head, resp, entry); 9817ac15270Sflorian free(resp); 9827ac15270Sflorian } 9837ac15270Sflorian while ((stdin_node = TAILQ_FIRST(&c->stdin_head))) { 9847ac15270Sflorian TAILQ_REMOVE(&c->stdin_head, stdin_node, entry); 9857ac15270Sflorian free(stdin_node); 9867ac15270Sflorian } 9877ac15270Sflorian SLIST_FOREACH_SAFE(ncs, &slowcgi_proc.clients, entry, tcs) { 9887ac15270Sflorian if (ncs->client == c) { 9897ac15270Sflorian SLIST_REMOVE(&slowcgi_proc.clients, ncs, clients, 9907ac15270Sflorian entry); 9917ac15270Sflorian free(ncs); 9927ac15270Sflorian break; 9937ac15270Sflorian } 9947ac15270Sflorian } 9957ac15270Sflorian free(c); 9967ac15270Sflorian } 9977ac15270Sflorian 998965b2218Sblambert void 99903e61f17Sblambert dump_fcgi_record(const char *p, struct fcgi_record_header *h) 100003e61f17Sblambert { 100103e61f17Sblambert dump_fcgi_record_header(p, h); 100203e61f17Sblambert 100303e61f17Sblambert if (h->type == FCGI_BEGIN_REQUEST) 100403e61f17Sblambert dump_fcgi_begin_request_body(p, 100503e61f17Sblambert (struct fcgi_begin_request_body *)((char *)h) + sizeof(*h)); 100603e61f17Sblambert else if (h->type == FCGI_END_REQUEST) 100703e61f17Sblambert dump_fcgi_end_request_body(p, 100803e61f17Sblambert (struct fcgi_end_request_body *)((char *)h) + sizeof(*h)); 100903e61f17Sblambert } 101003e61f17Sblambert 101103e61f17Sblambert void 1012965b2218Sblambert dump_fcgi_record_header(const char* p, struct fcgi_record_header *h) 10137ac15270Sflorian { 10147ac15270Sflorian ldebug("%sversion: %d", p, h->version); 10157ac15270Sflorian ldebug("%stype: %d", p, h->type); 10167ac15270Sflorian ldebug("%srequestId: %d", p, ntohs(h->id)); 10177ac15270Sflorian ldebug("%scontentLength: %d", p, ntohs(h->content_len)); 10187ac15270Sflorian ldebug("%spaddingLength: %d", p, h->padding_len); 10197ac15270Sflorian ldebug("%sreserved: %d", p, h->reserved); 10207ac15270Sflorian } 10217ac15270Sflorian 10227ac15270Sflorian void 102303e61f17Sblambert dump_fcgi_begin_request_body(const char *p, struct fcgi_begin_request_body *b) 102403e61f17Sblambert { 102503e61f17Sblambert ldebug("%srole %d", p, ntohs(b->role)); 102603e61f17Sblambert ldebug("%sflags %d", p, b->flags); 102703e61f17Sblambert } 102803e61f17Sblambert 102903e61f17Sblambert void 103003e61f17Sblambert dump_fcgi_end_request_body(const char *p, struct fcgi_end_request_body *b) 103103e61f17Sblambert { 103203e61f17Sblambert ldebug("%sappStatus: %d", p, ntohl(b->app_status)); 103303e61f17Sblambert ldebug("%sprotocolStatus: %d", p, b->protocol_status); 103403e61f17Sblambert } 103503e61f17Sblambert 103603e61f17Sblambert void 10377ac15270Sflorian syslog_vstrerror(int e, int priority, const char *fmt, va_list ap) 10387ac15270Sflorian { 10397ac15270Sflorian char *s; 10407ac15270Sflorian 10417ac15270Sflorian if (vasprintf(&s, fmt, ap) == -1) { 10427ac15270Sflorian syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror"); 10437ac15270Sflorian exit(1); 10447ac15270Sflorian } 10457ac15270Sflorian syslog(priority, "%s: %s", s, strerror(e)); 10467ac15270Sflorian free(s); 10477ac15270Sflorian } 10487ac15270Sflorian 10497ac15270Sflorian void 10507ac15270Sflorian syslog_err(int ecode, const char *fmt, ...) 10517ac15270Sflorian { 10527ac15270Sflorian va_list ap; 10537ac15270Sflorian 10547ac15270Sflorian va_start(ap, fmt); 10557ac15270Sflorian syslog_vstrerror(errno, LOG_EMERG, fmt, ap); 10567ac15270Sflorian va_end(ap); 10577ac15270Sflorian exit(ecode); 10587ac15270Sflorian } 10597ac15270Sflorian 10607ac15270Sflorian void 10617ac15270Sflorian syslog_errx(int ecode, const char *fmt, ...) 10627ac15270Sflorian { 10637ac15270Sflorian va_list ap; 10647ac15270Sflorian 10657ac15270Sflorian va_start(ap, fmt); 10667ac15270Sflorian vsyslog(LOG_WARNING, fmt, ap); 10677ac15270Sflorian va_end(ap); 10687ac15270Sflorian exit(ecode); 10697ac15270Sflorian } 10707ac15270Sflorian 10717ac15270Sflorian void 10727ac15270Sflorian syslog_warn(const char *fmt, ...) 10737ac15270Sflorian { 10747ac15270Sflorian va_list ap; 10757ac15270Sflorian 10767ac15270Sflorian va_start(ap, fmt); 10777ac15270Sflorian syslog_vstrerror(errno, LOG_WARNING, fmt, ap); 10787ac15270Sflorian va_end(ap); 10797ac15270Sflorian } 10807ac15270Sflorian 10817ac15270Sflorian void 10827ac15270Sflorian syslog_warnx(const char *fmt, ...) 10837ac15270Sflorian { 10847ac15270Sflorian va_list ap; 10857ac15270Sflorian 10867ac15270Sflorian va_start(ap, fmt); 10877ac15270Sflorian vsyslog(LOG_WARNING, fmt, ap); 10887ac15270Sflorian va_end(ap); 10897ac15270Sflorian } 10907ac15270Sflorian 10917ac15270Sflorian void 10927ac15270Sflorian syslog_info(const char *fmt, ...) 10937ac15270Sflorian { 10947ac15270Sflorian va_list ap; 10957ac15270Sflorian 10967ac15270Sflorian va_start(ap, fmt); 10977ac15270Sflorian vsyslog(LOG_INFO, fmt, ap); 10987ac15270Sflorian va_end(ap); 10997ac15270Sflorian } 11007ac15270Sflorian 11017ac15270Sflorian void 11027ac15270Sflorian syslog_debug(const char *fmt, ...) 11037ac15270Sflorian { 11047ac15270Sflorian va_list ap; 11057ac15270Sflorian 11067ac15270Sflorian if (!debug) 11077ac15270Sflorian return; 11087ac15270Sflorian 11097ac15270Sflorian va_start(ap, fmt); 11107ac15270Sflorian vsyslog(LOG_DEBUG, fmt, ap); 11117ac15270Sflorian va_end(ap); 11127ac15270Sflorian } 1113