Add Unix daemon mode with PID file and privilege dropping

Implement core daemon infrastructure for running telemt as a background
  service on Unix platforms (Linux, FreeBSD, etc.):

  - Add src/daemon module with classic double-fork daemonization
  - Implement flock-based PID file management to prevent duplicate instances
  - Add privilege dropping (setuid/setgid) after socket binding
  - New CLI flags: --daemon, --foreground, --pid-file, --run-as-user,
    --run-as-group, --working-dir

  Daemonization occurs before tokio runtime starts to ensure clean fork.
  PID file uses exclusive locking to detect already-running instances.
  Privilege dropping happens after bind_listeners() to allow binding
  to privileged ports (< 1024) before switching to unprivileged user.

Signed-off-by: Vladimir Krivopalov <argenet@yandex.ru>
This commit is contained in:
Vladimir Krivopalov
2026-03-20 20:31:47 +02:00
committed by Vladimir Krivopalov
parent 2d3c2807ab
commit 2ea7813ed4
6 changed files with 789 additions and 34 deletions

View File

@@ -4,6 +4,8 @@ mod api;
mod cli;
mod config;
mod crypto;
#[cfg(unix)]
mod daemon;
mod error;
mod ip_tracker;
#[cfg(test)]
@@ -27,7 +29,42 @@ mod tls_front;
mod transport;
mod util;
#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
maestro::run().await
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
// On Unix, handle daemonization before starting tokio runtime
#[cfg(unix)]
{
let args: Vec<String> = std::env::args().skip(1).collect();
let daemon_opts = cli::parse_daemon_args(&args);
// Daemonize if requested (must happen before tokio runtime starts)
if daemon_opts.should_daemonize() {
match daemon::daemonize(daemon_opts.working_dir.as_deref()) {
Ok(daemon::DaemonizeResult::Parent) => {
// Parent process exits successfully
std::process::exit(0);
}
Ok(daemon::DaemonizeResult::Child) => {
// Continue as daemon child
}
Err(e) => {
eprintln!("[telemt] Daemonization failed: {}", e);
std::process::exit(1);
}
}
}
// Now start tokio runtime and run the server
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()?
.block_on(maestro::run_with_daemon(daemon_opts))
}
#[cfg(not(unix))]
{
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()?
.block_on(maestro::run())
}
}