From 2ee152f5438ff60e79909024b2d06a7865b2ab6d Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Tue, 21 Apr 2026 11:46:35 +0200 Subject: [PATCH] add more child handling --- nwwebui.c | 141 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 104 insertions(+), 37 deletions(-) diff --git a/nwwebui.c b/nwwebui.c index 7b7389d..c38716f 100644 --- a/nwwebui.c +++ b/nwwebui.c @@ -435,10 +435,14 @@ static void cleanup_child_process(int *child_stdin_fd, int *child_stdout_fd, pid if (*child_pid > 0) { int status = 0; pid_t w = waitpid(*child_pid, &status, WNOHANG); + if (w == 0) { kill(*child_pid, SIGTERM); waitpid(*child_pid, &status, 0); + } else if (w < 0 && errno != ECHILD) { + log_msg(LOG_LEVEL_ERROR, "waitpid failed for smart child: %s", strerror(errno)); } + *child_pid = -1; } } @@ -455,31 +459,53 @@ static void handle_connection_plain(int client_fd, const nw_config_t *cfg) { child_pid = spawn_smart_perl(cfg, &child_stdin_fd, &child_stdout_fd); + if (set_nonblocking(client_fd) < 0) { + log_msg(LOG_LEVEL_ERROR, "Could not set client socket non-blocking"); + goto cleanup; + } + if (set_nonblocking(child_stdout_fd) < 0) { log_msg(LOG_LEVEL_ERROR, "Could not set child stdout non-blocking"); goto cleanup; } for (;;) { - struct pollfd pfd; - pfd.fd = child_stdout_fd; - pfd.events = POLLIN | POLLHUP | POLLERR; - pfd.revents = 0; + struct pollfd pfds[2]; + pfds[0].fd = client_fd; + pfds[0].events = POLLIN | POLLHUP | POLLERR; + pfds[0].revents = 0; - ssize_t n = read(client_fd, buf, sizeof(buf)); - if (n > 0) { - if (write_all_fd(child_stdin_fd, buf, (size_t)n) < 0) { - log_msg(LOG_LEVEL_ERROR, "Write to Perl stdin failed"); - break; + pfds[1].fd = child_stdout_fd; + pfds[1].events = POLLIN | POLLHUP | POLLERR; + pfds[1].revents = 0; + + int pr = poll(pfds, 2, -1); + if (pr < 0) { + if (errno == EINTR) { + continue; } - } else if (n == 0) { - break; - } else if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { + log_msg(LOG_LEVEL_ERROR, "poll failed in plain handler: %s", strerror(errno)); break; } - int pr = poll(&pfd, 1, 10); - if (pr > 0 && (pfd.revents & (POLLIN | POLLHUP | POLLERR))) { + /* Client -> Perl */ + if (pfds[0].revents & (POLLIN | POLLHUP | POLLERR)) { + ssize_t n = read(client_fd, buf, sizeof(buf)); + if (n > 0) { + if (write_all_fd(child_stdin_fd, buf, (size_t)n) < 0) { + log_msg(LOG_LEVEL_ERROR, "Write to Perl stdin failed"); + break; + } + } else if (n == 0) { + break; + } else if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { + log_msg(LOG_LEVEL_ERROR, "Read from HTTP client failed: %s", strerror(errno)); + break; + } + } + + /* Perl -> Client */ + if (pfds[1].revents & (POLLIN | POLLHUP | POLLERR)) { ssize_t rn = read(child_stdout_fd, buf, sizeof(buf)); if (rn > 0) { if (write_all_fd(client_fd, buf, (size_t)rn) < 0) { @@ -489,6 +515,7 @@ static void handle_connection_plain(int client_fd, const nw_config_t *cfg) { } else if (rn == 0) { break; } else if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { + log_msg(LOG_LEVEL_ERROR, "Read from Perl stdout failed: %s", strerror(errno)); break; } } @@ -518,6 +545,11 @@ static void handle_connection_tls(SSL_CTX *ctx, int client_fd, const nw_config_t pid_t child_pid = -1; unsigned char buf[NW_BUF_SZ]; + if (set_nonblocking(client_fd) < 0) { + log_msg(LOG_LEVEL_ERROR, "Could not set client socket non-blocking"); + goto cleanup; + } + ssl = SSL_new(ctx); if (!ssl) { log_msg(LOG_LEVEL_ERROR, "SSL_new failed"); @@ -542,31 +574,46 @@ static void handle_connection_tls(SSL_CTX *ctx, int client_fd, const nw_config_t } for (;;) { - struct pollfd pfd; - pfd.fd = child_stdout_fd; - pfd.events = POLLIN | POLLHUP | POLLERR; - pfd.revents = 0; + struct pollfd pfds[2]; + pfds[0].fd = client_fd; + pfds[0].events = POLLIN | POLLHUP | POLLERR; + pfds[0].revents = 0; - /* Read from TLS client, decrypt data, and forward it to Perl stdin */ - int n = SSL_read(ssl, buf, sizeof(buf)); - if (n > 0) { - if (write_all_fd(child_stdin_fd, buf, (size_t)n) < 0) { - log_msg(LOG_LEVEL_ERROR, "Write to Perl stdin failed"); - break; + pfds[1].fd = child_stdout_fd; + pfds[1].events = POLLIN | POLLHUP | POLLERR; + pfds[1].revents = 0; + + int pr = poll(pfds, 2, -1); + if (pr < 0) { + if (errno == EINTR) { + continue; } - } else { - int err = SSL_get_error(ssl, n); - if (err == SSL_ERROR_ZERO_RETURN) { - break; - } - if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) { - break; + log_msg(LOG_LEVEL_ERROR, "poll failed in TLS handler: %s", strerror(errno)); + break; + } + + /* TLS client -> Perl */ + if ((pfds[0].revents & (POLLIN | POLLHUP | POLLERR)) || SSL_pending(ssl) > 0) { + int n = SSL_read(ssl, buf, sizeof(buf)); + if (n > 0) { + if (write_all_fd(child_stdin_fd, buf, (size_t)n) < 0) { + log_msg(LOG_LEVEL_ERROR, "Write to Perl stdin failed"); + break; + } + } else { + int err = SSL_get_error(ssl, n); + if (err == SSL_ERROR_ZERO_RETURN) { + break; + } + if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) { + log_msg(LOG_LEVEL_ERROR, "SSL_read failed"); + break; + } } } - /* Read any output from the Perl process and send it back through TLS */ - int pr = poll(&pfd, 1, 10); - if (pr > 0 && (pfd.revents & (POLLIN | POLLHUP | POLLERR))) { + /* Perl -> TLS client */ + if (pfds[1].revents & (POLLIN | POLLHUP | POLLERR)) { ssize_t rn = read(child_stdout_fd, buf, sizeof(buf)); if (rn > 0) { if (send_all_ssl(ssl, buf, (size_t)rn) < 0) { @@ -576,11 +623,11 @@ static void handle_connection_tls(SSL_CTX *ctx, int client_fd, const nw_config_t } else if (rn == 0) { break; } else if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { + log_msg(LOG_LEVEL_ERROR, "Read from Perl stdout failed: %s", strerror(errno)); break; } } - /* Exit if the child process is already gone */ if (child_pid > 0) { int status = 0; pid_t w = waitpid(child_pid, &status, WNOHANG); @@ -601,9 +648,18 @@ cleanup: close(client_fd); } +/* ------------------------------------------------------------ */ +/* SIGCHLD handler */ +/* ------------------------------------------------------------ */ + static void reap_children(int sig) { (void)sig; - while (waitpid(-1, NULL, WNOHANG) > 0) { + + for (;;) { + pid_t pid = waitpid(-1, NULL, WNOHANG); + if (pid <= 0) { + break; + } } } @@ -621,7 +677,18 @@ int main(int argc, char **argv) { smart_conf_path = argv[1]; } - signal(SIGCHLD, reap_children); + { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = reap_children; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; + + if (sigaction(SIGCHLD, &sa, NULL) < 0) { + die("sigaction(SIGCHLD)"); + } + } + signal(SIGPIPE, SIG_IGN); log_open();