| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 |
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- r"""Linux/macOS 服务主体 —— systemd (`Type=simple`) 的前台守护进程 (2026-09-17 用户令 1)。
- 为什么是"前台常驻"而不是让 systemd 直接跑 `guanlan.py serve`:
- `guanlan.py serve` 起完 6 个组件就**返回**(组件是 detached 的),systemd 会以为"主进程退出",
- 于是 `Restart=always` 会不停重启、也管不了组件死活。所以这里做一个**常驻守护**:
- ExecStart=<PY> scripts/service_main.py # 起组件 → 每 15 s 巡检(缺谁补谁)→ 收 SIGTERM 时全停
- Type=simple / Restart=always / KillMode=process
- 用法:
- python scripts/service_main.py # 前台守护(systemd 用这个)
- python scripts/service_main.py --once # 只跑一轮巡检就退出(无 root 也能自证)
- python scripts/service_main.py --selftest # 前置条件自检
- 注册/卸载走 `scripts/service_ctl.py`(需 root),或用 install.sh --service。
- """
- from __future__ import annotations
- import pathlib
- import signal
- import sys
- import threading
- ROOT = pathlib.Path(__file__).resolve().parents[1]
- sys.path.insert(0, str(ROOT))
- sys.path.insert(0, str(ROOT / 'scripts'))
- import service_worker as SW # noqa: E402
- def main() -> int:
- if '--selftest' in sys.argv:
- return SW.selftest()
- stop_evt = threading.Event()
- def _on_term(signum, frame):
- SW.log(f'收到信号 {signum} → 停服')
- stop_evt.set()
- signal.signal(signal.SIGTERM, _on_term)
- signal.signal(signal.SIGINT, _on_term)
- SW.log('服务模式启动(systemd Type=simple)')
- SW.start()
- try:
- SW.supervise(stop_evt, once=('--once' in sys.argv))
- finally:
- if '--once' not in sys.argv:
- SW.stop()
- SW.log('服务已退出')
- return 0
- if __name__ == '__main__':
- sys.exit(main())
|