macOS support

This commit is contained in:
sarah committed 2026-10-14 13:18:18 +02:00
1 parent 5613f4f8fe
commit 1bd37db1ba
8 files changed
+113 -29

No files matched your search

+28 -5
View File
@@ -2,19 +2,21 @@
and its logs. SaraSortd's own log files are read from its LogDir.
Linux with systemd: its systemd user unit (systemctl / journalctl).
Windows, and Linux without systemd (runit, OpenRC, s6, ...): SaraSuite runs
Windows, macOS, and Linux without systemd (runit, OpenRC, s6, ...): SaraSuite runs
it itself (its Services.py), and this does the same:
- it runs as `python ServiceRun.py SaraSortd SaraSortd.py <ConfigDir>`
(pythonw.exe on Windows); ServiceRun.py restarts it if it fails, keeps its
output in SaraSuite's Services folder as SaraSortd.log (the "Service log"
here) and records its PID in SaraSortd.pid (that's how "running" is told);
- autostart is a shortcut to that command in the Startup folder (Windows)
or an XDG autostart entry, ~/.config/autostart/SaraSortd.desktop (Linux).
- autostart is a shortcut to that command in the Startup folder (Windows),
a LaunchAgent, ~/Library/LaunchAgents/srhtnin.SaraSortd.plist (macOS), or
an XDG autostart entry, ~/.config/autostart/SaraSortd.desktop (Linux).
Unit is the systemd unit name ("SaraSortd.service"); run by SaraSuite, the
service is called the same without ".service".
"""
import os
import plistlib
import re
import signal
import subprocess
@@ -23,7 +25,7 @@ from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from Core import AutostartDir, ChildEnvironment, ConfigHome, DataHome, IsWindows, LoadConfig, RunBySaraSuite
from Core import AutostartDir, ChildEnvironment, ConfigHome, DataHome, Home, IsMac, IsWindows, LoadConfig, RunBySaraSuite
@dataclass
@@ -48,7 +50,7 @@ def _Run(Arguments: list[str], Timeout: int = 15) -> subprocess.CompletedProcess
creationflags=_NoWindow)
# --- Run by SaraSuite (Windows; Linux without systemd) ------------------------------------------
# --- Run by SaraSuite (Windows; macOS; Linux without systemd) ------------------------------------------
def _Name(Unit: str) -> str:
@@ -62,6 +64,8 @@ def _ServicesFile(Name: str, Suffix: str) -> Path:
def _AutostartFile(Name: str) -> Path:
if IsWindows:
return ConfigHome / "Microsoft" / "Windows" / "Start Menu" / "Programs" / "Startup" / f"{Name}.lnk"
if IsMac:
return Home / "Library" / "LaunchAgents" / f"srhtnin.{Name}.plist"
return AutostartDir / f"{Name}.desktop"
@@ -85,6 +89,11 @@ def _Pid(Name: str) -> str:
Pid = int(_ServicesFile(Name, ".pid").read_text().strip())
except (OSError, ValueError):
return ""
if IsMac: # no /proc: ask ps for its command line
try:
return str(Pid) if "ServiceRun.py" in _Run(["ps", "-o", "command=", "-p", str(Pid)]).stdout else ""
except (OSError, subprocess.SubprocessError):
return ""
if not IsWindows:
try:
return str(Pid) if b"ServiceRun.py" in Path(f"/proc/{Pid}/cmdline").read_bytes() else ""
@@ -134,6 +143,18 @@ def _AutostartEntry(Python: Path, Arguments: list[str], Name: str) -> str:
)
def _LaunchAgent(Python: Path, Arguments: list[str], Name: str, Folder: Path) -> bytes:
"""The same LaunchAgent SaraSuite writes (its MacSupport.LaunchAgent)."""
return plistlib.dumps({
"Label": f"srhtnin.{Name}",
"ProgramArguments": [str(Python), *Arguments],
"WorkingDirectory": str(Folder),
"RunAtLoad": True,
"ProcessType": "Background",
"StandardErrorPath": str(Home / "Library" / "Logs" / f"{Name}.launchd.log"),
})
def _OwnControl(Name: str, Action: str) -> str:
Python, Arguments, Folder = _Command(Name)
try:
@@ -169,6 +190,8 @@ def _OwnControl(Name: str, Action: str) -> str:
Quoted = " ".join(f'"{Item}"' if " " in Item or "\\" in Item else Item for Item in Arguments)
pylnk3.for_file(str(Python), str(Entry), arguments=Quoted, work_dir=str(Folder),
description=f"{Name} (background service)")
elif IsMac:
Entry.write_bytes(_LaunchAgent(Python, Arguments, Name, Folder))
else:
Entry.write_text(_AutostartEntry(Python, Arguments, Name))
elif Action == "disable":