0

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 ??

jww
  • 97,681
  • 90
  • 411
  • 885
Srivatsa M
  • 13
  • 5
  • You tell the parent to `sys.exit` after the first fork in the first daemon. So of course it doesn't get to the rest of the code. You probably wanted to just `return` there. – abarnert Mar 22 '18 at 05:18
  • Did you write this code, or copy it from somewhere else? If the latter, where did it come from? (And is there a reason you chose it rather than the `daemon` library on PyPI, or one of its competitors, which are well documented and tested?) – abarnert Mar 22 '18 at 05:19
  • i found this Daemon class online & the reason i chose this over daemon library is the way of controlling the daemon this offers. – Srivatsa M Mar 22 '18 at 05:31
  • do you know a better alternative ? – Srivatsa M Mar 22 '18 at 05:34
  • What way of controlling the daemon does the daemon library not offer that you need? Meanwhile, "online" isn't exactly helpful. If I could see the code you borrowed this from, and the blog post or repository or whatever that goes with it, it would be a lot easier to figure out what the author was intending for bits that don't make sense (and to spot if you changed anything that you didn't expect to break anything but it did). – abarnert Mar 22 '18 at 05:39
  • here is where i found it from https://gist.github.com/tzuryby/961228 i just wanted a generic daemonizing class – Srivatsa M Mar 22 '18 at 05:44
  • OK, that code is clearly not designed to handle spawning multiple daemons, and you don't understand how to change it to do that yourself, so the easy answer is to just not use it. Again, what do you need that the `daemon` library or something else can't do for you? – abarnert Mar 22 '18 at 05:47
  • i just need a class/library that can provide an easier way of creating & controlling daemons in linux so i am not able to find much examples of the daemon library on PyPi you are suggesting – Srivatsa M Mar 22 '18 at 05:54
  • https://pypi.python.org/pypi/python-daemon/ has a link to a PEP, which include sample code. If you don't like it for some reason, it also has extensive links to other popular daemon libraries, most of which come with documentation and sample code. That gist, by contrast, is incomplete and has no documentation or samples at all. It would be a fine starting point if you understood the code and how daemonizing works (i.e., you knew the book Unix Network Programming) but if you don't, it's useless. – abarnert Mar 22 '18 at 06:00

1 Answers1

0

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.

abarnert
  • 354,177
  • 51
  • 601
  • 671