3

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.

0

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

-1

maybe you just need to do:

stty sane

I notice that the forkpty() man page says it will copy the termios settings of *termp to the newly opened pty, but it doesn't specifically say what it does with these otherwise, and the only not-NULL arg you hand forkpty() is for the pty master fd. I would guess you'd wind up with an entirely NULL termios structure, which can't be very useful. It wouldn't bother bash terribly which has got readline() to handle all its own terminal stuff, and so it would interpret all of the default characters by default anyhow.

here's a blockquote from a pretty informative article on the subject:

stty's -F option can be great for peeking at what some other program is doing to its terminal. If you run tty in a shell, it will print the path to that shell's terminal device (usually of the form /dev/pts/N, at least under Linux). Now, from a different shell, you can run stty -a -F /dev/pts/N to see how the first shell's terminal is configured. You can then run programs in the first shell, and repeat the stty incant in shell two to see what settings are getting set. For example, if I run stty -F /dev/pts/10 right now (while I have a bash talking to a gnome-terminal via that pty), I see:

    $ stty -F /dev/pts/10
      speed 38400 baud; line = 0;
      eol = M-^?; eol2 = M-^?; swtch = M-^?; lnext = <undef>; min = 1; time = 0;
     -icrnl iutf8
     -icanon -echo

So we can see that bash/readline has disabled CR→LF translation on input (icrnl), disabled canonical mode and echo, but turned on UTF-8 mode (because bash detected a utf-8 locale). Note that if I run stty directly in that shell, I see something slightly different:

     $ stty
       speed 38400 baud; line = 0;
       eol = M-^?; eol2 = M-^?; swtch = M-^?;
       iutf8

This is because bash maintains its own set of termios settings (for readline), and saves and restores settings around running programs, so that settings in place while running a program are different from those in place while you're typing at the bash prompt.

  • That doesn't seem to do it. Additionally, I looked into the parameters set by the PTY using tcgetattr() and they are identical to standard shells that DO work. – djc6535 Nov 9 '15 at 22:09
  • @djc6535 - you did your check while some other process was reading the terminal right? like you did cat in your bg'd bash and then did stty -F/dev/pts/[pty] -a from another terminal or something, right? – mikeserv Nov 9 '15 at 22:18
  • I ran the stty while cat > /dev/null was running – djc6535 Nov 9 '15 at 22:25
  • @djc6535 - then i dunno. i did notice the related shellout() function indicates that default terminal settings are likely unsuitable for interactive use - whatever that means - and i suspected something similar might be going on with you. – mikeserv Nov 9 '15 at 22:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.