refactoring, more config, packaging
This commit is contained in:
@@ -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 ---------------------------------
|
||||
|
@@ -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
|
||||
|
@@ -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 })
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
})
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
Reference in New Issue
Block a user