diff --git a/Cargo.lock b/Cargo.lock index 0e679da..6dcae4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,6 +50,7 @@ dependencies = [ "lazy_static", "ldap3", "log", + "nix", "rand", "rmp-serde", "rpassword", @@ -432,6 +433,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + [[package]] name = "bitmaps" version = "2.1.0" @@ -571,7 +578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", "clap_derive", "clap_lex", "indexmap", @@ -1433,7 +1440,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" dependencies = [ "arrayvec", - "bitflags", + "bitflags 1.3.2", "cfg-if", "ryu", "static_assertions", @@ -1563,6 +1570,17 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "libc", +] + [[package]] name = "nom" version = "2.2.1" @@ -1793,7 +1811,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", - "bitflags", + "bitflags 1.3.2", "cfg-if", "concurrent-queue", "libc", @@ -1901,7 +1919,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1910,7 +1928,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -2109,7 +2127,7 @@ version = "0.37.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", @@ -2218,7 +2236,7 @@ version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", diff --git a/Cargo.toml b/Cargo.toml index e7c3c17..bfda58e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ rusoto_credential = "0.48.0" rusoto_s3 = { version = "0.48.0", default_features = false, features = ["rustls"] } hyper-rustls = { version = "0.24", features = ["http2"] } rusoto_signature = "0.48.0" +nix = { version = "0.27", features = ["signal"] } serde = "1.0.137" rand = "0.8.5" rmp-serde = "0.15" diff --git a/src/config.rs b/src/config.rs index e50cd68..1438910 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct CompanionConfig { - pub pid: Option, + pub pid: Option, pub imap: ImapConfig, #[serde(flatten)] @@ -17,7 +17,7 @@ pub struct CompanionConfig { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct ProviderConfig { - pub pid: Option, + pub pid: Option, pub imap: ImapConfig, pub lmtp: LmtpConfig, pub users: UserManagement, diff --git a/src/main.rs b/src/main.rs index 02ba5e4..f08f1a3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,9 +13,11 @@ mod server; mod storage; use std::path::PathBuf; +use std::io::Read; use anyhow::{bail, Result, Context}; use clap::{Parser, Subcommand}; +use nix::{unistd::Pid, sys::signal}; use config::*; use server::Server; @@ -92,7 +94,7 @@ enum CompanionCommand { Daemon, Reload { #[clap(short, long, env = "AEROGRAMME_PID")] - pid: Option, + pid: Option, }, Wizard, #[clap(subcommand)] @@ -104,7 +106,10 @@ enum ProviderCommand { /// Runs the IMAP+LMTP server daemon Daemon, /// Reload the daemon - Reload, + Reload { + #[clap(short, long, env = "AEROGRAMME_PID")] + pid: Option, + }, /// Manage static accounts #[clap(subcommand)] Account(AccountManagement), @@ -161,9 +166,7 @@ async fn main() -> Result<()> { let server = Server::from_companion_config(config).await?; server.run().await?; }, - CompanionCommand::Reload { pid: _pid } => { - unimplemented!(); - }, + CompanionCommand::Reload { pid } => reload(*pid, config.pid)?, CompanionCommand::Wizard => { unimplemented!(); }, @@ -177,9 +180,7 @@ async fn main() -> Result<()> { let server = Server::from_provider_config(config).await?; server.run().await?; }, - ProviderCommand::Reload => { - unimplemented!(); - }, + ProviderCommand::Reload { pid } => reload(*pid, config.pid)?, ProviderCommand::Account(cmd) => { let user_file = match config.users { UserManagement::Static(conf) => conf.user_list, @@ -260,6 +261,22 @@ async fn main() -> Result<()> { Ok(()) } +fn reload(pid: Option, pid_path: Option) -> Result<()> { + let final_pid = match (pid, pid_path) { + (Some(pid), _) => pid, + (_, Some(path)) => { + let mut f = std::fs::OpenOptions::new().read(true).open(path)?; + let mut pidstr = String::new(); + f.read_to_string(&mut pidstr)?; + pidstr.parse::()? + }, + _ => bail!("Unable to infer your daemon's PID"), + }; + let pid = Pid::from_raw(final_pid); + signal::kill(pid, signal::Signal::SIGUSR1)?; + Ok(()) +} + fn account_management(root: &Command, cmd: &AccountManagement, users: PathBuf) -> Result<()> { let mut ulist: UserList = read_config(users.clone()).context(format!("'{:?}' must be a user database", users))?; diff --git a/src/server.rs b/src/server.rs index 8abdb86..552a0e6 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,4 +1,6 @@ use std::sync::Arc; +use std::path::PathBuf; +use std::io::Write; use anyhow::Result; use futures::try_join; @@ -14,18 +16,21 @@ use crate::login::{ldap_provider::*, static_provider::*}; pub struct Server { lmtp_server: Option>, imap_server: Option, + pid_file: Option, } impl Server { pub async fn from_companion_config(config: CompanionConfig) -> Result { + tracing::info!("Init as companion"); let login = Arc::new(StaticLoginProvider::new(config.users).await?); let lmtp_server = None; let imap_server = Some(imap::new(config.imap, login.clone()).await?); - Ok(Self { lmtp_server, imap_server }) + Ok(Self { lmtp_server, imap_server, pid_file: config.pid }) } pub async fn from_provider_config(config: ProviderConfig) -> Result { + tracing::info!("Init as provider"); let login: ArcLoginProvider = match config.users { UserManagement::Static(x) => Arc::new(StaticLoginProvider::new(x).await?), UserManagement::Ldap(x) => Arc::new(LdapLoginProvider::new(x)?), @@ -34,11 +39,24 @@ impl Server { let lmtp_server = Some(LmtpServer::new(config.lmtp, login.clone())); let imap_server = Some(imap::new(config.imap, login.clone()).await?); - Ok(Self { lmtp_server, imap_server }) + Ok(Self { lmtp_server, imap_server, pid_file: config.pid }) } pub async fn run(self) -> Result<()> { - tracing::info!("Starting Aerogramme..."); + let pid = std::process::id(); + tracing::info!(pid=pid, "Starting main loops"); + + // write the pid file + if let Some(pid_file) = self.pid_file { + let mut file = std::fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(pid_file)?; + file.write_all(pid.to_string().as_bytes())?; + drop(file); + } + let (exit_signal, provoke_exit) = watch_ctrl_c(); let _exit_on_err = move |err: anyhow::Error| {