def create_service_template(args): here = os.path.abspath(".") fname, extra_args = args.fname_or_cmd.split()[0], " ".join( args.fname_or_cmd.split()[1:]) binary, cmd = get_cmd_from_filename(fname) service_name = fname + "_" + here.split("/")[-1] if binary else fname service_name = to_sn(service_name) fname = fname + " " # other binary if binary: fname = "" if args.notify_cmd != "-1": on_failure = "OnFailure={}-onfailure@%i.service".format( args.notify_cmd) else: on_failure = "" timer = run_quiet("systemd-analyze calendar '{}'".format(args.timer)) if bool(timer): service_type = "oneshot" restart = "" part_of = "" else: service_type = "simple" restart = "Restart=always\nRestartSec={delay}".format(delay=args.delay) part_of = "PartOf={service_name}_monitor.service".format( service_name=service_name) service = (""" [Unit] Description={service_name} service (generated by sysdm) After=network-online.target {part_of} {on_failure} [Service] {user_and_group} Type={service_type} {restart} ExecStart={cmd} {fname} {extra_args} WorkingDirectory={here} [Install] WantedBy=multi-user.target """.replace("\n ", "\n").format( service_name=service_name, cmd=cmd, fname=fname, extra_args=extra_args, here=here, restart=restart, part_of=part_of, on_failure=on_failure, service_type=service_type, user_and_group=user_and_group_if_sudo(args), ).strip()) return service_name, service
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_service_template(fname_or_cmd, notifier, timer, delay, root, killaftertimeout, restart, workdir: str = "", env_vars=[]): here = workdir or os.path.abspath(".") fname, extra_args = fname_or_cmd.split()[0], " ".join( fname_or_cmd.split()[1:]) binary, cmd = get_cmd_from_filename(fname) service_name = get_service_name(fname_or_cmd) + "_" + here.split( "/")[-1] if binary else fname service_name = to_sn(service_name) fname = fname + " " start_info = "" # other binary if binary: fname = "" if notifier is not None: on_failure = "OnFailure={}-onfailure@%i.service".format(notifier) else: on_failure = "" if timer is not None: timer = run_quiet("systemd-analyze calendar '{}'".format(timer)) if bool(timer): service_type = "oneshot" restart = "" part_of = "" else: service_type = "simple" start_info = "StartLimitBurst=2\nStartLimitIntervalSec=15s" if restart else "" restart = "Restart=always\nRestartSec={delay}".format( delay=delay) if restart else "" part_of = "PartOf={service_name}_monitor.service".format( service_name=service_name) user_and_group = user_and_group_if_sudo(root) if timer: install = "" else: wanted_by = "multi-user.target" if user_and_group.strip( ) else "default.target" wanted_by += " " + service_name + "_monitor.service" install = "[Install]\nWantedBy={wanted_by}".format(wanted_by=wanted_by) env_var_section = "\n".join([ f'Environment={x if "=" in x else x + "=" + os.environ[x]}' for x in env_vars ]) service = (""" [Unit] Description={service_name} service (generated by sysdm) After=network-online.target {part_of} {on_failure} {start_info} [Service] {user_and_group} Type={service_type} {restart} TimeoutStopSec={killaftertimeout} ExecStart={cmd} {fname} {extra_args} WorkingDirectory={here} Environment=PYTHONUNBUFFERED=1 {env_var_section} {install} """.replace("\n ", "\n").format(**locals()).strip()) return service_name, service
def create_service_template(fname_or_cmd, notify_cmd, timer, delay, root, killaftertimeout): here = os.path.abspath(".") fname, extra_args = fname_or_cmd.split()[0], " ".join( fname_or_cmd.split()[1:]) binary, cmd = get_cmd_from_filename(fname) service_name = fname + "_" + here.split("/")[-1] if binary else fname service_name = to_sn(service_name) fname = fname + " " # other binary if binary: fname = "" if notify_cmd != "-1": on_failure = "OnFailure={}-onfailure@%i.service".format(notify_cmd) else: on_failure = "" if timer is not None: timer = run_quiet("systemd-analyze calendar '{}'".format(timer)) if bool(timer): service_type = "oneshot" restart = "" part_of = "" else: service_type = "simple" restart = "Restart=always\nRestartSec={delay}".format(delay=delay) part_of = "PartOf={service_name}_monitor.service".format( service_name=service_name) user_and_group = user_and_group_if_sudo(root) if timer: install = "" else: wanted_by = "multi-user.target" if user_and_group.strip( ) else "default.target" wanted_by += " " + service_name + "_monitor.service" install = "[Install]\nWantedBy={wanted_by}".format(wanted_by=wanted_by) service = (""" [Unit] Description={service_name} service (generated by sysdm) After=network-online.target {part_of} {on_failure} [Service] {user_and_group} Type={service_type} {restart} TimeoutStopSec={killaftertimeout} ExecStart={cmd} {fname} {extra_args} WorkingDirectory={here} {install} """.replace("\n ", "\n").format( service_name=service_name, cmd=cmd, fname=fname, extra_args=extra_args, here=here, restart=restart, part_of=part_of, on_failure=on_failure, service_type=service_type, user_and_group=user_and_group, install=install, killaftertimeout=killaftertimeout, ).strip()) return service_name, service
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)