python daemon signal does not work
Question: I wrote a python daemon script and registered some signal handlers , but when
i use `kill -1 pid` , i found that signal handler does not work.

Any body know why?

Here is the script:

test.py:

    
    
    #!/usr/bin/env python
    
    import sys, time
    from daemon import Daemon
    import threading
    import signal
    
    class MyDaemon(Daemon):
        def run(self):
            time.sleep(1)
            threading.Thread(target=mythread).start()
            threading.Thread(target=mythread2).start()
    
    
    def mythread():
        while 1:
            raise Exception,"shit"
    
    def mythread2():
        while 1:
            time.sleep(1)
            print "22222222222"
    
    
    
    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()
            else:
                print "Unknown command"
                sys.exit(2)
            sys.exit(0)
        else:
            print "usage: %s start|stop|restart" % sys.argv[0]
            sys.exit(2)
    

daemon.py:

    
    
    #!/usr/bin/env python
    
    import sys, os, time, atexit
    from signal import SIGTERM 
    import signal
    import logging
    import logging.handlers
    
    LOG_FILENAME = "/tmp/myservice.log"
    LOG_LEVEL = logging.INFO  # Could be e.g. "DEBUG" or "WARNING"
    
    # Configure logging to log to a file, making a new file at midnight and keeping the last 3 day's data
    # Give the logger a unique name (good practice)
    logger = logging.getLogger(__name__)
    # Set the log level to LOG_LEVEL
    logger.setLevel(LOG_LEVEL)
    # Make a handler that writes to a file, making a new file at midnight and keeping 3 backups
    handler = logging.handlers.TimedRotatingFileHandler(LOG_FILENAME, when="midnight", backupCount=3)
    # Format each log message like this
    formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s')
    # Attach the formatter to the handler
    handler.setFormatter(formatter)
    # Attach the handler to the logger
    logger.addHandler(handler)
    
    # Make a class we can use to capture stdout and sterr in the log
    class MyLogger(object):
        def __init__(self, logger, level):
                """Needs a logger and a logger level."""
                self.logger = logger
                self.level = level
    
        def write(self, message):
                # Only log if there is a message (not just a new line)
                if message.rstrip() != "":
                        self.logger.log(self.level, message.rstrip())
    
    # Replace stdout with logging to file at INFO level
    sys.stdout = MyLogger(logger, logging.INFO)
    # Replace stderr with logging to file at ERROR level
    sys.stderr = MyLogger(logger, logging.ERROR)
    
    
    def signal_handler(signum,stack):
        print "signal:%s" % signum
    
    
    signal.signal(signal.SIGHUP,signal_handler)
    signal.signal(signal.SIGINT,signal_handler) 
    signal.signal(signal.SIGTERM,signal_handler)
    
    
    class Daemon:
        """
        A generic daemon class.
    
        Usage: subclass the Daemon class and override the run() method
        """
        def __init__(self, pidfile, stdin='/dev/null', stdout='/tmp/out.txt', stderr='/tmp/err.txt'):
            self.stdin = stdin
            self.stdout = stdout
            self.stderr = stderr
            self.pidfile = pidfile
    
        def daemonize(self):
            """
            do the UNIX double-fork magic, see 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+', 0)
            # 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().
            """
    

As you can see, when i use kill command to the test.py , there is nothing
print in the log files.


Answer: Signals are only delivered to a single thread. If delivered to your first
thread `mythread()` since it has stopped due to an exception, nothing will
happen.

Try reversing the order of dispatch of threads to see.

