2022-08-23 17:30:49 +00:00
use crate ::settings ::* ;
use crate ::* ;
use clap ::{ Arg , ArgMatches , Command } ;
use std ::ffi ::OsStr ;
use std ::path ::Path ;
use std ::str ::FromStr ;
2023-03-01 20:50:30 +00:00
use veilid_core ::{ TypedKeySet , TypedSecretSet } ;
2022-08-23 17:30:49 +00:00
fn do_clap_matches ( default_config_path : & OsStr ) -> Result < clap ::ArgMatches , clap ::Error > {
let matches = Command ::new ( " veilid-server " )
. version ( " 0.1 " )
. about ( " Veilid Server " )
. color ( clap ::ColorChoice ::Auto )
. arg (
Arg ::new ( " daemon " )
. long ( " daemon " )
. short ( 'd' )
. help ( " Run in daemon mode in the background " ) ,
)
. arg (
Arg ::new ( " foreground " )
. long ( " foreground " )
. short ( 'f' )
. conflicts_with ( " daemon " )
. help ( " Run in the foreground " ) ,
)
. arg (
Arg ::new ( " config-file " )
. short ( 'c' )
. long ( " config-file " )
. takes_value ( true )
. value_name ( " FILE " )
. default_value_os ( default_config_path )
. allow_invalid_utf8 ( true )
. help ( " Specify a configuration file to use " ) ,
)
. arg (
Arg ::new ( " set-config " )
. short ( 's' )
. long ( " set-config " )
. takes_value ( true )
. multiple_occurrences ( true )
. help ( " Specify configuration value to set (key in dot format, value in json format), eg: logging.api.enabled=true " )
)
2023-05-29 19:24:57 +00:00
. arg (
Arg ::new ( " password " )
. short ( 'p' )
. long ( " password " )
. takes_value ( true )
. help ( " Specify password to use to protect the device encryption key " )
)
. arg (
Arg ::new ( " new-password " )
. long ( " new-password " )
. takes_value ( true )
. help ( " Change password used to protect the device encryption key. Device storage will be migrated. " )
)
2022-08-23 17:30:49 +00:00
. arg (
Arg ::new ( " attach " )
. long ( " attach " )
. takes_value ( true )
. value_name ( " BOOL " )
. possible_values ( & [ " false " , " true " ] )
. help ( " Automatically attach the server to the Veilid network " ) ,
)
// Dev options
. arg (
Arg ::new ( " debug " )
. long ( " debug " )
. help ( " Turn on debug logging on the terminal " ) ,
)
. arg (
Arg ::new ( " trace " )
. long ( " trace " )
. conflicts_with ( " debug " )
. help ( " Turn on trace logging on the terminal " ) ,
)
. arg (
Arg ::new ( " otlp " )
. long ( " otlp " )
. takes_value ( true )
. value_name ( " endpoint " )
. default_missing_value ( " localhost:4317 " )
. help ( " Turn on OpenTelemetry tracing " )
. long_help ( " This option uses the GRPC OpenTelemetry protocol, not HTTP. The format for the endpoint is host:port, like 'localhost:4317' " ) ,
)
. arg (
Arg ::new ( " subnode-index " )
. long ( " subnode-index " )
. takes_value ( true )
. help ( " Run as an extra daemon on the same machine for testing purposes, specify a number greater than zero to offset the listening ports " ) ,
)
. arg (
2023-03-01 20:50:30 +00:00
Arg ::new ( " generate-key-pair " )
. long ( " generate-key-pair " )
. takes_value ( true )
. value_name ( " crypto_kind " )
2023-05-29 19:24:57 +00:00
. default_missing_value ( " " )
2023-03-01 20:50:30 +00:00
. help ( " Only generate a new keypair and print it " )
. long_help ( " Generate a new keypair for a specific crypto kind and print both the key and its secret to the terminal, then exit immediately. " ) ,
2022-08-23 17:30:49 +00:00
)
. arg (
Arg ::new ( " set-node-id " )
. long ( " set-node-id " )
. takes_value ( true )
2023-03-01 20:50:30 +00:00
. value_name ( " key_set " )
. help ( " Set the node ids and secret keys " )
. long_help ( " 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. " )
2022-08-23 17:30:49 +00:00
)
. arg (
Arg ::new ( " delete-protected-store " )
. long ( " delete-protected-store " )
. help ( " Delete the entire contents of the protected store (DANGER, NO UNDO!) " ) ,
)
. arg (
Arg ::new ( " delete-table-store " )
. long ( " delete-table-store " )
. help ( " Delete the entire contents of the table store (DANGER, NO UNDO!) " ) ,
)
. arg (
Arg ::new ( " delete-block-store " )
. long ( " delete-block-store " )
. help ( " Delete the entire contents of the block store (DANGER, NO UNDO!) " ) ,
)
. arg (
Arg ::new ( " dump-config " )
. long ( " dump-config " )
. help ( " Instead of running the server, print the configuration it would use to the console " ) ,
)
. arg (
Arg ::new ( " dump-txt-record " )
. long ( " dump-txt-record " )
. help ( " Prints the bootstrap TXT record for this node and then quits " )
)
2023-06-03 22:33:27 +00:00
. arg (
Arg ::new ( " emit-schema " )
. long ( " emit-schema " )
. takes_value ( true )
. value_name ( " schema_name " )
. default_missing_value ( " " )
. help ( " Emits a JSON-Schema for a named type " )
)
2022-08-23 17:30:49 +00:00
. arg (
Arg ::new ( " bootstrap " )
. long ( " bootstrap " )
. takes_value ( true )
. value_name ( " BOOTSTRAP_LIST " )
. help ( " Specify a list of bootstrap hostnames to use " )
)
2022-11-03 15:28:29 +00:00
. arg (
Arg ::new ( " panic " )
. long ( " panic " )
. help ( " panic on ctrl-c instead of graceful shutdown " ) ,
)
2022-09-03 17:57:25 +00:00
;
2022-08-23 17:30:49 +00:00
2022-11-03 15:28:29 +00:00
#[ cfg(feature = " rt-tokio " ) ]
let matches = matches . arg (
Arg ::new ( " console " )
. long ( " console " )
. help ( " enable tokio console " ) ,
) ;
2022-08-23 17:30:49 +00:00
#[ cfg(debug_assertions) ]
let matches = matches . arg (
Arg ::new ( " wait-for-debug " )
. long ( " wait-for-debug " )
. help ( " Wait for debugger to attach " ) ,
) ;
Ok ( matches . get_matches ( ) )
}
pub fn process_command_line ( ) -> EyreResult < ( Settings , ArgMatches ) > {
// Get command line options
let default_config_path = Settings ::get_default_config_path ( ) ;
let matches = do_clap_matches ( default_config_path . as_os_str ( ) )
. wrap_err ( " failed to parse command line: {} " ) ? ;
// Check for one-off commands
#[ cfg(debug_assertions) ]
if matches . occurrences_of ( " wait-for-debug " ) ! = 0 {
use bugsalot ::debugger ;
debugger ::wait_until_attached ( None ) . expect ( " state() not implemented on this platform " ) ;
}
// Attempt to load configuration
let settings_path = if let Some ( config_file ) = matches . value_of_os ( " config-file " ) {
if Path ::new ( config_file ) . exists ( ) {
Some ( config_file )
} else {
None
}
} else {
None
} ;
let settings = Settings ::new ( settings_path ) . wrap_err ( " configuration is invalid " ) ? ;
// write lock the settings
let mut settingsrw = settings . write ( ) ;
// Set config from command line
if matches . occurrences_of ( " daemon " ) ! = 0 {
settingsrw . daemon . enabled = true ;
settingsrw . logging . terminal . enabled = false ;
}
if matches . occurrences_of ( " foreground " ) ! = 0 {
settingsrw . daemon . enabled = false ;
}
if matches . occurrences_of ( " subnode-index " ) ! = 0 {
let subnode_index = match matches . value_of ( " subnode-index " ) {
Some ( x ) = > x . parse ( ) . wrap_err ( " couldn't parse subnode index " ) ? ,
None = > {
bail! ( " value not specified for subnode-index " ) ;
}
} ;
if subnode_index = = 0 {
bail! ( " value of subnode_index should be between 1 and 65535 " ) ;
}
settingsrw . testing . subnode_index = subnode_index ;
}
if matches . occurrences_of ( " debug " ) ! = 0 {
settingsrw . logging . terminal . enabled = true ;
settingsrw . logging . terminal . level = LogLevel ::Debug ;
}
if matches . occurrences_of ( " trace " ) ! = 0 {
settingsrw . logging . terminal . enabled = true ;
settingsrw . logging . terminal . level = LogLevel ::Trace ;
}
if matches . occurrences_of ( " otlp " ) ! = 0 {
settingsrw . logging . otlp . enabled = true ;
settingsrw . logging . otlp . grpc_endpoint = NamedSocketAddrs ::from_str (
& matches
. value_of ( " otlp " )
. expect ( " should not be null because of default missing value " )
. to_string ( ) ,
)
. wrap_err ( " failed to parse OTLP address " ) ? ;
settingsrw . logging . otlp . level = LogLevel ::Trace ;
}
if matches . is_present ( " attach " ) {
settingsrw . auto_attach = ! matches! ( matches . value_of ( " attach " ) , Some ( " true " ) ) ;
}
if matches . occurrences_of ( " delete-protected-store " ) ! = 0 {
settingsrw . core . protected_store . delete = true ;
}
if matches . occurrences_of ( " delete-block-store " ) ! = 0 {
settingsrw . core . block_store . delete = true ;
}
if matches . occurrences_of ( " delete-table-store " ) ! = 0 {
settingsrw . core . table_store . delete = true ;
}
2023-05-29 19:24:57 +00:00
if matches . occurrences_of ( " password " ) ! = 0 {
settingsrw . core . protected_store . device_encryption_key_password = matches . value_of ( " password " ) . unwrap ( ) . to_owned ( ) ;
}
if matches . occurrences_of ( " new-password " ) ! = 0 {
settingsrw . core . protected_store . new_device_encryption_key_password = Some ( matches . value_of ( " new-password " ) . unwrap ( ) . to_owned ( ) ) ;
}
2022-08-23 17:30:49 +00:00
if matches . occurrences_of ( " dump-txt-record " ) ! = 0 {
// Turn off terminal logging so we can be interactive
settingsrw . logging . terminal . enabled = false ;
}
if let Some ( v ) = matches . value_of ( " set-node-id " ) {
// Turn off terminal logging so we can be interactive
settingsrw . logging . terminal . enabled = false ;
// Split or get secret
2023-03-01 02:11:26 +00:00
let tks =
TypedKeySet ::from_str ( v ) . wrap_err ( " failed to decode node id set from command line " ) ? ;
2023-03-01 20:50:30 +00:00
let buffer = rpassword ::prompt_password ( " Enter secret key set (will not echo): " )
2023-03-01 02:11:26 +00:00
. wrap_err ( " invalid secret key " ) ? ;
let buffer = buffer . trim ( ) . to_string ( ) ;
2023-03-01 20:50:30 +00:00
let tss = TypedSecretSet ::from_str ( & buffer ) . wrap_err ( " failed to decode secret set " ) ? ;
2023-03-01 02:11:26 +00:00
2023-03-01 20:50:30 +00:00
settingsrw . core . network . routing_table . node_id = Some ( tks ) ;
settingsrw . core . network . routing_table . node_id_secret = Some ( tss ) ;
2022-08-23 17:30:49 +00:00
}
if matches . occurrences_of ( " bootstrap " ) ! = 0 {
let bootstrap_list = match matches . value_of ( " bootstrap " ) {
Some ( x ) = > {
println! ( " Overriding bootstrap list with: " ) ;
let mut out : Vec < String > = Vec ::new ( ) ;
for x in x . split ( ',' ) {
let x = x . trim ( ) . to_string ( ) ;
println! ( " {} " , x ) ;
out . push ( x ) ;
}
out
}
None = > {
bail! ( " value not specified for bootstrap " ) ;
}
} ;
2023-03-01 20:50:30 +00:00
settingsrw . core . network . routing_table . bootstrap = bootstrap_list ;
2022-08-23 17:30:49 +00:00
}
2022-11-16 17:49:53 +00:00
#[ cfg(feature = " rt-tokio " ) ]
2022-11-03 15:28:29 +00:00
if matches . occurrences_of ( " console " ) ! = 0 {
settingsrw . logging . console . enabled = true ;
}
2022-08-23 17:30:49 +00:00
drop ( settingsrw ) ;
// Set specific config settings
if let Some ( set_configs ) = matches . values_of ( " set-config " ) {
for set_config in set_configs {
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 " ) ? ;
Ok ( ( settings , matches ) )
}