How do I set up a daemon with python-daemon? How do I set up a daemon with python-daemon? python python

How do I set up a daemon with python-daemon?


Here is what I have, that works for me. It also has a sysv init script. Repo is at GitHub, and I also have a brief blog post with links to other possible solutions I found.

There can only be one daemon process running: that is managed by the PID lock file, like most other Linux daemons. To stop it, do

kill `cat /var/run/eg_daemon.pid`

To see if it is running:

ps -elf | grep `cat /var/run/eg_daemon.pid`

Using the pidfile submodule, the PID file is managed automatically. When the daemon is stopped, the pidfile is cleared up. Please see the linked GitHub repo for the init script.

Here's the Python daemon code:

#!/usr/bin/env python3.5import sysimport osimport timeimport argparseimport loggingimport daemonfrom daemon import pidfiledebug_p = Falsedef do_something(logf):    ### This does the "work" of the daemon    logger = logging.getLogger('eg_daemon')    logger.setLevel(logging.INFO)    fh = logging.FileHandler(logf)    fh.setLevel(logging.INFO)    formatstr = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'    formatter = logging.Formatter(formatstr)    fh.setFormatter(formatter)    logger.addHandler(fh)    while True:        logger.debug("this is a DEBUG message")        logger.info("this is an INFO message")        logger.error("this is an ERROR message")        time.sleep(5)def start_daemon(pidf, logf):    ### This launches the daemon in its context    ### XXX pidfile is a context    with daemon.DaemonContext(        working_directory='/var/lib/eg_daemon',        umask=0o002,        pidfile=pidfile.TimeoutPIDLockFile(pidf),        ) as context:        do_something(logf)if __name__ == "__main__":    parser = argparse.ArgumentParser(description="Example daemon in Python")    parser.add_argument('-p', '--pid-file', default='/var/run/eg_daemon.pid')    parser.add_argument('-l', '--log-file', default='/var/log/eg_daemon.log')    args = parser.parse_args()    start_daemon(pidf=args.pid_file, logf=args.log_file)

For completeness' sake, here is the init script. Note that "kill" is really just a method for sending a POSIX signal -- see man page for signal(7) for an overview. The python-daemon context will catch the signal, terminate the process cleanly closing file descriptors, and delete the PID file automatically. So, it really is a clean termination.

You can write your code to catch SIGUSR1 or something similar, in order to do a reload of the daemon config. There is no advantage to writing Python stop the daemon.

#!/bin/bash## eg_daemon      Startup script for eg_daemon## chkconfig: - 87 12# description: eg_daemon is a dummy Python-based daemon# config: /etc/eg_daemon/eg_daemon.conf# config: /etc/sysconfig/eg_daemon# pidfile: /var/run/eg_daemon.pid#### BEGIN INIT INFO# Provides: eg_daemon# Required-Start: $local_fs# Required-Stop: $local_fs# Short-Description: start and stop eg_daemon server# Description: eg_daemon is a dummy Python-based daemon### END INIT INFO# Source function library.. /etc/rc.d/init.d/functionsif [ -f /etc/sysconfig/eg_daemon ]; then        . /etc/sysconfig/eg_daemonfieg_daemon=/var/lib/eg_daemon/eg_daemon.pyprog=eg_daemonpidfile=${PIDFILE-/var/run/eg_daemon.pid}logfile=${LOGFILE-/var/log/eg_daemon.log}RETVAL=0OPTIONS=""start() {        echo -n $"Starting $prog: "        if [[ -f ${pidfile} ]] ; then            pid=$( cat $pidfile  )            isrunning=$( ps -elf | grep  $pid | grep $prog | grep -v grep )            if [[ -n ${isrunning} ]] ; then                echo $"$prog already running"                return 0            fi        fi        $eg_daemon -p $pidfile -l $logfile $OPTIONS        RETVAL=$?        [ $RETVAL = 0 ] && success || failure        echo        return $RETVAL}stop() {    if [[ -f ${pidfile} ]] ; then        pid=$( cat $pidfile )        isrunning=$( ps -elf | grep $pid | grep $prog | grep -v grep | awk '{print $4}' )        if [[ ${isrunning} -eq ${pid} ]] ; then            echo -n $"Stopping $prog: "            kill $pid        else            echo -n $"Stopping $prog: "            success        fi        RETVAL=$?    fi    echo    return $RETVAL}reload() {    echo -n $"Reloading $prog: "    echo}# See how we were called.case "$1" in  start)    start    ;;  stop)    stop    ;;  status)    status -p $pidfile $eg_daemon    RETVAL=$?    ;;  restart)    stop    start    ;;  force-reload|reload)    reload    ;;  *)    echo $"Usage: $prog {start|stop|restart|force-reload|reload|status}"    RETVAL=2esacexit $RETVAL


A full example is available here.

You should be able to better understand the inner workings of python-daemon.

Moreover the code provided also gives an example of an init script to simply start/stop the daemon. However, you can start/stop it simply by calling the original function again with the argument stop:

python original_func.py stop


As you can see in the 'with' statement documentation, it statement does perform some 'magic', which is related to our purpose. Specifically:

The execution of the with statement with one “item” proceeds as follows:

  1. The context expression (the expression given in the with_item) is evaluated to obtain a context manager.

  2. The context manager’s __exit__() is loaded for later use.

  3. he context manager’s __enter__() method is invoked.

  4. If a target was included in the with statement, the return value from __enter__() is assigned to it.

  5. The suite is executed.

  6. The context manager’s __exit__() method is invoked. If an exception caused the suite to be exited, its type, value, and traceback are passed as arguments to __exit__(). Otherwise, three None arguments are supplied.

What does this mean? If you look closely to the PEP in question, which serves as python-daemon documentation as well (and which indeed could be much improved), you'll see that it implements __enter__() and __exit__():

The class also implements the context manager protocol via __enter__ and __exit__ methods.

__enter__()

Call the instance's open() method, then return the instance.

__exit__(exc_type, exc_value, exc_traceback)

Call the instance's close() method, then return True if the exception was handled or False if it was not.

In other words, open() is not needed, the example given in the PEP (though not explained correctly) works as is. While the with statement does mean something, it does not keep any loop, once the end of its scope is reached, it calls exit(), which in python-daemon means close(). Therefore, you need to put there a while True or which ever infinite loop you consider.

On behave of your second script not working, I can't really tell you, I'm surprised the first already works. If your daemon is stopping, there is a problem with your scripts for sure, you can check your consumerDaemonLogFile. (as a side note, you have a typo 'sderr' --> 'stderr')

Also, you can see in the PEP that if not specified, the working directory property defaults to '/'. this might be the source of your problem if you are using relative paths in your scripts.

Finally, about the last question, you can easily kill you're daemon finding its PID:

ps ax | grep startConsumerDaemons.py

and sending it a SIGTERM:

kill <pid>

The answer provided by gromain does provide a more handy way to start and stop it, with 'daemon.runner()', but it's far more complicated to setup.