Merge branch 'server-clap-derive' into 'main'
veilid-server with Clap v4 See merge request veilid/veilid!140
This commit is contained in:
commit
c1d11f0e33
53
Cargo.lock
generated
53
Cargo.lock
generated
@ -977,26 +977,11 @@ dependencies = [
|
|||||||
"atty",
|
"atty",
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"strsim 0.8.0",
|
"strsim 0.8.0",
|
||||||
"textwrap 0.11.0",
|
"textwrap",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
"vec_map",
|
"vec_map",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clap"
|
|
||||||
version = "3.2.25"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
|
|
||||||
dependencies = [
|
|
||||||
"atty",
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
"clap_lex 0.2.4",
|
|
||||||
"indexmap 1.9.3",
|
|
||||||
"strsim 0.10.0",
|
|
||||||
"termcolor",
|
|
||||||
"textwrap 0.16.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.3.23"
|
version = "4.3.23"
|
||||||
@ -1016,8 +1001,9 @@ checksum = "f8ce6fffb678c9b80a70b6b6de0aad31df727623a70fd9a842c30cd573e2fa98"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"clap_lex 0.5.0",
|
"clap_lex",
|
||||||
"strsim 0.10.0",
|
"strsim 0.10.0",
|
||||||
|
"terminal_size",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1032,15 +1018,6 @@ dependencies = [
|
|||||||
"syn 2.0.29",
|
"syn 2.0.29",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clap_lex"
|
|
||||||
version = "0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
|
||||||
dependencies = [
|
|
||||||
"os_str_bytes",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@ -3570,12 +3547,6 @@ dependencies = [
|
|||||||
"hashbrown 0.12.3",
|
"hashbrown 0.12.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "os_str_bytes"
|
|
||||||
version = "6.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oslog"
|
name = "oslog"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -4942,6 +4913,16 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "terminal_size"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
|
||||||
|
dependencies = [
|
||||||
|
"rustix 0.37.23",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "textwrap"
|
name = "textwrap"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
@ -4951,12 +4932,6 @@ dependencies = [
|
|||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "textwrap"
|
|
||||||
version = "0.16.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.47"
|
version = "1.0.47"
|
||||||
@ -5818,7 +5793,7 @@ dependencies = [
|
|||||||
"backtrace",
|
"backtrace",
|
||||||
"bugsalot",
|
"bugsalot",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"clap 3.2.25",
|
"clap 4.3.23",
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
"config",
|
"config",
|
||||||
"console-subscriber",
|
"console-subscriber",
|
||||||
|
@ -27,7 +27,7 @@ cursive_buffered_backend = { path = "../external/cursive_buffered_backend" }
|
|||||||
cursive_table_view = "0.14.0"
|
cursive_table_view = "0.14.0"
|
||||||
arboard = "3.2.0"
|
arboard = "3.2.0"
|
||||||
# cursive-tabs = "0.5.0"
|
# cursive-tabs = "0.5.0"
|
||||||
clap = {version= "4", features = ["derive"]}
|
clap = { version= "4", features = ["derive"] }
|
||||||
directories = "^4"
|
directories = "^4"
|
||||||
log = "^0"
|
log = "^0"
|
||||||
futures = "^0"
|
futures = "^0"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "veilid-server"
|
name = "veilid-server"
|
||||||
|
description = "Veilid Server"
|
||||||
version = "0.1.10"
|
version = "0.1.10"
|
||||||
authors = ["Veilid Team <contact@veilid.com>"]
|
authors = ["Veilid Team <contact@veilid.com>"]
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
@ -37,7 +38,7 @@ tokio-util = { version = "^0", features = ["compat"], optional = true}
|
|||||||
async-tungstenite = { version = "^0", features = ["async-tls"] }
|
async-tungstenite = { version = "^0", features = ["async-tls"] }
|
||||||
color-eyre = { version = "^0", default-features = false }
|
color-eyre = { version = "^0", default-features = false }
|
||||||
backtrace = "^0"
|
backtrace = "^0"
|
||||||
clap = "^3"
|
clap = { version= "4", features = ["derive", "string", "wrap_help"] }
|
||||||
directories = "^4"
|
directories = "^4"
|
||||||
parking_lot = "^0"
|
parking_lot = "^0"
|
||||||
config = { version = "^0", features = ["yaml"] }
|
config = { version = "^0", features = ["yaml"] }
|
||||||
|
@ -1,337 +0,0 @@
|
|||||||
use crate::settings::*;
|
|
||||||
use crate::*;
|
|
||||||
use clap::{Arg, ArgMatches, Command};
|
|
||||||
use std::ffi::OsStr;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::str::FromStr;
|
|
||||||
use veilid_core::{TypedKeyGroup, TypedSecretGroup};
|
|
||||||
|
|
||||||
fn do_clap_matches(default_config_path: &OsStr) -> Result<clap::ArgMatches, clap::Error> {
|
|
||||||
let matches = Command::new("veilid-server")
|
|
||||||
.version(env!("CARGO_PKG_VERSION"))
|
|
||||||
.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")
|
|
||||||
)
|
|
||||||
.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.")
|
|
||||||
)
|
|
||||||
.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(
|
|
||||||
Arg::new("generate-key-pair")
|
|
||||||
.long("generate-key-pair")
|
|
||||||
.takes_value(true)
|
|
||||||
.value_name("crypto_kind")
|
|
||||||
.default_missing_value("")
|
|
||||||
.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."),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new("set-node-id")
|
|
||||||
.long("set-node-id")
|
|
||||||
.takes_value(true)
|
|
||||||
.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.")
|
|
||||||
)
|
|
||||||
.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")
|
|
||||||
)
|
|
||||||
.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")
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new("bootstrap")
|
|
||||||
.long("bootstrap")
|
|
||||||
.takes_value(true)
|
|
||||||
.value_name("BOOTSTRAP_LIST")
|
|
||||||
.help("Specify a list of bootstrap hostnames to use")
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new("panic")
|
|
||||||
.long("panic")
|
|
||||||
.help("panic on ctrl-c instead of graceful shutdown"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new("network-key")
|
|
||||||
.long("network-key")
|
|
||||||
.takes_value(true)
|
|
||||||
.help("password override to use for network isolation"),
|
|
||||||
)
|
|
||||||
;
|
|
||||||
|
|
||||||
#[cfg(feature = "rt-tokio")]
|
|
||||||
let matches = matches.arg(
|
|
||||||
Arg::new("console")
|
|
||||||
.long("console")
|
|
||||||
.help("enable tokio console"),
|
|
||||||
);
|
|
||||||
|
|
||||||
#[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;
|
|
||||||
}
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
if matches.occurrences_of("network-key") != 0 {
|
|
||||||
settingsrw.core.network.network_key_password = Some(matches.value_of("network-key").unwrap().to_owned());
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
let tks =
|
|
||||||
TypedKeyGroup::from_str(v).wrap_err("failed to decode node id set from command line")?;
|
|
||||||
|
|
||||||
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 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();
|
|
||||||
if !x.is_empty() {
|
|
||||||
println!(" {}", x);
|
|
||||||
out.push(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
bail!("value not specified for bootstrap");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
settingsrw.core.network.routing_table.bootstrap = bootstrap_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "rt-tokio")]
|
|
||||||
if matches.occurrences_of("console") != 0 {
|
|
||||||
settingsrw.logging.console.enabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
@ -4,7 +4,6 @@
|
|||||||
#![recursion_limit = "256"]
|
#![recursion_limit = "256"]
|
||||||
|
|
||||||
mod client_api;
|
mod client_api;
|
||||||
mod cmdline;
|
|
||||||
mod server;
|
mod server;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod tools;
|
mod tools;
|
||||||
@ -14,82 +13,337 @@ mod veilid_logs;
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
mod windows;
|
mod windows;
|
||||||
|
|
||||||
|
use crate::settings::*;
|
||||||
|
|
||||||
|
use clap::{Args, Parser};
|
||||||
use server::*;
|
use server::*;
|
||||||
|
use settings::LogLevel;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::ffi::{OsString, OsStr};
|
||||||
|
use std::path::Path;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use tools::*;
|
use tools::*;
|
||||||
|
use veilid_core::{TypedKeyGroup, TypedSecretGroup};
|
||||||
use veilid_logs::*;
|
use veilid_logs::*;
|
||||||
|
|
||||||
|
#[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
|
||||||
|
///
|
||||||
|
/// 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
|
||||||
|
///
|
||||||
|
/// 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
|
||||||
|
///
|
||||||
|
/// 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
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
#[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,
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(err)]
|
#[instrument(err)]
|
||||||
fn main() -> EyreResult<()> {
|
fn main() -> EyreResult<()> {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let _ = ansi_term::enable_ansi_support();
|
let _ = ansi_term::enable_ansi_support();
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
|
|
||||||
let (settings, matches) = cmdline::process_command_line()?;
|
// Get command line options
|
||||||
|
let args = CmdlineArgs::parse();
|
||||||
|
|
||||||
|
let svc_args = args.clone();
|
||||||
|
|
||||||
|
// Check for one-off commands
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
if args.wait_for_debug{
|
||||||
|
use bugsalot::debugger;
|
||||||
|
debugger::wait_until_attached(None).expect("state() not implemented on this platform");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to load configuration
|
||||||
|
let settings_path: Option<&OsStr> = if let Some(config_file) = &args.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 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(
|
||||||
|
args.otlp.expect("should not be null because of default missing value").as_str(),
|
||||||
|
)
|
||||||
|
.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 {
|
||||||
|
settingsrw.core.protected_store.device_encryption_key_password = password;
|
||||||
|
}
|
||||||
|
if let Some(new_password) = args.new_password {
|
||||||
|
settingsrw.core.protected_store.new_device_encryption_key_password = Some(new_password);
|
||||||
|
}
|
||||||
|
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
|
||||||
|
let tks =
|
||||||
|
TypedKeyGroup::from_str(&key_set).wrap_err("failed to decode node id set from command line")?;
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
#[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")?;
|
||||||
|
|
||||||
// --- Dump Config ---
|
// --- Dump Config ---
|
||||||
if matches.occurrences_of("dump-config") != 0 {
|
if args.dump_config {
|
||||||
return serde_yaml::to_writer(std::io::stdout(), &*settings.read())
|
return serde_yaml::to_writer(std::io::stdout(), &*settings.read())
|
||||||
.wrap_err("failed to write yaml");
|
.wrap_err("failed to write yaml");
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Generate DHT Key ---
|
// --- Generate DHT Key ---
|
||||||
if matches.occurrences_of("generate-key-pair") != 0 {
|
if let Some(ckstr) = args.generate_key_pair {
|
||||||
if let Some(ckstr) = matches.get_one::<String>("generate-key-pair") {
|
if ckstr == "" {
|
||||||
if ckstr == "" {
|
let mut tks = veilid_core::TypedKeyGroup::new();
|
||||||
let mut tks = veilid_core::TypedKeyGroup::new();
|
let mut tss = veilid_core::TypedSecretGroup::new();
|
||||||
let mut tss = veilid_core::TypedSecretGroup::new();
|
for ck in veilid_core::VALID_CRYPTO_KINDS {
|
||||||
for ck in veilid_core::VALID_CRYPTO_KINDS {
|
let tkp = veilid_core::Crypto::generate_keypair(ck)
|
||||||
let tkp = veilid_core::Crypto::generate_keypair(ck)
|
.wrap_err("invalid crypto kind")?;
|
||||||
.wrap_err("invalid crypto kind")?;
|
tks.add(veilid_core::TypedKey::new(tkp.kind, tkp.value.key));
|
||||||
tks.add(veilid_core::TypedKey::new(tkp.kind, tkp.value.key));
|
tss.add(veilid_core::TypedSecret::new(tkp.kind, tkp.value.secret));
|
||||||
tss.add(veilid_core::TypedSecret::new(tkp.kind, tkp.value.secret));
|
|
||||||
}
|
|
||||||
println!(
|
|
||||||
"Public Keys:\n{}\nSecret Keys:\n{}\n",
|
|
||||||
tks.to_string(),
|
|
||||||
tss.to_string()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
let ck: veilid_core::CryptoKind =
|
|
||||||
veilid_core::FourCC::from_str(ckstr).wrap_err("couldn't parse crypto kind")?;
|
|
||||||
let tkp =
|
|
||||||
veilid_core::Crypto::generate_keypair(ck).wrap_err("invalid crypto kind")?;
|
|
||||||
println!("{}", tkp.to_string());
|
|
||||||
}
|
}
|
||||||
return Ok(());
|
println!(
|
||||||
|
"Public Keys:\n{}\nSecret Keys:\n{}\n",
|
||||||
|
tks.to_string(),
|
||||||
|
tss.to_string()
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
bail!("missing crypto kind");
|
let ck: veilid_core::CryptoKind =
|
||||||
|
veilid_core::FourCC::from_str(&ckstr).wrap_err("couldn't parse crypto kind")?;
|
||||||
|
let tkp =
|
||||||
|
veilid_core::Crypto::generate_keypair(ck).wrap_err("invalid crypto kind")?;
|
||||||
|
println!("{}", tkp.to_string());
|
||||||
}
|
}
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Emit JSON-Schema --
|
// -- Emit JSON-Schema --
|
||||||
if matches.occurrences_of("emit-schema") != 0 {
|
if let Some(esstr) = args.emit_schema {
|
||||||
if let Some(esstr) = matches.value_of("emit-schema") {
|
let mut schemas = HashMap::<String, String>::new();
|
||||||
let mut schemas = HashMap::<String, String>::new();
|
veilid_core::json_api::emit_schemas(&mut schemas);
|
||||||
veilid_core::json_api::emit_schemas(&mut schemas);
|
|
||||||
|
|
||||||
if let Some(schema) = schemas.get(esstr) {
|
if let Some(schema) = schemas.get(&esstr) {
|
||||||
println!("{}", schema);
|
println!("{}", schema);
|
||||||
} else {
|
} else {
|
||||||
println!("Valid schemas:");
|
println!("Valid schemas:");
|
||||||
for s in schemas.keys() {
|
for s in schemas.keys() {
|
||||||
println!(" {}", s);
|
println!(" {}", s);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if we're just running a quick command
|
// See if we're just running a quick command
|
||||||
let (server_mode, success, failure) = if matches.occurrences_of("set-node-id") != 0 {
|
let (server_mode, success, failure) = if node_id_set {
|
||||||
(
|
(
|
||||||
ServerMode::ShutdownImmediate,
|
ServerMode::ShutdownImmediate,
|
||||||
"Node Id and Secret set successfully",
|
"Node Id and Secret set successfully",
|
||||||
"Failed to set Node Id and Secret",
|
"Failed to set Node Id and Secret",
|
||||||
)
|
)
|
||||||
} else if matches.occurrences_of("dump-txt-record") != 0 {
|
} else if args.dump_txt_record {
|
||||||
(ServerMode::DumpTXTRecord, "", "Failed to dump txt record")
|
(ServerMode::DumpTXTRecord, "", "Failed to dump txt record")
|
||||||
} else {
|
} else {
|
||||||
(ServerMode::Normal, "", "")
|
(ServerMode::Normal, "", "")
|
||||||
@ -118,9 +372,9 @@ fn main() -> EyreResult<()> {
|
|||||||
if settings.read().daemon.enabled {
|
if settings.read().daemon.enabled {
|
||||||
cfg_if! {
|
cfg_if! {
|
||||||
if #[cfg(windows)] {
|
if #[cfg(windows)] {
|
||||||
return windows::run_service(settings, matches);
|
return windows::run_service(settings, svc_args);
|
||||||
} else if #[cfg(unix)] {
|
} else if #[cfg(unix)] {
|
||||||
return unix::run_daemon(settings, matches);
|
return unix::run_daemon(settings, svc_args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,7 +392,7 @@ fn main() -> EyreResult<()> {
|
|||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let panic_on_shutdown = matches.occurrences_of("panic") != 0;
|
let panic_on_shutdown = args.panic;
|
||||||
ctrlc::set_handler(move || {
|
ctrlc::set_handler(move || {
|
||||||
if panic_on_shutdown {
|
if panic_on_shutdown {
|
||||||
panic!("panic requested");
|
panic!("panic requested");
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![allow(clippy::bool_assert_comparison)]
|
#![allow(clippy::bool_assert_comparison)]
|
||||||
|
|
||||||
|
use clap::ValueEnum;
|
||||||
use directories::*;
|
use directories::*;
|
||||||
|
|
||||||
use crate::tools::*;
|
use crate::tools::*;
|
||||||
@ -229,7 +230,7 @@ pub fn load_config(cfg: config::Config, config_file: &Path) -> EyreResult<config
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq, ValueEnum)]
|
||||||
pub enum LogLevel {
|
pub enum LogLevel {
|
||||||
Off,
|
Off,
|
||||||
Error,
|
Error,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
use crate::*;
|
||||||
use crate::server::*;
|
use crate::server::*;
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
use crate::tools::*;
|
use crate::tools::*;
|
||||||
use crate::veilid_logs::*;
|
use crate::veilid_logs::*;
|
||||||
use clap::ArgMatches;
|
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use signal_hook::consts::signal::*;
|
use signal_hook::consts::signal::*;
|
||||||
use signal_hook_async_std::Signals;
|
use signal_hook_async_std::Signals;
|
||||||
@ -26,7 +26,7 @@ async fn handle_signals(mut signals: Signals) {
|
|||||||
|
|
||||||
#[warn(missing_docs)]
|
#[warn(missing_docs)]
|
||||||
#[instrument(err)]
|
#[instrument(err)]
|
||||||
pub fn run_daemon(settings: Settings, _matches: ArgMatches) -> EyreResult<()> {
|
pub fn run_daemon(settings: Settings, _args: CmdlineArgs) -> EyreResult<()> {
|
||||||
let daemon = {
|
let daemon = {
|
||||||
let mut daemon = daemonize::Daemonize::new();
|
let mut daemon = daemonize::Daemonize::new();
|
||||||
let s = settings.read();
|
let s = settings.read();
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use crate::settings::*;
|
use crate::settings::*;
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use clap::ArgMatches;
|
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tracing::*;
|
use tracing::*;
|
||||||
@ -12,7 +11,7 @@ use windows_service::*;
|
|||||||
|
|
||||||
// Register generated `ffi_service_main` with the system and start the service, blocking
|
// Register generated `ffi_service_main` with the system and start the service, blocking
|
||||||
// this thread until the service is stopped.
|
// this thread until the service is stopped.
|
||||||
pub fn run_service(settings: Settings, matches: ArgMatches) -> EyreResult<()> {
|
pub fn run_service(settings: Settings, _args: CmdlineArgs) -> EyreResult<()> {
|
||||||
eprintln!("Windows Service mode not implemented yet.");
|
eprintln!("Windows Service mode not implemented yet.");
|
||||||
|
|
||||||
//service_dispatcher::start("veilid-server", ffi_veilid_service_main)?;
|
//service_dispatcher::start("veilid-server", ffi_veilid_service_main)?;
|
||||||
|
Loading…
Reference in New Issue
Block a user