Browse Source

Add daemon to webui

oz123 10 years ago
parent
commit
eed145511f
2 changed files with 259 additions and 13 deletions
  1. 168 0
      scripts/daemon.py
  2. 91 13
      scripts/webui.py

+ 168 - 0
scripts/daemon.py

@@ -0,0 +1,168 @@
+#!/usr/bin/env python
+
+import sys
+import os
+from signal import SIGTERM
+
+
+class Daemon(object):
+
+    """
+    A generic daemon class.
+    Usage: subclass the Daemon class and override the run() method
+    """
+
+    def __init__(self, name, pidfile, stdin='/dev/null', stdout='/dev/null',
+                 stderr='/dev/null', noisy=False):
+        """
+        initialize some properties needed later.
+        """
+        self.stdin = stdin
+        self.stdout = stdout
+        self.stderr = stderr
+        self.pidfile = pidfile
+        self.name = name
+        self.noisy = noisy
+
+    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))
+            print("SSSSSDDD")
+            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)
+
+        pid = str(os.getpid())
+        print("[%s pid: %s]" % (self.name, pid))
+        # redirect standard file descriptors
+        sys.stdout.flush()
+        sys.stderr.flush()
+        si = open(self.stdin, 'r')
+        so = open(self.stdout, 'a+')
+        se = open(self.stderr, 'a+', 0)
+
+        if not self.noisy:
+            os.dup2(si.fileno(), sys.stdin.fileno())
+            os.dup2(so.fileno(), sys.stdout.fileno())
+            os.dup2(se.fileno(), sys.stderr.fileno())
+
+        open(self.pidfile, 'w+').write("%s\n" % (pid))
+
+    def start(self):
+        """
+        Start the daemon
+        """
+        # Check for a pidfile to see if the daemon already runs
+        try:
+            pf = open(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 = open(self.pidfile, 'r')
+            pid = int(pf.read().strip())
+            pf.close()
+            os.remove(self.pidfile)
+        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:
+            os.kill(pid, SIGTERM)
+        except OSError, err:
+            err = str(err)
+            print err
+            sys.exit(1)
+
+    def check_proc(self):
+        """
+        check for pid file
+        """
+        # Check for a pidfile to see if the daemon already runs
+        # instead opening self pidfile many times, we should just
+        # set self.controlpid
+        try:
+            pf = open(self.pidfile, 'r')
+            proc = pf.read()
+            pid = int(proc.strip())
+            pf.close()
+            return pid
+        except IOError:
+            message = "pidfile %s does not exist. Daemon not running?\n"
+            sys.stderr.write(message % self.pidfile)
+
+    def restart(self):
+        """
+        Restart the daemon
+        """
+        self.stop()
+        self.start()
+
+    def exit_running(self):
+        """
+        do not start if already running
+        """
+        if os.path.exists(self.pidfile):
+            pf = open(self.pidfile, 'r')
+            proc = pf.read()
+            running = int(proc.strip())
+            message = "pidfile %s already exists. Daemon already running?\n"
+            message += "check if process %d still exists\n"
+            sys.stderr.write(message % (self.pidfile, running))
+            sys.exit(1)
+
+    def startd(self):
+        raise Exception("You should override this method!")
+
+    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().
+        """
+        raise Exception("You should override this method!")

+ 91 - 13
scripts/webui.py

@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-#============================================================================
+# ============================================================================
 # This file is part of Pwman3.
 #
 # Pwman3 is free software; you can redistribute it and/or modify
@@ -14,17 +14,21 @@
 # You should have received a copy of the GNU General Public License
 # along with Pwman3; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-#============================================================================
+# ============================================================================
 # Copyright (C) 2012-2014 Oz Nahum <nahumoz@gmail.com>
-#============================================================================
+# ============================================================================
 from __future__ import print_function
 from bottle import route, run, debug, template, request, redirect, static_file
 from pwman.util.crypto import CryptoEngine
 import pwman.data.factory
 from pwman.data.tags import TagNew
 from pwman import parser_options, get_conf_options
+from daemon import Daemon
 from pkg_resources import resource_filename
 import itertools
+import argparse
+import sys
+from os.path import expanduser, join
 
 templates_path = [resource_filename('pwman', 'ui/templates')]
 statics = [resource_filename('pwman', 'ui/templates/static')][0]
@@ -38,6 +42,8 @@ DB = None
 # When issuing multiple times filter
 
 # WEB GUI shows multiple tags as one tag!
+
+
 def require_auth(fn):
     def check_auth(**kwargs):
         if AUTHENTICATED:
@@ -157,15 +163,87 @@ def server_static(filepath):
     return static_file(filepath, root=statics)
 
 
-if __name__ == '__main__':
-    OSX = False
-    args = parser_options().parse_args()
-    xselpath, dbtype = get_conf_options(args, OSX)
-    dbver = 0.5
-    DB = pwman.data.factory.create(dbtype, dbver)
-    DB.open(dbver=0.5)
+class Pwman3WebDaemon(Daemon):
+
+    def startd(self):
+        """
+        Start the daemon
+        """
+        # Check for a pidfile to see if the daemon already runs
+        self.exit_running()
+        #if not os.path.exists(self.pidfile):
+        # Start the daemon
+        self.daemonize()
+        # after self.daemonize()
+        # all output is redirected
+        print(open(self.pidfile).read())
+        self.run()
+
+    def run(self):
+        global AUTHENTICATED, TAGS, DB
+
+        OSX = False
+        sys.argv = []
+
+        args = parser_options().parse_args()
+        xselpath, dbtype = get_conf_options(args, OSX)
+        dbver = 0.5
+        DB = pwman.data.factory.create(dbtype, dbver)
+        DB.open(dbver=0.5)
+        print(dir(DB))
+        CryptoEngine.get(dbver=0.5)
+        print(pwman.config._conf)
+        debug(True)
+        run(port=9030)
 
-    crypto = CryptoEngine.get(dbver=0.5)
 
-    debug(True)
-    run(reloader=True, port=9030)
+if __name__ == '__main__':
+
+    parser = argparse.ArgumentParser(
+        description="Start the webui of pwman3",
+        usage='%(prog)s start|stop|restart|status [-p|-d]\n',
+        )
+    parser.add_argument('-D', '--NoDaemon',
+                        help='Do not fork, start in foreground',
+                        action="store_true", default=False)
+
+    ext_usage = ("{prog} start - starts the webui."
+                 "{prog} stop - stops the webui."
+                 "{prog} status - check if the process is already running."
+                 "".format(prog=parser.prog))
+
+    if len(sys.argv) == 1:
+        parser.print_help()
+        sys.exit(1)
+
+    action = sys.argv[1]
+
+    if action not in ['start', 'stop', 'restart', 'status']:
+        parser.print_help()
+        print(ext_usage)
+        sys.exit(1)
+
+    sys.argv = sys.argv[1:]
+
+    path = join(expanduser("~"), '.pwman')
+    daemon = Pwman3WebDaemon(parser.prog, join(path, '%s.pid' % parser.prog),
+                             noisy=True)
+
+    args = parser.parse_args()
+
+    if action == 'status':
+        running = daemon.check_proc()
+        if running:
+            print("%s already runs with pid: %d" % (parser.prog, running))
+    if action == 'start' and args.NoDaemon:
+        try:
+            daemon.start()
+        except KeyboardInterrupt:
+            daemon.set_level("auto")
+            daemon.stop()
+    if action == 'start' and not args.NoDaemon:
+        daemon.startd()
+    if action == "restart":
+        daemon.restart()
+    if action == "stop":
+        daemon.stop()