def _system_started(self, startup, future=None): if future and not future.cancelled() and future.exception(): self._system_coro_check(future) return info(self.version + ", ready.") if startup: future = self.activate(startup) future.add_done_callback(self._system_coro_check)
def __exit__(self, a, b, c): with self._lock: self._forks -= 1 if self._forks or not self._zombies: return collateral_victims = str(self._zombies) self._zombies.clear() info("Caught subprocesses termination from unknown pids: %s", collateral_victims)
def __exit__(self, a, b, c): with self._lock: self._forks -= 1 if self._forks or not self._zombies: return collateral_victims = str(self._zombies) self._zombies.clear() info( "Caught subprocesses termination from unknown pids: %s", collateral_victims)
def _kill_system_co(self): self.notify.stopping() self._cancel_pending() # Tell the family it's been nice. It's unlikely we won't have a process family, but # it's optional, so we should handle the situation. wait_done = False # indicates if shutdown_timeout has expired if self._family: for f in self._family.values(): yield from f.final_stop() # let normal shutdown happen if self._watcher.number_of_waiters > 0 and self._shutdown_timeout: debug( "still have {0} waiting, sleeping for shutdown_timeout={1}" .format(self._watcher.number_of_waiters, self._shutdown_timeout)) yield from asyncio.sleep(self._shutdown_timeout) wait_done = True try: os.kill(-1, signal.SIGTERM) # first try a sig term if self.send_sighup: os.kill(-1, signal.SIGHUP) except ProcessLookupError: debug( "No processes remain when attempting to kill system, just stop." ) self._no_processes(True) return if wait_done: # give a short wait just so the signals fire yield from asyncio.sleep(1) # these processes are unknowns else: yield from asyncio.sleep(self._shutdown_timeout) if self._all_killed: return info("Some processes remain after {0}secs. Forcing kill".format( self._shutdown_timeout)) try: os.kill(-1, signal.SIGKILL) except ProcessLookupError: debug("No processes when attempting to force quit") self._no_processes(True) return
def _process_logger(stream, kind, service): name = service.name.replace('.service', '') while True: data = yield from stream.readline() if not data: return line = data.decode('ascii', 'ignore').rstrip() if not line: continue # ignore blank lines in stdout/stderr if kind == 'stderr': # we map to warning because stderr output is "to be considered" and not strictly # erroneous warn(line, program=name, pid=service.pid, facility=syslog_info.LOG_DAEMON) else: info(line, program=name, pid=service.pid, facility=syslog_info.LOG_DAEMON)
def do_exec(self, opts, controller): delay = opts['<delay>'] if delay is None or delay.lower() == "now": delay = 0.1 message = "Shutting down now" else: try: delay = float(delay) except ValueError: return "Specified delay is not a valid decimal number: " + str(delay) message = "Shutting down in {0} seconds".format(delay) info("requested shutdown scheduled to occur in {0} seconds".format(delay)) asyncio.get_event_loop().call_later(delay, controller.kill_system) return message
def _kill_system_co(self): self.notify.stopping() self._cancel_pending() # Tell the family it's been nice. It's unlikely we won't have a process family, but # it's optional, so we should handle the situation. wait_done = False # indicates if shutdown_timeout has expired if self._family: for f in self._family.values(): yield from f.final_stop() # let normal shutdown happen if self._watcher.number_of_waiters > 0 and self._shutdown_timeout: debug("still have {0} waiting, sleeping for shutdown_timeout={1}".format(self._watcher.number_of_waiters, self._shutdown_timeout)) yield from asyncio.sleep(self._shutdown_timeout) wait_done = True try: os.kill(-1, signal.SIGTERM) # first try a sig term if self.send_sighup: os.kill(-1, signal.SIGHUP) except ProcessLookupError: debug("No processes remain when attempting to kill system, just stop.") self._no_processes(True) return if wait_done: # give a short wait just so the signals fire yield from asyncio.sleep(1) # these processes are unknowns else: yield from asyncio.sleep(self._shutdown_timeout) if self._all_killed: return info("Some processes remain after {0}secs. Forcing kill".format(self._shutdown_timeout)) try: os.kill(-1, signal.SIGKILL) except ProcessLookupError: debug("No processes when attempting to force quit") self._no_processes(True) return
def do_exec(self, opts, controller): delay = opts['<delay>'] if delay is None or delay.lower() == "now": delay = 0.1 message = "Shutting down now" else: try: delay = float(delay) except ValueError: return "Specified delay is not a valid decimal number: " + str( delay) message = "Shutting down in {0} seconds".format(delay) info("requested shutdown scheduled to occur in {0} seconds".format( delay)) asyncio.get_event_loop().call_later(delay, controller.kill_system) return message
def _do_waitpid_all(self): # Because of signal coalescing, we must keep calling waitpid() as # long as we're able to reap a child. while True: try: pid, status = os.waitpid(-1, os.WNOHANG) debug("REAP pid={0},status={1}".format(pid,status)) except ChildProcessError: # No more child processes exist. if self._had_children: debug("no child processes present") self.events.onNoProcesses() return else: self._had_children = True if pid == 0: # A child process is still alive. return returncode = ProcStatus(status) with self._lock: try: callback, args = self._callbacks.pop(pid) except KeyError: # unknown child if self._forks: # It may not be registered yet. self._zombies[pid] = returncode continue callback = None if callback is None: info( "Caught subprocess termination from unknown pid: " "%d -> %d", pid, returncode) else: callback(pid, returncode, *args)
def _do_waitpid_all(self): # Because of signal coalescing, we must keep calling waitpid() as # long as we're able to reap a child. while True: try: pid, status = os.waitpid(-1, os.WNOHANG) debug("REAP pid={0},status={1}".format(pid, status)) except ChildProcessError: # No more child processes exist. if self._had_children: debug("no child processes present") self.events.onNoProcesses() return else: self._had_children = True if pid == 0: # A child process is still alive. return returncode = ProcStatus(status) with self._lock: try: callback, args = self._callbacks.pop(pid) except KeyError: # unknown child if self._forks: # It may not be registered yet. self._zombies[pid] = returncode continue callback = None if callback is None: info( "Caught subprocess termination from unknown pid: " "%d -> %d", pid, returncode) else: callback(pid, returncode, *args)
def _start_system_services(self): self._notify_enabled = yield from self.notify.connect() self._syslog = SyslogServer() self._syslog.configure(self._config, self._minimum_syslog_level) try: yield from self._syslog.run() except PermissionError as ex: self._syslog = None warn("syslog service cannot be started: {0}", ex) else: self._syslog.capture_python_logging() info("Switching all chaperone logging to /dev/log") self._command = CommandServer(self) try: yield from self._command.run() except PermissionError as ex: self._command = None warn("command service cannot be started: {0}", ex)
def force_log_level(self, level=None): """ Specifies the *minimum* logging level that will be applied to all syslog entries. This is primarily useful for debugging, where you want to override any limitations imposed on log file entries. As a (convenient) side-effect, if the level is DEBUG, then debug features of both asyncio as well as chaperone will be enabled. If level is not provided, then returns the current setting. """ if level is None: return self._minimum_syslog_level levid = syslog_info.PRIORITY_DICT.get(level.lower(), None) if not levid: raise Exception("Not a valid log level: {0}".format(level)) set_log_level(levid) self._minimum_syslog_level = levid self.debug = (levid == syslog_info.LOG_DEBUG) if self._syslog: self._syslog.reset_minimum_priority(levid) info("Forcing all log output to '{0}' or greater", level)
def force_log_level(self, level = None): """ Specifies the *minimum* logging level that will be applied to all syslog entries. This is primarily useful for debugging, where you want to override any limitations imposed on log file entries. As a (convenient) side-effect, if the level is DEBUG, then debug features of both asyncio as well as chaperone will be enabled. If level is not provided, then returns the current setting. """ if level is None: return self._minimum_syslog_level levid = syslog_info.PRIORITY_DICT.get(level.lower(), None) if not levid: raise Exception("Not a valid log level: {0}".format(level)) set_log_level(levid) self._minimum_syslog_level = levid self.debug = (levid == syslog_info.LOG_DEBUG) if self._syslog: self._syslog.reset_minimum_priority(levid) info("Forcing all log output to '{0}' or greater", level)
def loginfo(self, *args, **kwargs): info(*args, facility=self.syslog_facility, **kwargs)