polaris-mirror/src/main.rs
Antoine Gersant 524e072e9f Fixed typo
2024-10-09 11:12:44 -07:00

165 lines
4.5 KiB
Rust

#![cfg_attr(all(windows, feature = "ui"), windows_subsystem = "windows")]
#![recursion_limit = "256"]
use log::{error, info};
use options::CLIOptions;
use simplelog::{
ColorChoice, CombinedLogger, LevelFilter, SharedLogger, TermLogger, TerminalMode, WriteLogger,
};
use std::fs;
use std::path::{Path, PathBuf};
mod app;
mod options;
mod paths;
mod server;
#[cfg(test)]
mod test;
mod ui;
mod utils;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error(transparent)]
App(#[from] app::Error),
#[error("Could not start web services")]
ServiceStartup(std::io::Error),
#[error("Could not parse command line arguments:\n\n{0}")]
CliArgsParsing(getopts::Fail),
#[cfg(unix)]
#[error("Failed to turn polaris process into a daemon:\n\n{0}")]
Daemonize(daemonize::Error),
#[error("Could not create log directory `{0}`:\n\n{1}")]
LogDirectoryCreationError(PathBuf, std::io::Error),
#[error("Could not create log file `{0}`:\n\n{1}")]
LogFileCreationError(PathBuf, std::io::Error),
#[error("Could not initialize log system:\n\n{0}")]
LogInitialization(log::SetLoggerError),
#[cfg(unix)]
#[error("Could not create pid directory `{0}`:\n\n{1}")]
PidDirectoryCreationError(PathBuf, std::io::Error),
#[cfg(unix)]
#[error("Could not notify systemd of initialization success:\n\n{0}")]
SystemDNotify(std::io::Error),
}
#[cfg(unix)]
fn daemonize<T: AsRef<Path>>(foreground: bool, pid_file_path: T) -> Result<(), Error> {
if foreground {
return Ok(());
}
if let Some(parent) = pid_file_path.as_ref().parent() {
fs::create_dir_all(parent)
.map_err(|e| Error::PidDirectoryCreationError(parent.to_owned(), e))?;
}
let daemonize = daemonize::Daemonize::new()
.pid_file(pid_file_path.as_ref())
.working_directory(".");
daemonize.start().map_err(Error::Daemonize)?;
Ok(())
}
#[cfg(unix)]
fn notify_ready() -> Result<(), Error> {
if let Ok(true) = sd_notify::booted() {
sd_notify::notify(true, &[sd_notify::NotifyState::Ready]).map_err(Error::SystemDNotify)?;
}
Ok(())
}
fn init_logging<T: AsRef<Path>>(
log_level: LevelFilter,
log_file_path: &Option<T>,
) -> Result<(), Error> {
let log_config = simplelog::ConfigBuilder::new()
.set_location_level(LevelFilter::Error)
.build();
let mut loggers: Vec<Box<dyn SharedLogger>> = vec![TermLogger::new(
log_level,
log_config.clone(),
TerminalMode::Mixed,
ColorChoice::Auto,
)];
if let Some(path) = log_file_path {
if let Some(parent) = path.as_ref().parent() {
fs::create_dir_all(parent)
.map_err(|e| Error::LogDirectoryCreationError(parent.to_owned(), e))?;
}
loggers.push(WriteLogger::new(
log_level,
log_config,
fs::File::create(path)
.map_err(|e| Error::LogFileCreationError(path.as_ref().to_owned(), e))?,
));
}
CombinedLogger::init(loggers).map_err(Error::LogInitialization)?;
Ok(())
}
fn main() -> Result<(), Error> {
// Parse CLI options
let args: Vec<String> = std::env::args().collect();
let options_manager = options::Manager::new();
let cli_options = options_manager
.parse(&args[1..])
.map_err(Error::CliArgsParsing)?;
if cli_options.show_help {
let program = args[0].clone();
let brief = format!("Usage: {} [options]", program);
print!("{}", options_manager.usage(&brief));
return Ok(());
}
let paths = paths::Paths::new(&cli_options);
// Logging
let log_level = cli_options.log_level.unwrap_or(LevelFilter::Info);
init_logging(log_level, &paths.log_file_path)?;
// Fork
#[cfg(unix)]
daemonize(cli_options.foreground, &paths.pid_file_path)?;
info!("Cache files location is {:#?}", paths.cache_dir_path);
info!("Data files location is {:#?}", paths.data_dir_path);
info!("Config file location is {:#?}", paths.config_file_path);
info!("Database file location is {:#?}", paths.db_file_path);
info!("Log file location is {:#?}", paths.log_file_path);
#[cfg(unix)]
if !cli_options.foreground {
info!("Pid file location is {:#?}", paths.pid_file_path);
}
info!("Swagger files location is {:#?}", paths.swagger_dir_path);
info!("Web client files location is {:#?}", paths.web_dir_path);
async_main(cli_options, paths)
}
#[tokio::main]
async fn async_main(cli_options: CLIOptions, paths: paths::Paths) -> Result<(), Error> {
// Create and run app
let app = app::App::new(cli_options.port.unwrap_or(5050), paths).await?;
app.scanner.begin_periodic_scans();
app.ddns_manager.begin_periodic_updates();
// Start server
info!("Starting up server");
if let Err(e) = server::launch(app).await {
return Err(Error::ServiceStartup(e));
}
// Send readiness notification
#[cfg(unix)]
notify_ready()?;
// Run UI
ui::run();
info!("Shutting down server");
Ok(())
}