def test_uid_and_gid_without_permission(): nobody = getpwnam('nobody') daemon = Daemon(worker=lambda: None, uid=nobody.pw_uid, gid=nobody.pw_gid) with pytest.raises(DaemonError) as excinfo: daemon.do_action('start') assert ('Unable to setuid or setgid ' '([Errno 1] Operation not permitted') in str(excinfo.value)
def test_uid_and_gid_without_permission(): nobody = getpwnam('nobody') daemon = Daemon(worker=lambda: None, uid=nobody.pw_uid, gid=nobody.pw_gid) with pytest.raises(DaemonError) as exc_info: daemon.do_action('start') assert ('Unable to setuid or setgid ' '([Errno 1] Operation not permitted') in str(exc_info.value)
def test_self_reload(pyscript): script = pyscript(""" import os from daemonocle import Daemon daemon = Daemon(name='foo', pid_file='foo.pid', detach=False) def worker(): print('here is my pid: {}'.format(os.getpid())) if not os.environ.get('DAEMONOCLE_RELOAD'): daemon.reload() daemon.worker = worker daemon.do_action('start') """) result = script.run() assert result.returncode == 0 match = re.match(( br'^Starting foo \.\.\. OK\n' br'here is my pid: (\d+)\n' br'Reloading foo \.\.\. OK\n' br'here is my pid: (\d+)\n' br'All children are gone\. Parent is exiting\.\.\.\n$'), result.stdout) assert match assert match.group(1) != match.group(2) assert result.stderr == b'' daemon = Daemon() with pytest.raises(DaemonError): # Don't allow calling reload like this daemon.reload()
def test_is_detach_necessary_ppid1(monkeypatch): def mock_os_getppid_1(): return 1 monkeypatch.setattr(os, 'getppid', mock_os_getppid_1) # FIXME: This isn't really how I would prefer to test this, # but this is a difficult thing to test. assert Daemon._is_detach_necessary() == Daemon._is_in_container()
def daemonize( action: str, name: str, command: str | None = None, log_file: str | None = None, ): # pragma: no cover """Executes a command as a daemon. Runs a COMMAND as a detached daemon assigning it a given NAME. The daemon can be stopped by calling daemonize stop NAME or its status checked with daemonize status NAME. If --log-file is used, a rotating log file with the STDOUT and STDERR of the worker subprocess will be generated. If STATUS is "debug", runs the worker subprocess without detaching. """ def worker(): if command is None: return if log_file: path = os.path.realpath(os.path.expanduser(os.path.expandvars(log_file))) if os.path.exists(path): date = datetime.now() suffix = date.strftime(".%Y-%m-%d_%H:%M:%S") move(path, path + suffix) os.makedirs(os.path.dirname(path), exist_ok=True) f = open(log_file, "w") sys.stdout = f sys.stderr = f subprocess.run( " ".join(command), shell=True, capture_output=False, cwd=os.getcwd(), ) pid_file = f"/var/tmp/{name}.pid" if action == "debug": log_file = None worker() else: daemon = Daemon( worker=worker, pid_file=pid_file, work_dir=os.getcwd(), ) daemon.do_action(action)
def test_default_actions(): daemon = Daemon() assert daemon.list_actions() == ['start', 'stop', 'restart', 'status'] assert daemon.get_action('start') == daemon.start assert daemon.get_action('stop') == daemon.stop assert daemon.get_action('restart') == daemon.restart assert daemon.get_action('status') == daemon.status with pytest.raises(DaemonError): daemon.get_action('banana')
def test_is_detach_necessary_pid1(monkeypatch): def mock_os_getpid_1(): return 1 monkeypatch.setattr(os, 'getpid', mock_os_getpid_1) assert not Daemon._is_detach_necessary()
def start(ctx, debug): click.secho('PKNS Server Address: ', nl=False) click.secho( f"{ctx.obj['WORKER'].ip_address}:{ctx.obj['WORKER'].port}", fg='green' ) daemon = Daemon('PKNS Server', worker=ctx.obj['WORKER'].serve_endless, detach=(not debug), pidfile=os.path.abspath( os.environ['HOME']+"/.pkns/PKNS.pid"), work_dir=os.path.abspath(os.environ['HOME']), stdout_file=os.path.abspath( os.environ['HOME'] + "/.pkns/PKNS.log"), stderr_file=os.path.abspath( os.environ['HOME'] + "/.pkns/PKNS_error.log"), uid=os.getuid(), gid=os.getgid()) daemon.do_action('start')
def test_deprecated_shutdown_callback_converted_to_hook(): def shutdown_callback(message, returncode): pass daemon = Daemon(name='foo', worker=lambda: None, shutdown_callback=shutdown_callback) assert daemon.hooks['shutdown'] is shutdown_callback
def get_command(self, ctx, name): """Get a callable command object.""" if name not in Daemon.list_actions(): return None # Assign the daemon worker as the partial of the group callback # with the parameters received (these are the parameters of the # group, not of the command). assert self.group_cb self.daemon.worker = partial(self.group_cb, **ctx.params) return self.commands[name]
def __init__( self, *args, callback: Union[Callable[[Any], Any], None] = None, log_file: Optional[str] = None, **kwargs, ): if "prog" not in kwargs: raise RuntimeError("Daemon prog not defined.") prog = kwargs.pop("prog") # Here kwargs are the parameters in the @click.group decorator. # pidfile is deprecated in Daemon and instead we should use pid_file, but some # code already use pidfile so let's support both but not at the same time. if "pidfile" in kwargs and "pid_file" in kwargs: raise RuntimeError("pid_file and pidfile are mutually exclusive.") base_pidfile = f"/var/tmp/{prog}.pid" if "pid_file" in kwargs: pid_file = kwargs.pop("pid_file", base_pidfile) elif "pidfile" in kwargs: pid_file = kwargs.pop("pidfile", base_pidfile) else: pid_file = base_pidfile daemon_params = {} signature = inspect.signature(Daemon).parameters for param in kwargs.copy(): if param in signature and param != "name": daemon_params.update({param: kwargs.pop(param)}) self.log_file = log_file self.daemon = Daemon(pid_file=pid_file, **daemon_params) # Callback is the function that @click.group decorates. What we # do is store it because it will become the worker for the daemon, # but then set it to none because we don't want the code in the group # function to be executed outside the daemon. self.group_cb = callback callback = None self.ignore_unknown_options = True super().__init__(*args, callback=callback, **kwargs) self.add_command(start) self.add_command(stop) self.add_command(restart) self.add_command(status)
def test_self_reload(pyscript): script = pyscript( """ import os from daemonocle import Daemon daemon = Daemon(prog='foo', pidfile='foo.pid', detach=False) def worker(): print('here is my pid: {}'.format(os.getpid())) if not os.environ.get('DAEMONOCLE_RELOAD'): daemon.reload() daemon.worker = worker daemon.do_action('start') """ ) result = script.run() assert result.returncode == 0 match = re.match( ( br"^Starting foo \.\.\. OK\n" br"here is my pid: (\d+)\n" br"Reloading foo \.\.\. OK\n" br"here is my pid: (\d+)\n" br"All children are gone\. Parent is exiting\.\.\.\n$" ), result.stdout, ) assert match assert match.group(1) != match.group(2) assert result.stderr == b"" daemon = Daemon() with pytest.raises(DaemonError): # Don't allow calling reload like this daemon.reload()
def test_bad_shutdown_hook(): with pytest.raises(DaemonError) as exc_info: Daemon(name='foo', worker=lambda: None, hooks={'shutdown': 1}) assert str( exc_info.value) == ('Callback for hook "shutdown" is not callable')
def test_no_worker(): daemon = Daemon() with pytest.raises(DaemonError): daemon.do_action("start")
def test_chrootdir_without_permission(): daemon = Daemon(worker=lambda: None, chroot_dir=os.getcwd()) with pytest.raises(DaemonError) as exc_info: daemon.do_action('start') assert ('Unable to change root directory ' '([Errno 1] Operation not permitted') in str(exc_info.value)
def get_app_daemon(pid_file): # FIXME: figure out the log prefix (opencanaryd) return Daemon(worker=run_twisted_app, pidfile=pid_file)
def stop(force): daemon = Daemon('PKNS Server', pidfile=os.path.abspath( os.environ['HOME']+"/.pkns/PKNS.pid")) daemon.stop(force=force)
def status(json): daemon = Daemon('PKNS Server', pidfile=os.path.abspath( os.environ['HOME']+"/.pkns/PKNS.pid")) daemon.status(json=json)
def test_chrootdir_without_permission(): daemon = Daemon(worker=lambda: None, chrootdir=os.getcwd()) with pytest.raises(DaemonError) as excinfo: daemon.do_action('start') assert ('Unable to change root directory ' '([Errno 1] Operation not permitted') in str(excinfo.value)
def test_no_args_or_worker(): daemon = Daemon() assert daemon.name == posixpath.basename(sys.argv[0]) with pytest.raises(DaemonError): daemon.do_action('start')
def test_no_worker(): daemon = Daemon() with pytest.raises(DaemonError): daemon.do_action('start')