def ls(self): """ Interactively show units and allow viewing them """ while True: units = ls(self.systempath) if units: unit = choose_unit(self.systempath, units) if unit is None: sys.exit() monitor(unit, self.systempath) else: print( "sysdm knows of no units. Why don't you make one? `sysdm create file_i_want_as_service.py`" ) break
def view(self, unit: str): """ Monitor a unit :param unit: File/cmd/unit to observe. Dots will be replaced with _ automatically """ service_name = to_sn(unit) if not os.path.exists(self.systempath + "/" + service_name + ".service"): print( "Service file does not exist. You can start by running:\n\n sysdm create {}\n\nto create a service or run:\n\n sysdm ls\n\nto see the services already created by sysdm." .format(unit)) sys.exit(1) monitor(service_name, self.systempath)
def create( self, fname_or_cmd: str, restart=True, timer: str = None, killaftertimeout=90, delay=0.2, extensions=[], exclude_patterns=[], ls=True, root=False, notify_cmd="-1", notify_status_cmd="systemctl --user status -l -n 1000 %i", notify_cmd_args='-s "%i failed on %H"', ): """ Create a systemd unit file :param fname_or_cmd: File/cmd to run :param restart: Whether to prevent auto restart on error :param timer: Used to set timer. Checked to be valid. E.g. *-*-* 03:00:00 for daily at 3 am. :param killaftertimeout: Time before sending kill signal if unresponsive when try to restart :param delay: Set a delay in the unit file before attempting restart :param extensions: Patterns of files to watch (by default inferred) :param exclude_patterns: Patterns of files to ignore (by default inferred) :param ls: Only create but do not list :param root: Only possible when using sudo :param notify_cmd: Binary command that will notify. -1 will add no notifier. Possible: e.g. yagmail :param notify_status_cmd: Command that echoes output to the notifier on failure :param notify_cmd_args: Arguments passed to notify command. """ print("Creating systemd unit...") service_name, service = create_service_template( fname_or_cmd, notify_cmd, timer, delay, root, killaftertimeout) try: with open( os.path.join(self.systempath, service_name) + ".service", "w") as f: print(service) f.write(service) except PermissionError: print("Need sudo to create systemd unit service file.") sys.exit(1) create_mail_on_failure_service(self.systempath, notify_cmd, notify_status_cmd, notify_status_cmd, root) _ = systemctl("daemon-reload") create_timer = create_timer_service(self.systempath, service_name, timer) if create_timer: _ = systemctl("enable {}.timer".format(service_name)) _ = systemctl("start {}.timer".format(service_name)) else: _ = systemctl("enable {}".format(service_name)) monitor_str = create_service_monitor_template( service_name, fname_or_cmd, extensions, exclude_patterns, root) with open( os.path.join(self.systempath, service_name) + "_monitor.service", "w") as f: f.write(monitor_str) _ = systemctl("start --no-block {}".format(service_name)) _ = systemctl("enable {}_monitor".format(service_name)) _ = systemctl("start {}_monitor".format(service_name)) print(linger()) print("Done") if ls: monitor(service_name, self.systempath)
def create(self, fname_or_cmd: str, restart=True, timer: str = None, killaftertimeout=90, delay=0.2, extensions=[], exclude_patterns=[], ls=True, root=False, n_notifier: Optional[str] = None, n_user: Optional[str] = None, n_to: Optional[Union[str, int]] = None, n_pw: Optional[str] = None, n_msg: Optional[str] = "%i failed on %H", n_status_cmd="journalctl {user} --no-pager -n 1000", workdir: str = "", env_vars: list[str] = []): """ Create a systemd unit file :param fname_or_cmd: File/cmd to run :param restart: Whether to prevent auto restart on error :param timer: Used to set timer. Checked to be valid. E.g. *-*-* 03:00:00 for daily at 3 am. :param killaftertimeout: Time before sending kill signal if unresponsive when try to restart :param delay: Set a delay in the unit file before attempting restart :param extensions: Patterns of files to watch (by default inferred) :param exclude_patterns: Patterns of files to ignore (by default inferred) :param ls: Only create but do not list :param root: Only possible when using sudo :param notify_cmd: Binary command that will notify. -1 will add no notifier. Possible: e.g. yagmail :param notify_status_cmd: Command that echoes output to the notifier on failure :param notify_cmd_args: Arguments passed to notify command. :param workdir: Location from which command is run :param env_vars: can be passed like FOO=1 or FOO (which would inherit FOO current shell) """ if n_notifier is not None: install_notifier_dependencies(n_notifier) print("Creating systemd unit...") service_name, service = create_service_template( fname_or_cmd, n_notifier, timer, delay, root, killaftertimeout, restart, workdir, env_vars) user = "******" if IS_SUDO else "--user-unit %i" n_status_cmd = n_status_cmd.format(user=user) try: with open( os.path.join(self.systempath, service_name) + ".service", "w") as f: print(service) f.write(service) except PermissionError: print("Need sudo to create systemd unit service file.") sys.exit(1) create_notification_on_failure_service(self.systempath, service_name, n_notifier, n_user, n_to, n_pw, n_msg, n_status_cmd, root) _ = systemctl("daemon-reload") create_timer = create_timer_service(self.systempath, service_name, timer) if create_timer: _ = systemctl("enable {}.timer".format(service_name)) _ = systemctl("start {}.timer".format(service_name)) else: _ = systemctl("enable {}".format(service_name)) monitor_str = create_service_monitor_template( service_name, fname_or_cmd, extensions, exclude_patterns, root) with open( os.path.join(self.systempath, service_name) + "_monitor.service", "w") as f: f.write(monitor_str) _ = systemctl("start --no-block {}".format(service_name)) _ = systemctl("enable {}_monitor".format(service_name)) _ = systemctl("start {}_monitor".format(service_name)) print(linger()) print("Done") if ls: monitor(service_name, self.systempath)
def _main(): parser, args = get_argparser() try: if args.systempath is None: args.systempath = "/etc/systemd/system" if IS_SUDO else "~/.config/systemd/user" args.systempath = os.path.expanduser(args.systempath) args.systempath = args.systempath.rstrip("/") try: os.makedirs(args.systempath) except FileExistsError: pass except AttributeError: # most commands have it, but not all pass if args.command == "create": print("Creating systemd unit...") service_name = install(args) print("Done") if not args.nolist: monitor(service_name, args.systempath) elif args.command == "view": service_name = to_sn(args.unit) if not os.path.exists(args.systempath + "/" + service_name + ".service"): print( "Service file does not exist. You can start by running:\n\n sysdm create {}\n\nto create a service or run:\n\n sysdm ls\n\nto see the services already created by sysdm." .format(args.unit)) sys.exit(1) monitor(service_name, args.systempath) elif args.command == "run": if args.unit is None: units = ls(args) unit = choose_unit(args.systempath, units) if unit is None: sys.exit() else: unit = args.unit with open(args.systempath + "/" + unit + ".service") as f: for line in f: line = line.strip() if line.startswith("ExecStart="): cmd = line.split("ExecStart=")[1] if args.debug: cmd = cmd.replace("python3 -u", "python3 -u -m pdb") cmd = cmd.replace("python -u", "python -u -m pdb") elif line.startswith("WorkingDirectory="): cwd = line.split("WorkingDirectory=")[1] os.system("cd {!r} && {}".format(cwd, cmd)) elif args.command == "show_unit": show(args) elif args.command == "reload": systemctl("daemon-reload") elif args.command == "watch": watch(args) elif args.command == "delete": if args.unit is None: units = ls(args) unit = choose_unit(args.systempath, units) if unit is None: sys.exit() inp = input( "Are you sure you want to delete '{}'? [y/N]: ".format(unit)) if inp.lower().strip() != "y": print("Aborting") return else: unit = args.unit delete(unit, args.systempath) elif args.command == "edit": if args.unit is None: units = ls(args) unit = choose_unit(args.systempath, units) if unit is None: sys.exit() else: unit = args.unit unit = unit if unit.endswith(".service") else unit + ".service" os.system("$EDITOR {}/{}".format(args.systempath, unit)) elif args.command == "ls": while True: units = ls(args) if units: unit = choose_unit(args.systempath, units) if unit is None: sys.exit() monitor(unit, args.systempath) else: print( "sysdm knows of no units. Why don't you make one? `sysdm create file_i_want_as_service.py`" ) break else: parser.print_help(sys.stderr) sys.exit(1)