17

This question is old, and I am still not clear about why.


Original question in 2014:

In a Gnome Terminal tab, I ran

$ nohup chromium-browser &

But when I close the terminal tab, chromium-browser also exits. Isn't nohup supposed to prevent that? Gilles said:

nohup and disown both can be said to suppress SIGHUP, but in different ways. nohup makes the program ignore the signal initially (the program may change this). nohup also tries to arrange for the program not to have a controlling terminal, so that it won't be sent SIGHUP by the kernel when the terminal is closed. disown is purely internal to the shell; it causes the shell not to send SIGHUP when it terminates.

So doesn't nohup make chromium-browser ignore SIGHUP?

I am seeing this on other executables, too, such as and Emacs (GUI mode). But not on xeyes.

This is happening on Ubuntu 12.04, 32-bit when the question was posted.


Update in 2015,

Now I am running Ubuntu 14.04, with google-chrome instead of chromium-browser installed. Same thing that happened to chromium-browser before also happens to google-chrome now. nohup google-chrome 2>/dev/null & doesn't save it from being closed when the terminal tab is closed. /usr/bin/google-chrome is a link to a bash script /opt/google/chrome/google-chrome. Why does nohup applied to the bash script not work? How can we make it work on bash scripts? What about Python scripts?

Tim
  • 101,790

3 Answers3

16

When you close a GNOME Terminal window, a SIGHUP is sent to the shell that it was running. The shell will typically send a SIGHUP to every process group that it knows it created - even ones started with nohup - and then exit. If the shell is bash, it will skip sending a SIGHUP to any process group that the user marked with disown.

Running a command with nohup makes it ignore SIGHUP, but the process can change that. When the disposition of SIGHUP for a process is the default, then if it receives a SIGHUP, the process will be terminated.

Linux provides some tools to examine a running process's signal settings.

The chromium-browser shell script does an exec of the compiled app, so its process ID remains the same. So to see its signal settings, I ran nohup chromium-browser & and then looked at /proc/$!/status to see the signal disposition.

SigBlk: 0000000000000000
SigIgn: 0000000000001000
SigCgt: 0000000180014003

Those are hex numbers. This shows that SIGHUP is not caught and is not ignored. Only SIGPIPE (the 13th bit in SigIgn) is ignored. I traced this to the following code:

// Setup signal-handling state: resanitize most signals, ignore SIGPIPE.
void SetupSignalHandlers() {
  // Sanitise our signal handling state. Signals that were ignored by our
  // parent will also be ignored by us. We also inherit our parent's sigmask.
  sigset_t empty_signal_set;
  CHECK(0 == sigemptyset(&empty_signal_set));
  CHECK(0 == sigprocmask(SIG_SETMASK, &empty_signal_set, NULL));

  struct sigaction sigact;
  memset(&sigact, 0, sizeof(sigact));
  sigact.sa_handler = SIG_DFL;
  static const int signals_to_reset[] =
      {SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV,
       SIGALRM, SIGTERM, SIGCHLD, SIGBUS, SIGTRAP};  // SIGPIPE is set below.
  for (unsigned i = 0; i < arraysize(signals_to_reset); i++) {
    CHECK(0 == sigaction(signals_to_reset[i], &sigact, NULL));
  }

  // Always ignore SIGPIPE.  We check the return value of write().
  CHECK(signal(SIGPIPE, SIG_IGN) != SIG_ERR);
}

Despite the comment, signals ignored by the parent are not being ignored. A SIGHUP will kill chromium.

The workaround is to do what @xx4h points out: use the disown command in your bash so that, if bash has to exit, it does not send SIGHUP to the chromium-browser process group. You can write a function to do this:

mychromium () { /usr/bin/chromium-browser & disown $!; }
Mark Plotnick
  • 25,413
  • 3
  • 64
  • 82
  • Thanks. Now I come to understand mostly what you meant. "Since the script doesn't ever reset this trap, the actual chromium binary is called with SIGHUP's disposition set to the default." By "reset this trap", do you mean to change the trap for SIGHUP back to the default one, which terminates the process? How do you "reset this trap"? – Tim Mar 01 '16 at 16:00
  • Oh, by "reset this trap", I meant "set the disposition of this signal back to the way it was before the shell script set a handler for it". If the shell did trap "" 1, that would make the shell (and its children) ignore SIGHUP. I will clarify this. – Mark Plotnick Mar 01 '16 at 16:15
  • Thanks. Will check it after your clarification. I am now trying to understand daemon, nohup, disown and background, so I came back to revisit my old questions and replies that I didn't understood. The confusions about nohup, disown and background are mostly solved, and I am stuck with the concept of daemon and how to daemonize a process (see below). I appreciate again if you have time to help again. – Tim Mar 01 '16 at 16:23
  • see http://unix.stackexchange.com/questions/266565/daemonize-a-process-in-shell, http://unix.stackexchange.com/questions/266594/order-between-nohup-and-redirection, http://stackoverflow.com/questions/35705451/is-changing-parent-process-necessary-when-daemonize-a-process, http://unix.stackexchange.com/questions/266434/are-kernel-threads-processes-and-daemons, http://unix.stackexchange.com/questions/266600/have-i-created-a-daemon-successfuly – Tim Mar 01 '16 at 16:23
  • I looked at the chromium app code, and it turns out the C++ code in the application resets SIGHUP to the default unconditionally. The trap commands in the shell script wrapper don't prevent this, and running nohup can't prevent this. I will revise my answer to reflect this. – Mark Plotnick Mar 01 '16 at 18:08
  • How did you "traced this to the following code"? What tools did you use? – Tim Mar 01 '16 at 19:29
  • I ran strace -f -o s.out nohup chromium-browser, which logs all the system calls, then looked in the output file s.out for all uses of SIGHUP. I found one call that set SIGHUP to SIG_DFL. I then manually matched the sequence of system calls in the vicinity to the source code, which I had downloaded using apt-get source chromium-browser. – Mark Plotnick Mar 01 '16 at 19:41
  • Thanks. How did you "then matched the sequence of system calls in that file to the source code"? – Tim Mar 01 '16 at 19:43
  • I used grep to find patterns such as sig.*SIGHUP, then examined those files in a text editor. – Mark Plotnick Mar 01 '16 at 19:46
  • Does sigaction system call override what nohup did? – Tim Mar 01 '16 at 19:58
  • Yes, sigaction replaces the existing action. – Mark Plotnick Mar 01 '16 at 20:23
  • Thanks. I appreciate if you could also consider, https://unix.stackexchange.com/questions/484315/why-does-disown-h-make-chromium-browser-survive-closing-its-terminal-emulator – Tim Nov 26 '18 at 22:15
2

Chromium seems special.

nohup chromium-browser & disown should work in this case. See also: https://stackoverflow.com/questions/11421810/nohup-doesnt-work-with-chromium

xx4h
  • 2,400
2

If chromium-browser is anything like google-chrome then I think the most likely issue is that chromium-browser isn't chromium but is instead a shell-wrapper which initializes state then execs chromium.

In my google-chrome installation the binary is actually located in /opt/google/chrome and the wrapper in /usr/bin is just a shell script which sets up a lot of environment concerning xdg-* defaults and absolute paths and similar before replacing itself with the binary proper.

At this point any signals which nohup might have initially ignored on behalf of the script it called as its child will cease to matter and unless the wrapper script is careful to arrange it otherwise (which it isn't) the ctty is inherited.

Try file /usr/bin/chromium-browser to check if it is the shell-script I think it is. If so, consider rewriting it to better suit you.

I can say that just doing google-chrome 2>/dev/null & keeps it open for me, but I can't recall if that is a likely result of modifications I made to the script - it was over a year ago.

mikeserv
  • 58,310
  • Thanks. Same thing that happened to chromium-browser before also happens to google-chrome now. I don't have chromium-browser installed. nohup google-chrome 2>/dev/null & doesn't save it from being closed when the terminal tab is closed. google-chrome is a bash script /opt/google/chrome/google-chrome. Why does nohup applied to a bash script not work? How can we make it work on bash scripts? What about Python scripts? – Tim Apr 02 '15 at 13:47
  • Well, it does work on the script, but the script only runs for a few nano-seconds before being replaced by the actual chrome binary. – mikeserv Apr 02 '15 at 17:57
  • are you saying in the script there is exec on the actualy chrome binary? How do I modify the script then? Here is my /opt/google/chrome/google-chrome – Tim Apr 04 '15 at 21:13
  • @Tim - yeah - see the bottom? exec -a "$0" "$HERE/chrome" ... || exec -a "$0" "$HERE/chrome"... At the top $HERE is set to the value of readlink $0 (which is your installation path for google-chrome) but /opt/google/chrome/chrome is the binary - which is what the script replaces itself with. – mikeserv Apr 04 '15 at 21:22
  • @Tim: As far as how goes - you can just drop the exec probably. You'll wind up w/ an extra pid (beyond its 1000 others) and a waiting shell process, but i think that's the extent of it. Or else you might replace exec w/ nohup maybe. It all depends mostly on what chrome will tolerate - but it should work like that. – mikeserv Apr 05 '15 at 00:01