85 lines
2.5 KiB
Python
85 lines
2.5 KiB
Python
import subprocess
|
|
from typing import Callable, Iterator, Sequence
|
|
|
|
from l4d2host.process import CommandResult, run_command
|
|
|
|
|
|
SYSTEMCTL_HELPER = "/usr/local/libexec/left4me/left4me-systemctl"
|
|
JOURNALCTL_HELPER = "/usr/local/libexec/left4me/left4me-journalctl"
|
|
|
|
|
|
def systemctl_command(action: str, name: str) -> list[str]:
|
|
return ["sudo", "-n", SYSTEMCTL_HELPER, action, name]
|
|
|
|
|
|
def journalctl_command(name: str, lines: int = 200, follow: bool = True) -> list[str]:
|
|
follow_arg = "--follow" if follow else "--no-follow"
|
|
return ["sudo", "-n", JOURNALCTL_HELPER, name, "--lines", str(lines), follow_arg]
|
|
|
|
|
|
def start_service(
|
|
name: str,
|
|
*,
|
|
on_stdout: Callable[[str], None] | None = None,
|
|
on_stderr: Callable[[str], None] | None = None,
|
|
passthrough: bool = False,
|
|
should_cancel: Callable[[], bool] | None = None,
|
|
) -> CommandResult:
|
|
return run_command(
|
|
systemctl_command("start", name),
|
|
on_stdout=on_stdout,
|
|
on_stderr=on_stderr,
|
|
passthrough=passthrough,
|
|
should_cancel=should_cancel,
|
|
)
|
|
|
|
|
|
def stop_service(
|
|
name: str,
|
|
*,
|
|
on_stdout: Callable[[str], None] | None = None,
|
|
on_stderr: Callable[[str], None] | None = None,
|
|
passthrough: bool = False,
|
|
should_cancel: Callable[[], bool] | None = None,
|
|
) -> CommandResult:
|
|
return run_command(
|
|
systemctl_command("stop", name),
|
|
on_stdout=on_stdout,
|
|
on_stderr=on_stderr,
|
|
passthrough=passthrough,
|
|
should_cancel=should_cancel,
|
|
)
|
|
|
|
|
|
def show_service(name: str) -> CommandResult:
|
|
return run_command(systemctl_command("show", name))
|
|
|
|
|
|
def stream_command(cmd: Sequence[str]) -> Iterator[str]:
|
|
command = list(cmd)
|
|
proc = subprocess.Popen(
|
|
command,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
text=True,
|
|
bufsize=1,
|
|
)
|
|
try:
|
|
if proc.stdout is None:
|
|
return
|
|
for raw in iter(proc.stdout.readline, ""):
|
|
yield raw.rstrip("\n")
|
|
returncode = proc.wait()
|
|
if returncode is None:
|
|
returncode = proc.poll()
|
|
stderr = proc.stderr.read() if proc.stderr is not None else ""
|
|
if returncode:
|
|
raise subprocess.CalledProcessError(returncode=returncode, cmd=command, stderr=stderr)
|
|
finally:
|
|
if proc.poll() is None:
|
|
proc.terminate()
|
|
proc.wait(timeout=2)
|
|
|
|
|
|
def stream_journal(name: str, *, lines: int = 200, follow: bool = True) -> Iterator[str]:
|
|
return stream_command(journalctl_command(name, lines=lines, follow=follow))
|