refactoring, more config, packaging

This commit is contained in:
John Smith
2022-05-16 11:52:48 -04:00
parent 444f65d76d
commit ef1f5d7b52
42 changed files with 1329 additions and 368 deletions

View File

@@ -36,16 +36,26 @@ fn convert_update(
mut rpc_update: crate::veilid_client_capnp::veilid_update::Builder,
) {
match update {
veilid_core::VeilidUpdate::Log {
veilid_core::VeilidUpdate::Log(veilid_core::VeilidStateLog {
log_level: _,
message: _,
} => {
}) => {
panic!("Should not be logging to api in server!");
}
veilid_core::VeilidUpdate::Attachment { state } => {
veilid_core::VeilidUpdate::Attachment(veilid_core::VeilidStateAttachment { state }) => {
let mut att = rpc_update.init_attachment();
att.set_state(convert_attachment_state(state));
}
veilid_core::VeilidUpdate::Network(veilid_core::VeilidStateNetwork {
started,
bps_down,
bps_up,
}) => {
let mut nb = rpc_update.init_network();
nb.set_started(*started);
nb.set_bps_down(*bps_down);
nb.set_bps_up(*bps_up);
}
veilid_core::VeilidUpdate::Shutdown => {
rpc_update.set_shutdown(());
}
@@ -54,11 +64,15 @@ fn convert_update(
fn convert_state(
state: &veilid_core::VeilidState,
rpc_state: crate::veilid_client_capnp::veilid_state::Builder,
mut rpc_state: crate::veilid_client_capnp::veilid_state::Builder,
) {
rpc_state
.init_attachment()
.set_state(convert_attachment_state(&state.attachment));
let mut ab = rpc_state.reborrow().init_attachment();
ab.set_state(convert_attachment_state(&state.attachment.state));
let mut nb = rpc_state.reborrow().init_network();
nb.set_started(state.network.started);
nb.set_bps_down(state.network.bps_down);
nb.set_bps_up(state.network.bps_up);
}
// --- interface Registration ---------------------------------

View File

@@ -2,6 +2,7 @@ use crate::settings::*;
use clap::{Arg, ArgMatches, Command};
use std::ffi::OsStr;
use std::str::FromStr;
use veilid_core::{DHTKey, DHTKeySecret};
fn do_clap_matches(default_config_path: &OsStr) -> Result<clap::ArgMatches, clap::Error> {
let matches = Command::new("veilid-server")
@@ -44,8 +45,8 @@ fn do_clap_matches(default_config_path: &OsStr) -> Result<clap::ArgMatches, clap
.help("Turn on trace logging on the terminal"),
)
.arg(
Arg::new("subnode_index")
.long("subnode_index")
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"),
)
@@ -54,6 +55,14 @@ fn do_clap_matches(default_config_path: &OsStr) -> Result<clap::ArgMatches, clap
.long("generate-dht-key")
.help("Only generate a new dht key and print it"),
)
.arg(
Arg::new("set-node-id")
.long("set-node-id")
.takes_value(true)
.value_name("ID")
.help("Set the node id and secret key")
.long_help("To specify both node id and secret key on the command line, use a ID:SECRET syntax with a colon, like:\n zsVXz5aTU98vZxwTcDmvpcnO5g1B2jRO3wpdNiDrRgw:gJzQLmzuBvA-dFvEmLcYvLoO5bh7hzCWFzfpJHapZKg\nIf no colon is used, the node id is specified, and a prompt appears to enter the secret key interactively.")
)
.arg(
Arg::new("delete-protected-store")
.long("delete-protected-store")
@@ -74,12 +83,25 @@ fn do_clap_matches(default_config_path: &OsStr) -> Result<clap::ArgMatches, clap
.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("bootstrap")
.long("bootstrap")
.takes_value(true)
.value_name("BOOTSTRAP_LIST")
.help("Specify a list of bootstrap servers to use"),
.help("Specify a list of bootstrap hostnames to use")
)
.arg(
Arg::new("bootstrap-nodes")
.conflicts_with("bootstrap")
.long("bootstrap-nodes")
.takes_value(true)
.value_name("BOOTSTRAP_NODE_LIST")
.help("Specify a list of bootstrap node dialinfos to use"),
)
.arg(
Arg::new("local")
@@ -123,16 +145,16 @@ pub fn process_command_line() -> Result<(Settings, ArgMatches), String> {
// Set config from command line
if matches.occurrences_of("daemon") != 0 {
settingsrw.daemon = true;
settingsrw.daemon.enabled = true;
settingsrw.logging.terminal.enabled = false;
}
if matches.occurrences_of("subnode_index") != 0 {
let subnode_index = match matches.value_of("subnode_index") {
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());
return Err("value not specified for subnode-index".to_owned());
}
};
if subnode_index == 0 {
@@ -164,16 +186,61 @@ pub fn process_command_line() -> Result<(Settings, ArgMatches), String> {
if matches.occurrences_of("delete-table-store") != 0 {
settingsrw.core.table_store.delete = true;
}
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 (k, s) = if let Some((k, s)) = v.split_once(':') {
let k = DHTKey::try_decode(k)?;
let s = DHTKeySecret::try_decode(s)?;
(k, s)
} else {
let k = DHTKey::try_decode(v)?;
let buffer = rpassword::prompt_password("Enter secret key (will not echo): ")
.map_err(|e| e.to_string())?;
let buffer = buffer.trim().to_string();
let s = DHTKeySecret::try_decode(&buffer)?;
(k, s)
};
settingsrw.core.network.node_id = k;
settingsrw.core.network.node_id_secret = s;
}
if matches.occurrences_of("bootstrap") != 0 {
let bootstrap = match matches.value_of("bootstrap") {
let bootstrap_list = match matches.value_of("bootstrap-list") {
Some(x) => {
println!("Overriding bootstrap with: ");
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 => {
return Err("value not specified for bootstrap list".to_owned());
}
};
settingsrw.core.network.bootstrap = bootstrap_list;
}
if matches.occurrences_of("bootstrap-nodes") != 0 {
let bootstrap_list = match matches.value_of("bootstrap-list") {
Some(x) => {
println!("Overriding bootstrap node list with: ");
let mut out: Vec<ParsedNodeDialInfo> = Vec::new();
for x in x.split(',') {
let x = x.trim();
println!(" {}", x);
out.push(ParsedNodeDialInfo::from_str(x).map_err(|e| {
format!(
"unable to parse dial info in bootstrap list: {} for {}",
"unable to parse dial info in bootstrap node list: {} for {}",
e, x
)
})?);
@@ -181,10 +248,10 @@ pub fn process_command_line() -> Result<(Settings, ArgMatches), String> {
out
}
None => {
return Err("value not specified for bootstrap".to_owned());
return Err("value not specified for bootstrap node list".to_owned());
}
};
settingsrw.core.network.bootstrap = bootstrap;
settingsrw.core.network.bootstrap_nodes = bootstrap_list;
}
// Apply subnode index if we're testing

View File

@@ -28,7 +28,6 @@ fn main() -> Result<(), String> {
// --- Dump Config ---
if matches.occurrences_of("dump-config") != 0 {
//let cfg = config::Config::try_from(&*settingsr);
return serde_yaml::to_writer(std::io::stdout(), &*settings.read())
.map_err(|e| e.to_string());
}
@@ -40,8 +39,37 @@ fn main() -> Result<(), String> {
return Ok(());
}
// See if we're just running a quick command
let (server_mode, success, failure) = if matches.occurrences_of("set-node-id") != 0 {
(
ServerMode::ShutdownImmediate,
"Node Id and Secret set successfully",
"Failed to set Node Id and Secret",
)
} else if matches.occurrences_of("dump-txt-record") != 0 {
(ServerMode::DumpTXTRecord, "", "Failed to dump txt record")
} else {
(ServerMode::Normal, "", "")
};
// Handle non-normal server modes
if !matches!(server_mode, ServerMode::Normal) {
// Init combined console/file logger
let logs = VeilidLogs::setup_normal_logs(settings.clone())?;
// run the server to set the node id and quit
return task::block_on(async { run_veilid_server(settings, logs, server_mode).await })
.map(|v| {
println!("{}", success);
v
})
.map_err(|e| {
println!("{}", failure);
e
});
}
// --- Daemon Mode ----
if settings.read().daemon {
if settings.read().daemon.enabled {
cfg_if! {
if #[cfg(windows)] {
return windows::run_service(settings, matches).map_err(|e| format!("{}", e));
@@ -61,5 +89,5 @@ fn main() -> Result<(), String> {
.expect("Error setting Ctrl-C handler");
// Run the server loop
task::block_on(async { run_veilid_server(settings, logs).await })
task::block_on(async { run_veilid_server(settings, logs, server_mode).await })
}

View File

@@ -9,6 +9,13 @@ use std::sync::Arc;
use std::time::{Duration, Instant};
use veilid_core::xx::SingleShotEventual;
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum ServerMode {
Normal,
ShutdownImmediate,
DumpTXTRecord,
}
lazy_static! {
static ref SHUTDOWN_SWITCH: Mutex<Option<SingleShotEventual<()>>> =
Mutex::new(Some(SingleShotEventual::new(())));
@@ -21,7 +28,11 @@ pub fn shutdown() {
}
}
pub async fn run_veilid_server(settings: Settings, logs: VeilidLogs) -> Result<(), String> {
pub async fn run_veilid_server(
settings: Settings,
logs: VeilidLogs,
server_mode: ServerMode,
) -> Result<(), String> {
let settingsr = settings.read();
// Create client api state change pipe
@@ -44,7 +55,7 @@ pub async fn run_veilid_server(settings: Settings, logs: VeilidLogs) -> Result<(
.map_err(|e| format!("VeilidCore startup failed: {}", e))?;
// Start client api if one is requested
let mut capi = if settingsr.client_api.enabled {
let mut capi = if settingsr.client_api.enabled && matches!(server_mode, ServerMode::Normal) {
let some_capi = client_api::ClientApi::new(veilid_api.clone());
some_capi
.clone()
@@ -55,16 +66,18 @@ pub async fn run_veilid_server(settings: Settings, logs: VeilidLogs) -> Result<(
};
// Drop rwlock on settings
let auto_attach = settingsr.auto_attach;
let auto_attach = settingsr.auto_attach || !matches!(server_mode, ServerMode::Normal);
drop(settingsr);
// Handle state changes on main thread for capnproto rpc
let update_receiver_jh = capi.clone().map(|capi| {
async_std::task::spawn_local(async move {
while let Ok(change) = receiver.recv_async().await {
// Process all updates
let capi2 = capi.clone();
let update_receiver_jh = async_std::task::spawn_local(async move {
while let Ok(change) = receiver.recv_async().await {
if let Some(capi) = &capi2 {
// Handle state changes on main thread for capnproto rpc
capi.clone().handle_update(change);
}
})
}
});
// Handle log messages on main thread for capnproto rpc
let client_log_receiver_jh = capi.clone().and_then(|capi| {
@@ -103,14 +116,49 @@ pub async fn run_veilid_server(settings: Settings, logs: VeilidLogs) -> Result<(
});
// Auto-attach if desired
let mut out = Ok(());
if auto_attach {
info!("Auto-attach to the Veilid network");
if let Err(e) = veilid_api.attach().await {
error!("Auto-attaching to the Veilid network failed: {:?}", e);
let outerr = format!("Auto-attaching to the Veilid network failed: {:?}", e);
error!("{}", outerr);
out = Err(outerr);
shutdown();
}
}
// Process dump-txt-record
if matches!(server_mode, ServerMode::DumpTXTRecord) {
let start_time = Instant::now();
while Instant::now().duration_since(start_time) < Duration::from_secs(10) {
match veilid_api.get_state().await {
Ok(vs) => {
if vs.network.started {
break;
}
}
Err(e) => {
let outerr = format!("Getting state failed: {:?}", e);
error!("{}", outerr);
out = Err(outerr);
break;
}
}
async_std::task::sleep(Duration::from_millis(100)).await;
}
match veilid_api.debug("dialinfo txt".to_string()).await {
Ok(v) => {
print!("{}", v);
}
Err(e) => {
let outerr = format!("Getting dial info failed: {:?}", e);
error!("{}", outerr);
out = Err(outerr);
}
};
shutdown();
}
// Idle while waiting to exit
let shutdown_switch = {
let shutdown_switch_locked = SHUTDOWN_SWITCH.lock();
@@ -134,14 +182,12 @@ pub async fn run_veilid_server(settings: Settings, logs: VeilidLogs) -> Result<(
}
// Wait for update receiver to exit
if let Some(update_receiver_jh) = update_receiver_jh {
update_receiver_jh.await;
}
update_receiver_jh.await;
// Wait for client api log receiver to exit
if let Some(client_log_receiver_jh) = client_log_receiver_jh {
client_log_receiver_jh.await;
}
Ok(())
out
}

View File

@@ -16,12 +16,16 @@ use veilid_core::xx::*;
pub fn load_default_config() -> Result<config::Config, config::ConfigError> {
let default_config = String::from(
r#"---
daemon: false
daemon:
enabled: false
client_api:
enabled: true
listen_address: 'localhost:5959'
auto_attach: true
logging:
logging:
system:
enabled: false
level: 'info'
terminal:
enabled: true
level: 'info'
@@ -31,14 +35,14 @@ logging:
append: true
level: 'info'
client:
enabled: true
enabled: false
level: 'info'
testing:
subnode_index: 0
core:
protected_store:
allow_insecure_fallback: true
always_use_insecure_storage: false
always_use_insecure_storage: true
insecure_fallback_directory: '%INSECURE_FALLBACK_DIRECTORY%'
delete: false
table_store:
@@ -55,9 +59,12 @@ core:
max_connections_per_ip6_prefix_size: 56
max_connection_frequency_per_min: 8
client_whitelist_timeout_ms: 300000
reverse_connection_receipt_time_ms: 5000
hole_punch_receipt_time_ms: 5000
node_id: ''
node_id_secret: ''
bootstrap: []
bootstrap: ['bootstrap.veilid.net']
bootstrap_nodes: []
routing_table:
limit_over_attached: 64
limit_fully_attached: 32
@@ -90,8 +97,8 @@ core:
enable_local_peer_scope: false
restricted_nat_retries: 3
tls:
certificate_path: '/etc/veilid/server.crt'
private_key_path: '/etc/veilid/private/server.key'
certificate_path: '/etc/veilid-server/server.crt'
private_key_path: '/etc/veilid-server/private/server.key'
connection_initial_timeout_ms: 2000
application:
https:
@@ -130,11 +137,6 @@ core:
listen_address: ':5150'
path: 'ws'
# url: ''
leases:
max_server_signal_leases: 256
max_server_relay_leases: 8
max_client_signal_leases: 2
max_client_relay_leases: 2
"#,
)
.replace(
@@ -402,6 +404,12 @@ pub struct File {
pub level: LogLevel,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct System {
pub enabled: bool,
pub level: LogLevel,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Client {
pub enabled: bool,
@@ -416,6 +424,7 @@ pub struct ClientApi {
#[derive(Debug, Deserialize, Serialize)]
pub struct Logging {
pub system: System,
pub terminal: Terminal,
pub file: File,
pub client: Client,
@@ -522,14 +531,6 @@ pub struct Dht {
pub validate_dial_info_receipt_time_ms: u32,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Leases {
pub max_server_signal_leases: u32,
pub max_server_relay_leases: u32,
pub max_client_signal_leases: u32,
pub max_client_relay_leases: u32,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct RoutingTable {
pub limit_over_attached: u32,
@@ -548,9 +549,12 @@ pub struct Network {
pub max_connections_per_ip6_prefix_size: u32,
pub max_connection_frequency_per_min: u32,
pub client_whitelist_timeout_ms: u32,
pub reverse_connection_receipt_time_ms: u32,
pub hole_punch_receipt_time_ms: u32,
pub node_id: veilid_core::DHTKey,
pub node_id_secret: veilid_core::DHTKeySecret,
pub bootstrap: Vec<ParsedNodeDialInfo>,
pub bootstrap: Vec<String>,
pub bootstrap_nodes: Vec<ParsedNodeDialInfo>,
pub routing_table: RoutingTable,
pub rpc: Rpc,
pub dht: Dht,
@@ -561,7 +565,6 @@ pub struct Network {
pub tls: Tls,
pub application: Application,
pub protocol: Protocol,
pub leases: Leases,
}
#[derive(Debug, Deserialize, Serialize)]
@@ -597,9 +600,21 @@ pub struct Core {
pub network: Network,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Daemon {
pub enabled: bool,
pub pid_file: Option<String>,
pub chroot: Option<String>,
pub working_directory: Option<String>,
pub user: Option<String>,
pub group: Option<String>,
pub stdout_file: Option<String>,
pub stderr_file: Option<String>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct SettingsInner {
pub daemon: bool,
pub daemon: Daemon,
pub client_api: ClientApi,
pub auto_attach: bool,
pub logging: Logging,
@@ -708,56 +723,68 @@ impl Settings {
Ok(())
}
pub fn get_default_config_path() -> PathBuf {
// Get default configuration file location
let mut default_config_path =
if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
PathBuf::from(my_proj_dirs.config_dir())
fn is_root() -> bool {
cfg_if::cfg_if! {
if #[cfg(unix)] {
use nix::unistd::Uid;
Uid::effective().is_root()
} else {
PathBuf::from("./")
};
false
}
}
}
pub fn get_default_config_path() -> PathBuf {
let mut default_config_path = if Self::is_root() {
PathBuf::from("/etc/veilid-server")
} else if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
PathBuf::from(my_proj_dirs.config_dir())
} else {
PathBuf::from("./")
};
default_config_path.push("veilid-server.conf");
default_config_path
}
pub fn get_default_table_store_path() -> PathBuf {
// Get default configuration file location
let mut default_config_path =
if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
PathBuf::from(my_proj_dirs.data_local_dir())
} else {
PathBuf::from("./")
};
default_config_path.push("table_store");
let mut default_db_path = if Self::is_root() {
PathBuf::from("/var/db/veilid-server")
} else if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
PathBuf::from(my_proj_dirs.data_local_dir())
} else {
PathBuf::from("./")
};
default_db_path.push("table_store");
default_config_path
default_db_path
}
pub fn get_default_block_store_path() -> PathBuf {
// Get default configuration file location
let mut default_config_path =
if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
PathBuf::from(my_proj_dirs.data_local_dir())
} else {
PathBuf::from("./")
};
default_config_path.push("block_store");
let mut default_db_path = if Self::is_root() {
PathBuf::from("/var/db/veilid-server")
} else if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
PathBuf::from(my_proj_dirs.data_local_dir())
} else {
PathBuf::from("./")
};
default_db_path.push("block_store");
default_config_path
default_db_path
}
pub fn get_default_protected_store_insecure_fallback_directory() -> PathBuf {
// Get default configuration file location
let mut default_config_path =
if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
PathBuf::from(my_proj_dirs.data_local_dir())
} else {
PathBuf::from("./")
};
default_config_path.push("protected_store");
let mut default_db_path = if Self::is_root() {
PathBuf::from("/var/db/veilid-server")
} else if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
PathBuf::from(my_proj_dirs.data_local_dir())
} else {
PathBuf::from("./")
};
default_db_path.push("protected_store");
default_config_path
default_db_path
}
pub fn get_core_config_callback(&self) -> veilid_core::ConfigCallback {
@@ -834,17 +861,23 @@ impl Settings {
"network.max_connection_frequency_per_min" => Ok(Box::new(
inner.core.network.max_connection_frequency_per_min,
)),
"network.client_whitelist_timeout_ms" => {
Ok(Box::new(inner.core.network.client_whitelist_timeout_ms))
}
"network.reverse_connection_receipt_time_ms" => Ok(Box::new(
inner.core.network.reverse_connection_receipt_time_ms,
)),
"network.hole_punch_receipt_time_ms" => {
Ok(Box::new(inner.core.network.hole_punch_receipt_time_ms))
}
"network.node_id" => Ok(Box::new(inner.core.network.node_id)),
"network.node_id_secret" => Ok(Box::new(inner.core.network.node_id_secret)),
"network.bootstrap" => Ok(Box::new(
"network.bootstrap" => Ok(Box::new(inner.core.network.bootstrap.clone())),
"network.bootstrap_nodes" => Ok(Box::new(
inner
.core
.network
.bootstrap
.bootstrap_nodes
.clone()
.into_iter()
.map(|e| e.node_dial_info_string)
@@ -1112,18 +1145,6 @@ impl Settings {
.as_ref()
.map(|a| a.urlstring.clone()),
)),
"network.leases.max_server_signal_leases" => {
Ok(Box::new(inner.core.network.leases.max_server_signal_leases))
}
"network.leases.max_server_relay_leases" => {
Ok(Box::new(inner.core.network.leases.max_server_relay_leases))
}
"network.leases.max_client_signal_leases" => {
Ok(Box::new(inner.core.network.leases.max_client_signal_leases))
}
"network.leases.max_client_relay_leases" => {
Ok(Box::new(inner.core.network.leases.max_client_relay_leases))
}
_ => Err(format!("config key '{}' doesn't exist", key)),
};
out
@@ -1150,7 +1171,14 @@ mod tests {
let settings = Settings::new(None).unwrap();
let s = settings.read();
assert_eq!(s.daemon, false);
assert_eq!(s.daemon.enabled, false);
assert_eq!(s.daemon.pid_file, None);
assert_eq!(s.daemon.chroot, None);
assert_eq!(s.daemon.working_directory, None);
assert_eq!(s.daemon.user, None);
assert_eq!(s.daemon.group, None);
assert_eq!(s.daemon.stdout_file, None);
assert_eq!(s.daemon.stderr_file, None);
assert_eq!(s.client_api.enabled, true);
assert_eq!(s.client_api.listen_address.name, "localhost:5959");
assert_eq!(
@@ -1164,7 +1192,7 @@ mod tests {
assert_eq!(s.logging.file.path, "");
assert_eq!(s.logging.file.append, true);
assert_eq!(s.logging.file.level, LogLevel::Info);
assert_eq!(s.logging.client.enabled, true);
assert_eq!(s.logging.client.enabled, false);
assert_eq!(s.logging.client.level, LogLevel::Info);
assert_eq!(s.testing.subnode_index, 0);
@@ -1181,7 +1209,7 @@ mod tests {
assert_eq!(s.core.block_store.delete, false);
assert_eq!(s.core.protected_store.allow_insecure_fallback, true);
assert_eq!(s.core.protected_store.always_use_insecure_storage, false);
assert_eq!(s.core.protected_store.always_use_insecure_storage, true);
assert_eq!(
s.core.protected_store.insecure_fallback_directory,
Settings::get_default_protected_store_insecure_fallback_directory()
@@ -1195,13 +1223,19 @@ mod tests {
assert_eq!(s.core.network.max_connections_per_ip6_prefix_size, 56u32);
assert_eq!(s.core.network.max_connection_frequency_per_min, 8u32);
assert_eq!(s.core.network.client_whitelist_timeout_ms, 300_000u32);
assert_eq!(s.core.network.reverse_connection_receipt_time_ms, 5_000u32);
assert_eq!(s.core.network.hole_punch_receipt_time_ms, 5_000u32);
assert_eq!(s.core.network.node_id, veilid_core::DHTKey::default());
assert_eq!(
s.core.network.node_id_secret,
veilid_core::DHTKeySecret::default()
);
//
assert!(s.core.network.bootstrap.is_empty());
assert_eq!(
s.core.network.bootstrap,
vec!["bootstrap.veilid.net".to_owned()]
);
assert_eq!(s.core.network.bootstrap_nodes, vec![]);
//
assert_eq!(s.core.network.rpc.concurrency, 0);
assert_eq!(s.core.network.rpc.queue_size, 1024);
@@ -1234,11 +1268,11 @@ mod tests {
//
assert_eq!(
s.core.network.tls.certificate_path,
std::path::PathBuf::from("/etc/veilid/server.crt")
std::path::PathBuf::from("/etc/veilid-server/server.crt")
);
assert_eq!(
s.core.network.tls.private_key_path,
std::path::PathBuf::from("/etc/veilid/private/server.key")
std::path::PathBuf::from("/etc/veilid-server/private/server.key")
);
assert_eq!(s.core.network.tls.connection_initial_timeout_ms, 2_000u32);
//
@@ -1317,9 +1351,5 @@ mod tests {
);
assert_eq!(s.core.network.protocol.wss.url, None);
//
assert_eq!(s.core.network.leases.max_server_signal_leases, 256);
assert_eq!(s.core.network.leases.max_server_relay_leases, 8);
assert_eq!(s.core.network.leases.max_client_signal_leases, 2);
assert_eq!(s.core.network.leases.max_client_relay_leases, 2);
}
}

View File

@@ -1,8 +1,102 @@
use crate::server::*;
use crate::settings::Settings;
use crate::veilid_logs::*;
use async_std::stream::StreamExt;
use async_std::task;
use clap::ArgMatches;
// use log::*;
use signal_hook::consts::signal::*;
use signal_hook_async_std::Signals;
pub fn run_daemon(_settings: Settings, _matches: ArgMatches) -> Result<(), String> {
eprintln!("Windows Service mode not implemented yet.");
Ok(())
async fn handle_signals(mut signals: Signals) {
while let Some(signal) = signals.next().await {
match signal {
SIGHUP => {
// XXX: reload configuration?
}
SIGTERM | SIGINT | SIGQUIT => {
// Shutdown the system;
shutdown();
}
_ => unreachable!(),
}
}
}
pub fn run_daemon(settings: Settings, _matches: ArgMatches) -> Result<(), String> {
let daemon = {
let mut daemon = daemonize::Daemonize::new();
let s = settings.read();
if let Some(pid_file) = &s.daemon.pid_file {
daemon = daemon.pid_file(pid_file).chown_pid_file(true);
}
if let Some(chroot) = &s.daemon.chroot {
daemon = daemon.chroot(chroot);
}
if let Some(working_directory) = &s.daemon.working_directory {
daemon = daemon.working_directory(working_directory);
}
if let Some(user) = &s.daemon.user {
daemon = daemon.user(user.as_str());
}
if let Some(group) = &s.daemon.group {
daemon = daemon.group(group.as_str());
}
let stdout_file = if let Some(stdout_file) = &s.daemon.stdout_file {
Some(
std::fs::File::create(stdout_file)
.map_err(|e| format!("Failed to create stdio file: {}", e))?,
)
} else {
None
};
if let Some(stderr_file) = &s.daemon.stderr_file {
if Some(stderr_file) == s.daemon.stdout_file.as_ref() {
// same output file for stderr and stdout
daemon = daemon.stderr(
stdout_file
.as_ref()
.unwrap()
.try_clone()
.map_err(|e| format!("Failed to clone stdout file: {}", e))?,
);
} else {
daemon = daemon.stderr(
std::fs::File::create(stderr_file)
.map_err(|e| format!("Failed to create stderr file: {}", e))?,
);
}
}
if let Some(stdout_file) = stdout_file {
daemon = daemon.stdout(stdout_file);
}
daemon
};
// Init combined console/file logger
let logs = VeilidLogs::setup_normal_logs(settings.clone())?;
// Daemonize
daemon
.start()
.map_err(|e| format!("Failed to daemonize: {}", e))?;
// Now, run the server
task::block_on(async {
// Catch signals
let signals = Signals::new(&[SIGHUP, SIGTERM, SIGINT, SIGQUIT])
.map_err(|e| format!("failed to init signals: {}", e))?;
let handle = signals.handle();
let signals_task = async_std::task::spawn(handle_signals(signals));
let res = run_veilid_server(settings, logs, ServerMode::Normal).await;
// Terminate the signal stream.
handle.close();
signals_task.await;
res
})
}

View File

@@ -1,5 +1,7 @@
use crate::log_safe_channel::*;
use crate::settings::*;
use cfg_if::*;
use log::*;
use simplelog::*;
use std::fs::OpenOptions;
use std::path::Path;
@@ -9,6 +11,84 @@ pub struct VeilidLogs {
pub client_log_channel_closer: Option<LogSafeChannelCloser>,
}
cfg_if! {
if #[cfg(target_os = "linux")] {
use systemd_journal_logger::JournalLog;
pub struct SystemLogger {
level_filter: LevelFilter,
config: Config,
journal_log: JournalLog<String,String>,
}
impl SystemLogger {
pub fn new(level_filter: LevelFilter, config: Config) -> Box<Self> {
Box::new(Self {
level_filter,
config,
journal_log: JournalLog::with_extra_fields(Vec::new())
})
}
pub fn should_skip(record: &Record<'_>) -> bool {
// // If a module path and allowed list are available
// match (record.target(), &*config.filter_allow) {
// (path, allowed) if !allowed.is_empty() => {
// // Check that the module path matches at least one allow filter
// if !allowed.iter().any(|v| path.starts_with(&**v)) {
// // If not, skip any further writing
// return true;
// }
// }
// _ => {}
// }
// If a module path and ignore list are available
match (record.target(), &veilid_core::DEFAULT_LOG_IGNORE_LIST) {
(path, ignore) if !ignore.is_empty() => {
// Check that the module path does not match any ignore filters
if ignore.iter().any(|v| path.starts_with(&**v)) {
// If not, skip any further writing
return true;
}
}
_ => {}
}
false
}
}
impl Log for SystemLogger {
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
metadata.level() <= self.level_filter
}
fn log(&self, record: &Record<'_>) {
if self.enabled(record.metadata()) && ! Self::should_skip(record) {
self.journal_log.log(record);
}
}
fn flush(&self) {
self.journal_log.flush();
}
}
impl SharedLogger for SystemLogger {
fn level(&self) -> LevelFilter {
self.level_filter
}
fn config(&self) -> Option<&Config> {
Some(&self.config)
}
fn as_log(self: Box<Self>) -> Box<dyn Log> {
Box::new(*self)
}
}
}
}
impl VeilidLogs {
pub fn setup_normal_logs(settings: Settings) -> Result<VeilidLogs, String> {
let settingsr = settings.read();
@@ -64,6 +144,14 @@ impl VeilidLogs {
))
}
cfg_if! {
if #[cfg(target_os = "linux")] {
if settingsr.logging.system.enabled {
logs.push(SystemLogger::new(convert_loglevel(settingsr.logging.system.level), cb.build()))
}
}
}
CombinedLogger::init(logs).map_err(|e| format!("failed to init logs: {}", e))?;
Ok(VeilidLogs {