content-type support
[lnanohttp.git] / lnanohttp.c
blob8116863d0355865244134a8575ef97a49d838235
1 /* this code is protected by the GNU affero GPLv3
2 author:Sylvain BERTRAND <sylvain.bertrand AT gmail dot com>
3 <sylware AT legeek dot net> */
4 /*----------------------------------------------------------------------------*/
5 /* compiler stuff */
6 #include <stdarg.h>
7 /*----------------------------------------------------------------------------*/
9 /*----------------------------------------------------------------------------*/
10 /* ulinux stuff */
11 #include <ulinux/compiler_types.h>
12 #include <ulinux/types.h>
13 #include <ulinux/sysc.h>
15 #include <ulinux/file.h>
16 #include <ulinux/socket/socket.h>
17 #include <ulinux/socket/in.h>
18 #include <ulinux/signal/signal.h>
19 #include <ulinux/error.h>
20 #include <ulinux/epoll.h>
21 #include <ulinux/utils/mem.h>
22 #include <ulinux/utils/endian.h>
23 #include <ulinux/mmap.h>
24 #include <ulinux/time.h>
25 #include <ulinux/select.h>
26 #include <ulinux/stat.h>
28 #include <ulinux/utils/ascii/string/vsprintf.h>
30 #include "ulinux_namespace.h"
31 #include "exit_codes.h"
32 /*----------------------------------------------------------------------------*/
34 /******************************************************************************/
35 /* configuration */
36 /* 16 bits value for the port (below 1024, must be root, that you must be for
37 chroot anyway) */
38 #define LISTENING_PORT 80
39 /* 32 bits value for the IPv4, can be INADDR_ANY */
40 #define LISTENING_IPV4 INADDR_ANY
41 /* the chroot patch used upon start */
42 #define CHROOT_PATH "/root/http/chroot"
43 /* time out for a socket read/write, in seconds. 4 secs is huge */
44 #define CNX_WAIT_TIMEOUT 4
45 /* default file */
46 #define DEFAULT_FILE (u8*)"/index.xhtml"
47 #define DEFAULT_FILE_MIME (u8*)"/index.xhtml.mime"
49 #define RESP_HDR_FMT (u8*)"\
50 HTTP/1.1 200 \r\n\
51 content-length:%u\r\n\r\n"
53 #define RESP_HDR_CONTENT_TYPE_FMT (u8*)"\
54 HTTP/1.1 200 \r\n\
55 content-length:%u\r\n\
56 content-type:%s\r\n\r\n"
57 /******************************************************************************/
59 #define SIGBIT(sig) (1<<(sig-1))
61 /*----------------------------------------------------------------------------*/
62 /* sockets stuff */
63 static struct sockaddr_in srv_addr;
64 static si srv_sock; /* the main listening socket */
65 static si cnx_sock; /* a cnx socket from accept */
67 static ul cnx_sock_fd_set[FD_SET_ULS_N];
68 static u8 cnx_sock_fd_set_ul_idx;
69 static u8 cnx_sock_fd_ul_shift;
70 /*----------------------------------------------------------------------------*/
72 /*----------------------------------------------------------------------------*/
73 /* fd facilities */
74 static si sigs_fd; /* fd for signals */
75 static si epfd; /* epoll fd */
76 /*----------------------------------------------------------------------------*/
78 /*----------------------------------------------------------------------------*/
79 #define HTTP_METHOD_GET 0
80 #define HTTP_METHOD_HEAD 1
81 static u8 http_method;
82 /*----------------------------------------------------------------------------*/
84 /*----------------------------------------------------------------------------*/
85 /* the buffer page */
86 static u8 *page;
87 static ul page_bytes_rd_n; /* keep an eye on how much was read */
89 static u8 *method_target_space;
90 static u8 *target_start;
91 static u8 *target_end; /* point the space char right after */
92 /*----------------------------------------------------------------------------*/
94 /*----------------------------------------------------------------------------*/
95 static u8 target_file_is_default;
96 static si target_file_fd;
97 static ul target_file_sz;
98 static u8 target_file_mime[255 + 1]; /* actually the content-type */
99 /*----------------------------------------------------------------------------*/
101 /*----------------------------------------------------------------------------*/
102 static sl resp_hdr_sz; /* the generated response header */
103 /*----------------------------------------------------------------------------*/
106 /******************************************************************************/
107 /******************************************************************************/
108 /******************************************************************************/
111 static void epoll_srv_sock_setup(void)
113 struct epoll_event ep_evt;
114 sl r;
116 memset(&ep_evt, 0, sizeof(ep_evt));
117 ep_evt.events = EPOLLIN | EPOLLPRI;
118 ep_evt.data.fd = srv_sock;
120 r = epoll_ctl(epfd, EPOLL_CTL_ADD, srv_sock, &ep_evt);
121 if(ISERR(r))
122 exit(SRV_SOCK_SETUP_EPOLL_CTL_ADD_FAILURE);
125 /*---------------------------------------------------------------------------*/
126 /* http method can request to close the connexion exiting, we are violent: we close the tcp cnx
127 though we should send a response in order to be rfc correct */
128 #define HTTP_CLOSE 1
130 /* PAGE_SZ is way bigger than 4 bytes */
131 static u8 http_method_is_get(void)
133 if ( page[0] == 'G'
134 && page[1] == 'E'
135 && page[2] == 'T'
136 && page[3] == ' ')
137 return 1;
138 return 0;
141 /* PAGE_SZ is way bigger than 4 bytes */
142 static u8 http_method_is_head(void)
144 if ( page[0] == 'H'
145 && page[1] == 'E'
146 && page[2] == 'A'
147 && page[3] == 'D')
148 return 1;
149 return 0;
152 static u8 http_method_match(void)
154 if (http_method_is_get())
155 http_method = HTTP_METHOD_GET;
156 else if (http_method_is_head())
157 http_method = HTTP_METHOD_HEAD;
158 else
159 return HTTP_CLOSE; /* garbage or method not handled */
160 return 0;
163 /* if we have an error or we time out, notify for socket closing */
164 #define CNX_SOCK_RD_WAIT_FAILURE 1
165 #define fd_set cnx_sock_fd_set
166 #define ul_idx cnx_sock_fd_set_ul_idx
167 #define ul_shift cnx_sock_fd_ul_shift
168 static u8 cnx_sock_rd_wait(void)
170 struct timeval tv;
171 sl r;
173 fd_set[ul_idx] = (1UL << ul_shift); /* was zero-ed in global_init */
175 tv.sec = CNX_WAIT_TIMEOUT;
176 tv.usec = 0;
178 loop {
179 r = select(cnx_sock + 1, &cnx_sock_fd_set[0], 0, 0, &tv);
180 if (r != -EINTR)
181 break;
184 if (ISERR(r) || r == 0) /* r != 1 */
185 return CNX_SOCK_RD_WAIT_FAILURE;
186 return 0;
188 #undef fd_set
189 #undef byte_offset
190 #undef byte_shift
192 #define CNX_SOCK_RD_FAILURE -1
193 static sl cnx_sock_rd(void)
195 sl bytes_rd_n;
197 loop {
198 bytes_rd_n = read(cnx_sock, page + page_bytes_rd_n,
199 PAGE_SZ - page_bytes_rd_n);
200 if (bytes_rd_n != -EINTR) {
201 if (ISERR(bytes_rd_n))
202 bytes_rd_n = CNX_SOCK_RD_FAILURE;
203 break;
206 return bytes_rd_n;
209 static u8 http_method_rd(void)
211 u8 *c;
213 c = page;
215 loop {
216 u8 r;
217 sl bytes_rd_n;
219 r = cnx_sock_rd_wait();
220 if (r == CNX_SOCK_RD_WAIT_FAILURE)
221 break;
223 bytes_rd_n = cnx_sock_rd();
224 if (bytes_rd_n == CNX_SOCK_RD_FAILURE || bytes_rd_n == 0)
225 break;
227 page_bytes_rd_n += bytes_rd_n;
229 loop {
230 if (bytes_rd_n-- == 0)
231 break;
233 if (*c == ' ') {
234 /* got the space separator:method' 'target */
235 method_target_space = c;
236 return 0;
239 ++c;
242 if (page_bytes_rd_n == PAGE_SZ) /* no more page room */
243 break;
245 return HTTP_CLOSE;
248 /* XXX:must be called with room in the page or it will loop with 0 read bytes
249 till the other end decides to shutdown the connection */
250 #define UNABLE_TO_READ_AT_LEAST_ONE_BYTE 1
251 static u8 http_rd_at_least_one_byte(void)
253 loop {
254 u8 r;
255 sl bytes_rd_n;
257 r = cnx_sock_rd_wait();
258 if (r == CNX_SOCK_RD_WAIT_FAILURE)
259 break;
261 bytes_rd_n = cnx_sock_rd();
262 if (bytes_rd_n == CNX_SOCK_RD_FAILURE || bytes_rd_n == 0)
263 break;
265 if (bytes_rd_n != 0) {
266 page_bytes_rd_n += bytes_rd_n;
267 return 0;
270 return UNABLE_TO_READ_AT_LEAST_ONE_BYTE;
273 static u8 http_target_end_locate(void)
275 u8 *c;
277 c = method_target_space + 1;
279 loop {
280 /* need at least 1 more byte to keep going */
281 if (c == (page + page_bytes_rd_n)) {
282 u8 r;
284 if (page_bytes_rd_n == PAGE_SZ)
285 break;/* but no more room in our page */
287 /* we have still room in our page */
289 r = http_rd_at_least_one_byte();
290 if (r == UNABLE_TO_READ_AT_LEAST_ONE_BYTE)
291 break;
294 if (*c == ' ') {
295 target_end = c;
296 return 0;
298 ++c;
300 return HTTP_CLOSE;
303 static u8 http_target_file_open(void)
305 sl r;
307 loop {
308 r = open(target_start, O_RDONLY, 0);
309 if (r != -EINTR)
310 break;
312 if (ISERR(r))
313 return HTTP_CLOSE;
315 target_file_fd = (si)r;
316 return 0;
319 static u8 http_target_file_sz_get(void)
321 struct stat target_stat;
322 sl r;
324 memset(&target_stat, 0, sizeof(target_stat));
326 r = fstat(target_file_fd, &target_stat);
327 if (ISERR(r))
328 return HTTP_CLOSE;
330 target_file_sz = target_stat.sz;
331 return 0;
334 /* if we have an error or we time out, notify for socket closing */
335 #define CNX_SOCK_SEND_WAIT_FAILURE 1
336 #define fd_set cnx_sock_fd_set
337 #define ul_idx cnx_sock_fd_set_ul_idx
338 #define ul_shift cnx_sock_fd_ul_shift
339 static u8 cnx_sock_send_wait(void)
341 struct timeval tv;
342 sl r;
344 fd_set[ul_idx] = (1UL << ul_shift); /* was zero-ed in global_init */
346 tv.sec = CNX_WAIT_TIMEOUT;
347 tv.usec = 0;
349 loop {
350 r = select(cnx_sock + 1, 0, &cnx_sock_fd_set[0], 0, &tv);
351 if (r != -EINTR)
352 break;
355 if (ISERR(r) || r == 0) /* r != 1 */
356 return CNX_SOCK_SEND_WAIT_FAILURE;
357 return 0;
359 #undef fd_set
360 #undef byte_offset
361 #undef byte_shift
363 #define CNX_SOCK_SEND_RESP_HDR_FAILURE -1
364 static sl cnx_sock_send_resp_hdr(void)
366 sl bytes_sent_n;
368 loop {
369 bytes_sent_n = write(cnx_sock, page, resp_hdr_sz);
370 if (bytes_sent_n != -EINTR) {
371 if (ISERR(bytes_sent_n))
372 bytes_sent_n = CNX_SOCK_SEND_RESP_HDR_FAILURE;
373 break;
376 return bytes_sent_n;
379 static u8 http_resp_hdr_send(void)
381 if (target_file_mime[0] == 0)
382 resp_hdr_sz = (sl)snprintf(page, PAGE_SZ, RESP_HDR_FMT,
383 target_file_sz);
384 else
385 resp_hdr_sz = (sl)snprintf(page, PAGE_SZ,
386 RESP_HDR_CONTENT_TYPE_FMT,
387 target_file_sz,
388 &target_file_mime[0]);
390 if (resp_hdr_sz == 0)
391 return HTTP_CLOSE;
393 loop {
394 u8 r;
395 sl bytes_sent_n;
397 r = cnx_sock_send_wait();
398 if (r == CNX_SOCK_SEND_WAIT_FAILURE)
399 return HTTP_CLOSE;
401 bytes_sent_n = cnx_sock_send_resp_hdr();
402 if (bytes_sent_n == CNX_SOCK_SEND_RESP_HDR_FAILURE)
403 break;
405 resp_hdr_sz -= bytes_sent_n;
407 if (resp_hdr_sz == 0)
408 return 0; /* resp hrd was sent */
410 return HTTP_CLOSE;
413 #define CNX_SOCK_SENDFILE_FAILURE -1
414 static sl cnx_sock_sendfile(void)
416 sl bytes_sent_n;
418 loop {
419 bytes_sent_n = sendfile(cnx_sock, target_file_fd, 0,
420 target_file_sz);
421 if (bytes_sent_n != -EINTR) {
422 if (ISERR(bytes_sent_n))
423 bytes_sent_n = CNX_SOCK_SENDFILE_FAILURE;
424 break;
427 return bytes_sent_n;
430 static u8 http_sendfile(void)
432 loop {
433 u8 r;
434 sl bytes_sent_n;
436 if (target_file_sz == 0)
437 break;
439 r = cnx_sock_send_wait();
440 if (r == CNX_SOCK_SEND_WAIT_FAILURE)
441 return HTTP_CLOSE;
443 bytes_sent_n = cnx_sock_sendfile();
444 if (bytes_sent_n == CNX_SOCK_SENDFILE_FAILURE)
445 break;
447 target_file_sz -= bytes_sent_n;
449 return HTTP_CLOSE;
452 static u8 *http_target_mime_file_path(void)
454 /* We have room in the page. target_end has not be modified */
455 target_end[0] = '.';
456 target_end[1] = 'm';
457 target_end[2] = 'i';
458 target_end[3] = 'm';
459 target_end[4] = 'e';
460 target_end[5] = 0;
461 return target_start;
464 static void http_target_mime_file(void)
466 u8 *mime_file_path;
467 sl r;
468 si mime_file_fd;
469 struct stat mime_file_stat;
470 ul bytes_to_read_n;
472 if (target_file_is_default)
473 mime_file_path = DEFAULT_FILE_MIME;
474 else
475 mime_file_path = http_target_mime_file_path();
477 /*--------------------------------------------------------------------*/
478 /* open */
479 loop {
480 r = open(mime_file_path, O_RDONLY, 0);
481 if (r != -EINTR)
482 break;
484 if (ISERR(r))
485 goto direct_exit;
487 mime_file_fd = (si)r;
488 /*--------------------------------------------------------------------*/
490 /*--------------------------------------------------------------------*/
491 /* get size */
492 memset(&mime_file_stat, 0, sizeof(mime_file_stat));
494 r = fstat(mime_file_fd, &mime_file_stat);
495 if (ISERR(r))
496 goto direct_exit;
498 /* mime_file_stat.sz */
499 /*--------------------------------------------------------------------*/
501 /*--------------------------------------------------------------------*/
502 /* check size */
503 if ((mime_file_stat.sz + 1) > sizeof(target_file_mime)) {
504 r = -1;
505 goto direct_exit;
507 bytes_to_read_n = mime_file_stat.sz;
508 /*--------------------------------------------------------------------*/
510 /*--------------------------------------------------------------------*/
511 /* read it */
512 loop {
513 r = read(mime_file_fd, &target_file_mime[0] + mime_file_stat.sz
514 - bytes_to_read_n, bytes_to_read_n);
515 if (r != -EINTR) {
516 if (ISERR(r))
517 goto close_mime_file;
519 bytes_to_read_n -= r;
521 if (bytes_to_read_n == 0)
522 break;
525 /*--------------------------------------------------------------------*/
527 target_file_mime[mime_file_stat.sz] = 0;
529 close_mime_file:
530 close(mime_file_fd);
532 direct_exit:
533 if (ISERR(r))
534 target_file_mime[0] = 0; /* no mime */
537 /*---------------------------------------------------------------------------*/
539 static void cnx_handle(void)
541 u8 r;
543 page_bytes_rd_n = 0;
545 /* read till we find the first space */
546 r = http_method_rd();
547 if (r == HTTP_CLOSE)
548 goto direct_exit;
550 /* is this space defining a reasonable method name? */
551 if (method_target_space == (page + PAGE_SZ)) /* huge method name */
552 goto direct_exit;
554 r = http_method_match();
555 if (r == HTTP_CLOSE)
556 goto direct_exit;
558 r = http_target_end_locate();
559 if (r == HTTP_CLOSE)
560 goto direct_exit;
562 /* now we check we have room for the ".mime" extension */
563 if ((target_end - 1 + sizeof(".mime")) >= (page + PAGE_SZ))
564 goto direct_exit;
566 /* target is exactly "/" or prepare the path */
567 if ((target_end - (method_target_space + 1)) == 1
568 && method_target_space[1] == '/') {
569 target_file_is_default = 1;
570 target_start = DEFAULT_FILE;
571 } else {
572 target_file_is_default = 0;
573 target_start = method_target_space + 1;
574 *target_end = 0;
577 r = http_target_file_open();
578 if (r == HTTP_CLOSE)
579 goto direct_exit;
581 r = http_target_file_sz_get();
582 if (r == HTTP_CLOSE)
583 goto close_target_file;
585 http_target_mime_file();
587 r = http_resp_hdr_send();
588 if (r == HTTP_CLOSE)
589 goto close_target_file;
591 if (http_method == HTTP_METHOD_GET)
592 (void)http_sendfile();
594 close_target_file:
595 close(target_file_fd);
596 direct_exit:
597 return;
600 static void cnx_sock_close(void)
602 loop {
603 sl r;
605 r = close(cnx_sock);
606 if (r != -EINTR) /* ignores errors */
607 break;
611 static void cnx_sock_fd_set_params(void)
613 si ul_bits_n;
615 ul_bits_n = 8 * sizeof(ul);
617 cnx_sock_fd_set_ul_idx = cnx_sock / ul_bits_n;
618 cnx_sock_fd_ul_shift = cnx_sock % ul_bits_n;
621 static void cnxs_consume(void)
623 loop {
624 sl r;
625 struct sockaddr_in peer;
626 sl peer_len;
628 peer_len = sizeof(peer);
629 loop {
630 r = accept(srv_sock, &peer, &peer_len);
631 if (r != -EINTR && r != ECONNABORTED) /* based on man page */
632 break;
633 /* SIGSTOP will generate a EINTR */
636 if (r != -EAGAIN && ISERR(r))
637 exit(CNXS_CONSUME_ACCEPT_GENERIC_FAILURE);
638 if (peer_len != sizeof(peer))
639 exit(CNXS_CONSUME_ACCEPT_WRONG_PEER);
640 if (r == -EAGAIN)
641 break; /* no more connexion pending */
643 cnx_sock=(si)r;
645 cnx_sock_fd_set_params();
647 cnx_handle();
648 cnx_sock_close();
652 static void sigs_consume(void)
654 struct signalfd_siginfo info;
656 loop {
657 sl r;
659 loop {
660 memset(&info, 0, sizeof(info));
661 r = read(sigs_fd, &info, sizeof(info));
662 if (r != -EINTR)
663 break;
665 if (r != -EAGAIN && ((ISERR(r) || (r > 0
666 && r != sizeof(info)))))
667 exit(SIGS_CONSUME_SIGINFO_READ_FAILURE);
668 if (r == 0 || r == -EAGAIN)
669 break;
671 switch (info.ssi_signo) {
672 case SIGTERM:
673 exit(0);
674 break;
675 /* please, do add the ones you like */
680 static void main_loop(void)
682 loop {
683 struct epoll_event evts[2]; /*sigs_fd and srv_sock */
684 sl r;
685 sl j;
687 loop {
688 memset(evts, 0, sizeof(evts));
689 r = epoll_wait(epfd, evts, 2, -1);
690 if (r != -EINTR)
691 break;
693 if (ISERR(r))
694 exit(MAIN_LOOP_EPOLL_WAIT_GENERIC_FAILURE);
696 j = 0;
697 loop {
698 if (j == r)
699 break;
701 if (evts[j].data.fd == sigs_fd) {
702 if(evts[j].events & EPOLLIN)
703 sigs_consume();
704 else
705 exit(MAIN_LOOP_EPOLL_WAIT_SIGS_FD_EVENT_IS_NOT_EPOLLIN);
706 } else if (evts[j].data.fd == srv_sock) {
707 if (evts[j].events & (EPOLLERR | EPOLLHUP | EPOLLPRI))
708 exit(MAIN_LOOP_EPOLL_WAIT_SRV_SOCK_UNEXPECTED_EVENT);
709 else if (evts[j].events & EPOLLIN)
710 cnxs_consume();
711 else
712 exit(MAIN_LOOP_EPOLL_WAIT_SRV_SOCK_UNKNOWN_FAILURE);
715 ++j;
720 static void srv_sock_create(void)
722 sl bool_true;
723 sl r;
725 /* TCP on IPv4... erk! */
726 r = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
727 if (ISERR(r))
728 exit(SRV_SOCK_CREATE_FAILURE);
729 srv_sock = (si)r;
731 bool_true = 1;
732 r = setsockopt(srv_sock, SOL_SOCKET, SO_REUSEADDR, &bool_true,
733 sizeof(bool_true));
734 if (ISERR(r))
735 exit(SRV_SOCK_SET_SOCK_OPTION_FAILURE);
737 r = bind(srv_sock, &srv_addr, sizeof(srv_addr));
738 if (ISERR(r))
739 exit(SRV_SOCK_BIND_FAILURE);
741 r = listen(srv_sock, 0);
742 if (ISERR(r))
743 exit(SRV_SOCK_LISTEN_FAILURE);
746 static void epoll_sigs_setup(void)
748 struct epoll_event ep_evt;
749 sl r;
751 memset(&ep_evt, 0, sizeof(ep_evt));
752 ep_evt.events = EPOLLET | EPOLLIN;
753 ep_evt.data.fd = sigs_fd;
754 r = epoll_ctl(epfd, EPOLL_CTL_ADD, sigs_fd, &ep_evt);
755 if (ISERR(r))
756 exit(EPOLL_SIGS_SETUP_EPOLL_ADD_FAILURE);
759 static void sigs_setup(void)
760 {/* synchronous treatement of signals with signalfd
761 cannot change SIGKILL, neither SIGSTOP */
762 u64 mask;
763 sl r;
765 mask = (~0);
766 r = rt_sigprocmask(SIG_BLOCK, &mask, 0, sizeof(mask));
767 if (ISERR(r))
768 exit(SIGS_SETUP_BLOCKING_FAILURE);
770 mask = SIGBIT(SIGTERM) | SIGBIT(SIGCHLD);
771 sigs_fd = (si)signalfd4(-1, &mask, sizeof(mask), SFD_NONBLOCK);
772 if (ISERR(sigs_fd))
773 exit(SIGS_SETUP_HANDLERS_FAILURE);
776 static void page_mmap(void)
778 sl addr;
780 addr = mmap(PAGE_SZ, RD | WR, PRIVATE | ANONYMOUS);
781 if(addr == 0 || ISERR(addr))
782 exit(RCV_PAGE_MMAP_FAILURE);
784 page = (u8*)addr;
787 static void setup(void)
789 sigs_setup();
791 epfd =(si)epoll_create1(0);
792 if (ISERR(epfd))
793 exit(SETUP_EPOLL_CREATE_FAILURE);
795 epoll_sigs_setup();
796 srv_sock_create();
797 epoll_srv_sock_setup();
799 page_mmap();
802 static void globals_init(void)
804 srv_addr.sin_family = AF_INET;
806 /* big endian port */
807 srv_addr.sin_port = cpu2be16(LISTENING_PORT);
808 srv_addr.sin_addr.s_addr = cpu2be32(LISTENING_IPV4);
810 srv_sock = -1; /* our listening socket */
811 cnx_sock = -1; /* a cnx socket from accept */
813 sigs_fd = -1; /* fd for signals */
814 epfd = -1; /* epoll fd */
816 memset(&cnx_sock_fd_set[0], 0, sizeof(cnx_sock_fd_set));
819 static void chroot_do(void)
821 sl r;
823 r = chroot(CHROOT_PATH);
824 if (ISERR(r))
825 exit(CHROOT_FAILURE);
827 r = chdir("/");
828 if (ISERR(r))
829 exit(CHDIR_FAILURE);
833 /******************************************************************************/
834 /******************************************************************************/
835 /******************************************************************************/
838 /* XXX:may do the daemonic stuff if _really_ we need it */
839 void _start(void)
841 close(0);
842 close(1);
843 close(2);
844 chroot_do();
845 globals_init();
846 setup();
847 main_loop();