Skip to content

Commit

Permalink
Added options and daemon mode to Aaptos.py
Browse files Browse the repository at this point in the history
Aaptos.py can be run in two ways: as a daemon or in the console.
In both cases, the db logger can be started optionnally as a separate
thread. In console mode, the client can be started as well.
  • Loading branch information
delaere committed Aug 4, 2014
1 parent 81de1fe commit e45c89c
Show file tree
Hide file tree
Showing 2 changed files with 198 additions and 7 deletions.
76 changes: 69 additions & 7 deletions Aaptos.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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()
Expand Down
129 changes: 129 additions & 0 deletions Daemon.py
Original file line number Diff line number Diff line change
@@ -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().
"""

0 comments on commit e45c89c

Please sign in to comment.