1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include "cgi.h"
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34
35 #include <sys/types.h>
36 #include <signal.h>
37 #include <sys/wait.h>
38
39 #include <cx/string.h>
40
41 #include "../util/util.h"
42 #include "../util/pblock.h"
43 #include "../daemon/netsite.h"
44 #include "../daemon/vfs.h"
45 #include "../util/io.h"
46 #include "../daemon/event.h"
47
48 #include "cgiutils.h"
49
50 #define CGI_VARS 32
51
52 #define CGI_RESPONSE_PARSER_BUFLEN 2048
53 #define CGI_RESPONSE_MAX_LINE_LENGTH 512
54
55 int send_cgi(pblock *pb, Session *sn, Request *rq) {
56 char *path = pblock_findkeyval(pb_key_path, rq->vars);
57 char *ctlen = pblock_findkeyval(pb_key_content_length, rq->headers);
58 int64_t content_length =
0;
59
60 log_ereport(
LOG_DEBUG,
"cgi-send: path: %s req: %p content-length: %s", path, rq, ctlen);
61
62 if(ctlen) {
63 if(!util_strtoint(ctlen, &content_length)) {
64 log_ereport(
65 LOG_FAILURE,
66 "send-cgi: content-length header is not an integer");
67 protocol_status(sn, rq,
400,
NULL);
68 return REQ_ABORTED;
69 }
70 }
71
72
73
74 if(!vfs_is_sys(rq->vfs)) {
75 log_ereport(
LOG_WARN,
"send-cgi: VFS setting ignored");
76 }
77
78 struct stat s;
79 if(stat(path, &s)) {
80 int statuscode = util_errno2status(errno);
81 protocol_status(sn, rq, statuscode,
NULL);
82 return REQ_ABORTED;
83 }
84 if(
S_ISDIR(s.st_mode)) {
85 protocol_status(sn, rq,
403,
NULL);
86 return REQ_ABORTED;
87 }
88
89 param_free(pblock_remove(
"content-type", rq->srvhdrs));
90
91 const char *args = pblock_findval(
"query", rq->reqpb);
92 char **argv = cgi_create_argv(path,
NULL, args);
93 if(!argv) {
94 return REQ_ABORTED;
95 }
96
97 char **env = http_hdrs2env(rq->headers);
98 env = cgi_common_vars(sn, rq, env);
99 env = cgi_specific_vars(sn, rq, args, env,
1);
100
101
102 CGIHandler *handler = pool_malloc(sn->pool,
sizeof(CGIHandler));
103 if(!handler) {
104 return REQ_ABORTED;
105 }
106 ZERO(handler,
sizeof(CGIHandler));
107 handler->path = path;
108
109 int ret = cgi_start(&handler->process, path, argv, env);
110 if(ret !=
REQ_PROCEED) {
111 util_env_free(env);
112 cgi_free_argv(argv);
113 return ret;
114 }
115 log_ereport(
LOG_DEBUG,
"send-cgi: req: %p pid: %d", rq, (
int)handler->process.pid);
116
117 util_env_free(env);
118 cgi_free_argv(argv);
119
120 char buf[
4096];
121 ssize_t r;
122
123 if(content_length >
0) {
124 ssize_t n =
0;
125 while(n < content_length) {
126 r = netbuf_getbytes(sn->inbuf, buf,
4096);
127 if(r <=
0) {
128 log_ereport(
129 LOG_FAILURE,
130 "send-cgi: script: %s: cannot read request body",
131 path);
132 kill(handler->process.pid,
SIGTERM);
133 cgi_close(&handler->process);
134 return REQ_ABORTED;
135 }
136 ssize_t w = write(handler->process.in[
1], buf, r);
137 if(w <=
0) {
138 log_ereport(
139 LOG_FAILURE,
140 "send-cgi: script: %s: cannot send request body to cgi process",
141 path);
142 kill(handler->process.pid,
SIGTERM);
143 cgi_close(&handler->process);
144 return REQ_ABORTED;
145 }
146 n += r;
147 }
148 }
149 system_close(handler->process.in[
1]);
150 handler->process.in[
1] = -
1;
151
152 handler->parser = cgi_parser_new(sn, rq);
153
154
155 int flags;
156 if ((flags = fcntl(handler->process.err[
0],
F_GETFL,
0)) == -
1) {
157 flags =
0;
158 }
159 if (fcntl(handler->process.err[
0],
F_SETFL, flags |
O_NONBLOCK) !=
0) {
160 log_ereport(
LOG_FAILURE,
"cgi-bin: fcntl err[0] failed: %s", strerror(errno));
161 }
162 if ((flags = fcntl(handler->process.out[
0],
F_GETFL,
0)) == -
1) {
163 flags =
0;
164 }
165 if (fcntl(handler->process.out[
0],
F_SETFL, flags |
O_NONBLOCK) !=
0) {
166 log_ereport(
LOG_FAILURE,
"cgi-bin: fcntl out[0] failed: %s", strerror(errno));
167 }
168
169
170 Event *readev = pool_malloc(sn->pool,
sizeof(Event));
171 ZERO(readev,
sizeof(Event));
172 readev->cookie = handler;
173 readev->fn = cgi_stdout_readevent;
174 readev->finish = cgi_event_finish;
175
176 Event *stderr_readev = pool_malloc(sn->pool,
sizeof(Event));
177 ZERO(stderr_readev,
sizeof(Event));
178 stderr_readev->cookie = handler;
179 stderr_readev->fn = cgi_stderr_readevent;
180 stderr_readev->finish = cgi_event_finish;
181
182 Event *writeev = pool_malloc(sn->pool,
sizeof(Event));
183 ZERO(writeev,
sizeof(Event));
184 writeev->cookie = handler;
185 writeev->fn = cgi_writeevent;
186 writeev->finish = cgi_event_finish;
187
188 handler->readev = readev;
189 handler->writeev = writeev;
190
191 net_setnonblock(sn->csd,
1);
192
193
194 int error =
0;
195 if(ev_pollin(sn->ev, handler->process.err[
0], stderr_readev)) {
196 log_ereport(
LOG_FAILURE,
"send-cgi: stderr ev_pollin failed");
197 error =
1;
198 }
else {
199 handler->wait_read =
TRUE;
200 handler->events++;
201 }
202 if(!error && ev_pollin(sn->ev, handler->process.out[
0], readev)) {
203 log_ereport(
LOG_FAILURE,
"send-cgi: stdout ev_pollin failed");
204 error =
1;
205 }
else {
206 handler->events++;
207 }
208
209
210
211 if(error) {
212 log_ereport(
LOG_FAILURE,
"cgi-send: initialization error: kill script: %s", path);
213 kill(handler->process.pid,
SIGKILL);
214 cgi_parser_free(handler->parser);
215 cgi_close(&handler->process);
216 return REQ_ABORTED;
217 }
218
219 return REQ_PROCESSING;
220 }
221
222
223
224
225
226
227
228
229
230 static int cgi_try_write_flush(CGIHandler *handler, Session *sn) {
231 ssize_t wr =
0;
232 while(
233 handler->writebuf_size - handler->writebuf_pos >
0 &&
234 (wr = net_write(
235 sn->csd,
236 handler->writebuf + handler->writebuf_pos,
237 handler->writebuf_size - handler->writebuf_pos))
238 >
0)
239 {
240 handler->writebuf_pos += wr;
241 handler->count_write += wr;
242 }
243 if(handler->writebuf_size - handler->writebuf_pos >
0) {
244 if(net_errno(sn->csd) !=
EAGAIN) {
245 handler->result =
REQ_ABORTED;
246 log_ereport(
247 LOG_FAILURE,
248 "cgi pid %d %s: network error: %s",
249 (
int)handler->process.pid,
250 handler->path,
251 strerror(net_errno(sn->csd)));
252 }
253
254 return 1;
255 }
256 return 0;
257 }
258
259
260
261
262
263
264
265
266
267
268
269 static int cgi_try_write(CGIHandler *handler, EventHandler *ev, Session *sn,
char *buf,
size_t size) {
270
271 size_t pos =
0;
272 ssize_t wr =
0;
273 while(size - pos >
0 && (wr = net_write(sn->csd, buf + pos, size - pos)) >
0) {
274 pos += wr;
275 handler->count_write += wr;
276 }
277
278 if(pos < size) {
279 if(net_errno(sn->csd) ==
EAGAIN) {
280
281
282 size_t remaining = size-pos;
283 if(remaining > handler->writebuf_alloc) {
284 handler->writebuf_alloc = size >
4096 ? size :
4096;
285 handler->writebuf = pool_realloc(sn->pool, handler->writebuf, handler->writebuf_alloc);
286 if(!handler->writebuf) {
287 handler->result =
REQ_ABORTED;
288 return 1;
289 }
290 }
291 memcpy(handler->writebuf, buf+pos, remaining);
292 handler->writebuf_size = remaining;
293 handler->writebuf_pos =
0;
294 }
else {
295 handler->result =
REQ_ABORTED;
296 log_ereport(
297 LOG_FAILURE,
298 "cgi pid %d %s: network error: %s",
299 (
int)handler->process.pid,
300 handler->path,
301 strerror(net_errno(sn->csd)));
302 }
303 return 1;
304 }
305
306 return 0;
307 }
308
309 int cgi_stdout_readevent(EventHandler *ev, Event *event) {
310 CGIHandler *handler = event->cookie;
311
312 if(handler->debug_finished) {
313 log_ereport(
LOG_DEBUG,
"cgi-send: req: %p debug_finished: 1 cgi_stdout_readevent events: %d", handler->parser->rq, handler->events);
314 }
315
316 if(handler->cgi_eof || handler->result ==
REQ_ABORTED) {
317
318
319
320
321 log_ereport(
LOG_DEBUG,
"cgi-send: req: %p readevent cgi_eof = TRUE result: %d", handler->parser->rq, handler->result);
322 handler->wait_read =
FALSE;
323 event->finish =
NULL;
324 return 0;
325 }
326
327 event->finish = cgi_event_finish;
328 handler->writeev->finish =
NULL;
329 CgiIOResult ret = cgi_read_output(handler, ev,
"readevent");
330 switch(ret) {
331 case CGI_IO_COMPLETE: {
332 break;
333 }
334 case CGI_IO_NEED_READ: {
335 return 1;
336 }
337 case CGI_IO_NEED_WRITE: {
338
339 if(handler->poll_out) {
340 return 1;
341 }
342 if(event_pollout(ev, handler->parser->sn->csd, handler->writeev)) {
343 handler->result =
REQ_ABORTED;
344 }
else {
345 handler->poll_out =
TRUE;
346 log_ereport(
LOG_DEBUG,
"cgi-send: req: %p enable poll out", handler->parser->rq);
347 return 1;
348 }
349 }
350 case CGI_IO_ERROR: {
351 break;
352 }
353 }
354
355 handler->wait_read =
FALSE;
356 return 0;
357 }
358
359 int cgi_writeevent(EventHandler *ev, Event *event) {
360 CGIHandler *handler = event->cookie;
361
362 if(handler->cgi_eof || handler->result ==
REQ_ABORTED) {
363
364
365 log_ereport(
LOG_DEBUG,
"cgi-send: req: %p writeevent cgi_eof = TRUE result: %d", handler->parser->rq, handler->result);
366 handler->poll_out =
FALSE;
367 event->finish =
NULL;
368 return 0;
369 }
370
371 event->finish = cgi_event_finish;
372 handler->readev->finish =
NULL;
373 CgiIOResult ret = cgi_read_output(handler, ev,
"writeevent");
374 switch(ret) {
375 case CGI_IO_COMPLETE: {
376 break;
377 }
378 case CGI_IO_NEED_READ: {
379 return 1;
380 }
381 case CGI_IO_NEED_WRITE: {
382 return 1;
383 }
384 case CGI_IO_ERROR: {
385 break;
386 }
387 }
388
389 handler->poll_out =
FALSE;
390 return 0;
391 }
392
393
394
395 CgiIOResult cgi_read_output(CGIHandler *handler, EventHandler *ev,
const char *debug_log) {
396 CGIResponseParser *parser = handler->parser;
397 Session *sn = parser->sn;
398 Request *rq = parser->rq;
399
400 if(handler->result ==
REQ_ABORTED) {
401 return CGI_IO_ERROR;
402 }
403
404
405
406 if(cgi_try_write_flush(handler, sn)) {
407 if(handler->result ==
REQ_ABORTED) {
408 return CGI_IO_ERROR;
409 }
else {
410 return CGI_IO_NEED_WRITE;
411 }
412 }
413
414 char buf[
4096];
415 ssize_t r;
416
417 int ret =
CGI_IO_COMPLETE;
418 handler->result =
REQ_PROCEED;
419 while((r = read(handler->process.out[
0], buf,
4096)) >
0) {
420 if(parser->cgiheader) {
421 size_t pos;
422 int ret = cgi_parse_response(parser, buf, r, &pos);
423 if(ret == -
1) {
424 log_ereport(
425 LOG_FAILURE,
426 "broken cgi script response: path: %s", handler->path);
427 protocol_status(sn, rq,
500,
NULL);
428 handler->result =
REQ_ABORTED;
429 return CGI_IO_ERROR;
430 }
else if(ret ==
1) {
431 WS_ASSERT(pos <= r);
432
433 parser->response_length += r-pos;
434
435 parser->cgiheader =
FALSE;
436 if(parser->status >
0) {
437 protocol_status(sn, rq, parser->status, parser->msg);
438 }
439
440 handler->response = http_create_response(sn, rq);
441 if(!handler->response) {
442 handler->result =
REQ_ABORTED;
443 return CGI_IO_ERROR;
444 }
445
446 int send_response = http_send_response(handler->response);
447 if(send_response <
0) {
448 handler->result =
REQ_ABORTED;
449 ret =
CGI_IO_ERROR;
450 break;
451 }
else if(send_response ==
1) {
452
453 if(!handler->poll_out) {
454 if(event_pollout(ev, sn->csd, handler->writeev)) {
455 handler->result =
REQ_ABORTED;
456 return CGI_IO_ERROR;
457 }
458 handler->poll_out =
TRUE;
459 return CGI_IO_NEED_WRITE;
460 }
461 }
else {
462 handler->response =
NULL;
463 }
464
465 if(pos < r) {
466 if(cgi_try_write(handler, ev, sn, &buf[pos], r-pos)) {
467 return handler->result ==
REQ_ABORTED ?
CGI_IO_ERROR :
CGI_IO_NEED_WRITE;
468 }
469 }
470 }
471 }
else {
472 parser->response_length += r;
473 if(cgi_try_write(handler, ev, sn, buf, r)) {
474 return handler->result ==
REQ_ABORTED ?
CGI_IO_ERROR :
CGI_IO_NEED_WRITE;
475 }
476 }
477 }
478 if(r <
0 && errno ==
EAGAIN) {
479 return CGI_IO_NEED_READ;
480 }
481 handler->cgi_eof =
TRUE;
482 log_ereport(
LOG_DEBUG,
"cgi-send: req: %p pid: %d set cgi_eof : %s", rq, handler->process.pid, debug_log);
483 return ret;
484 }
485
486 int cgi_stderr_readevent(EventHandler *ev, Event *event) {
487 CGIHandler *handler = event->cookie;
488 pool_handle_t *pool = handler->parser->sn->pool;
489
490 char buf[
4096];
491 char *line = buf;
492 int line_start;
493 ssize_t r;
494 while((r = read(handler->process.err[
0], buf,
4096)) >
0) {
495 line_start =
0;
496 int pos =
0;
497
498 for(
int i=
0;i<r;i++) {
499 if(buf[i] ==
'\n') {
500 log_ereport(
501 LOG_INFORM,
502 "cgi pid %d %s stderr: %.*s%.*s",
503 (
int)handler->process.pid,
504 handler->path,
505 (
int)handler->stderr_tmplen,
506 handler->stderr_tmp,
507 i - line_start,
508 line + line_start);
509 line_start = i+
1;
510 pos = i+
1;
511
512 if(handler->stderr_tmp) {
513 handler->stderr_tmplen =
0;
514 }
515 }
516 }
517
518
519 if(pos < r) {
520 int tmplen = r-pos;
521 if(handler->stderr_tmplen >
0) {
522
523 if(handler->stderr_tmplen + tmplen > handler->stderr_tmpalloc) {
524 handler->stderr_tmpalloc = handler->stderr_tmplen + tmplen;
525 handler->stderr_tmp = pool_realloc(pool, handler->stderr_tmp, handler->stderr_tmpalloc);
526 if(!handler->stderr_tmp) {
527 log_ereport(
LOG_FAILURE,
"send-cgi: cannot create tmp buffer for parsing stderr");
528 handler->stderr_tmpalloc =
0;
529 handler->stderr_tmplen =
0;
530 continue;
531 }
532 }
533 memcpy(handler->stderr_tmp + handler->stderr_tmplen, line + line_start, tmplen);
534 handler->stderr_tmplen += tmplen;
535 }
else {
536 if(handler->stderr_tmpalloc < tmplen) {
537
538 handler->stderr_tmpalloc = tmplen <
256 ?
256 : tmplen;
539 if(handler->stderr_tmp) {
540
541
542
543
544
545 pool_free(pool, handler->stderr_tmp);
546 }
547 handler->stderr_tmp = pool_malloc(pool, handler->stderr_tmpalloc);
548 if(!handler->stderr_tmp) {
549 log_ereport(
LOG_FAILURE,
"send-cgi: cannot create tmp buffer for parsing stderr");
550 handler->stderr_tmpalloc =
0;
551 handler->stderr_tmplen =
0;
552 continue;
553 }
554 }
555 memcpy(handler->stderr_tmp, line + line_start, tmplen);
556 handler->stderr_tmplen = tmplen;
557 }
558 }
else {
559 handler->stderr_tmplen =
0;
560 }
561 }
562
563
564 if(r <
0 && errno ==
EAGAIN) {
565 return 1;
566 }
567
568 if(handler->stderr_tmp) {
569 pool_free(handler->parser->sn->pool, handler->stderr_tmp);
570 }
571 return 0;
572 }
573
574 int cgi_event_finish(EventHandler *ev, Event *event) {
575 CGIHandler *handler = event->cookie;
576 CGIResponseParser *parser = handler->parser;
577 Session *sn = parser->sn;
578 Request *rq = parser->rq;
579
580 char *event_fn =
"";
581 if(event->fn == cgi_stdout_readevent) {
582 event_fn =
"stdout";
583 }
else if(event->fn == cgi_stderr_readevent) {
584 event_fn =
"stderr";
585 }
else if(event->fn == cgi_writeevent) {
586 event_fn =
"httpout";
587 log_ereport(
LOG_DEBUG,
"cgi-send: req: %p finish: pid: %d", rq, handler->process.pid);
588 }
589 log_ereport(
LOG_DEBUG,
"cgi-send: req: %p finish: event: %d pollout: %d wait_read: %d cgi_eof: %d fn: %s", rq, handler->events, handler->poll_out, handler->wait_read, handler->cgi_eof, event_fn);
590
591 handler->debug_finished =
TRUE;
592 if(event->fn != cgi_stderr_readevent) {
593 log_ereport(
LOG_DEBUG,
"cgi-send: req: %p finish set cgi_eof: %s", rq, event_fn);
594 handler->cgi_eof =
TRUE;
595 }
596
597 if(handler->result ==
REQ_ABORTED && handler->process.pid !=
0 && handler->cgi_kill ==
0) {
598 log_ereport(
LOG_FAILURE,
"cgi-send: kill script: %s pid: %d", handler->path, (
int)handler->process.pid);
599 if(kill(handler->process.pid,
SIGTERM)) {
600 log_ereport(
LOG_FAILURE,
"cgi-send: pid: %d kill failed: %s", (
int)handler->process.pid, strerror(errno));
601 }
else {
602 log_ereport(
LOG_DEBUG,
"cgi-send: finish: req: %p kill %d successful", rq, (
int)handler->process.pid);
603 handler->cgi_kill =
SIGTERM;
604 }
605 }
606
607 if(--handler->events >
0) {
608 return 0;
609 }
610
611 if(handler->poll_out) {
612
613
614 log_ereport(
LOG_DEBUG,
"cgi-send: req: %p finish: remove-poll write", rq);
615 if(event_removepoll(ev, sn->csd)) {
616 log_ereport(
LOG_FAILURE,
"cgi_event_finish: event_removepoll: %s", strerror(errno));
617 }
618 handler->poll_out =
FALSE;
619 }
620
621 if(handler->wait_read) {
622
623
624 log_ereport(
LOG_DEBUG,
"cgi-send: req: %p finish: remove-poll read", rq);
625 if(ev_remove_poll(ev, handler->process.out[
0])) {
626 log_ereport(
LOG_FAILURE,
"cgi_event_finish: req: %p ev_remove_poll: %s", rq, strerror(errno));
627 }
628 handler->wait_read =
FALSE;
629 }
630
631 log_ereport(
LOG_DEBUG,
"cgi-send: req: %p cgi_close", rq);
632
633 int exit_code = cgi_close(&handler->process);
634 if(exit_code !=
0) {
635 log_ereport(
LOG_FAILURE,
"send-cgi: script: %s exited with code %d", handler->path, exit_code);
636 handler->result =
REQ_ABORTED;
637 }
638
639 cgi_parser_free(parser);
640
641 WSBool response_length_error =
FALSE;
642
643
644
645 char *ctlen_header = pblock_findkeyval(pb_key_content_length, rq->srvhdrs);
646 if(ctlen_header) {
647 int64_t ctlenhdr;
648 if(util_strtoint(ctlen_header, &ctlenhdr)) {
649 if(ctlenhdr != parser->response_length) {
650 log_ereport(
651 LOG_FAILURE,
652 "cgi-send: script: %s: content length mismatch",
653 handler->path);
654 response_length_error =
TRUE;
655 }
656 }
657 }
658
659
660 if(handler->result !=
REQ_ABORTED && handler->parser->response_length != handler->count_write) {
661 log_ereport(
662 LOG_FAILURE,
663 "cgi-send: script: %s: IO error: cgi response length != http response length",
664 handler->path);
665 response_length_error =
TRUE;
666 }
667
668
669 if(response_length_error) {
670 rq->rq_attr.keep_alive =
0;
671 handler->result =
REQ_ABORTED;
672 }
673
674 net_setnonblock(sn->csd,
0);
675
676
677 log_ereport(
LOG_DEBUG,
"cgi-send: req: %p event-finish nsapi return", rq);
678 nsapi_function_return(sn, rq, handler->result);
679 return 0;
680 }
681
682 int cgi_start(CGIProcess *p,
char *path,
char *
const argv[],
char *
const envp[]) {
683 if(pipe(p->in) || pipe(p->out) || pipe(p->err)) {
684 log_ereport(
685 LOG_FAILURE,
686 "send-cgi: cannot create pipe: %s",
687 strerror(errno));
688 return REQ_ABORTED;
689 }
690
691 p->pid = fork();
692 if(p->pid ==
0) {
693
694
695
696 cxstring script = cx_str(path);
697 cxmutstr parent;
698 int len = strlen(path);
699 for(
int i=len-
1;i>=
0;i--) {
700 if(path[i] ==
'/') {
701 script = cx_strn(path + i +
1, len - i);
702 parent = cx_strdup(cx_strn(path, i));
703 if(chdir(parent.ptr)) {
704 perror(
"cgi_start: chdir");
705 free(parent.ptr);
706 exit(-
1);
707 }
708 free(parent.ptr);
709 break;
710 }
711 }
712
713 if(dup2(p->in[
0],
STDIN_FILENO) == -
1) {
714 perror(
"cgi_start: dup2");
715 exit(
EXIT_FAILURE);
716 }
717 if(dup2(p->out[
1],
STDOUT_FILENO) == -
1) {
718 perror(
"cgi_start: dup2");
719 exit(
EXIT_FAILURE);
720 }
721 if(dup2(p->err[
1],
STDERR_FILENO) == -
1) {
722 perror(
"cgi_start: dup2");
723 exit(
EXIT_FAILURE);
724 }
725
726
727
728 system_close(p->in[
1]);
729
730
731 exit(execve(script.ptr, argv, envp));
732 }
else {
733
734 system_close(p->out[
1]);
735 system_close(p->err[
1]);
736 p->out[
1] = -
1;
737 p->err[
1] = -
1;
738 }
739
740 return REQ_PROCEED;
741 }
742
743 int cgi_close(CGIProcess *p) {
744 if(p->in[
0] != -
1) {
745 system_close(p->in[
0]);
746 }
747 if(p->in[
1] != -
1) {
748 system_close(p->in[
1]);
749 }
750 if(p->out[
0] != -
1) {
751 system_close(p->out[
0]);
752 }
753 if(p->out[
1] != -
1) {
754 system_close(p->out[
1]);
755 }
756 if(p->err[
0] != -
1) {
757 system_close(p->err[
0]);
758 }
759 if(p->err[
1] != -
1) {
760 system_close(p->err[
1]);
761 }
762
763
764
765
766 int status = -
1;
767 if(waitpid(p->pid, &status,
WNOHANG) ==
0) {
768 log_ereport(
LOG_DEBUG,
"cgi_close: waitpid returned 0: pid: %d", (
int)p->pid);
769
770
771 sleep(
1);
772 if(waitpid(p->pid, &status,
WNOHANG) ==
0) {
773 log_ereport(
LOG_DEBUG,
"cgi_close: waitpid returned 0 again: pid: %d", (
int)p->pid);
774 }
775 }
776
777 return status;
778 }
779
780 CGIResponseParser* cgi_parser_new(Session *sn, Request *rq) {
781 CGIResponseParser* parser = pool_malloc(sn->pool,
sizeof(CGIResponseParser));
782 parser->sn = sn;
783 parser->rq = rq;
784 parser->status =
0;
785 parser->msg =
NULL;
786 parser->response_length =
0;
787 parser->cgiheader =
TRUE;
788 cxBufferInit(&parser->tmp,
NULL,
64, pool_allocator(sn->pool),
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
789 return parser;
790 }
791
792 void cgi_parser_free(CGIResponseParser *parser) {
793 if(parser->tmp.space) {
794 cxBufferDestroy(&parser->tmp);
795 }
796 pool_free(parser->sn->pool, parser);
797 }
798
799
800
801
802
803
804
805
806 static int parse_lines(CGIResponseParser *parser,
char *buf,
size_t len,
int *pos) {
807 CxAllocator *a = pool_allocator(parser->sn->pool);
808 cxmutstr name;
809 cxmutstr value;
810 WSBool space =
TRUE;
811 int i;
812
813 int line_begin =
0;
814 int value_begin =
0;
815 for(i=
0;i<len;i++) {
816 char c = buf[i];
817 if(value_begin == line_begin && c ==
':') {
818 name = cx_mutstrn(buf + line_begin, i - line_begin);
819 value_begin = i +
1;
820 }
else if(c ==
'\n') {
821 if(value_begin == line_begin) {
822 if(space) {
823 *pos = i +
1;
824 return 2;
825 }
else {
826
827 return -
1;
828 }
829 }
830 value = cx_mutstrn(buf + value_begin, i - value_begin);
831
832 cx_strlower(name);
833 name = cx_strdup_a(a, cx_strtrim((cxstring){name.ptr, name.length}));
834 value = cx_strtrim_m(value);
835
836 if(name.length ==
0 || value.length ==
0) {
837 return -
1;
838 }
839
840 if(!cx_strcmp((cxstring){name.ptr, name.length}, (cxstring)
CX_STR(
"status"))) {
841 cxmutstr codestr = value;
842 int j;
843 for(j=
0;j<codestr.length;j++) {
844 if(!isdigit(codestr.ptr[j])) {
845 break;
846 }
847 if(j >
2) {
848 break;
849 }
850 }
851 codestr.ptr[j] =
'\0';
852
853 int64_t s =
0;
854 util_strtoint(codestr.ptr, &s);
855 parser->status = (
int)s;
856
857 cxmutstr msg = cx_strtrim_m(cx_strsubs_m(value, j +
1));
858
859 if(msg.length >
0) {
860 parser->msg = cx_strdup_pool(parser->sn->pool, msg).ptr;
861 }
862 }
else {
863 pblock_nvlinsert(
864 name.ptr,
865 name.length,
866 value.ptr,
867 value.length,
868 parser->rq->srvhdrs);
869 }
870
871 line_begin = i+
1;
872 value_begin = line_begin;
873 space =
TRUE;
874 }
else if(!isspace(c)) {
875 space =
FALSE;
876 }
877 }
878
879 if(i < len) {
880 *pos = i;
881 return 0;
882 }
883 return 1;
884 }
885
886
887
888
889
890
891 int cgi_parse_response(CGIResponseParser *parser,
char *buf,
size_t len,
size_t *bpos) {
892 *bpos =
0;
893 int pos =
0;
894 if(parser->tmp.pos >
0) {
895
896
897 WSBool nb =
FALSE;
898 for(pos=
0;pos<len;pos++) {
899 if(buf[pos] ==
'\n') {
900 nb =
TRUE;
901 break;
902 }
903 }
904 cxBufferWrite(buf,
1, pos, &parser->tmp);
905
906 if(nb) {
907
908 int npos;
909 int r = parse_lines(parser, parser->tmp.space, parser->tmp.pos, &npos);
910 switch(r) {
911 case -
1:
return -
1;
912 case 0:
return -
1;
913 case 1:
break;
914 case 2: {
915 *bpos = pos +
1;
916 return 1;
917 }
918 }
919
920 parser->tmp.pos =
0;
921 }
else {
922 if(parser->tmp.pos >
CGI_RESPONSE_MAX_LINE_LENGTH) {
923 return -
1;
924 }
925 }
926 }
927
928 int npos =
0;
929 int r = parse_lines(parser, buf + pos, len - pos, &npos);
930 switch(r) {
931 default:
return -
1;
932 case 0:
933 case 1: {
934 int newlen = len - npos;
935 if(npos >
0) {
936 cxBufferWrite(buf + npos,
1, newlen, &parser->tmp);
937 }
938 return 0;
939 }
940 case 2: {
941 *bpos = pos + npos;
942 return 1;
943 }
944 }
945 }
946