Which PID belongs into the systemd PIDFile section when creating a shell script daemon

daemonprocesssystemd

I've created a shell script daemon, based on the code of this answer. I've written a systemd service file with the following contents:

[Unit]
Description=My Daemon
After=network.target

[Service]
Type=forking
PIDFile=/run/daemon.pid
ExecStart=/root/bin/daemon.sh
ExecReload=/bin/kill -1 -- $MAINPID
ExecStop=/bin/kill -- $MAINPID
TimeoutStopSec=5
KillMode=process

[Install]
WantedBy=multi-user.target

Just before the while loop starts, I'm creating a PID file (it is going to be created by the daemon, not by the child, or the parent, because they don't come to this point): echo $$ > /run/daemon.pid;. It works fine, but each time I call systemctl status daemon.service, I get the following warning:

daemon.service: PID file /run/daemon.pid not readable (yet?) after start: No such file or directory

If I insert the PID creaton statement echo $$ > /run/daemon.pid; at the very beginning of the script (which will be used by the children and parent, too), I get the following warning:

daemon.service: PID 30631 read from file /run/daemon.pid does not exist or is a zombie.

What would be the best approach for creating the PID file without getting any warning messages by systemd?

Best Answer

So the problem you're seeing here is because when Type=forking is in use, then the pid file must be created (with the correct pid) before the parent process exits.

If you create the pidfile from the child, then it will race with the exit of the parent and in some (many?) cases will cause the first error you're seeing.

If you create the pidfile writing $$ to it before you start the child, then it will have the pid of the parent, which will have exited, so you'll see the other error.

One way to do this correctly is to write the pidfile from the parent, just before exiting. In that case, write $! (and not $$), which returns the pid of the last process spawned in background.

For example:

#!/bin/bash

# Run the following code in background:
(
    while keep_running; do
        do_something
    done
) &

# Write pid of the child to the pidfile:
echo "$!" >/run/daemon.pid
exit

This should work correctly... HOWEVER, there's a much better way to accomplish this! Read on...


Actually, the whole point of systemd is to daemonize processes and run them in background for you... By trying to do that yourself, you're just preventing systemd from doing it for you. Which is making your life much harder at the same time...

Instead of using Type=forking, simply write your shell script to run in foreground and set up the service to use Type=simple. You don't need any pidfiles then.

Update your /root/bin/daemon.sh to simply do this:

#!/bin/bash

# Run the following code in foreground:
while keep_running; do
    do_something
done

(NOTE: Perhaps daemon.sh is not the best name for it at this point... Since that would imply it runs in background. Maybe name it something more appropriate, related to what it actually does.)

Then update the .service file to use Type=simple (which would actually be used by default here, so you could even omit it.)

[Service]
Type=simple
ExecStart=/root/bin/daemon.sh
ExecReload=/bin/kill -1 -- $MAINPID
ExecStop=/bin/kill -- $MAINPID
TimeoutStopSec=5
KillMode=process

BTW, you can probably drop ExecStop=, since killing the process with a signal is the default behavior as well...

systemd's Type=forking is really there only for legacy programs that only work that way and can't be easily fixed to work in foreground... It's hacky and inefficient. The whole point of systemd (and some of its alternatives, predecessors) is to do the forking and daemonizing itself and let services just worry about doing what they need to do! :-)

I hope you find this helpful... And I really hope you choose to let systemd do the heavy lifting for you! It's much more efficient that way.