• Uncategorized

About python : Extending-Daemon-class-by-two-subclasses-does-not-work

Question Detail

this is the daemon class i am using

it is acting as a base class which i want to spawn 2 seperate daemons from another controller file

class Daemon:

    """A generic daemon class.

    Usage: subclass the daemon class and override the run() method."""

    def __init__(self, pidfile,outfile='/tmp/daemon_out',errfile='/tmp/daemon_log'): 
        self.pidfile = pidfile
        self.outfile = outfile
        self.errfile = errfile

    def daemonize(self):

        """Deamonize class. UNIX double fork mechanism."""

        try: 
            pid = os.fork() 
            if pid > 0:
                # exit first parent
                sys.exit(0) 
        except OSError as err: 
            sys.stderr.write('fork #1 failed: {0}\n'.format(err))
            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 as err: 
            sys.stderr.write('fork #2 failed: {0}\n'.format(err))
            sys.exit(1) 

        # redirect standard file descriptors
        sys.stdout.flush()
        sys.stderr.flush()
        si = open(os.devnull, 'r')
        so = open(self.outfile, 'a+')
        se = open(self.errfile, 'a+')

        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())
        with open(self.pidfile,'w+') as f:
            f.write(pid + '\n')

    #method for removing the pidfile before stopping the program
    #remove the commented part if you want to delete the output & error file before stopping the program
    def delpid(self):
        os.remove(self.pidfile)
        #os.remove(self.outfile)
        #os.remove(self.errfile)

    def start(self):
        """Start the daemon."""

        # Check for a pidfile to see if the daemon already runs
        try:
            with open(self.pidfile,'r') as pf:

                pid = int(pf.read().strip())
        except IOError:
            pid = None

        if pid:
            message = "pidfile {0} already exist. " + \
                    "Daemon already running?\n"
            sys.stderr.write(message.format(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:
            with open(self.pidfile,'r') as pf:
                pid = int(pf.read().strip())
        except IOError:
            pid = None

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

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

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

    def run(self):
        """override this method when you subclass Daemon.

        It will be called after the process has been daemonized by 
        start() or restart()."""

here is the code i am using in a different file

in this file i am extending the daemon class from seperate classes & overriding the run() method.

#! /usr/bin/python3.6
import sys, time, os, psutil, datetime
from daemon import Daemon

class net(Daemon):
    def run(self):
        while(True):
            print("net daemon : ",os.getpid())
            time.sleep(200)

class file(Daemon):
    def run(self):
        while(True):
            print("file daemon : ",os.getpid())
            time.sleep(200)



if __name__ == "__main__":
    net_daemon = net(pidfile='/tmp/net_pidFile',outfile='/tmp/network_out.log',errfile='/tmp/net_error.log')
    file_daemon = file(pidfile='/tmp/file_pidFile',outfile='/tmp/filesys_out.log',errfile='/tmp/file_error.log')

    if len(sys.argv) == 2:
        if 'start' == sys.argv[1]:
            net_daemon.start()
            file_daemon.start()
        elif 'stop' == sys.argv[1]:
            file_daemon.stop()
            net_daemon.stop()
        elif 'restart' == sys.argv[1]:
            file_daemon.restart()
            net_daemon.restart()
        else:
            print("Unknown command")
            sys.exit(2)
        sys.exit(0)
    else:
        print("usage: %s start|stop|restart" % sys.argv[0])
        sys.exit(2)

the first class to run the start() method is running currently &
only the net Daemon works now how do i make the 2 classes spawn 2 seperate daemons ??

Question Answer

The real problem here is that you’ve chosen the wrong code for the task you want. You’re asking “How do I use this power saw to hammer in this nail?” And in this case, it’s not even a professionally-produced saw with an instruction manual, it’s a home-made saw you found in someone’s garage, built by a guy who probably knew what he was doing but you can’t actually be sure because you don’t know what he was doing.

The proximate problem that you’re complaining about is in daemonize:

try: 
    pid = os.fork() 
    if pid > 0:
        # exit first parent
        sys.exit(0) 

The first time you call this, the parent process exits. Which means the parent process never gets to launch the second daemon, or do anything else.

For a self-daemonizing program that can be managed by a separate program, this is exactly what you want. (Whether it gets all the details right, I don’t know, but the basic idea is definitely right.)

For a managing program that spawns daemons, this is exactly what you don’t want. And that’s what you’re trying to write. So this is the wrong tool for the job.

But the tasks aren’t that much different. If you understand what you’re doing (and crack open your copy of Unix Network Programming—nobody understands this stuff well enough to get it right off the top of their head), you can convert one into the other. Which might be a useful exercise, even if for any real application I’d just use one of the well-tested, well-documented, nicely-maintained libraries on PyPI.

What happens if you just replace the sys.exit(0) calls that happen in the parent process (but not the ones that happen in the intermediate child!) with return True? (Well, you probably want to also replace the sys.exit(1) in the parent with a return False or raise some kind of exception.) Then daemonize no longer daemonizes you, but instead spawns a daemon and reports back on whether it succeeded. Which is what you wanted, right?

No guarantees that it does everything else right (and I’d bet it doesn’t), but it does solve the specific problem you were asking about.

If nothing obvious is going wrong after that, the next step would probably be to read through PEP 3143 (which does a pretty nice job translating all the details in Stevens’ book into Python terms and making sure they’re up to date for 21st century linux and BSD) and come up with a checklist of tests to run, and then run them to see what less obvious things you’re still getting wrong.

You may also like...

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.