I wrote a server that accepts a socket connection on a secondary port to stream debug information, which usually goes to stderr . This second port, the error service port, is only for one connection at a time, which is convenient because it allows me to redirect stderr using the dup2(2) call. (See Is it possible to redirect the stderr of the parent process to the socket file descriptor to a forked process? ).
The following code is almost satisfactory in every way. When the client enters the port, the stderr stream is routed to the socket. When another client enters the system, the flow is redirected again, and the first client stops receiving: completely satisfactory.
If this does not match the design, when the client closes the connection, the server crashes because it tries to write() close the socket.
I have a rudimentary signal handler for normal child processes, but I'm not sure how to handle a specific signal from the parent process when the error socket closes.
How can I grab the signal (from the parent) that the connection on the ERR_PORT_NUM server is closed and return the signal handler (or dup ) stderr back to /dev/null for the next client client error?
Also, what should I do with the original client connection errors when connecting the second? Currently, the first customer remains dangling. Even unsightly termination of the first connection is acceptable.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <fcntl.h> #include <errno.h> #include <pwd.h> #include <signal.h> #include <netinet/in.h> #include <sys/mman.h> #define PORT_NUM 12345 #define ERR_PORT_NUM 54321 static void child_handler(int signum) { switch (signum) { case SIGALRM: exit(EXIT_FAILURE); break; case SIGUSR1: exit(EXIT_SUCCESS); break; case SIGCHLD: exit(EXIT_FAILURE); break; } } static void daemonize(void) { /* Trap signals that we expect to recieve */ signal(SIGUSR1, child_handler); signal(SIGALRM, child_handler); signal(SIGCHLD, SIG_IGN); /* A child process dies */ signal(SIGTSTP, SIG_IGN); /* Various TTY signals */ signal(SIGTTOU, SIG_IGN); signal(SIGTTIN, SIG_IGN); signal(SIGHUP, SIG_IGN); /* Ignore hangup signal */ signal(SIGTERM, SIG_DFL); /* Die on SIGTERM */ freopen("/dev/null", "r", stdin); freopen("/dev/null", "w", stdout); freopen("/dev/null", "w", stderr); } static void server_work(void) { int sockfd, err_sockfd; socklen_t clilen; struct sockaddr_in serv_addr, cli_addr, err_serv_addr, err_cli_addr; struct timeval tv = { 0 }; int new_stderr; sockfd = socket(AF_INET, SOCK_STREAM, 0); err_sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0 || err_sockfd < 0) return; memset((char *) &serv_addr, '\0', sizeof(serv_addr)); memset((char *) &err_serv_addr, '\0', sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(PORT_NUM); err_serv_addr.sin_family = AF_INET; err_serv_addr.sin_addr.s_addr = INADDR_ANY; err_serv_addr.sin_port = htons(ERR_PORT_NUM); if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) return; if (bind (err_sockfd, (struct sockaddr *) &err_serv_addr, sizeof(err_serv_addr)) < 0) return; listen(sockfd, 5); listen(err_sockfd, 5); clilen = sizeof(cli_addr); while (1) { int maxfd; fd_set read_sockets_set; FD_ZERO(&read_sockets_set); FD_SET(sockfd, &read_sockets_set); FD_SET(err_sockfd, &read_sockets_set); maxfd = (err_sockfd > sockfd) ? err_sockfd : sockfd; if (select(maxfd + 1, &read_sockets_set, NULL, NULL, NULL) < 0) { break; } if (FD_ISSET(sockfd, &read_sockets_set)) { /* Typical process fork(2) and such ... not gremaine to the question. */ } if (FD_ISSET(err_sockfd, &read_sockets_set)) { new_stderr = accept(err_sockfd, (struct sockaddr *) &err_cli_addr, &clilen); dup2(new_stderr, STDERR_FILENO); } } close(sockfd); close(err_sockfd); return; } int main(int argc, char *argv[]) { daemonize(); /* greatly abbreviated for question */ server_work(); return 0; }