1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//! This crate contains the main ntp-daemon code for ntpd-rs and is not intended as
//! a public interface at this time. It follows the same version as the main ntpd-rs
//! crate, but that version is not intended to give any stability guarantee. Use at
//! your own risk.
//!
//! Please visit the [ntpd-rs](https://github.com/pendulum-project/ntpd-rs) project
//! for more information.
#![forbid(unsafe_code)]

pub mod config;
mod ipfilter;
pub mod keyexchange;
pub mod nts_key_provider;
pub mod observer;
mod peer;
mod server;
pub mod sockets;
pub mod spawn;
mod system;
pub mod tracing;

use std::{error::Error, sync::Arc};

use clap::Parser;
pub use config::dynamic::ConfigUpdate;
pub use config::Config;
pub use observer::{ObservablePeerState, ObservableState};
pub use system::spawn;
//#[cfg(fuzz)]
pub use ipfilter::fuzz::fuzz_ipfilter;
use tracing_subscriber::EnvFilter;

use crate::config::CmdArgs;

pub async fn main() -> Result<(), Box<dyn Error>> {
    let args = CmdArgs::parse();
    let has_log_override = args.log_filter.is_some();
    let has_format_override = args.log_format.is_some();
    let log_filter = args
        .log_filter
        // asserts that the arc is not shared. There is no reason it would be,
        // we just use Arc to work around EnvFilter not implementing Clone
        .map(|this| Arc::try_unwrap(this).unwrap())
        .unwrap_or_else(|| EnvFilter::new("info"));

    // Setup some basic tracing now so we are able
    // to log errors when loading the full configuration.
    let finish_tracing_init = crate::tracing::init(log_filter, args.log_format.unwrap_or_default());

    let mut config = match Config::from_args(args.config, args.peers, args.servers).await {
        Ok(c) => c,
        Err(e) => {
            // print to stderr because tracing is not yet setup
            eprintln!("There was an error loading the config: {e}");
            std::process::exit(exitcode::CONFIG);
        }
    };

    // Sentry has a guard we need to keep alive, so store it.
    // The compiler will optimize this away when not using sentry.
    let tracing_state =
        match finish_tracing_init(&mut config, has_log_override, has_format_override) {
            Ok(s) => s,
            Err(e) => {
                // print to stderr because tracing was not correctly initialized
                eprintln!("Failed to complete logging setup: {e}");
                std::process::exit(exitcode::CONFIG);
            }
        };

    // Warn/error if the config is unreasonable. We do this after finishing
    // tracing setup to ensure logging is fully configured.
    config.check();

    // we always generate the keyset (even if NTS is not used)
    let keyset = crate::nts_key_provider::spawn(config.keyset).await;

    ::tracing::debug!("Configuration loaded, spawning daemon jobs");
    let (main_loop_handle, channels) = crate::spawn(
        config.system,
        config.clock,
        &config.peers,
        &config.servers,
        keyset.clone(),
    )
    .await?;

    if let Some(nts_ke_config) = config.nts_ke {
        let _join_handle = crate::keyexchange::spawn(nts_ke_config, keyset);
    }

    crate::observer::spawn(
        &config.observe,
        channels.peer_snapshots_receiver,
        channels.server_data_receiver,
        channels.system_snapshot_receiver,
    )
    .await;

    crate::config::dynamic::spawn(
        config.configure,
        channels.config_sender,
        tracing_state.reload_handle,
    )
    .await;

    Ok(main_loop_handle.await??)
}