def create_notification_on_failure_service(systempath, service_name, n_notifier, n_user, n_to, n_pw, n_msg, n_status_cmd, root): if n_notifier is None: return notify = get_sysdm_executable() + " notify" user = get_output("echo $USER") home = get_output("echo ~" + user) host = get_output("echo $HOSTNAME") n_msg = n_msg.format(home=home, host=host) exec_start = f"""/bin/bash -c "{n_status_cmd} | {notify} {n_notifier} -m {n_msg!r}""" if n_user is not None: exec_start += f" -u {n_user!r}" if n_to is not None: exec_start += f" -t {n_to!r}" if n_pw is not None: exec_start += f" -p {n_pw!r}" exec_start += '"' print("Testing notifier ({})".format(n_notifier)) test_cmd = exec_start.replace("%i", service_name).replace("%H", host).replace( "failed on", "test succeeded on") print(test_cmd) outp = get_output(test_cmd) if "Fault" in outp or "invalid" in outp: print(outp) sys.exit(1) print("") print("Test succeeded.") service = """ [Unit] Description={notify_cmd} OnFailure for %i [Service] {user_and_group} Type=oneshot ExecStart={exec_start} """.replace("\n ", "\n").format( exec_start=exec_start, notify_cmd=n_notifier, user_and_group=user_and_group_if_sudo(root), ) with open( os.path.join(systempath, "{}[email protected]".format(n_notifier)), "w") as f: f.write(service)
def create_mail_on_failure_service(systempath, notify_cmd, notify_cmd_args, notify_status_cmd, root): if notify_cmd == "-1": return notifier = get_output("which " + notify_cmd) user = get_output("echo $USER") home = get_output("echo ~" + user) host = get_output("echo $HOSTNAME") notify_cmd_args = notify_cmd_args.format(home=home, host=host) exec_start = """/bin/bash -c '{notify_status_cmd} | {notifier} {notify_cmd_args}' """.format( notify_status_cmd=notify_status_cmd, notifier=notifier, notify_cmd_args=notify_cmd_args) print("Testing notifier ({})".format(notify_cmd)) test_args = (notify_cmd_args.replace("%i", notify_cmd).replace( "failed", "test succeeded").replace("%H", host).format(home=home, host=host)) test_cmd = """/bin/bash -c '{notify_status_cmd} | {notifier} {notify_cmd_args}' """.format( notify_status_cmd=notify_status_cmd.replace("%i", ""), notifier=notifier, notify_cmd_args=test_args, ) print(get_output(test_cmd)) print("") print("Test succeeded.") service = """ [Unit] Description={notify_cmd} OnFailure for %i [Service] {user_and_group} Type=oneshot ExecStart={exec_start} """.replace("\n ", "\n").format( exec_start=exec_start, notify_cmd=notify_cmd, user_and_group=user_and_group_if_sudo(root), ) with open( os.path.join(systempath, "{}[email protected]".format(notify_cmd)), "w") as f: f.write(service)
def choose_unit(systempath, units): options = [] ss = get_output("ss -l -p -n") ps_aux = get_output("ps ax -o pid,%cpu,%mem,ppid,args -ww") for unit in units: running = "✓" if is_unit_running(unit) or is_unit_running( unit + ".timer") else "✗" enabled = "✓" if is_unit_enabled(unit) or is_unit_enabled( unit + ".timer") else "✗" ps = read_ps_aux_by_unit(systempath, unit, ps_aux) if ps is None: port = "" else: pid, *_ = ps port = get_port_from_ps_and_ss(pid, ss) options.append((unit, running, enabled, port)) pad = "{}| {} | {} | {}" offset = max([len(x[0]) for x in options]) + 3 formatted_options = [ pad.format(x.ljust(offset), r, e, p) for x, r, e, p in options ] quit = "-- Quit --" formatted_options.append(" ") formatted_options.append(quit) title = "These are known units:\n\n{}| Active | On boot | Port".format( " " * (offset + 2)) default_index = 0 while True: p = Picker(formatted_options, title, default_index=default_index) p.register_custom_handler(ord('q'), lambda _: sys.exit(0)) chosen, index = p.start() if chosen == quit: return None elif chosen == " ": default_index = index continue else: break return units[index]
def user_and_group_if_sudo(args): global USER_AND_GROUP if USER_AND_GROUP is not None: return USER_AND_GROUP else: if IS_SUDO: if args.root: user = "******" user_group = get_output( """getent group | grep :0: | awk -F ":" '{ print $1}'""" ).split("\n")[0] else: user = get_output("echo $SUDO_USER") user_group = get_output( """getent group | grep $SUDO_GID: | awk -F ":" '{ print $1}'""" ).split("\n")[0] output = "User={user}\nGroup={user_group}".format( user=user, user_group=user_group) else: output = "" USER_AND_GROUP = output return output
def get_cmd_from_filename(fname): cmd = None binary = False if fname.endswith(".py"): cmd = (get_output("which python3") or get_output("which python")) + " -u" elif fname.endswith(".sh"): cmd = get_output("which bash") elif fname.endswith(".js"): cmd = get_output("which node") else: if "." in fname.split("/")[-1]: tmpl = "WARNING: File extension of file '{}' not supported. Treating as executable." print(tmpl.format(fname)) if os.path.isfile(fname): cmd = os.path.abspath(fname) else: cmd = get_output("which " + fname) if not cmd: print("Do not understand how to run '{}'".format(fname)) sys.exit(1) binary = True return binary, cmd.strip()
def monitor(unit, systempath): t = Terminal() print(t.enter_fullscreen()) mapping = [ "[R] Restart service ", "[S] Stop service ", "[T] Enable on startup [b] Back ", "[g] Grep (filter) a pattern [q] Quit view ", ] OFFSET = 12 resized = [True] signal.signal(signal.SIGWINCH, lambda *args: resized.append(True)) logo = "[sysdm]" # seen = set() Y_BANNER_OFFSET = len(mapping) + 1 + 2 # mapping, banner, in between lines grep = "" x_banner_offset = 0 left_offset = 0 log_offset = 0 timer = None with t.hidden_cursor(): try: while True: print(t.move(0, 0)) y, x = t.get_location() if resized: print(t.clear()) resized = [] timed = is_unit_running(unit + ".timer") is_running = is_unit_running(unit) or timed is_enabled = is_unit_enabled(unit) if timed: timer_text = "" while not timer_text: status = systemctl("list-timers " + unit + ".timer") timer_text = status.split("\n")[1][4 : status.index("LEFT") - 2] status = "Next: " + t.green(timer_text) timer = datetime.strptime(timer_text[:19], "%Y-%m-%d %H:%M:%S") else: status = "Active: " + (t.green("✓") if is_running else t.red("✗")) with t.location(OFFSET, 0): enabled = t.green("✓") if is_enabled else t.red("✗") line = "Unit: {} {} On Startup: {}".format(t.bold(unit), status, enabled) x_banner_offset = len(line) print(line) with t.location(t.width - len(logo), 0): print(t.bold(logo)) with t.location(0, 1): print(t.center("-" * (t.width - 16))) for num, line in enumerate(mapping): with t.location(0, num + 2): if not is_running: line = line.replace("Stop service ", "Start service") if is_enabled: line = line.replace("Enable on startup", "Disable on startup") line = line.replace("[", "[" + t.green).replace("]", t.normal + "]") print(" " * OFFSET + (line + " " * t.width)[: t.width + 3]) with t.location(0, 6): print(t.center("-" * (t.width - 16))) # if timer just expired, refresh to get the new date if timer is not None and datetime.now() > timer: resized = [True] if t.width - x_banner_offset > 50: res = "| {} |".format(time.asctime()) if is_running: ps_aux = get_output("ps ax -o pid,%cpu,%mem,ppid,args -ww") ps_info = read_ps_aux_by_unit(systempath, unit, ps_aux) if ps_info is not None: res = "| {} | PID={} | CPU {:>4}% | MEM {:>4}%".format( time.asctime(), *ps_info ) with t.location(x_banner_offset, 0): print(res) with t.location(0, Y_BANNER_OFFSET): n = t.height - Y_BANNER_OFFSET - 1 w = t.width g = "--grep " + grep if grep else "" u_sep = "-u" if IS_SUDO else "--user-unit" output = journalctl( "journalctl {u_sep} {u} {u_sep} {u}_monitor {u_sep} {u}.timer -n {n} --no-pager {g}".format( u=unit, n=n + log_offset + 100, g=g, u_sep=u_sep ) ) outp = [] for line in output.split("\n"): # replace e.g. python[pidnum123]: real output line = re.sub("(?<=:\d\d ).+?\[\d+\]: ", "| ", line) if grep: rmatch = re.search(grep, line) if rmatch is not None: s = rmatch.start() e = rmatch.end() line = line[:s] + t.red(line[s:e]) + line[e:] l = (line + " " * 200)[left_offset : w + left_offset - 5] if "Stopped" in l: l = t.bold(l) if "Started" in l: if timed: ln = len(l) white = " " * 200 l = (l.split("|")[0] + "| Succesfully ran on timer" + white)[:ln] l = t.green(l) if "WARNING: " in l: l = t.yellow(l) if "ERROR: " in l: l = t.red(l) if "Failed to start " in l: l = t.red(l) if "Triggering OnFailure= " in l: l = t.yellow(l) outp.append(l) if log_offset: print("\n".join(outp[-n - log_offset + 1 : -log_offset])) else: print("\n".join(outp[-n - log_offset + 1 :])) with t.cbreak(): inp = t.inkey(0.3) if inp == "q": if grep: grep = "" else: print(t.clear()) sys.exit(0) elif inp == "S": print(t.clear()) if is_running: print("Stopping unit {unit}".format(unit=unit)) systemctl("stop {unit}".format(unit=unit)) systemctl("stop {unit}.timer".format(unit=unit)) else: print("Starting unit {unit}".format(unit=unit)) systemctl("start --no-block {unit}".format(unit=unit)) systemctl("start {unit}.timer".format(unit=unit)) resized = [True] elif inp == "R": print(t.clear()) print("Restarting unit {unit}".format(unit=unit)) systemctl("restart {unit}".format(unit=unit)) resized = [True] elif inp == "b" or inp.name == "KEY_DELETE": return elif inp == "T": print(t.clear()) if is_enabled: print("Disabling unit {unit} on startup".format(unit=unit)) systemctl("disable {unit}".format(unit=unit)) systemctl("disable {unit}.timer".format(unit=unit)) else: print("Enabling unit {unit} on startup".format(unit=unit)) systemctl("enable {unit}".format(unit=unit)) systemctl("enable {unit}.timer".format(unit=unit)) resized = [True] elif inp == " ": print(t.clear()) resized = [True] elif inp == "g": print(t.clear()) if grep: grep = "" else: grep = input( "Grep pattern to search for (leave blank for cancel): " ).strip() resized = [True] elif inp.name == "KEY_RIGHT": print(t.erase()) left_offset = min(left_offset + 5, t.width) elif inp.name == "KEY_LEFT": print(t.erase()) left_offset = max(0, left_offset - 5) elif inp.name == "KEY_UP": print(t.erase()) log_offset = min(log_offset + 5, t.height) elif inp.name == "KEY_DOWN": print(t.erase()) log_offset = max(0, log_offset - 5) else: print(t.erase()) except KeyboardInterrupt: pass print(t.clear())
def home(self): return get_output("echo ~" + self.user)
def user(self): return get_output("echo $USER")
def get_version_tuple(py): out = get_output(f"{py} --version") if out.startswith("Python"): return tuple(int(x) for x in out.split()[-1].split(".")) return (0, 0, 0)
def contains_python(fname): fname_cmd = get_output("which " + fname) if os.path.exists(fname_cmd): with open(fname_cmd) as f: return "python" in f.read() return False