Bash shell launched by forkpty() spawns child processes that ignore SIGINT. Why and how to ensure SIGINT is not ignored

bashcptyshellsignals

I have a C program that uses forkpty to execute a bash shell. I'm finding that the programs launched by this shell are launched with SIGINT ignored, so when I send a Ctrl-C to the shell they never close.

example:

int masterFd;
char* args[] = {"/bin/bash", "-i", NULL };
int procId = forkpty(&masterFd, NULL, NULL,  NULL);
if( procId == 0 ){
  execve( args[0], args, NULL);
}
else {
   // Simple code that reads from standard in and writes to masterFd.  
   // I also register for Ctrl-C and write it to masterFd if caught
}

Other control characters seem to make it through, ctrl-D, ctrl-? etc. However, whenever I look at the status of a process launched by the new bash shell it appears as if SIGINT is masked out.

MyShell:# sleep 1000

StandardTerm:#  ps -ef | grep sleep
root    26611  19278  0  17:44  pts/1   00:00:00 sleep 1000
root    26613  32376  0  17:44  pts/1   00:00:00 grep sleep

StandardTerm:# grep Sig proc/26611/status
SigQ:    0/256428
SigPnd:  0000000000000000
SigBlk:  0000000000000000
SigIgn:  0000000000010006   <- THE 6 IS THE PROBLEM
SigCgt:  0000000180000000

SigIgn has the 2 bit set, which means 2 (SIGINT) is ignored. if I do the exact same thing, but run sleep (or cat a giant file or whatever) in a standard terminal, this bit is cleared. What am I doing when I launch my pty bash that is causing it to create grandchildren programs with SIGINT ignored?

Moreover, if I send a SIGINT signal to the process

StandardTerm:# kill -2 26611

nothing happens. What's strange is when I send the same command to the bash shell I forkpty'ed IT works, because that bash shell is not ignoring SIGINT.

Best Answer

This outcome cannot be due to the code block you showed in your question. There must be something else in other parts of your overall setup.

Given only the usage scenario you described, the most likely reason is that you actually set SIGINT to be ignored in your code somewhere before the forkpty, or before the execve in forkpty’s child.

This would yield the outcome you describe because then your forkpty’ed bash -i would inherit such SIGINT-ignored setup, and while it would set it otherwise for its own internal purposes (thus not ignoring SIGINT) it would also reset it to the inherited ignored state for each and every command it would spawn.

This is documented Bash’s behavior, see Bash’s man-page at “COMMAND EXECUTION ENVIRONMENT” chapter, specifically the paragraph saying:

When a simple command other than a builtin or shell function is to be executed, it is invoked in a separate execution environment that consists of the following. [...]

[...]

o traps caught by the shell are reset to the values inherited from the shell's parent, and traps ignored by the shell are ignored

HTH

Related Question