Initial commit
[lnanohtmltiledmap.git] / http.c
blob0291044bb990f83f37e5bd31fcff7ea4381bd1fb
1 #ifndef LNANOHTMLTILEDMAP_HTTP_C
2 #define LNANOHTMLTILEDMAP_HTTP_C
3 /*
4 * this code is protected by the GNU affero GPLv3 license
5 * author:Sylvain BERTRAND
6 */
7 #include <stdbool.h>
8 #include <ulinux/compiler_types.h>
9 #include <ulinux/types.h>
10 #include <ulinux/start.h>
11 #include <ulinux/error.h>
12 #include <ulinux/sysc.h>
13 #include <ulinux/file.h>
14 #include <ulinux/time.h>
15 #include <ulinux/mmap.h>
16 #include <ulinux/signal/signal.h>
17 #include <ulinux/socket/socket.h>
18 #ifdef CONFIG_IPV4
19 #include <ulinux/socket/in.h>
20 #else
21 #include <ulinux/socket/in6.h>
22 #endif
23 #include <ulinux/epoll.h>
24 #include <ulinux/utils/endian.h>
26 #include "config.h"
27 #include "common_cpp.h"
28 #include "html.h"
29 #include "exit_codes.h"
30 /*============================================================================*/
31 #include "namespace/ulinux.h"
32 /*============================================================================*/
33 #define SIGBIT(sig) (1<<(sig-1))
34 #ifdef CONFIG_IPV4
35 static struct sockaddr_in srv_addr;
36 #else
37 static struct sockaddr_in6 srv_addr;
38 #endif
39 static sl page_sz; /* from linux auxiliary vector */
40 static si srv_sock; /* the main listening socket */
41 static si sigs_fd; /* synchronous signal handling */
42 static si epfd; /* the main epoll file descriptor */
43 static si cnx_sock; /* a connection socket from accept */
44 static si idletimerfd; /* the idle timer for the connection */
45 static u64 urandom; /* to try to workaround client cache */
46 /* request line */
47 #define RFC7230_REQ_LINE_RECOMMENDED_MIN_SZ 8000
48 static u8 *req; /* buffer for the request line buffer */
49 static u8 *req_e; /* pointer on the byte past the last mmaped byte */
50 static u8 *req_recv; /* pointer on the byte past the last received byte */
51 static u32 req_sz_max; /* the size of buffer for the request line */
53 /* state machine */
54 #define STATE_RDY 0x00
55 #define STATE_RECV_REQ_LINE 0x01
56 #define STATE_DISCARD_REQ_REM 0x02
57 #define STATE_SEND_RESP 0x03
58 static u8 state;
59 /* data storage for states */
60 static u8 *req_line_crlf_lookup; /* tracker pointer for the request line CRLF */
61 static u8 *req_line_e; /* pointer on the '\r' of the request line CRLF */
63 * count how much of the crlf crlf sequence we have seen in order to locate
64 * the end of a request without a body. If we get a weird request with a
65 * huge payload, the idle timeout will get rid of the connection
67 static u8 req_crlf_crlf_lookup;
68 static u8 *crlf_crlf;
69 static u8 *aux; /* auxiliary buffer of page_sz bytes */
70 static u8 *aux_e;/* pointer on the byte past the last mmaped byte */
71 /* resp */
72 static u8 *resp;
73 static u8 *resp_p; /* track what we have sent already */
74 static u8 *resp_html_e; /* pointer past the last byte of the generated response */
75 static u8 *resp_e;
76 static u32 resp_sz_max;
77 /*----------------------------------------------------------------------------*/
78 #define AT_NULL 0
79 #define AT_PAGESIZE 6
80 struct auxv_t {
81 sl type;
82 sl data;
84 static void pagesize_get(u8 *abi_stack)
86 abi_stack += sizeof(ul); /* skip argc */
87 /* argv */
88 loop {
89 ul *p;
91 p = (ul*)abi_stack;
92 if (*p == 0)
93 break;
94 abi_stack += sizeof(ul);
96 abi_stack += sizeof(ul); /* skip argv terminator */
97 loop {
98 ul *p;
100 p = (ul*)abi_stack;
101 if (*p == 0)
102 break;
103 abi_stack += sizeof(ul);
105 abi_stack += sizeof(ul); /* skip envp terminator */
106 loop {
107 struct auxv_t *auxv;
109 auxv = (struct auxv_t*)abi_stack;
110 if (auxv->type == AT_NULL)
111 exit(SETUP_AUXVEC_PAGESIZE_NOT_FOUND);
112 else if (auxv->type == AT_PAGESIZE) {
113 page_sz = (u32)auxv->data;
114 break;
116 abi_stack += 2 * sizeof(ul);
119 #undef AT_NULL
120 #undef AT_PAGESIZE
121 /* best effort */
122 static void urandom_init(void)
124 si fd;
126 loop {
127 sl r;
128 r = openat(0, "/dev/urandom", O_RDONLY, 0);
129 if (r != -EINTR) {
130 if (ISERR(r)) /* ignored */
131 return;
132 fd = (si)r;
133 break;
136 read(fd, &urandom, sizeof(urandom));
137 close(fd);
139 static void globals_init(u8 *abi_stack)
141 /* XXX: sockaddr_in? structures are of different sizes */
142 memset(&srv_addr, 0, sizeof(srv_addr));
143 #ifdef CONFIG_IPV4
144 srv_addr.family = AF_INET;
145 /* big endian port */
146 srv_addr.port = cpu_to_be16_const(CONFIG_LISTENING_PORT);
147 /* zero address = any ipv4 address */
148 #else
149 srv_addr.family = AF_INET6;
150 /* big endian port */
151 srv_addr.port = cpu_to_be16_const(CONFIG_LISTENING_PORT);
152 /* zero address = any ipv6 address */
153 #endif
154 srv_sock = -1;
155 cnx_sock = -1;
156 sigs_fd = -1;
157 epfd = -1;
158 cnx_sock = -1;
159 crlf_crlf = "\r\n\r\n";
160 state = STATE_RDY;
161 pagesize_get(abi_stack);
162 urandom_init();
165 * handling of synchronous signals with signalfd
166 * cannot change SIGKILL, neither SIGSTOP
168 static void sigs_setup(void)
170 u64 mask;
171 sl r;
173 mask = ~0;
174 r = rt_sigprocmask(SIG_BLOCK, &mask, 0, sizeof(mask));
175 if (ISERR(r))
176 exit(SIGS_SETUP_BLOCKING_FAILURE);
177 mask = SIGBIT(SIGTERM);
178 sigs_fd = (si)signalfd4(-1, &mask, sizeof(mask), SFD_NONBLOCK);
179 if (ISERR(sigs_fd))
180 exit(SIGS_SETUP_HANDLERS_FAILURE);
182 static void epoll_sigs_setup(void)
184 struct epoll_event ep_evt;
185 sl r;
187 memset(&ep_evt, 0, sizeof(ep_evt));
188 ep_evt.events = EPOLLET | EPOLLIN;
189 ep_evt.data.fd = sigs_fd;
190 r = epoll_ctl(epfd, EPOLL_CTL_ADD, sigs_fd, &ep_evt);
191 if (ISERR(r))
192 exit(EPOLL_SIGS_SETUP_EPOLL_ADD_FAILURE);
194 static void srv_sock_create(void)
196 sl bool_true;
197 sl r;
198 #ifdef CONFIG_IPV4
199 r = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
200 #else
201 r = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
202 #endif
203 if (ISERR(r))
204 exit(SRV_SOCK_CREATE_FAILURE);
205 srv_sock = (si)r;
206 bool_true = 1;
207 r = setsockopt(srv_sock, SOL_SOCKET, SO_REUSEADDR, &bool_true,
208 sizeof(bool_true));
209 if (ISERR(r))
210 exit(SRV_SOCK_SET_SOCK_OPTION_FAILURE);
211 r = bind(srv_sock, &srv_addr, sizeof(srv_addr));
212 if (ISERR(r))
213 exit(SRV_SOCK_BIND_FAILURE);
214 r = listen(srv_sock, 0);
215 if (ISERR(r))
216 exit(SRV_SOCK_LISTEN_FAILURE);
218 static void epoll_srv_sock_setup(void)
220 struct epoll_event ep_evt;
221 sl r;
223 memset(&ep_evt, 0, sizeof(ep_evt));
224 ep_evt.events = EPOLLIN | EPOLLPRI;
225 ep_evt.data.fd = srv_sock;
226 r = epoll_ctl(epfd, EPOLL_CTL_ADD, srv_sock, &ep_evt);
227 if(ISERR(r))
228 exit(SRV_SOCK_SETUP_EPOLL_CTL_ADD_FAILURE);
230 static void req_mmap(void)
232 sl addr;
234 req_sz_max = RFC7230_REQ_LINE_RECOMMENDED_MIN_SZ / page_sz + page_sz;
235 addr = mmap(req_sz_max, RD | WR, PRIVATE | ANONYMOUS);
236 if(addr == 0 || ISERR(addr))
237 exit(PAGE_MMAP_FAILURE);
238 req = (u8*)addr;
239 req_e = req + req_sz_max;
240 req_recv = req;
241 /* random initial content */
243 static void aux_mmap(void)
245 sl addr;
247 addr = mmap(page_sz, RD | WR, PRIVATE | ANONYMOUS);
248 if(addr == 0 || ISERR(addr))
249 exit(PAGE_MMAP_FAILURE);
250 aux = (u8*)addr;
251 aux_e = req + page_sz;
252 /* random initial content */
254 static void resp_mmap(void)
256 sl addr;
258 /* enough room for our template, don't need much more */
259 resp_sz_max = html_sz_max();
260 resp_sz_max = resp_sz_max / page_sz + page_sz;
261 addr = mmap(resp_sz_max, RD | WR, PRIVATE | ANONYMOUS);
262 if(addr == 0 || ISERR(addr))
263 exit(PAGE_MMAP_FAILURE);
264 resp = (u8*)addr;
265 resp_e = resp + resp_sz_max;
266 /* random initial content */
268 static void idletimerfd_create(void)
270 sl r;
272 r = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
273 if (ISERR(r))
274 exit(IDLETIMERFD_UNABLE_TO_CREATE_FD);
275 idletimerfd = (si)r;
277 static void epoll_idletimerfd_setup(void)
279 struct epoll_event ep_evt;
280 sl r;
282 memset(&ep_evt, 0, sizeof(ep_evt));
283 ep_evt.events = EPOLLIN;
284 ep_evt.data.fd = idletimerfd;
285 r = epoll_ctl(epfd, EPOLL_CTL_ADD, idletimerfd, &ep_evt);
286 if (ISERR(r))
287 exit(EPOLL_IDLETIMERFD_SETUP_EPOLL_ADD_FAILURE);
289 static void setup(void)
291 sigs_setup();
292 epfd =(si)epoll_create1(0);
293 if (ISERR(epfd))
294 exit(SETUP_EPOLL_CREATE_FAILURE);
295 epoll_sigs_setup();
296 srv_sock_create();
297 epoll_srv_sock_setup();
298 idletimerfd_create();
299 epoll_idletimerfd_setup();
300 req_mmap();
301 aux_mmap();
302 resp_mmap();
304 /* consuming synchronous signals */
305 static void sigs_consume(void)
307 struct signalfd_siginfo info;
308 loop {
309 sl r;
311 loop {
312 memset(&info, 0, sizeof(info));
313 /* atomic read / no short reads last time we checked */
314 r = read(sigs_fd, &info, sizeof(info));
315 if (r != -EINTR)
316 break;
318 /* not supposed to happen, but we do it anyway */
319 if (r != -EAGAIN && ((ISERR(r) || (r > 0
320 && r != sizeof(info)))))
321 exit(SIGS_CONSUME_SIGINFO_READ_FAILURE);
322 if (r == 0 || r == -EAGAIN)
323 break;
324 switch (info.ssi_signo) {
325 case SIGTERM:
326 exit(0);
327 break;
328 /* please, do add the ones you like */
332 /* reasonable and generic idle timer, for now */
333 static void idletimer_arm(void)
335 sl r;
336 struct itimerspec t;
338 memset(&t, 0, sizeof(t));
339 t.value.sec = CONFIG_IDLETIMER_TIMEOUT_SECONDS;
340 r = timerfd_settime(idletimerfd, 0, &t, 0);
341 if (ISERR(r))
342 exit(IDLETIMERFD_FAILED_TO_ARM);
344 static void idletimer_disarm(void)
346 sl r;
347 struct itimerspec t;
349 memset(&t, 0, sizeof(t));
350 r = timerfd_settime(idletimerfd, 0, &t, 0);
351 if (ISERR(r))
352 exit(IDLETIMERFD_FAILED_TO_DISARM);
354 /* may log any pb one day */
355 static void state_rdy_force(void)
357 struct epoll_event ep_evt;
359 * XXX: events can be reported for a "closed" file descriptor: *ALL*
360 * file descriptors related to the underlaying objects must be closed
361 * in order to avoid spurious events
363 epoll_ctl(epfd, EPOLL_CTL_DEL, cnx_sock, 0);
364 close(cnx_sock);
365 cnx_sock = -1;
366 memset(&ep_evt, 0, sizeof(ep_evt));
367 /* switch on the listening socket */
368 ep_evt.events = EPOLLIN | EPOLLPRI;
369 ep_evt.data.fd = srv_sock;
370 epoll_ctl(epfd, EPOLL_CTL_MOD, srv_sock, &ep_evt);
371 idletimer_disarm();
372 state = STATE_RDY;
374 static bool state_recv_req_line(si cnx_sock_from_accept)
376 struct epoll_event ep_evt;
377 sl r;
379 /* must be in ready state */
380 if (state != STATE_RDY)
381 goto force_rdy;
382 /* install the connection socket */
383 memset(&ep_evt, 0, sizeof(ep_evt));
384 ep_evt.events = EPOLLIN | EPOLLPRI; /* EPOLLPRI = TCP urgent data */
385 ep_evt.data.fd = cnx_sock_from_accept;
386 r = epoll_ctl(epfd, EPOLL_CTL_ADD, cnx_sock_from_accept, &ep_evt);
387 if(ISERR(r)) /* since we are from the ready state, no EEXIST */
388 goto force_rdy;
389 /* ignore events from the listening socket */
390 memset(&ep_evt, 0, sizeof(ep_evt));
391 ep_evt.data.fd = srv_sock;
392 r = epoll_ctl(epfd, EPOLL_CTL_MOD, srv_sock, &ep_evt);
393 if(ISERR(r))
394 goto force_rdy;
395 cnx_sock = cnx_sock_from_accept;
396 req_recv = req;
397 req_line_crlf_lookup = req;
398 idletimer_arm();
399 state = STATE_RECV_REQ_LINE;
400 return true;
401 force_rdy:
402 state_rdy_force();
403 return false;
405 static void state_send_resp(struct html_ctx_t *c)
407 struct epoll_event ep_evt;
409 resp_p = resp;
410 resp_html_e = c->p; /* point on the byte past the last generated byte */
412 memset(&ep_evt, 0, sizeof(ep_evt));
413 ep_evt.events = EPOLLOUT | EPOLLPRI;
414 ep_evt.data.fd = cnx_sock;
415 epoll_ctl(epfd, EPOLL_CTL_MOD, cnx_sock, &ep_evt);
416 idletimer_arm();
417 state = STATE_SEND_RESP;
419 /* unforgiving */
420 static void req_decode(struct html_query_t *q)
422 u8 *p;
423 u8 *q_s;
424 u8 *q_e;
426 p = req;
427 /* skip the http verb */
428 loop {
429 if (p == req_line_e)
430 return;
431 if (*p == ' ')
432 break;
433 ++p;
435 /* skip the spaces between the verb and the request-target */
436 loop {
437 if (p == req_line_e)
438 return;
439 if (*p != ' ')
440 break;
441 ++p;
443 /* from here we are in the request-target */
445 * reach the http scheme URI '?' query delimiter (which should be uniq)
446 * or the various ends
448 loop {
449 if (p == req_line_e || *p == ' ')
450 return;
451 if (*p == '?')
452 break;
453 ++p;
455 /* here, we have a html query marker '?' */
456 ++p; /* skip the marker '?' */
457 q_s = p;
458 q_e = p;
459 loop {
460 if (p == req_line_e || *p == '#' || *p == ' ') {
461 q_e = p;
462 break;
464 ++p;
466 html_query_decode(q, q_s, q_e);
469 * we use such function because scanning of CRLFCRLF can happen across 2
470 * buffers: the request-line buffer and the auxiliary buffer used to discard
471 * the remainder of the request
473 static bool crlf_crlf_scan(u8 *start, u8 *end)
474 { loop {
475 if (start == end)
476 return false;
477 if (*start == crlf_crlf[req_crlf_crlf_lookup]) {
478 if (req_crlf_crlf_lookup == 3)
479 return true;
480 ++req_crlf_crlf_lookup;
481 } else /* not the CRLFCRLF sequence */
482 req_crlf_crlf_lookup = 0;
483 ++start;
485 static void resp_compute(void)
487 struct html_query_t q;
488 struct html_ctx_t c;
490 html_query_init(&q);
491 req_decode(&q);
493 html_gen_ctx_init_from_query(&c, &q, resp, urandom);
494 ++urandom;
495 html_majortbl_gen(&c);
497 state_send_resp(&c);
499 static void state_discard_req_rem(u8 *req_line_cr)
501 /* we will use data from this state */
502 if (state != STATE_RECV_REQ_LINE) {
503 state_rdy_force();
504 return;
506 /* we are still reading from the connection socket */
507 req_line_e = req_line_cr;
508 /* we have seen already the crlf from the request-line */
509 req_crlf_crlf_lookup = 2;
510 /* scan what we have already received in the request line buffer */
511 if (crlf_crlf_scan(req_line_e + 2, req_recv)) {
512 /* got the request terminationg CRLFCRLF */
513 resp_compute();
514 return;
516 /* the request terminating CRLFCRLF were not received yet */
517 idletimer_arm();
518 state = STATE_DISCARD_REQ_REM;
520 /* we may get more or less */
521 static void state_discard_req_rem_sock_evts(u32 sock_evts)
522 { loop {
523 sl r;
525 r = read(cnx_sock, aux, page_sz);
526 if (r == -EINTR)
527 continue; /* SIGSTOP? */
528 else if (r == -EAGAIN || r == -EWOULDBLOCK)
529 break; /* no more available */
531 * error or the client did shutdown the connection while in the
532 * syscall (r = 0 with page_sz being non zero)
534 else if (ISERR(r) || r == 0) {
535 state_rdy_force();
536 return;
538 /* r > 0 */
539 if (crlf_crlf_scan(aux, aux + r)) {
540 resp_compute();
541 return;
544 /* we may get more or less */
545 static void state_recv_req_line_sock_evts(u32 sock_evts)
547 u8 *p;
549 loop { /* read of much as we can */
550 sl r;
551 ul buf_remaining_bytes_n;
553 /* here, we must have req_e != req_recv */
554 buf_remaining_bytes_n = (ul)(req_e - req_recv);
555 r = read(cnx_sock, req_recv, buf_remaining_bytes_n);
556 if (r == -EINTR)
557 continue; /* SIGSTOP? */
558 else if (r == -EAGAIN || r == -EWOULDBLOCK)
559 break; /* no more available, do something with that */
561 * error or the client did shutdown the connection while in the
562 * syscall
564 else if (ISERR(r) || (buf_remaining_bytes_n != 0 && r == 0)) {
565 state_rdy_force();
566 return;
568 /* r >= 0 */
569 req_recv += r;
570 if (req_recv == req_e)
571 break; /* no more room, try to do something with that */
573 /* lookup for the request-line terminating CRLF */
574 p = req_line_crlf_lookup;
575 loop {
576 if (p == req_recv) {
577 req_line_crlf_lookup = p;
578 break; /* nothing yet */
580 if (p[0] == '\n') {
581 if (((p - 1) >= req) && (p[-1] == '\r')) {
582 state_discard_req_rem(&p[-1]);
583 return;
585 /* \n without a previous \r, don't like that */
586 state_rdy_force();
587 return;
589 ++p;
591 /* nothing interesting and no more room :(, bail out */
592 if (req_e == req_recv)
593 state_rdy_force();
595 static void state_send_resp_sock_evts(u32 evts)
597 sl r;
599 loop {
600 r = write(cnx_sock, resp_p, resp_html_e - resp_p);
601 if (r != -EINTR)
602 break;
604 if (r == -EAGAIN)
605 return;
606 if (ISERR(r)) { /* ignore and reset */
607 state_rdy_force();
608 return;
610 /* account for the bytes sent and acked */
611 resp_p += r;
612 if (resp_p == resp_html_e) /* whole response sent and acked */
613 state_rdy_force();
615 static void cnxs_consume(void)
617 /* we service synchronously 1 connection at a time, for now */
618 if (state != STATE_RDY)
619 return;
620 loop {
621 sl r;
622 #ifdef CONFIG_IPV4
623 struct sockaddr_in peer;
624 #else
625 struct sockaddr_in6 peer;
626 #endif
627 sl peer_len;
629 peer_len = sizeof(peer);
630 loop {
632 * from the linux man page, already pending network
633 * errors for the was-about-to-be-returned network
634 * socket may be returned by the accept syscall, namely
635 * the was-about-to-be-returned network socket is
636 * trashed. It means, if we get errors related to
637 * TCP/IP from the accept syscall, those have to be
638 * considered like EAGAIN.
640 r = accept4(srv_sock, &peer, &peer_len, SOCK_NONBLOCK
641 | SOCK_CLOEXEC);
642 if (r != -EINTR)
643 break;
644 /* SIGSTOP? */
647 * no clearcut and accurate return errors for this syscall:
648 * we will consider all errors as retry-able like EAGAIN
649 * I guess, reading the code would help to sort out error codes
650 * which are fatal from those which would be retry-able.
652 if (ISERR(r) || peer_len != sizeof(peer))
653 break;
655 * from here, we should have a valid socket file descriptor with
656 * an expected peer address size
658 if (state_recv_req_line((si)r))
659 break;
661 * failed state switch, then back in ready state , next accept
662 * syscall
666 static void cnx_sock_evts(u32 evts)
668 /* common to all states, TCP urgent data is not expected with EPOLLPRI */
669 if ((evts & (EPOLLERR | EPOLLHUP | EPOLLPRI)) != 0) {
670 state_rdy_force();
671 return;
673 /* ofc, will get an interface as a function table if going bigger */
674 switch(state) {
675 case STATE_RECV_REQ_LINE:
676 state_recv_req_line_sock_evts(evts);
677 break;
678 case STATE_DISCARD_REQ_REM:
679 state_discard_req_rem_sock_evts(evts);
680 break;
681 case STATE_SEND_RESP:
682 state_send_resp_sock_evts(evts);
683 break;
684 case STATE_RDY:
685 break;
686 default:
687 state_rdy_force();
688 break;
691 static void state_discard_req_rem_idletimerfd_expiration(void)
693 /* trash the connection */
694 state_rdy_force();
696 static void state_recv_req_line_idletimerfd_expiration(void)
698 /* trash the connection */
699 state_rdy_force();
701 static void state_send_resp_idletimerfd_expiration(void)
703 /* trash the connection */
704 state_rdy_force();
707 * XXX: must read the idletimer file descriptor count of expirations in order to
708 * disable EPOLLIN
710 static void idletimerfd_evts(u32 evts)
712 u64 expirations_n;
714 if ((evts & EPOLLIN) == 0)
715 return; /* spurious, may log */
716 /* from here EPOLLIN */
717 loop {
718 sl r;
720 /* atomic read */
721 expirations_n = 0;
722 r = read(idletimerfd, &expirations_n, sizeof(expirations_n));
723 if (r == -EINTR)
724 continue; /* SIGSTOP? */
725 /* we are not supposed to get that in EPOLLIN, may log */
726 if (r == -EAGAIN)
727 return;
728 if (ISERR(r) || r != sizeof(expirations_n))
729 exit(IDLETIMERFD_READ_FAILURE);
730 /* r == 8 */
731 break;
733 if (expirations_n == 0) /* spurious, may log */
734 return;
735 /* ofc, will get an interface as a function table if going bigger */
736 switch(state) {
737 case STATE_DISCARD_REQ_REM:
738 state_discard_req_rem_idletimerfd_expiration();
739 break;
740 case STATE_RECV_REQ_LINE:
741 state_recv_req_line_idletimerfd_expiration();
742 break;
743 case STATE_SEND_RESP:
744 state_send_resp_idletimerfd_expiration();
745 break;
746 case STATE_RDY:
747 default:
748 /* never mind */
749 break;
752 static void main_loop(void)
754 loop {
755 struct epoll_event evts[2]; /* sigs_fd and srv_sock */
756 sl r;
757 sl j;
759 loop {
760 memset(evts, 0, sizeof(evts));
761 r = epoll_pwait(epfd, evts, 2, -1, 0);
762 if (r != -EINTR) /* SIGSTOP? */
763 break;
765 if (ISERR(r))
766 exit(MAIN_LOOP_EPOLL_WAIT_GENERIC_FAILURE);
767 /* r >= 0 from here */
768 j = 0;
769 loop {
770 if (j == r)
771 break;
772 if (evts[j].data.fd == sigs_fd) {
773 if((evts[j].events & EPOLLIN) != 0)
774 sigs_consume();
775 else
776 exit(MAIN_LOOP_EPOLL_WAIT_SIGS_FD_EVENT_IS_NOT_EPOLLIN);
777 } else if (evts[j].data.fd == srv_sock) {
778 if ((evts[j].events & (EPOLLERR | EPOLLHUP
779 | EPOLLPRI)) != 0)
780 exit(MAIN_LOOP_EPOLL_WAIT_SRV_SOCK_UNEXPECTED_EVENT);
781 else if ((evts[j].events & EPOLLIN) != 0) {
782 cnxs_consume();
783 } else
784 exit(MAIN_LOOP_EPOLL_WAIT_SRV_SOCK_UNKNOWN_FAILURE);
785 } else if (evts[j].data.fd == cnx_sock)
786 cnx_sock_evts(evts[j].events);
787 else if (evts[j].data.fd == idletimerfd)
788 idletimerfd_evts(evts[j].events);
789 ++j;
794 * daemon-ization, not done, maybe one day, in the meantime, usage of
795 * setsuid/env/etc commands is recommended
797 void ulinux_start(u8 *abi_stack)
799 close(0);
800 close(1);
801 close(2);
802 globals_init(abi_stack);
803 setup();
804 main_loop();
806 #undef RFC7230_REQ_LINE_RECOMMENDED_MIN_SZ
807 #undef SIGBIT
808 #undef STATE_RDY
809 #undef STATE_RECV_REQ_LINE
810 #undef STATE_DISCARD_REQ_REM
811 #define CLEANUP
812 #include "common_cpp.h"
813 /*============================================================================*/
814 #include "namespace/ulinux.h"
815 /*============================================================================*/
816 #undef CLEANUP
817 #endif