From: Stefan Sperling Subject: graceful gotwebd stop To: gameoftrees@openbsd.org Date: Mon, 22 Dec 2025 11:55:35 +0100 This patch changes the way gotwebd reacts to the TERM signal, allowing any running requests to complete before gotwebd terminates. While gotwebd does not need to support configuration reloading we should be taking better care of in-flight requests while stopping gotwebd. Because once gotsysd supports gotwebd, gotsys.conf changes can happen at any moment and require gotsysd to restart gotwebd. I would like to avoid disrupting in-flight requests and sending "500 internal server error" to clients. The idea is to have the existing gotwebd instance step away from FastCGi and login sockets, allowing a new instance of gotwebd to take them over, while the previous instance finishes serving responses to its own clients. In a future patch I plan to add a control socket to gotwebd which gotsysd will use to send a stop request and receive an ackknowledgement once all sockets have been closed by gotwebd and are ready for reuse. ok? M gotwebd/auth.c | 3+ 1- M gotwebd/fcgi.c | 3+ 1- M gotwebd/gotweb.c | 3+ 1- M gotwebd/gotwebd.c | 14+ 0- M gotwebd/gotwebd.h | 1+ 0- M gotwebd/login.c | 28+ 4- M gotwebd/sockets.c | 48+ 7- 7 files changed, 100 insertions(+), 14 deletions(-) commit - f8c15ec8a9ddce2f08959da8a558e2f50b8738b8 commit + 3865fac52c80823e0147e500c27787097b9d0928 blob - 7a7f27f7ca9352028b2f3268554e61dbcd06b73e blob + 3e9cee3c275c4af7cd66b09401c857dac8177c95 --- gotwebd/auth.c +++ gotwebd/auth.c @@ -100,8 +100,10 @@ auth_sighdlr(int sig, short event, void *arg) break; case SIGCHLD: break; - case SIGINT: case SIGTERM: + /* continue until the parent exits */ + break; + case SIGINT: auth_shutdown(); break; default: blob - 01e5003ac8ede257607fe188eab2161e67dcae4e blob + 15a0de5387f3beba35baa991838f86100d51a553 --- gotwebd/fcgi.c +++ gotwebd/fcgi.c @@ -99,8 +99,10 @@ fcgi_sighdlr(int sig, short event, void *arg) break; case SIGCHLD: break; - case SIGINT: case SIGTERM: + /* continue until the parent exits */ + break; + case SIGINT: fcgi_shutdown(); break; default: blob - f1002b8b938177ae95c844cfb196955ad824a0a4 blob + 4f10d5b34758e3295506e6968f6484005cde9612 --- gotwebd/gotweb.c +++ gotwebd/gotweb.c @@ -1874,8 +1874,10 @@ gotweb_sighdlr(int sig, short event, void *arg) break; case SIGCHLD: break; - case SIGINT: case SIGTERM: + /* continue until the parent exits */ + break; + case SIGINT: gotweb_shutdown(); break; default: blob - 38dae76d2223e3d613a2588c2d45c1016115083e blob + f868bed8cfb869337fbd6d4150b9ca347187f066 --- gotwebd/gotwebd.c +++ gotwebd/gotwebd.c @@ -423,6 +423,18 @@ gotwebd_dispatch_gotweb(int fd, short event, void *arg } } +static void +gotwebd_stop(void) +{ + if (main_compose_sockets(gotwebd_env, GOTWEBD_IMSG_CTL_STOP, + -1, NULL, 0) == -1) + fatal("send_imsg GOTWEBD_IMSG_CTL_STOP"); + + if (main_compose_login(gotwebd_env, GOTWEBD_IMSG_CTL_STOP, + -1, NULL, 0) == -1) + fatal("send_imsg GOTWEBD_IMSG_CTL_STOP"); +} + void gotwebd_sighdlr(int sig, short event, void *arg) { @@ -439,6 +451,8 @@ gotwebd_sighdlr(int sig, short event, void *arg) log_info("%s: ignoring SIGUSR1", __func__); break; case SIGTERM: + gotwebd_stop(); + break; case SIGINT: gotwebd_shutdown(); break; blob - 892c1cb41050f6c7bf46f99e16faad73265e9d2c blob + b15f41142f74f4762fc71d191f488305088de5ce --- gotwebd/gotwebd.h +++ gotwebd/gotwebd.h @@ -149,6 +149,7 @@ enum imsg_type { GOTWEBD_IMSG_CFG_DONE, GOTWEBD_IMSG_CTL_PIPE, GOTWEBD_IMSG_CTL_START, + GOTWEBD_IMSG_CTL_STOP, GOTWEBD_IMSG_LOGIN_SECRET, GOTWEBD_IMSG_AUTH_SECRET, GOTWEBD_IMSG_AUTH_CONF, blob - 9504381f3d7a57fcce332e523404ead0eb3ebe74 blob + 7cad84cee74bf07fc1a726a883a768f6a4ae69dd --- gotwebd/login.c +++ gotwebd/login.c @@ -56,6 +56,7 @@ struct gotwebd_login_client { static volatile int client_cnt; static int inflight; +static volatile int stopping; static char login_token_secret[32]; @@ -411,6 +412,22 @@ login_shutdown(void) } static void +login_stop(void) +{ + struct gotwebd *env = gotwebd_env; + + stopping = 1; + + if (env->iev_gotsh) { + evtimer_del(&env->login_pause_ev); + event_del(&env->iev_gotsh->ev); + close(env->iev_gotsh->ibuf.fd); + env->iev_gotsh->ibuf.fd = -1; + imsgbuf_clear(&env->iev_gotsh->ibuf); + } +} + +static void login_sighdlr(int sig, short event, void *arg) { switch (sig) { @@ -425,8 +442,10 @@ login_sighdlr(int sig, short event, void *arg) break; case SIGCHLD: break; - case SIGINT: case SIGTERM: + login_stop(); + break; + case SIGINT: login_shutdown(); break; default: @@ -603,7 +622,7 @@ login_accept(int fd, short event, void *arg) backoff.tv_sec = 1; backoff.tv_usec = 0; - if (event_add(&iev->ev, NULL) == -1) { + if (!stopping && event_add(&iev->ev, NULL) == -1) { log_warn("event_add"); return; } @@ -624,7 +643,8 @@ login_accept(int fd, short event, void *arg) case EMFILE: case ENFILE: event_del(&iev->ev); - evtimer_add(&env->login_pause_ev, &backoff); + if (!stopping) + evtimer_add(&env->login_pause_ev, &backoff); return; default: log_warn("accept"); @@ -766,6 +786,9 @@ login_dispatch_main(int fd, short event, void *arg) case GOTWEBD_IMSG_CTL_START: login_launch(env); break; + case GOTWEBD_IMSG_CTL_STOP: + login_stop(); + break; case GOTWEBD_IMSG_LOGIN_SECRET: if (imsg_get_data(&imsg, login_token_secret, sizeof(login_token_secret)) == -1) @@ -793,7 +816,8 @@ accept_paused(int fd, short event, void *arg) { struct gotwebd *env = gotwebd_env; - event_add(&env->iev_gotsh->ev, NULL); + if (!stopping) + event_add(&env->iev_gotsh->ev, NULL); } void blob - dfc46f68407be06d91de5ec11f3b91c189d4ec8a blob + 387e6c57b105661f9840264b1240b2880aba9517 --- gotwebd/sockets.c +++ gotwebd/sockets.c @@ -66,6 +66,7 @@ #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) static volatile int client_cnt; +static volatile int stopping; static struct timeval timeout = { TIMEOUT_DEFAULT, 0 }; @@ -148,7 +149,8 @@ cleanup_request(struct request *c) del_request(c); - event_add(&c->sock->ev, NULL); + if (!stopping) + event_add(&c->sock->ev, NULL); if (evtimer_initialized(&c->tmo)) evtimer_del(&c->tmo); @@ -158,6 +160,9 @@ cleanup_request(struct request *c) close(c->fd); free(c->buf); free(c); + + if (stopping && client_cnt <= 0) + sockets_shutdown(); } static void @@ -757,6 +762,32 @@ recv_fcgi_pipe(struct gotwebd *env, struct imsg *imsg) } static void +sockets_stop(void) +{ + struct socket *sock; + + stopping = 1; + + /* + * Deactivate sockets but to do not free them. + * Client connections maintain a pointer to them via c->sock. + */ + TAILQ_FOREACH(sock, &gotwebd_env->sockets, entry) { + if (sock->fd != -1) { + close(sock->fd); + sock->fd = -1; + } + if (event_initialized(&sock->ev)) + event_del(&sock->ev); + if (evtimer_initialized(&sock->ev)) + evtimer_del(&sock->pause); + } + + if (client_cnt == 0) + sockets_shutdown(); +} + +static void sockets_dispatch_main(int fd, short event, void *arg) { struct imsgev *iev = arg; @@ -804,6 +835,9 @@ sockets_dispatch_main(int fd, short event, void *arg) case GOTWEBD_IMSG_CTL_START: sockets_launch(env); break; + case GOTWEBD_IMSG_CTL_STOP: + sockets_stop(); + break; default: fatalx("%s: unknown imsg type %d", __func__, imsg.hdr.type); @@ -836,8 +870,10 @@ sockets_sighdlr(int sig, short event, void *arg) break; case SIGCHLD: break; - case SIGINT: case SIGTERM: + sockets_stop(); + break; + case SIGINT: sockets_shutdown(); break; default: @@ -1193,7 +1229,8 @@ sockets_socket_accept(int fd, short event, void *arg) backoff.tv_usec = 0; if (event & EV_TIMEOUT) { - event_add(&sock->ev, NULL); + if (!stopping) + event_add(&sock->ev, NULL); return; } @@ -1207,7 +1244,8 @@ sockets_socket_accept(int fd, short event, void *arg) case EINTR: case EWOULDBLOCK: case ECONNABORTED: - event_add(&sock->ev, NULL); + if (!stopping) + event_add(&sock->ev, NULL); return; case EMFILE: case ENFILE: @@ -1225,7 +1263,8 @@ sockets_socket_accept(int fd, short event, void *arg) close(s); if (c != NULL) free(c); - event_add(&sock->ev, NULL); + if (!stopping) + event_add(&sock->ev, NULL); return; } @@ -1234,7 +1273,8 @@ sockets_socket_accept(int fd, short event, void *arg) log_warn("%s: calloc", __func__); close(s); cgi_inflight--; - event_add(&sock->ev, NULL); + if (!stopping) + event_add(&sock->ev, NULL); return; } @@ -1244,7 +1284,8 @@ sockets_socket_accept(int fd, short event, void *arg) close(s); cgi_inflight--; free(c); - event_add(&sock->ev, NULL); + if (!stopping) + event_add(&sock->ev, NULL); return; }