From 5ba58b1bd7ca9376b20c31c17ce86dd161c053e7 Mon Sep 17 00:00:00 2001 From: Tom MTT Date: Tue, 29 Nov 2022 10:27:56 +0100 Subject: [PATCH 01/12] feat: reduce timeout, max file size, and use more workers by default --- feuille.1 | 7 ++++--- feuille.1.md | 7 ++++--- feuille.c | 10 +++++----- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/feuille.1 b/feuille.1 index 80877ba..f790afe 100644 --- a/feuille.1 +++ b/feuille.1 @@ -68,13 +68,13 @@ Default: \f[V]/var/www/feuille\f[R] .TP \f[B]-s bytes\f[R] Sets the maximum size for every paste (in bytes). -Default: \f[V]2097152\f[R]B (2MiB) +Default: \f[V]1048576\f[R]B (1MiB) .TP \f[B]-t seconds\f[R] Sets the timeout for the client to send the paste (in seconds). If set to zero, no timeout is set. (Not recommended.) -Default: \f[V]4\f[R]s +Default: \f[V]2\f[R]s .TP \f[B]-u\f[R] Sets the user that will be used when dropping root privileges. @@ -99,7 +99,8 @@ Sets the number of processes that will be spawned to handle the connections. Those are \f[I]real\f[R] processes, not green / posix threads, you might not want to set this to a huge number. -Default: the number of threads configured on your machine. +Default: the greater of the number of cores in your computer and +\f[V]4\f[R] workers. .SH EXAMPLES .TP \f[B]sudo feuille\f[R] diff --git a/feuille.1.md b/feuille.1.md index b11458d..aecc491 100644 --- a/feuille.1.md +++ b/feuille.1.md @@ -53,12 +53,12 @@ if possible). **-s bytes** : Sets the maximum size for every paste (in bytes). -: Default: `2097152`B (2MiB) +: Default: `1048576`B (1MiB) **-t seconds** : Sets the timeout for the client to send the paste (in seconds). : If set to zero, no timeout is set. (Not recommended.) -: Default: `4`s +: Default: `2`s **-u** : Sets the user that will be used when dropping root privileges. @@ -83,7 +83,8 @@ client. connections. : Those are *real* processes, not green / posix threads, you might not want to set this to a huge number. -: Default: the number of threads configured on your machine. +: Default: the greater of the number of cores in your computer and +`4` workers. # EXAMPLES diff --git a/feuille.c b/feuille.c index a3064b9..c262bcd 100644 --- a/feuille.c +++ b/feuille.c @@ -44,11 +44,11 @@ Settings settings = { .user = "www", .id_length = 4, - .worker_count = 1, + .worker_count = 4, .port = 9999, - .timeout = 4, - .max_size = 2097152, /* = 2MiB = 1024 * 1024 * 2 */ - .buffer_size = 131072, /* = 128KiB = 1024 * 128 */ + .timeout = 2, + .max_size = 1048576, /* = 1MiB = 1024 * 1024 */ + .buffer_size = 131072, /* = 128KiB = 1024 * 128 */ .verbose = 0, .foreground = 0 @@ -94,7 +94,7 @@ int main(int argc, char *argv[]) long long tmp; /* set number of workers */ - if ((tmp = sysconf(_SC_NPROCESSORS_ONLN)) > 0 && tmp <= USHRT_MAX) + if ((tmp = sysconf(_SC_NPROCESSORS_ONLN)) > settings.worker_count && tmp <= USHRT_MAX) settings.worker_count = tmp; ARGBEGIN { From bb9a60b240d04e4e538e023304272d75db18f8b7 Mon Sep 17 00:00:00 2001 From: Tom MTT Date: Tue, 29 Nov 2022 10:28:56 +0100 Subject: [PATCH 02/12] style(feuille.c): minor padding fix --- feuille.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feuille.c b/feuille.c index c262bcd..2059836 100644 --- a/feuille.c +++ b/feuille.c @@ -88,7 +88,7 @@ int main(int argc, char *argv[]) setlocale(LC_ALL, ""); /* syslog setup */ - openlog("feuille", LOG_NDELAY | LOG_PERROR, LOG_USER); + openlog("feuille", LOG_NDELAY | LOG_PERROR, LOG_USER); /* settings */ long long tmp; From 46d7d4bff36a2ff9f71a6261086e938a60832f03 Mon Sep 17 00:00:00 2001 From: Tom MTT Date: Tue, 29 Nov 2022 10:30:57 +0100 Subject: [PATCH 03/12] style(server.c): macros indentation --- server.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/server.c b/server.c index 2a1ec64..2efde86 100644 --- a/server.c +++ b/server.c @@ -105,16 +105,17 @@ int initialize_server() /* Enable dual-stack mode on supported platforms */ #ifndef __OpenBSD__ - verbose(3, " IPV6_V6ONLY..."); - if (setsockopt(server, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only, sizeof(ipv6_only)) < 0) - return -1; + verbose(3, " IPV6_V6ONLY..."); + if (setsockopt(server, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only, sizeof(ipv6_only)) < 0) + return -1; + #else - if (ipv6_only == 0) { - puts(""); - syslog(LOG_WARNING, "dual-stack mode is disabled on OpenBSD."); - syslog(LOG_WARNING, "feuille will only listen on the `::' IPv6 address."); - puts(""); - } + if (ipv6_only == 0) { + puts(""); + syslog(LOG_WARNING, "dual-stack mode is disabled on OpenBSD."); + syslog(LOG_WARNING, "feuille will only listen on the `::' IPv6 address."); + puts(""); + } #endif /* bind address and port */ From 43be904636546364a8fa5e5c3b0a320b5c005152 Mon Sep 17 00:00:00 2001 From: Tom MTT Date: Tue, 29 Nov 2022 10:33:15 +0100 Subject: [PATCH 04/12] style: macros indentations (again) --- feuille.c | 4 ++-- server.c | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/feuille.c b/feuille.c index 2059836..1698be7 100644 --- a/feuille.c +++ b/feuille.c @@ -294,9 +294,9 @@ int main(int argc, char *argv[]) } /* OpenBSD-only security measures */ - #ifdef __OpenBSD__ +#ifdef __OpenBSD__ pledge("proc stdio rpath wpath cpath inet", "stdio rpath wpath cpath inet"); - #endif +#endif /* create a thread pool for incoming connections */ diff --git a/server.c b/server.c index 2efde86..27d3bfe 100644 --- a/server.c +++ b/server.c @@ -104,19 +104,18 @@ int initialize_server() return -1; /* Enable dual-stack mode on supported platforms */ - #ifndef __OpenBSD__ +#ifndef __OpenBSD__ verbose(3, " IPV6_V6ONLY..."); if (setsockopt(server, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only, sizeof(ipv6_only)) < 0) return -1; - - #else +#else if (ipv6_only == 0) { puts(""); syslog(LOG_WARNING, "dual-stack mode is disabled on OpenBSD."); syslog(LOG_WARNING, "feuille will only listen on the `::' IPv6 address."); puts(""); } - #endif +#endif /* bind address and port */ verbose(3, "binding address on the socket..."); From bf42903592a8c3495a7981adc1477dac78aeaf12 Mon Sep 17 00:00:00 2001 From: Tom MTT Date: Tue, 29 Nov 2022 10:38:00 +0100 Subject: [PATCH 05/12] feat(feuille.c): do not create thread pool if in DEBUG mode Will be useful when a batch of static/dynamic analysis tools will check feuille's code. --- feuille.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/feuille.c b/feuille.c index 1698be7..9673c85 100644 --- a/feuille.c +++ b/feuille.c @@ -293,20 +293,20 @@ int main(int argc, char *argv[]) } - /* OpenBSD-only security measures */ -#ifdef __OpenBSD__ +#ifdef __OpenBSD__ /* OpenBSD-only security measures */ pledge("proc stdio rpath wpath cpath inet", "stdio rpath wpath cpath inet"); #endif + int pid; +#ifndef DEBUG /* do not fork if in DEBUG mode */ /* create a thread pool for incoming connections */ verbose(1, "initializing worker pool..."); - int pid; for (int i = 1; i <= settings.worker_count; i++) { if ((pid = fork()) == 0) { verbose(2, " worker n. %d...", i); - +#endif pid = getpid(); /* feed the random number god */ @@ -380,10 +380,11 @@ int main(int argc, char *argv[]) /* close connection */ close_connection(connection); } - +#ifndef DEBUG } else if (pid < 0) die(errno, "Could not initialize worker n. %d: %s\n", i, strerror(errno)); } +#endif sleep(1); From 2ed08dc5965deafad6f7a3a0d0a82260a75ce5e1 Mon Sep 17 00:00:00 2001 From: Tom MTT Date: Tue, 29 Nov 2022 10:39:14 +0100 Subject: [PATCH 06/12] feat(config.mk): add DEBUG flag to build options --- config.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.mk b/config.mk index 71e21c2..98f1021 100644 --- a/config.mk +++ b/config.mk @@ -18,7 +18,7 @@ LIBS = -L/usr/lib -lc CC = cc # debug build -CFLAGS = -g -std=c99 -Wall -Wextra -Wpedantic -Wno-sign-compare -DVERSION=\"$(VERSION)\" $(INCS) +CFLAGS = -g -std=c99 -Wall -Wextra -Wpedantic -Wno-sign-compare -DVERSION=\"$(VERSION)\" -DDEBUG $(INCS) LDFLAGS = -g $(LIBS) # release build From 00e6fe5582997e934a2915eaaf164895c3f701de Mon Sep 17 00:00:00 2001 From: Tom MTT Date: Tue, 29 Nov 2022 10:49:39 +0100 Subject: [PATCH 07/12] feat(feuille.c): accept_loop, better DEBUG mode, some style fixes --- feuille.c | 165 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 87 insertions(+), 78 deletions(-) diff --git a/feuille.c b/feuille.c index 9673c85..ed4c2cf 100644 --- a/feuille.c +++ b/feuille.c @@ -57,6 +57,7 @@ Settings settings = { /* functions declarations */ static void usage(int exit_code); static void version(void); +static void accept_loop(int); /** * Display feuille's basic usage. @@ -76,6 +77,83 @@ void version(void) die(0, "%s %s by Tom MTT. \n", argv0, VERSION); } +void accept_loop(int server) +{ + int pid = getpid(); + + /* feed the random number god */ + srand(time(0) + pid); + + /* accept loop */ + int connection; + while ((connection = accept_connection(server))) { + verbose(1, "--- new incoming connection. connection ID: %d:%d ---", pid, time(0)); + + char *paste = NULL; + char *id = NULL; + char *url = NULL; + + /* read paste from connection */ + verbose(1, "reading paste from incoming connection..."); + + if ((paste = read_paste(connection)) != NULL) { + /* generate random ID */ + verbose(1, "done."); + verbose(2, "generating a random ID..."); + + if ((id = generate_id(settings.id_length)) != NULL) { + /* write paste to disk */ + verbose(2, "done."); + verbose(1, "writing paste `%s' to disk...", id); + + if (write_paste(paste, id) == 0) { + /* create URL */ + verbose(1, "done."); + verbose(2, "making the right URL..."); + + if ((url = create_url(id)) != NULL) { + verbose(2, "done.", url); + verbose(1, "sending the link to the client..."); + + send_response(connection, url); + + verbose(1, "All done."); + + free(url); + } else { + error("error while making a valid URL."); + send_response(connection, "Could not create your paste URL.\nPlease try again later.\n"); + } + } else { + error("error while writing paste to disk."); + send_response(connection, "Could not write your paste to disk.\nPlease try again later.\n"); + } + + free(id); + } else { + error("error while generating a random ID."); + send_response(connection, "Could not generate your paste ID.\nPlease try again later.\n"); + } + + free(paste); + } else { + if (errno == EFBIG) + send_response(connection, "Paste too big.\n"); + + if (errno == ENOENT) + send_response(connection, "Empty paste.\n"); + + if (errno == EAGAIN) + send_response(connection, "Timeout'd.\n"); + + error("error %d while reading paste from incoming connection.", errno); + } + + /* close connection */ + close_connection(connection); + } +} + /** * Feuille's main function. * argc: the argument count. @@ -293,97 +371,28 @@ int main(int argc, char *argv[]) } -#ifdef __OpenBSD__ /* OpenBSD-only security measures */ +#ifdef __OpenBSD__ + /* OpenBSD-only security measures */ pledge("proc stdio rpath wpath cpath inet", "stdio rpath wpath cpath inet"); #endif - int pid; - -#ifndef DEBUG /* do not fork if in DEBUG mode */ +#ifndef DEBUG /* create a thread pool for incoming connections */ verbose(1, "initializing worker pool..."); + int pid; for (int i = 1; i <= settings.worker_count; i++) { if ((pid = fork()) == 0) { verbose(2, " worker n. %d...", i); -#endif - pid = getpid(); + accept_loop(server); - /* feed the random number god */ - srand(time(0) + pid); - - /* accept loop */ - int connection; - while ((connection = accept_connection(server))) { - verbose(1, "--- new incoming connection. connection ID: %d:%d ---", pid, time(0)); - - char *paste = NULL; - char *id = NULL; - char *url = NULL; - - /* read paste from connection */ - verbose(1, "reading paste from incoming connection..."); - - if ((paste = read_paste(connection)) != NULL) { - /* generate random ID */ - verbose(1, "done."); - verbose(2, "generating a random ID..."); - - if ((id = generate_id(settings.id_length)) != NULL) { - /* write paste to disk */ - verbose(2, "done."); - verbose(1, "writing paste `%s' to disk...", id); - - if (write_paste(paste, id) == 0) { - /* create URL */ - verbose(1, "done."); - verbose(2, "making the right URL..."); - - if ((url = create_url(id)) != NULL) { - verbose(2, "done.", url); - verbose(1, "sending the link to the client..."); - - send_response(connection, url); - - verbose(1, "All done."); - - free(url); - } else { - error("error while making a valid URL."); - send_response(connection, "Could not create your paste URL.\nPlease try again later.\n"); - } - } else { - error("error while writing paste to disk."); - send_response(connection, "Could not write your paste to disk.\nPlease try again later.\n"); - } - - free(id); - } else { - error("error while generating a random ID."); - send_response(connection, "Could not generate your paste ID.\nPlease try again later.\n"); - } - - free(paste); - } else { - if (errno == EFBIG) - send_response(connection, "Paste too big.\n"); - - if (errno == ENOENT) - send_response(connection, "Empty paste.\n"); - - if (errno == EAGAIN) - send_response(connection, "Timeout'd.\n"); - - error("error %d while reading paste from incoming connection.", errno); - } - - /* close connection */ - close_connection(connection); - } -#ifndef DEBUG } else if (pid < 0) die(errno, "Could not initialize worker n. %d: %s\n", i, strerror(errno)); } +#else + /* do not create a thread pool if in DEBUG mode */ + verbose(1, "running in DEBUG mode, won't create a worker pool."); + accept_loop(server); #endif From 9e011de4abcee54003cca5d77a56aead61c4bd8f Mon Sep 17 00:00:00 2001 From: Tom MTT Date: Tue, 29 Nov 2022 10:50:16 +0100 Subject: [PATCH 08/12] style(server.c): comment --- server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.c b/server.c index 27d3bfe..ff560d7 100644 --- a/server.c +++ b/server.c @@ -103,8 +103,8 @@ int initialize_server() if (setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) < 0) return -1; - /* Enable dual-stack mode on supported platforms */ #ifndef __OpenBSD__ + /* Enable dual-stack mode on supported platforms */ verbose(3, " IPV6_V6ONLY..."); if (setsockopt(server, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only, sizeof(ipv6_only)) < 0) return -1; From 741800f7f35bde7185a02c5f86b030f70ef498e9 Mon Sep 17 00:00:00 2001 From: Tom MTT Date: Tue, 29 Nov 2022 10:53:11 +0100 Subject: [PATCH 09/12] fix(feuille.c): documentation --- feuille.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/feuille.c b/feuille.c index ed4c2cf..0c3c1db 100644 --- a/feuille.c +++ b/feuille.c @@ -77,8 +77,13 @@ void version(void) die(0, "%s %s by Tom MTT. \n", argv0, VERSION); } +/** + * Feuille's accept loop. + * server: the server socket. + */ void accept_loop(int server) { + /* get current process' pid */ int pid = getpid(); /* feed the random number god */ From 4894825dac8a6bacae0f793e4651b485b917e183 Mon Sep 17 00:00:00 2001 From: Tom MTT Date: Tue, 29 Nov 2022 10:54:07 +0100 Subject: [PATCH 10/12] fix(feuille.c): documentation (x2) --- feuille.c | 1 + 1 file changed, 1 insertion(+) diff --git a/feuille.c b/feuille.c index 0c3c1db..6f15a3e 100644 --- a/feuille.c +++ b/feuille.c @@ -117,6 +117,7 @@ void accept_loop(int server) verbose(2, "making the right URL..."); if ((url = create_url(id)) != NULL) { + /* send URL */ verbose(2, "done.", url); verbose(1, "sending the link to the client..."); From 1694929221a316837c10440cff82fcfed351479b Mon Sep 17 00:00:00 2001 From: Tom MTT Date: Tue, 29 Nov 2022 11:07:49 +0100 Subject: [PATCH 11/12] fix(feuille.c): reorganize some of its code --- feuille.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/feuille.c b/feuille.c index 6f15a3e..034f0cf 100644 --- a/feuille.c +++ b/feuille.c @@ -171,9 +171,13 @@ int main(int argc, char *argv[]) /* locale */ setlocale(LC_ALL, ""); - /* syslog setup */ + /* syslog */ openlog("feuille", LOG_NDELAY | LOG_PERROR, LOG_USER); + /* ignore signals that could kill feuille */ + signal(SIGPIPE, SIG_IGN); /* when send(2) or write(2) fails */ + + /* settings */ long long tmp; @@ -347,6 +351,7 @@ int main(int argc, char *argv[]) if ((server = initialize_server()) == -1) die(errno, "Failed to initialize server socket: %s\n", strerror(errno)); + /* make feuille run in the background */ if (!settings.foreground) { verbose(1, "making feuille run in the background..."); @@ -355,11 +360,6 @@ int main(int argc, char *argv[]) daemon(1, 0); } - /* ignore most signals that could kill feuille */ - verbose(3, "ignoring signals that could kill feuille..."); - - signal(SIGPIPE, SIG_IGN); /* when send(2) or write(2) fails */ - /* chroot and drop root permissions */ if (getuid() == 0) { @@ -382,6 +382,7 @@ int main(int argc, char *argv[]) pledge("proc stdio rpath wpath cpath inet", "stdio rpath wpath cpath inet"); #endif + #ifndef DEBUG /* create a thread pool for incoming connections */ verbose(1, "initializing worker pool..."); From f3fbb4c77f39cea4e38794e41b321326827a1ecf Mon Sep 17 00:00:00 2001 From: Tom MTT Date: Tue, 29 Nov 2022 11:30:29 +0100 Subject: [PATCH 12/12] feat(feuille.c): handle children exit codes --- feuille.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/feuille.c b/feuille.c index 034f0cf..9c261db 100644 --- a/feuille.c +++ b/feuille.c @@ -408,10 +408,24 @@ int main(int argc, char *argv[]) verbose(1, "all workers have been initialized."); verbose(1, "beginning to accept incoming connections."); - /* wait for children to finish */ - // TODO: handle children exit codes properly - while (wait(0) > 0); - close(server); + /* fork again if a child dies */ + int status; + int child_pid; + while ((child_pid = wait(&status)) > 0) { + error("child %d unexpectedly died with exit code %d.", child_pid, WEXITSTATUS(status)); + + /* do not fork if child was KILL'ed */ + if (WTERMSIG(status) == 9) + continue; + + if ((pid = fork()) == 0) { + accept_loop(server); + + } else if (pid < 0) + error("could not fork killed child again: ", strerror(errno)); + } + + close(server); return 0; }