csci4061/lab07-code/select_AB.c
Michael Zhang 041f660ccd
f
2018-01-29 17:28:37 -06:00

89 lines
4.5 KiB
C

// Start two child processes which run sleep_print with differing
// delays and messages. Their output is redirected through pipes
// readable by the parent. Use select() to grab input from either
// pipe as it becomes available. Handle interrupt and terminate
// signals gracefully by setting a flag to shut down.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <signal.h>
#include <errno.h>
#define PREAD 0 // read and write ends of pipes
#define PWRITE 1
int signaled = 0; // global variable controls exit from main loop
void handle_signals(int signo){ // handler for some signals
char *msg = "select_AB: signaled, setting flag\n"; // print a message about the signal
write(STDERR_FILENO,msg,strlen(msg)); // avoid fprintf() as it is not reentrant
signaled = 1; // set global variable to indicate signal received
return;
}
int make_child(int pip[], char *delay, char *msg){ // Create a child which runs sleep_print
pipe(pip);
int pid = fork();
if(pid != 0){ // PARENT
return pid; // just return child pid
}
dup2(pip[PWRITE], STDOUT_FILENO); // CHILD: redirect standard out to pipe
execlp("./sleep_print","./sleep_print",delay,msg,NULL); // should not return
perror("exec failed");
exit(1);
}
int main() {
struct sigaction my_sa = {}; // portable signal handling setup with sigaction()
my_sa.sa_handler = handle_signals; // run function handle_signals
sigemptyset(&my_sa.sa_mask); // don't block any other signals during handling
my_sa.sa_flags = SA_RESTART; // always restart system calls on signals possible
sigaction(SIGTERM, &my_sa, NULL); // register SIGTERM with given action
sigaction(SIGINT, &my_sa, NULL); // register SIGINT with given action
int pipeA[2], pipeB[2]; // pipes for children to speak to parent
int pidA = make_child(pipeA, "1", "AAAA"); // create two children with different
int pidB = make_child(pipeB, "3", "BBBB"); // messages and delays between prints
close(pipeA[PWRITE]); // Parent doesn't use the write ends
close(pipeB[PWRITE]);
printf("select_AB: listening for children\n");
while(!signaled){ // enter a loop to listen until signaled or children die
fd_set read_set; // set of file descriptors for select()
FD_ZERO(&read_set); // init the set
FD_SET(pipeA[PREAD], &read_set); // set fds to contain read ends of pipe
FD_SET(pipeB[PREAD], &read_set);
int maxfd = pipeA[PREAD]; // need maximum fd for select()
maxfd = (maxfd < pipeB[PREAD]) ? pipeB[PREAD] : maxfd;
select(maxfd+1, &read_set, NULL, NULL, NULL); // sleep, wake up if any pipe ready for reading
char buf[1024];
int n;
if(FD_ISSET(pipeA[PREAD], &read_set)){ // check if pipeA has anything
n = read(pipeA[PREAD], buf, 1024); // read from pipe
buf[n] = '\0';
fprintf(stdout,"A had: |%s|\n",buf);
}
if(FD_ISSET(pipeB[PREAD], &read_set)){ // check if pipeB has anything
n = read(pipeB[PREAD], buf, 1024); // read from pipe
buf[n] = '\0';
fprintf(stdout,"B had: |%s|\n",buf);
}
}
kill(pidA, SIGTERM); // keyboard interrupt will signal children but a
kill(pidB, SIGTERM); // 'kill select_AB' will not, so always signal them here
fprintf(stderr,"select_AB: finishing\n");
exit(0);
}