Compare commits
No commits in common. "f3fbb4c77f39cea4e38794e41b321326827a1ecf" and "955c840ba48bec0df904f1a03d771e310465ad95" have entirely different histories.
f3fbb4c77f
...
955c840ba4
5 changed files with 111 additions and 144 deletions
|
@ -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
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
211
feuille.c
211
feuille.c
|
@ -44,10 +44,10 @@ 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,
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
6
server.c
6
server.c
|
@ -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...");
|
||||||
|
|
Reference in a new issue