#!/usr/bin/env python3 """Uruchamia komendę w tle, odczepioną od bieżącej sesji terminala. Powód istnienia: `nohup ... &` wewnątrz zadania go-task nie wystarcza - interpreter zadania kończy się razem z krokiem i zabija potomka. `start_new_session=True` robi to, co `setsid` na Linuksie, a działa też na macOS, gdzie `setsid` nie istnieje. Użycie: daemonize.py --pidfile .runs/llm/mock.pid --log .runs/llm/mock.log -- python3 llm/mock_server.py """ from __future__ import annotations import argparse import os import signal import subprocess import sys from pathlib import Path def already_running(pidfile: Path) -> int | None: if not pidfile.exists(): return None try: pid = int(pidfile.read_text().strip()) os.kill(pid, 0) except (ValueError, ProcessLookupError, PermissionError, OSError): return None return pid def main() -> int: parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument("--pidfile", required=True) parser.add_argument("--log", required=True) parser.add_argument("--stop", action="store_true", help="zatrzymaj proces z pidfile zamiast startować") parser.add_argument("command", nargs=argparse.REMAINDER) args = parser.parse_args() pidfile = Path(args.pidfile) pidfile.parent.mkdir(parents=True, exist_ok=True) if args.stop: pid = already_running(pidfile) if pid is None: print(f"nic nie działa pod {pidfile}") pidfile.unlink(missing_ok=True) return 0 os.kill(pid, signal.SIGTERM) pidfile.unlink(missing_ok=True) print(f"zatrzymano pid {pid}") return 0 command = [arg for arg in args.command if arg != "--"] if not command: parser.error("podaj komendę do uruchomienia po --") running = already_running(pidfile) if running is not None: print(f"już działa (pid {running})") return 0 log = Path(args.log) log.parent.mkdir(parents=True, exist_ok=True) with log.open("ab") as handle: process = subprocess.Popen( command, stdout=handle, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL, start_new_session=True, # odpowiednik setsid, przenośny między Linuksem a macOS ) pidfile.write_text(str(process.pid)) print(f"uruchomiono pid {process.pid} (log: {log})") return 0 if __name__ == "__main__": sys.exit(main())