xref: /openbsd/usr.sbin/slowcgi/slowcgi.c (revision 17276fc5)
1*17276fc5Sop /*	$OpenBSD: slowcgi.c,v 1.64 2022/08/07 07:43:53 op Exp $ */
27ac15270Sflorian /*
37ac15270Sflorian  * Copyright (c) 2013 David Gwynne <dlg@openbsd.org>
47ac15270Sflorian  * Copyright (c) 2013 Florian Obser <florian@openbsd.org>
57ac15270Sflorian  *
67ac15270Sflorian  * Permission to use, copy, modify, and distribute this software for any
77ac15270Sflorian  * purpose with or without fee is hereby granted, provided that the above
87ac15270Sflorian  * copyright notice and this permission notice appear in all copies.
97ac15270Sflorian  *
107ac15270Sflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
117ac15270Sflorian  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
127ac15270Sflorian  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
137ac15270Sflorian  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
147ac15270Sflorian  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
157ac15270Sflorian  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
167ac15270Sflorian  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
177ac15270Sflorian  */
187ac15270Sflorian 
197ac15270Sflorian #include <sys/types.h>
207ac15270Sflorian #include <sys/ioctl.h>
217ac15270Sflorian #include <sys/queue.h>
227ac15270Sflorian #include <sys/socket.h>
237ac15270Sflorian #include <sys/stat.h>
24e09b0855Sflorian #include <sys/time.h>
257ac15270Sflorian #include <sys/un.h>
267ac15270Sflorian #include <sys/wait.h>
27e09b0855Sflorian #include <arpa/inet.h>
287ac15270Sflorian #include <err.h>
2994ee156dSflorian #include <fcntl.h>
307ac15270Sflorian #include <errno.h>
317ac15270Sflorian #include <event.h>
32b9fc9a72Sderaadt #include <limits.h>
337ac15270Sflorian #include <pwd.h>
347ac15270Sflorian #include <signal.h>
35e09b0855Sflorian #include <stdarg.h>
367ac15270Sflorian #include <stdio.h>
377ac15270Sflorian #include <stdlib.h>
387ac15270Sflorian #include <string.h>
397ac15270Sflorian #include <syslog.h>
407ac15270Sflorian #include <unistd.h>
417ac15270Sflorian 
427ac15270Sflorian #define TIMEOUT_DEFAULT		 120
43068d93a3Sop #define TIMEOUT_MAX		 (86400 * 365)
447ac15270Sflorian #define SLOWCGI_USER		 "www"
450ee0284dSblambert 
460ee0284dSblambert #define FCGI_CONTENT_SIZE	 65535
470ee0284dSblambert #define FCGI_PADDING_SIZE	 255
480ee0284dSblambert #define FCGI_RECORD_SIZE	 \
490ee0284dSblambert     (sizeof(struct fcgi_record_header) + FCGI_CONTENT_SIZE + FCGI_PADDING_SIZE)
500ee0284dSblambert 
5107240181Sreyk #define FCGI_ALIGNMENT		 8
5207240181Sreyk #define FCGI_ALIGN(n)		 \
5307240181Sreyk     (((n) + (FCGI_ALIGNMENT - 1)) & ~(FCGI_ALIGNMENT - 1))
5407240181Sreyk 
557ac15270Sflorian #define STDOUT_DONE		 1
567ac15270Sflorian #define STDERR_DONE		 2
577ac15270Sflorian #define SCRIPT_DONE		 4
587ac15270Sflorian 
597ac15270Sflorian #define FCGI_BEGIN_REQUEST	 1
607ac15270Sflorian #define FCGI_ABORT_REQUEST	 2
617ac15270Sflorian #define FCGI_END_REQUEST	 3
627ac15270Sflorian #define FCGI_PARAMS		 4
637ac15270Sflorian #define FCGI_STDIN		 5
647ac15270Sflorian #define FCGI_STDOUT		 6
657ac15270Sflorian #define FCGI_STDERR		 7
667ac15270Sflorian #define FCGI_DATA		 8
677ac15270Sflorian #define FCGI_GET_VALUES		 9
687ac15270Sflorian #define FCGI_GET_VALUES_RESULT	10
697ac15270Sflorian #define FCGI_UNKNOWN_TYPE	11
707ac15270Sflorian #define FCGI_MAXTYPE		(FCGI_UNKNOWN_TYPE)
717ac15270Sflorian 
727ac15270Sflorian #define FCGI_REQUEST_COMPLETE	0
737ac15270Sflorian #define FCGI_CANT_MPX_CONN	1
747ac15270Sflorian #define FCGI_OVERLOADED		2
757ac15270Sflorian #define FCGI_UNKNOWN_ROLE	3
767ac15270Sflorian 
773f9f8982Sbenno #define FD_RESERVE		5
783f9f8982Sbenno #define FD_NEEDED		6
79d2b70008Sflorian int cgi_inflight = 0;
807ac15270Sflorian 
817ac15270Sflorian struct listener {
827ac15270Sflorian 	struct event	ev, pause;
837ac15270Sflorian };
847ac15270Sflorian 
857ac15270Sflorian struct env_val {
867ac15270Sflorian 	SLIST_ENTRY(env_val)	 entry;
877ac15270Sflorian 	char			*val;
887ac15270Sflorian };
897ac15270Sflorian SLIST_HEAD(env_head, env_val);
907ac15270Sflorian 
917ac15270Sflorian struct fcgi_record_header {
927ac15270Sflorian 	uint8_t		version;
937ac15270Sflorian 	uint8_t		type;
947ac15270Sflorian 	uint16_t	id;
957ac15270Sflorian 	uint16_t	content_len;
967ac15270Sflorian 	uint8_t		padding_len;
977ac15270Sflorian 	uint8_t		reserved;
987ac15270Sflorian }__packed;
997ac15270Sflorian 
1007ac15270Sflorian struct fcgi_response {
1017ac15270Sflorian 	TAILQ_ENTRY(fcgi_response)	entry;
1020ee0284dSblambert 	uint8_t				data[FCGI_RECORD_SIZE];
1037ac15270Sflorian 	size_t				data_pos;
1047ac15270Sflorian 	size_t				data_len;
1057ac15270Sflorian };
1067ac15270Sflorian TAILQ_HEAD(fcgi_response_head, fcgi_response);
1077ac15270Sflorian 
1087ac15270Sflorian struct fcgi_stdin {
1097ac15270Sflorian 	TAILQ_ENTRY(fcgi_stdin)	entry;
1107ac15270Sflorian 	uint8_t			data[FCGI_RECORD_SIZE];
1117ac15270Sflorian 	size_t			data_pos;
1127ac15270Sflorian 	size_t			data_len;
1137ac15270Sflorian };
1147ac15270Sflorian TAILQ_HEAD(fcgi_stdin_head, fcgi_stdin);
1157ac15270Sflorian 
116c3b13bc8Sblambert struct request {
117f3a4f9e5Sclaudio 	LIST_ENTRY(request)		entry;
1187ac15270Sflorian 	struct event			ev;
1197ac15270Sflorian 	struct event			resp_ev;
1207ac15270Sflorian 	struct event			tmo;
1217ac15270Sflorian 	int				fd;
1227ac15270Sflorian 	uint8_t				buf[FCGI_RECORD_SIZE];
1237ac15270Sflorian 	size_t				buf_pos;
1247ac15270Sflorian 	size_t				buf_len;
1257ac15270Sflorian 	struct fcgi_response_head	response_head;
1267ac15270Sflorian 	struct fcgi_stdin_head		stdin_head;
1277ac15270Sflorian 	uint16_t			id;
128b9fc9a72Sderaadt 	char				script_name[PATH_MAX];
1297ac15270Sflorian 	struct env_head			env;
1307ac15270Sflorian 	int				env_count;
1317ac15270Sflorian 	pid_t				script_pid;
1327ac15270Sflorian 	int				script_status;
1337ac15270Sflorian 	struct event			script_ev;
1347ac15270Sflorian 	struct event			script_err_ev;
1357ac15270Sflorian 	struct event			script_stdin_ev;
1366bdc2311Sflorian 	int				stdin_fd_closed;
1376bdc2311Sflorian 	int				stdout_fd_closed;
1386bdc2311Sflorian 	int				stderr_fd_closed;
1397ac15270Sflorian 	uint8_t				script_flags;
1407ac15270Sflorian 	uint8_t				request_started;
1413f9f8982Sbenno 	int				inflight_fds_accounted;
1427ac15270Sflorian };
1437ac15270Sflorian 
144f3a4f9e5Sclaudio LIST_HEAD(requests_head, request);
1457ac15270Sflorian 
1467ac15270Sflorian struct slowcgi_proc {
147c3b13bc8Sblambert 	struct requests_head	requests;
1487ac15270Sflorian 	struct event		ev_sigchld;
1497ac15270Sflorian };
1507ac15270Sflorian 
1517ac15270Sflorian struct fcgi_begin_request_body {
1527ac15270Sflorian 	uint16_t	role;
1537ac15270Sflorian 	uint8_t		flags;
1547ac15270Sflorian 	uint8_t		reserved[5];
1557ac15270Sflorian }__packed;
1567ac15270Sflorian 
15703e61f17Sblambert struct fcgi_end_request_body {
1587ac15270Sflorian 	uint32_t	app_status;
1597ac15270Sflorian 	uint8_t		protocol_status;
1607ac15270Sflorian 	uint8_t		reserved[3];
1617ac15270Sflorian }__packed;
16203e61f17Sblambert 
1637ac15270Sflorian __dead void	usage(void);
1640897de71Sflorian int		slowcgi_listen(char *, struct passwd *);
1657ac15270Sflorian void		slowcgi_paused(int, short, void *);
166f178db20Smillert int		accept_reserve(int, struct sockaddr *, socklen_t *, int, int *);
1677ac15270Sflorian void		slowcgi_accept(int, short, void *);
1687ac15270Sflorian void		slowcgi_request(int, short, void *);
1697ac15270Sflorian void		slowcgi_response(int, short, void *);
1704404b4d5Sflorian void		slowcgi_add_response(struct request *, struct fcgi_response *);
1717ac15270Sflorian void		slowcgi_timeout(int, short, void *);
1727ac15270Sflorian void		slowcgi_sig_handler(int, short, void *);
173c3b13bc8Sblambert size_t		parse_record(uint8_t * , size_t, struct request *);
174c3b13bc8Sblambert void		parse_begin_request(uint8_t *, uint16_t, struct request *,
1757ac15270Sflorian 		    uint16_t);
176c3b13bc8Sblambert void		parse_params(uint8_t *, uint16_t, struct request *, uint16_t);
177c3b13bc8Sblambert void		parse_stdin(uint8_t *, uint16_t, struct request *, uint16_t);
178c3b13bc8Sblambert void		exec_cgi(struct request *);
179c3b13bc8Sblambert void		script_in(int, struct event *, struct request *, uint8_t);
1807ac15270Sflorian void		script_std_in(int, short, void *);
1817ac15270Sflorian void		script_err_in(int, short, void *);
1827ac15270Sflorian void		script_out(int, short, void *);
183c3b13bc8Sblambert void		create_end_record(struct request *);
18403e61f17Sblambert void		dump_fcgi_record(const char *,
18503e61f17Sblambert 		    struct fcgi_record_header *);
1867ac15270Sflorian void		dump_fcgi_record_header(const char *,
1877ac15270Sflorian 		    struct fcgi_record_header *);
18803e61f17Sblambert void		dump_fcgi_begin_request_body(const char *,
18903e61f17Sblambert 		    struct fcgi_begin_request_body *);
19003e61f17Sblambert void		dump_fcgi_end_request_body(const char *,
19103e61f17Sblambert 		    struct fcgi_end_request_body *);
192c3b13bc8Sblambert void		cleanup_request(struct request *);
19303e61f17Sblambert 
1947ac15270Sflorian struct loggers {
195553d5caeSflorian 	__dead void (*err)(int, const char *, ...)
196553d5caeSflorian 	    __attribute__((__format__ (printf, 2, 3)));
197553d5caeSflorian 	__dead void (*errx)(int, const char *, ...)
198553d5caeSflorian 	    __attribute__((__format__ (printf, 2, 3)));
199553d5caeSflorian 	void (*warn)(const char *, ...)
200553d5caeSflorian 	    __attribute__((__format__ (printf, 1, 2)));
201553d5caeSflorian 	void (*warnx)(const char *, ...)
202553d5caeSflorian 	    __attribute__((__format__ (printf, 1, 2)));
203553d5caeSflorian 	void (*info)(const char *, ...)
204553d5caeSflorian 	    __attribute__((__format__ (printf, 1, 2)));
205553d5caeSflorian 	void (*debug)(const char *, ...)
206553d5caeSflorian 	    __attribute__((__format__ (printf, 1, 2)));
2077ac15270Sflorian };
2087ac15270Sflorian 
2097ac15270Sflorian const struct loggers conslogger = {
2107ac15270Sflorian 	err,
2117ac15270Sflorian 	errx,
2127ac15270Sflorian 	warn,
2137ac15270Sflorian 	warnx,
2147ac15270Sflorian 	warnx, /* info */
2157ac15270Sflorian 	warnx /* debug */
2167ac15270Sflorian };
2177ac15270Sflorian 
218553d5caeSflorian __dead void	syslog_err(int, const char *, ...)
219553d5caeSflorian 		    __attribute__((__format__ (printf, 2, 3)));
220553d5caeSflorian __dead void	syslog_errx(int, const char *, ...)
221553d5caeSflorian 		    __attribute__((__format__ (printf, 2, 3)));
222553d5caeSflorian void		syslog_warn(const char *, ...)
223553d5caeSflorian 		    __attribute__((__format__ (printf, 1, 2)));
224553d5caeSflorian void		syslog_warnx(const char *, ...)
225553d5caeSflorian 		    __attribute__((__format__ (printf, 1, 2)));
226553d5caeSflorian void		syslog_info(const char *, ...)
227553d5caeSflorian 		    __attribute__((__format__ (printf, 1, 2)));
228553d5caeSflorian void		syslog_debug(const char *, ...)
229553d5caeSflorian 		    __attribute__((__format__ (printf, 1, 2)));
230553d5caeSflorian void		syslog_vstrerror(int, int, const char *, va_list)
231553d5caeSflorian 		    __attribute__((__format__ (printf, 3, 0)));
2327ac15270Sflorian 
2337ac15270Sflorian const struct loggers syslogger = {
2347ac15270Sflorian 	syslog_err,
2357ac15270Sflorian 	syslog_errx,
2367ac15270Sflorian 	syslog_warn,
2377ac15270Sflorian 	syslog_warnx,
2387ac15270Sflorian 	syslog_info,
2397ac15270Sflorian 	syslog_debug
2407ac15270Sflorian };
2417ac15270Sflorian 
2427ac15270Sflorian const struct loggers *logger = &conslogger;
2437ac15270Sflorian 
2447ac15270Sflorian #define lerr(_e, _f...) logger->err((_e), _f)
2457ac15270Sflorian #define lerrx(_e, _f...) logger->errx((_e), _f)
2467ac15270Sflorian #define lwarn(_f...) logger->warn(_f)
2477ac15270Sflorian #define lwarnx(_f...) logger->warnx(_f)
2487ac15270Sflorian #define linfo(_f...) logger->info(_f)
2497ac15270Sflorian #define ldebug(_f...) logger->debug(_f)
2507ac15270Sflorian 
2517ac15270Sflorian __dead void
usage(void)2527ac15270Sflorian usage(void)
2537ac15270Sflorian {
2547ac15270Sflorian 	extern char *__progname;
2558f6f6407Sflorian 	fprintf(stderr,
256068d93a3Sop 	    "usage: %s [-dv] [-p path] [-s socket] [-t timeout] [-U user] "
257068d93a3Sop 	    "[-u user]\n", __progname);
2587ac15270Sflorian 	exit(1);
2597ac15270Sflorian }
2607ac15270Sflorian 
2617ac15270Sflorian struct timeval		timeout = { TIMEOUT_DEFAULT, 0 };
2627ac15270Sflorian struct slowcgi_proc	slowcgi_proc;
2637ac15270Sflorian int			debug = 0;
26404ab3ac4Sflorian int			verbose = 0;
2657ac15270Sflorian int			on = 1;
2660b5ee19eSblambert char			*fcgi_socket = "/var/www/run/slowcgi.sock";
2677ac15270Sflorian 
2687ac15270Sflorian int
main(int argc,char * argv[])2697ac15270Sflorian main(int argc, char *argv[])
2707ac15270Sflorian {
27106619349Sflorian 	extern char *__progname;
2720897de71Sflorian 	struct listener	*l = NULL;
2737ac15270Sflorian 	struct passwd	*pw;
274f4a8b980Sflorian 	struct stat	 sb;
275f4a8b980Sflorian 	int		 c, fd;
276a3b046bcSflorian 	const char	*chrootpath = NULL;
2778f6f6407Sflorian 	const char	*sock_user = SLOWCGI_USER;
278a3b046bcSflorian 	const char	*slowcgi_user = SLOWCGI_USER;
279068d93a3Sop 	const char	*errstr;
280f4a8b980Sflorian 
281f4a8b980Sflorian 	/*
282f4a8b980Sflorian 	 * Ensure we have fds 0-2 open so that we have no fd overlaps
283f4a8b980Sflorian 	 * in exec_cgi() later. Just exit on error, we don't have enough
284f4a8b980Sflorian 	 * fds open to output an error message anywhere.
285f4a8b980Sflorian 	 */
286f4a8b980Sflorian 	for (c=0; c < 3; c++) {
287f4a8b980Sflorian 		if (fstat(c, &sb) == -1) {
288f4a8b980Sflorian 			if ((fd = open("/dev/null", O_RDWR)) != -1) {
289f4a8b980Sflorian 				if (dup2(fd, c) == -1)
290f4a8b980Sflorian 					exit(1);
291f4a8b980Sflorian 				if (fd > c)
292f4a8b980Sflorian 					close(fd);
293f4a8b980Sflorian 			} else
294f4a8b980Sflorian 				exit(1);
295f4a8b980Sflorian 		}
296f4a8b980Sflorian 	}
2977ac15270Sflorian 
298068d93a3Sop 	while ((c = getopt(argc, argv, "dp:s:t:U:u:v")) != -1) {
2997ac15270Sflorian 		switch (c) {
3007ac15270Sflorian 		case 'd':
30147939e88Sclaudio 			debug++;
3027ac15270Sflorian 			break;
303a3b046bcSflorian 		case 'p':
304a3b046bcSflorian 			chrootpath = optarg;
305a3b046bcSflorian 			break;
3060b5ee19eSblambert 		case 's':
3070b5ee19eSblambert 			fcgi_socket = optarg;
3080b5ee19eSblambert 			break;
309068d93a3Sop 		case 't':
310068d93a3Sop 			timeout.tv_sec = strtonum(optarg, 1, TIMEOUT_MAX,
311068d93a3Sop 			    &errstr);
312068d93a3Sop 			if (errstr != NULL)
313068d93a3Sop 				errx(1, "timeout is %s: %s", errstr, optarg);
314068d93a3Sop 			break;
3158f6f6407Sflorian 		case 'U':
3168f6f6407Sflorian 			sock_user = optarg;
3178f6f6407Sflorian 			break;
318a3b046bcSflorian 		case 'u':
319a3b046bcSflorian 			slowcgi_user = optarg;
320a3b046bcSflorian 			break;
32104ab3ac4Sflorian 		case 'v':
32204ab3ac4Sflorian 			verbose++;
32304ab3ac4Sflorian 			break;
3247ac15270Sflorian 		default:
3257ac15270Sflorian 			usage();
3267ac15270Sflorian 			/* NOTREACHED */
3277ac15270Sflorian 		}
3287ac15270Sflorian 	}
3297ac15270Sflorian 
3307ac15270Sflorian 	if (geteuid() != 0)
3317ac15270Sflorian 		errx(1, "need root privileges");
3327ac15270Sflorian 
33383926339Sflorian 	if (!debug && daemon(0, 0) == -1)
3347ac15270Sflorian 		err(1, "daemon");
3357ac15270Sflorian 
33606619349Sflorian 	if (!debug) {
33706619349Sflorian 		openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON);
33806619349Sflorian 		logger = &syslogger;
33906619349Sflorian 	}
34006619349Sflorian 
3418f6f6407Sflorian 	ldebug("sock_user: %s", sock_user);
3428f6f6407Sflorian 	pw = getpwnam(sock_user);
343a3b046bcSflorian 	if (pw == NULL)
3448f6f6407Sflorian 		lerrx(1, "no %s user", sock_user);
345a3b046bcSflorian 
3460897de71Sflorian 	fd = slowcgi_listen(fcgi_socket, pw);
3470b5ee19eSblambert 
3488f6f6407Sflorian 	ldebug("slowcgi_user: %s", slowcgi_user);
349a3b046bcSflorian 	pw = getpwnam(slowcgi_user);
350a3b046bcSflorian 	if (pw == NULL)
3516af1d02fSclaudio 		lerrx(1, "no %s user", slowcgi_user);
352a3b046bcSflorian 
353a3b046bcSflorian 	if (chrootpath == NULL)
354a3b046bcSflorian 		chrootpath = pw->pw_dir;
355a3b046bcSflorian 
356a3b046bcSflorian 	if (chroot(chrootpath) == -1)
357a3b046bcSflorian 		lerr(1, "chroot(%s)", chrootpath);
358a3b046bcSflorian 
359a3b046bcSflorian 	ldebug("chroot: %s", chrootpath);
3607ac15270Sflorian 
3617ac15270Sflorian 	if (chdir("/") == -1)
362a3b046bcSflorian 		lerr(1, "chdir(/)");
363bde99fd8Sblambert 
3647ac15270Sflorian 	if (setgroups(1, &pw->pw_gid) ||
3657ac15270Sflorian 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
3667ac15270Sflorian 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
3677ac15270Sflorian 		lerr(1, "unable to revoke privs");
3687ac15270Sflorian 
3691bfdb260Sflorian 	if (pledge("stdio rpath unix proc exec", NULL) == -1)
3701bfdb260Sflorian 		lerr(1, "pledge");
3711bfdb260Sflorian 
372f3a4f9e5Sclaudio 	LIST_INIT(&slowcgi_proc.requests);
3730897de71Sflorian 	event_init();
3740897de71Sflorian 
3750897de71Sflorian 	l = calloc(1, sizeof(*l));
3760897de71Sflorian 	if (l == NULL)
3770897de71Sflorian 		lerr(1, "listener ev alloc");
3780897de71Sflorian 
3790897de71Sflorian 	event_set(&l->ev, fd, EV_READ | EV_PERSIST, slowcgi_accept, l);
3800897de71Sflorian 	event_add(&l->ev, NULL);
3810897de71Sflorian 	evtimer_set(&l->pause, slowcgi_paused, l);
3827ac15270Sflorian 
3837ac15270Sflorian 	signal_set(&slowcgi_proc.ev_sigchld, SIGCHLD, slowcgi_sig_handler,
3847ac15270Sflorian 	    &slowcgi_proc);
385f94ba4a2Sclaudio 	signal(SIGPIPE, SIG_IGN);
3867ac15270Sflorian 
3877ac15270Sflorian 	signal_add(&slowcgi_proc.ev_sigchld, NULL);
3887ac15270Sflorian 
3897ac15270Sflorian 	event_dispatch();
3907ac15270Sflorian 	return (0);
3917ac15270Sflorian }
3920897de71Sflorian 
3930897de71Sflorian int
slowcgi_listen(char * path,struct passwd * pw)394f6474c66Sflorian slowcgi_listen(char *path, struct passwd *pw)
3957ac15270Sflorian {
3967ac15270Sflorian 	struct sockaddr_un	 sun;
397d0c661abSflorian 	mode_t			 old_umask;
3987ac15270Sflorian 	int			 fd;
3997ac15270Sflorian 
400d253f95aSflorian 	if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
401d253f95aSflorian 	    0)) == -1)
4027ac15270Sflorian 		lerr(1, "slowcgi_listen: socket");
4037ac15270Sflorian 
4047ac15270Sflorian 	bzero(&sun, sizeof(sun));
4057ac15270Sflorian 	sun.sun_family = AF_UNIX;
406cdf4d38dSguenther 	if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
407cdf4d38dSguenther 	    sizeof(sun.sun_path))
408d444d2f2Stb 		lerrx(1, "socket path too long");
4097ac15270Sflorian 
4107ac15270Sflorian 	if (unlink(path) == -1)
4117ac15270Sflorian 		if (errno != ENOENT)
4127ac15270Sflorian 			lerr(1, "slowcgi_listen: unlink %s", path);
4137ac15270Sflorian 
41475cde9b6Sclaudio 	old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
4157ac15270Sflorian 
4167ac15270Sflorian 	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
4177ac15270Sflorian 		lerr(1,"slowcgi_listen: bind: %s", path);
4187ac15270Sflorian 
4197ac15270Sflorian 	umask(old_umask);
4207ac15270Sflorian 
421f6474c66Sflorian 	if (chown(path, pw->pw_uid, pw->pw_gid) == -1)
4227ac15270Sflorian 		lerr(1, "slowcgi_listen: chown: %s", path);
4237ac15270Sflorian 
4247ac15270Sflorian 	if (listen(fd, 5) == -1)
4257ac15270Sflorian 		lerr(1, "listen");
4267ac15270Sflorian 
427bde99fd8Sblambert 	ldebug("socket: %s", path);
4280897de71Sflorian 	return fd;
4297ac15270Sflorian }
4307ac15270Sflorian 
4317ac15270Sflorian void
slowcgi_paused(int fd,short events,void * arg)4327ac15270Sflorian slowcgi_paused(int fd, short events, void *arg)
4337ac15270Sflorian {
4347ac15270Sflorian 	struct listener	*l = arg;
4357ac15270Sflorian 	event_add(&l->ev, NULL);
4367ac15270Sflorian }
4377ac15270Sflorian 
4383f9f8982Sbenno int
accept_reserve(int sockfd,struct sockaddr * addr,socklen_t * addrlen,int reserve,int * counter)4393f9f8982Sbenno accept_reserve(int sockfd, struct sockaddr *addr, socklen_t *addrlen,
440f178db20Smillert     int reserve, int *counter)
4413f9f8982Sbenno {
4423f9f8982Sbenno 	int ret;
4433f9f8982Sbenno 	if (getdtablecount() + reserve +
44473e38a72Sclaudio 	    ((*counter + 1) * FD_NEEDED) >= getdtablesize()) {
445f8701677Sderaadt 		ldebug("inflight fds exceeded");
4463f9f8982Sbenno 		errno = EMFILE;
4473f9f8982Sbenno 		return -1;
4483f9f8982Sbenno 	}
4493f9f8982Sbenno 
450d253f95aSflorian 	if ((ret = accept4(sockfd, addr, addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC))
451d253f95aSflorian 	    > -1) {
4523f9f8982Sbenno 		(*counter)++;
4533f9f8982Sbenno 		ldebug("inflight incremented, now %d", *counter);
4543f9f8982Sbenno 	}
4553f9f8982Sbenno 	return ret;
4563f9f8982Sbenno }
4573f9f8982Sbenno 
4587ac15270Sflorian void
slowcgi_accept(int fd,short events,void * arg)4597ac15270Sflorian slowcgi_accept(int fd, short events, void *arg)
4607ac15270Sflorian {
4617ac15270Sflorian 	struct listener		*l;
4627ac15270Sflorian 	struct sockaddr_storage	 ss;
463adf2d9e3Sflorian 	struct timeval		 backoff;
464c3b13bc8Sblambert 	struct request		*c;
4657ac15270Sflorian 	socklen_t		 len;
4667ac15270Sflorian 	int			 s;
4677ac15270Sflorian 
4687ac15270Sflorian 	l = arg;
469adf2d9e3Sflorian 	backoff.tv_sec = 1;
470adf2d9e3Sflorian 	backoff.tv_usec = 0;
4717ac15270Sflorian 	c = NULL;
4727ac15270Sflorian 
4737ac15270Sflorian 	len = sizeof(ss);
4743f9f8982Sbenno 	if ((s = accept_reserve(fd, (struct sockaddr *)&ss,
4753f9f8982Sbenno 	    &len, FD_RESERVE, &cgi_inflight)) == -1) {
4767ac15270Sflorian 		switch (errno) {
4777ac15270Sflorian 		case EINTR:
4787ac15270Sflorian 		case EWOULDBLOCK:
4797ac15270Sflorian 		case ECONNABORTED:
4807ac15270Sflorian 			return;
4817ac15270Sflorian 		case EMFILE:
4827ac15270Sflorian 		case ENFILE:
4837ac15270Sflorian 			event_del(&l->ev);
484adf2d9e3Sflorian 			evtimer_add(&l->pause, &backoff);
4857ac15270Sflorian 			return;
4867ac15270Sflorian 		default:
4877ac15270Sflorian 			lerr(1, "accept");
4887ac15270Sflorian 		}
4897ac15270Sflorian 	}
4907ac15270Sflorian 
4917ac15270Sflorian 	c = calloc(1, sizeof(*c));
4927ac15270Sflorian 	if (c == NULL) {
493c3b13bc8Sblambert 		lwarn("cannot calloc request");
4947ac15270Sflorian 		close(s);
4953f9f8982Sbenno 		cgi_inflight--;
4967ac15270Sflorian 		return;
4977ac15270Sflorian 	}
4987ac15270Sflorian 	c->fd = s;
4997ac15270Sflorian 	c->buf_pos = 0;
5007ac15270Sflorian 	c->buf_len = 0;
5017ac15270Sflorian 	c->request_started = 0;
5026bdc2311Sflorian 	c->stdin_fd_closed = c->stdout_fd_closed = c->stderr_fd_closed = 0;
5033f9f8982Sbenno 	c->inflight_fds_accounted = 0;
5047ac15270Sflorian 	TAILQ_INIT(&c->response_head);
5057ac15270Sflorian 	TAILQ_INIT(&c->stdin_head);
5067ac15270Sflorian 
5077ac15270Sflorian 	event_set(&c->ev, s, EV_READ | EV_PERSIST, slowcgi_request, c);
5087ac15270Sflorian 	event_add(&c->ev, NULL);
5094404b4d5Sflorian 	event_set(&c->resp_ev, s, EV_WRITE | EV_PERSIST, slowcgi_response, c);
510bf6ece35Sflorian 	evtimer_set(&c->tmo, slowcgi_timeout, c);
511bf6ece35Sflorian 	evtimer_add(&c->tmo, &timeout);
512f3a4f9e5Sclaudio 	LIST_INSERT_HEAD(&slowcgi_proc.requests, c, entry);
5137ac15270Sflorian }
5147ac15270Sflorian 
5157ac15270Sflorian void
slowcgi_timeout(int fd,short events,void * arg)5167ac15270Sflorian slowcgi_timeout(int fd, short events, void *arg)
5177ac15270Sflorian {
518c3b13bc8Sblambert 	cleanup_request((struct request*) arg);
5197ac15270Sflorian }
5207ac15270Sflorian 
5217ac15270Sflorian void
slowcgi_sig_handler(int sig,short event,void * arg)5227ac15270Sflorian slowcgi_sig_handler(int sig, short event, void *arg)
5237ac15270Sflorian {
524c3b13bc8Sblambert 	struct request		*c;
5257ac15270Sflorian 	struct slowcgi_proc	*p;
5267ac15270Sflorian 	pid_t			 pid;
5277ac15270Sflorian 	int			 status;
5287ac15270Sflorian 
5297ac15270Sflorian 	p = arg;
5307ac15270Sflorian 
5317ac15270Sflorian 	switch (sig) {
5327ac15270Sflorian 	case SIGCHLD:
5331878c91fSdjm 		while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
534f3a4f9e5Sclaudio 			LIST_FOREACH(c, &p->requests, entry)
535f3a4f9e5Sclaudio 				if (c->script_pid == pid)
5367ac15270Sflorian 					break;
537bde99fd8Sblambert 			if (c == NULL) {
538bde99fd8Sblambert 				lwarnx("caught exit of unknown child %i", pid);
539e0571d83Sflorian 				continue;
540bde99fd8Sblambert 			}
541bde99fd8Sblambert 
542bde99fd8Sblambert 			if (WIFSIGNALED(status))
543bde99fd8Sblambert 				c->script_status = WTERMSIG(status);
544bde99fd8Sblambert 			else
5457ac15270Sflorian 				c->script_status = WEXITSTATUS(status);
546bde99fd8Sblambert 
5477ac15270Sflorian 			if (c->script_flags == (STDOUT_DONE | STDERR_DONE))
548c3b13bc8Sblambert 				create_end_record(c);
5497ac15270Sflorian 			c->script_flags |= SCRIPT_DONE;
550bde99fd8Sblambert 
551bde99fd8Sblambert 			ldebug("wait: %s", c->script_name);
552e0571d83Sflorian 		}
5531878c91fSdjm 		if (pid == -1 && errno != ECHILD)
5541878c91fSdjm 			lwarn("waitpid");
555bde99fd8Sblambert 		break;
5567ac15270Sflorian 	default:
5577ac15270Sflorian 		lerr(1, "unexpected signal: %d", sig);
558f8701677Sderaadt 		break;
5597ac15270Sflorian 	}
5607ac15270Sflorian }
5617ac15270Sflorian 
5627ac15270Sflorian void
slowcgi_add_response(struct request * c,struct fcgi_response * resp)5634404b4d5Sflorian slowcgi_add_response(struct request *c, struct fcgi_response *resp)
5644404b4d5Sflorian {
56507240181Sreyk 	struct fcgi_record_header	*header;
56607240181Sreyk 	size_t				 padded_len;
56707240181Sreyk 
56807240181Sreyk 	header = (struct fcgi_record_header*)resp->data;
56907240181Sreyk 
57007240181Sreyk 	/* The FastCGI spec suggests to align the output buffer */
57107240181Sreyk 	padded_len = FCGI_ALIGN(resp->data_len);
57207240181Sreyk 	if (padded_len > resp->data_len) {
57307240181Sreyk 		/* There should always be FCGI_PADDING_SIZE bytes left */
57407240181Sreyk 		if (padded_len > FCGI_RECORD_SIZE)
57507240181Sreyk 			lerr(1, "response too long");
57607240181Sreyk 		header->padding_len = padded_len - resp->data_len;
57707240181Sreyk 		resp->data_len = padded_len;
57807240181Sreyk 	}
57907240181Sreyk 
5804404b4d5Sflorian 	TAILQ_INSERT_TAIL(&c->response_head, resp, entry);
5814404b4d5Sflorian 	event_add(&c->resp_ev, NULL);
5824404b4d5Sflorian }
5834404b4d5Sflorian 
5844404b4d5Sflorian void
slowcgi_response(int fd,short events,void * arg)5857ac15270Sflorian slowcgi_response(int fd, short events, void *arg)
5867ac15270Sflorian {
587c3b13bc8Sblambert 	struct request			*c;
5887ac15270Sflorian 	struct fcgi_record_header	*header;
5897ac15270Sflorian 	struct fcgi_response		*resp;
5907ac15270Sflorian 	ssize_t				 n;
5917ac15270Sflorian 
5927ac15270Sflorian 	c = arg;
5937ac15270Sflorian 
5947ac15270Sflorian 	while ((resp = TAILQ_FIRST(&c->response_head))) {
5957ac15270Sflorian 		header = (struct fcgi_record_header*) resp->data;
59647939e88Sclaudio 		if (debug > 1)
59703e61f17Sblambert 			dump_fcgi_record("resp ", header);
5987ac15270Sflorian 
5997ac15270Sflorian 		n = write(fd, resp->data + resp->data_pos, resp->data_len);
6007ac15270Sflorian 		if (n == -1) {
60114379226Sflorian 			if (errno == EAGAIN || errno == EINTR)
6027ac15270Sflorian 				return;
603c3b13bc8Sblambert 			cleanup_request(c);
6047ac15270Sflorian 			return;
6057ac15270Sflorian 		}
6067ac15270Sflorian 		resp->data_pos += n;
6077ac15270Sflorian 		resp->data_len -= n;
6087ac15270Sflorian 		if (resp->data_len == 0) {
6097ac15270Sflorian 			TAILQ_REMOVE(&c->response_head, resp, entry);
6107ac15270Sflorian 			free(resp);
6117ac15270Sflorian 		}
6127ac15270Sflorian 	}
6137ac15270Sflorian 
6147ac15270Sflorian 	if (TAILQ_EMPTY(&c->response_head)) {
6157ac15270Sflorian 		if (c->script_flags == (STDOUT_DONE | STDERR_DONE |
6167ac15270Sflorian 		    SCRIPT_DONE))
617c3b13bc8Sblambert 			cleanup_request(c);
6187ac15270Sflorian 		else
6197ac15270Sflorian 			event_del(&c->resp_ev);
6207ac15270Sflorian 	}
6217ac15270Sflorian }
6227ac15270Sflorian 
6237ac15270Sflorian void
slowcgi_request(int fd,short events,void * arg)6247ac15270Sflorian slowcgi_request(int fd, short events, void *arg)
6257ac15270Sflorian {
626c3b13bc8Sblambert 	struct request	*c;
627bcf12a6bSblambert 	ssize_t		 n;
628bcf12a6bSblambert 	size_t		 parsed;
6297ac15270Sflorian 
6307ac15270Sflorian 	c = arg;
6317ac15270Sflorian 
6327ac15270Sflorian 	n = read(fd, c->buf + c->buf_pos + c->buf_len,
6337ac15270Sflorian 	    FCGI_RECORD_SIZE - c->buf_pos-c->buf_len);
6347ac15270Sflorian 
6357ac15270Sflorian 	switch (n) {
6367ac15270Sflorian 	case -1:
6377ac15270Sflorian 		switch (errno) {
6387ac15270Sflorian 		case EINTR:
6397ac15270Sflorian 		case EAGAIN:
6407ac15270Sflorian 			return;
6417ac15270Sflorian 		default:
6427ac15270Sflorian 			goto fail;
6437ac15270Sflorian 		}
6447ac15270Sflorian 		break;
6457ac15270Sflorian 
6467ac15270Sflorian 	case 0:
6477ac15270Sflorian 		ldebug("closed connection");
6487ac15270Sflorian 		goto fail;
6497ac15270Sflorian 	default:
6507ac15270Sflorian 		break;
6517ac15270Sflorian 	}
6527ac15270Sflorian 
6537ac15270Sflorian 	c->buf_len += n;
6547ac15270Sflorian 
65502ad6491Sblambert 	/*
65602ad6491Sblambert 	 * Parse the records as they are received. Per the FastCGI
65702ad6491Sblambert 	 * specification, the server need only receive the FastCGI
65802ad6491Sblambert 	 * parameter records in full; it is free to begin execution
65902ad6491Sblambert 	 * at that point, which is what happens here.
66002ad6491Sblambert 	 */
6617ac15270Sflorian 	do {
662c3b13bc8Sblambert 		parsed = parse_record(c->buf + c->buf_pos, c->buf_len, c);
6637ac15270Sflorian 		c->buf_pos += parsed;
6647ac15270Sflorian 		c->buf_len -= parsed;
6657ac15270Sflorian 	} while (parsed > 0 && c->buf_len > 0);
6667ac15270Sflorian 
667965b2218Sblambert 	/* Make space for further reads */
6687ac15270Sflorian 	if (c->buf_len > 0) {
6697ac15270Sflorian 		bcopy(c->buf + c->buf_pos, c->buf, c->buf_len);
6707ac15270Sflorian 		c->buf_pos = 0;
6717ac15270Sflorian 	}
6727ac15270Sflorian 	return;
6737ac15270Sflorian fail:
674c3b13bc8Sblambert 	cleanup_request(c);
6757ac15270Sflorian }
6767ac15270Sflorian 
6777ac15270Sflorian void
parse_begin_request(uint8_t * buf,uint16_t n,struct request * c,uint16_t id)678c3b13bc8Sblambert parse_begin_request(uint8_t *buf, uint16_t n, struct request *c, uint16_t id)
6797ac15270Sflorian {
680965b2218Sblambert 	/* XXX -- FCGI_CANT_MPX_CONN */
6817ac15270Sflorian 	if (c->request_started) {
6827ac15270Sflorian 		lwarnx("unexpected FCGI_BEGIN_REQUEST, ignoring");
6837ac15270Sflorian 		return;
6847ac15270Sflorian 	}
6857ac15270Sflorian 
6867ac15270Sflorian 	if (n != sizeof(struct fcgi_begin_request_body)) {
687553d5caeSflorian 		lwarnx("wrong size %d != %lu", n,
6887ac15270Sflorian 		    sizeof(struct fcgi_begin_request_body));
6897ac15270Sflorian 		return;
6907ac15270Sflorian 	}
6917ac15270Sflorian 
6927ac15270Sflorian 	c->request_started = 1;
6937ac15270Sflorian 
6947ac15270Sflorian 	c->id = id;
6957ac15270Sflorian 	SLIST_INIT(&c->env);
6967ac15270Sflorian 	c->env_count = 0;
6977ac15270Sflorian }
69875cde9b6Sclaudio 
6997ac15270Sflorian void
parse_params(uint8_t * buf,uint16_t n,struct request * c,uint16_t id)700c3b13bc8Sblambert parse_params(uint8_t *buf, uint16_t n, struct request *c, uint16_t id)
7017ac15270Sflorian {
7027ac15270Sflorian 	struct env_val			*env_entry;
7037ac15270Sflorian 	uint32_t			 name_len, val_len;
7047ac15270Sflorian 
7057ac15270Sflorian 	if (!c->request_started) {
7067ac15270Sflorian 		lwarnx("FCGI_PARAMS without FCGI_BEGIN_REQUEST, ignoring");
7077ac15270Sflorian 		return;
7087ac15270Sflorian 	}
7097ac15270Sflorian 
7107ac15270Sflorian 	if (c->id != id) {
7117ac15270Sflorian 		lwarnx("unexpected id, ignoring");
7127ac15270Sflorian 		return;
7137ac15270Sflorian 	}
7147ac15270Sflorian 
71502ad6491Sblambert 	/*
71602ad6491Sblambert 	 * If this is the last FastCGI parameter record,
71702ad6491Sblambert 	 * begin execution of the CGI script.
71802ad6491Sblambert 	 */
7197ac15270Sflorian 	if (n == 0) {
7207ac15270Sflorian 		exec_cgi(c);
7217ac15270Sflorian 		return;
7227ac15270Sflorian 	}
72302ad6491Sblambert 
7247ac15270Sflorian 	while (n > 0) {
7257ac15270Sflorian 		if (buf[0] >> 7 == 0) {
7267ac15270Sflorian 			name_len = buf[0];
7277ac15270Sflorian 			n--;
7287ac15270Sflorian 			buf++;
7297ac15270Sflorian 		} else {
7307ac15270Sflorian 			if (n > 3) {
73110fd764fSflorian 				name_len = ((buf[0] & 0x7f) << 24) +
73210fd764fSflorian 				    (buf[1] << 16) + (buf[2] << 8) + buf[3];
7337ac15270Sflorian 				n -= 4;
7347ac15270Sflorian 				buf += 4;
7357ac15270Sflorian 			} else
7367ac15270Sflorian 				return;
7377ac15270Sflorian 		}
7387ac15270Sflorian 
7397ac15270Sflorian 		if (n > 0) {
7407ac15270Sflorian 			if (buf[0] >> 7 == 0) {
7417ac15270Sflorian 				val_len = buf[0];
7427ac15270Sflorian 				n--;
7437ac15270Sflorian 				buf++;
7447ac15270Sflorian 			} else {
7457ac15270Sflorian 				if (n > 3) {
74610fd764fSflorian 					val_len = ((buf[0] & 0x7f) << 24) +
74710fd764fSflorian 					    (buf[1] << 16) + (buf[2] << 8) +
74810fd764fSflorian 					     buf[3];
7497ac15270Sflorian 					n -= 4;
7507ac15270Sflorian 					buf += 4;
7517ac15270Sflorian 				} else
7527ac15270Sflorian 					return;
7537ac15270Sflorian 			}
754f58bfe4fSflorian 		} else
755f58bfe4fSflorian 			return;
756f58bfe4fSflorian 
7577ac15270Sflorian 		if (n < name_len + val_len)
7587ac15270Sflorian 			return;
7597ac15270Sflorian 
7607ac15270Sflorian 		if ((env_entry = malloc(sizeof(struct env_val))) == NULL) {
7617ac15270Sflorian 			lwarnx("cannot allocate env_entry");
7627ac15270Sflorian 			return;
7637ac15270Sflorian 		}
7647ac15270Sflorian 
7657ac15270Sflorian 		if ((env_entry->val = calloc(sizeof(char), name_len + val_len +
7667ac15270Sflorian 		    2)) == NULL) {
7677ac15270Sflorian 			lwarnx("cannot allocate env_entry->val");
7687ac15270Sflorian 			free(env_entry);
7697ac15270Sflorian 			return;
7707ac15270Sflorian 		}
7717ac15270Sflorian 
7727ac15270Sflorian 		bcopy(buf, env_entry->val, name_len);
773965b2218Sblambert 		buf += name_len;
774965b2218Sblambert 		n -= name_len;
7757ac15270Sflorian 
7767ac15270Sflorian 		env_entry->val[name_len] = '\0';
777b9fc9a72Sderaadt 		if (val_len < PATH_MAX && strcmp(env_entry->val,
778efec7dbcSflorian 		    "SCRIPT_NAME") == 0 && c->script_name[0] == '\0') {
779efec7dbcSflorian 			bcopy(buf, c->script_name, val_len);
780efec7dbcSflorian 			c->script_name[val_len] = '\0';
781b9fc9a72Sderaadt 		} else if (val_len < PATH_MAX && strcmp(env_entry->val,
782efec7dbcSflorian 		    "SCRIPT_FILENAME") == 0) {
7837ac15270Sflorian 			bcopy(buf, c->script_name, val_len);
78453847fa2Sflorian 			c->script_name[val_len] = '\0';
7857ac15270Sflorian 		}
7867ac15270Sflorian 		env_entry->val[name_len] = '=';
7877ac15270Sflorian 
7887ac15270Sflorian 		bcopy(buf, (env_entry->val) + name_len + 1, val_len);
789965b2218Sblambert 		buf += val_len;
790965b2218Sblambert 		n -= val_len;
7917ac15270Sflorian 
7927ac15270Sflorian 		SLIST_INSERT_HEAD(&c->env, env_entry, entry);
793bcbc98ecSmillert 		ldebug("env[%d], %s", c->env_count, env_entry->val);
7947ac15270Sflorian 		c->env_count++;
7957ac15270Sflorian 	}
7967ac15270Sflorian }
7977ac15270Sflorian 
7987ac15270Sflorian void
parse_stdin(uint8_t * buf,uint16_t n,struct request * c,uint16_t id)799c3b13bc8Sblambert parse_stdin(uint8_t *buf, uint16_t n, struct request *c, uint16_t id)
8007ac15270Sflorian {
8017ac15270Sflorian 	struct fcgi_stdin	*node;
8027ac15270Sflorian 
8037ac15270Sflorian 	if (c->id != id) {
8047ac15270Sflorian 		lwarnx("unexpected id, ignoring");
8057ac15270Sflorian 		return;
8067ac15270Sflorian 	}
8077ac15270Sflorian 
8087ac15270Sflorian 	if ((node = calloc(1, sizeof(struct fcgi_stdin))) == NULL) {
8097ac15270Sflorian 		lwarnx("cannot calloc stdin node");
8107ac15270Sflorian 		return;
8117ac15270Sflorian 	}
8127ac15270Sflorian 
8137ac15270Sflorian 	bcopy(buf, node->data, n);
8147ac15270Sflorian 	node->data_pos = 0;
8157ac15270Sflorian 	node->data_len = n;
8167ac15270Sflorian 
8177ac15270Sflorian 	TAILQ_INSERT_TAIL(&c->stdin_head, node, entry);
8187ac15270Sflorian 
819a9461a24Sflorian 	if (event_initialized(&c->script_stdin_ev))
8207ac15270Sflorian 		event_add(&c->script_stdin_ev, NULL);
8217ac15270Sflorian }
8227ac15270Sflorian 
8237ac15270Sflorian size_t
parse_record(uint8_t * buf,size_t n,struct request * c)824c3b13bc8Sblambert parse_record(uint8_t *buf, size_t n, struct request *c)
8257ac15270Sflorian {
8267ac15270Sflorian 	struct fcgi_record_header	*h;
8277ac15270Sflorian 
8287ac15270Sflorian 	if (n < sizeof(struct fcgi_record_header))
8297ac15270Sflorian 		return (0);
8307ac15270Sflorian 
8317ac15270Sflorian 	h = (struct fcgi_record_header*) buf;
8327ac15270Sflorian 
83347939e88Sclaudio 	if (debug > 1)
83403e61f17Sblambert 		dump_fcgi_record("", h);
8357ac15270Sflorian 
8367ac15270Sflorian 	if (n < sizeof(struct fcgi_record_header) + ntohs(h->content_len)
8377ac15270Sflorian 	    + h->padding_len)
8387ac15270Sflorian 		return (0);
8397ac15270Sflorian 
8407ac15270Sflorian 	if (h->version != 1)
8417ac15270Sflorian 		lerrx(1, "wrong version");
8427ac15270Sflorian 
8437ac15270Sflorian 	switch (h->type) {
8447ac15270Sflorian 	case FCGI_BEGIN_REQUEST:
8457ac15270Sflorian 		parse_begin_request(buf + sizeof(struct fcgi_record_header),
8467ac15270Sflorian 		    ntohs(h->content_len), c, ntohs(h->id));
8477ac15270Sflorian 		break;
8487ac15270Sflorian 	case FCGI_PARAMS:
8497ac15270Sflorian 		parse_params(buf + sizeof(struct fcgi_record_header),
8507ac15270Sflorian 		    ntohs(h->content_len), c, ntohs(h->id));
8517ac15270Sflorian 		break;
8527ac15270Sflorian 	case FCGI_STDIN:
8537ac15270Sflorian 		parse_stdin(buf + sizeof(struct fcgi_record_header),
8547ac15270Sflorian 		    ntohs(h->content_len), c, ntohs(h->id));
8557ac15270Sflorian 		break;
8567ac15270Sflorian 	default:
8577ac15270Sflorian 		lwarnx("unimplemented type %d", h->type);
858f8701677Sderaadt 		break;
8597ac15270Sflorian 	}
8607ac15270Sflorian 
8617ac15270Sflorian 	return (sizeof(struct fcgi_record_header) + ntohs(h->content_len)
8627ac15270Sflorian 	    + h->padding_len);
8637ac15270Sflorian }
8647ac15270Sflorian 
86502ad6491Sblambert /*
86602ad6491Sblambert  * Fork a new CGI process to handle the request, translating
86702ad6491Sblambert  * between FastCGI parameter records and CGI's environment variables,
86802ad6491Sblambert  * as well as between the CGI process' stdin/stdout and the
86902ad6491Sblambert  * corresponding FastCGI records.
87002ad6491Sblambert  */
8717ac15270Sflorian void
exec_cgi(struct request * c)872c3b13bc8Sblambert exec_cgi(struct request *c)
8737ac15270Sflorian {
8747ac15270Sflorian 	struct env_val	*env_entry;
875a9461a24Sflorian 	int		 s_in[2], s_out[2], s_err[2], i;
8767ac15270Sflorian 	pid_t		 pid;
8777ac15270Sflorian 	char		*argv[2];
8787ac15270Sflorian 	char		**env;
8796bcf2ad3Sflorian 	char		*path;
8807ac15270Sflorian 
8817ac15270Sflorian 	i = 0;
8827ac15270Sflorian 
883a9461a24Sflorian 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s_in) == -1)
884a9461a24Sflorian 		lerr(1, "socketpair");
885a9461a24Sflorian 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s_out) == -1)
8867ac15270Sflorian 		lerr(1, "socketpair");
8877ac15270Sflorian 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s_err) == -1)
8887ac15270Sflorian 		lerr(1, "socketpair");
8893f9f8982Sbenno 	cgi_inflight--;
8903f9f8982Sbenno 	c->inflight_fds_accounted = 1;
8917ac15270Sflorian 	ldebug("fork: %s", c->script_name);
892965b2218Sblambert 
8937ac15270Sflorian 	switch (pid = fork()) {
8947ac15270Sflorian 	case -1:
895e014c219Sflorian 		c->script_status = errno;
896e014c219Sflorian 
8977ac15270Sflorian 		lwarn("fork");
898e014c219Sflorian 
899e014c219Sflorian 		close(s_in[0]);
900e014c219Sflorian 		close(s_out[0]);
901e014c219Sflorian 		close(s_err[0]);
902e014c219Sflorian 
903e014c219Sflorian 		close(s_in[1]);
904e014c219Sflorian 		close(s_out[1]);
905e014c219Sflorian 		close(s_err[1]);
906e014c219Sflorian 
907e014c219Sflorian 		c->stdin_fd_closed = c->stdout_fd_closed =
908e014c219Sflorian 		    c->stderr_fd_closed = 1;
909e014c219Sflorian 		c->script_flags = (STDOUT_DONE | STDERR_DONE | SCRIPT_DONE);
910e014c219Sflorian 
911e014c219Sflorian 		create_end_record(c);
9127ac15270Sflorian 		return;
9137ac15270Sflorian 	case 0:
9147ac15270Sflorian 		/* Child process */
9151bfdb260Sflorian 		if (pledge("stdio rpath exec", NULL) == -1)
9161bfdb260Sflorian 			lerr(1, "pledge");
917a9461a24Sflorian 		close(s_in[0]);
918a9461a24Sflorian 		close(s_out[0]);
9197ac15270Sflorian 		close(s_err[0]);
920f4a8b980Sflorian 
921a9461a24Sflorian 		if (dup2(s_in[1], STDIN_FILENO) == -1)
9227ac15270Sflorian 			_exit(1);
923a9461a24Sflorian 		if (dup2(s_out[1], STDOUT_FILENO) == -1)
9247ac15270Sflorian 			_exit(1);
9257ac15270Sflorian 		if (dup2(s_err[1], STDERR_FILENO) == -1)
9267ac15270Sflorian 			_exit(1);
927f4a8b980Sflorian 
928f4a8b980Sflorian 		close(s_in[1]);
929f4a8b980Sflorian 		close(s_out[1]);
930f4a8b980Sflorian 		close(s_err[1]);
931f4a8b980Sflorian 
932f94ba4a2Sclaudio 		signal(SIGPIPE, SIG_DFL);
933f94ba4a2Sclaudio 
9346bcf2ad3Sflorian 		path = strrchr(c->script_name, '/');
9356bcf2ad3Sflorian 		if (path != NULL) {
93676f0500eSflorian 			if (path != c->script_name) {
9376bcf2ad3Sflorian 				*path = '\0';
9386bcf2ad3Sflorian 				if (chdir(c->script_name) == -1)
93976f0500eSflorian 					lwarn("cannot chdir to %s",
94076f0500eSflorian 					    c->script_name);
9416bcf2ad3Sflorian 				*path = '/';
94276f0500eSflorian 			} else
94376f0500eSflorian 				if (chdir("/") == -1)
94476f0500eSflorian 					lwarn("cannot chdir to /");
9456bcf2ad3Sflorian 		}
9466bcf2ad3Sflorian 
9477ac15270Sflorian 		argv[0] = c->script_name;
9487ac15270Sflorian 		argv[1] = NULL;
9497ac15270Sflorian 		if ((env = calloc(c->env_count + 1, sizeof(char*))) == NULL)
9507ac15270Sflorian 			_exit(1);
9517ac15270Sflorian 		SLIST_FOREACH(env_entry, &c->env, entry)
9527ac15270Sflorian 			env[i++] = env_entry->val;
9537ac15270Sflorian 		env[i++] = NULL;
9547ac15270Sflorian 		execve(c->script_name, argv, env);
9551878c91fSdjm 		lwarn("execve %s", c->script_name);
9567ac15270Sflorian 		_exit(1);
9577ac15270Sflorian 
9587ac15270Sflorian 	}
959f8701677Sderaadt 
9607ac15270Sflorian 	/* Parent process*/
961a9461a24Sflorian 	close(s_in[1]);
962a9461a24Sflorian 	close(s_out[1]);
9637ac15270Sflorian 	close(s_err[1]);
96494ee156dSflorian 
965a9461a24Sflorian 	fcntl(s_in[0], F_SETFD, FD_CLOEXEC);
966a9461a24Sflorian 	fcntl(s_out[0], F_SETFD, FD_CLOEXEC);
96794ee156dSflorian 	fcntl(s_err[0], F_SETFD, FD_CLOEXEC);
96894ee156dSflorian 
969a9461a24Sflorian 	if (ioctl(s_in[0], FIONBIO, &on) == -1)
970a9461a24Sflorian 		lerr(1, "script ioctl(FIONBIO)");
971a9461a24Sflorian 	if (ioctl(s_out[0], FIONBIO, &on) == -1)
9727ac15270Sflorian 		lerr(1, "script ioctl(FIONBIO)");
9737ac15270Sflorian 	if (ioctl(s_err[0], FIONBIO, &on) == -1)
9747ac15270Sflorian 		lerr(1, "script ioctl(FIONBIO)");
9757ac15270Sflorian 
9767ac15270Sflorian 	c->script_pid = pid;
977a9461a24Sflorian 	event_set(&c->script_stdin_ev, s_in[0], EV_WRITE | EV_PERSIST,
978a9461a24Sflorian 	    script_out, c);
979a9461a24Sflorian 	event_add(&c->script_stdin_ev, NULL);
980a9461a24Sflorian 	event_set(&c->script_ev, s_out[0], EV_READ | EV_PERSIST,
981a9461a24Sflorian 	    script_std_in, c);
9827ac15270Sflorian 	event_add(&c->script_ev, NULL);
9837ac15270Sflorian 	event_set(&c->script_err_ev, s_err[0], EV_READ | EV_PERSIST,
9847ac15270Sflorian 	    script_err_in, c);
9857ac15270Sflorian 	event_add(&c->script_err_ev, NULL);
9867ac15270Sflorian }
9877ac15270Sflorian 
9887ac15270Sflorian void
create_end_record(struct request * c)989c3b13bc8Sblambert create_end_record(struct request *c)
9907ac15270Sflorian {
9917ac15270Sflorian 	struct fcgi_response		*resp;
9927ac15270Sflorian 	struct fcgi_record_header	*header;
99303e61f17Sblambert 	struct fcgi_end_request_body	*end_request;
9947ac15270Sflorian 
99507240181Sreyk 	if ((resp = calloc(1, sizeof(struct fcgi_response))) == NULL) {
9967ac15270Sflorian 		lwarnx("cannot malloc fcgi_response");
9977ac15270Sflorian 		return;
9987ac15270Sflorian 	}
9997ac15270Sflorian 	header = (struct fcgi_record_header*) resp->data;
10007ac15270Sflorian 	header->version = 1;
10017ac15270Sflorian 	header->type = FCGI_END_REQUEST;
10027ac15270Sflorian 	header->id = htons(c->id);
10037ac15270Sflorian 	header->content_len = htons(sizeof(struct
100403e61f17Sblambert 	    fcgi_end_request_body));
10057ac15270Sflorian 	header->padding_len = 0;
10067ac15270Sflorian 	header->reserved = 0;
10078c347892Sflorian 	end_request = (struct fcgi_end_request_body *) (resp->data +
10088c347892Sflorian 	    sizeof(struct fcgi_record_header));
10097ac15270Sflorian 	end_request->app_status = htonl(c->script_status);
10107ac15270Sflorian 	end_request->protocol_status = FCGI_REQUEST_COMPLETE;
10118c347892Sflorian 	end_request->reserved[0] = 0;
10128c347892Sflorian 	end_request->reserved[1] = 0;
10138c347892Sflorian 	end_request->reserved[2] = 0;
10147ac15270Sflorian 	resp->data_pos = 0;
101503e61f17Sblambert 	resp->data_len = sizeof(struct fcgi_end_request_body) +
10167ac15270Sflorian 	    sizeof(struct fcgi_record_header);
10174404b4d5Sflorian 	slowcgi_add_response(c, resp);
10187ac15270Sflorian }
10197ac15270Sflorian 
10207ac15270Sflorian void
script_in(int fd,struct event * ev,struct request * c,uint8_t type)1021c3b13bc8Sblambert script_in(int fd, struct event *ev, struct request *c, uint8_t type)
10227ac15270Sflorian {
10237ac15270Sflorian 	struct fcgi_response		*resp;
10247ac15270Sflorian 	struct fcgi_record_header	*header;
10257ac15270Sflorian 	ssize_t				 n;
10267ac15270Sflorian 
102707240181Sreyk 	if ((resp = calloc(1, sizeof(struct fcgi_response))) == NULL) {
10287ac15270Sflorian 		lwarnx("cannot malloc fcgi_response");
10297ac15270Sflorian 		return;
10307ac15270Sflorian 	}
10317ac15270Sflorian 	header = (struct fcgi_record_header*) resp->data;
10327ac15270Sflorian 	header->version = 1;
10337ac15270Sflorian 	header->type = type;
10347ac15270Sflorian 	header->id = htons(c->id);
10357ac15270Sflorian 	header->padding_len = 0;
10367ac15270Sflorian 	header->reserved = 0;
10377ac15270Sflorian 
10387ac15270Sflorian 	n = read(fd, resp->data + sizeof(struct fcgi_record_header),
10390ee0284dSblambert 	    FCGI_CONTENT_SIZE);
10407ac15270Sflorian 
10417ac15270Sflorian 	if (n == -1) {
10427ac15270Sflorian 		switch (errno) {
10437ac15270Sflorian 		case EINTR:
10447ac15270Sflorian 		case EAGAIN:
1045aa48dbd7Sjasper 			free(resp);
10467ac15270Sflorian 			return;
10477ac15270Sflorian 		default:
10487ac15270Sflorian 			n = 0; /* fake empty FCGI_STD{OUT,ERR} response */
10497ac15270Sflorian 		}
10507ac15270Sflorian 	}
10517ac15270Sflorian 	header->content_len = htons(n);
10527ac15270Sflorian 	resp->data_pos = 0;
10537ac15270Sflorian 	resp->data_len = n + sizeof(struct fcgi_record_header);
10544404b4d5Sflorian 	slowcgi_add_response(c, resp);
10557ac15270Sflorian 
10567ac15270Sflorian 	if (n == 0) {
10577ac15270Sflorian 		if (type == FCGI_STDOUT)
10587ac15270Sflorian 			c->script_flags |= STDOUT_DONE;
10597ac15270Sflorian 		else
10607ac15270Sflorian 			c->script_flags |= STDERR_DONE;
10617ac15270Sflorian 
10627ac15270Sflorian 		if (c->script_flags == (STDOUT_DONE | STDERR_DONE |
10637ac15270Sflorian 		    SCRIPT_DONE)) {
1064c3b13bc8Sblambert 			create_end_record(c);
10657ac15270Sflorian 		}
10667ac15270Sflorian 		event_del(ev);
10677ac15270Sflorian 		close(fd);
10686bdc2311Sflorian 		if (type == FCGI_STDOUT)
10696bdc2311Sflorian 			c->stdout_fd_closed = 1;
10706bdc2311Sflorian 		else
10716bdc2311Sflorian 			c->stderr_fd_closed = 1;
10727ac15270Sflorian 	}
10737ac15270Sflorian }
10747ac15270Sflorian 
10757ac15270Sflorian void
script_std_in(int fd,short events,void * arg)10767ac15270Sflorian script_std_in(int fd, short events, void *arg)
10777ac15270Sflorian {
1078c3b13bc8Sblambert 	struct request *c = arg;
10797ac15270Sflorian 	script_in(fd, &c->script_ev, c, FCGI_STDOUT);
10807ac15270Sflorian }
10817ac15270Sflorian 
10827ac15270Sflorian void
script_err_in(int fd,short events,void * arg)10837ac15270Sflorian script_err_in(int fd, short events, void *arg)
10847ac15270Sflorian {
1085c3b13bc8Sblambert 	struct request *c = arg;
10867ac15270Sflorian 	script_in(fd, &c->script_err_ev, c, FCGI_STDERR);
10877ac15270Sflorian }
10887ac15270Sflorian 
10897ac15270Sflorian void
script_out(int fd,short events,void * arg)10907ac15270Sflorian script_out(int fd, short events, void *arg)
10917ac15270Sflorian {
1092c3b13bc8Sblambert 	struct request		*c;
10937ac15270Sflorian 	struct fcgi_stdin	*node;
10947ac15270Sflorian 	ssize_t			 n;
10957ac15270Sflorian 
10967ac15270Sflorian 	c = arg;
10977ac15270Sflorian 
10987ac15270Sflorian 	while ((node = TAILQ_FIRST(&c->stdin_head))) {
10997ac15270Sflorian 		if (node->data_len == 0) { /* end of stdin marker */
1100a9461a24Sflorian 			close(fd);
11016bdc2311Sflorian 			c->stdin_fd_closed = 1;
11027ac15270Sflorian 			break;
11037ac15270Sflorian 		}
11047ac15270Sflorian 		n = write(fd, node->data + node->data_pos, node->data_len);
11057ac15270Sflorian 		if (n == -1) {
110614379226Sflorian 			if (errno == EAGAIN || errno == EINTR)
11077ac15270Sflorian 				return;
11087ac15270Sflorian 			event_del(&c->script_stdin_ev);
11097ac15270Sflorian 			return;
11107ac15270Sflorian 		}
11117ac15270Sflorian 		node->data_pos += n;
11127ac15270Sflorian 		node->data_len -= n;
11137ac15270Sflorian 		if (node->data_len == 0) {
11147ac15270Sflorian 			TAILQ_REMOVE(&c->stdin_head, node, entry);
11157ac15270Sflorian 			free(node);
11167ac15270Sflorian 		}
11177ac15270Sflorian 	}
1118a9461a24Sflorian 	event_del(&c->script_stdin_ev);
11197ac15270Sflorian }
11207ac15270Sflorian 
11217ac15270Sflorian void
cleanup_request(struct request * c)1122c3b13bc8Sblambert cleanup_request(struct request *c)
11237ac15270Sflorian {
11247ac15270Sflorian 	struct fcgi_response	*resp;
11257ac15270Sflorian 	struct fcgi_stdin	*stdin_node;
11267ac15270Sflorian 	struct env_val		*env_entry;
11277ac15270Sflorian 
11287ac15270Sflorian 	evtimer_del(&c->tmo);
11297ac15270Sflorian 	if (event_initialized(&c->ev))
11307ac15270Sflorian 		event_del(&c->ev);
11317ac15270Sflorian 	if (event_initialized(&c->resp_ev))
11327ac15270Sflorian 		event_del(&c->resp_ev);
11337ac15270Sflorian 	if (event_initialized(&c->script_ev)) {
11346bdc2311Sflorian 		if (!c->stdout_fd_closed)
11357ac15270Sflorian 			close(EVENT_FD(&c->script_ev));
11367ac15270Sflorian 		event_del(&c->script_ev);
11377ac15270Sflorian 	}
11387ac15270Sflorian 	if (event_initialized(&c->script_err_ev)) {
11396bdc2311Sflorian 		if (!c->stderr_fd_closed)
11407ac15270Sflorian 			close(EVENT_FD(&c->script_err_ev));
11417ac15270Sflorian 		event_del(&c->script_err_ev);
11427ac15270Sflorian 	}
11437ac15270Sflorian 	if (event_initialized(&c->script_stdin_ev)) {
11446bdc2311Sflorian 		if (!c->stdin_fd_closed)
11457ac15270Sflorian 			close(EVENT_FD(&c->script_stdin_ev));
11467ac15270Sflorian 		event_del(&c->script_stdin_ev);
11477ac15270Sflorian 	}
11487ac15270Sflorian 	close(c->fd);
11497ac15270Sflorian 	while (!SLIST_EMPTY(&c->env)) {
11507ac15270Sflorian 		env_entry = SLIST_FIRST(&c->env);
11517ac15270Sflorian 		SLIST_REMOVE_HEAD(&c->env, entry);
11527ac15270Sflorian 		free(env_entry->val);
11537ac15270Sflorian 		free(env_entry);
11547ac15270Sflorian 	}
11557ac15270Sflorian 
11567ac15270Sflorian 	while ((resp = TAILQ_FIRST(&c->response_head))) {
11577ac15270Sflorian 		TAILQ_REMOVE(&c->response_head, resp, entry);
11587ac15270Sflorian 		free(resp);
11597ac15270Sflorian 	}
11607ac15270Sflorian 	while ((stdin_node = TAILQ_FIRST(&c->stdin_head))) {
11617ac15270Sflorian 		TAILQ_REMOVE(&c->stdin_head, stdin_node, entry);
11627ac15270Sflorian 		free(stdin_node);
11637ac15270Sflorian 	}
1164f3a4f9e5Sclaudio 	LIST_REMOVE(c, entry);
11653f9f8982Sbenno 	if (! c->inflight_fds_accounted)
11663f9f8982Sbenno 		cgi_inflight--;
11677ac15270Sflorian 	free(c);
11687ac15270Sflorian }
11697ac15270Sflorian 
1170965b2218Sblambert void
dump_fcgi_record(const char * p,struct fcgi_record_header * h)117103e61f17Sblambert dump_fcgi_record(const char *p, struct fcgi_record_header *h)
117203e61f17Sblambert {
117303e61f17Sblambert 	dump_fcgi_record_header(p, h);
117403e61f17Sblambert 
117503e61f17Sblambert 	if (h->type == FCGI_BEGIN_REQUEST)
117603e61f17Sblambert 		dump_fcgi_begin_request_body(p,
1177d241f573Sflorian 		    (struct fcgi_begin_request_body *)(h + 1));
117803e61f17Sblambert 	else if (h->type == FCGI_END_REQUEST)
117903e61f17Sblambert 		dump_fcgi_end_request_body(p,
1180d241f573Sflorian 		    (struct fcgi_end_request_body *)(h + 1));
118103e61f17Sblambert }
118203e61f17Sblambert 
118303e61f17Sblambert void
dump_fcgi_record_header(const char * p,struct fcgi_record_header * h)1184965b2218Sblambert dump_fcgi_record_header(const char* p, struct fcgi_record_header *h)
11857ac15270Sflorian {
11867ac15270Sflorian 	ldebug("%sversion:         %d", p, h->version);
11877ac15270Sflorian 	ldebug("%stype:            %d", p, h->type);
11887ac15270Sflorian 	ldebug("%srequestId:       %d", p, ntohs(h->id));
11897ac15270Sflorian 	ldebug("%scontentLength:   %d", p, ntohs(h->content_len));
11907ac15270Sflorian 	ldebug("%spaddingLength:   %d", p, h->padding_len);
11917ac15270Sflorian 	ldebug("%sreserved:        %d", p, h->reserved);
11927ac15270Sflorian }
11937ac15270Sflorian 
11947ac15270Sflorian void
dump_fcgi_begin_request_body(const char * p,struct fcgi_begin_request_body * b)119503e61f17Sblambert dump_fcgi_begin_request_body(const char *p, struct fcgi_begin_request_body *b)
119603e61f17Sblambert {
119703e61f17Sblambert 	ldebug("%srole             %d", p, ntohs(b->role));
119803e61f17Sblambert 	ldebug("%sflags            %d", p, b->flags);
119903e61f17Sblambert }
120003e61f17Sblambert 
120103e61f17Sblambert void
dump_fcgi_end_request_body(const char * p,struct fcgi_end_request_body * b)120203e61f17Sblambert dump_fcgi_end_request_body(const char *p, struct fcgi_end_request_body *b)
120303e61f17Sblambert {
120403e61f17Sblambert 	ldebug("%sappStatus:       %d", p, ntohl(b->app_status));
120503e61f17Sblambert 	ldebug("%sprotocolStatus:  %d", p, b->protocol_status);
120603e61f17Sblambert }
120703e61f17Sblambert 
120803e61f17Sblambert void
syslog_vstrerror(int e,int priority,const char * fmt,va_list ap)12097ac15270Sflorian syslog_vstrerror(int e, int priority, const char *fmt, va_list ap)
12107ac15270Sflorian {
12117ac15270Sflorian 	char *s;
12127ac15270Sflorian 
12137ac15270Sflorian 	if (vasprintf(&s, fmt, ap) == -1) {
12147ac15270Sflorian 		syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror");
12157ac15270Sflorian 		exit(1);
12167ac15270Sflorian 	}
12177ac15270Sflorian 	syslog(priority, "%s: %s", s, strerror(e));
12187ac15270Sflorian 	free(s);
12197ac15270Sflorian }
12207ac15270Sflorian 
1221d0a52cffSflorian __dead void
syslog_err(int ecode,const char * fmt,...)12227ac15270Sflorian syslog_err(int ecode, const char *fmt, ...)
12237ac15270Sflorian {
12247ac15270Sflorian 	va_list ap;
12257ac15270Sflorian 
12267ac15270Sflorian 	va_start(ap, fmt);
1227a52b97d1Sflorian 	syslog_vstrerror(errno, LOG_CRIT, fmt, ap);
12287ac15270Sflorian 	va_end(ap);
12297ac15270Sflorian 	exit(ecode);
12307ac15270Sflorian }
12317ac15270Sflorian 
1232d0a52cffSflorian __dead void
syslog_errx(int ecode,const char * fmt,...)12337ac15270Sflorian syslog_errx(int ecode, const char *fmt, ...)
12347ac15270Sflorian {
12357ac15270Sflorian 	va_list ap;
12367ac15270Sflorian 
12377ac15270Sflorian 	va_start(ap, fmt);
1238a52b97d1Sflorian 	vsyslog(LOG_CRIT, fmt, ap);
12397ac15270Sflorian 	va_end(ap);
12407ac15270Sflorian 	exit(ecode);
12417ac15270Sflorian }
12427ac15270Sflorian 
12437ac15270Sflorian void
syslog_warn(const char * fmt,...)12447ac15270Sflorian syslog_warn(const char *fmt, ...)
12457ac15270Sflorian {
12467ac15270Sflorian 	va_list ap;
12477ac15270Sflorian 
12487ac15270Sflorian 	va_start(ap, fmt);
1249a52b97d1Sflorian 	syslog_vstrerror(errno, LOG_ERR, fmt, ap);
12507ac15270Sflorian 	va_end(ap);
12517ac15270Sflorian }
12527ac15270Sflorian 
12537ac15270Sflorian void
syslog_warnx(const char * fmt,...)12547ac15270Sflorian syslog_warnx(const char *fmt, ...)
12557ac15270Sflorian {
12567ac15270Sflorian 	va_list ap;
12577ac15270Sflorian 
12587ac15270Sflorian 	va_start(ap, fmt);
1259a52b97d1Sflorian 	vsyslog(LOG_ERR, fmt, ap);
12607ac15270Sflorian 	va_end(ap);
12617ac15270Sflorian }
12627ac15270Sflorian 
12637ac15270Sflorian void
syslog_info(const char * fmt,...)12647ac15270Sflorian syslog_info(const char *fmt, ...)
12657ac15270Sflorian {
12667ac15270Sflorian 	va_list ap;
12677ac15270Sflorian 
12687ac15270Sflorian 	va_start(ap, fmt);
12697ac15270Sflorian 	vsyslog(LOG_INFO, fmt, ap);
12707ac15270Sflorian 	va_end(ap);
12717ac15270Sflorian }
12727ac15270Sflorian 
12737ac15270Sflorian void
syslog_debug(const char * fmt,...)12747ac15270Sflorian syslog_debug(const char *fmt, ...)
12757ac15270Sflorian {
127604ab3ac4Sflorian 	if (verbose > 0) {
12777ac15270Sflorian 		va_list ap;
12787ac15270Sflorian 		va_start(ap, fmt);
12797ac15270Sflorian 		vsyslog(LOG_DEBUG, fmt, ap);
12807ac15270Sflorian 		va_end(ap);
12817ac15270Sflorian 	}
128204ab3ac4Sflorian }
1283