aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--ex.c5
-rw-r--r--gmid.c110
-rw-r--r--gmid.h7
-rw-r--r--parse.y16
-rw-r--r--server.c11
-rw-r--r--utils.c28
7 files changed, 167 insertions, 14 deletions
diff --git a/ChangeLog b/ChangeLog
index 56a44fa..d4ae6db 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2021-02-04 Omar Polo <op@omarpolo.com>
+
+ * gmid.c (main): reload configuration on SIGHUP, without disconnecting the clients
+
2021-02-02 Omar Polo <op@omarpolo.com>
* server.c (handle_dirlist_head): print the header in the directory listing
diff --git a/ex.c b/ex.c
index cc1d02d..1be5891 100644
--- a/ex.c
+++ b/ex.c
@@ -350,7 +350,7 @@ executor_main()
err(1, "pledge");
#endif
- for (;;) {
+ while (!hupped) {
if (!recv_iri(exfd, &iri)
|| !recv_string(exfd, &spath)
|| !recv_string(exfd, &relpath)
@@ -376,6 +376,9 @@ executor_main()
free(chash);
}
+ if (hupped)
+ _exit(0);
+
/* kill all process in my group. This means the listener and
* every pending CGI script. */
kill(0, SIGINT);
diff --git a/gmid.c b/gmid.c
index d74e633..20b4abe 100644
--- a/gmid.c
+++ b/gmid.c
@@ -31,6 +31,8 @@
#include "gmid.h"
+volatile sig_atomic_t hupped;
+
struct vhost hosts[HOSTSLEN];
int exfd, foreground, verbose, sock4, sock6;
@@ -39,6 +41,7 @@ const char *config_path, *certs_dir, *hostname;
struct conf conf;
+struct tls_config *tlsconf;
struct tls *ctx;
void
@@ -170,6 +173,8 @@ void
sig_handler(int sig)
{
(void)sig;
+
+ hupped = sig == SIGHUP;
}
void
@@ -353,7 +358,6 @@ make_socket(int port, int family)
void
setup_tls(void)
{
- struct tls_config *tlsconf;
struct vhost *h;
if ((tlsconf = tls_config_new()) == NULL)
@@ -371,7 +375,8 @@ setup_tls(void)
/* we need to set something, then we can add how many key we want */
if (tls_config_set_keypair_file(tlsconf, hosts->cert, hosts->key))
- fatal("tls_config_set_keypair_file failed");
+ fatal("tls_config_set_keypair_file failed for (%s, %s)",
+ hosts->cert, hosts->key);
for (h = &hosts[1]; h->domain != NULL; ++h) {
if (tls_config_add_keypair_file(tlsconf, h->cert, h->key) == -1)
@@ -413,6 +418,47 @@ init_config(void)
}
void
+free_config(void)
+{
+ struct vhost *h;
+ struct location *l;
+
+ free(conf.chroot);
+ free(conf.user);
+ memset(&conf, 0, sizeof(conf));
+
+ for (h = hosts; h->domain != NULL; ++h) {
+ free((char*)h->domain);
+ free((char*)h->cert);
+ free((char*)h->key);
+ free((char*)h->dir);
+ free((char*)h->cgi);
+
+ for (l = h->locations; l->match != NULL; ++l) {
+ free((char*)l->match);
+ free((char*)l->lang);
+ free((char*)l->default_mime);
+ free((char*)l->index);
+ }
+ }
+ memset(hosts, 0, sizeof(hosts));
+
+ tls_free(ctx);
+ tls_config_free(tlsconf);
+}
+
+static void
+wait_sighup(void)
+{
+ sigset_t mask;
+ int signo;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGHUP);
+ sigwait(&mask, &signo);
+}
+
+void
drop_priv(void)
{
struct passwd *pw = NULL;
@@ -490,16 +536,20 @@ serve(int argc, char **argv, int *p)
fatal("fork: %s", strerror(errno));
case 0: /* child */
+ setproctitle("listener");
close(p[0]);
exfd = p[1];
drop_priv();
+ unblock_signals();
listener_main();
_exit(0);
default: /* parent */
+ setproctitle("executor");
close(p[1]);
exfd = p[0];
drop_priv();
+ unblock_signals();
return executor_main();
}
}
@@ -509,6 +559,7 @@ main(int argc, char **argv)
{
int ch, p[2];
int conftest = 0, configless = 0;
+ int old_ipv6, old_port;
init_config();
@@ -520,7 +571,7 @@ main(int argc, char **argv)
break;
case 'c':
- config_path = optarg;
+ config_path = absolutify_path(optarg);
break;
case 'd':
@@ -591,7 +642,7 @@ main(int argc, char **argv)
signal(SIGINFO, sig_handler);
#endif
signal(SIGUSR2, sig_handler);
- signal(SIGHUP, SIG_IGN);
+ signal(SIGHUP, sig_handler);
if (!foreground && !configless) {
if (daemon(1, 1) == -1)
@@ -610,5 +661,54 @@ main(int argc, char **argv)
if (conf.ipv6)
sock6 = make_socket(conf.port, AF_INET6);
- return serve(argc, argv, p);
+ if (configless)
+ return serve(argc, argv, p);
+
+ /* wait a sighup and reload the daemon */
+ for (;;) {
+ block_signals();
+
+ hupped = 0;
+ switch (fork()) {
+ case -1:
+ fatal("fork: %s", strerror(errno));
+ case 0:
+ return serve(argc, argv, p);
+ }
+
+ close(p[0]);
+ close(p[1]);
+
+ unblock_signals();
+ wait_sighup();
+ LOGI("reloading configuration %s", config_path);
+
+ old_ipv6 = conf.ipv6;
+ old_port = conf.port;
+
+ free_config();
+ init_config();
+ parse_conf(config_path);
+
+ if (old_port != conf.port) {
+ close(sock4);
+ close(sock6);
+ sock4 = -1;
+ sock6 = -1;
+ }
+
+ if (sock6 != -1 && old_ipv6 != conf.ipv6) {
+ close(sock6);
+ sock6 = -1;
+ }
+
+ if (sock4 == -1)
+ sock4 = make_socket(conf.port, AF_INET);
+ if (sock6 == -1 && conf.ipv6)
+ sock6 = make_socket(conf.port, AF_INET6);
+
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC,
+ PF_UNSPEC, p) == -1)
+ fatal("socketpair: %s", strerror(errno));
+ }
}
diff --git a/gmid.h b/gmid.h
index 07f6f1a..e1e4e87 100644
--- a/gmid.h
+++ b/gmid.h
@@ -25,6 +25,7 @@
#include <dirent.h>
#include <poll.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
@@ -109,6 +110,8 @@ struct conf {
extern struct conf conf;
extern int exfd;
+extern volatile sig_atomic_t hupped;
+
struct iri {
char *schema;
char *host;
@@ -195,6 +198,7 @@ void load_vhosts(void);
int make_socket(int, int);
void setup_tls(void);
void init_config(void);
+void free_config(void);
void drop_priv(void);
void usage(const char*);
@@ -250,9 +254,12 @@ int serialize_iri(struct iri*, char*, size_t);
int puny_decode(const char*, char*, size_t, const char**);
/* utils.c */
+void block_signals(void);
+void unblock_signals(void);
int starts_with(const char*, const char*);
int ends_with(const char*, const char*);
ssize_t filesize(int);
char *absolutify_path(const char*);
+char *xstrdup(const char*);
#endif
diff --git a/parse.y b/parse.y
index 7123035..35742c8 100644
--- a/parse.y
+++ b/parse.y
@@ -28,11 +28,10 @@
* int yydebug = 1;
*/
-struct vhost *host = &hosts[0];
-size_t ihost = 0;
-
-struct location *loc = &hosts[0].locations[0];
-size_t iloc = 0;
+struct vhost *host;
+size_t ihost;
+struct location *loc;
+size_t iloc;
int goterror = 0;
const char *config_path;
@@ -85,7 +84,7 @@ vhosts : /* empty */
;
vhost : TSERVER TSTRING '{' servopts locations '}' {
- host->locations[0].match = (char*)"*";
+ host->locations[0].match = xstrdup("*");
host->domain = $2;
if (strstr($2, "xn--") != NULL) {
@@ -183,6 +182,11 @@ parse_portno(const char *p)
void
parse_conf(const char *path)
{
+ host = &hosts[0];
+ ihost = 0;
+ loc = &hosts[0].locations[0];
+ iloc = 0;
+
config_path = path;
if ((yyin = fopen(path, "r")) == NULL)
fatal("cannot open config %s", path);
diff --git a/server.c b/server.c
index 8c40bd3..7a9a11c 100644
--- a/server.c
+++ b/server.c
@@ -906,8 +906,6 @@ loop(struct tls *ctx, int sock4, int sock6)
struct client clients[MAX_USERS];
struct pollfd fds[MAX_USERS];
- connected_clients = 0;
-
for (i = 0; i < MAX_USERS; ++i) {
fds[i].fd = -1;
fds[i].events = POLLIN;
@@ -952,5 +950,14 @@ loop(struct tls *ctx, int sock4, int sock6)
else
clients[i].state(&fds[i], &clients[i]);
}
+
+ if (hupped) {
+ if (connected_clients == 0)
+ return;
+
+ fds[0].fd = -1;
+ if (sock6 != -1)
+ fds[1].fd = -1;
+ }
}
}
diff --git a/utils.c b/utils.c
index c32ecb8..6254ded 100644
--- a/utils.c
+++ b/utils.c
@@ -19,6 +19,24 @@
#include "gmid.h"
+static sigset_t set;
+
+void
+block_signals(void)
+{
+ sigset_t new;
+
+ sigemptyset(&new);
+ sigaddset(&new, SIGHUP);
+ sigprocmask(SIG_BLOCK, &new, &set);
+}
+
+void
+unblock_signals(void)
+{
+ sigprocmask(SIG_SETMASK, &set, NULL);
+}
+
int
starts_with(const char *str, const char *prefix)
{
@@ -80,3 +98,13 @@ absolutify_path(const char *path)
free(wd);
return r;
}
+
+char *
+xstrdup(const char *s)
+{
+ char *d;
+
+ if ((d = strdup(s)) == NULL)
+ err(1, "strdup");
+ return d;
+}