Compare commits

..

12 commits

Author SHA1 Message Date
Tom MTT
f3fbb4c77f feat(feuille.c): handle children exit codes 2022-11-29 11:30:29 +01:00
Tom MTT
1694929221 fix(feuille.c): reorganize some of its code 2022-11-29 11:07:49 +01:00
Tom MTT
4894825dac fix(feuille.c): documentation (x2) 2022-11-29 10:54:07 +01:00
Tom MTT
741800f7f3 fix(feuille.c): documentation 2022-11-29 10:53:11 +01:00
Tom MTT
9e011de4ab style(server.c): comment 2022-11-29 10:50:16 +01:00
Tom MTT
00e6fe5582 feat(feuille.c): accept_loop, better DEBUG mode, some style fixes 2022-11-29 10:49:39 +01:00
Tom MTT
2ed08dc596 feat(config.mk): add DEBUG flag to build options 2022-11-29 10:39:14 +01:00
Tom MTT
bf42903592 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.
2022-11-29 10:38:04 +01:00
Tom MTT
43be904636 style: macros indentations (again) 2022-11-29 10:33:15 +01:00
Tom MTT
46d7d4bff3 style(server.c): macros indentation 2022-11-29 10:30:57 +01:00
Tom MTT
bb9a60b240 style(feuille.c): minor padding fix 2022-11-29 10:28:56 +01:00
Tom MTT
5ba58b1bd7 feat: reduce timeout, max file size, and use more workers by default 2022-11-29 10:27:56 +01:00
5 changed files with 144 additions and 111 deletions

View file

@ -18,7 +18,7 @@ LIBS = -L/usr/lib -lc
CC = cc CC = cc
# debug build # 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) LDFLAGS = -g $(LIBS)
# release build # release build

View file

@ -68,13 +68,13 @@ Default: \f[V]/var/www/feuille\f[R]
.TP .TP
\f[B]-s bytes\f[R] \f[B]-s bytes\f[R]
Sets the maximum size for every paste (in bytes). 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 .TP
\f[B]-t seconds\f[R] \f[B]-t seconds\f[R]
Sets the timeout for the client to send the paste (in seconds). Sets the timeout for the client to send the paste (in seconds).
If set to zero, no timeout is set. If set to zero, no timeout is set.
(Not recommended.) (Not recommended.)
Default: \f[V]4\f[R]s Default: \f[V]2\f[R]s
.TP .TP
\f[B]-u\f[R] \f[B]-u\f[R]
Sets the user that will be used when dropping root privileges. 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. connections.
Those are \f[I]real\f[R] processes, not green / posix threads, you might Those are \f[I]real\f[R] processes, not green / posix threads, you might
not want to set this to a huge number. 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 .SH EXAMPLES
.TP .TP
\f[B]sudo feuille\f[R] \f[B]sudo feuille\f[R]

View file

@ -53,12 +53,12 @@ if possible).
**-s bytes** **-s bytes**
: Sets the maximum size for every paste (in bytes). : Sets the maximum size for every paste (in bytes).
: Default: `2097152`B (2MiB) : Default: `1048576`B (1MiB)
**-t seconds** **-t seconds**
: Sets the timeout for the client to send the paste (in seconds). : Sets the timeout for the client to send the paste (in seconds).
: If set to zero, no timeout is set. (Not recommended.) : If set to zero, no timeout is set. (Not recommended.)
: Default: `4`s : Default: `2`s
**-u** **-u**
: Sets the user that will be used when dropping root privileges. : Sets the user that will be used when dropping root privileges.
@ -83,7 +83,8 @@ client.
connections. connections.
: Those are *real* processes, not green / posix threads, : Those are *real* processes, not green / posix threads,
you might not want to set this to a huge number. 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 # EXAMPLES

215
feuille.c
View file

@ -44,11 +44,11 @@ Settings settings = {
.user = "www", .user = "www",
.id_length = 4, .id_length = 4,
.worker_count = 1, .worker_count = 4,
.port = 9999, .port = 9999,
.timeout = 4, .timeout = 2,
.max_size = 2097152, /* = 2MiB = 1024 * 1024 * 2 */ .max_size = 1048576, /* = 1MiB = 1024 * 1024 */
.buffer_size = 131072, /* = 128KiB = 1024 * 128 */ .buffer_size = 131072, /* = 128KiB = 1024 * 128 */
.verbose = 0, .verbose = 0,
.foreground = 0 .foreground = 0
@ -57,6 +57,7 @@ Settings settings = {
/* functions declarations */ /* functions declarations */
static void usage(int exit_code); static void usage(int exit_code);
static void version(void); static void version(void);
static void accept_loop(int);
/** /**
* Display feuille's basic usage. * Display feuille's basic usage.
@ -76,6 +77,89 @@ void version(void)
die(0, "%s %s by Tom MTT. <tom@heimdall.pm>\n", argv0, VERSION); die(0, "%s %s by Tom MTT. <tom@heimdall.pm>\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 */
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) {
/* send URL */
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. * Feuille's main function.
* argc: the argument count. * argc: the argument count.
@ -87,14 +171,18 @@ int main(int argc, char *argv[])
/* locale */ /* locale */
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
/* syslog setup */ /* syslog */
openlog("feuille", LOG_NDELAY | LOG_PERROR, LOG_USER); 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 */ /* settings */
long long tmp; long long tmp;
/* set number of workers */ /* 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; settings.worker_count = tmp;
ARGBEGIN { ARGBEGIN {
@ -263,6 +351,7 @@ int main(int argc, char *argv[])
if ((server = initialize_server()) == -1) if ((server = initialize_server()) == -1)
die(errno, "Failed to initialize server socket: %s\n", strerror(errno)); die(errno, "Failed to initialize server socket: %s\n", strerror(errno));
/* make feuille run in the background */ /* make feuille run in the background */
if (!settings.foreground) { if (!settings.foreground) {
verbose(1, "making feuille run in the background..."); verbose(1, "making feuille run in the background...");
@ -271,11 +360,6 @@ int main(int argc, char *argv[])
daemon(1, 0); 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 */ /* chroot and drop root permissions */
if (getuid() == 0) { if (getuid() == 0) {
@ -293,12 +377,13 @@ int main(int argc, char *argv[])
} }
#ifdef __OpenBSD__
/* OpenBSD-only security measures */ /* OpenBSD-only security measures */
#ifdef __OpenBSD__
pledge("proc stdio rpath wpath cpath inet", "stdio rpath wpath cpath inet"); pledge("proc stdio rpath wpath cpath inet", "stdio rpath wpath cpath inet");
#endif #endif
#ifndef DEBUG
/* create a thread pool for incoming connections */ /* create a thread pool for incoming connections */
verbose(1, "initializing worker pool..."); verbose(1, "initializing worker pool...");
@ -306,84 +391,16 @@ int main(int argc, char *argv[])
for (int i = 1; i <= settings.worker_count; i++) { for (int i = 1; i <= settings.worker_count; i++) {
if ((pid = fork()) == 0) { if ((pid = fork()) == 0) {
verbose(2, " worker n. %d...", i); verbose(2, " worker n. %d...", i);
accept_loop(server);
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);
}
} else if (pid < 0) } else if (pid < 0)
die(errno, "Could not initialize worker n. %d: %s\n", i, strerror(errno)); 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
sleep(1); sleep(1);
@ -391,10 +408,24 @@ int main(int argc, char *argv[])
verbose(1, "all workers have been initialized."); verbose(1, "all workers have been initialized.");
verbose(1, "beginning to accept incoming connections."); 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; return 0;
} }

View file

@ -103,19 +103,19 @@ int initialize_server()
if (setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) < 0) if (setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) < 0)
return -1; return -1;
#ifndef __OpenBSD__
/* Enable dual-stack mode on supported platforms */ /* Enable dual-stack mode on supported platforms */
#ifndef __OpenBSD__ verbose(3, " IPV6_V6ONLY...");
verbose(3, " IPV6_V6ONLY..."); if (setsockopt(server, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only, sizeof(ipv6_only)) < 0)
if (setsockopt(server, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only, sizeof(ipv6_only)) < 0) return -1;
return -1; #else
#else if (ipv6_only == 0) {
if (ipv6_only == 0) { puts("");
puts(""); syslog(LOG_WARNING, "dual-stack mode is disabled on OpenBSD.");
syslog(LOG_WARNING, "dual-stack mode is disabled on OpenBSD."); syslog(LOG_WARNING, "feuille will only listen on the `::' IPv6 address.");
syslog(LOG_WARNING, "feuille will only listen on the `::' IPv6 address."); puts("");
puts(""); }
} #endif
#endif
/* bind address and port */ /* bind address and port */
verbose(3, "binding address on the socket..."); verbose(3, "binding address on the socket...");