Compare commits

...

12 Commits

@ -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

@ -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]

@ -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

@ -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,95 +391,41 @@ 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(); } else if (pid < 0)
die(errno, "Could not initialize worker n. %d: %s\n", i, strerror(errno));
/* feed the random number god */ }
srand(time(0) + pid); #else
/* do not create a thread pool if in DEBUG mode */
/* accept loop */ verbose(1, "running in DEBUG mode, won't create a worker pool.");
int connection; accept_loop(server);
while ((connection = accept_connection(server))) { #endif
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); sleep(1);
} else {
error("error while generating a random ID.");
send_response(connection, "Could not generate your paste ID.\nPlease try again later.\n");
}
free(paste); verbose(1, "all workers have been initialized.");
} else { verbose(1, "beginning to accept incoming connections.");
if (errno == EFBIG)
send_response(connection, "Paste too big.\n");
if (errno == ENOENT)
send_response(connection, "Empty paste.\n");
if (errno == EAGAIN) /* fork again if a child dies */
send_response(connection, "Timeout'd.\n"); int status;
int child_pid;
while ((child_pid = wait(&status)) > 0) {
error("child %d unexpectedly died with exit code %d.", child_pid, WEXITSTATUS(status));
error("error %d while reading paste from incoming connection.", errno); /* do not fork if child was KILL'ed */
} if (WTERMSIG(status) == 9)
continue;
/* close connection */ if ((pid = fork()) == 0) {
close_connection(connection); accept_loop(server);
}
} else if (pid < 0) } else if (pid < 0)
die(errno, "Could not initialize worker n. %d: %s\n", i, strerror(errno)); error("could not fork killed child again: ", strerror(errno));
} }
sleep(1);
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); close(server);
return 0; return 0;
} }

@ -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...");

Loading…
Cancel
Save