Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What should interactive shells do in orphaned process groups?

Tags:

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?

like image 678
ridiculous_fish Avatar asked Dec 05 '12 07:12

ridiculous_fish


People also ask

What happens to orphaned processes in Linux?

In a Unix-like operating system any orphaned process will be immediately adopted by an implementation-defined system process: the kernel sets the parent to this process. This operation is called re-parenting and occurs automatically.

How do you identify orphaned processes?

Orphan Process:A process whose parent process no more exists i.e. either finished or terminated without waiting for its child process to terminate is called an orphan process. In the following code, parent finishes execution and exits while the child process is still executing and is called an orphan process now.

What is orphaned process in Unix?

Orphan processes are those processes that are still running even though their parent process has terminated or finished. A process can be orphaned intentionally or unintentionally. An intentionally orphaned process runs in the background without any manual support.


1 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.

like image 53
Phil Frost Avatar answered Sep 29 '22 10:09

Phil Frost