5

Can systemd handle double-fork daemons ?

I previously used init.d/ script to manage daemons I wrote which have worked flawless for years. Now I find myself needing to provide a systemd/ script for and I cannot get that to work with the way I do daemon setup code.

When I setup a daemon I fork twice to loose controlling terminal and avoid being session leader.

The problem with this is that I have confirmed that when you do that systemd/ looses track and kills the daemon. I have also confirmed that skipping the second fork makes it work again with systemd/.

My basic (slightly simplified for the purpose of this discussion) systemd/ script is

[Unit]
Description=GM7 Service Daemon

[Service]
Type=forking
ExecStart=/usr/bin/g7ctrl

[Install]
WantedBy=multi-user.target

So I guess my question is : Do I need to change the way I have done daemon setup code in C/C++ for the last decade or is there is options to get systemd/ to track a double-fork ?

I haven't quite read-up on the inner workings if systemd itself spawns processes for each daemon it starts in which case my double-fork would indeed be obsolete

I admit that the way I have done it (double fork) is partially for historic reasons but at the time I did some quite intensive read-up on best-practice and this is what I then came up with (a decade or so ago)

2 Answers 2

5

Do I need to change the way I have done daemon setup code in C/C++ for the last decade?

Yes.

Even a decade or so ago this wasn't the right way to go. It hasn't been right since the early 1990s with the AIX System Resource Controller. daemontools users advocated not doing things this way from the late 1990s onwards. It wasn't right for Upstart which mainstream Linux operating systems began adopting in 2006. It wasn't right for running things from AT&T System 5 Release 3 inittab back in the early 1980s.

It isn't right for systemd. Even though people might tell you about the forking type, what they usually forget, or do not even know, to tell you is that that is a specific service readiness protocol, that depends from double-forking being done in a particular way. Worse, the way that one needs to do it in order to speak the protocol correctly does not mix well with the way that you and others will be writing your C/C++ programs.

Back in 2008, my Frequently Given Answer on the subject had been in existence for roughly three quarters of a decade, and I and others had been telling people the same thing for quite a while before I put it into FGA form.

IBM had been saying it in a redbook since 1995.

Some Johnny-come-latelys have been saying this same thing, from a systemd point of view, in the systemd manual for a mere 8 years or so. ☺

Let the service management subsystem handle all this. Your program is already executing in a dæmon context when it starts running.

If you really want to keep all of the code that depends from the dæmonization fallacy being true, then at least do what many other people have (partly in response to the daemontools people) done over the past two decades and give the world a command-line option that turns all of it off. But please don't make that command-line option do double duty as a debugging switch.

Further reading

2
  • Thanks! something I "just have been doing" for the last decades turned out to be less than optimal. However, to be able to create an asynchronous process without a controlling terminal "manually" wouldn't forking' (and closing descriptors etc.) be needed (nohup and '&' wouldn't be enough)?
    – Johan
    Commented Oct 20, 2018 at 7:05
  • @Johan to do it manually, write a separate program that does the double-fork, setsid, and closing FDs magic, and then does execvp into the remainder of its argv. Name it something like daemonize, then call it like this: daemonize <program> [<argument>...] - so for your example: daemonize g7ctrl. (Of course you can just give the full path explicitly if you want: daemonize /usr/bin/g7ctrl.) You can also get pretty close in the shell: (g7ctrl 0>&- 1>&- 2>&- &). The subshell (...) is one fork and & is two, and redirecting to &- is shell syntax for closing file descriptors.
    – mtraceur
    Commented Oct 9, 2022 at 14:37
3

Yes, systemd is capable of handling double forking daemons when using Type=forking.

As long as there is a process left when the main process exits, systemd will assume it's the main process.

See this example on traditional forking services in systemd documentation for more details and a more thorough description of how Type=forking works.

systemd is more strict about the initial starter process exiting and the daemon being started and ready. From that example:

Once it exits successfully and at least a process remains (and RemainAfterExit=no), the service is considered started.

So it's possible that what is happening in your case is that the starter process is exiting right after forking the first child (which systemd then thinks is the main process of your service) and then the first child forks again and exits, so systemd thinks the service is finished, when in fact it's not.

Some would argue this is broken behavior even on System V init:

Again, not synchronizing properly is broken for sysvinit as much as it is broken for systemd. We deal with it to some point, but the only fully safe and correct way to handle this is to fix the programs in question.

In any case, the best advice I'd have for you is to skip double forking when running under systemd. You don't have to remove double forking ability from your daemons, just add a command line option (like -f, -F or --foreground) which skips the daemonizing code and runs the program in foreground. Use that option in your systemd service file.

(Such an option would also be helpful when running the daemon for debugging or troubleshooting anyways.)

systemd's Type=forking is able to work with double forking daemons, but in the end, it's there just for compatibility. There are advantages to not forking (easier to track main process), so if you're able to modify your program, doing so is best.

1
  • 2
    Thanks! Yes, I always have "foreground" option so I could use this. However I think I need to do some updating my old ways of doing things so I no longer manage a PID file and fork myself but rather let systemd handle all the administrative stuff.
    – Johan
    Commented Oct 20, 2018 at 7:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .