134 lines
4.1 KiB
Python
134 lines
4.1 KiB
Python
from __future__ import annotations
|
|
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
import plistlib
|
|
|
|
from .paths import app_data_dir
|
|
|
|
TASK_NAME = "ProxyChainManagerAutoStart"
|
|
LAUNCH_AGENT_ID = "local.proxy-god.mac"
|
|
|
|
|
|
def _launch_agent_path() -> Path:
|
|
p = Path.home() / "Library" / "LaunchAgents"
|
|
p.mkdir(parents=True, exist_ok=True)
|
|
return p / f"{LAUNCH_AGENT_ID}.plist"
|
|
|
|
|
|
def _mac_program_arguments() -> list[str]:
|
|
root = Path(__file__).resolve().parent.parent
|
|
return [str(root / "scripts" / "run_proxy_god.sh")]
|
|
|
|
|
|
def _install_launch_agent() -> tuple[bool, str]:
|
|
plist_path = _launch_agent_path()
|
|
data = {
|
|
"Label": LAUNCH_AGENT_ID,
|
|
"ProgramArguments": _mac_program_arguments(),
|
|
"RunAtLoad": True,
|
|
"WorkingDirectory": str(Path(__file__).resolve().parent.parent),
|
|
"StandardOutPath": str(Path.home() / "Library" / "Logs" / "ProxyGod.out.log"),
|
|
"StandardErrorPath": str(Path.home() / "Library" / "Logs" / "ProxyGod.err.log"),
|
|
}
|
|
try:
|
|
plist_path.write_bytes(plistlib.dumps(data))
|
|
subprocess.run(["launchctl", "unload", str(plist_path)], capture_output=True, text=True, timeout=10)
|
|
r = subprocess.run(["launchctl", "load", str(plist_path)], capture_output=True, text=True, timeout=10)
|
|
out = (r.stdout or "") + (r.stderr or "")
|
|
return r.returncode == 0, out.strip() or "LaunchAgent installed"
|
|
except Exception as e:
|
|
return False, str(e)
|
|
|
|
|
|
def _uninstall_launch_agent() -> tuple[bool, str]:
|
|
plist_path = _launch_agent_path()
|
|
try:
|
|
subprocess.run(["launchctl", "unload", str(plist_path)], capture_output=True, text=True, timeout=10)
|
|
plist_path.unlink(missing_ok=True)
|
|
return True, "LaunchAgent removed"
|
|
except Exception as e:
|
|
return False, str(e)
|
|
|
|
|
|
def _write_launcher_cmd() -> Path:
|
|
"""Create a stable launcher script so Task Scheduler does not need fragile quoting."""
|
|
if getattr(sys, "frozen", False):
|
|
exe = Path(sys.executable).resolve()
|
|
body = f'@echo off\r\nstart "" /B "{exe}"\r\n'
|
|
else:
|
|
py = Path(sys.executable).resolve()
|
|
if py.name.lower() == "python.exe":
|
|
cand = py.with_name("pythonw.exe")
|
|
if cand.is_file():
|
|
py = cand
|
|
root = Path(__file__).resolve().parent.parent
|
|
run_py = (root / "run.py").resolve()
|
|
body = f'@echo off\r\n"{py}" "{run_py}"\r\n'
|
|
|
|
p = app_data_dir() / "launch_proxy_chain_manager.cmd"
|
|
p.write_text(body, encoding="utf-8")
|
|
return p
|
|
|
|
|
|
def install_logon_task() -> tuple[bool, str]:
|
|
if sys.platform == "darwin":
|
|
return _install_launch_agent()
|
|
launcher = _write_launcher_cmd()
|
|
tr = str(launcher)
|
|
try:
|
|
r = subprocess.run(
|
|
[
|
|
"schtasks",
|
|
"/Create",
|
|
"/F",
|
|
"/TN",
|
|
TASK_NAME,
|
|
"/TR",
|
|
tr,
|
|
"/SC",
|
|
"ONLOGON",
|
|
"/RL",
|
|
"LIMITED",
|
|
],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=60,
|
|
)
|
|
out = (r.stdout or "") + (r.stderr or "")
|
|
return r.returncode == 0, out.strip() or ("OK" if r.returncode == 0 else "Failed")
|
|
except Exception as e:
|
|
return False, str(e)
|
|
|
|
|
|
def uninstall_logon_task() -> tuple[bool, str]:
|
|
if sys.platform == "darwin":
|
|
return _uninstall_launch_agent()
|
|
try:
|
|
r = subprocess.run(
|
|
["schtasks", "/Delete", "/F", "/TN", TASK_NAME],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=60,
|
|
)
|
|
out = (r.stdout or "") + (r.stderr or "")
|
|
return r.returncode == 0, out.strip() or ("OK" if r.returncode == 0 else "Failed")
|
|
except Exception as e:
|
|
return False, str(e)
|
|
|
|
|
|
def task_exists() -> bool:
|
|
if sys.platform == "darwin":
|
|
return _launch_agent_path().is_file()
|
|
try:
|
|
r = subprocess.run(
|
|
["schtasks", "/Query", "/TN", TASK_NAME],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=30,
|
|
)
|
|
return r.returncode == 0
|
|
except Exception:
|
|
return False
|