Why does “su -c &” seemingly allow a command to run in the background without hanging up

background-processdaemonsu

I was helping a colleague who was having problems with a background process intermittently dying.

I found out that they were starting the background process by logging in to the server and executing:

su - <user> -c '<command>' &

"Aha", I exclaimed. "If you start a command with "&" it will hang-up when you exit the controlling terminal. You need to use something like nohup to achieve this. Really this process needs to support running as a daemon, tut tut."

We tested the above command to demonstrate my point and… it seemed to work: the process started by command didn't exit when we exited the terminal that ran the above command.

command is a custom Python script whose output goes to a file. As far as I can tell, there is no intelligent "daemonize" like capability in the script. It doesn't do any of the things needed to run as a daemon listed in the Wikipedia: Daemon (computing): Creation page.

Running the command like so behaves as expected:

<command> &
exit

In the above case the background process started by command exits when we exit the terminal.

My question is this:

  1. What is happening when we add "su – -c &" that prevents the process from exiting when our terminal exits. I would like to understand in detail with respect to the controlling terminal, standard input and output etc.

  2. Is this a reasonable way to achieve the goal of running this command as a background process. If not why, not?

I want to propagate best practices within my company, but I need to be able to demonstrate and back-up any recommendations I make.

I also want to understand what is going on exactly.

Best Answer

There are several ways a process might be killed because of a dying terminal.

  1. The first way is that the terminal driver in the kernel sends a SIGHUP signal to the controlling process for which the terminal is the controlling terminal. In most cases, the controlling process is the shell that is initially started in the terminal, and its controlling terminal is the one that its stdin, stdout and stderr are connected to. A process becomes disconnected from the controlling terminal if it calls setsid.

  2. The second way is that the shell, when it receives SIGHUP, re-sends that signal to its subprocesses — more precisely, to its background jobs. Some shells (ksh, bash, zsh) have a disown builtin to tell the shell not to send a SIGHUP to a particular job.

  3. If the terminal goes away and a program tries to read from it, it is notified of an end-of-file condition (or possibly EIO for a background job). If the terminal goes away and a program tries to write to it, the write returns with an EIO error. These might cause the program to exit.

So what su changes here is that (2) would normally cause the initial shell to kill the background job, but since that background process is running as a different user, the signal cannot be delivered, and the background process lives on.

Related Question