macOS support (code only, no build yet)

This commit is contained in:
sarah committed 2026-10-14 13:18:20 +02:00
1 parent 1feb6d4336
commit ca04dcd4a7
17 files changed
+572 -126

No files matched your search

+17 -8
View File
@@ -1,8 +1,8 @@
"""Runs a SaraSuite program as a background service where there's no systemd
--user to do it: on Windows (started windowless with pythonw.exe), and on
Linux with another init system (runit on Void, OpenRC, s6, dinit, ...).
Started at login (Windows: a shortcut in the Startup folder; Linux: an XDG
autostart entry) and when SaraSuite or the program's own GUI (e.g. SaraSort
--user to do it: on Windows (started windowless with pythonw.exe), on macOS,
and on Linux with another init system (runit on Void, OpenRC, s6, dinit, ...).
Started at login (Windows: a shortcut in the Startup folder; macOS: a
LaunchAgent; Linux: an XDG autostart entry) and when SaraSuite or the program's own GUI (e.g. SaraSort
for SaraSortd) starts it:
python ServiceRun.py <Name> <Script> [<WorkingDir>]
@@ -12,7 +12,8 @@ seconds after it fails (an exception or a non-zero exit), not after a clean
exit or a stop; its output goes to <Name>.log. Its PID is in <Name>.pid, so
anything can tell whether it runs, and stop it, without admin rights. Both
files are in SaraSuite's Services folder: %LOCALAPPDATA%\\SaraSuite\\Services
on Windows, ~/.local/share/SaraSuite/Services on Linux.
on Windows, ~/Library/Application Support/SaraSuite/Services on macOS,
~/.local/share/SaraSuite/Services on Linux.
Installed next to each program that has a service (Programs/<Name>/Manifest.toml).
"""
@@ -20,6 +21,7 @@ Installed next to each program that has a service (Programs/<Name>/Manifest.toml
import os
import runpy
import signal
import subprocess
import sys
import time
import traceback
@@ -27,6 +29,7 @@ from datetime import datetime
from pathlib import Path
IsWindows = sys.platform == "win32"
IsMac = sys.platform == "darwin"
RestartSeconds = 10 # the unit's RestartSec
MaxLogBytes = 1_000_000 # then the log starts over, the old one kept as <Name>.log.1
Stopping = False
@@ -35,22 +38,28 @@ Stopping = False
def ServicesDir() -> Path:
if IsWindows:
Base = Path(os.environ.get("LOCALAPPDATA") or Path.home() / "AppData" / "Local")
elif IsMac:
Base = Path.home() / "Library" / "Application Support"
else:
Base = Path(os.environ.get("XDG_DATA_HOME") or Path.home() / ".local" / "share")
return Base / "SaraSuite" / "Services"
def _AlreadyRunning(PidFile: Path) -> bool:
"""Linux: whether the PID file's process is another ServiceRun.py still
running (not a reused PID). Windows relies on whoever starts it to check."""
"""Linux and macOS: whether the PID file's process is another
ServiceRun.py still running (not a reused PID). Windows relies on whoever
starts it to check."""
if IsWindows:
return False
try:
Pid = int(PidFile.read_text().strip())
if Pid == os.getpid():
return False
if IsMac: # no /proc
Output = subprocess.run(["ps", "-o", "command=", "-p", str(Pid)], capture_output=True, text=True, timeout=15).stdout
return "ServiceRun.py" in Output
return b"ServiceRun.py" in Path(f"/proc/{Pid}/cmdline").read_bytes()
except (OSError, ValueError):
except (OSError, ValueError, subprocess.SubprocessError):
return False