| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| |
| import atexit |
| import os |
| import platform |
| import sys |
| import time |
| from signal import SIGTERM |
| |
| |
| def pidExists(pid): |
| if not pid: |
| return False |
| |
| if (platform.system().lower() != "linux"): |
| return True |
| |
| if not os.path.exists("/proc"): |
| return True |
| |
| cmdline = "" |
| try: |
| cmdline = open("/proc/{0}/cmdline".format(pid), "r").read() |
| except Exception: |
| return False |
| if cmdline.find(os.path.split(__file__)[1]): |
| return True |
| else: |
| return False |
| |
| |
| class Daemon(object): |
| """ |
| A generic daemon class. |
| |
| Usage: subclass the Daemon class and override the run() method |
| """ |
| def __init__(self, **kwargs): |
| self.stdin = kwargs.get('stdin', '/dev/null') |
| self.stdout = kwargs.get('stdout', '/dev/null') |
| self.stderr = kwargs.get('stderr', '/dev/null') |
| self.pidfile = kwargs['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) |
| |
| if (self.stdout != "/dev/null"): |
| os.chmod(self.stdout, 0600) |
| if (self.stderr != self.stdout) and (self.stderr != "/dev/null"): |
| os.chmod(self.stderr, 0600) |
| |
| 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) |
| os.chmod(self.pidfile, 0600) |
| |
| def delpid(self): |
| sys.stdout.write("Stopping.\n") |
| sys.stdout.flush() |
| 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 pidExists(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(). |
| """ |