def daemonize(self): """Creates the daemon. It will fork a child process and then exit parent. By performing a double fork, set the current process's user id, change the current working directory, set the current numeric umask, redirect standard streams and write the pid to a file. """ def redirect_stream(system_stream, target_stream): """Redirect a system stream to a specified file. """ if target_stream is None: target_f = os.open(os.devnull, os.O_RDWR) else: target_f = target_stream.fileno() os.dup2(target_f, system_stream.fileno()) def fork_then_exit_parent(error_message): """Fork a child process, then exit the parent process. """ try: pid = os.fork() if pid > 0: os._exit(0) # pylint: disable=W0212 except OSError as err: msg = "{0}: [{1}] {2}".format(error_message, err.errno, err.strerror) self._report(msg, logging.CRITICAL) raise UtilDaemonError(msg) # Fork fork_then_exit_parent("Failed first fork.") try: os.setsid() os.chdir(self.chdir) os.umask(self.umask) except Exception as err: msg = "Unable to change directory ({0})".format(err) self._report(msg, logging.CRITICAL) raise UtilDaemonError(msg) # Double fork fork_then_exit_parent("Failed second fork.") # Redirect streams redirect_stream(sys.stdin, self.stdin) redirect_stream(sys.stdout, self.stdout) redirect_stream(sys.stderr, self.stderr) # write pidfile atexit.register(self.delete_pidfile) pid = str(os.getpid()) try: with open(self.pidfile, "w") as f: f.write("{0}\n".format(pid)) except IOError as err: msg = "Unable to write pidfile: {0}".format(err.strerror) self._report(msg, logging.CRITICAL) raise UtilDaemonError(msg)
def start(self, detach_process=True): """Starts the daemon. It will start the daemon if detach_process is True. """ if detach_process: # Check for a pidfile presence try: with open(self.pidfile, "rb") as f: self.pid = int(f.read().strip()) except IOError: self.pid = None except SystemExit: self.pid = None except ValueError: self.pid = None if self.pid: # Daemon already runs msg = ("pidfile {0} already exists. The daemon is already " "running?".format(self.pidfile)) self._report(msg, logging.CRITICAL) raise UtilDaemonError(msg) # Start the daemon self.daemonize() # Run automatic failover return self.run()
def stop(self): """Stops the daemon. It will stop the daemon by sending a signal.SIGTERM to the process. """ # Get the pid from the pidfile try: with open(self.pidfile, "rb") as f: self.pid = int(f.read().strip()) except IOError: self._report("pidfile {0} does not exist.".format(self.pidfile), logging.ERROR) return False except ValueError: self._report("Invalid pid in pidfile {0}.".format(self.pidfile), logging.ERROR) return False # Kill the daemon process try: while 1: os.kill(self.pid, signal.SIGTERM) time.sleep(0.1) except OSError as err: strerror = err.strerror if err.errno == 3: # No such process if os.path.exists(self.pidfile): self.delete_pidfile() else: msg = "Unable to delete pidfile: {0}".format(strerror) self._report(msg, logging.ERROR) raise UtilDaemonError(msg) return True
def delete_pidfile(self): """Deletes pidfile. """ try: os.remove(self.pidfile) except (OSError, IOError) as err: msg = "Unable to delete pidfile: {0}".format(err.strerror) self._report(msg, logging.ERROR) raise UtilDaemonError(msg)
def fork_then_exit_parent(error_message): """Fork a child process, then exit the parent process. """ try: pid = os.fork() if pid > 0: os._exit(0) # pylint: disable=W0212 except OSError as err: msg = "{0}: [{1}] {2}".format(error_message, err.errno, err.strerror) self._report(msg, logging.CRITICAL) raise UtilDaemonError(msg)