What should interactive shells do in orphaned process groups?

The short question is, what should a shell do if it is in an orphaned process group that doesn't own the tty? But I recommend reading the long question because it's amusing.

Here is a fun and exciting way to turn your laptop into a portable space heater, using your favorite shell (unless you're one of those tcsh weirdos):

#include <unistd.h>   
int main(void) {
    if (fork() == 0) {
        execl("/bin/bash", "/bin/bash", NULL);
    }
    return 0;
}

This causes bash to peg the CPU at 100%. zsh and fish do the same, while ksh and tcsh mumble something about job control and then keel over, which is a bit better, but not much. Oh, and it's a platform agnostic offender: OS X and Linux are both affected.

My (potentially wrong) explanation is as follows: the child shell detects it is not in the foreground: tcgetpgrp(0) != getpgrp(). Therefore it tries to stop itself: killpg(getpgrp(), SIGTTIN). But its process group is orphaned, because its parent (the C program) was the leader and died, and SIGTTIN sent to an orphaned process group is just dropped (otherwise nothing could start it again). Therefore, the child shell is not stopped, but it's still in the background, so it does it all again, right away. Rinse and repeat.

My question is, how can a command line shell detect this scenario, and what is the right thing for it to do? My thought is that the shell tries to read from stdin, and just exits if read gives it EIO.

Thanks for your thoughts!

Edit: I tried doing a zero-length read() on /dev/tty, and that succeeded, which is bad. To get the EIO, I actually have to be prepared to read some data off of /dev/tty.

Edit: Another thought I had was to kill(getpgrp(), 0). If the process group is orphaned, then I believe this will always fail. However, it may also fail because I don't have permission to signal the session leader.

Edit: For anyone finding this later, what I ended up doing is described at https://github.com/fish-shell/fish-shell/issues/422 . Also, how's the future?

Answers


Here's what strace says is happening:

--- SIGTTIN (Stopped (tty input)) @ 0 (0) ---
rt_sigaction(SIGTTIN, {SIG_IGN, [], SA_RESTORER, 0x7fd5f6989d80}, {SIG_DFL, [], SA_RESTORER, 0x7fd5f6989d80}, 8) = 0
ioctl(255, TIOCGPGRP, [9954])           = 0
rt_sigaction(SIGTTIN, {SIG_DFL, [], SA_RESTORER, 0x7fd5f6989d80}, {SIG_IGN, [], SA_RESTORER, 0x7fd5f6989d80}, 8) = 0
kill(0, SIGTTIN)                        = 0
--- SIGTTIN (Stopped (tty input)) @ 0 (0) ---
rt_sigaction(SIGTTIN, {SIG_IGN, [], SA_RESTORER, 0x7fd5f6989d80}, {SIG_DFL, [], SA_RESTORER, 0x7fd5f6989d80}, 8) = 0
ioctl(255, TIOCGPGRP, [9954])           = 0
rt_sigaction(SIGTTIN, {SIG_DFL, [], SA_RESTORER, 0x7fd5f6989d80}, {SIG_IGN, [], SA_RESTORER, 0x7fd5f6989d80}, 8) = 0
kill(0, SIGTTIN)                        = 0
[repeat...]

and here is why, from jobs.c, bash 4.2:

  while ((terminal_pgrp = tcgetpgrp (shell_tty)) != -1)
    {
      if (shell_pgrp != terminal_pgrp)
        {
          SigHandler *ottin;

          ottin = set_signal_handler(SIGTTIN, SIG_DFL);
          kill (0, SIGTTIN);
          set_signal_handler (SIGTTIN, ottin);
          continue;
        } 
      break;
    } 

Concerning what to do about it...well that's beyond my ability. But, I thought this was useful information, and a bit much for a comment.


Need Your Help

SecurityError: Blocked a frame with origin from accessing a cross-origin frame

javascript jquery iframe same-origin-policy

I am loading an &lt;iframe&gt; in my HTML page and trying to access the elements within it using Javascript, but when I try to execute my code, I get the following error:

UIBarButtonItem with UIImage Always Tinted iOS 7

objective-c ios7 uibarbuttonitem uitoolbar

I'm trying to add a UIBarButtonItem containing a UIImage to a UIToolbar. The image keeps being tinted and I can't get it to show as the original colored image - all I want to do is display an image,