fix: auto-enable systemd linger during gateway install on headless servers
Fixes #1005 Without linger, user-level systemd services stop when the SSH session ends — even though systemctl --user status shows active (running). Changes to systemd_install(): - Try loginctl enable-linger automatically (succeeds when the process has the required privileges) - If loginctl fails (no privileges), print a clear, copy-pasteable warning with the exact command the user must run New helper: _ensure_linger_enabled() - Fast path: checks /var/lib/systemd/linger/<user> (no subprocess) - Auto-enable: loginctl enable-linger <user> - Fallback: actionable warning with sudo command + restart instructions Tests: 4 new tests in TestEnsureLingerEnabled, 205 passed total
This commit is contained in:
parent
1114841a2c
commit
f10e26f731
3 changed files with 183 additions and 5 deletions
|
|
@ -251,7 +251,6 @@ StandardError=journal
|
|||
WantedBy=default.target
|
||||
"""
|
||||
|
||||
|
||||
def _normalize_service_definition(text: str) -> str:
|
||||
return "\n".join(line.rstrip() for line in text.strip().splitlines())
|
||||
|
||||
|
|
@ -279,6 +278,65 @@ def refresh_systemd_unit_if_needed() -> bool:
|
|||
return True
|
||||
|
||||
|
||||
|
||||
def _print_linger_enable_warning(username: str, detail: str | None = None) -> None:
|
||||
print()
|
||||
print("⚠ Linger not enabled — gateway may stop when you close this terminal.")
|
||||
if detail:
|
||||
print(f" Auto-enable failed: {detail}")
|
||||
print()
|
||||
print(" On headless servers (VPS, cloud instances) run:")
|
||||
print(f" sudo loginctl enable-linger {username}")
|
||||
print()
|
||||
print(" Then restart the gateway:")
|
||||
print(f" systemctl --user restart {SERVICE_NAME}.service")
|
||||
print()
|
||||
|
||||
|
||||
|
||||
def _ensure_linger_enabled() -> None:
|
||||
"""Enable linger when possible so the user gateway survives logout."""
|
||||
if not is_linux():
|
||||
return
|
||||
|
||||
import getpass
|
||||
import shutil
|
||||
|
||||
username = getpass.getuser()
|
||||
linger_file = Path(f"/var/lib/systemd/linger/{username}")
|
||||
if linger_file.exists():
|
||||
print("✓ Systemd linger is enabled (service survives logout)")
|
||||
return
|
||||
|
||||
linger_enabled, linger_detail = get_systemd_linger_status()
|
||||
if linger_enabled is True:
|
||||
print("✓ Systemd linger is enabled (service survives logout)")
|
||||
return
|
||||
|
||||
if not shutil.which("loginctl"):
|
||||
_print_linger_enable_warning(username, linger_detail or "loginctl not found")
|
||||
return
|
||||
|
||||
print("Enabling linger so the gateway survives SSH logout...")
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["loginctl", "enable-linger", username],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
except Exception as e:
|
||||
_print_linger_enable_warning(username, str(e))
|
||||
return
|
||||
|
||||
if result.returncode == 0:
|
||||
print("✓ Linger enabled — gateway will persist after logout")
|
||||
return
|
||||
|
||||
detail = (result.stderr or result.stdout or f"exit {result.returncode}").strip()
|
||||
_print_linger_enable_warning(username, detail or linger_detail)
|
||||
|
||||
|
||||
def systemd_install(force: bool = False):
|
||||
unit_path = get_systemd_unit_path()
|
||||
|
||||
|
|
@ -302,7 +360,7 @@ def systemd_install(force: bool = False):
|
|||
print(f" hermes gateway status # Check status")
|
||||
print(f" journalctl --user -u {SERVICE_NAME} -f # View logs")
|
||||
print()
|
||||
print_systemd_linger_guidance()
|
||||
_ensure_linger_enabled()
|
||||
|
||||
def systemd_uninstall():
|
||||
subprocess.run(["systemctl", "--user", "stop", SERVICE_NAME], check=False)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue