//! `gatewayd serve --config `: the Mattermost channel. It loads its configuration and its //! token, prepares its directory, then serves until it must stop (exit 1). use std::os::unix::fs::DirBuilderExt; use std::path::Path; use std::process::ExitCode; use std::sync::Arc; use std::sync::atomic::AtomicBool; use bxcore::{instance, secrets}; use gatewayd::config::{Config, MATTERMOST_TOKEN}; use gatewayd::serve::{START_FAILED, Tuning, run}; fn main() -> ExitCode { // `args_os`: the config path need not be UTF-8, and `args` would panic on one that is not. let args: Vec = std::env::args_os().skip(1).collect(); let words: Vec> = args.iter().map(|a| a.to_str()).collect(); match (words.as_slice(), args.get(2)) { ([Some("serve"), Some("--config"), _], Some(path)) => serve(Path::new(path)), _ => { eprintln!("usage: gatewayd serve --config "); ExitCode::from(2) } } } fn serve(path: &Path) -> ExitCode { // Each failure prints its message with `eprintln!` and returns `ExitCode::from(1)`. // 1. `let config = match Config::load(path) { ... }`: an Err(e) prints // "gatewayd: {e}\n{START_FAILED}". // 2. `let source = match config.token_source() { ... }`: an Err(why) prints // "gatewayd: {}: {why}\n{START_FAILED}" with `path.display()`. // 3. `let env = |k: &str| std::env::var_os(k);`, then // `let loaded = match secrets::load("gatewayd", MATTERMOST_TOKEN, &source, &env)`: an Err(e) // prints "gatewayd: {e}" (the error carries its own pointer). // Then `if let Some(warning) = &loaded.warning { eprintln!("{warning}"); }`. // 3b. The instance lock, `/run/gatewayd.lock` (M5b spec, section 9a): after the secret, // which is read before anything is made, and before the directory and the socket. // 4. The state file's directory: // `let dir = config.state_path().parent().map(Path::to_path_buf).unwrap_or_default();` // `std::fs::DirBuilder::new().recursive(true).mode(0o700).create(&dir)`: an Err(e) prints // "gatewayd: cannot prepare {}: {e}\n{START_FAILED}" with `dir.display()`. // 5. `let log: gatewayd::serve::Log = Arc::new(|line: &str| eprintln!("{line}"));` // `let stop = run(config, loaded.secret, Tuning::default(), log, &AtomicBool::new(false));` // `eprintln!("{stop}");` and `ExitCode::from(1)`. let config = match Config::load(path) { Ok(config) => config, Err(e) => { eprintln!("gatewayd: {e}\n{START_FAILED}"); return ExitCode::from(1); } }; let source = match config.token_source() { Ok(source) => source, Err(why) => { eprintln!("gatewayd: {}: {why}\n{START_FAILED}", path.display()); return ExitCode::from(1); } }; let env = |k: &str| std::env::var_os(k); let loaded = match secrets::load("gatewayd", MATTERMOST_TOKEN, &source, &env) { Ok(loaded) => loaded, Err(e) => { eprintln!("gatewayd: {e}"); return ExitCode::from(1); } }; if let Some(warning) = &loaded.warning { eprintln!("{warning}"); } let lock_path = config.paths.home.join("run/gatewayd.lock"); let _instance = match instance::lock(&lock_path) { Ok(held) => held, Err(e) => { eprintln!("{}", e.line("gatewayd", &lock_path)); return ExitCode::from(1); } }; let dir = config .state_path() .parent() .map(Path::to_path_buf) .unwrap_or_default(); if let Err(e) = std::fs::DirBuilder::new() .recursive(true) .mode(0o700) .create(&dir) { eprintln!( "gatewayd: cannot prepare {}: {e}\n{START_FAILED}", dir.display() ); return ExitCode::from(1); } let log: gatewayd::serve::Log = Arc::new(|line: &str| eprintln!("{line}")); let stop = run( config, loaded.secret, Tuning::default(), log, &AtomicBool::new(false), ); eprintln!("{stop}"); ExitCode::from(1) }