add more child handling
This commit is contained in:
141
nwwebui.c
141
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();
|
||||
|
||||
Reference in New Issue
Block a user