#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SER_PORT_ID 1234 /* The server's port */ static int child; /* child = 0, parent != 0 */ static void child1(void); static void child2(void); static void sigChld(int signum, siginfo_t * siginfo, void * ucontext); static void sigCaught(int signum, siginfo_t * siginfo, void * ucontext); static void setupEnv(void); static void serverLoop(void); static void processConnect(int csfd); static void sigtestExit(); /** * main: * @argc: The count of command line data * @argv: The array of command line strings * * The entry point of the executable. */ int main(int argc, char * argv[]) { setupEnv(); /* * spawn two child processes so they (the children) can be sent SIGNAL's * and the behaviour can be observed... */ if ((child = fork()) < 0) /* fork error */ { perror("Child1 could not be created"); exit(1); } else if (child == 0) /* In child */ { child1(); /* never returns... */ } if ((child = fork()) < 0) /* fork error */ { perror("Monitor child could not be created"); exit(1); } else if (child == 0) /* In child */ { child2(); /* never returns... */ } serverLoop(); /* Never returns */ return 0; } /** * child1: * * This is the first child. */ static void child1(void) { printf("child1 started\n"); for ( ; ; ); } /** * child2: * * This is the second child. */ static void child2(void) { printf("child2 started\n"); for ( ; ; ); } /** * sigChild: * @signum: The signal that got us here. * @siginfo: Additional data associated with the signal. * @ucontext: Pointer to ucontext_t struct. Unused here. * * Signal handler for catching SIGCHLD. Is re-entrant... */ static void sigChld(int signum, siginfo_t * siginfo, void * ucontext) { int childStat; printf("sigChld: %d receieved signal %d\n", getpid(), signum); if (signum != SIGCHLD) /* technically can't happen here... */ return; /* but ignore it if it does */ waitpid(siginfo->si_pid, &childStat, 0); /* orderly child shutdown... */ /* * These next 4 if blocks are where it gets interesting...this I believe * demonstrates the inconsistency. */ if (WIFEXITED(childStat)) printf("sigChld: %d child %d WIFEXITED with childStat %d\n", getpid(), siginfo->si_pid, WEXITSTATUS(childStat)); if (WIFEXITED(siginfo->si_status)) printf("sigChld: %d child %d WIFEXITED with si_status %d\n", getpid(), siginfo->si_pid, WEXITSTATUS(siginfo->si_status)); if (WIFSIGNALED(childStat)) { printf("sigChld: %d child %d WIFSIGNALED with childStat %d\n", getpid(), siginfo->si_pid, WTERMSIG(childStat)); } if (WIFSIGNALED(siginfo->si_status)) { printf("sigChld: %d child %d WIFSIGNALED with si_status %d\n", getpid(), siginfo->si_pid, WTERMSIG(siginfo->si_status)); } return; } /** * sigCaught: * @signum: The signal that got us here. * @siginfo: Additional data associated with the signal. * @ucontext: Pointer to ucontext_t struct. Unused here. * * Signal handler for catching all but SIGCHLD. Is re-entrant... */ static void sigCaught(int signum, siginfo_t * siginfo, void * ucontext) { int childStat; printf("sigCaught: %d receieved signal %d\n", getpid(), signum); if (signum == SIGCHLD) /* technically can't happen here... */ return; /* but ignore it if it does */ exit(signum); } /** * setupEnv: * * Setup the environment, i.e, signal handlers, etc. */ static void setupEnv(void) { struct sigaction action; printf("sigtest started\n"); atexit(sigtestExit); action.sa_handler = NULL; action.sa_sigaction = sigChld; sigemptyset(&action.sa_mask); /* * Since sigChld is re-entrant, its okay to have SA_NODEFER, and necessary, * too, since I want to waitpid() on ALL child processes, and since * waitpid() won't block if called after the child croaked, its just simply * a way to affect an orderly shutdown of child processes, avoiding the * zombie state...of course, I could be wrong, and without SA_NODEFER, the * SIGCHLD is only _temporarily_ blocked, then I'll get subsequent SIGCHLDs * after this handler completes...either way, in this case its irrelevant. */ action.sa_flags = SA_SIGINFO | SA_NOCLDSTOP | SA_RESTART | SA_NODEFER; sigaction(SIGCHLD, &action, NULL); /* * Now catch the rest of the signals... */ action.sa_handler = NULL; action.sa_sigaction = sigCaught; sigfillset(&action.sa_mask); action.sa_flags = SA_SIGINFO; sigaction(SIGINT, &action, NULL); sigaction(SIGHUP, &action, NULL); sigaction(SIGTERM, &action, NULL); sigaction(SIGQUIT, &action, NULL); sigaction(SIGTSTP, &action, NULL); sigaction(SIGSTOP, &action, NULL); return; } /** * serverLoop: * * The main loop for listening for and handling connections. */ static void serverLoop(void) { fd_set read_fs; int sfd, csfd, n, from_len, saved_errno; struct sockaddr_in sin; FD_ZERO(&read_fs); /* Clear the file descriptors */ if ((sfd = socket(AF_INET,SOCK_STREAM,0)) < 0) /* Create the socket */ { perror("Socket creation failed"); exit(1); } n = 1; if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) < 0) { perror("setsockopt failed"); exit(1); } bzero((caddr_t)&sin,sizeof(struct sockaddr_in));/* Zero out the sock_addr */ sin.sin_family = AF_INET; /* Internet protocol */ sin.sin_addr.s_addr = INADDR_ANY; /* bind all local interfaces */ sin.sin_port = htons(SER_PORT_ID); /* Receive on the server port */ if (bind(sfd, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) < 0) /* Bind socket to port */ { perror("Socket binding failed"); exit(1); } if (listen(sfd, 10) < 0) /* max. 10 q'd connections */ { perror("Listen failed"); exit(1); } for ( ; ; ) /* main server loop */ { FD_SET(sfd,&read_fs); do { printf("selecting...\n"); n = select(sfd+1,&read_fs,NULL,NULL,NULL); if (n < 0) { saved_errno = errno; perror("select error"); if (saved_errno != EINTR) { perror("Server select error"); exit(1); } } else break; /* no error, so handle it... */ } while (saved_errno == EINTR); /* restart select on EINTR */ if (FD_ISSET(sfd, &read_fs)) { from_len = sizeof(struct sockaddr_in); printf("accepting...\n"); if ((csfd = accept(sfd, (struct sockaddr *)&sin, (socklen_t *)&from_len)) < 0) { perror("Accept error"); exit(1); } if ((child = fork()) < 0) /* fork error */ { perror("Child could not be created"); exit(1); } else if (child == 0) /* In child */ { close(sfd); /* Child doesn't need this */ processConnect(csfd); /* never returns... */ } close(csfd); /* Parent doesn't need this one */ } /* if (FD_ISSET(sfd,&read_fs)) */ } /* for ( ; ; ) */ exit(99); /* Never gets here... */ } /** * processConnect: * @csfd: The socket for this connection * * This is the child process dedicated to this connection. */ static void processConnect(int csfd) { uint8_t * buf; int n = 0; if (!(buf = calloc((size_t)1024, sizeof(uint8_t)))) { perror("calloc failed"); exit(1); } /* * This means we're in the child, and have an active connection... */ printf("\nReading csfd %d...\n", csfd); if ((n = read(csfd, buf, sizeof(buf))) < 0) { perror("Server read error"); exit(1); } if (n == 0) { printf("Connection closed...exiting\n"); exit(2); } printf("Read 0x%x bytes...\n", n); buf[n] = 0; printf("%s", buf); // would do something with the data here, but this is just for testing... close(csfd); exit(0); /* child exits... */ } /** * sigtestExit: * * Since master server is exiting, everyone must be killed... */ static void sigtestExit() { sigset_t sigset; printf("sigtest %d exiting\n", getpid()); if (child) /* if the main server is exiting... */ { sigfillset(&sigset); sigprocmask(SIG_BLOCK,&sigset,NULL); /* block all sigs... */ kill(0,SIGTERM); /* everybody in this proc. group */ } }