veilid/veilid-server/src/main.rs

410 lines
13 KiB
Rust
Raw Normal View History

2021-11-22 16:28:30 +00:00
#![forbid(unsafe_code)]
2021-12-08 03:09:45 +00:00
#![deny(clippy::all)]
2023-09-17 23:37:02 +00:00
#![allow(clippy::comparison_chain, clippy::upper_case_acronyms)]
2021-12-09 21:11:52 +00:00
#![deny(unused_must_use)]
#![recursion_limit = "256"]
2021-11-22 16:28:30 +00:00
mod client_api;
2022-01-15 23:24:37 +00:00
mod server;
2021-11-22 16:28:30 +00:00
mod settings;
2022-06-28 03:46:29 +00:00
mod tools;
2022-01-15 23:24:37 +00:00
#[cfg(unix)]
mod unix;
mod veilid_logs;
#[cfg(windows)]
mod windows;
2023-08-22 21:12:23 +00:00
use crate::settings::*;
use clap::{Args, Parser};
2022-01-15 23:24:37 +00:00
use server::*;
2023-08-22 21:12:23 +00:00
use settings::LogLevel;
2023-06-03 22:33:27 +00:00
use std::collections::HashMap;
2023-09-18 19:22:40 +00:00
use std::ffi::OsString;
2023-08-22 21:12:23 +00:00
use std::path::Path;
2023-03-03 15:55:31 +00:00
use std::str::FromStr;
2022-06-28 03:46:29 +00:00
use tools::*;
2023-08-22 21:12:23 +00:00
use veilid_core::{TypedKeyGroup, TypedSecretGroup};
2022-01-15 23:24:37 +00:00
use veilid_logs::*;
2021-11-22 16:28:30 +00:00
2023-08-22 21:12:23 +00:00
#[derive(Args, Debug, Clone)]
#[group(multiple = false)]
pub struct Logging {
/// Turn on debug logging on the terminal
#[arg(long)]
debug: bool,
/// Turn on trace logging on the terminal
#[arg(long)]
trace: bool,
}
#[derive(Parser, Debug, Clone)]
#[command(author, version, about)]
pub struct CmdlineArgs {
/// Run in daemon mode in the background
#[arg(short, long)]
daemon: bool,
/// Run in the foreground
#[arg(short, long)]
foreground: bool,
/// Specify a configuration file to use
#[arg(short, long, value_name = "FILE", default_value = OsString::from(Settings::get_default_config_path()))]
config_file: Option<OsString>,
/// Specify configuration value to set (key in dot format, value in json format), eg: logging.api.enabled=true
#[arg(short, long, value_name = "CONFIG")]
set_config: Vec<String>,
/// Specify password to use to protect the device encryption key
#[arg(short, long, value_name = "PASSWORD")]
password: Option<String>,
/// Change password used to protect the device encryption key. Device storage will be migrated.
#[arg(long, value_name = "PASSWORD")]
new_password: Option<String>,
/// Do not automatically attach the server to the Veilid network
2023-08-27 21:39:50 +00:00
///
2023-08-22 21:12:23 +00:00
/// Default behaviour is to automatically attach the server to the Veilid network, this option disables this behaviour.
#[arg(long, value_name = "BOOL")]
no_attach: bool,
#[command(flatten)]
logging: Logging,
/// Turn on OpenTelemetry tracing
2023-08-27 21:39:50 +00:00
///
2023-08-22 21:12:23 +00:00
/// This option uses the GRPC OpenTelemetry protocol, not HTTP. The format for the endpoint is host:port, like 'localhost:4317'
#[arg(long, value_name = "endpoint")]
otlp: Option<String>,
/// Run as an extra daemon on the same machine for testing purposes, specify a number greater than zero to offset the listening ports
#[arg(long)]
subnode_index: Option<u16>,
/// Only generate a new keypair and print it
2023-08-27 21:39:50 +00:00
///
2023-08-22 21:12:23 +00:00
/// Generate a new keypair for a specific crypto kind and print both the key and its secret to the terminal, then exit immediately.
#[arg(long, value_name = "crypto_kind")]
generate_key_pair: Option<String>,
/// Set the node ids and secret keys
2023-08-27 21:39:50 +00:00
///
/// Specify node ids in typed key set format ('\[VLD0:xxxx,VLD1:xxxx\]') on the command line, a prompt appears to enter the secret key set interactively.
2023-08-22 21:12:23 +00:00
#[arg(long, value_name = "key_set")]
set_node_id: Option<String>,
/// Delete the entire contents of the protected store (DANGER, NO UNDO!)
#[arg(long)]
delete_protected_store: bool,
/// Delete the entire contents of the table store (DANGER, NO UNDO!)
#[arg(long)]
delete_table_store: bool,
/// Delete the entire contents of the block store (DANGER, NO UNDO!)
#[arg(long)]
delete_block_store: bool,
/// Instead of running the server, print the configuration it would use to the console
#[arg(long)]
dump_config: bool,
/// Prints the bootstrap TXT record for this node and then quits
#[arg(long)]
dump_txt_record: bool,
/// Emits a JSON-Schema for a named type
#[arg(long, value_name = "schema_name")]
emit_schema: Option<String>,
/// Specify a list of bootstrap hostnames to use
#[arg(long, value_name = "BOOTSTRAP_LIST")]
bootstrap: Option<String>,
/// panic on ctrl-c instead of graceful shutdown
#[arg(long)]
panic: bool,
/// password override to use for network isolation
#[arg(long, value_name = "KEY")]
network_key: Option<String>,
/// Wait for debugger to attach
#[cfg(debug_assertions)]
#[arg(long)]
wait_for_debug: bool,
/// enable tokio console
#[cfg(feature = "rt-tokio")]
#[arg(long)]
console: bool,
}
2022-06-10 21:07:10 +00:00
#[instrument(err)]
2022-07-07 03:15:51 +00:00
fn main() -> EyreResult<()> {
2022-06-08 01:31:05 +00:00
#[cfg(windows)]
let _ = ansi_term::enable_ansi_support();
2022-07-07 03:15:51 +00:00
color_eyre::install()?;
2022-06-08 01:31:05 +00:00
2023-08-22 21:12:23 +00:00
// Get command line options
let args = CmdlineArgs::parse();
let svc_args = args.clone();
// Check for one-off commands
#[cfg(debug_assertions)]
2023-08-27 21:39:50 +00:00
if args.wait_for_debug {
2023-08-22 21:12:23 +00:00
use bugsalot::debugger;
debugger::wait_until_attached(None).expect("state() not implemented on this platform");
}
// Attempt to load configuration
2023-09-18 19:22:40 +00:00
let settings_path: Option<OsString> = args
.config_file
.filter(|config_file| Path::new(&config_file).exists());
2023-08-22 21:12:23 +00:00
2023-09-18 19:22:40 +00:00
let settings = Settings::new(settings_path.as_deref()).wrap_err("configuration is invalid")?;
2023-08-22 21:12:23 +00:00
// write lock the settings
let mut settingsrw = settings.write();
// Set config from command line
if args.daemon {
settingsrw.daemon.enabled = true;
settingsrw.logging.terminal.enabled = false;
}
if args.foreground {
settingsrw.daemon.enabled = false;
}
if let Some(subnode_index) = args.subnode_index {
if subnode_index == 0 {
bail!("value of subnode_index should be between 1 and 65535");
}
settingsrw.testing.subnode_index = subnode_index;
};
if args.logging.debug {
settingsrw.logging.terminal.enabled = true;
settingsrw.logging.terminal.level = LogLevel::Debug;
}
if args.logging.trace {
settingsrw.logging.terminal.enabled = true;
settingsrw.logging.terminal.level = LogLevel::Trace;
}
if args.otlp.is_some() {
println!("Enabling OTLP tracing");
settingsrw.logging.otlp.enabled = true;
settingsrw.logging.otlp.grpc_endpoint = NamedSocketAddrs::from_str(
2023-08-27 21:39:50 +00:00
args.otlp
.expect("should not be null because of default missing value")
.as_str(),
2023-08-22 21:12:23 +00:00
)
.wrap_err("failed to parse OTLP address")?;
settingsrw.logging.otlp.level = LogLevel::Trace;
}
if args.no_attach {
settingsrw.auto_attach = false;
}
if args.delete_protected_store {
settingsrw.core.protected_store.delete = true;
}
if args.delete_block_store {
settingsrw.core.block_store.delete = true;
}
if args.delete_table_store {
settingsrw.core.table_store.delete = true;
}
if let Some(password) = args.password {
2023-08-27 21:39:50 +00:00
settingsrw
.core
.protected_store
.device_encryption_key_password = password;
2023-08-22 21:12:23 +00:00
}
if let Some(new_password) = args.new_password {
2023-08-27 21:39:50 +00:00
settingsrw
.core
.protected_store
.new_device_encryption_key_password = Some(new_password);
2023-08-22 21:12:23 +00:00
}
if let Some(network_key) = args.network_key {
settingsrw.core.network.network_key_password = Some(network_key);
}
if args.dump_txt_record {
// Turn off terminal logging so we can be interactive
settingsrw.logging.terminal.enabled = false;
}
let mut node_id_set = false;
if let Some(key_set) = args.set_node_id {
node_id_set = true;
// Turn off terminal logging so we can be interactive
settingsrw.logging.terminal.enabled = false;
// Split or get secret
2023-08-27 21:39:50 +00:00
let tks = TypedKeyGroup::from_str(&key_set)
.wrap_err("failed to decode node id set from command line")?;
2023-08-22 21:12:23 +00:00
let buffer = rpassword::prompt_password("Enter secret key set (will not echo): ")
.wrap_err("invalid secret key")?;
let buffer = buffer.trim().to_string();
let tss = TypedSecretGroup::from_str(&buffer).wrap_err("failed to decode secret set")?;
settingsrw.core.network.routing_table.node_id = Some(tks);
settingsrw.core.network.routing_table.node_id_secret = Some(tss);
}
if let Some(bootstrap) = args.bootstrap {
println!("Overriding bootstrap list with: ");
let mut bootstrap_list: Vec<String> = Vec::new();
for x in bootstrap.split(',') {
let x = x.trim().to_string();
if !x.is_empty() {
println!(" {}", x);
bootstrap_list.push(x);
}
}
settingsrw.core.network.routing_table.bootstrap = bootstrap_list;
};
2023-08-27 21:39:50 +00:00
2023-08-22 21:12:23 +00:00
#[cfg(feature = "rt-tokio")]
if args.console {
settingsrw.logging.console.enabled = true;
}
drop(settingsrw);
// Set specific config settings
for set_config in args.set_config {
if let Some((k, v)) = set_config.split_once('=') {
let k = k.trim();
let v = v.trim();
settings.set(k, v)?;
}
}
// Apply subnode index if we're testing
settings
.apply_subnode_index()
.wrap_err("failed to apply subnode index")?;
2021-11-22 16:28:30 +00:00
2022-01-15 23:24:37 +00:00
// --- Dump Config ---
2023-08-22 21:12:23 +00:00
if args.dump_config {
2022-01-15 23:24:37 +00:00
return serde_yaml::to_writer(std::io::stdout(), &*settings.read())
2022-07-07 03:15:51 +00:00
.wrap_err("failed to write yaml");
2022-01-15 23:24:37 +00:00
}
2022-01-19 02:21:11 +00:00
// --- Generate DHT Key ---
2023-08-22 21:12:23 +00:00
if let Some(ckstr) = args.generate_key_pair {
2023-09-18 19:22:40 +00:00
if ckstr.is_empty() {
2023-08-22 21:12:23 +00:00
let mut tks = veilid_core::TypedKeyGroup::new();
let mut tss = veilid_core::TypedSecretGroup::new();
for ck in veilid_core::VALID_CRYPTO_KINDS {
2023-08-27 21:39:50 +00:00
let tkp =
veilid_core::Crypto::generate_keypair(ck).wrap_err("invalid crypto kind")?;
2023-08-22 21:12:23 +00:00
tks.add(veilid_core::TypedKey::new(tkp.kind, tkp.value.key));
tss.add(veilid_core::TypedSecret::new(tkp.kind, tkp.value.secret));
2023-05-29 19:24:57 +00:00
}
2023-09-18 19:22:40 +00:00
println!("Public Keys:\n{}\nSecret Keys:\n{}\n", tks, tss);
2023-03-03 15:55:31 +00:00
} else {
2023-08-22 21:12:23 +00:00
let ck: veilid_core::CryptoKind =
veilid_core::FourCC::from_str(&ckstr).wrap_err("couldn't parse crypto kind")?;
2023-08-27 21:39:50 +00:00
let tkp = veilid_core::Crypto::generate_keypair(ck).wrap_err("invalid crypto kind")?;
2023-09-18 19:22:40 +00:00
println!("{}", tkp);
2023-03-03 15:55:31 +00:00
}
2023-08-22 21:12:23 +00:00
return Ok(());
2021-11-22 16:28:30 +00:00
}
2023-08-22 21:12:23 +00:00
2023-06-03 22:33:27 +00:00
// -- Emit JSON-Schema --
2023-08-22 21:12:23 +00:00
if let Some(esstr) = args.emit_schema {
let mut schemas = HashMap::<String, String>::new();
veilid_core::json_api::emit_schemas(&mut schemas);
2023-06-03 22:33:27 +00:00
2023-08-22 21:12:23 +00:00
if let Some(schema) = schemas.get(&esstr) {
println!("{}", schema);
} else {
println!("Valid schemas:");
for s in schemas.keys() {
println!(" {}", s);
}
2023-06-03 22:33:27 +00:00
}
2023-08-22 21:12:23 +00:00
return Ok(());
2023-06-03 22:33:27 +00:00
}
2021-11-22 16:28:30 +00:00
2022-05-16 15:52:48 +00:00
// See if we're just running a quick command
2023-08-22 21:12:23 +00:00
let (server_mode, success, failure) = if node_id_set {
2022-05-16 15:52:48 +00:00
(
ServerMode::ShutdownImmediate,
"Node Id and Secret set successfully",
"Failed to set Node Id and Secret",
)
2023-08-22 21:12:23 +00:00
} else if args.dump_txt_record {
2022-05-16 15:52:48 +00:00
(ServerMode::DumpTXTRecord, "", "Failed to dump txt record")
} else {
(ServerMode::Normal, "", "")
};
// Handle non-normal server modes
if !matches!(server_mode, ServerMode::Normal) {
// run the server to set the node id and quit
2022-06-28 03:46:29 +00:00
return block_on(async {
2022-06-11 22:47:58 +00:00
// Init combined console/file logger
2022-07-01 16:13:52 +00:00
let veilid_logs = VeilidLogs::setup(settings.clone())?;
2022-06-11 22:47:58 +00:00
2022-07-01 16:13:52 +00:00
run_veilid_server(settings, server_mode, veilid_logs).await
2022-06-11 22:47:58 +00:00
})
.map(|v| {
println!("{}", success);
v
})
.map_err(|e| {
println!("{}", failure);
e
});
2022-05-16 15:52:48 +00:00
}
2022-01-15 23:24:37 +00:00
// --- Daemon Mode ----
2022-05-16 15:52:48 +00:00
if settings.read().daemon.enabled {
2022-01-15 23:24:37 +00:00
cfg_if! {
if #[cfg(windows)] {
2023-08-22 21:12:23 +00:00
return windows::run_service(settings, svc_args);
2022-01-15 23:24:37 +00:00
} else if #[cfg(unix)] {
2023-08-22 21:12:23 +00:00
return unix::run_daemon(settings, svc_args);
2022-01-15 23:24:37 +00:00
}
2021-11-22 16:28:30 +00:00
}
}
2022-01-15 23:24:37 +00:00
// --- Normal Startup ---
2023-07-19 14:07:51 +00:00
let orig_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic_info| {
// invoke the default handler and exit the process
orig_hook(panic_info);
let backtrace = backtrace::Backtrace::new();
eprintln!("Backtrace:\n{:?}", backtrace);
eprintln!("exiting!");
std::process::exit(1);
}));
2023-08-22 21:12:23 +00:00
let panic_on_shutdown = args.panic;
2022-01-15 23:24:37 +00:00
ctrlc::set_handler(move || {
2022-11-03 15:28:29 +00:00
if panic_on_shutdown {
panic!("panic requested");
} else {
shutdown();
}
2022-01-15 23:24:37 +00:00
})
.expect("Error setting Ctrl-C handler");
// Run the server loop
2022-06-28 03:46:29 +00:00
block_on(async {
2022-06-11 22:47:58 +00:00
// Init combined console/file logger
2022-07-01 16:13:52 +00:00
let veilid_logs = VeilidLogs::setup(settings.clone())?;
2022-06-11 22:47:58 +00:00
2022-07-01 16:13:52 +00:00
run_veilid_server(settings, server_mode, veilid_logs).await
2022-06-11 22:47:58 +00:00
})
2021-11-22 16:28:30 +00:00
}