Commit Diff


commit - 1e3ef7ab4f803b6309fcddc11dc23ecc5f33be27
commit + ca21e1004303c6ccff7713813ab5238426414d8f
blob - 56a44faac59787607f05fa21207844decf2c387d
blob + d4ae6dbc8b4d2bcf6a5ef85a61e58f5aea7f27a0
--- ChangeLog
+++ 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
blob - cc1d02de812febf3ecbd74aaf79a51b64638a19d
blob + 1be5891757da66d7dd88503d7d4fc8ca5435376f
--- ex.c
+++ 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);
blob - d74e633af2f0e1461ed9767b18b27dc4eaebef70
blob + 20b4abec263d5b78e37cae39dfaeb5bdb7d7220b
--- gmid.c
+++ 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));
+	}
 }
blob - 07f6f1a52fa9307dc49c6622d9206138cbe521b4
blob + e1e4e87734ed4e4a51b346ed5885aefd62520146
--- gmid.h
+++ 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
blob - 7123035d79de816a83aeff8633388952bc2dc177
blob + 35742c81f4888e498a11b7c1bf5f59dd6796b4b3
--- parse.y
+++ parse.y
@@ -28,12 +28,11 @@
  * int yydebug = 1;
  */
 
-struct vhost *host = &hosts[0];
-size_t ihost = 0;
+struct vhost *host;
+size_t ihost;
+struct location *loc;
+size_t iloc;
 
-struct location *loc = &hosts[0].locations[0];
-size_t iloc = 0;
-
 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);
blob - 8c40bd34cf507b21d8ca9a69230e0e5ecd6559f2
blob + 7a9a11cd50f24722604489373ede2fe90a451ecd
--- server.c
+++ server.c
@@ -905,8 +905,6 @@ loop(struct tls *ctx, int sock4, int sock6)
 	int i, n;
 	struct client clients[MAX_USERS];
 	struct pollfd fds[MAX_USERS];
-
-	connected_clients = 0;
 
 	for (i = 0; i < MAX_USERS; ++i) {
 		fds[i].fd = -1;
@@ -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;
+		}
 	}
 }
blob - c32ecb8af4fe2267ab399151dff9bedf9ef4c50c
blob + 6254deded503db7af5034c28829cef3a936237e5
--- utils.c
+++ 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;
+}