use crate::settings::*; use clap::{Arg, ArgMatches, Command}; use std::ffi::OsStr; use std::str::FromStr; fn do_clap_matches(default_config_path: &OsStr) -> Result { 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("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("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("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( Arg::new("generate-dht-key") .long("generate-dht-key") .help("Only generate a new dht key and print it"), ) .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("bootstrap") .long("bootstrap") .takes_value(true) .value_name("BOOTSTRAP_LIST") .help("Specify a list of bootstrap servers to use"), ) .arg( Arg::new("local") .long("local") .help("Enable local peer scope") ); #[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() -> Result<(Settings, ArgMatches), String> { // Get command line options let default_config_path = Settings::get_default_config_path(); let matches = do_clap_matches(default_config_path.as_os_str()) .map_err(|e| format!("failed to parse command line: {}", e))?; // 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 = Settings::new(if matches.occurrences_of("config-file") == 0 { None } else { Some(matches.value_of_os("config-file").unwrap()) }) .map_err(|e| format!("configuration is invalid: {}", e))?; // write lock the settings let mut settingsrw = settings.write(); // Set config from command line if matches.occurrences_of("daemon") != 0 { settingsrw.daemon = true; settingsrw.logging.terminal.enabled = false; } if matches.occurrences_of("subnode_index") != 0 { let subnode_index = match matches.value_of("subnode_index") { Some(x) => x .parse() .map_err(|e| format!("couldn't parse subnode index: {}", e))?, None => { return Err("value not specified for subnode_index".to_owned()); } }; if subnode_index == 0 { return Err("value of subnode_index should be between 1 and 65535".to_owned()); } 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.is_present("attach") { settingsrw.auto_attach = !matches!(matches.value_of("attach"), Some("true")); } if matches.is_present("local") { settingsrw.core.network.enable_local_peer_scope = 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; } if matches.occurrences_of("bootstrap") != 0 { let bootstrap = match matches.value_of("bootstrap") { Some(x) => { println!("Overriding bootstrap with: "); let mut out: Vec = Vec::new(); for x in x.split(',') { println!(" {}", x); out.push(ParsedNodeDialInfo::from_str(x).map_err(|e| { format!( "unable to parse dial info in bootstrap list: {} for {}", e, x ) })?); } out } None => { return Err("value not specified for bootstrap".to_owned()); } }; settingsrw.core.network.bootstrap = bootstrap; } // Apply subnode index if we're testing drop(settingsrw); settings .apply_subnode_index() .map_err(|_| "failed to apply subnode index".to_owned())?; Ok((settings, matches)) }