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