add more child handling

This commit is contained in:
Mario Fetka
2026-04-21 11:46:35 +02:00
parent 829be767c9
commit 2ee152f543

141
nwwebui.c
View File

@@ -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();