xref: /openbsd-src/usr.sbin/bgplgd/slowcgi.c (revision 67a9111e03943a1561929d031491d5f59632415f)
1*67a9111eSjob /*	$OpenBSD: slowcgi.c,v 1.7 2024/01/26 18:11:49 job Exp $ */
2e76e7180Sclaudio /*
3e76e7180Sclaudio  * Copyright (c) 2020 Claudio Jeker <claudio@openbsd.org>
4e76e7180Sclaudio  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
5e76e7180Sclaudio  * Copyright (c) 2013 David Gwynne <dlg@openbsd.org>
6e76e7180Sclaudio  * Copyright (c) 2013 Florian Obser <florian@openbsd.org>
7e76e7180Sclaudio  *
8e76e7180Sclaudio  * Permission to use, copy, modify, and distribute this software for any
9e76e7180Sclaudio  * purpose with or without fee is hereby granted, provided that the above
10e76e7180Sclaudio  * copyright notice and this permission notice appear in all copies.
11e76e7180Sclaudio  *
12e76e7180Sclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13e76e7180Sclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14e76e7180Sclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15e76e7180Sclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16e76e7180Sclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17e76e7180Sclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18e76e7180Sclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19e76e7180Sclaudio  */
20e76e7180Sclaudio 
21e76e7180Sclaudio #include <sys/types.h>
22e76e7180Sclaudio #include <sys/ioctl.h>
23e76e7180Sclaudio #include <sys/queue.h>
24e76e7180Sclaudio #include <sys/socket.h>
25e76e7180Sclaudio #include <sys/stat.h>
26e76e7180Sclaudio #include <sys/time.h>
27e76e7180Sclaudio #include <sys/un.h>
28e76e7180Sclaudio #include <sys/wait.h>
29bedbc5ebSclaudio #include <arpa/inet.h>
30e76e7180Sclaudio #include <err.h>
31e76e7180Sclaudio #include <fcntl.h>
32e76e7180Sclaudio #include <errno.h>
33e76e7180Sclaudio #include <event.h>
34e76e7180Sclaudio #include <limits.h>
35e76e7180Sclaudio #include <pwd.h>
36e76e7180Sclaudio #include <signal.h>
37e76e7180Sclaudio #include <stdio.h>
38e76e7180Sclaudio #include <stdarg.h>
39e76e7180Sclaudio #include <stdlib.h>
40e76e7180Sclaudio #include <string.h>
41e76e7180Sclaudio #include <syslog.h>
42e76e7180Sclaudio #include <unistd.h>
43e76e7180Sclaudio 
44e76e7180Sclaudio #include "slowcgi.h"
45e76e7180Sclaudio #include "bgplgd.h"
46e76e7180Sclaudio #include "http.h"
47*67a9111eSjob #include "version.h"
48e76e7180Sclaudio 
4913a46ceeSclaudio #define TIMEOUT_DEFAULT		 30
50e76e7180Sclaudio #define WWW_USER		 "www"
51e76e7180Sclaudio #define BGPLGD_USER		 "_bgplgd"
52e76e7180Sclaudio 
53e76e7180Sclaudio #define FCGI_CONTENT_SIZE	 65535
54e76e7180Sclaudio #define FCGI_PADDING_SIZE	 255
55e76e7180Sclaudio #define FCGI_RECORD_SIZE	 \
56e76e7180Sclaudio     (sizeof(struct fcgi_record_header) + FCGI_CONTENT_SIZE + FCGI_PADDING_SIZE)
57e76e7180Sclaudio 
58e76e7180Sclaudio #define FCGI_ALIGNMENT		 8
59e76e7180Sclaudio #define FCGI_ALIGN(n)		 \
60e76e7180Sclaudio     (((n) + (FCGI_ALIGNMENT - 1)) & ~(FCGI_ALIGNMENT - 1))
61e76e7180Sclaudio 
62e76e7180Sclaudio #define STDOUT_DONE		0x1
63e76e7180Sclaudio #define STDERR_DONE		0x2
64e76e7180Sclaudio #define SCRIPT_DONE		0x4
65e76e7180Sclaudio 
66e76e7180Sclaudio #define FCGI_REQUEST_COMPLETE	0
67e76e7180Sclaudio #define FCGI_CANT_MPX_CONN	1
68e76e7180Sclaudio #define FCGI_OVERLOADED		2
69e76e7180Sclaudio #define FCGI_UNKNOWN_ROLE	3
70e76e7180Sclaudio 
71e76e7180Sclaudio #define FD_RESERVE		5
72e76e7180Sclaudio #define FD_NEEDED		6
73e76e7180Sclaudio int cgi_inflight = 0;
74e76e7180Sclaudio 
75e76e7180Sclaudio struct listener {
76e76e7180Sclaudio 	struct event	ev, pause;
77e76e7180Sclaudio };
78e76e7180Sclaudio 
79e76e7180Sclaudio struct env_val {
80e76e7180Sclaudio 	SLIST_ENTRY(env_val)	 entry;
81e76e7180Sclaudio 	char			*key;
82e76e7180Sclaudio 	char			*val;
83e76e7180Sclaudio };
84e76e7180Sclaudio SLIST_HEAD(env_head, env_val);
85e76e7180Sclaudio 
86e76e7180Sclaudio struct fcgi_record_header {
87e76e7180Sclaudio 	uint8_t		version;
88e76e7180Sclaudio 	uint8_t		type;
89e76e7180Sclaudio 	uint16_t	id;
90e76e7180Sclaudio 	uint16_t	content_len;
91e76e7180Sclaudio 	uint8_t		padding_len;
92e76e7180Sclaudio 	uint8_t		reserved;
93e76e7180Sclaudio }__packed;
94e76e7180Sclaudio 
95e76e7180Sclaudio struct fcgi_response {
96e76e7180Sclaudio 	TAILQ_ENTRY(fcgi_response)	entry;
97e76e7180Sclaudio 	uint8_t				data[FCGI_RECORD_SIZE];
98e76e7180Sclaudio 	size_t				data_pos;
99e76e7180Sclaudio 	size_t				data_len;
100e76e7180Sclaudio };
101e76e7180Sclaudio TAILQ_HEAD(fcgi_response_head, fcgi_response);
102e76e7180Sclaudio 
103e76e7180Sclaudio struct request {
104e76e7180Sclaudio 	LIST_ENTRY(request)		entry;
105e76e7180Sclaudio 	struct event			ev;
106e76e7180Sclaudio 	struct event			resp_ev;
107e76e7180Sclaudio 	struct event			tmo;
108e76e7180Sclaudio 	struct event			script_ev;
109e76e7180Sclaudio 	struct event			script_err_ev;
11013a46ceeSclaudio 	struct fcgi_response_head	response_head;
11113a46ceeSclaudio 	struct env_head			env;
112e76e7180Sclaudio 	uint8_t				buf[FCGI_RECORD_SIZE];
113e76e7180Sclaudio 	size_t				buf_pos;
114e76e7180Sclaudio 	size_t				buf_len;
11513a46ceeSclaudio 	int				fd;
116e76e7180Sclaudio 	int				env_count;
11713a46ceeSclaudio 	int				inflight_fds_accounted;
118e76e7180Sclaudio 	pid_t				command_pid;
119e76e7180Sclaudio 	int				command_status;
120e76e7180Sclaudio 	int				script_flags;
121e76e7180Sclaudio 	uint16_t			id;
122e76e7180Sclaudio 	uint8_t				request_started;
123e76e7180Sclaudio 	uint8_t				request_done;
12413a46ceeSclaudio 	uint8_t				timeout_fired;
125e76e7180Sclaudio };
126e76e7180Sclaudio 
127e76e7180Sclaudio LIST_HEAD(requests_head, request);
128e76e7180Sclaudio 
129e76e7180Sclaudio struct slowcgi_proc {
130e76e7180Sclaudio 	struct requests_head	requests;
131e76e7180Sclaudio 	struct event		ev_sigchld;
132e76e7180Sclaudio };
133e76e7180Sclaudio 
134e76e7180Sclaudio struct fcgi_begin_request_body {
135e76e7180Sclaudio 	uint16_t	role;
136e76e7180Sclaudio 	uint8_t		flags;
137e76e7180Sclaudio 	uint8_t		reserved[5];
138e76e7180Sclaudio }__packed;
139e76e7180Sclaudio 
140e76e7180Sclaudio struct fcgi_end_request_body {
141e76e7180Sclaudio 	uint32_t	app_status;
142e76e7180Sclaudio 	uint8_t		protocol_status;
143e76e7180Sclaudio 	uint8_t		reserved[3];
144e76e7180Sclaudio }__packed;
145e76e7180Sclaudio 
146e76e7180Sclaudio __dead void	usage(void);
147e76e7180Sclaudio int		slowcgi_listen(char *, struct passwd *);
148e76e7180Sclaudio void		slowcgi_paused(int, short, void *);
149e76e7180Sclaudio int		accept_reserve(int, struct sockaddr *, socklen_t *, int,
150e76e7180Sclaudio 		    volatile int *);
151e76e7180Sclaudio void		slowcgi_accept(int, short, void *);
152e76e7180Sclaudio void		slowcgi_request(int, short, void *);
153e76e7180Sclaudio void		slowcgi_response(int, short, void *);
154e76e7180Sclaudio void		slowcgi_add_response(struct request *, struct fcgi_response *);
155e76e7180Sclaudio void		slowcgi_timeout(int, short, void *);
156e76e7180Sclaudio void		slowcgi_sig_handler(int, short, void *);
157e76e7180Sclaudio size_t		parse_record(uint8_t * , size_t, struct request *);
158e76e7180Sclaudio void		parse_begin_request(uint8_t *, uint16_t, struct request *,
159e76e7180Sclaudio 		    uint16_t);
160e76e7180Sclaudio void		parse_params(uint8_t *, uint16_t, struct request *, uint16_t);
161e76e7180Sclaudio void		parse_stdin(uint8_t *, uint16_t, struct request *, uint16_t);
162e76e7180Sclaudio char		*env_get(struct request *, const char *);
163f889a45cSclaudio void		error_response(struct request *, int);
164e76e7180Sclaudio void		exec_cgi(struct request *);
165e76e7180Sclaudio void		script_std_in(int, short, void *);
166e76e7180Sclaudio void		script_err_in(int, short, void *);
167e76e7180Sclaudio void		create_data_record(struct request *, uint8_t, const void *,
168e76e7180Sclaudio 		    size_t);
169e76e7180Sclaudio void		create_end_record(struct request *);
170e76e7180Sclaudio void		cleanup_request(struct request *);
171e76e7180Sclaudio void		dump_fcgi_record(const char *,
172e76e7180Sclaudio 		    struct fcgi_record_header *);
173e76e7180Sclaudio void		dump_fcgi_record_header(const char *,
174e76e7180Sclaudio 		    struct fcgi_record_header *);
175e76e7180Sclaudio void		dump_fcgi_begin_request_body(const char *,
176e76e7180Sclaudio 		    struct fcgi_begin_request_body *);
177e76e7180Sclaudio void		dump_fcgi_end_request_body(const char *,
178e76e7180Sclaudio 		    struct fcgi_end_request_body *);
179e76e7180Sclaudio 
180e76e7180Sclaudio const struct loggers conslogger = {
181e76e7180Sclaudio 	err,
182e76e7180Sclaudio 	errx,
183e76e7180Sclaudio 	warn,
184e76e7180Sclaudio 	warnx,
185e76e7180Sclaudio 	warnx, /* info */
186e76e7180Sclaudio 	warnx /* debug */
187e76e7180Sclaudio };
188e76e7180Sclaudio 
189e76e7180Sclaudio __dead void	syslog_err(int, const char *, ...)
190e76e7180Sclaudio 		    __attribute__((__format__ (printf, 2, 3)));
191e76e7180Sclaudio __dead void	syslog_errx(int, const char *, ...)
192e76e7180Sclaudio 		    __attribute__((__format__ (printf, 2, 3)));
193e76e7180Sclaudio void		syslog_warn(const char *, ...)
194e76e7180Sclaudio 		    __attribute__((__format__ (printf, 1, 2)));
195e76e7180Sclaudio void		syslog_warnx(const char *, ...)
196e76e7180Sclaudio 		    __attribute__((__format__ (printf, 1, 2)));
197e76e7180Sclaudio void		syslog_info(const char *, ...)
198e76e7180Sclaudio 		    __attribute__((__format__ (printf, 1, 2)));
199e76e7180Sclaudio void		syslog_debug(const char *, ...)
200e76e7180Sclaudio 		    __attribute__((__format__ (printf, 1, 2)));
201e76e7180Sclaudio void		syslog_vstrerror(int, int, const char *, va_list)
202e76e7180Sclaudio 		    __attribute__((__format__ (printf, 3, 0)));
203e76e7180Sclaudio 
204e76e7180Sclaudio const struct loggers syslogger = {
205e76e7180Sclaudio 	syslog_err,
206e76e7180Sclaudio 	syslog_errx,
207e76e7180Sclaudio 	syslog_warn,
208e76e7180Sclaudio 	syslog_warnx,
209e76e7180Sclaudio 	syslog_info,
210e76e7180Sclaudio 	syslog_debug
211e76e7180Sclaudio };
212e76e7180Sclaudio 
213e76e7180Sclaudio const struct loggers *logger = &conslogger;
214e76e7180Sclaudio 
215e76e7180Sclaudio __dead void
usage(void)216e76e7180Sclaudio usage(void)
217e76e7180Sclaudio {
218e76e7180Sclaudio 	extern char *__progname;
219e76e7180Sclaudio 	fprintf(stderr,
220e76e7180Sclaudio 	    "usage: %s [-d] [-p path] [-S socket] [-s socket] [-U user]\n",
221e76e7180Sclaudio 	    __progname);
222e76e7180Sclaudio 	exit(1);
223e76e7180Sclaudio }
224e76e7180Sclaudio 
225e76e7180Sclaudio struct timeval		timeout = { TIMEOUT_DEFAULT, 0 };
22613a46ceeSclaudio struct timeval		kill_timeout = { 5, 0 };
227e76e7180Sclaudio struct slowcgi_proc	slowcgi_proc;
228e76e7180Sclaudio int			debug = 0;
229e76e7180Sclaudio int			on = 1;
230e76e7180Sclaudio char			*fcgi_socket = "/var/www/run/bgplgd.sock";
231e76e7180Sclaudio char			*bgpctlpath = "bgpctl";
232e76e7180Sclaudio char			*bgpctlsock = "/var/run/bgpd.rsock";
233e76e7180Sclaudio 
234e76e7180Sclaudio 
235e76e7180Sclaudio /*
236e76e7180Sclaudio  * Unveil the command we want to run.
237e76e7180Sclaudio  * If this has a pathname component in it, interpret as a file
238e76e7180Sclaudio  * and unveil the file directly.
239e76e7180Sclaudio  * Otherwise, look up the command in our PATH.
240e76e7180Sclaudio  */
241e76e7180Sclaudio static void
unveil_command(const char * prog)242e76e7180Sclaudio unveil_command(const char *prog)
243e76e7180Sclaudio {
244e76e7180Sclaudio 	const char *pp;
245e76e7180Sclaudio 	char *save, *cmd, *path;
246e76e7180Sclaudio 	struct stat st;
247e76e7180Sclaudio 
248e76e7180Sclaudio 	if (strchr(prog, '/') != NULL) {
249e76e7180Sclaudio 		if (unveil(prog, "x") == -1)
250e76e7180Sclaudio 			err(1, "%s: unveil", prog);
251e76e7180Sclaudio 		return;
252e76e7180Sclaudio 	}
253e76e7180Sclaudio 
254e76e7180Sclaudio 	if (getenv("PATH") == NULL)
255e76e7180Sclaudio 		lerrx(1, "PATH is unset");
256e76e7180Sclaudio 	if ((path = strdup(getenv("PATH"))) == NULL)
257e76e7180Sclaudio 		lerr(1, NULL);
258e76e7180Sclaudio 	save = path;
259e76e7180Sclaudio 	while ((pp = strsep(&path, ":")) != NULL) {
260e76e7180Sclaudio 		if (*pp == '\0')
261e76e7180Sclaudio 			continue;
262e76e7180Sclaudio 		if (asprintf(&cmd, "%s/%s", pp, prog) == -1)
263e76e7180Sclaudio 			lerr(1, NULL);
264e76e7180Sclaudio 		if (lstat(cmd, &st) == -1) {
265e76e7180Sclaudio 			free(cmd);
266e76e7180Sclaudio 			continue;
267e76e7180Sclaudio 		}
268e76e7180Sclaudio 		if (unveil(cmd, "x") == -1)
269e76e7180Sclaudio 			lerr(1, "%s: unveil", cmd);
270e76e7180Sclaudio 		free(cmd);
271e76e7180Sclaudio 		break;
272e76e7180Sclaudio 	}
273e76e7180Sclaudio 	free(save);
274e76e7180Sclaudio }
275e76e7180Sclaudio 
276e76e7180Sclaudio int
main(int argc,char * argv[])277e76e7180Sclaudio main(int argc, char *argv[])
278e76e7180Sclaudio {
279e76e7180Sclaudio 	extern char *__progname;
280e76e7180Sclaudio 	struct listener	*l = NULL;
281e76e7180Sclaudio 	struct passwd	*pw;
282e76e7180Sclaudio 	struct stat	 sb;
283e76e7180Sclaudio 	int		 c, fd;
284e76e7180Sclaudio 	const char	*sock_user = WWW_USER;
285e76e7180Sclaudio 	const char	*cgi_user = BGPLGD_USER;
286e76e7180Sclaudio 
287e76e7180Sclaudio 	/*
288e76e7180Sclaudio 	 * Ensure we have fds 0-2 open so that we have no fd overlaps
289e76e7180Sclaudio 	 * in exec_cgi() later. Just exit on error, we don't have enough
290e76e7180Sclaudio 	 * fds open to output an error message anywhere.
291e76e7180Sclaudio 	 */
292e76e7180Sclaudio 	for (c=0; c < 3; c++) {
293e76e7180Sclaudio 		if (fstat(c, &sb) == -1) {
294e76e7180Sclaudio 			if ((fd = open("/dev/null", O_RDWR)) != -1) {
295e76e7180Sclaudio 				if (dup2(fd, c) == -1)
296e76e7180Sclaudio 					exit(1);
297e76e7180Sclaudio 				if (fd > c)
298e76e7180Sclaudio 					close(fd);
299e76e7180Sclaudio 			} else
300e76e7180Sclaudio 				exit(1);
301e76e7180Sclaudio 		}
302e76e7180Sclaudio 	}
303e76e7180Sclaudio 
304*67a9111eSjob 	while ((c = getopt(argc, argv, "dp:S:s:U:u:V")) != -1) {
305e76e7180Sclaudio 		switch (c) {
306e76e7180Sclaudio 		case 'd':
307e76e7180Sclaudio 			debug++;
308e76e7180Sclaudio 			break;
309e76e7180Sclaudio 		case 'p':
310e76e7180Sclaudio 			bgpctlpath = optarg;
311e76e7180Sclaudio 			break;
312e76e7180Sclaudio 		case 'S':
313e76e7180Sclaudio 			bgpctlsock = optarg;
314e76e7180Sclaudio 			break;
315e76e7180Sclaudio 		case 's':
316e76e7180Sclaudio 			fcgi_socket = optarg;
317e76e7180Sclaudio 			break;
318e76e7180Sclaudio 		case 'U':
319e76e7180Sclaudio 			sock_user = optarg;
320e76e7180Sclaudio 			break;
321*67a9111eSjob 		case 'V':
322*67a9111eSjob 			fprintf(stderr, "OpenBGPD %s\n", BGPD_VERSION);
323*67a9111eSjob 			return 0;
324e76e7180Sclaudio 		default:
325e76e7180Sclaudio 			usage();
326e76e7180Sclaudio 			/* NOTREACHED */
327e76e7180Sclaudio 		}
328e76e7180Sclaudio 	}
329e76e7180Sclaudio 
330e76e7180Sclaudio 	if (geteuid() != 0)
331e76e7180Sclaudio 		errx(1, "need root privileges");
332e76e7180Sclaudio 
333e76e7180Sclaudio 	if (!debug && daemon(0, 0) == -1)
334e76e7180Sclaudio 		err(1, "daemon");
335e76e7180Sclaudio 
336e76e7180Sclaudio 	if (!debug) {
337e76e7180Sclaudio 		openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON);
338e76e7180Sclaudio 		logger = &syslogger;
339e76e7180Sclaudio 	}
340e76e7180Sclaudio 
341e76e7180Sclaudio 	ldebug("sock_user: %s", sock_user);
342e76e7180Sclaudio 	pw = getpwnam(sock_user);
343e76e7180Sclaudio 	if (pw == NULL)
344e76e7180Sclaudio 		lerrx(1, "no %s user", sock_user);
345e76e7180Sclaudio 
346e76e7180Sclaudio 	fd = slowcgi_listen(fcgi_socket, pw);
347e76e7180Sclaudio 
348e76e7180Sclaudio 	ldebug("cgi_user: %s", cgi_user);
349e76e7180Sclaudio 	pw = getpwnam(cgi_user);
350e76e7180Sclaudio 	if (pw == NULL)
351e76e7180Sclaudio 		lerrx(1, "no %s user", cgi_user);
352e76e7180Sclaudio 
353e76e7180Sclaudio 	if (setgroups(1, &pw->pw_gid) ||
354e76e7180Sclaudio 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
355e76e7180Sclaudio 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
356e76e7180Sclaudio 		lerr(1, "unable to revoke privs");
357e76e7180Sclaudio 
358e76e7180Sclaudio 	unveil_command(bgpctlpath);
359e76e7180Sclaudio 
360e76e7180Sclaudio 	if (pledge("stdio rpath unix proc exec", NULL) == -1)
361e76e7180Sclaudio 		lerr(1, "pledge");
362e76e7180Sclaudio 
363e76e7180Sclaudio 	LIST_INIT(&slowcgi_proc.requests);
364e76e7180Sclaudio 	event_init();
365e76e7180Sclaudio 
366e76e7180Sclaudio 	l = calloc(1, sizeof(*l));
367e76e7180Sclaudio 	if (l == NULL)
368e76e7180Sclaudio 		lerr(1, "listener ev alloc");
369e76e7180Sclaudio 
370e76e7180Sclaudio 	event_set(&l->ev, fd, EV_READ | EV_PERSIST, slowcgi_accept, l);
371e76e7180Sclaudio 	event_add(&l->ev, NULL);
372e76e7180Sclaudio 	evtimer_set(&l->pause, slowcgi_paused, l);
373e76e7180Sclaudio 
374e76e7180Sclaudio 	signal_set(&slowcgi_proc.ev_sigchld, SIGCHLD, slowcgi_sig_handler,
375e76e7180Sclaudio 	    &slowcgi_proc);
376e76e7180Sclaudio 	signal_add(&slowcgi_proc.ev_sigchld, NULL);
377e76e7180Sclaudio 
378e76e7180Sclaudio 	signal(SIGPIPE, SIG_IGN);
379e76e7180Sclaudio 
380e76e7180Sclaudio 	event_dispatch();
381e76e7180Sclaudio 	return (0);
382e76e7180Sclaudio }
383e76e7180Sclaudio 
384e76e7180Sclaudio int
slowcgi_listen(char * path,struct passwd * pw)385e76e7180Sclaudio slowcgi_listen(char *path, struct passwd *pw)
386e76e7180Sclaudio {
387e76e7180Sclaudio 	struct sockaddr_un	 sun;
388e76e7180Sclaudio 	mode_t			 old_umask;
389e76e7180Sclaudio 	int			 fd;
390e76e7180Sclaudio 
391e76e7180Sclaudio 	if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
392e76e7180Sclaudio 	    0)) == -1)
393e76e7180Sclaudio 		lerr(1, "slowcgi_listen: socket");
394e76e7180Sclaudio 
395e479af8fSclaudio 	memset(&sun, 0, sizeof(sun));
396e76e7180Sclaudio 	sun.sun_family = AF_UNIX;
397e76e7180Sclaudio 	if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
398e76e7180Sclaudio 	    sizeof(sun.sun_path))
399e76e7180Sclaudio 		lerrx(1, "socket path too long");
400e76e7180Sclaudio 
401e76e7180Sclaudio 	if (unlink(path) == -1)
402e76e7180Sclaudio 		if (errno != ENOENT)
403e76e7180Sclaudio 			lerr(1, "slowcgi_listen: unlink %s", path);
404e76e7180Sclaudio 
405e76e7180Sclaudio 	old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
406e76e7180Sclaudio 
407e76e7180Sclaudio 	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
408e76e7180Sclaudio 		lerr(1,"slowcgi_listen: bind: %s", path);
409e76e7180Sclaudio 
410e76e7180Sclaudio 	umask(old_umask);
411e76e7180Sclaudio 
412e76e7180Sclaudio 	if (chown(path, pw->pw_uid, pw->pw_gid) == -1)
413e76e7180Sclaudio 		lerr(1, "slowcgi_listen: chown: %s", path);
414e76e7180Sclaudio 
415e76e7180Sclaudio 	if (listen(fd, 5) == -1)
416e76e7180Sclaudio 		lerr(1, "listen");
417e76e7180Sclaudio 
418e76e7180Sclaudio 	ldebug("socket: %s", path);
419e76e7180Sclaudio 	return fd;
420e76e7180Sclaudio }
421e76e7180Sclaudio 
422e76e7180Sclaudio void
slowcgi_paused(int fd,short events,void * arg)423e76e7180Sclaudio slowcgi_paused(int fd, short events, void *arg)
424e76e7180Sclaudio {
425e76e7180Sclaudio 	struct listener	*l = arg;
426e76e7180Sclaudio 	event_add(&l->ev, NULL);
427e76e7180Sclaudio }
428e76e7180Sclaudio 
429e76e7180Sclaudio int
accept_reserve(int sockfd,struct sockaddr * addr,socklen_t * addrlen,int reserve,volatile int * counter)430e76e7180Sclaudio accept_reserve(int sockfd, struct sockaddr *addr, socklen_t *addrlen,
431e76e7180Sclaudio     int reserve, volatile int *counter)
432e76e7180Sclaudio {
433e76e7180Sclaudio 	int ret;
434e76e7180Sclaudio 	if (getdtablecount() + reserve +
435e76e7180Sclaudio 	    ((*counter + 1) * FD_NEEDED) >= getdtablesize()) {
436e76e7180Sclaudio 		ldebug("inflight fds exceeded");
437e76e7180Sclaudio 		errno = EMFILE;
438e76e7180Sclaudio 		return -1;
439e76e7180Sclaudio 	}
440e76e7180Sclaudio 
441e76e7180Sclaudio 	if ((ret = accept4(sockfd, addr, addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC))
442e76e7180Sclaudio 	    > -1) {
443e76e7180Sclaudio 		(*counter)++;
444e76e7180Sclaudio 		ldebug("inflight incremented, now %d", *counter);
445e76e7180Sclaudio 	}
446e76e7180Sclaudio 	return ret;
447e76e7180Sclaudio }
448e76e7180Sclaudio 
449e76e7180Sclaudio void
slowcgi_accept(int fd,short events,void * arg)450e76e7180Sclaudio slowcgi_accept(int fd, short events, void *arg)
451e76e7180Sclaudio {
452e76e7180Sclaudio 	struct listener		*l;
453e76e7180Sclaudio 	struct sockaddr_storage	 ss;
454e76e7180Sclaudio 	struct timeval		 backoff;
455e76e7180Sclaudio 	struct request		*c;
456e76e7180Sclaudio 	socklen_t		 len;
457e76e7180Sclaudio 	int			 s;
458e76e7180Sclaudio 
459e76e7180Sclaudio 	l = arg;
460e76e7180Sclaudio 	backoff.tv_sec = 1;
461e76e7180Sclaudio 	backoff.tv_usec = 0;
462e76e7180Sclaudio 	c = NULL;
463e76e7180Sclaudio 
464e76e7180Sclaudio 	len = sizeof(ss);
465e76e7180Sclaudio 	if ((s = accept_reserve(fd, (struct sockaddr *)&ss,
466e76e7180Sclaudio 	    &len, FD_RESERVE, &cgi_inflight)) == -1) {
467e76e7180Sclaudio 		switch (errno) {
468e76e7180Sclaudio 		case EINTR:
469e76e7180Sclaudio 		case EWOULDBLOCK:
470e76e7180Sclaudio 		case ECONNABORTED:
471e76e7180Sclaudio 			return;
472e76e7180Sclaudio 		case EMFILE:
473e76e7180Sclaudio 		case ENFILE:
474e76e7180Sclaudio 			event_del(&l->ev);
475e76e7180Sclaudio 			evtimer_add(&l->pause, &backoff);
476e76e7180Sclaudio 			return;
477e76e7180Sclaudio 		default:
478e76e7180Sclaudio 			lerr(1, "accept");
479e76e7180Sclaudio 		}
480e76e7180Sclaudio 	}
481e76e7180Sclaudio 
482e76e7180Sclaudio 	c = calloc(1, sizeof(*c));
483e76e7180Sclaudio 	if (c == NULL) {
484e76e7180Sclaudio 		lwarn("cannot calloc request");
485e76e7180Sclaudio 		close(s);
486e76e7180Sclaudio 		cgi_inflight--;
487e76e7180Sclaudio 		return;
488e76e7180Sclaudio 	}
489e76e7180Sclaudio 	c->fd = s;
490e76e7180Sclaudio 	c->buf_pos = 0;
491e76e7180Sclaudio 	c->buf_len = 0;
492e76e7180Sclaudio 	c->request_started = 0;
493e76e7180Sclaudio 	c->inflight_fds_accounted = 0;
494e76e7180Sclaudio 	TAILQ_INIT(&c->response_head);
495e76e7180Sclaudio 
496e76e7180Sclaudio 	event_set(&c->ev, s, EV_READ | EV_PERSIST, slowcgi_request, c);
497e76e7180Sclaudio 	event_add(&c->ev, NULL);
498e76e7180Sclaudio 	event_set(&c->resp_ev, s, EV_WRITE | EV_PERSIST, slowcgi_response, c);
499e76e7180Sclaudio 	evtimer_set(&c->tmo, slowcgi_timeout, c);
500e76e7180Sclaudio 	evtimer_add(&c->tmo, &timeout);
501e76e7180Sclaudio 	LIST_INSERT_HEAD(&slowcgi_proc.requests, c, entry);
502e76e7180Sclaudio }
503e76e7180Sclaudio 
504e76e7180Sclaudio void
slowcgi_timeout(int fd,short events,void * arg)505e76e7180Sclaudio slowcgi_timeout(int fd, short events, void *arg)
506e76e7180Sclaudio {
50713a46ceeSclaudio 	struct request	*c = arg;
50813a46ceeSclaudio 	int sig = SIGTERM;
50913a46ceeSclaudio 
51013a46ceeSclaudio 	if (c->script_flags & SCRIPT_DONE)
51113a46ceeSclaudio 		return;
51213a46ceeSclaudio 
513f889a45cSclaudio 	if (c->command_pid == 0) {
514f889a45cSclaudio 		c->command_status = SIGALRM;
515f889a45cSclaudio 		error_response(c, 408);
516f889a45cSclaudio 		return;
517f889a45cSclaudio 	}
518f889a45cSclaudio 
5195cc83d08Sclaudio 	ldebug("timeout fired for pid %d", c->command_pid);
52013a46ceeSclaudio 
52113a46ceeSclaudio 	if (c->timeout_fired)
52213a46ceeSclaudio 		sig = SIGKILL;
52313a46ceeSclaudio 
52413a46ceeSclaudio 	if (kill(c->command_pid, sig) == -1) {
52513a46ceeSclaudio 		lwarn("kill child %d after timeout", c->command_pid);
52613a46ceeSclaudio 		if (event_initialized(&c->script_ev)) {
52713a46ceeSclaudio 			close(EVENT_FD(&c->script_ev));
52813a46ceeSclaudio 			event_del(&c->script_ev);
52913a46ceeSclaudio 		}
53013a46ceeSclaudio 		if (event_initialized(&c->script_err_ev)) {
53113a46ceeSclaudio 			close(EVENT_FD(&c->script_err_ev));
53213a46ceeSclaudio 			event_del(&c->script_err_ev);
53313a46ceeSclaudio 		}
53413a46ceeSclaudio 
53513a46ceeSclaudio 		c->command_status = SIGALRM;
53613a46ceeSclaudio 		c->script_flags = STDOUT_DONE | STDERR_DONE | SCRIPT_DONE;
53713a46ceeSclaudio 		create_end_record(c);
53813a46ceeSclaudio 	}
53913a46ceeSclaudio 
54013a46ceeSclaudio 	if (c->timeout_fired++ == 0)
54113a46ceeSclaudio 		evtimer_add(&c->tmo, &kill_timeout);
542e76e7180Sclaudio }
543e76e7180Sclaudio 
544e76e7180Sclaudio void
slowcgi_sig_handler(int sig,short event,void * arg)545e76e7180Sclaudio slowcgi_sig_handler(int sig, short event, void *arg)
546e76e7180Sclaudio {
547e76e7180Sclaudio 	struct request		*c;
548e76e7180Sclaudio 	struct slowcgi_proc	*p;
549e76e7180Sclaudio 	pid_t			 pid;
550e76e7180Sclaudio 	int			 status;
551e76e7180Sclaudio 
552e76e7180Sclaudio 	p = arg;
553e76e7180Sclaudio 
554e76e7180Sclaudio 	switch (sig) {
555e76e7180Sclaudio 	case SIGCHLD:
556e76e7180Sclaudio 		while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
557e76e7180Sclaudio 			LIST_FOREACH(c, &p->requests, entry)
558e76e7180Sclaudio 				if (c->command_pid == pid)
559e76e7180Sclaudio 					break;
560e76e7180Sclaudio 			if (c == NULL) {
56113a46ceeSclaudio 				lwarnx("caught exit of unknown child %d", pid);
562e76e7180Sclaudio 				continue;
563e76e7180Sclaudio 			}
564e76e7180Sclaudio 
565e76e7180Sclaudio 			if (WIFSIGNALED(status))
566e76e7180Sclaudio 				c->command_status = WTERMSIG(status);
567e76e7180Sclaudio 			else
568e76e7180Sclaudio 				c->command_status = WEXITSTATUS(status);
569e76e7180Sclaudio 
5705cc83d08Sclaudio 			ldebug("pid %d exit %s%d", pid,
571e76e7180Sclaudio 			    WIFSIGNALED(status) ? "signal " : "",
572e76e7180Sclaudio 			    c->command_status);
573e76e7180Sclaudio 
574e76e7180Sclaudio 			c->script_flags |= SCRIPT_DONE;
575e76e7180Sclaudio 
576e76e7180Sclaudio 			if (c->script_flags == (STDOUT_DONE | STDERR_DONE |
577e76e7180Sclaudio 			    SCRIPT_DONE))
578e76e7180Sclaudio 				create_end_record(c);
579e76e7180Sclaudio 		}
580e76e7180Sclaudio 		if (pid == -1 && errno != ECHILD)
581e76e7180Sclaudio 			lwarn("waitpid");
582e76e7180Sclaudio 		break;
583e76e7180Sclaudio 	default:
584e76e7180Sclaudio 		lerr(1, "unexpected signal: %d", sig);
585e76e7180Sclaudio 		break;
586e76e7180Sclaudio 	}
587e76e7180Sclaudio }
588e76e7180Sclaudio 
589e76e7180Sclaudio void
slowcgi_add_response(struct request * c,struct fcgi_response * resp)590e76e7180Sclaudio slowcgi_add_response(struct request *c, struct fcgi_response *resp)
591e76e7180Sclaudio {
592e76e7180Sclaudio 	struct fcgi_record_header	*header;
593e76e7180Sclaudio 	size_t				 padded_len;
594e76e7180Sclaudio 
595e76e7180Sclaudio 	header = (struct fcgi_record_header*)resp->data;
596e76e7180Sclaudio 
597e76e7180Sclaudio 	/* The FastCGI spec suggests to align the output buffer */
598e76e7180Sclaudio 	padded_len = FCGI_ALIGN(resp->data_len);
599e76e7180Sclaudio 	if (padded_len > resp->data_len) {
600e76e7180Sclaudio 		/* There should always be FCGI_PADDING_SIZE bytes left */
601e76e7180Sclaudio 		if (padded_len > FCGI_RECORD_SIZE)
602e76e7180Sclaudio 			lerr(1, "response too long");
603e76e7180Sclaudio 		header->padding_len = padded_len - resp->data_len;
604e76e7180Sclaudio 		resp->data_len = padded_len;
605e76e7180Sclaudio 	}
606e76e7180Sclaudio 
607e76e7180Sclaudio 	TAILQ_INSERT_TAIL(&c->response_head, resp, entry);
608e76e7180Sclaudio 	event_add(&c->resp_ev, NULL);
609e76e7180Sclaudio }
610e76e7180Sclaudio 
611e76e7180Sclaudio void
slowcgi_response(int fd,short events,void * arg)612e76e7180Sclaudio slowcgi_response(int fd, short events, void *arg)
613e76e7180Sclaudio {
614e76e7180Sclaudio 	struct request			*c;
615e76e7180Sclaudio 	struct fcgi_record_header	*header;
616e76e7180Sclaudio 	struct fcgi_response		*resp;
617e76e7180Sclaudio 	ssize_t				 n;
618e76e7180Sclaudio 
619e76e7180Sclaudio 	c = arg;
620e76e7180Sclaudio 
621e76e7180Sclaudio 	while ((resp = TAILQ_FIRST(&c->response_head))) {
622e76e7180Sclaudio 		header = (struct fcgi_record_header*) resp->data;
623e76e7180Sclaudio 		if (debug > 1)
624e76e7180Sclaudio 			dump_fcgi_record("resp ", header);
625e76e7180Sclaudio 
626e76e7180Sclaudio 		n = write(fd, resp->data + resp->data_pos, resp->data_len);
627e76e7180Sclaudio 		if (n == -1) {
628e76e7180Sclaudio 			if (errno == EAGAIN || errno == EINTR)
629e76e7180Sclaudio 				return;
630e76e7180Sclaudio 			cleanup_request(c);
631e76e7180Sclaudio 			return;
632e76e7180Sclaudio 		}
633e76e7180Sclaudio 		resp->data_pos += n;
634e76e7180Sclaudio 		resp->data_len -= n;
635e76e7180Sclaudio 		if (resp->data_len == 0) {
636e76e7180Sclaudio 			TAILQ_REMOVE(&c->response_head, resp, entry);
637e76e7180Sclaudio 			free(resp);
638e76e7180Sclaudio 		}
639e76e7180Sclaudio 	}
640e76e7180Sclaudio 
641e76e7180Sclaudio 	if (TAILQ_EMPTY(&c->response_head)) {
642e76e7180Sclaudio 		if (c->request_done)
643e76e7180Sclaudio 			cleanup_request(c);
644e76e7180Sclaudio 		else
645e76e7180Sclaudio 			event_del(&c->resp_ev);
646e76e7180Sclaudio 	}
647e76e7180Sclaudio }
648e76e7180Sclaudio 
649e76e7180Sclaudio void
slowcgi_request(int fd,short events,void * arg)650e76e7180Sclaudio slowcgi_request(int fd, short events, void *arg)
651e76e7180Sclaudio {
652e76e7180Sclaudio 	struct request	*c;
653e76e7180Sclaudio 	ssize_t		 n;
654e76e7180Sclaudio 	size_t		 parsed;
655e76e7180Sclaudio 
656e76e7180Sclaudio 	c = arg;
657e76e7180Sclaudio 
658e76e7180Sclaudio 	n = read(fd, c->buf + c->buf_pos + c->buf_len,
659e76e7180Sclaudio 	    FCGI_RECORD_SIZE - c->buf_pos-c->buf_len);
660e76e7180Sclaudio 
661e76e7180Sclaudio 	switch (n) {
662e76e7180Sclaudio 	case -1:
663e76e7180Sclaudio 		switch (errno) {
664e76e7180Sclaudio 		case EINTR:
665e76e7180Sclaudio 		case EAGAIN:
666e76e7180Sclaudio 			return;
667e76e7180Sclaudio 		default:
668e76e7180Sclaudio 			goto fail;
669e76e7180Sclaudio 		}
670e76e7180Sclaudio 		break;
671e76e7180Sclaudio 
672e76e7180Sclaudio 	case 0:
673e76e7180Sclaudio 		ldebug("closed connection");
674e76e7180Sclaudio 		goto fail;
675e76e7180Sclaudio 	default:
676e76e7180Sclaudio 		break;
677e76e7180Sclaudio 	}
678e76e7180Sclaudio 
679e76e7180Sclaudio 	c->buf_len += n;
680e76e7180Sclaudio 
681e76e7180Sclaudio 	/*
682e76e7180Sclaudio 	 * Parse the records as they are received. Per the FastCGI
683e76e7180Sclaudio 	 * specification, the server need only receive the FastCGI
684e76e7180Sclaudio 	 * parameter records in full; it is free to begin execution
685e76e7180Sclaudio 	 * at that point, which is what happens here.
686e76e7180Sclaudio 	 */
687e76e7180Sclaudio 	do {
688e76e7180Sclaudio 		parsed = parse_record(c->buf + c->buf_pos, c->buf_len, c);
689e76e7180Sclaudio 		c->buf_pos += parsed;
690e76e7180Sclaudio 		c->buf_len -= parsed;
691e76e7180Sclaudio 	} while (parsed > 0 && c->buf_len > 0);
692e76e7180Sclaudio 
693e76e7180Sclaudio 	/* Make space for further reads */
694e76e7180Sclaudio 	if (c->buf_len > 0) {
695e479af8fSclaudio 		memmove(c->buf, c->buf + c->buf_pos, c->buf_len);
696e76e7180Sclaudio 		c->buf_pos = 0;
697e76e7180Sclaudio 	}
698e76e7180Sclaudio 	return;
699e76e7180Sclaudio fail:
700e76e7180Sclaudio 	cleanup_request(c);
701e76e7180Sclaudio }
702e76e7180Sclaudio 
703e76e7180Sclaudio void
parse_begin_request(uint8_t * buf,uint16_t n,struct request * c,uint16_t id)704e76e7180Sclaudio parse_begin_request(uint8_t *buf, uint16_t n, struct request *c, uint16_t id)
705e76e7180Sclaudio {
706e76e7180Sclaudio 	/* XXX -- FCGI_CANT_MPX_CONN */
707e76e7180Sclaudio 	if (c->request_started) {
708e76e7180Sclaudio 		lwarnx("unexpected FCGI_BEGIN_REQUEST, ignoring");
709e76e7180Sclaudio 		return;
710e76e7180Sclaudio 	}
711e76e7180Sclaudio 
712e76e7180Sclaudio 	if (n != sizeof(struct fcgi_begin_request_body)) {
713e76e7180Sclaudio 		lwarnx("wrong size %d != %lu", n,
714e76e7180Sclaudio 		    sizeof(struct fcgi_begin_request_body));
715e76e7180Sclaudio 		return;
716e76e7180Sclaudio 	}
717e76e7180Sclaudio 
718e76e7180Sclaudio 	c->request_started = 1;
719e76e7180Sclaudio 
720e76e7180Sclaudio 	c->id = id;
721e76e7180Sclaudio 	SLIST_INIT(&c->env);
722e76e7180Sclaudio 	c->env_count = 0;
723e76e7180Sclaudio }
724e76e7180Sclaudio 
725e76e7180Sclaudio void
parse_params(uint8_t * buf,uint16_t n,struct request * c,uint16_t id)726e76e7180Sclaudio parse_params(uint8_t *buf, uint16_t n, struct request *c, uint16_t id)
727e76e7180Sclaudio {
728e76e7180Sclaudio 	struct env_val			*env_entry;
729e76e7180Sclaudio 	uint32_t			 name_len, val_len;
730e76e7180Sclaudio 
731e76e7180Sclaudio 	if (!c->request_started) {
732e76e7180Sclaudio 		lwarnx("FCGI_PARAMS without FCGI_BEGIN_REQUEST, ignoring");
733e76e7180Sclaudio 		return;
734e76e7180Sclaudio 	}
735e76e7180Sclaudio 
736e76e7180Sclaudio 	if (c->id != id) {
737e76e7180Sclaudio 		lwarnx("unexpected id, ignoring");
738e76e7180Sclaudio 		return;
739e76e7180Sclaudio 	}
740e76e7180Sclaudio 
741e76e7180Sclaudio 	/*
742e76e7180Sclaudio 	 * If this is the last FastCGI parameter record,
743e76e7180Sclaudio 	 * begin execution of the CGI script.
744e76e7180Sclaudio 	 */
745e76e7180Sclaudio 	if (n == 0) {
746e76e7180Sclaudio 		exec_cgi(c);
747e76e7180Sclaudio 		return;
748e76e7180Sclaudio 	}
749e76e7180Sclaudio 
750e76e7180Sclaudio 	while (n > 0) {
751e76e7180Sclaudio 		if (buf[0] >> 7 == 0) {
752e76e7180Sclaudio 			name_len = buf[0];
753e76e7180Sclaudio 			n--;
754e76e7180Sclaudio 			buf++;
755e76e7180Sclaudio 		} else {
756e76e7180Sclaudio 			if (n > 3) {
757e76e7180Sclaudio 				name_len = ((buf[0] & 0x7f) << 24) +
758e76e7180Sclaudio 				    (buf[1] << 16) + (buf[2] << 8) + buf[3];
759e76e7180Sclaudio 				n -= 4;
760e76e7180Sclaudio 				buf += 4;
761e76e7180Sclaudio 			} else
762e76e7180Sclaudio 				return;
763e76e7180Sclaudio 		}
764e76e7180Sclaudio 
765e76e7180Sclaudio 		if (n > 0) {
766e76e7180Sclaudio 			if (buf[0] >> 7 == 0) {
767e76e7180Sclaudio 				val_len = buf[0];
768e76e7180Sclaudio 				n--;
769e76e7180Sclaudio 				buf++;
770e76e7180Sclaudio 			} else {
771e76e7180Sclaudio 				if (n > 3) {
772e76e7180Sclaudio 					val_len = ((buf[0] & 0x7f) << 24) +
773e76e7180Sclaudio 					    (buf[1] << 16) + (buf[2] << 8) +
774e76e7180Sclaudio 					     buf[3];
775e76e7180Sclaudio 					n -= 4;
776e76e7180Sclaudio 					buf += 4;
777e76e7180Sclaudio 				} else
778e76e7180Sclaudio 					return;
779e76e7180Sclaudio 			}
780e76e7180Sclaudio 		} else
781e76e7180Sclaudio 			return;
782e76e7180Sclaudio 
783e76e7180Sclaudio 		if (n < name_len + val_len)
784e76e7180Sclaudio 			return;
785e76e7180Sclaudio 
786e76e7180Sclaudio 		if ((env_entry = malloc(sizeof(struct env_val))) == NULL) {
787e76e7180Sclaudio 			lwarnx("cannot allocate env_entry");
788e76e7180Sclaudio 			return;
789e76e7180Sclaudio 		}
790e76e7180Sclaudio 
791e76e7180Sclaudio 		if ((env_entry->key = malloc(name_len + 1)) == NULL) {
792e76e7180Sclaudio 			lwarnx("cannot allocate env_entry->key");
793e76e7180Sclaudio 			free(env_entry);
794e76e7180Sclaudio 			return;
795e76e7180Sclaudio 		}
796e76e7180Sclaudio 		if ((env_entry->val = malloc(val_len + 1)) == NULL) {
797e76e7180Sclaudio 			lwarnx("cannot allocate env_entry->val");
798e76e7180Sclaudio 			free(env_entry->key);
799e76e7180Sclaudio 			free(env_entry);
800e76e7180Sclaudio 			return;
801e76e7180Sclaudio 		}
802e76e7180Sclaudio 
803e479af8fSclaudio 		memcpy(env_entry->key, buf, name_len);
804e76e7180Sclaudio 		buf += name_len;
805e76e7180Sclaudio 		n -= name_len;
806e76e7180Sclaudio 		env_entry->key[name_len] = '\0';
807e479af8fSclaudio 		memcpy(env_entry->val, buf, val_len);
808e76e7180Sclaudio 		buf += val_len;
809e76e7180Sclaudio 		n -= val_len;
810e76e7180Sclaudio 		env_entry->val[val_len] = '\0';
811e76e7180Sclaudio 
812e76e7180Sclaudio 		SLIST_INSERT_HEAD(&c->env, env_entry, entry);
813e76e7180Sclaudio 		ldebug("env[%d], %s=%s", c->env_count, env_entry->key,
814e76e7180Sclaudio 		    env_entry->val);
815e76e7180Sclaudio 		c->env_count++;
816e76e7180Sclaudio 	}
817e76e7180Sclaudio }
818e76e7180Sclaudio 
819e76e7180Sclaudio void
parse_stdin(uint8_t * buf,uint16_t n,struct request * c,uint16_t id)820e76e7180Sclaudio parse_stdin(uint8_t *buf, uint16_t n, struct request *c, uint16_t id)
821e76e7180Sclaudio {
822e76e7180Sclaudio 	if (c->id != id) {
823e76e7180Sclaudio 		lwarnx("unexpected id, ignoring");
824e76e7180Sclaudio 		return;
825e76e7180Sclaudio 	}
826e76e7180Sclaudio 
827e76e7180Sclaudio 	if (n != 0)
828e76e7180Sclaudio 		lwarnx("unexpected stdin input, ignoring");
829e76e7180Sclaudio }
830e76e7180Sclaudio 
831e76e7180Sclaudio size_t
parse_record(uint8_t * buf,size_t n,struct request * c)832e76e7180Sclaudio parse_record(uint8_t *buf, size_t n, struct request *c)
833e76e7180Sclaudio {
834e76e7180Sclaudio 	struct fcgi_record_header	*h;
835e76e7180Sclaudio 
836e76e7180Sclaudio 	if (n < sizeof(struct fcgi_record_header))
837e76e7180Sclaudio 		return (0);
838e76e7180Sclaudio 
839e76e7180Sclaudio 	h = (struct fcgi_record_header*) buf;
840e76e7180Sclaudio 
841e76e7180Sclaudio 	if (debug > 1)
842e76e7180Sclaudio 		dump_fcgi_record("", h);
843e76e7180Sclaudio 
844e76e7180Sclaudio 	if (n < sizeof(struct fcgi_record_header) + ntohs(h->content_len)
845e76e7180Sclaudio 	    + h->padding_len)
846e76e7180Sclaudio 		return (0);
847e76e7180Sclaudio 
848e76e7180Sclaudio 	if (h->version != 1)
849e76e7180Sclaudio 		lerrx(1, "wrong version");
850e76e7180Sclaudio 
851e76e7180Sclaudio 	switch (h->type) {
852e76e7180Sclaudio 	case FCGI_BEGIN_REQUEST:
853e76e7180Sclaudio 		parse_begin_request(buf + sizeof(struct fcgi_record_header),
854e76e7180Sclaudio 		    ntohs(h->content_len), c, ntohs(h->id));
855e76e7180Sclaudio 		break;
856e76e7180Sclaudio 	case FCGI_PARAMS:
857e76e7180Sclaudio 		parse_params(buf + sizeof(struct fcgi_record_header),
858e76e7180Sclaudio 		    ntohs(h->content_len), c, ntohs(h->id));
859e76e7180Sclaudio 		break;
860e76e7180Sclaudio 	case FCGI_STDIN:
861e76e7180Sclaudio 		parse_stdin(buf + sizeof(struct fcgi_record_header),
862e76e7180Sclaudio 		    ntohs(h->content_len), c, ntohs(h->id));
863e76e7180Sclaudio 		break;
864e76e7180Sclaudio 	default:
865e76e7180Sclaudio 		lwarnx("unimplemented type %d", h->type);
866e76e7180Sclaudio 		break;
867e76e7180Sclaudio 	}
868e76e7180Sclaudio 
869e76e7180Sclaudio 	return (sizeof(struct fcgi_record_header) + ntohs(h->content_len)
870e76e7180Sclaudio 	    + h->padding_len);
871e76e7180Sclaudio }
872e76e7180Sclaudio 
873e76e7180Sclaudio char *
env_get(struct request * c,const char * key)874e76e7180Sclaudio env_get(struct request *c, const char *key)
875e76e7180Sclaudio {
876e76e7180Sclaudio 	struct env_val	*env;
877e76e7180Sclaudio 
878e76e7180Sclaudio 	SLIST_FOREACH(env, &c->env, entry) {
879e76e7180Sclaudio 		if (strcmp(env->key, key) == 0)
880e76e7180Sclaudio 			return (env->val);
881e76e7180Sclaudio 	}
882e76e7180Sclaudio 	return (NULL);
883e76e7180Sclaudio }
884e76e7180Sclaudio 
885e76e7180Sclaudio static const char *
http_error(int * res)886e76e7180Sclaudio http_error(int *res)
887e76e7180Sclaudio {
888e76e7180Sclaudio 	const struct http_error errors[] = HTTP_ERRORS;
889e76e7180Sclaudio 	size_t i;
890e76e7180Sclaudio 
891e76e7180Sclaudio 	for (i = 0; errors[i].error_code != 0; i++)
892e76e7180Sclaudio 		if (errors[i].error_code == *res)
893e76e7180Sclaudio 			return errors[i].error_name;
894e76e7180Sclaudio 
895e76e7180Sclaudio 	/* unknown error - change to 500 */
896e76e7180Sclaudio 	lwarnx("unknown http error %d", *res);
897e76e7180Sclaudio 	*res = 500;
898e76e7180Sclaudio 	return "Internal Server Error";
899e76e7180Sclaudio }
900e76e7180Sclaudio 
901f889a45cSclaudio void
error_response(struct request * c,int res)902e76e7180Sclaudio error_response(struct request *c, int res)
903e76e7180Sclaudio {
904e76e7180Sclaudio 	const char *type = "text/html";
905e76e7180Sclaudio 	const char *errstr = http_error(&res);
906e76e7180Sclaudio 	char *buf;
907e76e7180Sclaudio 	int len;
908e76e7180Sclaudio 
909e76e7180Sclaudio 	lwarnx("HTTP status %d: %s", res, errstr);
910e76e7180Sclaudio 
911e76e7180Sclaudio 	len = asprintf(&buf,
912e76e7180Sclaudio 	    "Content-Type: %s\n"
913e76e7180Sclaudio 	    "Status: %d\n"
914e76e7180Sclaudio 	    "Cache-Control: no-cache\n"
915e76e7180Sclaudio 	    "\n"
916e76e7180Sclaudio 	    "<!DOCTYPE html>\n"
917e76e7180Sclaudio 	    "<html>\n"
918e76e7180Sclaudio 	    " <head>\n"
919e76e7180Sclaudio 	    "  <meta http-equiv=\"Content-Type\" "
920e76e7180Sclaudio 	    "content=\"%s; charset=utf-8\"/>\n"
921e76e7180Sclaudio 	    "  <title>%d %s</title>\n"
922e76e7180Sclaudio 	    " </head>\n"
923e76e7180Sclaudio 	    " <body>\n"
924e76e7180Sclaudio 	    "  <h1>%d %s</h1>\n"
925e76e7180Sclaudio 	    "  <hr>\n"
926e76e7180Sclaudio 	    "  <address>OpenBSD bgplgd</address>\n"
927e76e7180Sclaudio 	    " </body>\n"
928e76e7180Sclaudio 	    "</html>\n",
929e76e7180Sclaudio 	    type, res, type, res, errstr, res, errstr);
930e76e7180Sclaudio 
931e76e7180Sclaudio 	if (len == -1)
932e76e7180Sclaudio 		lerr(1, NULL);
933e76e7180Sclaudio 
934e76e7180Sclaudio 	create_data_record(c, FCGI_STDOUT, buf, len);
935e76e7180Sclaudio 	free(buf);
936e76e7180Sclaudio 	c->script_flags = (STDOUT_DONE | STDERR_DONE | SCRIPT_DONE);
937e76e7180Sclaudio 	create_end_record(c);
938e76e7180Sclaudio }
939e76e7180Sclaudio 
940e76e7180Sclaudio /*
941e76e7180Sclaudio  * Fork a new CGI process to handle the request, translating
942e76e7180Sclaudio  * between FastCGI parameter records and CGI's environment variables,
943e76e7180Sclaudio  * as well as between the CGI process' stdin/stdout and the
944e76e7180Sclaudio  * corresponding FastCGI records.
945e76e7180Sclaudio  */
946e76e7180Sclaudio void
exec_cgi(struct request * c)947e76e7180Sclaudio exec_cgi(struct request *c)
948e76e7180Sclaudio {
949e76e7180Sclaudio 	struct lg_ctx	 ctx = { 0 };
950e76e7180Sclaudio 	int		 s_in[2], s_out[2], s_err[2], res;
951e76e7180Sclaudio 	pid_t		 pid;
952e76e7180Sclaudio 
953e76e7180Sclaudio 	res = prep_request(&ctx, env_get(c, "REQUEST_METHOD"),
954e76e7180Sclaudio 	    env_get(c, "PATH_INFO"), env_get(c, "QUERY_STRING"));
955e76e7180Sclaudio 	if (res != 0) {
956e76e7180Sclaudio 		error_response(c, res);
957e76e7180Sclaudio 		return;
958e76e7180Sclaudio 	}
959e76e7180Sclaudio 
960e76e7180Sclaudio 	if (pipe(s_in) == -1)
961e76e7180Sclaudio 		lerr(1, "pipe");
962e76e7180Sclaudio 	if (pipe(s_out) == -1)
963e76e7180Sclaudio 		lerr(1, "pipe");
964e76e7180Sclaudio 	if (pipe(s_err) == -1)
965e76e7180Sclaudio 		lerr(1, "pipe");
966e76e7180Sclaudio 	cgi_inflight--;
967e76e7180Sclaudio 	c->inflight_fds_accounted = 1;
968e76e7180Sclaudio 
969e76e7180Sclaudio 	switch (pid = fork()) {
970e76e7180Sclaudio 	case -1:
971e76e7180Sclaudio 		c->command_status = errno;
972e76e7180Sclaudio 
973e76e7180Sclaudio 		lwarn("fork");
974e76e7180Sclaudio 
975e76e7180Sclaudio 		close(s_in[0]);
976e76e7180Sclaudio 		close(s_out[0]);
977e76e7180Sclaudio 		close(s_err[0]);
978e76e7180Sclaudio 
979e76e7180Sclaudio 		close(s_in[1]);
980e76e7180Sclaudio 		close(s_out[1]);
981e76e7180Sclaudio 		close(s_err[1]);
982e76e7180Sclaudio 
983e76e7180Sclaudio 		c->script_flags = (STDOUT_DONE | STDERR_DONE | SCRIPT_DONE);
984e76e7180Sclaudio 		create_end_record(c);
985e76e7180Sclaudio 		return;
986e76e7180Sclaudio 	case 0:
987e76e7180Sclaudio 		/* Child process */
988e76e7180Sclaudio 		if (pledge("stdio rpath exec", NULL) == -1)
989e76e7180Sclaudio 			lerr(1, "pledge");
990e76e7180Sclaudio 		close(s_in[0]);
991e76e7180Sclaudio 		close(s_out[0]);
992e76e7180Sclaudio 		close(s_err[0]);
993e76e7180Sclaudio 
994e76e7180Sclaudio 		if (dup2(s_in[1], STDIN_FILENO) == -1)
995e76e7180Sclaudio 			_exit(1);
996e76e7180Sclaudio 		if (dup2(s_out[1], STDOUT_FILENO) == -1)
997e76e7180Sclaudio 			_exit(1);
998e76e7180Sclaudio 		if (dup2(s_err[1], STDERR_FILENO) == -1)
999e76e7180Sclaudio 			_exit(1);
1000e76e7180Sclaudio 
1001e76e7180Sclaudio 		close(s_in[1]);
1002e76e7180Sclaudio 		close(s_out[1]);
1003e76e7180Sclaudio 		close(s_err[1]);
1004e76e7180Sclaudio 
1005e76e7180Sclaudio 		bgpctl_call(&ctx);
1006e76e7180Sclaudio 
1007e76e7180Sclaudio 		/* should not be reached */
1008e76e7180Sclaudio 		_exit(1);
1009e76e7180Sclaudio 
1010e76e7180Sclaudio 	}
1011e76e7180Sclaudio 
1012e76e7180Sclaudio 	ldebug("fork %d", pid);
1013e76e7180Sclaudio 
1014e76e7180Sclaudio 	/* Parent process*/
1015e76e7180Sclaudio 	close(s_in[1]);
1016e76e7180Sclaudio 	close(s_out[1]);
1017e76e7180Sclaudio 	close(s_err[1]);
1018e76e7180Sclaudio 
1019e76e7180Sclaudio 	fcntl(s_in[0], F_SETFD, FD_CLOEXEC);
1020e76e7180Sclaudio 	fcntl(s_out[0], F_SETFD, FD_CLOEXEC);
1021e76e7180Sclaudio 	fcntl(s_err[0], F_SETFD, FD_CLOEXEC);
1022e76e7180Sclaudio 
1023e76e7180Sclaudio 	if (ioctl(s_in[0], FIONBIO, &on) == -1)
1024e76e7180Sclaudio 		lerr(1, "script ioctl(FIONBIO)");
1025e76e7180Sclaudio 	if (ioctl(s_out[0], FIONBIO, &on) == -1)
1026e76e7180Sclaudio 		lerr(1, "script ioctl(FIONBIO)");
1027e76e7180Sclaudio 	if (ioctl(s_err[0], FIONBIO, &on) == -1)
1028e76e7180Sclaudio 		lerr(1, "script ioctl(FIONBIO)");
1029e76e7180Sclaudio 
1030e76e7180Sclaudio 	close(s_in[0]);	/* close stdin, bgpctl does not expect anything */
1031e76e7180Sclaudio 
1032e76e7180Sclaudio 	c->command_pid = pid;
1033e76e7180Sclaudio 	event_set(&c->script_ev, s_out[0], EV_READ | EV_PERSIST,
1034e76e7180Sclaudio 	    script_std_in, c);
1035e76e7180Sclaudio 	event_add(&c->script_ev, NULL);
1036e76e7180Sclaudio 	event_set(&c->script_err_ev, s_err[0], EV_READ | EV_PERSIST,
1037e76e7180Sclaudio 	    script_err_in, c);
1038e76e7180Sclaudio 	event_add(&c->script_err_ev, NULL);
1039e76e7180Sclaudio }
1040e76e7180Sclaudio 
1041e76e7180Sclaudio static void
script_in(int fd,struct event * ev,struct request * c,uint8_t type)1042e76e7180Sclaudio script_in(int fd, struct event *ev, struct request *c, uint8_t type)
1043e76e7180Sclaudio {
1044e76e7180Sclaudio 	struct fcgi_response		*resp;
1045e76e7180Sclaudio 	struct fcgi_record_header	*header;
1046e76e7180Sclaudio 	ssize_t				 n;
1047e76e7180Sclaudio 
1048e76e7180Sclaudio 	if ((resp = calloc(1, sizeof(struct fcgi_response))) == NULL) {
1049e76e7180Sclaudio 		lwarnx("cannot malloc fcgi_response");
1050e76e7180Sclaudio 		return;
1051e76e7180Sclaudio 	}
1052e76e7180Sclaudio 	header = (struct fcgi_record_header*) resp->data;
1053e76e7180Sclaudio 	header->version = 1;
1054e76e7180Sclaudio 	header->type = type;
1055e76e7180Sclaudio 	header->id = htons(c->id);
1056e76e7180Sclaudio 	header->padding_len = 0;
1057e76e7180Sclaudio 	header->reserved = 0;
1058e76e7180Sclaudio 
1059e76e7180Sclaudio 	n = read(fd, resp->data + sizeof(struct fcgi_record_header),
1060e76e7180Sclaudio 	    FCGI_CONTENT_SIZE);
1061e76e7180Sclaudio 
1062e76e7180Sclaudio 	if (n == -1) {
1063e76e7180Sclaudio 		switch (errno) {
1064e76e7180Sclaudio 		case EINTR:
1065e76e7180Sclaudio 		case EAGAIN:
1066e76e7180Sclaudio 			free(resp);
1067e76e7180Sclaudio 			return;
1068e76e7180Sclaudio 		default:
1069e76e7180Sclaudio 			n = 0; /* fake empty FCGI_STD{OUT,ERR} response */
1070e76e7180Sclaudio 		}
1071e76e7180Sclaudio 	}
1072e76e7180Sclaudio 	header->content_len = htons(n);
1073e76e7180Sclaudio 	resp->data_pos = 0;
1074e76e7180Sclaudio 	resp->data_len = n + sizeof(struct fcgi_record_header);
1075e76e7180Sclaudio 	slowcgi_add_response(c, resp);
1076e76e7180Sclaudio 
1077e76e7180Sclaudio 	if (n == 0) {
1078e76e7180Sclaudio 		if (type == FCGI_STDOUT)
1079e76e7180Sclaudio 			c->script_flags |= STDOUT_DONE;
1080e76e7180Sclaudio 		else
1081e76e7180Sclaudio 			c->script_flags |= STDERR_DONE;
1082e76e7180Sclaudio 
1083e76e7180Sclaudio 		if (c->script_flags == (STDOUT_DONE | STDERR_DONE |
1084e76e7180Sclaudio 		    SCRIPT_DONE))
1085e76e7180Sclaudio 			create_end_record(c);
1086e76e7180Sclaudio 		event_del(ev);
1087e76e7180Sclaudio 		close(fd);
1088e76e7180Sclaudio 	}
1089e76e7180Sclaudio }
1090e76e7180Sclaudio 
1091e76e7180Sclaudio void
script_std_in(int fd,short events,void * arg)1092e76e7180Sclaudio script_std_in(int fd, short events, void *arg)
1093e76e7180Sclaudio {
1094e76e7180Sclaudio 	struct request *c = arg;
1095e76e7180Sclaudio 	script_in(fd, &c->script_ev, c, FCGI_STDOUT);
1096e76e7180Sclaudio }
1097e76e7180Sclaudio 
1098e76e7180Sclaudio void
script_err_in(int fd,short events,void * arg)1099e76e7180Sclaudio script_err_in(int fd, short events, void *arg)
1100e76e7180Sclaudio {
1101e76e7180Sclaudio 	struct request *c = arg;
1102e76e7180Sclaudio 	script_in(fd, &c->script_err_ev, c, FCGI_STDERR);
1103e76e7180Sclaudio }
1104e76e7180Sclaudio 
1105e76e7180Sclaudio void
create_data_record(struct request * c,uint8_t type,const void * buf,size_t len)1106e76e7180Sclaudio create_data_record(struct request *c, uint8_t type, const void *buf, size_t len)
1107e76e7180Sclaudio {
1108e76e7180Sclaudio 	struct fcgi_response		*resp;
1109e76e7180Sclaudio 	struct fcgi_record_header	*header;
1110e76e7180Sclaudio 	const char			*d = buf;
1111e76e7180Sclaudio 	size_t				 n;
1112e76e7180Sclaudio 
1113e76e7180Sclaudio 	do {
1114e76e7180Sclaudio 		if ((resp = calloc(1, sizeof(struct fcgi_response))) == NULL) {
1115e76e7180Sclaudio 			lwarnx("cannot malloc fcgi_response");
1116e76e7180Sclaudio 			return;
1117e76e7180Sclaudio 		}
1118e76e7180Sclaudio 		header = (struct fcgi_record_header*) resp->data;
1119e76e7180Sclaudio 		header->version = 1;
1120e76e7180Sclaudio 		header->type = type;
1121e76e7180Sclaudio 		header->id = htons(c->id);
1122e76e7180Sclaudio 		header->padding_len = 0;
1123e76e7180Sclaudio 		header->reserved = 0;
1124e76e7180Sclaudio 
1125e76e7180Sclaudio 		n = len > FCGI_CONTENT_SIZE ? FCGI_CONTENT_SIZE : len;
1126e76e7180Sclaudio 		memcpy(resp->data + sizeof(struct fcgi_record_header), d, n);
1127e76e7180Sclaudio 
1128e76e7180Sclaudio 		header->content_len = htons(n);
1129e76e7180Sclaudio 		resp->data_pos = 0;
1130e76e7180Sclaudio 		resp->data_len = n + sizeof(struct fcgi_record_header);
1131e76e7180Sclaudio 		slowcgi_add_response(c, resp);
1132e76e7180Sclaudio 
1133e76e7180Sclaudio 		len -= n;
1134e76e7180Sclaudio 		d += n;
1135e76e7180Sclaudio 	} while (len > 0);
1136e76e7180Sclaudio }
1137e76e7180Sclaudio 
1138e76e7180Sclaudio void
create_end_record(struct request * c)1139e76e7180Sclaudio create_end_record(struct request *c)
1140e76e7180Sclaudio {
1141e76e7180Sclaudio 	struct fcgi_response		*resp;
1142e76e7180Sclaudio 	struct fcgi_record_header	*header;
1143e76e7180Sclaudio 	struct fcgi_end_request_body	*end_request;
1144e76e7180Sclaudio 
1145e76e7180Sclaudio 	if ((resp = calloc(1, sizeof(struct fcgi_response))) == NULL) {
1146e76e7180Sclaudio 		lwarnx("cannot malloc fcgi_response");
1147e76e7180Sclaudio 		return;
1148e76e7180Sclaudio 	}
1149e76e7180Sclaudio 	header = (struct fcgi_record_header*) resp->data;
1150e76e7180Sclaudio 	header->version = 1;
1151e76e7180Sclaudio 	header->type = FCGI_END_REQUEST;
1152e76e7180Sclaudio 	header->id = htons(c->id);
1153e76e7180Sclaudio 	header->content_len = htons(sizeof(struct
1154e76e7180Sclaudio 	    fcgi_end_request_body));
1155e76e7180Sclaudio 	header->padding_len = 0;
1156e76e7180Sclaudio 	header->reserved = 0;
1157e76e7180Sclaudio 	end_request = (struct fcgi_end_request_body *) (resp->data +
1158e76e7180Sclaudio 	    sizeof(struct fcgi_record_header));
1159e76e7180Sclaudio 	end_request->app_status = htonl(c->command_status);
1160e76e7180Sclaudio 	end_request->protocol_status = FCGI_REQUEST_COMPLETE;
1161e76e7180Sclaudio 	end_request->reserved[0] = 0;
1162e76e7180Sclaudio 	end_request->reserved[1] = 0;
1163e76e7180Sclaudio 	end_request->reserved[2] = 0;
1164e76e7180Sclaudio 	resp->data_pos = 0;
1165e76e7180Sclaudio 	resp->data_len = sizeof(struct fcgi_end_request_body) +
1166e76e7180Sclaudio 	    sizeof(struct fcgi_record_header);
1167e76e7180Sclaudio 	slowcgi_add_response(c, resp);
1168e76e7180Sclaudio 	c->request_done = 1;
1169e76e7180Sclaudio }
1170e76e7180Sclaudio 
1171e76e7180Sclaudio void
cleanup_request(struct request * c)1172e76e7180Sclaudio cleanup_request(struct request *c)
1173e76e7180Sclaudio {
1174e76e7180Sclaudio 	struct fcgi_response	*resp;
1175e76e7180Sclaudio 	struct env_val		*env_entry;
1176e76e7180Sclaudio 
1177e76e7180Sclaudio 	evtimer_del(&c->tmo);
1178e76e7180Sclaudio 	if (event_initialized(&c->ev))
1179e76e7180Sclaudio 		event_del(&c->ev);
1180e76e7180Sclaudio 	if (event_initialized(&c->resp_ev))
1181e76e7180Sclaudio 		event_del(&c->resp_ev);
1182e76e7180Sclaudio 	if (event_initialized(&c->script_ev)) {
1183e76e7180Sclaudio 		close(EVENT_FD(&c->script_ev));
1184e76e7180Sclaudio 		event_del(&c->script_ev);
1185e76e7180Sclaudio 	}
1186e76e7180Sclaudio 	if (event_initialized(&c->script_err_ev)) {
1187e76e7180Sclaudio 		close(EVENT_FD(&c->script_err_ev));
1188e76e7180Sclaudio 		event_del(&c->script_err_ev);
1189e76e7180Sclaudio 	}
1190e76e7180Sclaudio 
1191e76e7180Sclaudio 	close(c->fd);
1192e76e7180Sclaudio 	while (!SLIST_EMPTY(&c->env)) {
1193e76e7180Sclaudio 		env_entry = SLIST_FIRST(&c->env);
1194e76e7180Sclaudio 		SLIST_REMOVE_HEAD(&c->env, entry);
1195e76e7180Sclaudio 		free(env_entry->key);
1196e76e7180Sclaudio 		free(env_entry->val);
1197e76e7180Sclaudio 		free(env_entry);
1198e76e7180Sclaudio 	}
1199e76e7180Sclaudio 
1200e76e7180Sclaudio 	while ((resp = TAILQ_FIRST(&c->response_head))) {
1201e76e7180Sclaudio 		TAILQ_REMOVE(&c->response_head, resp, entry);
1202e76e7180Sclaudio 		free(resp);
1203e76e7180Sclaudio 	}
1204e76e7180Sclaudio 	LIST_REMOVE(c, entry);
1205e76e7180Sclaudio 	if (! c->inflight_fds_accounted)
1206e76e7180Sclaudio 		cgi_inflight--;
1207e76e7180Sclaudio 	free(c);
1208e76e7180Sclaudio }
1209e76e7180Sclaudio 
1210e76e7180Sclaudio void
dump_fcgi_record(const char * p,struct fcgi_record_header * h)1211e76e7180Sclaudio dump_fcgi_record(const char *p, struct fcgi_record_header *h)
1212e76e7180Sclaudio {
1213e76e7180Sclaudio 	dump_fcgi_record_header(p, h);
1214e76e7180Sclaudio 
1215e76e7180Sclaudio 	if (h->type == FCGI_BEGIN_REQUEST)
1216e76e7180Sclaudio 		dump_fcgi_begin_request_body(p,
1217e76e7180Sclaudio 		    (struct fcgi_begin_request_body *)(h + 1));
1218e76e7180Sclaudio 	else if (h->type == FCGI_END_REQUEST)
1219e76e7180Sclaudio 		dump_fcgi_end_request_body(p,
1220e76e7180Sclaudio 		    (struct fcgi_end_request_body *)(h + 1));
1221e76e7180Sclaudio }
1222e76e7180Sclaudio 
1223e76e7180Sclaudio void
dump_fcgi_record_header(const char * p,struct fcgi_record_header * h)1224e76e7180Sclaudio dump_fcgi_record_header(const char* p, struct fcgi_record_header *h)
1225e76e7180Sclaudio {
1226e76e7180Sclaudio 	ldebug("%sversion:         %d", p, h->version);
1227e76e7180Sclaudio 	ldebug("%stype:            %d", p, h->type);
1228e76e7180Sclaudio 	ldebug("%srequestId:       %d", p, ntohs(h->id));
1229e76e7180Sclaudio 	ldebug("%scontentLength:   %d", p, ntohs(h->content_len));
1230e76e7180Sclaudio 	ldebug("%spaddingLength:   %d", p, h->padding_len);
1231e76e7180Sclaudio 	ldebug("%sreserved:        %d", p, h->reserved);
1232e76e7180Sclaudio }
1233e76e7180Sclaudio 
1234e76e7180Sclaudio void
dump_fcgi_begin_request_body(const char * p,struct fcgi_begin_request_body * b)1235e76e7180Sclaudio dump_fcgi_begin_request_body(const char *p, struct fcgi_begin_request_body *b)
1236e76e7180Sclaudio {
1237e76e7180Sclaudio 	ldebug("%srole             %d", p, ntohs(b->role));
1238e76e7180Sclaudio 	ldebug("%sflags            %d", p, b->flags);
1239e76e7180Sclaudio }
1240e76e7180Sclaudio 
1241e76e7180Sclaudio void
dump_fcgi_end_request_body(const char * p,struct fcgi_end_request_body * b)1242e76e7180Sclaudio dump_fcgi_end_request_body(const char *p, struct fcgi_end_request_body *b)
1243e76e7180Sclaudio {
1244e76e7180Sclaudio 	ldebug("%sappStatus:       %d", p, ntohl(b->app_status));
1245e76e7180Sclaudio 	ldebug("%sprotocolStatus:  %d", p, b->protocol_status);
1246e76e7180Sclaudio }
1247e76e7180Sclaudio 
1248e76e7180Sclaudio void
syslog_vstrerror(int e,int priority,const char * fmt,va_list ap)1249e76e7180Sclaudio syslog_vstrerror(int e, int priority, const char *fmt, va_list ap)
1250e76e7180Sclaudio {
1251e76e7180Sclaudio 	char *s;
1252e76e7180Sclaudio 
1253e76e7180Sclaudio 	if (vasprintf(&s, fmt, ap) == -1) {
1254e76e7180Sclaudio 		syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror");
1255e76e7180Sclaudio 		exit(1);
1256e76e7180Sclaudio 	}
1257e76e7180Sclaudio 	syslog(priority, "%s: %s", s, strerror(e));
1258e76e7180Sclaudio 	free(s);
1259e76e7180Sclaudio }
1260e76e7180Sclaudio 
1261e76e7180Sclaudio __dead void
syslog_err(int ecode,const char * fmt,...)1262e76e7180Sclaudio syslog_err(int ecode, const char *fmt, ...)
1263e76e7180Sclaudio {
1264e76e7180Sclaudio 	va_list ap;
1265e76e7180Sclaudio 
1266e76e7180Sclaudio 	va_start(ap, fmt);
1267e76e7180Sclaudio 	syslog_vstrerror(errno, LOG_CRIT, fmt, ap);
1268e76e7180Sclaudio 	va_end(ap);
1269e76e7180Sclaudio 	exit(ecode);
1270e76e7180Sclaudio }
1271e76e7180Sclaudio 
1272e76e7180Sclaudio __dead void
syslog_errx(int ecode,const char * fmt,...)1273e76e7180Sclaudio syslog_errx(int ecode, const char *fmt, ...)
1274e76e7180Sclaudio {
1275e76e7180Sclaudio 	va_list ap;
1276e76e7180Sclaudio 
1277e76e7180Sclaudio 	va_start(ap, fmt);
1278e76e7180Sclaudio 	vsyslog(LOG_CRIT, fmt, ap);
1279e76e7180Sclaudio 	va_end(ap);
1280e76e7180Sclaudio 	exit(ecode);
1281e76e7180Sclaudio }
1282e76e7180Sclaudio 
1283e76e7180Sclaudio void
syslog_warn(const char * fmt,...)1284e76e7180Sclaudio syslog_warn(const char *fmt, ...)
1285e76e7180Sclaudio {
1286e76e7180Sclaudio 	va_list ap;
1287e76e7180Sclaudio 
1288e76e7180Sclaudio 	va_start(ap, fmt);
1289e76e7180Sclaudio 	syslog_vstrerror(errno, LOG_ERR, fmt, ap);
1290e76e7180Sclaudio 	va_end(ap);
1291e76e7180Sclaudio }
1292e76e7180Sclaudio 
1293e76e7180Sclaudio void
syslog_warnx(const char * fmt,...)1294e76e7180Sclaudio syslog_warnx(const char *fmt, ...)
1295e76e7180Sclaudio {
1296e76e7180Sclaudio 	va_list ap;
1297e76e7180Sclaudio 
1298e76e7180Sclaudio 	va_start(ap, fmt);
1299e76e7180Sclaudio 	vsyslog(LOG_ERR, fmt, ap);
1300e76e7180Sclaudio 	va_end(ap);
1301e76e7180Sclaudio }
1302e76e7180Sclaudio 
1303e76e7180Sclaudio void
syslog_info(const char * fmt,...)1304e76e7180Sclaudio syslog_info(const char *fmt, ...)
1305e76e7180Sclaudio {
1306e76e7180Sclaudio 	va_list ap;
1307e76e7180Sclaudio 
1308e76e7180Sclaudio 	va_start(ap, fmt);
1309e76e7180Sclaudio 	vsyslog(LOG_INFO, fmt, ap);
1310e76e7180Sclaudio 	va_end(ap);
1311e76e7180Sclaudio }
1312e76e7180Sclaudio 
1313e76e7180Sclaudio void
syslog_debug(const char * fmt,...)1314e76e7180Sclaudio syslog_debug(const char *fmt, ...)
1315e76e7180Sclaudio {
1316e76e7180Sclaudio 	va_list ap;
1317e76e7180Sclaudio 
1318e76e7180Sclaudio 	va_start(ap, fmt);
1319e76e7180Sclaudio 	vsyslog(LOG_DEBUG, fmt, ap);
1320e76e7180Sclaudio 	va_end(ap);
1321e76e7180Sclaudio }
1322