diff --git a/Aaptos.py b/Aaptos.py index 3720105..f5c4bd8 100644 --- a/Aaptos.py +++ b/Aaptos.py @@ -1,5 +1,10 @@ +#!/usr/bin/env python + import time +import sys from threading import Thread, Event +from optparse import OptionParser +from Daemon import Daemon import AaptosDb import AaptosSOAP import AaptosCli @@ -52,15 +57,72 @@ def run(self): cli_app = AaptosCli.MyAaptosCliApp(soapProxy=aaptos,loggerEnabled=self.loggerEnabled) cli_app.run() +class AaptosDaemon(Daemon): + def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null', withDb=False): + Daemon.__init__(self,pidfile,stdin,stdout,stderr) + self.withDb = withDb + + def run(self): + serverRunning = Event() + loggerEnabled = Event() + loggerEnabled.set() + serverThread(name="SOAPServer",daemon=False, kwargs = {"serverRunning":serverRunning}).start() + if self.withDb: + loggerThread(name="DBLogger",daemon=True, kwargs = {"serverRunning":serverRunning, "loggerEnabled":loggerEnabled}).start() + def main(): + # pidfile + pidfile = "/tmp/daemon-aaptos.pid" + # options handling + usage="""%prog [options] [start|stop|restart]""" + description="""The Aaptos daemon. +It can be started either as a deamon, or interactively, with or without db and cli components.""" + parser = OptionParser(usage=usage,add_help_option=True,description=description) + parser.add_option("-D", "--daemon", action="store_true", dest="daemon", default=False, + help="start as a daemon. Requires additional start/stop/restart argument.") + parser.add_option("-c", "--cli", action="store_true", dest="withCli", default=False, + help="also start the client.") + parser.add_option("-l", "--log", action="store_true", dest="withDb", default=False, + help="also start the database logger.") + (options, args) = parser.parse_args() + if options.daemon: + if not args or args[-1] not in ["start","stop","restart"]: + parser.error("Daemon mode requires an additional start/stop/restart argument.") + else: + mode = args[-1] + else: + if args and args[-1] in ["start","stop","restart"]: + parser.error("%s is intended for daemon mode. Use together with --daemon"%args[-1]) + if options.withCli: + if options.daemon: + parser.error("--cli is incompatible with --daemon.") - serverRunning = Event() - loggerEnabled = Event() - loggerEnabled.set() - - serverThread(name="SOAPServer",daemon=True, kwargs = {"serverRunning":serverRunning}).start() - loggerThread(name="DBLogger",daemon=True, kwargs = {"serverRunning":serverRunning, "loggerEnabled":loggerEnabled}).start() - cliThread(name="CLIent", kwargs = {"serverRunning":serverRunning, "loggerEnabled":loggerEnabled}).start() + # start Aaptos + if options.daemon: + # daemon mode + daemon = AaptosDaemon(pidfile, withDb=options.withDb) + getattr(daemon,mode)() + else: + # non-deamon mode + daemon = AaptosDaemon(pidfile) + if daemon.getpid(): + sys.stderr.write("pidfile %s already exist. Already running as a daemon?\n"%pidfile) + sys.exit(1) + serverRunning = Event() + loggerEnabled = Event() + loggerEnabled.set() + s = serverThread(name="SOAPServer",daemon=True, kwargs = {"serverRunning":serverRunning}) + if options.withDb: + loggerThread(name="DBLogger",daemon=True, kwargs = {"serverRunning":serverRunning, "loggerEnabled":loggerEnabled}).start() + if options.withCli: + s.start() + cliThread(name="CLIent", kwargs = {"serverRunning":serverRunning, "loggerEnabled":loggerEnabled}).start() + else: + try: + s.run() + except KeyboardInterrupt: + pass + print "\nAAPTOS SOAP server stopped." if __name__ == '__main__': main() diff --git a/Daemon.py b/Daemon.py new file mode 100644 index 0000000..c1aed57 --- /dev/null +++ b/Daemon.py @@ -0,0 +1,129 @@ +import sys, os, time, atexit +from signal import SIGTERM + +class Daemon: + """ + A generic daemon class. + Original code by Sander Marechal (http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/) + 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 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 getpid(self): + """ + Check for a pidfile to see if the daemon already runs + """ + # 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 + return pid + + def start(self): + """ + Start the daemon + """ + # Check for a pidfile to see if the daemon already runs + if self.getpid(): + 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 + pid = self.getpid() + + 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(). + """ +