xref: /openbsd/usr.sbin/slowcgi/slowcgi.c (revision 02ad6491)
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