pispas_service/
main.rs

1#![cfg_attr(windows, windows_subsystem = "windows")]
2//! # `pispas_service` — Windows service wrapper
3//!
4//! Thin wrapper that registers with the Windows Service Control Manager
5//! (SCM) and, once SCM has started it, spawns and supervises the real
6//! runtime binary ([`pispas_modules`](../pispas_modules/index.html)).
7//!
8//! Why this extra layer? Because:
9//!
10//! * SCM expects a binary that implements the service dispatch loop
11//!   (`ServiceMain`). `pispas_modules` is a plain `tokio::main` — easier
12//!   to develop and test stand-alone without SCM ceremony.
13//! * This wrapper handles `STOP` / `PAUSE` / `CONTINUE` events coming from
14//!   the SCM and translates them into graceful shutdown / restart signals
15//!   for `pispas_modules`.
16//! * Keeps SCM-specific Windows-API code isolated from the business logic.
17//!
18//! ```text
19//!   Windows SCM  --start/stop-->  pispas-service.exe  (THIS BIN)
20//!                                          |
21//!                                          | spawn / signal
22//!                                          v
23//!                                   pispas-modules.exe
24//! ```
25//!
26//! The installer (`pispas-installer.exe`, via `pispas-elevator.exe` for
27//! UAC) calls `sc create "PispasService" binPath=…` pointing at this
28//! binary.
29//!
30//! Implementation details live in the `service` module next to this file.
31//!
32//! Authors: Jorge.A Duran & Mario González · pispas Technologies SL · 2023.
33
34use easy_trace::instruments::tracing;
35mod service;
36
37fn init_logging() {
38    easy_trace::init(
39        sharing::paths::user_log_dir().to_str().unwrap_or_default(),
40        "pispas_service.log",
41    );
42    println!("Log path: {}", sharing::paths::user_log_dir().to_str().unwrap_or_default());
43    tracing::info!("Executed Pispas Service");
44}
45
46
47#[cfg(windows)]
48fn main() -> windows_service::Result<()> {
49    init_logging();
50    tracing::info!("Executed Pispas Service on Windows");
51
52    let args: Vec<String> = std::env::args().collect();
53    if args.get(1).map(|s| s == "run").unwrap_or(false) {
54        tracing::info!("Running in direct mode");
55        if let Err(e) = service::pispas_service::run_direct() {
56            eprintln!("Service error: {}", e);
57            std::process::exit(1);
58        }
59        return Ok(());
60    }
61
62    match service::pispas_service::run() {
63        Ok(()) => Ok(()),
64        Err(service::pispas_service::ServiceError::Windows(e)) => Err(e),
65        Err(e) => {
66            eprintln!("Service error: {}", e);
67            std::process::exit(1);
68        }
69    }
70}
71
72#[cfg(not(windows))]
73fn main() -> Result<(), Box<dyn std::error::Error>> {
74    init_logging();
75    tracing::info!("Executed Pispas Service on Unix/Linux");
76
77    let args: Vec<String> = std::env::args().collect();
78    if args.get(1).map(|s| s == "run" || s == "foreground").unwrap_or(false) {
79        tracing::info!("Running in direct mode");
80        return service::pispas_service::run_direct()
81            .map_err(|e| Box::new(e) as Box<dyn std::error::Error>);
82    }
83
84    tracing::info!("Running as daemon");
85    service::pispas_service::run()
86        .map_err(|e| Box::new(e) as Box<dyn std::error::Error>)
87}