arti_relay/
main.rs

1//! A relay binary use to join the Tor network to relay anonymous communication.
2//!
3//! NOTE: This binary is still highly experimental as in in active development, not stable and
4//! without any type of guarantee of running or even working.
5
6// @@ begin lint list maintained by maint/add_warning @@
7#![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
8#![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
9#![warn(missing_docs)]
10#![warn(noop_method_call)]
11#![warn(unreachable_pub)]
12#![warn(clippy::all)]
13#![deny(clippy::await_holding_lock)]
14#![deny(clippy::cargo_common_metadata)]
15#![deny(clippy::cast_lossless)]
16#![deny(clippy::checked_conversions)]
17#![warn(clippy::cognitive_complexity)]
18#![deny(clippy::debug_assert_with_mut_call)]
19#![deny(clippy::exhaustive_enums)]
20#![deny(clippy::exhaustive_structs)]
21#![deny(clippy::expl_impl_clone_on_copy)]
22#![deny(clippy::fallible_impl_from)]
23#![deny(clippy::implicit_clone)]
24#![deny(clippy::large_stack_arrays)]
25#![warn(clippy::manual_ok_or)]
26#![deny(clippy::missing_docs_in_private_items)]
27#![warn(clippy::needless_borrow)]
28#![warn(clippy::needless_pass_by_value)]
29#![warn(clippy::option_option)]
30#![deny(clippy::print_stderr)]
31#![deny(clippy::print_stdout)]
32#![warn(clippy::rc_buffer)]
33#![deny(clippy::ref_option_ref)]
34#![warn(clippy::semicolon_if_nothing_returned)]
35#![warn(clippy::trait_duplication_in_bounds)]
36#![deny(clippy::unchecked_duration_subtraction)]
37#![deny(clippy::unnecessary_wraps)]
38#![warn(clippy::unseparated_literal_suffix)]
39#![deny(clippy::unwrap_used)]
40#![deny(clippy::mod_module_files)]
41#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
42#![allow(clippy::uninlined_format_args)]
43#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
44#![allow(clippy::result_large_err)] // temporary workaround for arti#587
45#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
46#![allow(clippy::needless_lifetimes)] // See arti#1765
47//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
48
49mod cli;
50mod config;
51mod err;
52mod relay;
53
54use std::io::IsTerminal as _;
55
56use anyhow::Context;
57use clap::Parser;
58use safelog::with_safe_logging_suppressed;
59use tor_rtcompat::{PreferredRuntime, Runtime};
60use tracing_subscriber::filter::EnvFilter;
61use tracing_subscriber::util::SubscriberInitExt;
62use tracing_subscriber::FmtSubscriber;
63
64use crate::config::{base_resolver, TorRelayConfig, DEFAULT_LOG_LEVEL};
65use crate::relay::TorRelay;
66
67fn main() {
68    // Will exit if '--help' used or there's a parse error.
69    let cli = cli::Cli::parse();
70
71    if let Err(e) = main_main(cli) {
72        // TODO: Use arti_client's `HintableError` here (see `arti::main`)?
73        // TODO: Why do we suppress safe logging, and squash the anyhow result into a single line?
74        // TODO: Do we want to log the error?
75        with_safe_logging_suppressed(|| tor_error::report_and_exit::<_, ()>(e));
76    }
77}
78
79/// The real main without the error formatting.
80fn main_main(cli: cli::Cli) -> anyhow::Result<()> {
81    // Register a basic stderr logger until we have enough info to configure the main logger.
82    // Unlike arti, we enable timestamps for this pre-config logger.
83    // TODO: Consider using timestamps with reduced-granularity (see `LogPrecision`).
84    let level: tracing::metadata::Level = cli
85        .global
86        .log_level
87        .map(Into::into)
88        .unwrap_or(DEFAULT_LOG_LEVEL);
89    let filter = EnvFilter::builder()
90        .with_default_directive(level.into())
91        .parse("")
92        .expect("empty filter directive should be trivially parsable");
93    #[allow(clippy::print_stderr)]
94    FmtSubscriber::builder()
95        .with_env_filter(filter)
96        .with_ansi(std::io::stderr().is_terminal())
97        .with_writer(|| {
98            eprint!("arti-relay: ");
99            std::io::stderr()
100        })
101        .finish()
102        .init();
103
104    match cli.command {
105        #[allow(clippy::print_stdout)]
106        cli::Commands::BuildInfo => {
107            println!("Version: {}", env!("CARGO_PKG_VERSION"));
108            // these are set by our build script
109            println!("Features: {}", env!("BUILD_FEATURES"));
110            println!("Profile: {}", env!("BUILD_PROFILE"));
111            println!("Debug: {}", env!("BUILD_DEBUG"));
112            println!("Optimization level: {}", env!("BUILD_OPT_LEVEL"));
113            println!("Rust version: {}", env!("BUILD_RUSTC_VERSION"));
114            println!("Target triple: {}", env!("BUILD_TARGET"));
115            println!("Host triple: {}", env!("BUILD_HOST"));
116        }
117        cli::Commands::Run(args) => start_relay(args, cli.global)?,
118    }
119
120    Ok(())
121}
122
123/// Initialize and start the relay.
124// Pass by value so that we don't need to clone fields, which keeps the code simpler.
125#[allow(clippy::needless_pass_by_value)]
126fn start_relay(_args: cli::RunArgs, global_args: cli::GlobalArgs) -> anyhow::Result<()> {
127    let runtime = init_runtime().context("Failed to initialize the runtime")?;
128
129    let mut cfg_sources = global_args
130        .config()
131        .context("Failed to get configuration sources")?;
132
133    // A Mistrust object to use for loading our configuration.
134    // Elsewhere, we use the value _from_ the configuration.
135    let cfg_mistrust = if global_args.disable_fs_permission_checks {
136        fs_mistrust::Mistrust::new_dangerously_trust_everyone()
137    } else {
138        fs_mistrust::MistrustBuilder::default()
139            // By default, a `Mistrust` checks an environment variable.
140            // We do not (at the moment) want this behaviour for relays:
141            // https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/2699#note_3147502
142            .ignore_environment()
143            .build()
144            .expect("default fs-mistrust should be buildable")
145    };
146
147    cfg_sources.set_mistrust(cfg_mistrust);
148
149    let cfg = cfg_sources
150        .load()
151        .context("Failed to load configuration sources")?;
152    let config =
153        tor_config::resolve::<TorRelayConfig>(cfg).context("Failed to resolve configuration")?;
154
155    // TODO: Configure a proper logger, not just a simple stderr logger.
156    // TODO: We may want this to be the global logger, but if we use arti's `setup_logging` in the
157    // future, it returns a `LogGuards` which we'd have no way of holding on to until the
158    // application exits (see https://gitlab.torproject.org/tpo/core/arti/-/issues/1791).
159    let filter = EnvFilter::builder()
160        .parse(&config.logging.console)
161        .with_context(|| {
162            format!(
163                "Failed to parse console logging directive {:?}",
164                config.logging.console,
165            )
166        })?;
167    #[allow(clippy::print_stderr)]
168    let logger = tracing_subscriber::FmtSubscriber::builder()
169        .with_env_filter(filter)
170        .with_ansi(std::io::stderr().is_terminal())
171        .with_writer(|| {
172            eprint!("arti-relay: ");
173            std::io::stderr()
174        })
175        .finish();
176    let logger = tracing::Dispatch::new(logger);
177
178    tracing::dispatcher::with_default(&logger, || {
179        let path_resolver = base_resolver();
180        let relay =
181            TorRelay::new(runtime, &config, path_resolver).context("Failed to initialize relay")?;
182        run_relay(relay)
183    })?;
184
185    Ok(())
186}
187
188/// Run the relay.
189#[allow(clippy::unnecessary_wraps)] // TODO: not implemented yet; remove me
190fn run_relay<R: Runtime>(_relay: TorRelay<R>) -> anyhow::Result<()> {
191    Ok(())
192}
193
194/// Initialize a runtime.
195///
196/// Any commands that need a runtime should call this so that we use a consistent runtime.
197fn init_runtime() -> std::io::Result<impl Runtime> {
198    // Use the tokio runtime from tor_rtcompat unless we later find a reason to use tokio directly;
199    // see https://gitlab.torproject.org/tpo/core/arti/-/work_items/1744
200    PreferredRuntime::create()
201}