Python 2.7 Daemon Example

Aug 13, 2016 00:00 · 605 words · 3 minute read

EDIT: I gave a talk about this subject at my local python meetup and wrote a bit more in-depth code which I put on Github. You will more information there.

The title says it all. I had to do a project which required creating a daemon and use python 2.7 and had quite the time trying to get it work since there are not very many examples out there. I thought that I would share some of the knowledge that I gained over this little venture in the hopes that someone else will not have to go through the pain.

##Step 1: Do the pip

To get all the required materials simply run:

$ pip install python-daemon

##Step 2: Start the fun stuff

# -*- coding: utf-8 -*-
"""Example of how to run a daemon."""

import time

from daemon import runner


class DaemonApp(object):
    """Daemon App."""

    def __init__(self):
        """Initialize Daemon."""
        self.stdin_path = '/dev/null'
        self.stdout_path = '/dev/null'
        self.stderr_path = '/dev/null'
        self.pidfile_path = '/tmp/daemon.pid'
        self.pidfile_timeout = 1

    def run(self):
        """Main Daemon Code."""
        while True:
            time.sleep(1)

if __name__ == '__main__':
    app = DaemonApp()
    daemon_runner = runner.DaemonRunner(app)
    daemon_runner.do_action()

This is the easiest example I can show. To run this example save it to a file, I call mine main.py, and then run:

$ python main.py start

To check if your process is running you can check two things. First try to read the pid file:

$ cat /tmp/daemon.pid
3527

You won’t necessarily have the same process id but you should get a number. Now the pid file is more of just a check to see that your daemon started. If your daemon logic hits an exception after it starts it will die and the pid file will not always be removed. Only if the “happy path” of execution occurs will the pid file truly reflect the current state of the daemon. The other way to check if the process is currently running is to use:

$ ps aux | grep main.py
root            3991   0.0  0.0  2434840    792 s011  S+    9:43PM   0:00.00 grep main.py
root            3527   0.0  0.0  2487108   1896   ??  S     9:43PM   0:00.00 python main.py start

Here we can see that the daemon pid file matches the pid id from the ps aux command. Some other points to note are in general you probably won’t want to have stdout_path and stderr_path point to /dev/null because that means you can’t see any errors that will cause your daemon to die. I usually point these to files and then I will use ‘tail -f /path/to/file’ to watch for statuses. You could instead make the daemon app look like this instead.

class DaemonApp(object):
    """Daemon App."""

    def __init__(self):
        """Initialize Daemon."""
        self.stdin_path = '/dev/null'
        self.stdout_path = '/tmp/stdout.txt'
        self.stderr_path = '/tmp/stderr.txt'
        self.pidfile_path = '/tmp/daemon.pid'
        self.pidfile_timeout = 1

Now in the PEP 3143 documentation you will read about DaemonContext objects. The objects basically represent settings that are applied to your daemon class, things like ‘uid’ and ‘guid’. Following safety best practices you might want to have your daemon run as a lower level user instead of root, which we saw in our ‘ps aux’ example.

# New imports
import grp
import pwd
import time

...

if __name__ == '__main__':
    app = DaemonApp()
    daemon_runner = runner.DaemonRunner(app)
    daemon_gid = grp.getgrnam('hiarc').gr_gid
    daemon_uid = pwd.getpwnam('hiarc').pw_uid
    daemon_runner.daemon_context.gid = daemon_gid
    daemon_runner.daemon_context.uid = daemon_uid
    daemon_runner.do_action()

and now running ps aux:

$ ps aux | grep main.py
root           42784   0.0  0.0  2434840    712 s011  R+    9:59PM   0:00.00 grep main.py
hiarc           42597   0.0  0.0  2461524   1924   ??  S     9:59PM   0:00.00 python main.py start

WOOO! After you get it running with these lowered permissions you have all the main ingredients to start your daemon project. Good luck!