Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python daemon will not run in the background on Ubuntu

My Python daemon runs fine in the foreground of my Ubuntu system using this command in the terminal:

python /opt/my-daemon.py foreground

However when I try to call the daemon using the "start" command it fails, why?

python /opt/my-daemon.py start

This is how I call the command in the /etc/rc.local file:

python /opt/my-daemon.py start &

Herewith the code:

1.daemon.py

#!/usr/bin/env python
import sys, os, time, atexit
from signal import SIGTERM
class Daemon:
"""
A generic daemon class.

Usage: subclass the Daemon class and override the run() method
"""
def __init__(self, pidfile,
    stdin='/dev/null',stdout='/dev/null',stderr='/dev/null'):
    self.stdin = stdin
    self.stdout = stdout
    self.stderr = stderr
    self.pidfile = pidfile

def daemonize(self):
    """
    Do the UNIX double-fork magic. See Richard Stevens' "Advanced
    Programming in the UNIX Environment" for details (ISBN 0201563177)
    http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
    """
    try:
        pid = os.fork()
        if pid > 0:
            # exit first parent
            sys.exit(0)
    except OSError, e:
        sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno,
                    e.strerror))
        sys.exit(1)
    # Decouple from parent environment
    os.chdir("/")
    os.setsid()
    os.umask(0)

    # Do second fork
    try:
        pid = os.fork()
        if pid > 0:
            # Exit from second parent
            sys.exit(0)
    except OSError, e:
        sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
        sys.exit(1)

    # Redirect standard file descriptors
    sys.stdout.flush()
    sys.stderr.flush()
    si = file(self.stdin, 'r')
    so = file(self.stdout, 'a+')
    se = file(self.stderr, 'a+', 0)
    os.dup2(si.fileno(), sys.stdin.fileno())
    os.dup2(so.fileno(), sys.stdout.fileno())
    os.dup2(se.fileno(), sys.stderr.fileno())

    # Write pidfile
    atexit.register(self.delpid)
    pid = str(os.getpid())
    file(self.pidfile,'w+').write("%s\n" % pid)

def delpid(self):
    os.remove(self.pidfile)

def start(self):
    """
    Start the daemon
    """
    # Check for a pidfile to see if the daemon already runs
    try:
        pf = file(self.pidfile,'r')
        pid = int(pf.read().strip())
        pf.close()
    except IOError:
        pid = None

    if pid:
        message = "pidfile %s already exist. Daemon already running?\n"
        sys.stderr.write(message % self.pidfile)
        sys.exit(1)

    # Start the daemon
    self.daemonize()
    self.run()

def stop(self):
    """
    Stop the daemon
    """
    # Get the pid from the pidfile
    try:
        pf = file(self.pidfile,'r')
        pid = int(pf.read().strip())
        pf.close()
    except IOError:
        pid = None

    if not pid:
        message = "pidfile %s does not exist. Daemon not running?\n"
        sys.stderr.write(message % self.pidfile)
        return # not an error in a restart

    # Try killing the daemon process
    try:
        while 1:
            os.kill(pid, SIGTERM)
            time.sleep(0.1)
    except OSError, err:
        err = str(err)
        if err.find("No such process") > 0:
            if os.path.exists(self.pidfile):
                os.remove(self.pidfile)
        else:
            print str(err)
            sys.exit(1)

def restart(self):
    """
    Restart the daemon
    """
    self.stop()
    self.start()

def run(self):
    """
    You should override this method when you subclass Daemon. It will be called after the process has been
    daemonized by start() or restart().
    """

2.my-daemon.py

import sys, time
from daemon import Daemon
import MySQLdb #MySQL libraries
#Database parameters
config = {"host":"localhost",...}
try:
    conn = MySQLdb.connect(config['host'],...
class MyDaemon(Daemon):
def run(self):
    while True:
        time.sleep(2)
                    #{Do processes, connect to the database, etc....}
                    ...
if __name__ == "__main__":
daemon = MyDaemon('/tmp/daemon-example.pid')
if len(sys.argv) == 2:
    if 'start' == sys.argv[1]:
        daemon.start()
    elif 'stop' == sys.argv[1]:
        daemon.stop()
    elif 'restart' == sys.argv[1]:
        daemon.restart()
    elif 'foreground' == sys.argv[1]: #This runs the daemon in the foreground
        daemon.run()
    else:
        print "Unknown command"
        sys.exit(2)
    sys.exit(0)
else:
    print "usage: %s start|foreground|stop|restart" % sys.argv[0]
    sys.exit(2)
like image 860
Chesneycar Avatar asked Nov 14 '22 08:11

Chesneycar


1 Answers

SOLVED. I was under the impression that the foreground and the start parameter was two different things. It turns out I just needed to do the following.

def run(self):
    while True:
        time.sleep(2)

to

def start(self):
    while True:
        time.sleep(2)

I then removed the foreground parameter, because I can run the script from the terminal using the start command to see the output in the foreground.

python /opt/my-daemon.py start

Also, in rc.local I start the script as follows:

python /opt/my-daemon.py start &

This hides the daemon process and executes the script on startup regardless of the user who logs in :)

like image 126
Chesneycar Avatar answered Dec 06 '22 11:12

Chesneycar