I'm writing a basic unix shell in C and I would like to catch Cntrl-C signals in the shell and pass them to foreground processes only, but not to background processes. The shell itself should keep running (and it does), and the Background processes should ignore a Cntrl-C and only be killed by a kill signal sent specifically to them, perhaps via a command-line "kill pid". Both foreground and background processes, however, should trigger the handler with SIGCHLD. Right now, however, the shell catches the Cntrl-C signal, and seems to correctly identify that there is no foreground process to pass the signal to, but the background process still dies.
I tried setting the group id of the background process to something else, and that solves the problem, but it creates a new problem. When I do that, my signal handler no longer catches the signal when the background process completes.
So far, I've looked at the man pages for SIGINT, I've read about 20 SO answers, I've tried setting the group id of the child to something different than the parent (solves the problem, but now child can no longer send SIGCHLD to parent), and I've checked that childid != foreground process and foregroundProcess == 0 when I'm running a background process. But still the background process gets killed. Any ideas?
I think my problem is somewhere in my signal handler, but not really sure:
in main:
struct sigaction sa;
sa.sa_handler = &handleSignal; /*passing function ref. to handler */
sa.sa_flags = SA_RESTART;
sigfillset(&sa.sa_mask); /*block all other signals while handling sigs */
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGCHLD, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
handleSignal looks like this:
void handleSignal(int signal){
int childid;
switch (signal) {
/*if the signal came from a child*/
case SIGCHLD:
/*get the child's id and status*/
childid = waitpid(-1,&childStatus,0);
/*No action for foreground processes that exit w/status 0 */
/*otherwise show pid & showStatus */
if ((childid != foregroundProcess)){
printf("pid %i:",childid);
showStatus(childStatus);
fflush(stdout);
}
break;
/* if signal came from somewhere else, pass it to foreground child */
/* if one exists. */
default:
printf("Caught signal: %i and passing it", signal);
printf(" to child w/pid: %i\n\n:", foregroundProcess);
fflush(stdout);
/*If there is a child, send signal to it. */
if (foregroundProcess){
printf("trying to kill foreground.\n");
fflush(stdout);
kill(foregroundProcess, signal);
}
}
}