1 /* $Id$ */
2 /*
3 * Copyright (c) 2015--2016, 2018 Kristaps Dzonsons <kristaps@bsd.lv>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17 #include "config.h"
18
19 #include <sys/ioctl.h>
20 #include <sys/socket.h>
21
22 #include <assert.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <inttypes.h>
26 #include <limits.h>
27 #include <poll.h>
28 #include <signal.h>
29 #include <stdarg.h>
30 #include <stdint.h>
31 #include <stdlib.h>
32 #include <stdio.h> /* BUFSIZ */
33 #include <string.h>
34 #include <unistd.h>
35
36 #include "kcgi.h"
37 #include "extern.h"
38
39 struct kfcgi {
40 const struct kvalid *keys;
41 size_t keysz;
42 const char *const *mimes;
43 size_t mimesz;
44 size_t defmime;
45 unsigned int debugging;
46 const char *const *pages;
47 size_t pagesz;
48 size_t defpage;
49 const struct kmimemap *mimemap;
50 pid_t work_pid;
51 pid_t sock_pid;
52 int work_dat;
53 int sock_ctl;
54 struct kopts opts;
55 void *arg;
56 };
57
58 static volatile sig_atomic_t sig = 0;
59
60 static void
dosignal(int arg)61 dosignal(int arg)
62 {
63
64 sig = 1;
65 }
66
67 /*
68 * This is our control process.
69 * It listens for FastCGI connections on the manager connection in
70 * traditional mode ("fdaccept") xor extended mode ("fdfiled").
71 * When it has one, it reads and passes to the worker (sibling) process,
72 * which will be parsing the data.
73 * When the worker has finished, it passes back the request identifier,
74 * which this passes to the main application for output.
75 * If the current FastCGI connection closes, abandon it and wait for the
76 * next.
77 * This exits with the manager connection closes.
78 * On exit, it will close the fdaccept or fdfiled descriptor.
79 */
80 static int
kfcgi_control(int work,int ctrl,int fdaccept,int fdfiled,pid_t worker)81 kfcgi_control(int work, int ctrl,
82 int fdaccept, int fdfiled, pid_t worker)
83 {
84 struct sockaddr_storage ss;
85 socklen_t sslen;
86 int fd = -1, rc, ourfd, erc = EXIT_FAILURE;
87 uint64_t magic;
88 uint32_t cookie, test;
89 struct pollfd pfd[2];
90 char buf[BUFSIZ];
91 ssize_t ssz;
92 enum kcgi_err kerr;
93 uint16_t rid, rtest;
94
95 ourfd = fdaccept == -1 ? fdfiled : fdaccept;
96 assert(ourfd != -1);
97
98 if (kxsocketprep(ourfd) != KCGI_OK)
99 goto out;
100
101 for (;;) {
102 pfd[0].fd = ourfd;
103 pfd[0].events = POLLIN;
104 pfd[0].revents = 0;
105 pfd[1].fd = ctrl;
106 pfd[1].events = POLLIN;
107 pfd[1].revents = 0;
108
109 /*
110 * If either the worker or manager disconnect, then exit
111 * cleanly.
112 * The calling application will check the worker's exit
113 * code, which will say whether it did something bad, so
114 * we don't really care.
115 */
116
117 if ((rc = poll(pfd, 2, INFTIM)) < 0) {
118 kutil_warn(NULL, NULL, "poll");
119 goto out;
120 } else if (rc == 0) {
121 kutil_warnx(NULL, NULL, "poll: timeout!?");
122 continue;
123 }
124
125 if ((pfd[1].revents & POLLHUP) ||
126 !(pfd[0].revents & POLLIN))
127 break;
128
129 /*
130 * Accept a new connection.
131 * In the old way, a blocking accept from FastCGI
132 * socket.
133 * This will be round-robined by the kernel so that
134 * other control processes are fairly notified.
135 * In the new one, accepting a descriptor from the
136 * caller.
137 */
138
139 if (fdaccept != -1) {
140 assert(fdfiled == -1);
141 sslen = sizeof(ss);
142 fd = accept(fdaccept,
143 (struct sockaddr *)&ss, &sslen);
144 if (fd < 0) {
145 if (errno == EAGAIN ||
146 errno == EWOULDBLOCK)
147 continue;
148 kutil_warn(NULL, NULL, "accept");
149 goto out;
150 }
151 } else {
152 assert(fdfiled != -1);
153 rc = fullreadfd(fdfiled,
154 &fd, &magic, sizeof(uint64_t));
155 if (rc < 0)
156 goto out;
157 else if (rc == 0)
158 break;
159 }
160
161 /*
162 * We then set that the FastCGI socket is non-blocking,
163 * making it consistent with the behaviour of the CGI
164 * socket, which is also set as such.
165 */
166
167 if (kxsocketprep(fd) != KCGI_OK)
168 goto out;
169
170 /* This doesn't need to be crypto quality. */
171
172 #if HAVE_ARC4RANDOM
173 cookie = arc4random();
174 #else
175 cookie = random();
176 #endif
177
178 /* Write a header cookie to the work. */
179
180 fullwrite(work, &cookie, sizeof(uint32_t));
181
182 /*
183 * Keep pushing data into the worker til it has read it
184 * all, at which point it will write to us.
185 * If at any point we have errors (e.g., the connection
186 * closes), then write a zero-length frame.
187 * Write a zero-length frame at the end anyway.
188 */
189
190 pfd[0].fd = fd;
191 pfd[0].events = POLLIN;
192 pfd[1].fd = work;
193 pfd[1].events = POLLIN;
194
195 for (;;) {
196 if ((rc = poll(pfd, 2, INFTIM)) < 0) {
197 kutil_warn(NULL, NULL, "poll");
198 goto out;
199 } else if (rc == 0) {
200 kutil_warnx(NULL, NULL, "poll: timeout!?");
201 continue;
202 }
203
204 /*
205 * If the child responds, that means that the
206 * full request has been read and processed.
207 * If not, we probably still have data to write
208 * to it from the connection.
209 */
210
211 if (pfd[1].revents & POLLIN) {
212 ssz = 0;
213 kerr = fullwritenoerr
214 (pfd[1].fd, &ssz, sizeof(size_t));
215 if (kerr != KCGI_OK)
216 goto out;
217 break;
218 }
219
220 if (!(pfd[0].revents & POLLIN)) {
221 kutil_warnx(NULL, NULL, "poll: no input");
222 goto out;
223 } else if ((ssz = read(fd, buf, BUFSIZ)) < 0) {
224 kutil_warn(NULL, NULL, "read");
225 goto out;
226 }
227
228 /*
229 * Send the child the amount of data we've read.
230 * This will let the child see if the connection
231 * abruptly closes, at which point we'll have a
232 * read size of zero, and error out.
233 */
234
235 kerr = fullwritenoerr
236 (pfd[1].fd, &ssz, sizeof(size_t));
237 if (KCGI_OK != kerr)
238 goto out;
239
240 kerr = fullwritenoerr(pfd[1].fd, buf, ssz);
241 if (KCGI_OK != kerr)
242 goto out;
243
244 /*
245 * If we wrote a zero-sized buffer, it means
246 * that the connection has unexpectedly closed.
247 * The child will stop all processing for the
248 * request and will not return to the parsing
249 * routine for the given session.
250 */
251
252 if (ssz == 0) {
253 kutil_warnx(NULL, NULL, "read: "
254 "connection closed");
255 break;
256 }
257 }
258
259 /* Now verify that the worker is sane. */
260
261 if (fullread(pfd[1].fd, &rc,
262 sizeof(int), 0, &kerr) < 0)
263 goto out;
264
265 if (rc == 0) {
266 kutil_warnx(NULL, NULL, "FastCGI: bad code");
267 goto recover;
268 }
269
270 /*
271 * We have a non-zero return code.
272 * Check our cookie and responseId values.
273 */
274
275 if (fullread(pfd[1].fd, &test,
276 sizeof(uint32_t), 0, &kerr) < 0)
277 goto out;
278
279 if (cookie != test) {
280 kutil_warnx(NULL, NULL, "FastCGI: bad cookie");
281 goto out;
282 }
283
284 if (fullread(pfd[1].fd, &rid,
285 sizeof(uint16_t), 0, &kerr) < 0)
286 goto out;
287
288 /*
289 * Pass the file descriptor, which has had its data
290 * sucked dry, to the main application.
291 * It will do output, so it also needs the FastCGI
292 * socket request identifier.
293 */
294
295 if (!fullwritefd(ctrl, fd, &rid, sizeof(uint16_t)))
296 goto out;
297
298 /*
299 * This will wait til the application is finished.
300 * It will then double-check the requestId.
301 */
302
303 if (fullread(ctrl, &rtest,
304 sizeof(uint16_t), 0, &kerr) < 0)
305 goto out;
306
307 if (rid != rtest) {
308 kutil_warnx(NULL, NULL, "FastCGI: bad cookie");
309 goto out;
310 }
311
312 recover:
313 /*
314 * If we are being passed descriptors (instead of
315 * waiting on the accept()), then notify the manager
316 * that we've finished processing this request.
317 * We also jump to here if the connection fails in any
318 * way whilst being transcribed to the worker.
319 */
320
321 if (fdfiled != -1) {
322 kerr = fullwritenoerr(fdfiled,
323 &magic, sizeof(uint64_t));
324 if (KCGI_OK != kerr)
325 goto out;
326 }
327
328 close(fd);
329 fd = -1;
330 }
331
332 erc = EXIT_SUCCESS;
333 out:
334 if (fd != -1)
335 close(fd);
336 close(ourfd);
337 return erc;
338 }
339
340 void
khttp_fcgi_child_free(struct kfcgi * fcgi)341 khttp_fcgi_child_free(struct kfcgi *fcgi)
342 {
343
344 close(fcgi->sock_ctl);
345 close(fcgi->work_dat);
346 free(fcgi);
347 }
348
349 enum kcgi_err
khttp_fcgi_free(struct kfcgi * fcgi)350 khttp_fcgi_free(struct kfcgi *fcgi)
351 {
352
353 if (fcgi == NULL)
354 return KCGI_OK;
355
356 close(fcgi->sock_ctl);
357 close(fcgi->work_dat);
358 kxwaitpid(fcgi->work_pid);
359 kxwaitpid(fcgi->sock_pid);
360 free(fcgi);
361 return KCGI_OK;
362 }
363
364 enum kcgi_err
khttp_fcgi_initx(struct kfcgi ** fcgip,const char * const * mimes,size_t mimesz,const struct kvalid * keys,size_t keysz,const struct kmimemap * mimemap,size_t defmime,const char * const * pages,size_t pagesz,size_t defpage,void * arg,void (* argfree)(void *),unsigned int debugging,const struct kopts * opts)365 khttp_fcgi_initx(struct kfcgi **fcgip,
366 const char *const *mimes, size_t mimesz,
367 const struct kvalid *keys, size_t keysz,
368 const struct kmimemap *mimemap, size_t defmime,
369 const char *const *pages, size_t pagesz,
370 size_t defpage, void *arg, void (*argfree)(void *),
371 unsigned int debugging, const struct kopts *opts)
372 {
373 struct kfcgi *fcgi;
374 int er, fdaccept, fdfiled;
375 int work_ctl[2], work_dat[2], sock_ctl[2];
376 pid_t work_pid, sock_pid;
377 const char *cp, *ercp;
378 sigset_t mask;
379 enum sandtype st;
380
381 /*
382 * Determine whether we're supposed to accept() on a socket or,
383 * rather, we're supposed to receive file descriptors from a
384 * kfcgi-like manager.
385 */
386
387 st = SAND_CONTROL_OLD;
388 fdaccept = fdfiled = -1;
389
390 if ((cp = getenv("FCGI_LISTENSOCK_DESCRIPTORS")) != NULL) {
391 fdfiled = strtonum(cp, 0, INT_MAX, &ercp);
392 if (ercp != NULL) {
393 fdaccept = STDIN_FILENO;
394 fdfiled = -1;
395 } else
396 st = SAND_CONTROL_NEW;
397 } else
398 fdaccept = STDIN_FILENO;
399
400 /*
401 * Block this signal unless we're right at the fullreadfd
402 * function, at which point unblock and let it interrupt us.
403 * We don't save the signal mask because we're allowed free
404 * reign on the SIGTERM value.
405 */
406
407 if (signal(SIGTERM, dosignal) == SIG_ERR) {
408 kutil_warn(NULL, NULL, "signal");
409 return KCGI_SYSTEM;
410 }
411
412 sigemptyset(&mask);
413 sigaddset(&mask, SIGTERM);
414 sigprocmask(SIG_BLOCK, &mask, NULL);
415 sig = 0;
416
417 if (kxsocketpair(work_ctl) != KCGI_OK)
418 return KCGI_SYSTEM;
419
420 if (kxsocketpair(work_dat) != KCGI_OK) {
421 close(work_ctl[KWORKER_PARENT]);
422 close(work_ctl[KWORKER_CHILD]);
423 return KCGI_SYSTEM;
424 }
425
426 if ((work_pid = fork()) == -1) {
427 er = errno;
428 kutil_warn(NULL, NULL, "fork");
429 close(work_ctl[KWORKER_PARENT]);
430 close(work_ctl[KWORKER_CHILD]);
431 close(work_dat[KWORKER_PARENT]);
432 close(work_dat[KWORKER_CHILD]);
433 return (er == EAGAIN) ? KCGI_EAGAIN : KCGI_ENOMEM;
434 } else if (work_pid == 0) {
435 if (signal(SIGTERM, SIG_IGN) == SIG_ERR) {
436 kutil_warn(NULL, NULL, "signal");
437 _exit(EXIT_FAILURE);
438 }
439
440 if (argfree != NULL)
441 argfree(arg);
442
443 /*
444 * STDIN_FILENO isn't really stdin, it's the control
445 * socket used to pass input sockets to us.
446 * Thus, close the parent's control socket.
447 */
448
449 if (fdaccept != -1)
450 close(fdaccept);
451 if (fdfiled != -1)
452 close(fdfiled);
453
454 close(STDOUT_FILENO);
455 close(work_dat[KWORKER_PARENT]);
456 close(work_ctl[KWORKER_PARENT]);
457
458 /*
459 * Prep child sandbox and run worker process.
460 * The worker code will exit on failure, so no need to
461 * check any return codes on it.
462 */
463
464 er = EXIT_SUCCESS;
465 if (!ksandbox_init_child(SAND_WORKER,
466 work_dat[KWORKER_CHILD],
467 work_ctl[KWORKER_CHILD], -1, -1))
468 er = EXIT_FAILURE;
469 else
470 kworker_fcgi_child
471 (work_dat[KWORKER_CHILD],
472 work_ctl[KWORKER_CHILD],
473 keys, keysz, mimes, mimesz,
474 debugging);
475
476 close(work_dat[KWORKER_CHILD]);
477 close(work_ctl[KWORKER_CHILD]);
478 _exit(er);
479 /* NOTREACHED */
480 }
481
482 close(work_dat[KWORKER_CHILD]);
483 close(work_ctl[KWORKER_CHILD]);
484
485 if (kxsocketpair(sock_ctl) != KCGI_OK) {
486 close(work_dat[KWORKER_PARENT]);
487 close(work_ctl[KWORKER_PARENT]);
488 kxwaitpid(work_pid);
489 return KCGI_SYSTEM;
490 }
491
492 if ((sock_pid = fork()) == -1) {
493 er = errno;
494 kutil_warn(NULL, NULL, "fork");
495 close(work_dat[KWORKER_PARENT]);
496 close(work_ctl[KWORKER_PARENT]);
497 close(sock_ctl[KWORKER_CHILD]);
498 close(sock_ctl[KWORKER_PARENT]);
499 kxwaitpid(work_pid);
500 return (er == EAGAIN) ? KCGI_EAGAIN : KCGI_ENOMEM;
501 } else if (sock_pid == 0) {
502 if (signal(SIGTERM, SIG_IGN) == SIG_ERR) {
503 kutil_warn(NULL, NULL, "signal");
504 _exit(EXIT_FAILURE);
505 }
506
507 if (argfree != NULL)
508 argfree(arg);
509
510 close(STDOUT_FILENO);
511 close(work_dat[KWORKER_PARENT]);
512 close(sock_ctl[KWORKER_PARENT]);
513
514 if (!ksandbox_init_child(st,
515 sock_ctl[KWORKER_CHILD], -1, fdfiled, fdaccept))
516 er = EXIT_FAILURE;
517 else
518 er = kfcgi_control
519 (work_ctl[KWORKER_PARENT],
520 sock_ctl[KWORKER_CHILD],
521 fdaccept, fdfiled, work_pid);
522
523 close(work_ctl[KWORKER_PARENT]);
524 close(sock_ctl[KWORKER_CHILD]);
525 _exit(er);
526 /* NOTREACHED */
527 }
528
529 close(sock_ctl[KWORKER_CHILD]);
530 close(work_ctl[KWORKER_PARENT]);
531
532 if (fdaccept != -1)
533 close(fdaccept);
534 if (fdfiled != -1)
535 close(fdfiled);
536
537 /* Now allocate our device. */
538
539 *fcgip = fcgi = kxcalloc(1, sizeof(struct kfcgi));
540 if (fcgi == NULL) {
541 close(sock_ctl[KWORKER_PARENT]);
542 close(work_dat[KWORKER_PARENT]);
543 kxwaitpid(work_pid);
544 kxwaitpid(sock_pid);
545 return KCGI_ENOMEM;
546 }
547
548 if (opts == NULL)
549 fcgi->opts.sndbufsz = -1;
550 else
551 memcpy(&fcgi->opts, opts, sizeof(struct kopts));
552
553 if (fcgi->opts.sndbufsz < 0)
554 fcgi->opts.sndbufsz = UINT16_MAX;
555
556 fcgi->work_pid = work_pid;
557 fcgi->work_dat = work_dat[KWORKER_PARENT];
558 fcgi->sock_pid = sock_pid;
559 fcgi->sock_ctl = sock_ctl[KWORKER_PARENT];
560 fcgi->arg = arg;
561 fcgi->mimes = mimes;
562 fcgi->mimesz = mimesz;
563 fcgi->defmime = defmime;
564 fcgi->keys = keys;
565 fcgi->keysz = keysz;
566 fcgi->mimemap = mimemap;
567 fcgi->pages = pages;
568 fcgi->pagesz = pagesz;
569 fcgi->defpage = defpage;
570 fcgi->debugging = debugging;
571 return KCGI_OK;
572 }
573
574 enum kcgi_err
khttp_fcgi_init(struct kfcgi ** fcgi,const struct kvalid * keys,size_t keysz,const char * const * pages,size_t pagesz,size_t defpage)575 khttp_fcgi_init(struct kfcgi **fcgi,
576 const struct kvalid *keys, size_t keysz,
577 const char *const *pages, size_t pagesz,
578 size_t defpage)
579 {
580
581 return khttp_fcgi_initx(fcgi, kmimetypes,
582 KMIME__MAX, keys, keysz, ksuffixmap,
583 KMIME_TEXT_HTML, pages, pagesz, defpage,
584 NULL, NULL, 0, NULL);
585 }
586
587 /*
588 * Here we wait for the next FastCGI connection in such a way that, if
589 * we're notified that we must exit via a SIGTERM, we'll properly close
590 * down without spurious warnings.
591 */
592 static enum kcgi_err
fcgi_waitread(int fd)593 fcgi_waitread(int fd)
594 {
595 int rc;
596 struct pollfd pfd;
597 sigset_t mask;
598
599 again:
600 pfd.fd = fd;
601 pfd.events = POLLIN;
602
603 /*
604 * Unblock SIGTERM around the poll().
605 * XXX: this could be drastically simplified with ppoll(), but
606 * it's not available on many systems.
607 * TODO: provide a shim in wrappers.c.
608 */
609
610 sigemptyset(&mask);
611 sigaddset(&mask, SIGTERM);
612 sigprocmask(SIG_UNBLOCK, &mask, NULL);
613 rc = poll(&pfd, 1, 1000);
614 sigprocmask(SIG_BLOCK, &mask, NULL);
615
616 /* Exit signal has been set. */
617
618 if (sig)
619 return KCGI_EXIT;
620
621 /* Problems? Exit. Timeout? Retry. */
622
623 if (rc < 0) {
624 kutil_warn(NULL, NULL, "poll");
625 return KCGI_SYSTEM;
626 } else if (rc == 0)
627 goto again;
628
629 /* Only POLLIN is a "good" exit from this. */
630
631 if (pfd.revents & POLLIN)
632 return KCGI_OK;
633 else if (pfd.revents & POLLHUP)
634 return KCGI_EXIT;
635
636 kutil_warnx(NULL, NULL, "poll: error");
637 return KCGI_SYSTEM;
638 }
639
640 enum kcgi_err
khttp_fcgi_parse(struct kfcgi * fcgi,struct kreq * req)641 khttp_fcgi_parse(struct kfcgi *fcgi, struct kreq *req)
642 {
643 enum kcgi_err kerr;
644 const struct kmimemap *mm;
645 int c, fd = -1;
646 uint16_t rid;
647
648 memset(req, 0, sizeof(struct kreq));
649
650 /*
651 * Blocking wait until our control process sends us the file
652 * descriptor and requestId of the current sequence.
653 * It may also decide to exit, which we note by seeing that
654 * "sig" has been set (inheriting the SIGTERM) or the channel
655 * closed gracefully.
656 */
657
658 if ((kerr = fcgi_waitread(fcgi->sock_ctl)) != KCGI_OK)
659 return kerr;
660
661 c = fullreadfd(fcgi->sock_ctl, &fd, &rid, sizeof(uint16_t));
662 if (c < 0)
663 return KCGI_SYSTEM;
664 else if (c == 0)
665 return KCGI_EXIT;
666
667 /* Now get ready to receive data from the child. */
668
669 req->arg = fcgi->arg;
670 req->keys = fcgi->keys;
671 req->keysz = fcgi->keysz;
672 req->kdata = kdata_alloc(fcgi->sock_ctl,
673 fd, rid, fcgi->debugging, &fcgi->opts);
674
675 if (req->kdata == NULL) {
676 close(fd);
677 goto err;
678 }
679
680 if (fcgi->keysz) {
681 req->cookiemap = kxcalloc
682 (fcgi->keysz, sizeof(struct kpair *));
683 if (req->cookiemap == NULL)
684 goto err;
685 req->cookienmap = kxcalloc
686 (fcgi->keysz, sizeof(struct kpair *));
687 if (req->cookienmap == NULL)
688 goto err;
689 req->fieldmap = kxcalloc
690 (fcgi->keysz, sizeof(struct kpair *));
691 if (req->fieldmap == NULL)
692 goto err;
693 req->fieldnmap = kxcalloc
694 (fcgi->keysz, sizeof(struct kpair *));
695 if (req->fieldnmap == NULL)
696 goto err;
697 }
698
699 /*
700 * Read the request itself from the worker child.
701 * We'll wait perpetually on data until the channel closes or
702 * until we're interrupted during a read by the parent.
703 */
704
705 kerr = kworker_parent(fcgi->work_dat, req, 0, fcgi->mimesz);
706 if (KCGI_OK != kerr)
707 goto err;
708
709 /* Look up page type from component. */
710
711 req->page = fcgi->defpage;
712 if (*req->pagename != '\0')
713 for (req->page = 0; req->page < fcgi->pagesz; req->page++)
714 if (strcasecmp(fcgi->pages[req->page], req->pagename) == 0)
715 break;
716
717 /* Start with the default. */
718
719 req->mime = fcgi->defmime;
720 if (*req->suffix != '\0') {
721 for (mm = fcgi->mimemap; NULL != mm->name; mm++)
722 if (strcasecmp(mm->name, req->suffix) == 0) {
723 req->mime = mm->mime;
724 break;
725 }
726 /* Could not find this mime type! */
727 if (mm->name == NULL)
728 req->mime = fcgi->mimesz;
729 }
730
731 return kerr;
732 err:
733 kdata_free(req->kdata, 0);
734 req->kdata = NULL;
735 kreq_free(req);
736 return kerr;
737 }
738
739 int
khttp_fcgi_test(void)740 khttp_fcgi_test(void)
741 {
742 socklen_t len = 0;
743 const char *cp, *ercp = NULL;
744
745 if ((cp = getenv("FCGI_LISTENSOCK_DESCRIPTORS")) != NULL) {
746 strtonum(cp, 0, INT_MAX, &ercp);
747 if (ercp == NULL)
748 return 1;
749 }
750
751 if (getpeername(STDIN_FILENO, NULL, &len) != -1)
752 return 0;
753 return errno == ENOTCONN;
754 }
755