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) ]
2022-12-02 00:08:40 +00:00
#![ 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 {} \n Secret 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
}