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}