mod clock;
pub mod config;
pub mod keyexchange;
mod local_ip_provider;
mod ntp_source;
pub mod nts_key_provider;
pub mod observer;
#[cfg(feature = "pps")]
mod pps_source;
mod server;
mod sock_source;
pub mod sockets;
pub mod spawn;
mod system;
pub mod tracing;
mod util;
use std::{error::Error, io::IsTerminal, path::PathBuf};
use ::tracing::info;
pub use config::Config;
use ntp_proto::KalmanClockController;
pub use observer::ObservableState;
pub use system::spawn;
use tokio::runtime::Builder;
use tracing_subscriber::util::SubscriberInitExt;
use config::NtpDaemonOptions;
use crate::daemon::tracing::LogReloadTaskStarter;
use self::tracing::LogLevel;
const VERSION: &str = env!("CARGO_PKG_VERSION");
pub fn main() -> Result<(), Box<dyn Error>> {
let options = NtpDaemonOptions::try_parse_from(std::env::args())?;
match options.action {
config::NtpDaemonAction::Help => {
println!("{}", config::long_help_message());
}
config::NtpDaemonAction::Version => {
eprintln!("ntp-daemon {VERSION}");
}
config::NtpDaemonAction::Run => run(options)?,
}
Ok(())
}
#[derive(Debug, Copy, Clone)]
pub(crate) enum Application {
Deamon,
MetricsExporter,
Ctl,
}
pub(crate) fn initialize_logging_parse_config(
initial_log_level: Option<LogLevel>,
config_path: Option<PathBuf>,
app: Application,
) -> (Config, Option<LogReloadTaskStarter>) {
let mut log_level = initial_log_level.unwrap_or_default();
let (config_tracing, _) = crate::daemon::tracing::tracing_init(log_level, None, true);
let (config, tracing_inst, task_starter) =
::tracing::subscriber::with_default(config_tracing, || {
let config = match Config::from_args(config_path, vec![], vec![]) {
Ok(c) => c,
Err(e) => {
eprintln!("There was an error loading the config: {e}");
std::process::exit(exitcode::CONFIG);
}
};
if let Some(config_log_level) = config.observability.log_level
&& initial_log_level.is_none()
{
log_level = config_log_level;
}
let log_path = match app {
Application::Deamon => config.observability.log_path.clone(),
Application::MetricsExporter => {
config.observability.log_path_metrics_exporter.clone()
}
Application::Ctl => None,
};
let ansi_colors = config
.observability
.ansi_colors
.unwrap_or_else(|| log_path.is_none() && std::io::stdout().is_terminal());
let (tracing_inst, task_starter) =
self::tracing::tracing_init(log_level, log_path, ansi_colors);
(config, tracing_inst, task_starter)
});
tracing_inst.init();
(config, task_starter)
}
fn run(options: NtpDaemonOptions) -> Result<(), Box<dyn Error>> {
let (config, task_starter) =
initialize_logging_parse_config(options.log_level, options.config, Application::Deamon);
let runtime = if config.servers.is_empty() && config.nts_ke.is_empty() {
Builder::new_current_thread().enable_all().build()?
} else {
Builder::new_multi_thread().enable_all().build()?
};
runtime.block_on(async move {
if let Some(task_starter) = task_starter {
task_starter.start();
}
if config.observability.log_level.is_some() && options.log_level.is_some() {
info!("Log level override from command line arguments is active");
}
config.check();
let keyset = nts_key_provider::spawn(config.keyset).await;
#[cfg(feature = "hardware-timestamping")]
let clock_config = config.clock;
#[cfg(not(feature = "hardware-timestamping"))]
let clock_config = config::ClockConfig::default();
::tracing::debug!("Configuration loaded, spawning daemon jobs");
let clock = clock_config.clock;
let (main_loop_handle, channels) = spawn::<KalmanClockController<_, _>>(
config.synchronization.synchronization_base,
config.synchronization.algorithm,
config.source_defaults,
clock_config,
&config.sources,
&config.servers,
keyset.clone(),
)
.await?;
for nts_ke_config in config.nts_ke {
let _join_handle = keyexchange::spawn(nts_ke_config, keyset.clone());
}
observer::spawn(
&config.observability,
channels.source_snapshots,
channels.server_data_receiver,
channels.system_snapshot_receiver,
clock,
);
Ok(main_loop_handle.await??)
})
}
pub(crate) mod exitcode {
pub const SOFTWARE: i32 = 70;
pub const NOPERM: i32 = 77;
pub const CONFIG: i32 = 78;
}