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

95
Cargo.lock generated
View File

@ -1259,6 +1259,7 @@ checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
dependencies = [
"block-buffer 0.10.2",
"crypto-common",
"subtle",
]
[[package]]
@ -1308,7 +1309,7 @@ dependencies = [
"curve25519-dalek",
"ed25519",
"rand 0.7.3",
"sha2",
"sha2 0.9.9",
"zeroize",
]
@ -1813,7 +1814,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f"
dependencies = [
"digest 0.9.0",
"hmac",
"hmac 0.10.1",
]
[[package]]
@ -1826,6 +1827,15 @@ dependencies = [
"digest 0.9.0",
]
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest 0.10.3",
]
[[package]]
name = "http"
version = "0.2.6"
@ -2046,7 +2056,7 @@ dependencies = [
"log",
"ndk",
"ndk-glue",
"rpassword",
"rpassword 5.0.1",
"secret-service",
"security-framework",
"security-framework-sys",
@ -2167,6 +2177,24 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "libsystemd"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8144587c71c16756b1055d3dcb0c75cb605a10ecd6523cc33702d5f90902bf6d"
dependencies = [
"hmac 0.12.1",
"libc",
"log",
"nix 0.23.1",
"nom 7.1.0",
"once_cell",
"serde 1.0.136",
"sha2 0.10.2",
"thiserror",
"uuid",
]
[[package]]
name = "linked-hash-map"
version = "0.3.0"
@ -3280,6 +3308,18 @@ dependencies = [
"winapi",
]
[[package]]
name = "rpassword"
version = "6.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf099a1888612545b683d2661a1940089f6c2e5a8e38979b2159da876bfd956"
dependencies = [
"libc",
"serde 1.0.136",
"serde_json",
"winapi",
]
[[package]]
name = "rtnetlink"
version = "0.9.0"
@ -3451,7 +3491,7 @@ dependencies = [
"num 0.3.1",
"rand 0.8.5",
"serde 1.0.136",
"sha2",
"sha2 0.9.9",
"zbus",
"zbus_macros",
"zvariant",
@ -3699,6 +3739,17 @@ dependencies = [
"opaque-debug 0.3.0",
]
[[package]]
name = "sha2"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
dependencies = [
"cfg-if 1.0.0",
"cpufeatures 0.2.1",
"digest 0.10.3",
]
[[package]]
name = "signal-hook"
version = "0.3.13"
@ -3709,6 +3760,18 @@ dependencies = [
"signal-hook-registry",
]
[[package]]
name = "signal-hook-async-std"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4aa94397e2023af5b7cff5b8d4785e935cfb77f0e4aab0cae3b26258ace556"
dependencies = [
"async-io",
"futures-lite",
"libc",
"signal-hook",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.1"
@ -3861,6 +3924,16 @@ dependencies = [
"winapi",
]
[[package]]
name = "systemd-journal-logger"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2b2b2ff370208ad472629786a66dc252933843755a1d620a54a8fdd0fccb31f"
dependencies = [
"libsystemd",
"log",
]
[[package]]
name = "tap"
version = "1.0.1"
@ -4124,6 +4197,15 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "uuid"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
dependencies = [
"serde 1.0.136",
]
[[package]]
name = "value-bag"
version = "1.0.0-alpha.8"
@ -4294,12 +4376,17 @@ dependencies = [
"futures",
"lazy_static",
"log",
"nix 0.23.1",
"parking_lot 0.12.0",
"rpassword 6.0.1",
"serde 1.0.136",
"serde_derive",
"serde_yaml",
"serial_test 0.6.0",
"signal-hook",
"signal-hook-async-std",
"simplelog",
"systemd-journal-logger",
"url",
"veilid-core",
"windows-service",

View File

@ -72,7 +72,7 @@ deps:
code:
FROM +deps
COPY --dir .cargo external files scripts veilid-cli veilid-core veilid-server veilid-wasm Cargo.lock Cargo.toml /veilid
COPY --dir .cargo external files scripts veilid-cli veilid-core veilid-server veilid-flutter veilid-wasm Cargo.lock Cargo.toml /veilid
WORKDIR /veilid
# Clippy only
@ -113,3 +113,36 @@ unit-tests-linux-amd64:
unit-tests-linux-arm64:
FROM +code
RUN cargo test --target aarch64-unknown-linux-gnu --release
# Package
package-linux-amd64:
FROM +build-linux-amd64
#################################
### DEBIAN DPKG .DEB FILES
#################################
COPY --dir package /veilid
# veilid-server
RUN /veilid/package/debian/earthly_make_veilid_server_deb.sh amd64 x86_64-unknown-linux-gnu
SAVE ARTIFACT --keep-ts /dpkg/out/*.deb AS LOCAL ./target/packages/
# veilid-cli
RUN /veilid/package/debian/earthly_make_veilid_cli_deb.sh amd64 x86_64-unknown-linux-gnu
# save artifacts
SAVE ARTIFACT --keep-ts /dpkg/out/*.deb AS LOCAL ./target/packages/
package-linux-arm64:
FROM +build-linux-arm64
#################################
### DEBIAN DPKG .DEB FILES
#################################
COPY --dir package /veilid
# veilid-server
RUN /veilid/package/debian/earthly_make_veilid_server_deb.sh arm64 aarch64-unknown-linux-gnu
SAVE ARTIFACT --keep-ts /dpkg/out/*.deb AS LOCAL ./target/packages/
# veilid-cli
RUN /veilid/package/debian/earthly_make_veilid_cli_deb.sh arm64 aarch64-unknown-linux-gnu
# save artifacts
SAVE ARTIFACT --keep-ts /dpkg/out/*.deb AS LOCAL ./target/packages/
package-linux:
BUILD +package-linux-amd64
BUILD +package-linux-arm64

4
package/cargo_version.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
INPUTFILE=$1
cat $1 | grep version | head -n 1 | cut -d\" -f 2

View File

@ -0,0 +1,19 @@
#!/bin/bash
set -e
ARCH=$1
CARGO_ARCH=$2
CARGO_VERSION="$(/veilid/package/cargo_version.sh /veilid/veilid-cli/Cargo.toml)"
rm -rf /dpkg
mkdir -p /dpkg/out
# veilid-cli dpkg control
cp -rf /veilid/package/debian/veilid-cli /dpkg
/veilid/package/replace_variable.sh /dpkg/veilid-cli/DEBIAN/control CARGO_VERSION $CARGO_VERSION
/veilid/package/replace_variable.sh /dpkg/veilid-cli/DEBIAN/control ARCH $ARCH
# veilid-cli executable
mkdir -p /dpkg/veilid-cli/usr/bin
cp -f /veilid/target/$CARGO_ARCH/release/veilid-cli /dpkg/veilid-cli/usr/bin
# pack it up
dpkg-deb -b /dpkg/veilid-cli/
mv /dpkg/veilid-cli.deb /dpkg/out/veilid-cli-$CARGO_VERSION-$ARCH.deb

View File

@ -0,0 +1,25 @@
#!/bin/bash
set -e
ARCH=$1
CARGO_ARCH=$2
CARGO_VERSION="$(/veilid/package/cargo_version.sh /veilid/veilid-server/Cargo.toml)"
rm -rf /dpkg
mkdir -p /dpkg/out
# veilid-server dpkg control
cp -rf /veilid/package/debian/veilid-server /dpkg
/veilid/package/replace_variable.sh /dpkg/veilid-server/DEBIAN/control CARGO_VERSION $CARGO_VERSION
/veilid/package/replace_variable.sh /dpkg/veilid-server/DEBIAN/control ARCH $ARCH
# veilid-server configuration
mkdir -p /dpkg/veilid-server/etc/veilid-server
cp -f /veilid/package/linux/veilid-server.conf /dpkg/veilid-server/etc/veilid-server/veilid-server.conf
# veilid-server systemd unit file
mkdir -p /dpkg/veilid-server/etc/systemd/system
cp -f /veilid/package/systemd/veilid-server.service /dpkg/veilid-server/etc/systemd/system
# veilid-server executable
mkdir -p /dpkg/veilid-server/usr/bin
cp -f /veilid/target/$CARGO_ARCH/release/veilid-server /dpkg/veilid-server/usr/bin
# pack it up
dpkg-deb -b /dpkg/veilid-server/
mv /dpkg/veilid-server.deb /dpkg/out/veilid-server-$CARGO_VERSION-$ARCH.deb

View File

@ -0,0 +1,10 @@
Package: veilid-cli
Version: $CARGO_VERSION
Section: network
Priority: optional
Architecture: $ARCH
Depends: libc6 (>= 2.23)
Maintainer: jsmith@veilid.org
Description: Veilid Server Command Line Interface
The Veilid peer-to-peer network server command line interface

View File

@ -0,0 +1,6 @@
#!/bin/sh
set -e
# Set permissions
chmod 755 /usr/bin/veilid-cli

View File

@ -0,0 +1,9 @@
Package: veilid-server
Version: $CARGO_VERSION
Section: network
Priority: optional
Architecture: $ARCH
Depends: libc6 (>= 2.23)
Maintainer: jsmith@veilid.org
Description: Veilid Server
The Veilid peer-to-peer network server

View File

@ -0,0 +1,47 @@
#!/bin/bash
set -e
# Add veilid user and group
adduser --system --group veilid &>/dev/null || true
# Make db folders
mkdir -p /var/db/veilid-server/protected_store
mkdir -p /var/db/veilid-server/table_store
mkdir -p /var/db/veilid-server/block_store
# Set permissions
chown -R veilid:veilid /var/db/veilid-server
chmod 0750 /var/db/veilid-server/protected_store
chmod 0750 /var/db/veilid-server/table_store
chmod 0750 /var/db/veilid-server/block_store
chmod 0750 /var/db/veilid-server
chmod 755 /usr/bin/veilid-server
# Add and start systemd unit
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then
# This will only remove masks created by d-s-h on package removal.
deb-systemd-helper unmask 'veilid-server.service' >/dev/null || true
# was-enabled defaults to true, so new installations run enable.
if deb-systemd-helper --quiet was-enabled 'veilid-server.service'; then
# Enables the unit on first installation, creates new
# symlinks on upgrades if the unit file has changed.
deb-systemd-helper enable 'veilid-server.service' >/dev/null || true
else
# Update the statefile to add new symlinks (if any), which need to be
# cleaned up on purge. Also remove old symlinks.
deb-systemd-helper update-state 'veilid-server.service' >/dev/null || true
fi
if [ -d /run/systemd/system ]; then
systemctl --system daemon-reload >/dev/null || true
if [ -n "$2" ]; then
_dh_action=restart
else
_dh_action=start
fi
deb-systemd-invoke $_dh_action 'veilid-server.service' >/dev/null || true
fi
fi

View File

@ -0,0 +1,18 @@
#!/bin/sh
set -e
if [ -d /run/systemd/system ]; then
systemctl --system daemon-reload >/dev/null || true
fi
if [ "$1" = "remove" ]; then
if [ -x "/usr/bin/deb-systemd-helper" ]; then
deb-systemd-helper mask 'veilid-server.service' >/dev/null || true
fi
elif [ "$1" = "purge" ]; then
if [ -x "/usr/bin/deb-systemd-helper" ]; then
deb-systemd-helper purge 'veilid-server.service' >/dev/null || true
deb-systemd-helper unmask 'veilid-server.service' >/dev/null || true
fi
rm -rf /var/db/veilid-server
fi

View File

@ -0,0 +1,6 @@
#!/bin/sh
set -e
if [ -d /run/systemd/system ] && [ "$1" = remove ]; then
deb-systemd-invoke stop 'veilid-server.service' >/dev/null || true
fi

View File

@ -0,0 +1,20 @@
# Veilid Server
# =============
#
# Default Server Configuration
#
# -----------------------------------------------------------
---
daemon:
enabled: true
pid_file: '/run/veilid-server.pid'
working_directory: '/'
user: veilid
group: veilid
logging:
system:
enabled: true
level: info
terminal:
enabled: false

7
package/replace_variable.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
INPUTFILE=$1
VARIABLE_NAME=$2
VARIABLE_VALUE=$3
sed -i "s/\$$VARIABLE_NAME/$VARIABLE_VALUE/g" "$INPUTFILE"

View File

@ -0,0 +1,20 @@
# /etc/systemd/system/veilid-server.service
[Unit]
Description=The Veilid peer-to-peer network server
Requires=network-online.target
After=network-online.target
[Service]
Type=forking
PIDFile=/run/veilid-server.pid
ExecStartPre=/usr/bin/rm -f /run/veilid-server.pid
ExecStart=/usr/bin/veilid-server
ExecReload=/bin/kill -s HUP $MAINPID
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=mixed
PrivateTmp=true
[Install]
WantedBy=multi-user.target

View File

@ -146,8 +146,8 @@ def main():
time.sleep(1)
sub_args = base_args.copy()
sub_args.append("--subnode_index={}".format(n))
sub_args.append("--bootstrap={}".format(main_di))
sub_args.append("--subnode-index={}".format(n))
sub_args.append("--bootstrap-nodes={}".format(main_di))
if args.wait_for_debug and (str(n) in args.wait_for_debug):
sub_args.append("--wait-for-debug")

View File

@ -88,14 +88,29 @@ impl ClientApiConnection {
}
async fn process_veilid_state<'a>(
&'a mut self,
state: veilid_state::Reader<'a>,
veilid_state: veilid_state::Reader<'a>,
) -> Result<(), String> {
let mut inner = self.inner.borrow_mut();
// Process attachment state
let attachment = state.reborrow().get_attachment().map_err(map_to_string)?;
let state = attachment.get_state().map_err(map_to_string)?;
inner.comproc.update_attachment(state);
let attachment = veilid_state
.reborrow()
.get_attachment()
.map_err(map_to_string)?;
let attachment_state = attachment.get_state().map_err(map_to_string)?;
let network = veilid_state
.reborrow()
.get_network()
.map_err(map_to_string)?;
let started = network.get_started();
let bps_down = network.get_bps_down();
let bps_up = network.get_bps_up();
inner.comproc.update_attachment(attachment_state);
inner
.comproc
.update_network_status(started, bps_down, bps_up);
Ok(())
}

View File

@ -277,6 +277,12 @@ debug - send a debugging command to the Veilid server
self.inner_mut().ui.set_attachment_state(state);
}
pub fn update_network_status(&mut self, started: bool, bps_down: u64, bps_up: u64) {
self.inner_mut()
.ui
.set_network_status(started, bps_down, bps_up);
}
pub fn add_log_message(&mut self, message: &str) {
self.inner().ui.add_node_event(message);
}

View File

@ -49,6 +49,8 @@ pub type UICallback = Box<dyn Fn(&mut Cursive) + Send>;
struct UIState {
attachment_state: Dirty<AttachmentState>,
network_started: Dirty<bool>,
network_down_up: Dirty<(f32, f32)>,
connection_state: Dirty<ConnectionState>,
}
@ -56,6 +58,8 @@ impl UIState {
pub fn new() -> Self {
Self {
attachment_state: Dirty::new(AttachmentState::Detached),
network_started: Dirty::new(false),
network_down_up: Dirty::new((0.0, 0.0)),
connection_state: Dirty::new(ConnectionState::Disconnected),
}
}
@ -221,6 +225,15 @@ impl UI {
AttachmentState::Detaching => "Detaching [////]",
}
}
fn render_network_status(inner: &mut UIInner) -> String {
match inner.ui_state.network_started.get() {
false => "Down: ----KB/s Up: ----KB/s".to_owned(),
true => {
let (d, u) = inner.ui_state.network_down_up.get();
format!("Down: {:.2}KB/s Up: {:.2}KB/s", d, u)
}
}
}
fn render_button_attach<'a>(inner: &mut UIInner) -> (&'a str, bool) {
if let ConnectionState::Connected(_, _) = inner.ui_state.connection_state.get() {
match inner.ui_state.attachment_state.get() {
@ -576,7 +589,7 @@ impl UI {
status.append_styled("|", ColorStyle::highlight_inactive());
// Add bandwidth status
status.append_styled(
" Down: 0.0KB/s Up: 0.0KB/s ",
format!(" {} ", UI::render_network_status(&mut inner)),
ColorStyle::highlight_inactive(),
);
status.append_styled("|", ColorStyle::highlight_inactive());
@ -599,6 +612,12 @@ impl UI {
refresh_statusbar = true;
refresh_button_attach = true;
}
if inner.ui_state.network_started.take_dirty() {
refresh_statusbar = true;
}
if inner.ui_state.network_down_up.take_dirty() {
refresh_statusbar = true;
}
if inner.ui_state.connection_state.take_dirty() {
refresh_statusbar = true;
refresh_button_attach = true;
@ -757,6 +776,15 @@ impl UI {
inner.ui_state.attachment_state.set(state);
let _ = inner.cb_sink.send(Box::new(UI::update_cb));
}
pub fn set_network_status(&mut self, started: bool, bps_down: u64, bps_up: u64) {
let mut inner = self.inner.borrow_mut();
inner.ui_state.network_started.set(started);
inner.ui_state.network_down_up.set((
((bps_down as f64) / 1000.0f64) as f32,
((bps_up as f64) / 1000.0f64) as f32,
));
let _ = inner.cb_sink.send(Box::new(UI::update_cb));
}
pub fn set_connection_state(&mut self, state: ConnectionState) {
let mut inner = self.inner.borrow_mut();
inner.ui_state.connection_state.set(state);

View File

@ -136,10 +136,10 @@ impl Log for ApiLogger {
let s = format!("{}{}{}", tgt, loc, record.args());
(inner.update_callback)(VeilidUpdate::Log {
(inner.update_callback)(VeilidUpdate::Log(VeilidStateLog {
log_level: ll,
message: s,
})
}))
}
}
}

View File

@ -271,15 +271,18 @@ impl AttachmentManager {
let network_manager = {
let mut inner = self.inner.lock();
inner.update_callback = Some(update_callback.clone());
let update_callback2 = update_callback.clone();
inner.attachment_machine.set_state_change_callback(Arc::new(
move |_old_state: AttachmentState, new_state: AttachmentState| {
update_callback(VeilidUpdate::Attachment { state: new_state })
update_callback2(VeilidUpdate::Attachment(VeilidStateAttachment {
state: new_state,
}))
},
));
inner.network_manager.clone()
};
network_manager.init().await?;
network_manager.init(update_callback).await?;
Ok(())
}
@ -356,4 +359,10 @@ impl AttachmentManager {
let attachment_machine = self.inner.lock().attachment_machine.clone();
attachment_machine.state()
}
pub fn get_veilid_state(&self) -> VeilidStateAttachment {
VeilidStateAttachment {
state: self.get_state(),
}
}
}

View File

@ -214,7 +214,14 @@ impl Network {
.interfaces
.best_addresses()
.iter()
.map(|a| SocketAddr::new(*a, from.port()))
.filter_map(|a| {
// We create sockets that are only ipv6 or ipv6 (not dual, so only translate matching unspecified address)
if (a.is_ipv4() && from.is_ipv4()) || (a.is_ipv6() && from.is_ipv6()) {
Some(SocketAddr::new(*a, from.port()))
} else {
None
}
})
.collect()
}
}

View File

@ -445,6 +445,7 @@ impl Network {
log_net!("updating network class");
let protocol_config = self.inner.lock().protocol_config.unwrap_or_default();
let old_network_class = self.inner.lock().network_class;
let context = DiscoveryContext::new(self.routing_table(), self.clone());
@ -470,9 +471,10 @@ impl Network {
}
let network_class = context.inner.lock().network_class;
if network_class != old_network_class {
self.inner.lock().network_class = network_class;
log_net!(debug "network class set to {:?}", network_class);
log_net!(debug "network class changed to {:?}", network_class);
}
// send updates to everyone
self.routing_table().send_node_info_updates();

View File

@ -291,6 +291,8 @@ impl Network {
let local_dial_info_list = self.create_udp_inbound_sockets(ip_addrs, udp_port).await?;
let mut static_public = false;
trace!("UDP: listener started on {:#?}", local_dial_info_list);
// Register local dial info
for di in &local_dial_info_list {
// If the local interface address is global, or we are enabling local peer scope
@ -397,7 +399,7 @@ impl Network {
Box::new(|c, t, a| Box::new(WebsocketProtocolHandler::new(c, t, a))),
)
.await?;
trace!("WS: listener started");
trace!("WS: listener started on {:#?}", socket_addresses);
let mut static_public = false;
let mut registered_addresses: HashSet<IpAddr> = HashSet::new();
@ -515,7 +517,7 @@ impl Network {
wss_port,
ip_addrs
);
let _socket_addresses = self
let socket_addresses = self
.start_tcp_listener(
ip_addrs,
wss_port,
@ -523,7 +525,7 @@ impl Network {
Box::new(|c, t, a| Box::new(WebsocketProtocolHandler::new(c, t, a))),
)
.await?;
trace!("WSS: listener started");
trace!("WSS: listener started on {:#?}", socket_addresses);
// NOTE: No interface dial info for WSS, as there is no way to connect to a local dialinfo via TLS
// If the hostname is specified, it is the public dialinfo via the URL. If no hostname
@ -629,7 +631,7 @@ impl Network {
Box::new(|_, _, a| Box::new(RawTcpProtocolHandler::new(a))),
)
.await?;
trace!("TCP: listener started");
trace!("TCP: listener started on {:#?}", socket_addresses);
let mut static_public = false;
let mut registered_addresses: HashSet<IpAddr> = HashSet::new();

View File

@ -90,6 +90,7 @@ pub enum SendDataKind {
struct NetworkManagerInner {
routing_table: Option<RoutingTable>,
components: Option<NetworkComponents>,
update_callback: Option<UpdateCallback>,
stats: NetworkManagerStats,
client_whitelist: LruCache<key::DHTKey, ClientWhitelistEntry>,
relay_node: Option<NodeRef>,
@ -116,6 +117,7 @@ impl NetworkManager {
NetworkManagerInner {
routing_table: None,
components: None,
update_callback: None,
stats: NetworkManagerStats::default(),
client_whitelist: LruCache::new_unbounded(),
relay_node: None,
@ -205,10 +207,11 @@ impl NetworkManager {
self.inner.lock().relay_node.clone()
}
pub async fn init(&self) -> Result<(), String> {
pub async fn init(&self, update_callback: UpdateCallback) -> Result<(), String> {
let routing_table = RoutingTable::new(self.clone());
routing_table.init().await?;
self.inner.lock().routing_table = Some(routing_table.clone());
self.inner.lock().update_callback = Some(update_callback);
Ok(())
}
pub async fn terminate(&self) {
@ -219,6 +222,7 @@ impl NetworkManager {
if let Some(routing_table) = routing_table {
routing_table.terminate().await;
}
self.inner.lock().update_callback = None;
}
pub async fn internal_startup(&self) -> Result<(), String> {
@ -256,9 +260,16 @@ impl NetworkManager {
self.shutdown().await;
return Err(e);
}
self.send_network_update();
Ok(())
}
pub fn is_started(&self) -> bool {
self.inner.lock().components.is_some()
}
pub async fn shutdown(&self) {
trace!("NetworkManager::shutdown begin");
@ -272,8 +283,13 @@ impl NetworkManager {
}
// reset the state
{
let mut inner = self.inner.lock();
inner.components = None;
}
// send update
self.send_network_update();
trace!("NetworkManager::shutdown end");
}
@ -334,7 +350,9 @@ impl NetworkManager {
// if things can't restart, then we fail out of the attachment manager
if net.needs_restart() {
net.shutdown().await;
self.send_network_update();
net.startup().await?;
self.send_network_update();
}
// Run the routing table tick
@ -1155,6 +1173,7 @@ impl NetworkManager {
// Compute transfer statistics for the low level network
async fn rolling_transfers_task_routine(self, last_ts: u64, cur_ts: u64) -> Result<(), String> {
log_net!("--- network manager rolling_transfers task");
{
let inner = &mut *self.inner.lock();
// Roll the low level network transfer stats for our address
@ -1184,6 +1203,11 @@ impl NetworkManager {
for da in &dead_addrs {
inner.stats.per_address_stats.remove(da);
}
}
// Send update
self.send_network_update();
Ok(())
}
@ -1220,6 +1244,45 @@ impl NetworkManager {
.add_down(bytes);
}
// Get stats
pub fn get_stats(&self) -> NetworkManagerStats {
let inner = self.inner.lock();
inner.stats.clone()
}
fn get_veilid_state_inner(inner: &NetworkManagerInner) -> VeilidStateNetwork {
if inner.components.is_some() {
VeilidStateNetwork {
started: true,
bps_down: inner.stats.self_stats.transfer_stats.down.average,
bps_up: inner.stats.self_stats.transfer_stats.up.average,
}
} else {
VeilidStateNetwork {
started: false,
bps_down: 0,
bps_up: 0,
}
}
}
pub fn get_veilid_state(&self) -> VeilidStateNetwork {
let inner = self.inner.lock();
Self::get_veilid_state_inner(&*inner)
}
fn send_network_update(&self) {
let (update_cb, state) = {
let inner = self.inner.lock();
let update_cb = inner.update_callback.clone();
if update_cb.is_none() {
return;
}
let state = Self::get_veilid_state_inner(&*inner);
(update_cb.unwrap(), state)
};
update_cb(VeilidUpdate::Network(state));
}
// Determine if a local IP address has changed
// this means we should restart the low level network and and recreate all of our dial info
// Wait until we have received confirmation from N different peers

View File

@ -205,10 +205,6 @@ impl ReceiptManager {
let config = self.core().config();
let c = config.get();
let mut inner = self.inner.lock();
inner.max_server_signal_leases = c.network.leases.max_server_signal_leases as usize;
inner.max_server_relay_leases = c.network.leases.max_server_relay_leases as usize;
inner.max_client_signal_leases = c.network.leases.max_client_signal_leases as usize;
inner.max_client_relay_leases = c.network.leases.max_client_relay_leases as usize;
}
*/
Ok(())

View File

@ -22,7 +22,22 @@ impl RoutingTable {
out
}
pub fn debug_info_dialinfo(&self) -> String {
pub fn debug_info_dialinfo(&self, txt_format: bool) -> String {
if txt_format {
let mut out = String::new();
let gdis = self.dial_info_details(RoutingDomain::PublicInternet);
if gdis.is_empty() {
out += "No TXT Record DialInfo\n";
} else {
out += "TXT Record DialInfo:\n";
out += &format!("{}\n", self.node_id().encode());
for gdi in gdis {
out += &format!("{}\n", gdi.dial_info);
}
}
out
} else {
let ldis = self.dial_info_details(RoutingDomain::LocalNetwork);
let gdis = self.dial_info_details(RoutingDomain::PublicInternet);
let mut out = String::new();
@ -37,6 +52,7 @@ impl RoutingTable {
}
out
}
}
pub fn debug_info_entries(&self, limit: usize, min_state: BucketEntryState) -> String {
let inner = self.inner.lock();
let cur_ts = get_timestamp();

View File

@ -771,17 +771,36 @@ impl RoutingTable {
}
}
async fn resolve_bootstrap(&self, bootstrap: Vec<String>) -> Result<Vec<String>, String> {
let mut out = Vec::<String>::new();
for bh in bootstrap {
//
}
Ok(out)
}
async fn bootstrap_task_routine(self) -> Result<(), String> {
let bootstrap = {
let (bootstrap, bootstrap_nodes) = {
let c = self.config.get();
c.network.bootstrap.clone()
(
c.network.bootstrap.clone(),
c.network.bootstrap_nodes.clone(),
)
};
log_rtab!("--- bootstrap_task");
// If we aren't specifying a bootstrap node list explicitly, then pull from the bootstrap server(s)
let bootstrap_nodes = if !bootstrap_nodes.is_empty() {
bootstrap_nodes
} else {
// Resolve bootstrap servers and recurse their TXT entries
self.resolve_bootstrap(bootstrap).await?
};
// Map all bootstrap entries to a single key with multiple dialinfo
let mut bsmap: BTreeMap<DHTKey, Vec<DialInfoDetail>> = BTreeMap::new();
for b in bootstrap {
for b in bootstrap_nodes {
let ndis = NodeDialInfo::from_str(b.as_str())
.map_err(map_to_string)
.map_err(logthru_rtab!("Invalid dial info in bootstrap entry: {}", b))?;
@ -794,7 +813,7 @@ impl RoutingTable {
class: DialInfoClass::Direct, // Bootstraps are always directly reachable
});
}
log_rtab!(" bootstrap list: {:?}", bsmap);
log_rtab!(" bootstrap node dialinfo: {:?}", bsmap);
// Run all bootstrap operations concurrently
let mut unord = FuturesUnordered::new();

View File

@ -192,9 +192,12 @@ fn config_callback(key: String) -> ConfigCallbackReturn {
"network.max_connections_per_ip6_prefix_size" => Ok(Box::new(56u32)),
"network.max_connection_frequency_per_min" => Ok(Box::new(8u32)),
"network.client_whitelist_timeout_ms" => Ok(Box::new(300_000u32)),
"network.reverse_connection_receipt_time_ms" => Ok(Box::new(5_000u32)),
"network.hole_punch_receipt_time_ms" => Ok(Box::new(5_000u32)),
"network.node_id" => Ok(Box::new(dht::key::DHTKey::default())),
"network.node_id_secret" => Ok(Box::new(dht::key::DHTKeySecret::default())),
"network.bootstrap" => Ok(Box::new(Vec::<String>::new())),
"network.bootstrap_nodes" => Ok(Box::new(Vec::<String>::new())),
"network.routing_table.limit_over_attached" => Ok(Box::new(64u32)),
"network.routing_table.limit_fully_attached" => Ok(Box::new(32u32)),
"network.routing_table.limit_attached_strong" => Ok(Box::new(16u32)),
@ -255,10 +258,6 @@ fn config_callback(key: String) -> ConfigCallbackReturn {
"network.protocol.wss.listen_address" => Ok(Box::new("".to_owned())),
"network.protocol.wss.path" => Ok(Box::new(String::from("ws"))),
"network.protocol.wss.url" => Ok(Box::new(Option::<String>::None)),
"network.leases.max_server_signal_leases" => Ok(Box::new(256u32)),
"network.leases.max_server_relay_leases" => Ok(Box::new(8u32)),
"network.leases.max_client_signal_leases" => Ok(Box::new(2u32)),
"network.leases.max_client_relay_leases" => Ok(Box::new(2u32)),
_ => {
let err = format!("config key '{}' doesn't exist", key);
debug!("{}", err);
@ -318,9 +317,12 @@ pub async fn test_config() {
assert_eq!(inner.network.max_connections_per_ip6_prefix_size, 56u32);
assert_eq!(inner.network.max_connection_frequency_per_min, 8u32);
assert_eq!(inner.network.client_whitelist_timeout_ms, 300_000u32);
assert_eq!(inner.network.reverse_connection_receipt_time_ms, 5_000u32);
assert_eq!(inner.network.hole_punch_receipt_time_ms, 5_000u32);
assert!(!inner.network.node_id.valid);
assert!(!inner.network.node_id_secret.valid);
assert_eq!(inner.network.bootstrap, Vec::<String>::new());
assert_eq!(inner.network.bootstrap_nodes, Vec::<String>::new());
assert_eq!(inner.network.rpc.concurrency, 2u32);
assert_eq!(inner.network.rpc.queue_size, 128u32);
assert_eq!(inner.network.rpc.timeout_ms, 10_000u32);

View File

@ -79,10 +79,16 @@ impl VeilidAPI {
Ok(routing_table.debug_info_buckets(min_state))
}
async fn debug_dialinfo(&self, _args: String) -> Result<String, VeilidAPIError> {
async fn debug_dialinfo(&self, args: String) -> Result<String, VeilidAPIError> {
let args: Vec<String> = args.split_whitespace().map(|s| s.to_owned()).collect();
let is_txt = if args.len() == 1 {
args[0] == "txt"
} else {
false
};
// Dump routing table dialinfo
let routing_table = self.network_manager()?.routing_table();
Ok(routing_table.debug_info_dialinfo())
Ok(routing_table.debug_info_dialinfo(is_txt))
}
async fn debug_entries(&self, args: String) -> Result<String, VeilidAPIError> {
@ -147,7 +153,7 @@ impl VeilidAPI {
// Must be detached
if !matches!(
self.get_state().await?.attachment,
self.get_state().await?.attachment.state,
AttachmentState::Detached
) {
return Err(VeilidAPIError::Internal {
@ -168,7 +174,7 @@ impl VeilidAPI {
if args[0] == "buckets" {
// Must be detached
if matches!(
self.get_state().await?.attachment,
self.get_state().await?.attachment.state,
AttachmentState::Detached | AttachmentState::Detaching
) {
return Err(VeilidAPIError::Internal {
@ -194,7 +200,7 @@ impl VeilidAPI {
async fn debug_attach(&self, _args: String) -> Result<String, VeilidAPIError> {
if !matches!(
self.get_state().await?.attachment,
self.get_state().await?.attachment.state,
AttachmentState::Detached
) {
return Err(VeilidAPIError::Internal {
@ -209,7 +215,7 @@ impl VeilidAPI {
async fn debug_detach(&self, _args: String) -> Result<String, VeilidAPIError> {
if matches!(
self.get_state().await?.attachment,
self.get_state().await?.attachment.state,
AttachmentState::Detaching
) {
return Err(VeilidAPIError::Internal {

View File

@ -167,22 +167,37 @@ impl VeilidLogLevel {
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VeilidStateLog {
pub log_level: VeilidLogLevel,
pub message: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VeilidStateAttachment {
pub state: AttachmentState,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VeilidStateNetwork {
pub started: bool,
pub bps_down: u64,
pub bps_up: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind")]
pub enum VeilidUpdate {
Log {
log_level: VeilidLogLevel,
message: String,
},
Attachment {
state: AttachmentState,
},
Log(VeilidStateLog),
Attachment(VeilidStateAttachment),
Network(VeilidStateNetwork),
Shutdown,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VeilidState {
pub attachment: AttachmentState,
pub attachment: VeilidStateAttachment,
pub network: VeilidStateNetwork,
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
@ -745,8 +760,37 @@ impl fmt::Display for DialInfo {
match self {
DialInfo::UDP(di) => write!(f, "udp|{}", di.socket_address),
DialInfo::TCP(di) => write!(f, "tcp|{}", di.socket_address),
DialInfo::WS(di) => write!(f, "ws|{}|{}", di.socket_address, di.request),
DialInfo::WSS(di) => write!(f, "wss|{}|{}", di.socket_address, di.request),
DialInfo::WS(di) => {
let url = format!("ws://{}", di.request);
let split_url = SplitUrl::from_str(&url).unwrap();
match split_url.host {
SplitUrlHost::Hostname(_) => {
write!(f, "ws|{}|{}", di.socket_address.to_ip_addr(), di.request)
}
SplitUrlHost::IpAddr(a) => {
if di.socket_address.to_ip_addr() == a {
write!(f, "ws|{}", di.request)
} else {
panic!("resolved address does not match url: {}", di.request);
}
}
}
}
DialInfo::WSS(di) => {
let url = format!("wss://{}", di.request);
let split_url = SplitUrl::from_str(&url).unwrap();
match split_url.host {
SplitUrlHost::Hostname(_) => {
write!(f, "wss|{}|{}", di.socket_address.to_ip_addr(), di.request)
}
SplitUrlHost::IpAddr(_) => {
panic!(
"secure websockets can not use ip address in request: {}",
di.request
);
}
}
}
}
}
}
@ -767,18 +811,50 @@ impl FromStr for DialInfo {
Ok(DialInfo::tcp(socket_address))
}
"ws" => {
let (sa, rest) = rest.split_once('|').ok_or_else(|| {
parse_error!("DialInfo::from_str missing socket address '|' separator", s)
})?;
let socket_address = SocketAddress::from_str(sa)?;
DialInfo::try_ws(socket_address, format!("ws://{}", rest))
let url = format!("ws://{}", rest);
let split_url = SplitUrl::from_str(&url)
.map_err(|e| parse_error!(format!("unable to split WS url: {}", e), url))?;
if split_url.scheme != "ws" || !url.starts_with("ws://") {
return Err(parse_error!("incorrect scheme for WS dialinfo", url));
}
let url_port = split_url.port.unwrap_or(80u16);
match rest.split_once('|') {
Some((sa, rest)) => {
let address = Address::from_str(sa)?;
DialInfo::try_ws(
SocketAddress::new(address, url_port),
format!("ws://{}", rest),
)
}
None => {
let address = Address::from_str(&split_url.host.to_string())?;
DialInfo::try_ws(
SocketAddress::new(address, url_port),
format!("ws://{}", rest),
)
}
}
}
"wss" => {
let (sa, rest) = rest.split_once('|').ok_or_else(|| {
let url = format!("wss://{}", rest);
let split_url = SplitUrl::from_str(&url)
.map_err(|e| parse_error!(format!("unable to split WSS url: {}", e), url))?;
if split_url.scheme != "wss" || !url.starts_with("wss://") {
return Err(parse_error!("incorrect scheme for WSS dialinfo", url));
}
let url_port = split_url.port.unwrap_or(443u16);
let (a, rest) = rest.split_once('|').ok_or_else(|| {
parse_error!("DialInfo::from_str missing socket address '|' separator", s)
})?;
let socket_address = SocketAddress::from_str(sa)?;
DialInfo::try_wss(socket_address, format!("wss://{}", rest))
let address = Address::from_str(a)?;
DialInfo::try_wss(
SocketAddress::new(address, url_port),
format!("wss://{}", rest),
)
}
_ => Err(parse_error!("DialInfo::from_str has invalid scheme", s)),
}
@ -819,6 +895,14 @@ impl DialInfo {
url
));
}
if let SplitUrlHost::IpAddr(a) = split_url.host {
if socket_address.to_ip_addr() != a {
return Err(parse_error!(
format!("request address does not match socket address: {}", a),
socket_address
));
}
}
Ok(Self::WS(DialInfoWS {
socket_address: socket_address.to_canonical(),
request: url[5..].to_string(),
@ -1497,11 +1581,19 @@ impl VeilidAPI {
// get a full copy of the current state
pub async fn get_state(&self) -> Result<VeilidState, VeilidAPIError> {
let attachment_manager = self.attachment_manager()?;
let network_manager = attachment_manager.network_manager();
let attachment = attachment_manager.get_veilid_state();
let network = network_manager.get_veilid_state();
Ok(VeilidState {
attachment: attachment_manager.get_state(),
attachment,
network,
})
}
// get network connectedness
// connect to the network
pub async fn attach(&self) -> Result<(), VeilidAPIError> {
let attachment_manager = self.attachment_manager()?;

View File

@ -104,7 +104,6 @@ pub struct VeilidConfigDHT {
pub min_peer_count: u32,
pub min_peer_refresh_time_ms: u32,
pub validate_dial_info_receipt_time_ms: u32,
pub nearby_node_percentage: u32,
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
@ -117,13 +116,6 @@ pub struct VeilidConfigRPC {
pub max_route_hop_count: u8,
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct VeilidConfigLeases {
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(Default, Debug, Clone, Serialize, Deserialize)]
pub struct VeilidConfigRoutingTable {
pub limit_over_attached: u32,
@ -147,6 +139,7 @@ pub struct VeilidConfigNetwork {
pub node_id: key::DHTKey,
pub node_id_secret: key::DHTKeySecret,
pub bootstrap: Vec<String>,
pub bootstrap_nodes: Vec<String>,
pub routing_table: VeilidConfigRoutingTable,
pub rpc: VeilidConfigRPC,
pub dht: VeilidConfigDHT,
@ -157,7 +150,6 @@ pub struct VeilidConfigNetwork {
pub tls: VeilidConfigTLS,
pub application: VeilidConfigApplication,
pub protocol: VeilidConfigProtocol,
pub leases: VeilidConfigLeases,
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
@ -305,6 +297,7 @@ impl VeilidConfig {
get_config!(inner.network.max_connection_frequency_per_min);
get_config!(inner.network.client_whitelist_timeout_ms);
get_config!(inner.network.bootstrap);
get_config!(inner.network.bootstrap_nodes);
get_config!(inner.network.routing_table.limit_over_attached);
get_config!(inner.network.routing_table.limit_fully_attached);
get_config!(inner.network.routing_table.limit_attached_strong);
@ -365,10 +358,6 @@ impl VeilidConfig {
get_config!(inner.network.protocol.wss.listen_address);
get_config!(inner.network.protocol.wss.path);
get_config!(inner.network.protocol.wss.url);
get_config!(inner.network.leases.max_server_signal_leases);
get_config!(inner.network.leases.max_server_relay_leases);
get_config!(inner.network.leases.max_client_signal_leases);
get_config!(inner.network.leases.max_client_relay_leases);
}
// Validate settings
self.validate()?;

View File

@ -45,9 +45,12 @@ Future<VeilidConfig> getDefaultVeilidConfig() async {
maxConnectionsPerIp6PrefixSize: 56,
maxConnectionFrequencyPerMin: 8,
clientWhitelistTimeoutMs: 300000,
reverseConnectionReceiptTimeMs: 5000,
holePunchReceiptTimeMs: 5000,
nodeId: "",
nodeIdSecret: "",
bootstrap: [],
bootstrapNodes: [],
routingTable: VeilidConfigRoutingTable(
limitOverAttached: 64,
limitFullyAttached: 32,
@ -131,11 +134,6 @@ Future<VeilidConfig> getDefaultVeilidConfig() async {
url: null,
),
),
leases: VeilidConfigLeases(
maxServerSignalLeases: 256,
maxServerRelayLeases: 8,
maxClientSignalLeases: 2,
maxClientRelayLeases: 2,
)),
),
);
}

View File

@ -518,36 +518,6 @@ class VeilidConfigRoutingTable {
////////////
class VeilidConfigLeases {
int maxServerSignalLeases;
int maxServerRelayLeases;
int maxClientSignalLeases;
int maxClientRelayLeases;
VeilidConfigLeases(
{required this.maxServerSignalLeases,
required this.maxServerRelayLeases,
required this.maxClientSignalLeases,
required this.maxClientRelayLeases});
Map<String, dynamic> get json {
return {
'max_server_signal_leases': maxServerSignalLeases,
'max_server_relay_leases': maxServerRelayLeases,
'max_client_signal_leases': maxClientSignalLeases,
'max_client_relay_leases': maxClientRelayLeases
};
}
VeilidConfigLeases.fromJson(Map<String, dynamic> json)
: maxServerSignalLeases = json['max_server_signal_leases'],
maxServerRelayLeases = json['max_server_relay_leases'],
maxClientSignalLeases = json['max_client_signal_leases'],
maxClientRelayLeases = json['max_client_relay_leases'];
}
////////////
class VeilidConfigNetwork {
int connectionInitialTimeoutMs;
int connectionInactivityTimeoutMs;
@ -556,9 +526,12 @@ class VeilidConfigNetwork {
int maxConnectionsPerIp6PrefixSize;
int maxConnectionFrequencyPerMin;
int clientWhitelistTimeoutMs;
int reverseConnectionReceiptTimeMs;
int holePunchReceiptTimeMs;
String nodeId;
String nodeIdSecret;
List<String> bootstrap;
List<String> bootstrapNodes;
VeilidConfigRoutingTable routingTable;
VeilidConfigRPC rpc;
VeilidConfigDHT dht;
@ -569,7 +542,6 @@ class VeilidConfigNetwork {
VeilidConfigTLS tls;
VeilidConfigApplication application;
VeilidConfigProtocol protocol;
VeilidConfigLeases leases;
VeilidConfigNetwork({
required this.connectionInitialTimeoutMs,
@ -579,9 +551,12 @@ class VeilidConfigNetwork {
required this.maxConnectionsPerIp6PrefixSize,
required this.maxConnectionFrequencyPerMin,
required this.clientWhitelistTimeoutMs,
required this.reverseConnectionReceiptTimeMs,
required this.holePunchReceiptTimeMs,
required this.nodeId,
required this.nodeIdSecret,
required this.bootstrap,
required this.bootstrapNodes,
required this.routingTable,
required this.rpc,
required this.dht,
@ -592,7 +567,6 @@ class VeilidConfigNetwork {
required this.tls,
required this.application,
required this.protocol,
required this.leases,
});
Map<String, dynamic> get json {
@ -604,9 +578,12 @@ class VeilidConfigNetwork {
'max_connections_per_ip6_prefix_size': maxConnectionsPerIp6PrefixSize,
'max_connection_frequency_per_min': maxConnectionFrequencyPerMin,
'client_whitelist_timeout_ms': clientWhitelistTimeoutMs,
'reverse_connection_receipt_time_ms': reverseConnectionReceiptTimeMs,
'hole_punch_receipt_time_ms': holePunchReceiptTimeMs,
'node_id': nodeId,
'node_id_secret': nodeIdSecret,
'bootstrap': bootstrap,
'bootstrap_nodes': bootstrapNodes,
'routing_table': routingTable.json,
'rpc': rpc.json,
'dht': dht.json,
@ -617,7 +594,6 @@ class VeilidConfigNetwork {
'tls': tls.json,
'application': application.json,
'protocol': protocol.json,
'leases': leases.json,
};
}
@ -631,9 +607,13 @@ class VeilidConfigNetwork {
json['max_connections_per_ip6_prefix_size'],
maxConnectionFrequencyPerMin = json['max_connection_frequency_per_min'],
clientWhitelistTimeoutMs = json['client_whitelist_timeout_ms'],
reverseConnectionReceiptTimeMs =
json['reverse_connection_receipt_time_ms'],
holePunchReceiptTimeMs = json['hole_punch_receipt_time_ms'],
nodeId = json['node_id'],
nodeIdSecret = json['node_id_secret'],
bootstrap = json['bootstrap'],
bootstrapNodes = json['bootstrap_nodes'],
routingTable = VeilidConfigRoutingTable.fromJson(json['routing_table']),
rpc = VeilidConfigRPC.fromJson(json['rpc']),
dht = VeilidConfigDHT.fromJson(json['dht']),
@ -643,8 +623,7 @@ class VeilidConfigNetwork {
restrictedNatRetries = json['restricted_nat_retries'],
tls = VeilidConfigTLS.fromJson(json['tls']),
application = VeilidConfigApplication.fromJson(json['application']),
protocol = VeilidConfigProtocol.fromJson(json['protocol']),
leases = VeilidConfigLeases.fromJson(json['leases']);
protocol = VeilidConfigProtocol.fromJson(json['protocol']);
}
////////////
@ -824,6 +803,11 @@ abstract class VeilidUpdate {
{
return VeilidUpdateAttachment(attachmentStateFromJson(json["state"]));
}
case "Network":
{
return VeilidUpdateNetwork(
json["started"], json["bps_up"], json["bps_down"]);
}
default:
{
throw VeilidAPIExceptionInternal(
@ -846,16 +830,50 @@ class VeilidUpdateAttachment implements VeilidUpdate {
VeilidUpdateAttachment(this.state);
}
class VeilidUpdateNetwork implements VeilidUpdate {
final bool started;
final int bpsDown;
final int bpsUp;
//
VeilidUpdateNetwork(this.started, this.bpsDown, this.bpsUp);
}
//////////////////////////////////////
/// VeilidStateAttachment
class VeilidStateAttachment {
final AttachmentState state;
VeilidStateAttachment(this.state);
VeilidStateAttachment.fromJson(Map<String, dynamic> json)
: state = attachmentStateFromJson(json['state']);
}
//////////////////////////////////////
/// VeilidStateNetwork
class VeilidStateNetwork {
final bool started;
VeilidStateNetwork(this.started);
VeilidStateNetwork.fromJson(Map<String, dynamic> json)
: started = json['started'];
}
//////////////////////////////////////
/// VeilidState
class VeilidState {
final AttachmentState attachment;
final VeilidStateAttachment attachment;
final VeilidStateNetwork network;
VeilidState(this.attachment);
VeilidState(this.attachment, this.network);
VeilidState.fromJson(Map<String, dynamic> json)
: attachment = attachmentStateFromJson(json['attachment']);
: attachment = VeilidStateAttachment.fromJson(json['attachment']),
network = VeilidStateNetwork.fromJson(json['network']);
}
//////////////////////////////////////

View File

@ -33,12 +33,19 @@ ctrlc = "^3"
lazy_static = "^1"
bugsalot = "^0"
flume = { version = "^0", features = ["async"] }
rpassword = "^6"
[target.'cfg(windows)'.dependencies]
windows-service = "^0"
[target.'cfg(unix)'.dependencies]
daemonize = "^0"
signal-hook = "^0"
signal-hook-async-std = "^0"
nix = "^0"
[target.'cfg(target_os = "linux")'.dependencies]
systemd-journal-logger = "^0"
[dev-dependencies]
serial_test = "^0"

View File

@ -11,19 +11,27 @@ enum AttachmentState {
detaching @7;
}
struct Attachment {
struct VeilidStateAttachment {
state @0 :AttachmentState;
}
struct VeilidStateNetwork {
started @0 :Bool;
bpsDown @1 :UInt64;
bpsUp @2 :UInt64;
}
struct VeilidUpdate {
union {
attachment @0 :Attachment;
shutdown @1 :Void;
attachment @0 :VeilidStateAttachment;
network @1 :VeilidStateNetwork;
shutdown @2 :Void;
}
}
struct VeilidState {
attachment @0 :Attachment;
attachment @0 :VeilidStateAttachment;
network @1 :VeilidStateNetwork;
}
interface Registration {}

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 {
// 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;
}
// 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:
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(())
}
fn is_root() -> bool {
cfg_if::cfg_if! {
if #[cfg(unix)] {
use nix::unistd::Uid;
Uid::effective().is_root()
} else {
false
}
}
}
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") {
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") {
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_config_path.push("table_store");
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") {
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_config_path.push("block_store");
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") {
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_config_path.push("protected_store");
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 {