def _do_proxy_send(self, name, value): if not (parent_socket and self.proxy_enabled): return if not self.parent_client: self.parent_client = NotifyClient(parent_socket, onClose = self._parent_closed) yield from self.parent_client.run() yield from self.parent_client.send("{0}={1}".format(name, value))
class SDNotifyExec: exitcode = 0 sockname = None listener = None parent = None timeout = None wait_mode = None verbose = False parent_client = None proxy_enabled = True INFO_MESSAGE = { 'READY': "READY={1}{2}", 'MAINPID': "Process PID (={1}) notification{2}", 'ERRNO': "Process ERROR (={1}) notification{2}", 'STATUS': "Status message = '{1}'{2}", 'default': "{0}={1}{2}", } def __init__(self, options): self.sockname = options['--socket'] if not self.sockname: self.sockname = "/tmp/sdnotify-proxy-{0}.sock".format(os.getpid()) self.proxy_enabled = parent_socket and not options['--noproxy'] if options['--wait-stop']: self.wait_mode = 'stop' elif options['--wait-ready']: self.wait_mode = 'ready' if options['--timeout'] and self.wait_mode: self.timeout = float(options['--timeout']) self.verbose = options['--verbose'] # Modify original environment os.environ['NOTIFY_SOCKET'] = self.sockname # Set up the environment, reparse the options, build the final command Environment.set_parse_parameters('%', '{') env = Environment() env['PID'] = str(os.getpid()) env['SOCKET_ARGS'] = options['--template'] or DEFAULT_TEMPLATE if parent_socket: env['ORIG_NOTIFY_SOCKET'] = parent_socket env = env.expanded() self.proc_args = shlex.split(env.expand(' '.join(maybe_quote(arg) for arg in [options['COMMAND']] + options['ARGS']))) self.listener = NotifyListener(self.sockname, onNotify = self.notify_received, onClose = self._parent_closed) loop.add_signal_handler(signal.SIGTERM, self._got_sig) loop.add_signal_handler(signal.SIGINT, self._got_sig) proctitle = '[sdnotify-exec]' try: from setproctitle import setproctitle setproctitle(proctitle) except ImportError: pass def info(self, msg): if self.verbose: print("info: " + msg) def _got_sig(self): self.kill_program() def kill_program(self, exitcode = None): if exitcode is not None: self.exitcode = exitcode loop.call_soon(self._really_kill) def _really_kill(self): self.listener.close() loop.stop() def _parent_closed(self, which, ex): if which == self.parent_client: self.proxy_enabled = False self.parent_client = None @asyncio.coroutine def _do_proxy_send(self, name, value): if not (parent_socket and self.proxy_enabled): return if not self.parent_client: self.parent_client = NotifyClient(parent_socket, onClose = self._parent_closed) yield from self.parent_client.run() yield from self.parent_client.send("{0}={1}".format(name, value)) def send_to_proxy(self, name, value): asyncio.ensure_future(self._do_proxy_send(name, value)) def notify_received(self, which, name, value): self.send_to_proxy(name, value) sent_info = False if self.wait_mode: if name == "READY" and value == "1": if self.wait_mode == 'ready': sent_info = True self.info("ready notification received (will exit)") self.kill_program(0) elif name == "ERRNO": sent_info = True self.info("error notification ({0}) received from {1}".format(value, self.proc_args[0])) self.kill_program(int(value)) elif name == "STOPPING" and value == "1": sent_info = True self.info("STOP notification received from {0} (will exit)".format(self.proc_args[0])) self.kill_program() if not sent_info: self.info(self.INFO_MESSAGE.get(name, self.INFO_MESSAGE['default']). format(name, value, ' (ignored but passed on)' if self.proxy_enabled else ' (ignored)')) @asyncio.coroutine def _notify_timeout(self): self.info("waiting {0} seconds for notification".format(self.timeout)) yield from asyncio.sleep(self.timeout) print("ERROR: Timeout exceeded while waiting for notification from '{0}'".format(self.proc_args[0])) self.kill_program(1) @asyncio.coroutine def _run_process(self): self.info('running: {0}'.format(self.proc_args[0])) create = asyncio.create_subprocess_exec(*self.proc_args, start_new_session=bool(self.wait_mode)) proc = yield from create if self.timeout: asyncio.ensure_future(self._notify_timeout()) exitcode = yield from proc.wait() if not self.exitcode: # may have arrived from ERRNO self.exitcode = exitcode @asyncio.coroutine def run(self): try: yield from self.listener.run() except ValueError as ex: print("Error while trying to create socket: " + str(ex)) self.kill_program() else: try: yield from self._run_process() except Exception as ex: print("Error running command: " + str(ex)) self.kill_program() # Command has executed, now determine our exit and proxy disposition if not self.wait_mode: self.info("program {0} exit({1}), terminating since --wait not specified".format(self.proc_args[0], self.exitcode)) self.kill_program()
class SDNotifyExec: exitcode = 0 sockname = None listener = None parent = None timeout = None wait_mode = None verbose = False parent_client = None proxy_enabled = True INFO_MESSAGE = { 'READY': "READY={1}{2}", 'MAINPID': "Process PID (={1}) notification{2}", 'ERRNO': "Process ERROR (={1}) notification{2}", 'STATUS': "Status message = '{1}'{2}", 'default': "{0}={1}{2}", } def __init__(self, options): self.sockname = options['--socket'] if not self.sockname: self.sockname = "/tmp/sdnotify-proxy-{0}.sock".format(os.getpid()) self.proxy_enabled = parent_socket and not options['--noproxy'] if options['--wait-stop']: self.wait_mode = 'stop' elif options['--wait-ready']: self.wait_mode = 'ready' if options['--timeout'] and self.wait_mode: self.timeout = float(options['--timeout']) self.verbose = options['--verbose'] # Modify original environment os.environ['NOTIFY_SOCKET'] = self.sockname # Set up the environment, reparse the options, build the final command Environment.set_parse_parameters('%', '{') env = Environment() env['PID'] = str(os.getpid()) env['SOCKET_ARGS'] = options['--template'] or DEFAULT_TEMPLATE if parent_socket: env['ORIG_NOTIFY_SOCKET'] = parent_socket env = env.expanded() self.proc_args = shlex.split(env.expand(' '.join(maybe_quote(arg) for arg in [options['COMMAND']] + options['ARGS']))) self.listener = NotifyListener(self.sockname, onNotify = self.notify_received, onClose = self._parent_closed) loop.add_signal_handler(signal.SIGTERM, self._got_sig) loop.add_signal_handler(signal.SIGINT, self._got_sig) proctitle = '[sdnotify-exec]' try: from setproctitle import setproctitle setproctitle(proctitle) except ImportError: pass def info(self, msg): if self.verbose: print("info: " + msg) def _got_sig(self): self.kill_program() def kill_program(self, exitcode = None): if exitcode is not None: self.exitcode = exitcode loop.call_soon(self._really_kill) def _really_kill(self): self.listener.close() loop.stop() def _parent_closed(self, which, ex): if which == self.parent_client: self.proxy_enabled = False self.parent_client = None @asyncio.coroutine def _do_proxy_send(self, name, value): if not (parent_socket and self.proxy_enabled): return if not self.parent_client: self.parent_client = NotifyClient(parent_socket, onClose = self._parent_closed) yield from self.parent_client.run() yield from self.parent_client.send("{0}={1}".format(name, value)) def send_to_proxy(self, name, value): asyncio.async(self._do_proxy_send(name, value)) def notify_received(self, which, name, value): self.send_to_proxy(name, value) sent_info = False if self.wait_mode: if name == "READY" and value == "1": if self.wait_mode == 'ready': sent_info = True self.info("ready notification received (will exit)") self.kill_program(0) elif name == "ERRNO": sent_info = True self.info("error notification ({0}) received from {1}".format(value, self.proc_args[0])) self.kill_program(int(value)) elif name == "STOPPING" and value == "1": sent_info = True self.info("STOP notification received from {0} (will exit)".format(self.proc_args[0])) self.kill_program() if not sent_info: self.info(self.INFO_MESSAGE.get(name, self.INFO_MESSAGE['default']). format(name, value, ' (ignored but passed on)' if self.proxy_enabled else ' (ignored)')) @asyncio.coroutine def _notify_timeout(self): self.info("waiting {0} seconds for notification".format(self.timeout)) yield from asyncio.sleep(self.timeout) print("ERROR: Timeout exceeded while waiting for notification from '{0}'".format(self.proc_args[0])) self.kill_program(1) @asyncio.coroutine def _run_process(self): self.info('running: {0}'.format(self.proc_args[0])) create = asyncio.create_subprocess_exec(*self.proc_args, start_new_session=bool(self.wait_mode)) proc = yield from create if self.timeout: asyncio.async(self._notify_timeout()) exitcode = yield from proc.wait() if not self.exitcode: # may have arrived from ERRNO self.exitcode = exitcode @asyncio.coroutine def run(self): try: yield from self.listener.run() except ValueError as ex: print("Error while trying to create socket: " + str(ex)) self.kill_program() else: try: yield from self._run_process() except Exception as ex: print("Error running command: " + str(ex)) self.kill_program() # Command has executed, now determine our exit and proxy disposition if not self.wait_mode: self.info("program {0} exit({1}), terminating since --wait not specified".format(self.proc_args[0], self.exitcode)) self.kill_program()