def start(logging_config, daemon_config, seneschal_config): syslog.openlog('seneschal', 0, syslog.LOG_USER) engine = Engine(seneschal_config) pidfile, daemon_options = check_daemon_options(daemon_config) if is_pidfile_stale(pidfile): syslog.syslog(syslog.LOG_NOTICE, 'breaking stale PID file') pidfile.break_lock() # The remaining entries in daemon_options will be passed as-is to # daemon.DaemonContext. context = DaemonContext(pidfile=pidfile, **daemon_options) context.signal_map = make_signal_map() syslog.syslog(syslog.LOG_NOTICE, 'starting daemon context') try: with context: # Will fail if daemon already running pid = os.getpid() syslog.syslog(syslog.LOG_NOTICE, 'daemon running as: %s' % pid) config_logging(logging_config) logger.debug('========================================') logger.info('daemon running pid=%s', pid) logger.debug('args: %r', sys.argv) logger.debug('daemon_options: %r', daemon_options) logger.debug('seneschal_config: %r', seneschal_config) while Engine.running: engine.sweep() time.sleep(1) # TODO: Long polling times, may result in an unacceptable # delay during daemon shutdown. except Exception as e: syslog.syslog(syslog.LOG_ERR, str(e)) logger.exception(repr(e)) raise finally: syslog.syslog(syslog.LOG_NOTICE, 'exiting') logger.info('exiting')
def _reload(self): # the service is running if self.pidfile.is_locked() and not runner.is_pidfile_stale(self.pidfile): self.app.reload() # the service is not running else: raise RuntimeError("The service is not normally running.")
def main(args): if args['--daemonize']: acquire_pidfile_path = args['--pidfile'] + '.acquirelock' pidfile = pidlockfile.PIDLockFile(args['--pidfile'], timeout=0) # If the first pidfile is stale, use another pid lockfile to make sure we're not # racing someone else. This lockfile should be far less likely to be stale since # it is only kept during this check and breaking the stale lock. try: with pidlockfile.PIDLockFile(acquire_pidfile_path, timeout=0): if runner.is_pidfile_stale(pidfile): print('Stale lockfile detected, breaking the stale lock %s' % (args['--pidfile'],)) pidfile.break_lock() except LockError: print('Got an exception while attempting to check for a stale main pidfile.') print('There is likely to be a stale acquire pidfile at %s' % (acquire_pidfile_path,)) raise with daemon.DaemonContext( pidfile=pidfile, working_directory=os.getcwd(), stdout=open('log/stdout.log', 'a+'), stderr=open('log/stderr.log', 'a+'), ): run(args['--config']) else: run(args['--config'])
def do_stop(self, arg): """ stop the daemon (not implemented). """ pidfile = self.make_pid_file() pid = pidfile.read_pid() if not pid: print("Daemon not running.") return if is_pidfile_stale(pidfile): print("Removing stale pid file.") pidfile.break_lock() return try: os.kill(pid, signal.SIGTERM) try: pidfile.acquire(5) except lockfile.LockTimeout: print("Daemon still running (pid=%d)." % pid) else: pidfile.release() print("Daemon process terminated.") except OSError as e: print("Error terminating daemon (pid=%d)." % pid)
def __daemon(self): """ Daemonizes the process; returns a non-zero exit code in case of failure. """ # Daemonize try: # Create and check PID file oPidLockFile = make_pidlockfile(self.__oArguments.pid, 0) if is_pidfile_stale(oPidLockFile): oPidLockFile.break_lock() if oPidLockFile.is_locked(): sys.stderr.write( 'ERROR[Daemon]: Daemon process already running; PID=%s\n' % oPidLockFile.read_pid()) return errno.EEXIST # Create daemon context oDaemonContext = DaemonContext(pidfile=oPidLockFile) oDaemonContext.signal_map = {signal.SIGTERM: self.__signal} oDaemonContext.open() emit_message('[%s]' % os.getpid()) # Redirect standard error to syslog syslog.openlog('LogWatcher', syslog.LOG_PID, syslog.LOG_DAEMON) sys.stderr = Logger(self.__syslog) # Execute return self.__spawnWatchers(self.__oConfigObj) except Exception as e: sys.stderr.write( 'ERROR[Daemon]: Failed to fork to background; %s\n' % str(e)) return errno.ESRCH
def __daemon(self): """ Daemonizes the process; returns a non-zero exit code in case of failure. """ # Daemonize try: # Create and check PID file oPidLockFile = make_pidlockfile(self.__oArguments.pid, 0) if is_pidfile_stale(oPidLockFile): oPidLockFile.break_lock() if oPidLockFile.is_locked(): iPid = oPidLockFile.read_pid() logger.error(f"daemon: Process already running; PID={iPid}") return errno.EEXIST # Create daemon context oDaemonContext = DaemonContext(pidfile=oPidLockFile) oDaemonContext.signal_map = {signal.SIGTERM: self.__signal} oDaemonContext.open() emit_message(f"daemon: Forked to background; PID={os.getpid()}") # Redirect standard error to syslog oHandler = SysLogHandler(address="/dev/log") oHandler.setLevel(logger.level) oHandler.setFormatter( logging.Formatter( "%(name)s[%(process)d]: %(levelname)s: %(message)s")) logger.addHandler(oHandler) # Execute return self.__updater() except Exception as e: logger.error(f"daemon: Failed to fork to background; {str(e)}") return errno.ESRCH
def signal(self, sig): """Send a signal to the daemon process specified in the current PID file.""" self.assert_daemon() if runner.is_pidfile_stale(self.pidfile): self.pidfile.break_lock() else: os.kill(self.get_pid(), sig)
def do_status(self, arg): pidfile = self.make_pid_file() pid = pidfile.read_pid() if not pid: print("Daemon not running.") return if not is_pidfile_stale(pidfile): print("Daemon running (pid=%d)." % pid)
def _get_status(self): if not self.pidfile.is_locked(): return 0, None elif runner.is_pidfile_stale(self.pidfile): pid = self.pidfile.read_pid() self.pidfile.break_lock() return -1, pid else: return 1, None
def _start(self): if runner.is_pidfile_stale(self.pidfile): self.pidfile.break_lock() if self.pidfile.read_pid() is not None: raise runner.DaemonRunnerStartFailureError( u"PID file %r already locked" % self.app.pidfile_path) return super(_DaemonRunner, self)._start()
def start(self, log_dir, verbose, **signal_map): """Start the daemon process.""" if runner.is_pidfile_stale(self.pidfile): self.pidfile.break_lock() # Is it better to try to lock the pidfile here to avoid any race conditions? # Managing the release through potentially-failing detach_process_context() # calls sounds... tricky. if self.pidfile.is_locked(): raise RuntimeError('{} is already running'.format(self.name)) homedir = os.path.expanduser('~') log_dir = os.path.realpath(str(log_dir)) daemon.prevent_core_dump() for sig, handler in DEFAULT_SIGNAL_MAP.items(): if sig not in signal_map: signal_map[sig] = handler daemon.set_signal_handlers({getattr(signal, sig):handler for sig, handler in signal_map.items()}) daemon.close_all_open_files(exclude={sys.stdin.fileno(), sys.stdout.fileno(), sys.stderr.fileno()}) daemon.change_working_directory(homedir) #initialize logging logging.set_verbose(verbose) logging.attach_file_handlers(log_dir) logger.info('Starting {}', self.name) # detach parent process. Note: ZMQ and camera don't work in child process # after a fork. ZMQ contexts don't work, and andor finalize functions hang. # Thus we need to init these things AFTER detaching the process via fork(). # In order to helpfully print messages to stderr from the child process, # we use a custom detach_process_context that pipes stderr back to the # parent's stderr. (Otherwise the stderr would just spew all over the # terminal after the parent exits, which is ugly.) detach_process_context() with self.pidfile: try: #initialize scope server logger.debug('Initializing {}', self.name) self.initialize_daemon() # detach stderr logger, and redirect python-generated output to /dev/null # (preventing anything that tries to print to / read from these streams from # throwing an error) logging.detach_console_handler() daemon.redirect_stream(sys.stdin, None) daemon.redirect_stream(sys.stdout, None) # below also closes pipe to parent that was redirected from old stderr by # detach_process_contex, which allows parent to exit... daemon.redirect_stream(sys.stderr, None) except: logger.error('{} could not initialize after becoming daemonic:', self.name, exc_info=True) raise try: logger.debug('Running {}', self.name) self.run_daemon() except Exception: logger.error('{} terminating due to unhandled exception:', self.name, exc_info=True)
def _get_status(self): pidfile_path = self.pidfile.path if not self.pidfile.is_locked(): return 0, None elif runner.is_pidfile_stale(self.pidfile): pid = self.pidfile.read_pid() self.pidfile.break_lock() return -1, pid else: return 1, None
def _stop(self): """ Exit the daemon process specified in the current PID file. """ if not self.pidfile.is_locked(): pidfile_path = self.pidfile.path raise DaemonRunnerStopFailureError( "PID file %(pidfile_path)r not locked" % vars()) if is_pidfile_stale(self.pidfile): self.pidfile.break_lock() else: self._terminate_daemon_process()
def _reload(self): """ Reload the daemon process specified in the current PID file. """ if not self.pidfile.is_locked(): pidfile_path = self.pidfile.path print(u"PID file %(pidfile_path)r not locked" % vars()) if runner.is_pidfile_stale(self.pidfile): self.pidfile.break_lock() else: self._reload_daemon_process()
def _status(self): app_name = self.app.get_app_name() pid = self.pidfile.read_pid() if not pid: print "%s is not running" % app_name return if is_pidfile_stale(self.pidfile): print "%s is not running. Stale PID file %s" % (app_name, self.pidfile.path) else: print "%s (pid %s) is running..." % (app_name, pid)
def cmd_run(argv): parser = argparse.ArgumentParser( prog="aio run", description='aio app runner') parser.add_argument( "-d", action="store_true", help="daemonize process") parsed, remainder = parser.parse_known_args() aio.app.logging.start_logging(aio.app.config) aio.app.signal.start_listeners(aio.app.config) if parsed.d: pidfile = runner.make_pidlockfile( os.path.abspath('var/run/aio.pd'), 1) if runner.is_pidfile_stale(pidfile): pidfile.break_lock() if pidfile.is_locked(): print( "There seems to be another aio process running " + "already, stop that one first") exit() class open_logs: def __enter__(self): return ( open("var/log/aio.log", "a"), open("var/log/aio.log", "a")) def __exit__(self, *la): pass with open_logs() as (stdout, stderr): daemon_context = dict( stdout=stdout, stderr=stderr, working_directory=os.getcwd(), pidfile=pidfile) dc = daemon.DaemonContext(**daemon_context) try: dc.open() app_runner() except lockfile.AlreadyLocked: print('LOCKFILE LOCKED') else: app_runner()
def stop(self): """ Exit the daemon process specified in the current PID file. """ if not self.pidfile: self.daemon_context.close() if not self.pidfile.is_locked(): raise DaemonRunnerStopFailureError( u"PID file {0} not locked".format(self.pidfile.path)) if is_pidfile_stale(self.pidfile): self.pidfile.break_lock() else: self._terminate_daemon_process()
def start_daemon(pidfile_path, log_path, acquire_timeout=5): pidfile = TimeoutPIDLockFile(pidfile_path, acquire_timeout=acquire_timeout) if is_pidfile_stale(pidfile): pidfile.break_lock() if pidfile.is_locked(): pid = pidfile.read_pid() if pid is not None: puts(colored.red('Already running at pid: %d' % pid)) else: puts(colored.red('Already running')) return None logfile = open(log_path, 'w+t') puts(colored.blue('Starting')) return DaemonContext(pidfile=pidfile, stdout=logfile, stderr=logfile)
def _status(self): # the service is not running if not self.pidfile.is_locked(): status = "inactive" # the service is failed to start elif runner.is_pidfile_stale(self.pidfile): status = "fail" # the service is running else: status = "active" # display the status print " Active:", self.__status[status] print " Main PID:", self.pidfile.read_pid() # display the application specific status when service is running if status == "active": print "" print " App Specific:", self.app.getStatus()
def start_daemon(self): # root user check if os.geteuid() == 0: raise CommandError(u"Can not run daemon as root!\n") # PID file setuo self.init_pidfile() # remove pid file if PID is not active if is_pidfile_stale(self.pidfile): self.pidfile.break_lock() # check for existence if PID file, means another instance is already running if self.pidfile.is_locked(): pidfile_path = self.pidfile.path raise CommandError(u"PID file %(pidfile_path)r is locked. Daemon is probably already running." % vars()) # configure daemon context self.daemon_context = daemon.DaemonContext( working_directory=TWITTER_CACHE_WORKING_DIR, umask=0o002, detach_process=True, pidfile=self.pidfile ) if TWITTER_CACHE_LOG_FILE is not None: self.daemon_context.stdout = open(TWITTER_CACHE_LOG_FILE, 'a+') self.daemon_context.stderr = open(TWITTER_CACHE_LOG_FILE, 'a+', buffering=0) self.stdout.write(u"Starting daemon...\n") try: # become a daemon self.daemon_context.open() except lockfile.AlreadyLocked: pidfile_path = self.pidfile.path raise CommandError( u"PID file %(pidfile_path)r already locked" % vars()) except lockfile.LockTimeout: pidfile_path = self.pidfile.path raise CommandError( u"PID file %(pidfile_path)r lockfile.LockTimeout" % vars()) pid = os.getpid() message = self.start_message % vars() self.emit_formatted_message(message) # run app self.cache_tweets()
def _start(self): """ Open the daemon context and run the application. """ if is_pidfile_stale(self.pidfile): self.pidfile.break_lock() try: self.open() except daemon.pidlockfile.AlreadyLocked: pidfile_path = self.pidfile.path raise DaemonRunnerStartFailureError( "PID file %(pidfile_path)r already locked" % vars()) pid = os.getpid() message = self.start_message % vars() emit_message(message) self.app.run()
def stop(daemon_config): """Standard daemon stop logic.""" pidfile, _ = check_daemon_options(daemon_config) if not pidfile.is_locked(): error = DaemonStopError( "PID file {pidfile.path!r} not locked".format(pidfile=pidfile)) raise error if is_pidfile_stale(pidfile): syslog.syslog(syslog.LOG_NOTICE, 'breaking stale PID file') pidfile.break_lock() else: pid = pidfile.read_pid() try: os.kill(pid, signal.SIGTERM) except OSError as exc: error = DaemonStopError( "Failed to terminate {pid:d}: {exc}".format(pid=pid, exc=exc)) raise error
def start(self): """ Open the daemon context and run the application. """ if self.pidfile and is_pidfile_stale(self.pidfile): self.pidfile.break_lock() try: self.daemon_context.open() except lockfile.AlreadyLocked: raise DaemonRunnerStartFailureError( u"PID file %(pidfile_path)r already locked".format( self.pidfile.path)) pid = os.getpid() message = self.start_message.format(pid, self.app.name) emit_message(message) signal.signal(signal.SIGHUP, self.restart) self.app.run()
def stop_daemon(self): # PID file setup self.init_pidfile() # does a PID file exists if not self.pidfile.is_locked(): pidfile_path = self.pidfile.path raise CommandError(u"PID file %(pidfile_path)r not locked" % vars()) # is the PID in the pid file active if is_pidfile_stale(self.pidfile): self.pidfile.break_lock() self.stdout.write(u"Daemon is not running.\n") else: # get the PID from PID file pid = self.pidfile.read_pid() try: # terminate the daemon process os.kill(pid, signal.SIGTERM) except OSError as exc: raise CommandError(u"Failed to terminate %(pid)d: %(exc)s" % vars()) logfile = open(TWITTER_CACHE_LOG_FILE, 'a+', buffering=0) logfile.write(u"%s\tDaemon stopped\n" % datetime.datetime.now()) logfile.close() self.stdout.write(u"Daemon stopped.\n")
def start(self, log_dir, verbose, **signal_map): """Start the daemon process.""" if runner.is_pidfile_stale(self.pidfile): self.pidfile.break_lock() # Is it better to try to lock the pidfile here to avoid any race conditions? # Managing the release through potentially-failing detach_process_context() # calls sounds... tricky. if self.pidfile.is_locked(): raise RuntimeError('{} is already running'.format(self.name)) homedir = os.path.expanduser('~') log_dir = os.path.realpath(str(log_dir)) daemon.prevent_core_dump() for sig, handler in DEFAULT_SIGNAL_MAP.items(): if sig not in signal_map: signal_map[sig] = handler daemon.set_signal_handlers({ getattr(signal, sig): handler for sig, handler in signal_map.items() }) daemon.close_all_open_files(exclude={ sys.stdin.fileno(), sys.stdout.fileno(), sys.stderr.fileno() }) daemon.change_working_directory(homedir) #initialize logging logging.set_verbose(verbose) logging.attach_file_handlers(log_dir) logger.info('Starting {}', self.name) # detach parent process. Note: ZMQ and camera don't work in child process # after a fork. ZMQ contexts don't work, and andor finalize functions hang. # Thus we need to init these things AFTER detaching the process via fork(). # In order to helpfully print messages to stderr from the child process, # we use a custom detach_process_context that pipes stderr back to the # parent's stderr. (Otherwise the stderr would just spew all over the # terminal after the parent exits, which is ugly.) detach_process_context() with self.pidfile: try: #initialize scope server logger.debug('Initializing {}', self.name) self.initialize_daemon() # detach stderr logger, and redirect python-generated output to /dev/null # (preventing anything that tries to print to / read from these streams from # throwing an error) logging.detach_console_handler() daemon.redirect_stream(sys.stdin, None) daemon.redirect_stream(sys.stdout, None) # below also closes pipe to parent that was redirected from old stderr by # detach_process_contex, which allows parent to exit... daemon.redirect_stream(sys.stderr, None) except: logger.error( '{} could not initialize after becoming daemonic:', self.name, exc_info=True) raise try: logger.debug('Running {}', self.name) self.run_daemon() except Exception: logger.error('{} terminating due to unhandled exception:', self.name, exc_info=True)
def do_start(self, arg): """ start the daemon (not implemented). """ if not HAS_DAEMON: print("The package python-daemon is not available, please use fg.") return # As the python-daemon implementation will close all file # handlers by default, we have to defer the opening of all file # handlers that may end up being referenced after the fork to # prevent hilariously bad things from happening, like writing # the logs into a database file. logs = [] def get_uid_gid(username): # Get the uid/gid from username to drop into post-startup if not username: return None, None try: pwrec = pwd.getpwnam(username) except KeyError: try: pwrec = pwd.getpwuid(int(username)) except (KeyError, ValueError): logs.append((logging.WARNING, 'Invalid or unknown daemon.effective_user: `%r`', username)) return None, None if os.geteuid() == 0: logs.append((logging.INFO, 'Found daemon.effective_user: %s (uid=%d).', pwrec[0], pwrec[2])) return pwrec[2], pwrec[3] if os.geteuid() == pwrec[2]: logs.append((logging.INFO, 'Already running as daemon.effective_user: %s (uid=%d).', pwrec[0], pwrec[2])) return None, None logs.append((logging.WARNING, 'Process owner is not root; daemon.effective_user ignored.')) def make_daemon_context(): config_daemon = self.get_daemon_config() pidfile = self.make_pid_file() uid, gid = get_uid_gid(config_daemon.get('effective_user')) working_dir = config_daemon.get('working_directory') if working_dir is None: print('ERROR: working directory undefined; aborting.') return # need to figure out relative paths to pid? return daemon.DaemonContext(**{ 'pidfile': pidfile, 'uid': uid, 'gid': gid, 'stdin': sys.stdin, 'stdout': sys.stdout, 'stderr': sys.stderr, }) dcontext = make_daemon_context() if not dcontext: print("ERROR: Fail to construct daemon context.") return pid = dcontext.pidfile.read_pid() if pid: if is_pidfile_stale(dcontext.pidfile): print("Removing stale pid file.") dcontext.pidfile.break_lock() else: print("Daemon already running (pid=%d)." % pid) return try: print("Starting daemon; check logs for status.") dcontext.open() except (lockfile.LockTimeout, lockfile.AlreadyLocked): print("ERROR: Failed to acquire pid lock file.") # Elsie and Haqua's homeworld opened. logger = logging.getLogger('mtj.eve.tracker.ctrl') runner = self.make_runner(self.options.config) # Log the deferred logs. for log in logs: logger.log(*log) logger.info("Daemon running (pid=%d).", dcontext.pidfile.read_pid()) try: runner.initialize() runner.run(app=self.app) except SystemExit as e: logger.info('%s', e) except: logger.exception("Unexpected error broke the runner.")
logging.error("Exiting...") sys.exit(1) from daemon import runner #from daemon import runner, pidlockfile service = StorageService() daemon_runner = runner.DaemonRunner(service) global LOGGER if options.foreground: LOGGER = 'storage_service_console' service_logger = logging.getLogger(LOGGER) service.logger = service_logger if runner.is_pidfile_stale(daemon_runner.pidfile): daemon_runner.pidfile.break_lock() from lockfile import LockTimeout try: daemon_runner.pidfile.acquire() except LockTimeout: service_logger.error( "PID file %(service.pidfile_path)r already locked. Exiting..." % vars()) sys.exit(1) try: service_logger.info( "Running service in foreground mode. Press Control-c to stop.") service.run()