Daemonizing and Upstart

When creating command-line applications, the user is usually present at the terminal to provide commands to the application and read its output. This is called interactive mode. But sometimes, you want to start a long-running program in the background, so it runs while you’re not present. This is called daemon mode, and the programs themselves are usually called daemons.

Daemons?

In contrast to their mythical counterpart, daemons under Unix are usually much more benign. Daemons are usually started when the system boots, are will continue running until they are manually stopped or a fatal error occurs. Common examples include Apache, MySQL and OpenSSH; these tools are present on a lot of Unix servers. If these daemons are started during boot, this is handled by a process called init.

There are a few implementations of init under Linux, most notably of which are sysvinit and Upstart. Sysvinit was an implementation used by many Linux distributions until not long ago and mimics the init system used by Unix System V. Ubuntu has since moved to their own system called Upstart which, while still somewhat compatible with the traditional Unix way of starting daemons, adds a lot of new features. Some of these features have implications I was not aware of until recently, and can influence the daemonizing process itself.

The Process Tree

Daemonizing takes advantage of a mechanism defined in POSIX that makes sure that any orphaned process is made a subprocess of init. In Unix land there is one process list, and all processes are children of init either directly or indirectly.

On a (very) clean Linux system with Apache and MySQL installed, this might look like this:

init
  ├─apache2 -k start
  │   ├─apache2 -k start
  │   └─apache2 -k start
  └─mysqld_safe /usr/bin/mysqld_safe
      └─mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql--log-error=/var/log/mysql/

As you can see, all processes are children of init, and they themselves may also have subprocesses. Apache has started two worker processes, and MySQL seems to have forked into a master and worker process. When a user logins it might look like this:

init
  ├─apache2 -k start
  │   ├─apache2 -k start
  │   └─apache2 -k start
  ├─login --      
  │   └─bash
  └─mysqld_safe /usr/bin/mysqld_safe
      └─mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql--log-error=/var/log/mysql/

A login process has been created, and bash was started as a subprocess to provide a shell for the user.

In the situations above the apache and mysql processes are daemons, they are running non-interactively and are direct children of init. The bash process is running interactively, and provides the currently logged in user with a shell. We can see this if we were to run a command.

init
  ├─apache2 -k start
  │   ├─apache2 -k start
  │   └─apache2 -k start
  ├─login --      
  │   └─bash
  │       └─tail -f some.log
  └─mysqld_safe /usr/bin/mysqld_safe
      └─mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql--log-error=/var/log/mysql/

The user has started the tail -f some.log command, for which bash has forked a separate child process.

So far, so good. Up until here there is nothing uncommon about the behaviour of init, and everything is working as I would expect from a normal POSIX environment. Let’s look what happens when we introduce Upstart.

Upstart and init

In the previous examples, a user directly logged in on the terminal and a session was started for him. A similar case is when connecting to a machine using SSH, as you also only have a terminal available. In a desktop environment, an user will most likely be using some sort of graphical user environment, like Unity under Ubuntu. Note that in the examples to come, I will be filtering the output of the pstree command to only show relevant information. A desktop environment will cause a LOT of extra processes to spawn, which are not relevant for this article.

The process tree for a graphical user session in which an instance of terminator (my terminal emulator of choice) has been started, may look like this:

init
  ├─apache2 -k start
  │   ├─apache2 -k start
  │   └─apache2 -k start
  ├─lightdm
  │   ├─Xorg -core :0 -seat seat0 -auth /var/run/lightdm/root/:0 -nolisten tcp vt7 -novtswitch
  │   └─lightdm --session-child 12 19
  │       └─init --user
  │           ├─/usr/bin/termin /usr/bin/terminator
  │           │   └─bash
  │           └─gnome-session --session=ubuntu
  │               └─compiz
  └─mysqld_safe /usr/bin/mysqld_safe
      └─mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql--log-error=/var/log/mysql/

LightDM is a display manager, which in turn has started the X.org X server. It has also forked itself into a session-child, which in turn has started init --user, terminator and gnome-session. gnome-session has started compiz which will provide the actual graphical user interface that you see and interact with. terminator has also been started, and provides me with a terminal in which I can execute commands. The observant reader will already have noticed something odd in this process tree, but let’s move on.

Let’s what happens when I start a new openssh daemon instance from terminator.

init
  ├─apache2 -k start
  │   ├─apache2 -k start
  │   └─apache2 -k start
  ├─lightdm
  │   ├─Xorg -core :0 -seat seat0 -auth /var/run/lightdm/root/:0 -nolisten tcp vt7 -novtswitch
  │   └─lightdm --session-child 12 19
  │       └─init --user
  │           ├─/usr/bin/termin /usr/bin/terminator
  │           │   └─bash
  │           └─gnome-session --session=ubuntu
  │               └─compiz
  ├─mysqld_safe /usr/bin/mysqld_safe
  │   └─mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql--log-error=/var/log/mysql/
  └─sshd

sshd has been started as a child of init, and is now a daemon waiting for connections. Next, let’s start my own daemonizing software using its initd script from terminator. To clean up the output, I have also stopped apache and mysql.

init
  ├─lightdm
  │   ├─Xorg -core :0 -seat seat0 -auth /var/run/lightdm/root/:0 -nolisten tcp vt7 -novtswitch
  │   └─lightdm --session-child 12 19
  │       └─init --user
  │           ├─/usr/bin/python
  │           │   ├─openstack-worker
  │           │   ├─openstack-worker
  │           │   ├─openstack-worker
  │           │   ├─openstack-worker
  │           ├─/usr/bin/termin /usr/bin/terminator
  │           │   └─bash
  │           └─gnome-session --session=ubuntu
  │               └─compiz
  └─sshd

Well then, that’s not quite where I expected my daemon to end up. I expected to find it as a child of init.

So what happened?

Apparently, Upstart has a feature where it can be launched in user mode using the --user flag. In this mode, the PR_SET_CHILD_SUBREAPER flag is set on the process, using the prctl system call. This means that processes that are daemonized as children of init --user will not be attached to the main init instance (process ID 1), but on the init --user instance that is the closest parent. In other words, all orphaned children of init --user will be attached to init --user, and not on init.

Why is this a problem?

This system is called Session Init, and the main purpose is to make some features of Upstart available in an user context. The user is able to create his own startup scripts, and use them like the system startup scripts. The difference is that the user doesn’t need root privileges to start his own processes, as you would with most normal initd scripts.

The direct side-effect that I can see is the daemonizing behaviour above. Daemonizing programs and scripts that are not aware of Upstart will be placed under init --user, whether they like it or not. The documentation claims this to be intentional, so at least we’re not running into a bug.

This behaviour is unwanted for my use-case, because I want to be able to start daemons from an user context, that run in the system context (as a child of the main init).

The solution

There is no way of circumventing the behaviour of init --user and PR_SET_CHILD_SUBREAPER that I know of. It also seems that normal initd scripts, like those of Apache and MySQL, are also affected by this. All initd scripts that have been converted into Upstart jobs are unaffected. Or rather, Upstart provides a mechanism to launch a system daemon even though the command was executed by a process under init --user. Upstart jobs are executed using either the service command or using the start, stop, restart and status commands. Both use the same mechanism, which is uses a named Unix socked to connect directly to the main init instance. The main init instance will execute the command on behalf of the user. This way, any newly started process will never start as a child of init --user, because they were started directly by init.

Based on this behaviour, the solution would then to simply create an Upstart job for your daemon. An alternative would be to disable the usage of init --user, unfortunately this complicates the startup of your graphical session somewhat. You can find an excellent answer detailing all steps that LightDM executes during startup on AskUbuntu. Finally, you could stop using Upstart altogether, but this also complicates matters, because there might be some package or tools that rely on Upstart, because it’s been a part of Ubuntu for a while now.

Well, there is one (very dirty) way to disable the PR_SET_CHILD_SUBREAPER flag on the init --user process. Walter Doekes details in his blog article the exact steps needed to do this. It basically comes down to injecting some code into the running init --user process, which calls prctl and disables the PR_SET_CHILD_SUBREAPER flag. While educational, I can’t see myself using this method on a live environment.

I guess I’ll just write Upstart jobs for new daemons…

  • peter

    I would suggest writing an LKM that intercepts the prctl syscall and messes with it based on super-vague heuristics. That’s even cleaner than attaching to init with ptrace and issuing the nescessary prctl call directly. 😉

    • Lord_Gaav

      You did not disappoint. Also ew.

  • Pingback: Unix:New parent process when the parent process dies – Unix Questions()