Compare commits

..

No commits in common. "f3fbb4c77f39cea4e38794e41b321326827a1ecf" and "955c840ba48bec0df904f1a03d771e310465ad95" have entirely different histories.

5 changed files with 111 additions and 144 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)\" -DDEBUG $(INCS) CFLAGS = -g -std=c99 -Wall -Wextra -Wpedantic -Wno-sign-compare -DVERSION=\"$(VERSION)\" $(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]1048576\f[R]B (1MiB) Default: \f[V]2097152\f[R]B (2MiB)
.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]2\f[R]s Default: \f[V]4\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,8 +99,7 @@ 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 greater of the number of cores in your computer and Default: the number of threads configured on your machine.
\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: `1048576`B (1MiB) : Default: `2097152`B (2MiB)
**-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: `2`s : Default: `4`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,8 +83,7 @@ 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 greater of the number of cores in your computer and : Default: the number of threads configured on your machine.
`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 = 4, .worker_count = 1,
.port = 9999, .port = 9999,
.timeout = 2, .timeout = 4,
.max_size = 1048576, /* = 1MiB = 1024 * 1024 */ .max_size = 2097152, /* = 2MiB = 1024 * 1024 * 2 */
.buffer_size = 131072, /* = 128KiB = 1024 * 128 */ .buffer_size = 131072, /* = 128KiB = 1024 * 128 */
.verbose = 0, .verbose = 0,
.foreground = 0 .foreground = 0
@ -57,7 +57,6 @@ 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.
@ -77,89 +76,6 @@ 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.
@ -171,18 +87,14 @@ int main(int argc, char *argv[])
/* locale */ /* locale */
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
/* syslog */ /* syslog setup */
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)) > settings.worker_count && tmp <= USHRT_MAX) if ((tmp = sysconf(_SC_NPROCESSORS_ONLN)) > 0 && tmp <= USHRT_MAX)
settings.worker_count = tmp; settings.worker_count = tmp;
ARGBEGIN { ARGBEGIN {
@ -351,7 +263,6 @@ 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...");
@ -360,6 +271,11 @@ 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) {
@ -377,13 +293,12 @@ 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...");
@ -391,16 +306,84 @@ 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);
@ -408,24 +391,10 @@ 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 */
/* fork again if a child dies */ // TODO: handle children exit codes properly
int status; while (wait(0) > 0);
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); 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 */
verbose(3, " IPV6_V6ONLY..."); #ifndef __OpenBSD__
if (setsockopt(server, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only, sizeof(ipv6_only)) < 0) verbose(3, " IPV6_V6ONLY...");
return -1; if (setsockopt(server, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only, sizeof(ipv6_only)) < 0)
#else return -1;
if (ipv6_only == 0) { #else
puts(""); if (ipv6_only == 0) {
syslog(LOG_WARNING, "dual-stack mode is disabled on OpenBSD."); puts("");
syslog(LOG_WARNING, "feuille will only listen on the `::' IPv6 address."); syslog(LOG_WARNING, "dual-stack mode is disabled on OpenBSD.");
puts(""); syslog(LOG_WARNING, "feuille will only listen on the `::' IPv6 address.");
} 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...");