13

I want to write my own systemd unit files to manage really long running commands1 (in the order of hours). While looking the ArchWiki article on systemd, it says the following regarding choosing a start up type:

Type=simple (default): systemd considers the service to be started up immediately. The process must not fork. Do not use this type if other services need to be ordered on this service, unless it is socket activated.

Why must the process not fork at all? Is it referring to forking in the style of the daemon summoning process (parent forks, then exits), or any kind of forking?


1 I don't want tmux/screen because I want a more elegant way of checking status and restarting the service without resorting to tmux send-keys.

41

The service is allowed to call the fork system call. Systemd won't prevent it, or even notice if it does. This sentence is referring specifically to the practice of forking at the beginning of a daemon to isolate the daemon from its parent process. “The process must not fork [and exit the parent while running the service in a child process]”.

The man page explains this more verbosely, and with a wording that doesn't lead to this particular confusion.

Many programs that are meant to be used as daemons have a mode (often the default mode) where when they start, they isolate themselves from their parent. The daemon starts, calls fork(), and the parent exits. The child process calls setsid() so that it runs in its own process group and session, and runs the service. The purpose is that if the daemon is invoked from a shell command line, the daemon won't receive any signal from the kernel or from the shell even if something happens to the terminal such as the terminal closing (in which case the shell sends SIGHUP to all the process groups it knows of). This also causes the servicing process to be adopted by init, which will reap it when it exits, avoiding a zombie if the daemon was started by something that wouldn't wait() for it (this wouldn't happen if the daemon was started by a shell).

When a daemon is started by a monitoring process such as systemd, forking is counterproductive. The monitoring process is supposed to restart the service if it crashes, so it needs to know if the service exits, and that's difficult if the service isn't a direct child of the monitoring process. The monitoring process is not supposed to ever die and does not have a controlling terminal, so there are no concerns around unwanted signals or reaping. Thus there's no reason for the service process not to be a child of the monitor, and there's a good reason for it to be.

| improve this answer | |
15

Ignore this Arch wiki page.

It has got things quite considerably wrong with respect to the Type setting. This is not just limited to its descriptions of simple, moreover. What it says about forking is wrong, too.

The correct recommendations for this sort of thing have existed for decades longer than systemd itself has existed, and go back to at least the early 1990s. As I noted at https://unix.stackexchange.com/a/476608/5132 , in the systemd doco there's a Johnny-come-lately version of the recommendations for dæmons that largely repeats what daemontools users, IBM, people using inittab, and … well … I have been saying for decades. (It was already a frequently given answer when I wrote it up as such in 2001.)

To repeat:

If your program has some "dæmonization" mechanism, that in particular forks a child and exits the parent process, turn it off and do not use it. Thanks to daemontools et al. where this has been a requirement for a long time, many programs have grown the ability to not have such mechanisms in the past more than 20 years, and others simply don't default to "dæmonizing" in the first place so can be used in their default operating modes.

Service management subsystems launch service processes in dæmon context already. Those processes do not need to "dæmonize". (Indeed, it is a fallacy on many modern operating systems to think that programs even can "dæmonize" from a login session context, which is what "dæmonization" is actually about.) They already have the environment values, and open file descriptors, appropriate to dæmon context, and the several things done by "dæmonization" in fact thwart some of the conventional things that are regularly done with dæmons (e.g. capturing their standard outputs/errors to a log) by service managers.

Prefer Type=simple, with early socket opening (where the service management opens server sockets and passes them as already open file descriptors to the service program), or Type=notify.

  • Type=simple treats the service as ready (so that services ordered upon it can be started/stopped) as soon as the service process starts, with early socket opening employing socket connection semantics to delay service clients, at the points that they attempt to connect to servers for service, until servers are actually ready.
  • Type=notify has the disadvantage of being peculiar to systemd and to Linux (alongside the problems of not being functional from short-lived processes such as a shell spawning systemd-notify, and of employing parsing of human-readable forms to machine-readable forms in a privileged process, where parser problems have already happened in the past) but has the advantage of providing finer control (from the point of view of the service program) of when the service is actually considered to be ready. It also allows some customization of the status output.

Service programs, of both types, can fork. It is forking and then exiting the original process that is the problem.

(It should be noted that this is as much a problem for running programs from shells as it is for running programs from service managers, with users seeing programs terminate and cause another shell prompt almost immediately. Indeed, just today someone was asking, yet again, about running programs from the shell that fork and exit parent, at Why sometimes when i run a program in terminal, it won't run in the terminal? .)

Type=oneshot is probably not what you want in this particular case, as the service is considered ready only when the entire service program has run to completion. It has its uses, but by the sounds of it they do not apply to you.

Never use Type=forking. It should be a last resort of desperation, as almost no programs actually speak the protocol. They are doing something else, which is in fact not this protocol, not correctly interoperable with this protocol, and not in fact signalling readiness.

Further reading

| improve this answer | |
  • 2
    Since it's a wiki, a better version of the heading might be: "Improve this Arch Wiki page!" :) – muru Aug 19 at 6:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.