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