1*07240181Sreyk /* $OpenBSD: slowcgi.c,v 1.49 2016/08/16 08:23:18 reyk 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/stat.h> 24e09b0855Sflorian #include <sys/time.h> 257ac15270Sflorian #include <sys/un.h> 267ac15270Sflorian #include <sys/wait.h> 27e09b0855Sflorian #include <arpa/inet.h> 287ac15270Sflorian #include <err.h> 2994ee156dSflorian #include <fcntl.h> 307ac15270Sflorian #include <errno.h> 317ac15270Sflorian #include <event.h> 32b9fc9a72Sderaadt #include <limits.h> 337ac15270Sflorian #include <pwd.h> 347ac15270Sflorian #include <signal.h> 35e09b0855Sflorian #include <stdarg.h> 367ac15270Sflorian #include <stdio.h> 377ac15270Sflorian #include <stdlib.h> 387ac15270Sflorian #include <string.h> 397ac15270Sflorian #include <syslog.h> 407ac15270Sflorian #include <unistd.h> 417ac15270Sflorian 427ac15270Sflorian #define TIMEOUT_DEFAULT 120 437ac15270Sflorian #define SLOWCGI_USER "www" 440ee0284dSblambert 450ee0284dSblambert #define FCGI_CONTENT_SIZE 65535 460ee0284dSblambert #define FCGI_PADDING_SIZE 255 470ee0284dSblambert #define FCGI_RECORD_SIZE \ 480ee0284dSblambert (sizeof(struct fcgi_record_header) + FCGI_CONTENT_SIZE + FCGI_PADDING_SIZE) 490ee0284dSblambert 50*07240181Sreyk #define FCGI_ALIGNMENT 8 51*07240181Sreyk #define FCGI_ALIGN(n) \ 52*07240181Sreyk (((n) + (FCGI_ALIGNMENT - 1)) & ~(FCGI_ALIGNMENT - 1)) 53*07240181Sreyk 547ac15270Sflorian #define STDOUT_DONE 1 557ac15270Sflorian #define STDERR_DONE 2 567ac15270Sflorian #define SCRIPT_DONE 4 577ac15270Sflorian 587ac15270Sflorian #define FCGI_BEGIN_REQUEST 1 597ac15270Sflorian #define FCGI_ABORT_REQUEST 2 607ac15270Sflorian #define FCGI_END_REQUEST 3 617ac15270Sflorian #define FCGI_PARAMS 4 627ac15270Sflorian #define FCGI_STDIN 5 637ac15270Sflorian #define FCGI_STDOUT 6 647ac15270Sflorian #define FCGI_STDERR 7 657ac15270Sflorian #define FCGI_DATA 8 667ac15270Sflorian #define FCGI_GET_VALUES 9 677ac15270Sflorian #define FCGI_GET_VALUES_RESULT 10 687ac15270Sflorian #define FCGI_UNKNOWN_TYPE 11 697ac15270Sflorian #define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE) 707ac15270Sflorian 717ac15270Sflorian #define FCGI_REQUEST_COMPLETE 0 727ac15270Sflorian #define FCGI_CANT_MPX_CONN 1 737ac15270Sflorian #define FCGI_OVERLOADED 2 747ac15270Sflorian #define FCGI_UNKNOWN_ROLE 3 757ac15270Sflorian 763f9f8982Sbenno #define FD_RESERVE 5 773f9f8982Sbenno #define FD_NEEDED 6 78d2b70008Sflorian int cgi_inflight = 0; 797ac15270Sflorian 807ac15270Sflorian struct listener { 817ac15270Sflorian struct event ev, pause; 827ac15270Sflorian }; 837ac15270Sflorian 847ac15270Sflorian struct env_val { 857ac15270Sflorian SLIST_ENTRY(env_val) entry; 867ac15270Sflorian char *val; 877ac15270Sflorian }; 887ac15270Sflorian SLIST_HEAD(env_head, env_val); 897ac15270Sflorian 907ac15270Sflorian struct fcgi_record_header { 917ac15270Sflorian uint8_t version; 927ac15270Sflorian uint8_t type; 937ac15270Sflorian uint16_t id; 947ac15270Sflorian uint16_t content_len; 957ac15270Sflorian uint8_t padding_len; 967ac15270Sflorian uint8_t reserved; 977ac15270Sflorian }__packed; 987ac15270Sflorian 997ac15270Sflorian struct fcgi_response { 1007ac15270Sflorian TAILQ_ENTRY(fcgi_response) entry; 1010ee0284dSblambert uint8_t data[FCGI_RECORD_SIZE]; 1027ac15270Sflorian size_t data_pos; 1037ac15270Sflorian size_t data_len; 1047ac15270Sflorian }; 1057ac15270Sflorian TAILQ_HEAD(fcgi_response_head, fcgi_response); 1067ac15270Sflorian 1077ac15270Sflorian struct fcgi_stdin { 1087ac15270Sflorian TAILQ_ENTRY(fcgi_stdin) entry; 1097ac15270Sflorian uint8_t data[FCGI_RECORD_SIZE]; 1107ac15270Sflorian size_t data_pos; 1117ac15270Sflorian size_t data_len; 1127ac15270Sflorian }; 1137ac15270Sflorian TAILQ_HEAD(fcgi_stdin_head, fcgi_stdin); 1147ac15270Sflorian 115c3b13bc8Sblambert struct request { 1167ac15270Sflorian struct event ev; 1177ac15270Sflorian struct event resp_ev; 1187ac15270Sflorian struct event tmo; 1197ac15270Sflorian int fd; 1207ac15270Sflorian uint8_t buf[FCGI_RECORD_SIZE]; 1217ac15270Sflorian size_t buf_pos; 1227ac15270Sflorian size_t buf_len; 1237ac15270Sflorian struct fcgi_response_head response_head; 1247ac15270Sflorian struct fcgi_stdin_head stdin_head; 1257ac15270Sflorian uint16_t id; 126b9fc9a72Sderaadt char script_name[PATH_MAX]; 1277ac15270Sflorian struct env_head env; 1287ac15270Sflorian int env_count; 1297ac15270Sflorian pid_t script_pid; 1307ac15270Sflorian int script_status; 1317ac15270Sflorian struct event script_ev; 1327ac15270Sflorian struct event script_err_ev; 1337ac15270Sflorian struct event script_stdin_ev; 1346bdc2311Sflorian int stdin_fd_closed; 1356bdc2311Sflorian int stdout_fd_closed; 1366bdc2311Sflorian int stderr_fd_closed; 1377ac15270Sflorian uint8_t script_flags; 1387ac15270Sflorian uint8_t request_started; 1393f9f8982Sbenno int inflight_fds_accounted; 1407ac15270Sflorian }; 1417ac15270Sflorian 142c3b13bc8Sblambert struct requests { 143c3b13bc8Sblambert SLIST_ENTRY(requests) entry; 144c3b13bc8Sblambert struct request *request; 1457ac15270Sflorian }; 146c3b13bc8Sblambert SLIST_HEAD(requests_head, requests); 1477ac15270Sflorian 1487ac15270Sflorian struct slowcgi_proc { 149c3b13bc8Sblambert struct requests_head requests; 1507ac15270Sflorian struct event ev_sigchld; 1517ac15270Sflorian struct event ev_sigpipe; 1527ac15270Sflorian }; 1537ac15270Sflorian 1547ac15270Sflorian struct fcgi_begin_request_body { 1557ac15270Sflorian uint16_t role; 1567ac15270Sflorian uint8_t flags; 1577ac15270Sflorian uint8_t reserved[5]; 1587ac15270Sflorian }__packed; 1597ac15270Sflorian 16003e61f17Sblambert struct fcgi_end_request_body { 1617ac15270Sflorian uint32_t app_status; 1627ac15270Sflorian uint8_t protocol_status; 1637ac15270Sflorian uint8_t reserved[3]; 1647ac15270Sflorian }__packed; 16503e61f17Sblambert 1667ac15270Sflorian __dead void usage(void); 1670897de71Sflorian int slowcgi_listen(char *, struct passwd *); 1687ac15270Sflorian void slowcgi_paused(int, short, void *); 1693f9f8982Sbenno int accept_reserve(int, struct sockaddr *, socklen_t *, int, 1703f9f8982Sbenno volatile int *); 1717ac15270Sflorian void slowcgi_accept(int, short, void *); 1727ac15270Sflorian void slowcgi_request(int, short, void *); 1737ac15270Sflorian void slowcgi_response(int, short, void *); 1744404b4d5Sflorian void slowcgi_add_response(struct request *, struct fcgi_response *); 1757ac15270Sflorian void slowcgi_timeout(int, short, void *); 1767ac15270Sflorian void slowcgi_sig_handler(int, short, void *); 177c3b13bc8Sblambert size_t parse_record(uint8_t * , size_t, struct request *); 178c3b13bc8Sblambert void parse_begin_request(uint8_t *, uint16_t, struct request *, 1797ac15270Sflorian uint16_t); 180c3b13bc8Sblambert void parse_params(uint8_t *, uint16_t, struct request *, uint16_t); 181c3b13bc8Sblambert void parse_stdin(uint8_t *, uint16_t, struct request *, uint16_t); 182c3b13bc8Sblambert void exec_cgi(struct request *); 183c3b13bc8Sblambert void script_in(int, struct event *, struct request *, uint8_t); 1847ac15270Sflorian void script_std_in(int, short, void *); 1857ac15270Sflorian void script_err_in(int, short, void *); 1867ac15270Sflorian void script_out(int, short, void *); 187c3b13bc8Sblambert void create_end_record(struct request *); 18803e61f17Sblambert void dump_fcgi_record(const char *, 18903e61f17Sblambert struct fcgi_record_header *); 1907ac15270Sflorian void dump_fcgi_record_header(const char *, 1917ac15270Sflorian struct fcgi_record_header *); 19203e61f17Sblambert void dump_fcgi_begin_request_body(const char *, 19303e61f17Sblambert struct fcgi_begin_request_body *); 19403e61f17Sblambert void dump_fcgi_end_request_body(const char *, 19503e61f17Sblambert struct fcgi_end_request_body *); 196c3b13bc8Sblambert void cleanup_request(struct request *); 19703e61f17Sblambert 1987ac15270Sflorian struct loggers { 199d0a52cffSflorian __dead void (*err)(int, const char *, ...); 200d0a52cffSflorian __dead void (*errx)(int, const char *, ...); 2017ac15270Sflorian void (*warn)(const char *, ...); 2027ac15270Sflorian void (*warnx)(const char *, ...); 2037ac15270Sflorian void (*info)(const char *, ...); 2047ac15270Sflorian void (*debug)(const char *, ...); 2057ac15270Sflorian }; 2067ac15270Sflorian 2077ac15270Sflorian const struct loggers conslogger = { 2087ac15270Sflorian err, 2097ac15270Sflorian errx, 2107ac15270Sflorian warn, 2117ac15270Sflorian warnx, 2127ac15270Sflorian warnx, /* info */ 2137ac15270Sflorian warnx /* debug */ 2147ac15270Sflorian }; 2157ac15270Sflorian 216d0a52cffSflorian __dead void syslog_err(int, const char *, ...); 217d0a52cffSflorian __dead void syslog_errx(int, const char *, ...); 2187ac15270Sflorian void syslog_warn(const char *, ...); 2197ac15270Sflorian void syslog_warnx(const char *, ...); 2207ac15270Sflorian void syslog_info(const char *, ...); 2217ac15270Sflorian void syslog_debug(const char *, ...); 2227ac15270Sflorian void syslog_vstrerror(int, int, const char *, va_list); 2237ac15270Sflorian 2247ac15270Sflorian const struct loggers syslogger = { 2257ac15270Sflorian syslog_err, 2267ac15270Sflorian syslog_errx, 2277ac15270Sflorian syslog_warn, 2287ac15270Sflorian syslog_warnx, 2297ac15270Sflorian syslog_info, 2307ac15270Sflorian syslog_debug 2317ac15270Sflorian }; 2327ac15270Sflorian 2337ac15270Sflorian const struct loggers *logger = &conslogger; 2347ac15270Sflorian 2357ac15270Sflorian #define lerr(_e, _f...) logger->err((_e), _f) 2367ac15270Sflorian #define lerrx(_e, _f...) logger->errx((_e), _f) 2377ac15270Sflorian #define lwarn(_f...) logger->warn(_f) 2387ac15270Sflorian #define lwarnx(_f...) logger->warnx(_f) 2397ac15270Sflorian #define linfo(_f...) logger->info(_f) 2407ac15270Sflorian #define ldebug(_f...) logger->debug(_f) 2417ac15270Sflorian 2427ac15270Sflorian __dead void 2437ac15270Sflorian usage(void) 2447ac15270Sflorian { 2457ac15270Sflorian extern char *__progname; 246a3b046bcSflorian fprintf(stderr, "usage: %s [-d] [-p path] [-s socket] [-u user]\n", 247a3b046bcSflorian __progname); 2487ac15270Sflorian exit(1); 2497ac15270Sflorian } 2507ac15270Sflorian 2517ac15270Sflorian struct timeval timeout = { TIMEOUT_DEFAULT, 0 }; 2527ac15270Sflorian struct slowcgi_proc slowcgi_proc; 2537ac15270Sflorian int debug = 0; 2547ac15270Sflorian int on = 1; 2550b5ee19eSblambert char *fcgi_socket = "/var/www/run/slowcgi.sock"; 2567ac15270Sflorian 2577ac15270Sflorian int 2587ac15270Sflorian main(int argc, char *argv[]) 2597ac15270Sflorian { 26006619349Sflorian extern char *__progname; 2610897de71Sflorian struct listener *l = NULL; 2627ac15270Sflorian struct passwd *pw; 263f4a8b980Sflorian struct stat sb; 264f4a8b980Sflorian int c, fd; 265a3b046bcSflorian const char *chrootpath = NULL; 266a3b046bcSflorian const char *slowcgi_user = SLOWCGI_USER; 267f4a8b980Sflorian 268f4a8b980Sflorian /* 269f4a8b980Sflorian * Ensure we have fds 0-2 open so that we have no fd overlaps 270f4a8b980Sflorian * in exec_cgi() later. Just exit on error, we don't have enough 271f4a8b980Sflorian * fds open to output an error message anywhere. 272f4a8b980Sflorian */ 273f4a8b980Sflorian for (c=0; c < 3; c++) { 274f4a8b980Sflorian if (fstat(c, &sb) == -1) { 275f4a8b980Sflorian if ((fd = open("/dev/null", O_RDWR)) != -1) { 276f4a8b980Sflorian if (dup2(fd, c) == -1) 277f4a8b980Sflorian exit(1); 278f4a8b980Sflorian if (fd > c) 279f4a8b980Sflorian close(fd); 280f4a8b980Sflorian } else 281f4a8b980Sflorian exit(1); 282f4a8b980Sflorian } 283f4a8b980Sflorian } 2847ac15270Sflorian 285a3b046bcSflorian while ((c = getopt(argc, argv, "dp:s:u:")) != -1) { 2867ac15270Sflorian switch (c) { 2877ac15270Sflorian case 'd': 2887ac15270Sflorian debug = 1; 2897ac15270Sflorian break; 290a3b046bcSflorian case 'p': 291a3b046bcSflorian chrootpath = optarg; 292a3b046bcSflorian break; 2930b5ee19eSblambert case 's': 2940b5ee19eSblambert fcgi_socket = optarg; 2950b5ee19eSblambert break; 296a3b046bcSflorian case 'u': 297a3b046bcSflorian slowcgi_user = optarg; 298a3b046bcSflorian break; 2997ac15270Sflorian default: 3007ac15270Sflorian usage(); 3017ac15270Sflorian /* NOTREACHED */ 3027ac15270Sflorian } 3037ac15270Sflorian } 3047ac15270Sflorian 3057ac15270Sflorian if (geteuid() != 0) 3067ac15270Sflorian errx(1, "need root privileges"); 3077ac15270Sflorian 3087ac15270Sflorian if (!debug && daemon(1, 0) == -1) 3097ac15270Sflorian err(1, "daemon"); 3107ac15270Sflorian 31106619349Sflorian if (!debug) { 31206619349Sflorian openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON); 31306619349Sflorian logger = &syslogger; 31406619349Sflorian } 31506619349Sflorian 316a3b046bcSflorian pw = getpwnam(SLOWCGI_USER); 317a3b046bcSflorian if (pw == NULL) 3186af1d02fSclaudio lerrx(1, "no %s user", SLOWCGI_USER); 319a3b046bcSflorian 3200897de71Sflorian fd = slowcgi_listen(fcgi_socket, pw); 3210b5ee19eSblambert 322a3b046bcSflorian lwarnx("slowcgi_user: %s", slowcgi_user); 323a3b046bcSflorian pw = getpwnam(slowcgi_user); 324a3b046bcSflorian if (pw == NULL) 3256af1d02fSclaudio lerrx(1, "no %s user", slowcgi_user); 326a3b046bcSflorian 327a3b046bcSflorian if (chrootpath == NULL) 328a3b046bcSflorian chrootpath = pw->pw_dir; 329a3b046bcSflorian 330a3b046bcSflorian if (chroot(chrootpath) == -1) 331a3b046bcSflorian lerr(1, "chroot(%s)", chrootpath); 332a3b046bcSflorian 333a3b046bcSflorian ldebug("chroot: %s", chrootpath); 3347ac15270Sflorian 3357ac15270Sflorian if (chdir("/") == -1) 336a3b046bcSflorian lerr(1, "chdir(/)"); 337bde99fd8Sblambert 3387ac15270Sflorian if (setgroups(1, &pw->pw_gid) || 3397ac15270Sflorian setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 3407ac15270Sflorian setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 3417ac15270Sflorian lerr(1, "unable to revoke privs"); 3427ac15270Sflorian 3431bfdb260Sflorian if (pledge("stdio rpath unix proc exec", NULL) == -1) 3441bfdb260Sflorian lerr(1, "pledge"); 3451bfdb260Sflorian 346c3b13bc8Sblambert SLIST_INIT(&slowcgi_proc.requests); 3470897de71Sflorian event_init(); 3480897de71Sflorian 3490897de71Sflorian l = calloc(1, sizeof(*l)); 3500897de71Sflorian if (l == NULL) 3510897de71Sflorian lerr(1, "listener ev alloc"); 3520897de71Sflorian 3530897de71Sflorian event_set(&l->ev, fd, EV_READ | EV_PERSIST, slowcgi_accept, l); 3540897de71Sflorian event_add(&l->ev, NULL); 3550897de71Sflorian evtimer_set(&l->pause, slowcgi_paused, l); 3567ac15270Sflorian 3577ac15270Sflorian signal_set(&slowcgi_proc.ev_sigchld, SIGCHLD, slowcgi_sig_handler, 3587ac15270Sflorian &slowcgi_proc); 3597ac15270Sflorian signal_set(&slowcgi_proc.ev_sigpipe, SIGPIPE, slowcgi_sig_handler, 3607ac15270Sflorian &slowcgi_proc); 3617ac15270Sflorian 3627ac15270Sflorian signal_add(&slowcgi_proc.ev_sigchld, NULL); 3637ac15270Sflorian signal_add(&slowcgi_proc.ev_sigpipe, NULL); 3647ac15270Sflorian 3657ac15270Sflorian event_dispatch(); 3667ac15270Sflorian return (0); 3677ac15270Sflorian } 3680897de71Sflorian 3690897de71Sflorian int 370f6474c66Sflorian slowcgi_listen(char *path, struct passwd *pw) 3717ac15270Sflorian { 3727ac15270Sflorian struct sockaddr_un sun; 373d0c661abSflorian mode_t old_umask; 3747ac15270Sflorian int fd; 3757ac15270Sflorian 376d253f95aSflorian if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 377d253f95aSflorian 0)) == -1) 3787ac15270Sflorian lerr(1, "slowcgi_listen: socket"); 3797ac15270Sflorian 3807ac15270Sflorian bzero(&sun, sizeof(sun)); 3817ac15270Sflorian sun.sun_family = AF_UNIX; 382cdf4d38dSguenther if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >= 383cdf4d38dSguenther sizeof(sun.sun_path)) 384d444d2f2Stb lerrx(1, "socket path too long"); 3857ac15270Sflorian 3867ac15270Sflorian if (unlink(path) == -1) 3877ac15270Sflorian if (errno != ENOENT) 3887ac15270Sflorian lerr(1, "slowcgi_listen: unlink %s", path); 3897ac15270Sflorian 390f6474c66Sflorian old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH| 391f6474c66Sflorian S_IXOTH); 3927ac15270Sflorian 3937ac15270Sflorian if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) 3947ac15270Sflorian lerr(1,"slowcgi_listen: bind: %s", path); 3957ac15270Sflorian 3967ac15270Sflorian umask(old_umask); 3977ac15270Sflorian 398f6474c66Sflorian if (chown(path, pw->pw_uid, pw->pw_gid) == -1) 3997ac15270Sflorian lerr(1, "slowcgi_listen: chown: %s", path); 4007ac15270Sflorian 4017ac15270Sflorian if (listen(fd, 5) == -1) 4027ac15270Sflorian lerr(1, "listen"); 4037ac15270Sflorian 404bde99fd8Sblambert ldebug("socket: %s", path); 4050897de71Sflorian return fd; 4067ac15270Sflorian } 4077ac15270Sflorian 4087ac15270Sflorian void 4097ac15270Sflorian slowcgi_paused(int fd, short events, void *arg) 4107ac15270Sflorian { 4117ac15270Sflorian struct listener *l = arg; 4127ac15270Sflorian event_add(&l->ev, NULL); 4137ac15270Sflorian } 4147ac15270Sflorian 4153f9f8982Sbenno int 4163f9f8982Sbenno accept_reserve(int sockfd, struct sockaddr *addr, socklen_t *addrlen, 4173f9f8982Sbenno int reserve, volatile int *counter) 4183f9f8982Sbenno { 4193f9f8982Sbenno int ret; 4203f9f8982Sbenno if (getdtablecount() + reserve + 4213f9f8982Sbenno (*counter * FD_NEEDED) >= getdtablesize()) { 422f8701677Sderaadt ldebug("inflight fds exceeded"); 4233f9f8982Sbenno errno = EMFILE; 4243f9f8982Sbenno return -1; 4253f9f8982Sbenno } 4263f9f8982Sbenno 427d253f95aSflorian if ((ret = accept4(sockfd, addr, addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC)) 428d253f95aSflorian > -1) { 4293f9f8982Sbenno (*counter)++; 4303f9f8982Sbenno ldebug("inflight incremented, now %d", *counter); 4313f9f8982Sbenno } 4323f9f8982Sbenno return ret; 4333f9f8982Sbenno } 4343f9f8982Sbenno 4357ac15270Sflorian void 4367ac15270Sflorian slowcgi_accept(int fd, short events, void *arg) 4377ac15270Sflorian { 4387ac15270Sflorian struct listener *l; 4397ac15270Sflorian struct sockaddr_storage ss; 440adf2d9e3Sflorian struct timeval backoff; 441c3b13bc8Sblambert struct request *c; 442c3b13bc8Sblambert struct requests *requests; 4437ac15270Sflorian socklen_t len; 4447ac15270Sflorian int s; 4457ac15270Sflorian 4467ac15270Sflorian l = arg; 447adf2d9e3Sflorian backoff.tv_sec = 1; 448adf2d9e3Sflorian backoff.tv_usec = 0; 4497ac15270Sflorian c = NULL; 4507ac15270Sflorian 4517ac15270Sflorian len = sizeof(ss); 4523f9f8982Sbenno if ((s = accept_reserve(fd, (struct sockaddr *)&ss, 4533f9f8982Sbenno &len, FD_RESERVE, &cgi_inflight)) == -1) { 4547ac15270Sflorian switch (errno) { 4557ac15270Sflorian case EINTR: 4567ac15270Sflorian case EWOULDBLOCK: 4577ac15270Sflorian case ECONNABORTED: 4587ac15270Sflorian return; 4597ac15270Sflorian case EMFILE: 4607ac15270Sflorian case ENFILE: 4617ac15270Sflorian event_del(&l->ev); 462adf2d9e3Sflorian evtimer_add(&l->pause, &backoff); 4637ac15270Sflorian return; 4647ac15270Sflorian default: 4657ac15270Sflorian lerr(1, "accept"); 4667ac15270Sflorian } 4677ac15270Sflorian } 4687ac15270Sflorian 4697ac15270Sflorian c = calloc(1, sizeof(*c)); 4707ac15270Sflorian if (c == NULL) { 471c3b13bc8Sblambert lwarn("cannot calloc request"); 4727ac15270Sflorian close(s); 4733f9f8982Sbenno cgi_inflight--; 4747ac15270Sflorian return; 4757ac15270Sflorian } 476c3b13bc8Sblambert requests = calloc(1, sizeof(*requests)); 477c3b13bc8Sblambert if (requests == NULL) { 478c3b13bc8Sblambert lwarn("cannot calloc requests"); 4797ac15270Sflorian close(s); 4803f9f8982Sbenno cgi_inflight--; 4817ac15270Sflorian free(c); 4827ac15270Sflorian return; 4837ac15270Sflorian } 4847ac15270Sflorian c->fd = s; 4857ac15270Sflorian c->buf_pos = 0; 4867ac15270Sflorian c->buf_len = 0; 4877ac15270Sflorian c->request_started = 0; 4886bdc2311Sflorian c->stdin_fd_closed = c->stdout_fd_closed = c->stderr_fd_closed = 0; 4893f9f8982Sbenno c->inflight_fds_accounted = 0; 4907ac15270Sflorian TAILQ_INIT(&c->response_head); 4917ac15270Sflorian TAILQ_INIT(&c->stdin_head); 4927ac15270Sflorian 4937ac15270Sflorian event_set(&c->ev, s, EV_READ | EV_PERSIST, slowcgi_request, c); 4947ac15270Sflorian event_add(&c->ev, NULL); 4954404b4d5Sflorian event_set(&c->resp_ev, s, EV_WRITE | EV_PERSIST, slowcgi_response, c); 4967ac15270Sflorian event_set(&c->tmo, s, 0, slowcgi_timeout, c); 4977ac15270Sflorian event_add(&c->tmo, &timeout); 498c3b13bc8Sblambert requests->request = c; 499c3b13bc8Sblambert SLIST_INSERT_HEAD(&slowcgi_proc.requests, requests, entry); 5007ac15270Sflorian } 5017ac15270Sflorian 5027ac15270Sflorian void 5037ac15270Sflorian slowcgi_timeout(int fd, short events, void *arg) 5047ac15270Sflorian { 505c3b13bc8Sblambert cleanup_request((struct request*) arg); 5067ac15270Sflorian } 5077ac15270Sflorian 5087ac15270Sflorian void 5097ac15270Sflorian slowcgi_sig_handler(int sig, short event, void *arg) 5107ac15270Sflorian { 511c3b13bc8Sblambert struct request *c; 512c3b13bc8Sblambert struct requests *ncs; 5137ac15270Sflorian struct slowcgi_proc *p; 5147ac15270Sflorian pid_t pid; 5157ac15270Sflorian int status; 5167ac15270Sflorian 5177ac15270Sflorian p = arg; 5187ac15270Sflorian 5197ac15270Sflorian switch (sig) { 5207ac15270Sflorian case SIGCHLD: 5211878c91fSdjm while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) { 522e0571d83Sflorian c = NULL; 523c3b13bc8Sblambert SLIST_FOREACH(ncs, &p->requests, entry) 524c3b13bc8Sblambert if (ncs->request->script_pid == pid) { 525c3b13bc8Sblambert c = ncs->request; 5267ac15270Sflorian break; 5277ac15270Sflorian } 528bde99fd8Sblambert if (c == NULL) { 529bde99fd8Sblambert lwarnx("caught exit of unknown child %i", pid); 530e0571d83Sflorian continue; 531bde99fd8Sblambert } 532bde99fd8Sblambert 533bde99fd8Sblambert if (WIFSIGNALED(status)) 534bde99fd8Sblambert c->script_status = WTERMSIG(status); 535bde99fd8Sblambert else 5367ac15270Sflorian c->script_status = WEXITSTATUS(status); 537bde99fd8Sblambert 5387ac15270Sflorian if (c->script_flags == (STDOUT_DONE | STDERR_DONE)) 539c3b13bc8Sblambert create_end_record(c); 5407ac15270Sflorian c->script_flags |= SCRIPT_DONE; 541bde99fd8Sblambert 542bde99fd8Sblambert ldebug("wait: %s", c->script_name); 543e0571d83Sflorian } 5441878c91fSdjm if (pid == -1 && errno != ECHILD) 5451878c91fSdjm lwarn("waitpid"); 546bde99fd8Sblambert break; 5477ac15270Sflorian case SIGPIPE: 5487ac15270Sflorian /* ignore */ 5497ac15270Sflorian break; 5507ac15270Sflorian default: 5517ac15270Sflorian lerr(1, "unexpected signal: %d", sig); 552f8701677Sderaadt break; 5537ac15270Sflorian } 5547ac15270Sflorian } 5557ac15270Sflorian 5567ac15270Sflorian void 5574404b4d5Sflorian slowcgi_add_response(struct request *c, struct fcgi_response *resp) 5584404b4d5Sflorian { 559*07240181Sreyk struct fcgi_record_header *header; 560*07240181Sreyk size_t padded_len; 561*07240181Sreyk 562*07240181Sreyk header = (struct fcgi_record_header*)resp->data; 563*07240181Sreyk 564*07240181Sreyk /* The FastCGI spec suggests to align the output buffer */ 565*07240181Sreyk padded_len = FCGI_ALIGN(resp->data_len); 566*07240181Sreyk if (padded_len > resp->data_len) { 567*07240181Sreyk /* There should always be FCGI_PADDING_SIZE bytes left */ 568*07240181Sreyk if (padded_len > FCGI_RECORD_SIZE) 569*07240181Sreyk lerr(1, "response too long"); 570*07240181Sreyk header->padding_len = padded_len - resp->data_len; 571*07240181Sreyk resp->data_len = padded_len; 572*07240181Sreyk } 573*07240181Sreyk 5744404b4d5Sflorian TAILQ_INSERT_TAIL(&c->response_head, resp, entry); 5754404b4d5Sflorian event_add(&c->resp_ev, NULL); 5764404b4d5Sflorian } 5774404b4d5Sflorian 5784404b4d5Sflorian void 5797ac15270Sflorian slowcgi_response(int fd, short events, void *arg) 5807ac15270Sflorian { 581c3b13bc8Sblambert struct request *c; 5827ac15270Sflorian struct fcgi_record_header *header; 5837ac15270Sflorian struct fcgi_response *resp; 5847ac15270Sflorian ssize_t n; 5857ac15270Sflorian 5867ac15270Sflorian c = arg; 5877ac15270Sflorian 5887ac15270Sflorian while ((resp = TAILQ_FIRST(&c->response_head))) { 5897ac15270Sflorian header = (struct fcgi_record_header*) resp->data; 5907ac15270Sflorian if (debug) 59103e61f17Sblambert dump_fcgi_record("resp ", header); 5927ac15270Sflorian 5937ac15270Sflorian n = write(fd, resp->data + resp->data_pos, resp->data_len); 5947ac15270Sflorian if (n == -1) { 59514379226Sflorian if (errno == EAGAIN || errno == EINTR) 5967ac15270Sflorian return; 597c3b13bc8Sblambert cleanup_request(c); 5987ac15270Sflorian return; 5997ac15270Sflorian } 6007ac15270Sflorian resp->data_pos += n; 6017ac15270Sflorian resp->data_len -= n; 6027ac15270Sflorian if (resp->data_len == 0) { 6037ac15270Sflorian TAILQ_REMOVE(&c->response_head, resp, entry); 6047ac15270Sflorian free(resp); 6057ac15270Sflorian } 6067ac15270Sflorian } 6077ac15270Sflorian 6087ac15270Sflorian if (TAILQ_EMPTY(&c->response_head)) { 6097ac15270Sflorian if (c->script_flags == (STDOUT_DONE | STDERR_DONE | 6107ac15270Sflorian SCRIPT_DONE)) 611c3b13bc8Sblambert cleanup_request(c); 6127ac15270Sflorian else 6137ac15270Sflorian event_del(&c->resp_ev); 6147ac15270Sflorian } 6157ac15270Sflorian } 6167ac15270Sflorian 6177ac15270Sflorian void 6187ac15270Sflorian slowcgi_request(int fd, short events, void *arg) 6197ac15270Sflorian { 620c3b13bc8Sblambert struct request *c; 621bcf12a6bSblambert ssize_t n; 622bcf12a6bSblambert size_t parsed; 6237ac15270Sflorian 6247ac15270Sflorian c = arg; 6257ac15270Sflorian 6267ac15270Sflorian n = read(fd, c->buf + c->buf_pos + c->buf_len, 6277ac15270Sflorian FCGI_RECORD_SIZE - c->buf_pos-c->buf_len); 6287ac15270Sflorian 6297ac15270Sflorian switch (n) { 6307ac15270Sflorian case -1: 6317ac15270Sflorian switch (errno) { 6327ac15270Sflorian case EINTR: 6337ac15270Sflorian case EAGAIN: 6347ac15270Sflorian return; 6357ac15270Sflorian default: 6367ac15270Sflorian goto fail; 6377ac15270Sflorian } 6387ac15270Sflorian break; 6397ac15270Sflorian 6407ac15270Sflorian case 0: 6417ac15270Sflorian ldebug("closed connection"); 6427ac15270Sflorian goto fail; 6437ac15270Sflorian default: 6447ac15270Sflorian break; 6457ac15270Sflorian } 6467ac15270Sflorian 6477ac15270Sflorian c->buf_len += n; 6487ac15270Sflorian 64902ad6491Sblambert /* 65002ad6491Sblambert * Parse the records as they are received. Per the FastCGI 65102ad6491Sblambert * specification, the server need only receive the FastCGI 65202ad6491Sblambert * parameter records in full; it is free to begin execution 65302ad6491Sblambert * at that point, which is what happens here. 65402ad6491Sblambert */ 6557ac15270Sflorian do { 656c3b13bc8Sblambert parsed = parse_record(c->buf + c->buf_pos, c->buf_len, c); 6577ac15270Sflorian c->buf_pos += parsed; 6587ac15270Sflorian c->buf_len -= parsed; 6597ac15270Sflorian } while (parsed > 0 && c->buf_len > 0); 6607ac15270Sflorian 661965b2218Sblambert /* Make space for further reads */ 6627ac15270Sflorian if (c->buf_len > 0) { 6637ac15270Sflorian bcopy(c->buf + c->buf_pos, c->buf, c->buf_len); 6647ac15270Sflorian c->buf_pos = 0; 6657ac15270Sflorian } 6667ac15270Sflorian return; 6677ac15270Sflorian fail: 668c3b13bc8Sblambert cleanup_request(c); 6697ac15270Sflorian } 6707ac15270Sflorian 6717ac15270Sflorian void 672c3b13bc8Sblambert parse_begin_request(uint8_t *buf, uint16_t n, struct request *c, uint16_t id) 6737ac15270Sflorian { 674965b2218Sblambert /* XXX -- FCGI_CANT_MPX_CONN */ 6757ac15270Sflorian if (c->request_started) { 6767ac15270Sflorian lwarnx("unexpected FCGI_BEGIN_REQUEST, ignoring"); 6777ac15270Sflorian return; 6787ac15270Sflorian } 6797ac15270Sflorian 6807ac15270Sflorian if (n != sizeof(struct fcgi_begin_request_body)) { 6817ac15270Sflorian lwarnx("wrong size %d != %d", n, 6827ac15270Sflorian sizeof(struct fcgi_begin_request_body)); 6837ac15270Sflorian return; 6847ac15270Sflorian } 6857ac15270Sflorian 6867ac15270Sflorian c->request_started = 1; 6877ac15270Sflorian 6887ac15270Sflorian c->id = id; 6897ac15270Sflorian SLIST_INIT(&c->env); 6907ac15270Sflorian c->env_count = 0; 6917ac15270Sflorian } 6927ac15270Sflorian void 693c3b13bc8Sblambert parse_params(uint8_t *buf, uint16_t n, struct request *c, uint16_t id) 6947ac15270Sflorian { 6957ac15270Sflorian struct env_val *env_entry; 6967ac15270Sflorian uint32_t name_len, val_len; 6977ac15270Sflorian 6987ac15270Sflorian if (!c->request_started) { 6997ac15270Sflorian lwarnx("FCGI_PARAMS without FCGI_BEGIN_REQUEST, ignoring"); 7007ac15270Sflorian return; 7017ac15270Sflorian } 7027ac15270Sflorian 7037ac15270Sflorian if (c->id != id) { 7047ac15270Sflorian lwarnx("unexpected id, ignoring"); 7057ac15270Sflorian return; 7067ac15270Sflorian } 7077ac15270Sflorian 70802ad6491Sblambert /* 70902ad6491Sblambert * If this is the last FastCGI parameter record, 71002ad6491Sblambert * begin execution of the CGI script. 71102ad6491Sblambert */ 7127ac15270Sflorian if (n == 0) { 7137ac15270Sflorian exec_cgi(c); 7147ac15270Sflorian return; 7157ac15270Sflorian } 71602ad6491Sblambert 7177ac15270Sflorian while (n > 0) { 7187ac15270Sflorian if (buf[0] >> 7 == 0) { 7197ac15270Sflorian name_len = buf[0]; 7207ac15270Sflorian n--; 7217ac15270Sflorian buf++; 7227ac15270Sflorian } else { 7237ac15270Sflorian if (n > 3) { 72410fd764fSflorian name_len = ((buf[0] & 0x7f) << 24) + 72510fd764fSflorian (buf[1] << 16) + (buf[2] << 8) + buf[3]; 7267ac15270Sflorian n -= 4; 7277ac15270Sflorian buf += 4; 7287ac15270Sflorian } else 7297ac15270Sflorian return; 7307ac15270Sflorian } 7317ac15270Sflorian 7327ac15270Sflorian if (n > 0) { 7337ac15270Sflorian if (buf[0] >> 7 == 0) { 7347ac15270Sflorian val_len = buf[0]; 7357ac15270Sflorian n--; 7367ac15270Sflorian buf++; 7377ac15270Sflorian } else { 7387ac15270Sflorian if (n > 3) { 73910fd764fSflorian val_len = ((buf[0] & 0x7f) << 24) + 74010fd764fSflorian (buf[1] << 16) + (buf[2] << 8) + 74110fd764fSflorian buf[3]; 7427ac15270Sflorian n -= 4; 7437ac15270Sflorian buf += 4; 7447ac15270Sflorian } else 7457ac15270Sflorian return; 7467ac15270Sflorian } 747f58bfe4fSflorian } else 748f58bfe4fSflorian return; 749f58bfe4fSflorian 7507ac15270Sflorian if (n < name_len + val_len) 7517ac15270Sflorian return; 7527ac15270Sflorian 7537ac15270Sflorian if ((env_entry = malloc(sizeof(struct env_val))) == NULL) { 7547ac15270Sflorian lwarnx("cannot allocate env_entry"); 7557ac15270Sflorian return; 7567ac15270Sflorian } 7577ac15270Sflorian 7587ac15270Sflorian if ((env_entry->val = calloc(sizeof(char), name_len + val_len + 7597ac15270Sflorian 2)) == NULL) { 7607ac15270Sflorian lwarnx("cannot allocate env_entry->val"); 7617ac15270Sflorian free(env_entry); 7627ac15270Sflorian return; 7637ac15270Sflorian } 7647ac15270Sflorian 7657ac15270Sflorian bcopy(buf, env_entry->val, name_len); 766965b2218Sblambert buf += name_len; 767965b2218Sblambert n -= name_len; 7687ac15270Sflorian 7697ac15270Sflorian env_entry->val[name_len] = '\0'; 770b9fc9a72Sderaadt if (val_len < PATH_MAX && strcmp(env_entry->val, 771efec7dbcSflorian "SCRIPT_NAME") == 0 && c->script_name[0] == '\0') { 772efec7dbcSflorian bcopy(buf, c->script_name, val_len); 773efec7dbcSflorian c->script_name[val_len] = '\0'; 774b9fc9a72Sderaadt } else if (val_len < PATH_MAX && strcmp(env_entry->val, 775efec7dbcSflorian "SCRIPT_FILENAME") == 0) { 7767ac15270Sflorian bcopy(buf, c->script_name, val_len); 77753847fa2Sflorian c->script_name[val_len] = '\0'; 7787ac15270Sflorian } 7797ac15270Sflorian env_entry->val[name_len] = '='; 7807ac15270Sflorian 7817ac15270Sflorian bcopy(buf, (env_entry->val) + name_len + 1, val_len); 782965b2218Sblambert buf += val_len; 783965b2218Sblambert n -= val_len; 7847ac15270Sflorian 7857ac15270Sflorian SLIST_INSERT_HEAD(&c->env, env_entry, entry); 786bcbc98ecSmillert ldebug("env[%d], %s", c->env_count, env_entry->val); 7877ac15270Sflorian c->env_count++; 7887ac15270Sflorian } 7897ac15270Sflorian } 7907ac15270Sflorian 7917ac15270Sflorian void 792c3b13bc8Sblambert parse_stdin(uint8_t *buf, uint16_t n, struct request *c, uint16_t id) 7937ac15270Sflorian { 7947ac15270Sflorian struct fcgi_stdin *node; 7957ac15270Sflorian 7967ac15270Sflorian if (c->id != id) { 7977ac15270Sflorian lwarnx("unexpected id, ignoring"); 7987ac15270Sflorian return; 7997ac15270Sflorian } 8007ac15270Sflorian 8017ac15270Sflorian if ((node = calloc(1, sizeof(struct fcgi_stdin))) == NULL) { 8027ac15270Sflorian lwarnx("cannot calloc stdin node"); 8037ac15270Sflorian return; 8047ac15270Sflorian } 8057ac15270Sflorian 8067ac15270Sflorian bcopy(buf, node->data, n); 8077ac15270Sflorian node->data_pos = 0; 8087ac15270Sflorian node->data_len = n; 8097ac15270Sflorian 8107ac15270Sflorian TAILQ_INSERT_TAIL(&c->stdin_head, node, entry); 8117ac15270Sflorian 812a9461a24Sflorian if (event_initialized(&c->script_stdin_ev)) 8137ac15270Sflorian event_add(&c->script_stdin_ev, NULL); 8147ac15270Sflorian } 8157ac15270Sflorian 8167ac15270Sflorian size_t 817c3b13bc8Sblambert parse_record(uint8_t *buf, size_t n, struct request *c) 8187ac15270Sflorian { 8197ac15270Sflorian struct fcgi_record_header *h; 8207ac15270Sflorian 8217ac15270Sflorian if (n < sizeof(struct fcgi_record_header)) 8227ac15270Sflorian return (0); 8237ac15270Sflorian 8247ac15270Sflorian h = (struct fcgi_record_header*) buf; 8257ac15270Sflorian 8267ac15270Sflorian if (debug) 82703e61f17Sblambert dump_fcgi_record("", h); 8287ac15270Sflorian 8297ac15270Sflorian if (n < sizeof(struct fcgi_record_header) + ntohs(h->content_len) 8307ac15270Sflorian + h->padding_len) 8317ac15270Sflorian return (0); 8327ac15270Sflorian 8337ac15270Sflorian if (h->version != 1) 8347ac15270Sflorian lerrx(1, "wrong version"); 8357ac15270Sflorian 8367ac15270Sflorian switch (h->type) { 8377ac15270Sflorian case FCGI_BEGIN_REQUEST: 8387ac15270Sflorian parse_begin_request(buf + sizeof(struct fcgi_record_header), 8397ac15270Sflorian ntohs(h->content_len), c, ntohs(h->id)); 8407ac15270Sflorian break; 8417ac15270Sflorian case FCGI_PARAMS: 8427ac15270Sflorian parse_params(buf + sizeof(struct fcgi_record_header), 8437ac15270Sflorian ntohs(h->content_len), c, ntohs(h->id)); 8447ac15270Sflorian break; 8457ac15270Sflorian case FCGI_STDIN: 8467ac15270Sflorian parse_stdin(buf + sizeof(struct fcgi_record_header), 8477ac15270Sflorian ntohs(h->content_len), c, ntohs(h->id)); 8487ac15270Sflorian break; 8497ac15270Sflorian default: 8507ac15270Sflorian lwarnx("unimplemented type %d", h->type); 851f8701677Sderaadt break; 8527ac15270Sflorian } 8537ac15270Sflorian 8547ac15270Sflorian return (sizeof(struct fcgi_record_header) + ntohs(h->content_len) 8557ac15270Sflorian + h->padding_len); 8567ac15270Sflorian } 8577ac15270Sflorian 85802ad6491Sblambert /* 85902ad6491Sblambert * Fork a new CGI process to handle the request, translating 86002ad6491Sblambert * between FastCGI parameter records and CGI's environment variables, 86102ad6491Sblambert * as well as between the CGI process' stdin/stdout and the 86202ad6491Sblambert * corresponding FastCGI records. 86302ad6491Sblambert */ 8647ac15270Sflorian void 865c3b13bc8Sblambert exec_cgi(struct request *c) 8667ac15270Sflorian { 8677ac15270Sflorian struct env_val *env_entry; 868a9461a24Sflorian int s_in[2], s_out[2], s_err[2], i; 8697ac15270Sflorian pid_t pid; 8707ac15270Sflorian char *argv[2]; 8717ac15270Sflorian char **env; 8726bcf2ad3Sflorian char *path; 8737ac15270Sflorian 8747ac15270Sflorian i = 0; 8757ac15270Sflorian 876a9461a24Sflorian if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s_in) == -1) 877a9461a24Sflorian lerr(1, "socketpair"); 878a9461a24Sflorian if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s_out) == -1) 8797ac15270Sflorian lerr(1, "socketpair"); 8807ac15270Sflorian if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s_err) == -1) 8817ac15270Sflorian lerr(1, "socketpair"); 8823f9f8982Sbenno cgi_inflight--; 8833f9f8982Sbenno c->inflight_fds_accounted = 1; 8847ac15270Sflorian ldebug("fork: %s", c->script_name); 885965b2218Sblambert 8867ac15270Sflorian switch (pid = fork()) { 8877ac15270Sflorian case -1: 888e014c219Sflorian c->script_status = errno; 889e014c219Sflorian 8907ac15270Sflorian lwarn("fork"); 891e014c219Sflorian 892e014c219Sflorian close(s_in[0]); 893e014c219Sflorian close(s_out[0]); 894e014c219Sflorian close(s_err[0]); 895e014c219Sflorian 896e014c219Sflorian close(s_in[1]); 897e014c219Sflorian close(s_out[1]); 898e014c219Sflorian close(s_err[1]); 899e014c219Sflorian 900e014c219Sflorian c->stdin_fd_closed = c->stdout_fd_closed = 901e014c219Sflorian c->stderr_fd_closed = 1; 902e014c219Sflorian c->script_flags = (STDOUT_DONE | STDERR_DONE | SCRIPT_DONE); 903e014c219Sflorian 904e014c219Sflorian create_end_record(c); 9057ac15270Sflorian return; 9067ac15270Sflorian case 0: 9077ac15270Sflorian /* Child process */ 9081bfdb260Sflorian if (pledge("stdio rpath exec", NULL) == -1) 9091bfdb260Sflorian lerr(1, "pledge"); 910a9461a24Sflorian close(s_in[0]); 911a9461a24Sflorian close(s_out[0]); 9127ac15270Sflorian close(s_err[0]); 913f4a8b980Sflorian 914a9461a24Sflorian if (dup2(s_in[1], STDIN_FILENO) == -1) 9157ac15270Sflorian _exit(1); 916a9461a24Sflorian if (dup2(s_out[1], STDOUT_FILENO) == -1) 9177ac15270Sflorian _exit(1); 9187ac15270Sflorian if (dup2(s_err[1], STDERR_FILENO) == -1) 9197ac15270Sflorian _exit(1); 920f4a8b980Sflorian 921f4a8b980Sflorian close(s_in[1]); 922f4a8b980Sflorian close(s_out[1]); 923f4a8b980Sflorian close(s_err[1]); 924f4a8b980Sflorian 9256bcf2ad3Sflorian path = strrchr(c->script_name, '/'); 9266bcf2ad3Sflorian if (path != NULL) { 92776f0500eSflorian if (path != c->script_name) { 9286bcf2ad3Sflorian *path = '\0'; 9296bcf2ad3Sflorian if (chdir(c->script_name) == -1) 93076f0500eSflorian lwarn("cannot chdir to %s", 93176f0500eSflorian c->script_name); 9326bcf2ad3Sflorian *path = '/'; 93376f0500eSflorian } else 93476f0500eSflorian if (chdir("/") == -1) 93576f0500eSflorian lwarn("cannot chdir to /"); 9366bcf2ad3Sflorian } 9376bcf2ad3Sflorian 9387ac15270Sflorian argv[0] = c->script_name; 9397ac15270Sflorian argv[1] = NULL; 9407ac15270Sflorian if ((env = calloc(c->env_count + 1, sizeof(char*))) == NULL) 9417ac15270Sflorian _exit(1); 9427ac15270Sflorian SLIST_FOREACH(env_entry, &c->env, entry) 9437ac15270Sflorian env[i++] = env_entry->val; 9447ac15270Sflorian env[i++] = NULL; 9457ac15270Sflorian execve(c->script_name, argv, env); 9461878c91fSdjm lwarn("execve %s", c->script_name); 9477ac15270Sflorian _exit(1); 9487ac15270Sflorian 9497ac15270Sflorian } 950f8701677Sderaadt 9517ac15270Sflorian /* Parent process*/ 952a9461a24Sflorian close(s_in[1]); 953a9461a24Sflorian close(s_out[1]); 9547ac15270Sflorian close(s_err[1]); 95594ee156dSflorian 956a9461a24Sflorian fcntl(s_in[0], F_SETFD, FD_CLOEXEC); 957a9461a24Sflorian fcntl(s_out[0], F_SETFD, FD_CLOEXEC); 95894ee156dSflorian fcntl(s_err[0], F_SETFD, FD_CLOEXEC); 95994ee156dSflorian 960a9461a24Sflorian if (ioctl(s_in[0], FIONBIO, &on) == -1) 961a9461a24Sflorian lerr(1, "script ioctl(FIONBIO)"); 962a9461a24Sflorian if (ioctl(s_out[0], FIONBIO, &on) == -1) 9637ac15270Sflorian lerr(1, "script ioctl(FIONBIO)"); 9647ac15270Sflorian if (ioctl(s_err[0], FIONBIO, &on) == -1) 9657ac15270Sflorian lerr(1, "script ioctl(FIONBIO)"); 9667ac15270Sflorian 9677ac15270Sflorian c->script_pid = pid; 968a9461a24Sflorian event_set(&c->script_stdin_ev, s_in[0], EV_WRITE | EV_PERSIST, 969a9461a24Sflorian script_out, c); 970a9461a24Sflorian event_add(&c->script_stdin_ev, NULL); 971a9461a24Sflorian event_set(&c->script_ev, s_out[0], EV_READ | EV_PERSIST, 972a9461a24Sflorian script_std_in, c); 9737ac15270Sflorian event_add(&c->script_ev, NULL); 9747ac15270Sflorian event_set(&c->script_err_ev, s_err[0], EV_READ | EV_PERSIST, 9757ac15270Sflorian script_err_in, c); 9767ac15270Sflorian event_add(&c->script_err_ev, NULL); 9777ac15270Sflorian } 9787ac15270Sflorian 9797ac15270Sflorian void 980c3b13bc8Sblambert create_end_record(struct request *c) 9817ac15270Sflorian { 9827ac15270Sflorian struct fcgi_response *resp; 9837ac15270Sflorian struct fcgi_record_header *header; 98403e61f17Sblambert struct fcgi_end_request_body *end_request; 9857ac15270Sflorian 986*07240181Sreyk if ((resp = calloc(1, sizeof(struct fcgi_response))) == NULL) { 9877ac15270Sflorian lwarnx("cannot malloc fcgi_response"); 9887ac15270Sflorian return; 9897ac15270Sflorian } 9907ac15270Sflorian header = (struct fcgi_record_header*) resp->data; 9917ac15270Sflorian header->version = 1; 9927ac15270Sflorian header->type = FCGI_END_REQUEST; 9937ac15270Sflorian header->id = htons(c->id); 9947ac15270Sflorian header->content_len = htons(sizeof(struct 99503e61f17Sblambert fcgi_end_request_body)); 9967ac15270Sflorian header->padding_len = 0; 9977ac15270Sflorian header->reserved = 0; 9988c347892Sflorian end_request = (struct fcgi_end_request_body *) (resp->data + 9998c347892Sflorian sizeof(struct fcgi_record_header)); 10007ac15270Sflorian end_request->app_status = htonl(c->script_status); 10017ac15270Sflorian end_request->protocol_status = FCGI_REQUEST_COMPLETE; 10028c347892Sflorian end_request->reserved[0] = 0; 10038c347892Sflorian end_request->reserved[1] = 0; 10048c347892Sflorian end_request->reserved[2] = 0; 10057ac15270Sflorian resp->data_pos = 0; 100603e61f17Sblambert resp->data_len = sizeof(struct fcgi_end_request_body) + 10077ac15270Sflorian sizeof(struct fcgi_record_header); 10084404b4d5Sflorian slowcgi_add_response(c, resp); 10097ac15270Sflorian } 10107ac15270Sflorian 10117ac15270Sflorian void 1012c3b13bc8Sblambert script_in(int fd, struct event *ev, struct request *c, uint8_t type) 10137ac15270Sflorian { 10147ac15270Sflorian struct fcgi_response *resp; 10157ac15270Sflorian struct fcgi_record_header *header; 10167ac15270Sflorian ssize_t n; 10177ac15270Sflorian 1018*07240181Sreyk if ((resp = calloc(1, sizeof(struct fcgi_response))) == NULL) { 10197ac15270Sflorian lwarnx("cannot malloc fcgi_response"); 10207ac15270Sflorian return; 10217ac15270Sflorian } 10227ac15270Sflorian header = (struct fcgi_record_header*) resp->data; 10237ac15270Sflorian header->version = 1; 10247ac15270Sflorian header->type = type; 10257ac15270Sflorian header->id = htons(c->id); 10267ac15270Sflorian header->padding_len = 0; 10277ac15270Sflorian header->reserved = 0; 10287ac15270Sflorian 10297ac15270Sflorian n = read(fd, resp->data + sizeof(struct fcgi_record_header), 10300ee0284dSblambert FCGI_CONTENT_SIZE); 10317ac15270Sflorian 10327ac15270Sflorian if (n == -1) { 10337ac15270Sflorian switch (errno) { 10347ac15270Sflorian case EINTR: 10357ac15270Sflorian case EAGAIN: 1036aa48dbd7Sjasper free(resp); 10377ac15270Sflorian return; 10387ac15270Sflorian default: 10397ac15270Sflorian n = 0; /* fake empty FCGI_STD{OUT,ERR} response */ 10407ac15270Sflorian } 10417ac15270Sflorian } 10427ac15270Sflorian header->content_len = htons(n); 10437ac15270Sflorian resp->data_pos = 0; 10447ac15270Sflorian resp->data_len = n + sizeof(struct fcgi_record_header); 10454404b4d5Sflorian slowcgi_add_response(c, resp); 10467ac15270Sflorian 10477ac15270Sflorian if (n == 0) { 10487ac15270Sflorian if (type == FCGI_STDOUT) 10497ac15270Sflorian c->script_flags |= STDOUT_DONE; 10507ac15270Sflorian else 10517ac15270Sflorian c->script_flags |= STDERR_DONE; 10527ac15270Sflorian 10537ac15270Sflorian if (c->script_flags == (STDOUT_DONE | STDERR_DONE | 10547ac15270Sflorian SCRIPT_DONE)) { 1055c3b13bc8Sblambert create_end_record(c); 10567ac15270Sflorian } 10577ac15270Sflorian event_del(ev); 10587ac15270Sflorian close(fd); 10596bdc2311Sflorian if (type == FCGI_STDOUT) 10606bdc2311Sflorian c->stdout_fd_closed = 1; 10616bdc2311Sflorian else 10626bdc2311Sflorian c->stderr_fd_closed = 1; 10637ac15270Sflorian } 10647ac15270Sflorian } 10657ac15270Sflorian 10667ac15270Sflorian void 10677ac15270Sflorian script_std_in(int fd, short events, void *arg) 10687ac15270Sflorian { 1069c3b13bc8Sblambert struct request *c = arg; 10707ac15270Sflorian script_in(fd, &c->script_ev, c, FCGI_STDOUT); 10717ac15270Sflorian } 10727ac15270Sflorian 10737ac15270Sflorian void 10747ac15270Sflorian script_err_in(int fd, short events, void *arg) 10757ac15270Sflorian { 1076c3b13bc8Sblambert struct request *c = arg; 10777ac15270Sflorian script_in(fd, &c->script_err_ev, c, FCGI_STDERR); 10787ac15270Sflorian } 10797ac15270Sflorian 10807ac15270Sflorian void 10817ac15270Sflorian script_out(int fd, short events, void *arg) 10827ac15270Sflorian { 1083c3b13bc8Sblambert struct request *c; 10847ac15270Sflorian struct fcgi_stdin *node; 10857ac15270Sflorian ssize_t n; 10867ac15270Sflorian 10877ac15270Sflorian c = arg; 10887ac15270Sflorian 10897ac15270Sflorian while ((node = TAILQ_FIRST(&c->stdin_head))) { 10907ac15270Sflorian if (node->data_len == 0) { /* end of stdin marker */ 1091a9461a24Sflorian close(fd); 10926bdc2311Sflorian c->stdin_fd_closed = 1; 10937ac15270Sflorian break; 10947ac15270Sflorian } 10957ac15270Sflorian n = write(fd, node->data + node->data_pos, node->data_len); 10967ac15270Sflorian if (n == -1) { 109714379226Sflorian if (errno == EAGAIN || errno == EINTR) 10987ac15270Sflorian return; 10997ac15270Sflorian event_del(&c->script_stdin_ev); 11007ac15270Sflorian return; 11017ac15270Sflorian } 11027ac15270Sflorian node->data_pos += n; 11037ac15270Sflorian node->data_len -= n; 11047ac15270Sflorian if (node->data_len == 0) { 11057ac15270Sflorian TAILQ_REMOVE(&c->stdin_head, node, entry); 11067ac15270Sflorian free(node); 11077ac15270Sflorian } 11087ac15270Sflorian } 1109a9461a24Sflorian event_del(&c->script_stdin_ev); 11107ac15270Sflorian } 11117ac15270Sflorian 11127ac15270Sflorian void 1113c3b13bc8Sblambert cleanup_request(struct request *c) 11147ac15270Sflorian { 11157ac15270Sflorian struct fcgi_response *resp; 11167ac15270Sflorian struct fcgi_stdin *stdin_node; 11177ac15270Sflorian struct env_val *env_entry; 1118c3b13bc8Sblambert struct requests *ncs, *tcs; 11197ac15270Sflorian 11207ac15270Sflorian evtimer_del(&c->tmo); 11217ac15270Sflorian if (event_initialized(&c->ev)) 11227ac15270Sflorian event_del(&c->ev); 11237ac15270Sflorian if (event_initialized(&c->resp_ev)) 11247ac15270Sflorian event_del(&c->resp_ev); 11257ac15270Sflorian if (event_initialized(&c->script_ev)) { 11266bdc2311Sflorian if (!c->stdout_fd_closed) 11277ac15270Sflorian close(EVENT_FD(&c->script_ev)); 11287ac15270Sflorian event_del(&c->script_ev); 11297ac15270Sflorian } 11307ac15270Sflorian if (event_initialized(&c->script_err_ev)) { 11316bdc2311Sflorian if (!c->stderr_fd_closed) 11327ac15270Sflorian close(EVENT_FD(&c->script_err_ev)); 11337ac15270Sflorian event_del(&c->script_err_ev); 11347ac15270Sflorian } 11357ac15270Sflorian if (event_initialized(&c->script_stdin_ev)) { 11366bdc2311Sflorian if (!c->stdin_fd_closed) 11377ac15270Sflorian close(EVENT_FD(&c->script_stdin_ev)); 11387ac15270Sflorian event_del(&c->script_stdin_ev); 11397ac15270Sflorian } 11407ac15270Sflorian close(c->fd); 11417ac15270Sflorian while (!SLIST_EMPTY(&c->env)) { 11427ac15270Sflorian env_entry = SLIST_FIRST(&c->env); 11437ac15270Sflorian SLIST_REMOVE_HEAD(&c->env, entry); 11447ac15270Sflorian free(env_entry->val); 11457ac15270Sflorian free(env_entry); 11467ac15270Sflorian } 11477ac15270Sflorian 11487ac15270Sflorian while ((resp = TAILQ_FIRST(&c->response_head))) { 11497ac15270Sflorian TAILQ_REMOVE(&c->response_head, resp, entry); 11507ac15270Sflorian free(resp); 11517ac15270Sflorian } 11527ac15270Sflorian while ((stdin_node = TAILQ_FIRST(&c->stdin_head))) { 11537ac15270Sflorian TAILQ_REMOVE(&c->stdin_head, stdin_node, entry); 11547ac15270Sflorian free(stdin_node); 11557ac15270Sflorian } 1156c3b13bc8Sblambert SLIST_FOREACH_SAFE(ncs, &slowcgi_proc.requests, entry, tcs) { 1157c3b13bc8Sblambert if (ncs->request == c) { 1158c3b13bc8Sblambert SLIST_REMOVE(&slowcgi_proc.requests, ncs, requests, 11597ac15270Sflorian entry); 11607ac15270Sflorian free(ncs); 11617ac15270Sflorian break; 11627ac15270Sflorian } 11637ac15270Sflorian } 11643f9f8982Sbenno if (! c->inflight_fds_accounted) 11653f9f8982Sbenno cgi_inflight--; 11667ac15270Sflorian free(c); 11677ac15270Sflorian } 11687ac15270Sflorian 1169965b2218Sblambert void 117003e61f17Sblambert dump_fcgi_record(const char *p, struct fcgi_record_header *h) 117103e61f17Sblambert { 117203e61f17Sblambert dump_fcgi_record_header(p, h); 117303e61f17Sblambert 117403e61f17Sblambert if (h->type == FCGI_BEGIN_REQUEST) 117503e61f17Sblambert dump_fcgi_begin_request_body(p, 1176d241f573Sflorian (struct fcgi_begin_request_body *)(h + 1)); 117703e61f17Sblambert else if (h->type == FCGI_END_REQUEST) 117803e61f17Sblambert dump_fcgi_end_request_body(p, 1179d241f573Sflorian (struct fcgi_end_request_body *)(h + 1)); 118003e61f17Sblambert } 118103e61f17Sblambert 118203e61f17Sblambert void 1183965b2218Sblambert dump_fcgi_record_header(const char* p, struct fcgi_record_header *h) 11847ac15270Sflorian { 11857ac15270Sflorian ldebug("%sversion: %d", p, h->version); 11867ac15270Sflorian ldebug("%stype: %d", p, h->type); 11877ac15270Sflorian ldebug("%srequestId: %d", p, ntohs(h->id)); 11887ac15270Sflorian ldebug("%scontentLength: %d", p, ntohs(h->content_len)); 11897ac15270Sflorian ldebug("%spaddingLength: %d", p, h->padding_len); 11907ac15270Sflorian ldebug("%sreserved: %d", p, h->reserved); 11917ac15270Sflorian } 11927ac15270Sflorian 11937ac15270Sflorian void 119403e61f17Sblambert dump_fcgi_begin_request_body(const char *p, struct fcgi_begin_request_body *b) 119503e61f17Sblambert { 119603e61f17Sblambert ldebug("%srole %d", p, ntohs(b->role)); 119703e61f17Sblambert ldebug("%sflags %d", p, b->flags); 119803e61f17Sblambert } 119903e61f17Sblambert 120003e61f17Sblambert void 120103e61f17Sblambert dump_fcgi_end_request_body(const char *p, struct fcgi_end_request_body *b) 120203e61f17Sblambert { 120303e61f17Sblambert ldebug("%sappStatus: %d", p, ntohl(b->app_status)); 120403e61f17Sblambert ldebug("%sprotocolStatus: %d", p, b->protocol_status); 120503e61f17Sblambert } 120603e61f17Sblambert 120703e61f17Sblambert void 12087ac15270Sflorian syslog_vstrerror(int e, int priority, const char *fmt, va_list ap) 12097ac15270Sflorian { 12107ac15270Sflorian char *s; 12117ac15270Sflorian 12127ac15270Sflorian if (vasprintf(&s, fmt, ap) == -1) { 12137ac15270Sflorian syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror"); 12147ac15270Sflorian exit(1); 12157ac15270Sflorian } 12167ac15270Sflorian syslog(priority, "%s: %s", s, strerror(e)); 12177ac15270Sflorian free(s); 12187ac15270Sflorian } 12197ac15270Sflorian 1220d0a52cffSflorian __dead void 12217ac15270Sflorian syslog_err(int ecode, const char *fmt, ...) 12227ac15270Sflorian { 12237ac15270Sflorian va_list ap; 12247ac15270Sflorian 12257ac15270Sflorian va_start(ap, fmt); 12267ac15270Sflorian syslog_vstrerror(errno, LOG_EMERG, fmt, ap); 12277ac15270Sflorian va_end(ap); 12287ac15270Sflorian exit(ecode); 12297ac15270Sflorian } 12307ac15270Sflorian 1231d0a52cffSflorian __dead void 12327ac15270Sflorian syslog_errx(int ecode, const char *fmt, ...) 12337ac15270Sflorian { 12347ac15270Sflorian va_list ap; 12357ac15270Sflorian 12367ac15270Sflorian va_start(ap, fmt); 12377ac15270Sflorian vsyslog(LOG_WARNING, fmt, ap); 12387ac15270Sflorian va_end(ap); 12397ac15270Sflorian exit(ecode); 12407ac15270Sflorian } 12417ac15270Sflorian 12427ac15270Sflorian void 12437ac15270Sflorian syslog_warn(const char *fmt, ...) 12447ac15270Sflorian { 12457ac15270Sflorian va_list ap; 12467ac15270Sflorian 12477ac15270Sflorian va_start(ap, fmt); 12487ac15270Sflorian syslog_vstrerror(errno, LOG_WARNING, fmt, ap); 12497ac15270Sflorian va_end(ap); 12507ac15270Sflorian } 12517ac15270Sflorian 12527ac15270Sflorian void 12537ac15270Sflorian syslog_warnx(const char *fmt, ...) 12547ac15270Sflorian { 12557ac15270Sflorian va_list ap; 12567ac15270Sflorian 12577ac15270Sflorian va_start(ap, fmt); 12587ac15270Sflorian vsyslog(LOG_WARNING, fmt, ap); 12597ac15270Sflorian va_end(ap); 12607ac15270Sflorian } 12617ac15270Sflorian 12627ac15270Sflorian void 12637ac15270Sflorian syslog_info(const char *fmt, ...) 12647ac15270Sflorian { 12657ac15270Sflorian va_list ap; 12667ac15270Sflorian 12677ac15270Sflorian va_start(ap, fmt); 12687ac15270Sflorian vsyslog(LOG_INFO, fmt, ap); 12697ac15270Sflorian va_end(ap); 12707ac15270Sflorian } 12717ac15270Sflorian 12727ac15270Sflorian void 12737ac15270Sflorian syslog_debug(const char *fmt, ...) 12747ac15270Sflorian { 12757ac15270Sflorian va_list ap; 12767ac15270Sflorian 12777ac15270Sflorian if (!debug) 12787ac15270Sflorian return; 12797ac15270Sflorian 12807ac15270Sflorian va_start(ap, fmt); 12817ac15270Sflorian vsyslog(LOG_DEBUG, fmt, ap); 12827ac15270Sflorian va_end(ap); 12837ac15270Sflorian } 1284