/lnanohttp.c (41ea7a150b07f05277b2322b58067b87b921af53) (22354 bytes) (mode 100644) (type blob)

/* this code is protected by the GNU affero GPLv3
   author:Sylvain BERTRAND <sylvain DOT bertrand AT legeek DOT net>
/*----------------------------------------------------------------------------*/
/* compiler stuff */
#include <stdarg.h>
/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/* ulinux stuff */
#include <ulinux/compiler_types.h>
#include <ulinux/types.h>
#include <ulinux/sysc.h>

#include <ulinux/file.h>
#include <ulinux/socket/socket.h>
#ifdef CONFIG_IPV4
#include <ulinux/socket/in.h>
#else
#include <ulinux/socket/in6.h>
#endif
#include <ulinux/signal/signal.h>
#include <ulinux/error.h>
#include <ulinux/epoll.h>
#include <ulinux/utils/mem.h>
#include <ulinux/utils/endian.h>
#include <ulinux/mmap.h>
#include <ulinux/time.h>
#include <ulinux/select.h>
#include <ulinux/stat.h>

#include <ulinux/utils/ascii/string/vsprintf.h>

#include "ulinux_namespace.h"
#include "exit_codes.h"
/*----------------------------------------------------------------------------*/

/******************************************************************************/
#ifdef CONFIG_IPV4
/* 32 bits value for the IPv4 address, can be INADDR_ANY */
	#ifdef CONFIG_LISTENING_IPV4
		#define LISTENING_IPV4 CONFIG_LISTENING_IPV4
	#else
		#define LISTENING_IPV4 INADDR_ANY
	#endif
#else /* IPv6 */
static struct ulinux_in6_addr listening_ipv6;
/* 128 bits value for the IPv6 address, is "IN6ADDR_ANY" if all 0 */
static void listening_ipv6_init(void)
{
	/* yep, at that time, I really did not like initializers */
	#ifdef CONFIG_LISTENING_IPV6_BYTE_0
		listening_ipv6.s6_addr[0x0] = 0;
	#else
		listening_ipv6.s6_addr[0x0] = CONFIG_LISTENING_IPV6_BYTE_0;
	#endif
	#ifdef CONFIG_LISTENING_IPV6_BYTE_1
		listening_ipv6.s6_addr[0x1] = 0;
	#else
		listening_ipv6.s6_addr[0x1] = CONFIG_LISTENING_IPV6_BYTE_1;
	#endif
	#ifdef CONFIG_LISTENING_IPV6_BYTE_2
		listening_ipv6.s6_addr[0x2] = 0;
	#else
		listening_ipv6.s6_addr[0x2] = CONFIG_LISTENING_IPV6_BYTE_2;
	#endif
	#ifdef CONFIG_LISTENING_IPV6_BYTE_3
		listening_ipv6.s6_addr[0x3] = 0;
	#else
		listening_ipv6.s6_addr[0x3] = CONFIG_LISTENING_IPV6_BYTE_3;
	#endif
	#ifdef CONFIG_LISTENING_IPV6_BYTE_4
		listening_ipv6.s6_addr[0x4] = 0;
	#else
		listening_ipv6.s6_addr[0x4] = CONFIG_LISTENING_IPV6_BYTE_4;
	#endif
	#ifdef CONFIG_LISTENING_IPV6_BYTE_5
		listening_ipv6.s6_addr[0x5] = 0;
	#else
		listening_ipv6.s6_addr[0x5] = CONFIG_LISTENING_IPV6_BYTE_5;
	#endif
	#ifdef CONFIG_LISTENING_IPV6_BYTE_6
		listening_ipv6.s6_addr[0x6] = 0;
	#else
		listening_ipv6.s6_addr[0x6] = CONFIG_LISTENING_IPV6_BYTE_6;
	#endif
	#ifdef CONFIG_LISTENING_IPV6_BYTE_7
		listening_ipv6.s6_addr[0x7] = 0;
	#else
		listening_ipv6.s6_addr[0x7] = CONFIG_LISTENING_IPV6_BYTE_7;
	#endif
	#ifdef CONFIG_LISTENING_IPV6_BYTE_8
		listening_ipv6.s6_addr[0x8] = 0;
	#else
		listening_ipv6.s6_addr[0x8] = CONFIG_LISTENING_IPV6_BYTE_8;
	#endif
	#ifdef CONFIG_LISTENING_IPV6_BYTE_9
		listening_ipv6.s6_addr[0x9] = 0;
	#else
		listening_ipv6.s6_addr[0x9] = CONFIG_LISTENING_IPV6_BYTE_9;
	#endif
	#ifdef CONFIG_LISTENING_IPV6_BYTE_A
		listening_ipv6.s6_addr[0xa] = 0;
	#else
		listening_ipv6.s6_addr[0xa] = CONFIG_LISTENING_IPV6_BYTE_A;
	#endif
	#ifdef CONFIG_LISTENING_IPV6_BYTE_B
		listening_ipv6.s6_addr[0xb] = 0;
	#else
		listening_ipv6.s6_addr[0xb] = CONFIG_LISTENING_IPV6_BYTE_B;
	#endif
	#ifdef CONFIG_LISTENING_IPV6_BYTE_C
		listening_ipv6.s6_addr[0xc] = 0;
	#else
		listening_ipv6.s6_addr[0xc] = CONFIG_LISTENING_IPV6_BYTE_C;
	#endif
	#ifdef CONFIG_LISTENING_IPV6_BYTE_D
		listening_ipv6.s6_addr[0xd] = 0;
	#else
		listening_ipv6.s6_addr[0xd] = CONFIG_LISTENING_IPV6_BYTE_D;
	#endif
	#ifdef CONFIG_LISTENING_IPV6_BYTE_E
		listening_ipv6.s6_addr[0xe] = 0;
	#else
		listening_ipv6.s6_addr[0xe] = CONFIG_LISTENING_IPV6_BYTE_E;
	#endif
	#ifdef CONFIG_LISTENING_IPV6_BYTE_F
		listening_ipv6.s6_addr[0xf] = 0;
	#else
		listening_ipv6.s6_addr[0xf] = CONFIG_LISTENING_IPV6_BYTE_F;
	#endif
}
#endif /* IPv6 */
/*----------------------------------------------------------------------------*/
#define RESP_HDR_FMT (u8*)"\
HTTP/1.1 200 \r\n\
content-length:%u\r\n\r\n"

#define RESP_HDR_CONTENT_TYPE_FMT (u8*)"\
HTTP/1.1 200 \r\n\
content-length:%u\r\n\
content-type:%s\r\n\r\n"
/******************************************************************************/

#define SIGBIT(sig) (1<<(sig-1))

/*----------------------------------------------------------------------------*/
/* sockets stuff */
#ifdef CONFIG_IPV4
static struct sockaddr_in srv_addr;
#else
static struct sockaddr_in6 srv_addr;
#endif
static si srv_sock;	/* the main listening socket */
static si cnx_sock;	/* a cnx socket from accept */

static ul cnx_sock_fd_set[FD_SET_ULS_N];
static u8 cnx_sock_fd_set_ul_idx;
static u8 cnx_sock_fd_ul_shift;
/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/* fd facilities */
static si sigs_fd;	/* fd for signals */
static si epfd;		/* epoll fd */
/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
#define HTTP_METHOD_GET		0
#define HTTP_METHOD_HEAD	1
static u8 http_method;
/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/* the buffer page */
static u8 *page;
static ul page_bytes_rd_n;	/* keep an eye on how much was read */

static u8 *method_target_space;
static u8 *target_start;
static u8 *target_end;		/* point the space char right after */
/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
static u8 target_file_is_default;
static si target_file_fd;
static ul target_file_sz;
static u8 target_file_mime[255 + 1];	/* actually the content-type */
/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
static sl resp_hdr_sz;	/* the generated response header */
/*----------------------------------------------------------------------------*/


/******************************************************************************/
/******************************************************************************/
/******************************************************************************/


static void epoll_srv_sock_setup(void)
{
	struct epoll_event ep_evt;
	sl r;

	memset(&ep_evt, 0, sizeof(ep_evt));
	ep_evt.events = EPOLLIN | EPOLLPRI;
	ep_evt.data.fd = srv_sock;

	r = epoll_ctl(epfd, EPOLL_CTL_ADD, srv_sock, &ep_evt);
	if(ISERR(r))
		exit(SRV_SOCK_SETUP_EPOLL_CTL_ADD_FAILURE);
}

/*---------------------------------------------------------------------------*/
/* http method can request to close the connexion exiting, we are violent: we close the tcp cnx
   though we should send a response in order to be rfc correct */
#define HTTP_CLOSE 1

/* PAGE_SZ is way bigger than 4 bytes */
static u8 http_method_is_get(void)
{
	if (		page[0] == 'G'
		&&	page[1] == 'E'
		&&	page[2] == 'T'
		&&	page[3] == ' ')
		return 1;
	return 0;
}

/* PAGE_SZ is way bigger than 4 bytes */
static u8 http_method_is_head(void)
{
	if (		page[0] == 'H'
		&&	page[1] == 'E'
		&&	page[2] == 'A'
		&&	page[3] == 'D')
		return 1;
	return 0;
}

static u8 http_method_match(void)
{
	if (http_method_is_get())
		http_method = HTTP_METHOD_GET;
	else if (http_method_is_head())
		http_method = HTTP_METHOD_HEAD;
	else
		return HTTP_CLOSE; /* garbage or method not handled */
	return 0;
}

/* if we have an error or we time out, notify for socket closing */
#define CNX_SOCK_RD_WAIT_FAILURE 1
#define fd_set cnx_sock_fd_set
#define ul_idx cnx_sock_fd_set_ul_idx
#define ul_shift cnx_sock_fd_ul_shift
static u8 cnx_sock_rd_wait(void)
{
	struct timeval tv;
	sl r;

	fd_set[ul_idx] = (1UL << ul_shift); /* was zero-ed in global_init */

	tv.sec = CONFIG_CNX_WAIT_TIMEOUT;
	tv.usec = 0;

	loop {	
		/* pselect6 is common on x86_64 and aarch64 */
		r = pselect6(cnx_sock + 1, &cnx_sock_fd_set[0], 0, 0, &tv,0);
		/* XXX: to address some concerns: by design for the sake of
		   simplicity we ignore handled signals here. May do state
		   management around epoll_pwait someday. */
		if (r != -EINTR)
			break;
	}

	if (ISERR(r) || r == 0) /* r != 1 */
		return CNX_SOCK_RD_WAIT_FAILURE;
	return 0;
}
#undef fd_set
#undef byte_offset
#undef byte_shift

#define CNX_SOCK_RD_FAILURE -1 
static sl cnx_sock_rd(void)
{
	sl bytes_rd_n;

	loop {
		bytes_rd_n = read(cnx_sock, page + page_bytes_rd_n,
						PAGE_SZ - page_bytes_rd_n);
		if (bytes_rd_n != -EINTR) {
			if (ISERR(bytes_rd_n))
				bytes_rd_n = CNX_SOCK_RD_FAILURE;
			break;
		}
	}
	return bytes_rd_n;
}

static u8 http_method_rd(void)
{
	u8 *c;

	c = page;

	loop {
		u8 r;
		sl bytes_rd_n;

		r = cnx_sock_rd_wait();
		if (r == CNX_SOCK_RD_WAIT_FAILURE)
			break;

		bytes_rd_n = cnx_sock_rd();
		if (bytes_rd_n == CNX_SOCK_RD_FAILURE || bytes_rd_n == 0)
			break;

		page_bytes_rd_n += bytes_rd_n;

		loop {
			if (bytes_rd_n-- == 0)
				break;

			if (*c == ' ') {
				/* got the space separator:method' 'target */
				method_target_space = c;
				return 0;
			}

			++c;
		}

		if (page_bytes_rd_n == PAGE_SZ) /* no more page room */
			break;
	}
	return HTTP_CLOSE;
}

/* XXX:must be called with room in the page or it will loop with 0 read bytes
   till the other end decides to shutdown the connection  */
#define UNABLE_TO_READ_AT_LEAST_ONE_BYTE 1
static u8 http_rd_at_least_one_byte(void)
{
	loop {
		u8 r;
		sl bytes_rd_n;

		r = cnx_sock_rd_wait();
		if (r == CNX_SOCK_RD_WAIT_FAILURE)
			break;

		bytes_rd_n = cnx_sock_rd();
		if (bytes_rd_n == CNX_SOCK_RD_FAILURE || bytes_rd_n == 0)
			break;

		if (bytes_rd_n != 0) {
			page_bytes_rd_n += bytes_rd_n;
			return 0;
		}
	}
	return UNABLE_TO_READ_AT_LEAST_ONE_BYTE;
}

static u8 http_target_end_locate(void)
{
	u8 *c;

	c = method_target_space + 1;

	loop {
		/* need at least 1 more byte to keep going */
		if (c == (page + page_bytes_rd_n)) {
			u8 r;

			if (page_bytes_rd_n == PAGE_SZ)
				break;/* but no more room in our page */

			/* we have still room in our page */

			r = http_rd_at_least_one_byte();			
			if (r == UNABLE_TO_READ_AT_LEAST_ONE_BYTE)
				break;
		}

		if (*c == ' ') {
			target_end = c;
			return 0;	
		}
		++c;
	}
	return HTTP_CLOSE;
}

static u8 http_target_file_open(void)
{
	sl r;

	loop {
		/* openat is common to x86_64 and aarch64 */
		r = openat(target_start, O_RDONLY, 0);
		if (r != -EINTR)
			break;
	}
	if (ISERR(r))
		return HTTP_CLOSE;

	target_file_fd = (si)r;
	return 0;
}

static u8 http_target_file_sz_get(void)
{
	struct stat target_stat;
	sl r;

	memset(&target_stat, 0, sizeof(target_stat));
	r = fstat(target_file_fd, &target_stat);
	if (ISERR(r))
		return HTTP_CLOSE;

	target_file_sz = target_stat.size;
	return 0;
}

/* if we have an error or we time out, notify for socket closing */
#define CNX_SOCK_SEND_WAIT_FAILURE 1
#define fd_set cnx_sock_fd_set
#define ul_idx cnx_sock_fd_set_ul_idx
#define ul_shift cnx_sock_fd_ul_shift
static u8 cnx_sock_send_wait(void)
{
	struct timeval tv;
	sl r;

	fd_set[ul_idx] = (1UL << ul_shift); /* was zero-ed in global_init */

	tv.sec = CONFIG_CNX_WAIT_TIMEOUT;
	tv.usec = 0;

	loop {	
		/* pselect6 is common on x86_64 and aarch64 */
		r = pselect6(cnx_sock + 1, 0, &cnx_sock_fd_set[0], 0, &tv, 0);
		/* XXX: to address some concerns: by design for the sake of
		   simplicity we ignore handled signals here. May do state
		   management around epoll_pwait someday. */
		if (r != -EINTR)
			break;
	}

	if (ISERR(r) || r == 0) /* r != 1 */
		return CNX_SOCK_SEND_WAIT_FAILURE;
	return 0;
}
#undef fd_set
#undef byte_offset
#undef byte_shift

#define CNX_SOCK_SEND_RESP_HDR_FAILURE -1 
static sl cnx_sock_send_resp_hdr(void)
{
	sl bytes_sent_n;

	loop {
		bytes_sent_n = write(cnx_sock, page, resp_hdr_sz);
		if (bytes_sent_n != -EINTR) {
			if (ISERR(bytes_sent_n))
				bytes_sent_n = CNX_SOCK_SEND_RESP_HDR_FAILURE;
			break;
		}
	}
	return bytes_sent_n;
}

static u8 http_resp_hdr_send(void)
{
	if (target_file_mime[0] == 0)
		resp_hdr_sz = (sl)snprintf(page, PAGE_SZ, RESP_HDR_FMT,
								target_file_sz);
	else
		resp_hdr_sz = (sl)snprintf(page, PAGE_SZ,
						RESP_HDR_CONTENT_TYPE_FMT,
						target_file_sz,
						&target_file_mime[0]);

	if (resp_hdr_sz == 0)
		return HTTP_CLOSE;

	loop {
		u8 r;
		sl bytes_sent_n;

		r = cnx_sock_send_wait();
		if (r == CNX_SOCK_SEND_WAIT_FAILURE)
			return HTTP_CLOSE;

		bytes_sent_n = cnx_sock_send_resp_hdr();
		if (bytes_sent_n == CNX_SOCK_SEND_RESP_HDR_FAILURE)
			break;

		resp_hdr_sz -= bytes_sent_n;

		if (resp_hdr_sz == 0)
			return 0;	/* resp hrd was sent */
	}
	return HTTP_CLOSE;
}

#define CNX_SOCK_SENDFILE_FAILURE -1 
static sl cnx_sock_sendfile(void)
{
	sl bytes_sent_n;

	loop {
		bytes_sent_n = sendfile(cnx_sock, target_file_fd, 0,
								target_file_sz);
		if (bytes_sent_n != -EINTR) {
			if (ISERR(bytes_sent_n))
				bytes_sent_n = CNX_SOCK_SENDFILE_FAILURE;
			break;
		}
	}
	return bytes_sent_n;
}

static u8 http_sendfile(void)
{
	loop {
		u8 r;
		sl bytes_sent_n;

		if (target_file_sz == 0)
			break;

		r = cnx_sock_send_wait();
		if (r == CNX_SOCK_SEND_WAIT_FAILURE)
			return HTTP_CLOSE;

		bytes_sent_n = cnx_sock_sendfile();
		if (bytes_sent_n == CNX_SOCK_SENDFILE_FAILURE)
			break;

		target_file_sz -= bytes_sent_n;
	}
	return HTTP_CLOSE;
}

static u8 *http_target_mime_file_path(void)
{
	/* We have room in the page. target_end has not be modified */
	target_end[0] = '.';
	target_end[1] = 'm';
	target_end[2] = 'i';
	target_end[3] = 'm';
	target_end[4] = 'e';
	target_end[5] = 0;
	return target_start;
}

static void http_target_mime_file(void)
{
	u8 *mime_file_path;
	sl r;
	si mime_file_fd;
	struct stat mime_file_stat;
	ul bytes_to_read_n;

	if (target_file_is_default)
		mime_file_path = CONFIG_DEFAULT_FILE_MIME;
	else
		mime_file_path = http_target_mime_file_path();

	/*--------------------------------------------------------------------*/
	/* open */
	loop {
		/* openat is common to x86_64 and aarch64 */
		r = openat(mime_file_path, O_RDONLY, 0);
		if (r != -EINTR)
			break;
	}
	if (ISERR(r))
		goto direct_exit;

	mime_file_fd = (si)r;
	/*--------------------------------------------------------------------*/

	/*--------------------------------------------------------------------*/
	/* get size */
	memset(&mime_file_stat, 0, sizeof(mime_file_stat));

	r = fstat(mime_file_fd, &mime_file_stat);
	if (ISERR(r))
		goto direct_exit;

	/* mime_file_stat.size */
	/*--------------------------------------------------------------------*/

	/*--------------------------------------------------------------------*/
	/* check size */
	if ((mime_file_stat.size + 1) > sizeof(target_file_mime)) {
		r = -1;
		goto direct_exit;
	}
	bytes_to_read_n = mime_file_stat.size;
	/*--------------------------------------------------------------------*/

	/*--------------------------------------------------------------------*/
	/* read it */
	loop {
		r = read(mime_file_fd, &target_file_mime[0]
					+ mime_file_stat.size - bytes_to_read_n,
							bytes_to_read_n);
		if (r != -EINTR) {
			if (ISERR(r))
				goto close_mime_file;

			bytes_to_read_n -= r;

			if (bytes_to_read_n == 0)
				break;
		}
	}
	/*--------------------------------------------------------------------*/

	target_file_mime[mime_file_stat.size] = 0;

close_mime_file:
	close(mime_file_fd);

direct_exit:
	if (ISERR(r))
		target_file_mime[0] = 0;	/* no mime */
}

/*---------------------------------------------------------------------------*/

static void cnx_handle(void)
{
	u8 r;

	page_bytes_rd_n = 0;

	/* read till we find the first space */
	r = http_method_rd();
	if (r == HTTP_CLOSE)
		goto direct_exit;

	/* is this space defining a reasonable method name? */
	if (method_target_space == (page  + PAGE_SZ)) /* huge method name */
		goto direct_exit;

	r = http_method_match();
	if (r == HTTP_CLOSE)
		goto direct_exit;

	r = http_target_end_locate();
	if (r == HTTP_CLOSE)
		goto direct_exit;

	/* now we check we have room for the ".mime" extension */
	if ((target_end - 1 + sizeof(".mime")) >= (page + PAGE_SZ))
		goto direct_exit;

	/* target is exactly "/" or prepare the path */
	if ((target_end - (method_target_space + 1)) == 1
					&& method_target_space[1] == '/') {
		target_file_is_default = 1;
		target_start = CONFIG_DEFAULT_FILE;
	} else {
		target_file_is_default = 0;
		target_start = method_target_space + 1;
		*target_end = 0;
	}

	r = http_target_file_open();
	if (r == HTTP_CLOSE)
		goto direct_exit;

	r = http_target_file_sz_get();
	if (r == HTTP_CLOSE)
		goto close_target_file;

	http_target_mime_file();

	r = http_resp_hdr_send();
	if (r == HTTP_CLOSE)
		goto close_target_file;

	if (http_method == HTTP_METHOD_GET)
		(void)http_sendfile();

close_target_file:
	close(target_file_fd);
direct_exit:
	return;
}

static void cnx_sock_close(void)
{
	loop {
		sl r;

  		r = close(cnx_sock);
		if (r != -EINTR) /* ignores errors */
			break;
	}
}

static void cnx_sock_fd_set_params(void)
{
	si ul_bits_n;

	ul_bits_n = 8 * sizeof(ul);

	cnx_sock_fd_set_ul_idx = cnx_sock / ul_bits_n;
	cnx_sock_fd_ul_shift = cnx_sock % ul_bits_n;
}

static void cnxs_consume(void)
{
	loop {
		sl r;
#ifdef CONFIG_IPV4
		struct sockaddr_in peer;
#else
		struct sockaddr_in6 peer;
#endif
		sl peer_len;

		peer_len = sizeof(peer);
		loop {
			r = accept(srv_sock, &peer, &peer_len);
			if (r != -EINTR && r != ECONNABORTED) /* based on man page */
				break;
			/* SIGSTOP will generate a EINTR */
		}
		
		if (r != -EAGAIN && ISERR(r))
			exit(CNXS_CONSUME_ACCEPT_GENERIC_FAILURE);
		if (peer_len != sizeof(peer))
			exit(CNXS_CONSUME_ACCEPT_WRONG_PEER);
		if (r == -EAGAIN)
			break;	/* no more connexion pending */
		
		cnx_sock=(si)r;

		cnx_sock_fd_set_params();

		cnx_handle();
		cnx_sock_close();
	}
}

static void sigs_consume(void)
{
	struct signalfd_siginfo info;
	
	loop {
		sl r;

		loop {
			memset(&info, 0, sizeof(info));
			r = read(sigs_fd, &info, sizeof(info));
			if (r != -EINTR)
				break;
		}
		if (r != -EAGAIN && ((ISERR(r) || (r > 0
							&& r != sizeof(info)))))
			exit(SIGS_CONSUME_SIGINFO_READ_FAILURE);
		if (r == 0 || r == -EAGAIN)
			break;
	
		switch (info.ssi_signo) {
		case SIGTERM:
			exit(0);
			break;
		/* please, do add the ones you like */
		}
	}
}

static void main_loop(void)
{
	loop {
		struct epoll_event evts[2];	/*sigs_fd and srv_sock */
		sl r;
		sl j;
	
		loop {
			memset(evts, 0, sizeof(evts));
			/* epoll_pwait is common to aarch64 and x86_64 */
			r = epoll_pwait(epfd, evts, 2, -1, 0);
			if (r != -EINTR)
				break;
		}
		if (ISERR(r))
			exit(MAIN_LOOP_EPOLL_WAIT_GENERIC_FAILURE);

		j = 0;
		loop {
			if (j == r)
				break;

			if (evts[j].data.fd == sigs_fd) {
			  if(evts[j].events & EPOLLIN)
				sigs_consume();
			  else
				exit(MAIN_LOOP_EPOLL_WAIT_SIGS_FD_EVENT_IS_NOT_EPOLLIN);
			} else if (evts[j].data.fd == srv_sock) {
			  if (evts[j].events & (EPOLLERR | EPOLLHUP | EPOLLPRI))
				exit(MAIN_LOOP_EPOLL_WAIT_SRV_SOCK_UNEXPECTED_EVENT);
			  else if (evts[j].events & EPOLLIN)
				cnxs_consume();
			  else
				exit(MAIN_LOOP_EPOLL_WAIT_SRV_SOCK_UNKNOWN_FAILURE);
			}

			++j;
		}
	}
}

static void srv_sock_create(void)
{
	sl bool_true;
	sl r;

#ifdef CONFIG_IPV4
	r = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
#else
	r = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, 0);
#endif
	if (ISERR(r))
		exit(SRV_SOCK_CREATE_FAILURE);
	srv_sock = (si)r;
	
	bool_true = 1;
	r = setsockopt(srv_sock, SOL_SOCKET, SO_REUSEADDR, &bool_true,
							sizeof(bool_true));
	if (ISERR(r))
		exit(SRV_SOCK_SET_SOCK_OPTION_FAILURE);
	
	r = bind(srv_sock, &srv_addr, sizeof(srv_addr));
	if (ISERR(r))
		exit(SRV_SOCK_BIND_FAILURE);
	
	r = listen(srv_sock, 0);
	if (ISERR(r))
		exit(SRV_SOCK_LISTEN_FAILURE);
}

static void epoll_sigs_setup(void)
{
	struct epoll_event ep_evt;
	sl r;

	memset(&ep_evt, 0, sizeof(ep_evt));
	ep_evt.events = EPOLLET | EPOLLIN;
	ep_evt.data.fd = sigs_fd;
	r = epoll_ctl(epfd, EPOLL_CTL_ADD, sigs_fd, &ep_evt);
	if (ISERR(r))
		exit(EPOLL_SIGS_SETUP_EPOLL_ADD_FAILURE);
}

static void sigs_setup(void)
{/* synchronous treatement of signals with signalfd
    cannot change SIGKILL, neither SIGSTOP */
	u64 mask;
	sl r;

	mask = (~0);
	r = rt_sigprocmask(SIG_BLOCK, &mask, 0, sizeof(mask));
	if (ISERR(r))
		exit(SIGS_SETUP_BLOCKING_FAILURE);

	mask = SIGBIT(SIGTERM) | SIGBIT(SIGCHLD);
	sigs_fd = (si)signalfd4(-1, &mask, sizeof(mask), SFD_NONBLOCK);
	if (ISERR(sigs_fd))
		exit(SIGS_SETUP_HANDLERS_FAILURE);
}

static void page_mmap(void)
{
	sl addr;

	addr = mmap(PAGE_SZ, RD | WR, PRIVATE | ANONYMOUS);
	if(addr == 0 || ISERR(addr))
		exit(RCV_PAGE_MMAP_FAILURE);

	page = (u8*)addr;
}

static void setup(void)
{
	sigs_setup();

	epfd =(si)epoll_create1(0);
	if (ISERR(epfd))
		exit(SETUP_EPOLL_CREATE_FAILURE);

	epoll_sigs_setup();
	srv_sock_create();
	epoll_srv_sock_setup();

	page_mmap();
}

static void globals_init(void)
{
#ifdef CONFIG_IPV4
	srv_addr.family = AF_INET;

	/* big endian port */
	srv_addr.port = cpu_to_be16(CONFIG_LISTENING_PORT);
	srv_addr.addr = cpu_to_be32(LISTENING_IPV4);
#else
	srv_addr.sin6_family = AF_INET6;

	/* big endian port */
	srv_addr.sin6_port = cpu_to_be16(CONFIG_LISTENING_PORT);
	listening_ipv6_init();
	srv_addr.sin6_addr = listening_ipv6;	/* C compiler block copy */
#endif

	srv_sock = -1;	/* our listening socket */
	cnx_sock = -1;	/* a cnx socket from accept */

	sigs_fd = -1;	/* fd for signals */
	epfd = -1;	/* epoll fd */

	memset(&cnx_sock_fd_set[0], 0, sizeof(cnx_sock_fd_set));
}

static void chroot_do(void)
{
	sl r;

	r = chroot(CONFIG_CHROOT_PATH);
	if (ISERR(r))
		exit(CHROOT_FAILURE);

	r = chdir("/");
	if (ISERR(r))
		exit(CHDIR_FAILURE);
}


/******************************************************************************/
/******************************************************************************/
/******************************************************************************/


/* XXX:may do the daemonic stuff if _really_ we need it */
void _start(void)
{
	close(0);
	close(1);
	close(2);
	chroot_do();
	globals_init();
	setup();
	main_loop();
}


Mode Type Size Ref File
100644 blob 246 bbf313b25987d0d61b1cea33a8e6188501e221ac .gitignore
100644 blob 34520 dbbe3558157f5861bff35dcb37b328b679b0ccfd LICENSE
100644 blob 711 7b6f3427768af8d274abb84700dfc15a92c141f6 README
100644 blob 160 740a1744453fc75aebe4c3e1dacd117141dc0040 all.S
100644 blob 375 bf0a2131dfb6d9ae3646055f1e7035c7c6d60566 all.c
100644 blob 1659 1701bba0dec03fc5dd39e90846cfce49958176c6 config.default.h
100644 blob 1013 115022668b11bc675aac6c8e4d98592da7af4440 exit_codes.h
100644 blob 22354 41ea7a150b07f05277b2322b58067b87b921af53 lnanohttp.c
100755 blob 1193 ecd91729d1b15f7cae1080337b642133dec95aa6 make_gcc_aarch64
040000 tree - 7446ca8ef43378965dd632fa0945ece4c1239ef0 ulinux
100644 blob 3911 61d33c0f458518c884db1bfbc9542f8b5824a005 ulinux_namespace.h
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/sylware/lnanohttp

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/sylware/lnanohttp

Clone this repository using git:
git clone git://git.rocketgit.com/user/sylware/lnanohttp

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main