diff --git a/src/main.c b/src/main.c index c28b6dc..4dcbafd 100644 --- a/src/main.c +++ b/src/main.c @@ -18,200 +18,75 @@ #include #include #include +#include -#include #include #include #include #include #include +#include #include #include #include -static void daemonize(char *pidfile) { - pid_t pid; - FILE *pidout; - pid = fork(); - switch (pid) { - case -1: - exit(EXIT_FAILURE); - case 0: - break; - default: - pidout = fopen(pidfile, "w"); - fprintf(pidout, "%d\n", pid); - fclose(pidout); - exit(EXIT_SUCCESS); - } - - if (setsid() < 0) - exit(EXIT_FAILURE); -} - -static void printLongMessage(char *first, ...) { - va_list ap; - va_start(ap, first); - puts(first); - for (;;) { - char *nextarg = va_arg(ap, char *); - if (nextarg == NULL) - break; - puts(nextarg); - } - va_end(ap); -} - int main(int argc, char **argv) { - char *logout = "/var/log/swebs.log"; - char *sitefile = NULL; - int processes = sysconf(_SC_NPROCESSORS_ONLN) + 1; - int backlog = 100; - char shouldDaemonize = 0; - char *pidfile = "/run/swebs.pid"; - Sitefile *site; Listener *listener; + int processes; - int *pending, (*notify)[2]; + int *pending, pendingid, (*notify)[2]; pthread_t *threads; int i; - for (;;) { - int c = getopt(argc, argv, "o:j:s:b:c:Bp:hl"); - if (c == -1) - break; - switch (c) { - case 'o': - logout = optarg; - break; - case 'j': - processes = atoi(optarg); - break; - case 's': - sitefile = optarg; - break; - case 'b': - backlog = atoi(optarg); - break; - case 'B': - shouldDaemonize = 1; - break; - case 'p': - pidfile = optarg; - break; - case 'l': - printLongMessage( -"swebs Copyright (C) 2022 Nate Choe", -"This is free software, and you are welcome to redistribute under certain", -"conditions, but comes with ABSOLUTELY NO WARRANTY. For more details see the", -"GNU General Public License Version 3\n", -"This program dynamically links with:", -" gnutls (gnutls.org)\n", + setup(argc, argv, &site, &listener, &processes); -"For any complaints, email me at natechoe9@gmail.com", -"I'm a programmer not a lawyer, so there's a good chance I accidentally", -"violated the LGPL.", -NULL - ); - exit(EXIT_SUCCESS); - case 'h': - printLongMessage( -"Usage: swebs [options]", -" -o [out] Set the log file (default: /var/log/swebs.log)", -" -j [cores] Use that many cores (default: $(nproc)+1)", -" -s [site file] Use that site file (required)", -" -b [backlog] Set the socket backlog (default: 100)", -" -B Run swebs in the background and daemonize", -" -p [pidfile] Specify PID file if daemonizing", -" (defualt: /run/swebs.pid)", -" -l Show some legal details", -" -h Show this help message", -NULL - ); - exit(EXIT_SUCCESS); - case '?': - fprintf(stderr, "-h for help\n"); - exit(EXIT_FAILURE); - } - } - - if (sitefile == NULL) { - fprintf(stderr, "No sitefile configured\n"); + pendingid = smalloc(sizeof(int) * (processes - 1)); + if (pendingid < 0) { + createLog("smalloc() failed"); exit(EXIT_FAILURE); } - site = parseSitefile(sitefile); - if (site == NULL) { - fprintf(stderr, "Invalid sitefile %s\n", sitefile); + pending = saddr(pendingid);; + if (pending == NULL) { + createLog("saddr() failed"); exit(EXIT_FAILURE); } + memset(pending, 0, sizeof(int) * (processes - 1)); - switch (site->type) { - case TCP: default: - listener = createListener(TCP, site->port, backlog); - break; - case TLS: - initTLS(); - listener = createListener(TLS, site->port, backlog, - site->key, site->cert); - break; - } - if (listener == NULL) { - fprintf(stderr, "Failed to create socket\n"); - exit(EXIT_FAILURE); - } - - if (shouldDaemonize) - daemonize(pidfile); - - if (initLogging(logout)) { - fprintf(stderr, "Couldn't open logs file %s\n", logout); - exit(EXIT_FAILURE); - } - - { - struct passwd *swebs, *root; - swebs = getpwnam("swebs"); - if (swebs == NULL) - createLog("Couldn't find swebs user"); - else - if (seteuid(swebs->pw_uid)) - createLog("seteuid() failed"); - root = getpwnam("root"); - if (root == NULL) { - createLog("Couldn't find root user, quitting"); - exit(EXIT_FAILURE); - } - if (geteuid() == root->pw_uid) { - createLog("swebs should not be run as root"); - exit(EXIT_FAILURE); - } - } - - signal(SIGPIPE, SIG_IGN); - - pending = calloc(processes - 1, sizeof(int)); notify = malloc(sizeof(int[2]) * (processes - 1)); - threads = malloc(sizeof(pthread_t) * (processes - 1)); - if (threads == NULL) + if (notify == NULL) { + createLog("malloc() failed"); exit(EXIT_FAILURE); + } + + threads = malloc(sizeof(pthread_t) * (processes - 1)); + if (threads == NULL) { + createLog("malloc() failed"); + exit(EXIT_FAILURE); + } for (i = 0; i < processes - 1; i++) { RunnerArgs *args = malloc(sizeof(RunnerArgs)); - if (args == NULL) + if (args == NULL) { + createLog("malloc() failed"); exit(EXIT_FAILURE); - if (pipe(notify[i])) + } + if (pipe(notify[i])) { + createLog("pipe() failed"); exit(EXIT_FAILURE); + } args->site = site; - args->pending = pending; + args->pendingid = pendingid; args->notify = notify[i][0]; args->id = i; pthread_create(threads + i, NULL, (void*(*)(void*)) runServer, args); } + signal(SIGPIPE, SIG_IGN); + createLog("swebs started"); for (;;) { @@ -228,7 +103,7 @@ NULL if (pending[i] < pending[lowestThread]) lowestThread = i; if (write(notify[lowestThread][1], &stream, sizeof(&stream)) - < sizeof(&stream)) + < sizeof(&stream)) continue; } diff --git a/src/runner.c b/src/runner.c index b1a6fed..0544e31 100644 --- a/src/runner.c +++ b/src/runner.c @@ -30,7 +30,7 @@ void *runServer(RunnerArgs *args) { Sitefile *site = args->site; - int *pending = args->pending; + int *pending = saddr(args->pendingid); int notify = args->notify; int id = args->id; @@ -62,7 +62,7 @@ void *runServer(RunnerArgs *args) { remove: freeConnection(connections + i); connCount--; - memcpy(fds + i, fds + connCount, + memcpy(fds + i, fds + connCount - 1, sizeof(struct pollfd)); memcpy(connections + i, fds + connCount, sizeof(struct pollfd)); diff --git a/src/setup.c b/src/setup.c new file mode 100644 index 0000000..7392f9f --- /dev/null +++ b/src/setup.c @@ -0,0 +1,166 @@ +#include +#include +#include + +#include +#include + +#include +#include + +static void daemonize(char *pidfile) { + pid_t pid; + FILE *pidout; + pid = fork(); + switch (pid) { + case -1: + exit(EXIT_FAILURE); + case 0: + break; + default: + pidout = fopen(pidfile, "w"); + fprintf(pidout, "%d\n", pid); + fclose(pidout); + exit(EXIT_SUCCESS); + } + + if (setsid() < 0) + exit(EXIT_FAILURE); +} + +static void printLongMessage(char *first, ...) { + va_list ap; + va_start(ap, first); + puts(first); + for (;;) { + char *nextarg = va_arg(ap, char *); + if (nextarg == NULL) + break; + puts(nextarg); + } + va_end(ap); +} + +void setup(int argc, char **argv, + Sitefile **site, Listener **listener, int *processes) { + char *logout = "/var/log/swebs.log"; + char *sitefile = NULL; + int backlog = 100; + char shouldDaemonize = 0; + char *pidfile = "/run/swebs.pid"; + + *processes = sysconf(_SC_NPROCESSORS_ONLN) + 1; + + for (;;) { + int c = getopt(argc, argv, "o:j:s:b:c:Bp:hl"); + if (c == -1) + break; + switch (c) { + case 'o': + logout = optarg; + break; + case 'j': + *processes = atoi(optarg); + break; + case 's': + sitefile = optarg; + break; + case 'b': + backlog = atoi(optarg); + break; + case 'B': + shouldDaemonize = 1; + break; + case 'p': + pidfile = optarg; + break; + case 'l': + printLongMessage( +"swebs Copyright (C) 2022 Nate Choe", +"This is free software, and you are welcome to redistribute under certain", +"conditions, but comes with ABSOLUTELY NO WARRANTY. For more details see the", +"GNU General Public License Version 3\n", + +"This program dynamically links with:", +" gnutls (gnutls.org)\n", + +"For any complaints, email me at natechoe9@gmail.com", +"I'm a programmer not a lawyer, so there's a good chance I accidentally", +"violated the LGPL.", +NULL + ); + exit(EXIT_SUCCESS); + case 'h': + printLongMessage( +"Usage: swebs [options]", +" -o [out] Set the log file (default: /var/log/swebs.log)", +" -j [cores] Use that many cores (default: $(nproc)+1)", +" -s [site file] Use that site file (required)", +" -b [backlog] Set the socket backlog (default: 100)", +" -B Run swebs in the background and daemonize", +" -p [pidfile] Specify PID file if daemonizing", +" (defualt: /run/swebs.pid)", +" -l Show some legal details", +" -h Show this help message", +NULL + ); + exit(EXIT_SUCCESS); + case '?': + fprintf(stderr, "-h for help\n"); + exit(EXIT_FAILURE); + } + } + + if (sitefile == NULL) { + fprintf(stderr, "No sitefile configured\n"); + exit(EXIT_FAILURE); + } + + *site = parseSitefile(sitefile); + if (site == NULL) { + fprintf(stderr, "Invalid sitefile %s\n", sitefile); + exit(EXIT_FAILURE); + } + + switch ((*site)->type) { + case TCP: default: + *listener = createListener(TCP, (*site)->port, backlog); + break; + case TLS: + initTLS(); + *listener = createListener(TLS, (*site)->port, backlog, + (*site)->key, (*site)->cert); + break; + } + if (listener == NULL) { + fprintf(stderr, "Failed to create socket\n"); + exit(EXIT_FAILURE); + } + + if (shouldDaemonize) + daemonize(pidfile); + + if (initLogging(logout)) { + fprintf(stderr, "Couldn't open logs file %s\n", logout); + exit(EXIT_FAILURE); + } + + { + struct passwd *swebs, *root; + swebs = getpwnam("swebs"); + if (swebs == NULL) + createLog("Couldn't find swebs user"); + else + if (seteuid(swebs->pw_uid)) + createLog("seteuid() failed"); + root = getpwnam("root"); + if (root == NULL) { + createLog("Couldn't find root user, quitting"); + exit(EXIT_FAILURE); + } + if (geteuid() == root->pw_uid) { + createLog("swebs should not be run as root"); + exit(EXIT_FAILURE); + } + } +} diff --git a/src/swebs/runner.h b/src/swebs/runner.h index f5ac963..0f6e5c7 100644 --- a/src/swebs/runner.h +++ b/src/swebs/runner.h @@ -24,7 +24,8 @@ typedef struct { Sitefile *site; - int *pending; + int pendingid; + /* int *pending */ /* * pending[thread id] = the number of connections being handled by that * thread @@ -32,7 +33,7 @@ typedef struct { int notify; /* * When this runner should accept a connection, notify will contain an - * int ready to be read. + * int ready to be read. notify is an fd * */ int id; } RunnerArgs; diff --git a/src/swebs/setup.h b/src/swebs/setup.h new file mode 100644 index 0000000..626fe17 --- /dev/null +++ b/src/swebs/setup.h @@ -0,0 +1,12 @@ +#ifndef HAVE_SETUP +#define HAVE_SETUP + +#include +#include + +void setup(int argc, char **argv, + Sitefile **site, Listener **listener, int *processes); +/* Setup parses args, utilizes them, and returns only what is needed in the + * main loop. */ + +#endif diff --git a/src/swebs/util.h b/src/swebs/util.h index d706dcb..e100b61 100644 --- a/src/swebs/util.h +++ b/src/swebs/util.h @@ -21,6 +21,13 @@ #include int initLogging(char *path); + +int smalloc(size_t size); +/* returns an id passed to saddr, or -1 on error */ +void *saddr(int id); +void sfree(void *addr); +void sdestroy(int id); + int createLog(char *msg); int istrcmp(char *s1, char *s2); /* case insensitive strcmp */ diff --git a/src/util.c b/src/util.c index ac27122..6a04ec8 100644 --- a/src/util.c +++ b/src/util.c @@ -20,6 +20,9 @@ #include #include +#include +#include + #include #include @@ -30,6 +33,23 @@ int initLogging(char *path) { return logs == NULL; } +int smalloc(size_t size) { + return shmget(IPC_PRIVATE, size, + IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR); +} + +void *saddr(int id) { + return shmat(id, NULL, 0); +} + +void sfree(void *addr) { + shmdt(addr); +} + +void sdestroy(int id) { + shmctl(id, IPC_RMID, 0); +} + int createLog(char *msg) { time_t currenttime; struct tm *timeinfo;