2

I have written a small daemon in Python with:

#!/usr/bin/python3

import time
import click
import daemonocle

from daemonocle.cli import DaemonCLI

@click.command(cls=DaemonCLI, daemon_params={'pidfile': '/var/run/daemon.pid'})
def main():
    while True:
        time.sleep(3)

if __name__ == '__main__':
    main()

It works great as is. If I do the following I get a good exit status 0

$ sudo ./daemon start 
Starting daemon ... OK    

$ echo $?
0

Then I wrote a systemd service file:

[Unit]
SourcePath=/home/pi/daemon/daemon
Description=My First Daemon
Before=multi-user.target
After=mosquitto.target

[Service]
Type=forking
PIDFile=/var/run/daemon.pid
Restart=no
ExecStart=/home/pi/daemon/daemon start
ExecStop=/home/pi/daemon/daemon stop
ExecReload=/home/pi/daemon/daemon reload

And if I try to run my daemon with systemctl the command remains blocking:

$ sudo systemctl start daemon
^C
$ sudo systemctl status daemon
? daemon.service - XEMWAY Demo Service
   Loaded: loaded (/home/pi/daemon/daemon; static; vendor preset: enabled)
   Active: activating (start) since Wed 2019-02-13 13:47:40 GMT; 12s ago
  Process: 13044 ExecStop=/home/pi/daemon/daemon stop (code=exited, status=0/SUCCESS)
 Main PID: 12304 (code=exited, status=143); Control PID: 13079 (daemon)
   CGroup: /system.slice/daemon.service
           +-13079 /usr/bin/python3 /home/pi/daemon/daemon start
           +-13081 /usr/bin/python3 /home/pi/daemon/daemon start

After one minute systemd says:

Feb 13 13:49:10 raspberrypi systemd[1]: daemon.service: Unit entered failed state. Feb 13 13:49:10 raspberrypi systemd[1]: daemon.service: Failed with result 'timeout'.

What's wrong with this?

nowox
  • 25,978
  • 39
  • 143
  • 293
  • Maybe it's easier to use `one-shot` instead of `forking` and don't fork it? – deathangel908 Feb 13 '19 at 13:54
  • No it is not because my script is already a daemon – nowox Feb 13 '19 at 13:54
  • 1
    systemd, and other service monitoring tools, really prefer daemons that don't fork. If you change your daemon or add a flag to it so that it doesn't fork away from the first process, then you can make it Type=simple and it won't need a PID file or anything. – Zan Lynx Feb 13 '19 at 14:23

1 Answers1

4

The issue is that your service creates daemon by it's own and systemd doesn't know about it. It's just a simple program that runs and exit after little amount of time for systemd. Instead of forking you want to use simple:

Type=simple

Systemd wil still track your process as a daemon, since it knows its pid:

systemctl status test2.service

* test2.service - My First Daemon
   Loaded: loaded (/tmp/a.py; static; vendor preset: disabled)
   Active: active (running) since Wed 2019-02-13 16:06:27 EET; 5s ago
 Main PID: 18104 (python)
    Tasks: 2 (limit: 4915)
   Memory: 10.1M
   CGroup: /system.slice/test2.service
           |-18104 /usr/bin/python /tmp/a.py start
           `-18115 /usr/bin/python /tmp/a.py start
deathangel908
  • 8,601
  • 8
  • 47
  • 81
  • What I did not understand is my Python daemon does fork, because when I do `./daemon start` it starts and return, but the daemon remains in memory. So what's the meaning of `forking` daemons? – nowox Feb 13 '19 at 14:25
  • As I understand it doesn't fork. [daemonocle](https://pypi.org/project/daemonocle/#the-problem) just creates a child and detaches it from the main process. Correct me if I'm wrong, I don't have much time reading docs :( – deathangel908 Feb 13 '19 at 14:30
  • You perhaps know better than me about this. I can't say the difference between a child process and a fork :( – nowox Feb 13 '19 at 14:48
  • [here](https://stackoverflow.com/a/49628039/2612235) I can read than `os.fork` creates a child process :( Now I am confused – nowox Feb 13 '19 at 14:49
  • 1
    yep, but fork is not the only one method that creates a child process. `exec` creates a child process as well. – deathangel908 Feb 13 '19 at 14:50