From ef1f5d7b5271d5194184d7391246dcd5d40fd619 Mon Sep 17 00:00:00 2001 From: John Smith Date: Mon, 16 May 2022 11:52:48 -0400 Subject: [PATCH] refactoring, more config, packaging --- Cargo.lock | 95 ++++++++- Earthfile | 35 +++- package/cargo_version.sh | 4 + package/debian/earthly_make_veilid_cli_deb.sh | 19 ++ .../debian/earthly_make_veilid_server_deb.sh | 25 +++ package/debian/veilid-cli/DEBIAN/control | 10 + package/debian/veilid-cli/DEBIAN/postinst | 6 + package/debian/veilid-server/DEBIAN/control | 9 + package/debian/veilid-server/DEBIAN/postinst | 47 +++++ package/debian/veilid-server/DEBIAN/postrm | 18 ++ package/debian/veilid-server/DEBIAN/prerm | 6 + package/linux/veilid-server.conf | 20 ++ package/replace_variable.sh | 7 + package/systemd/veilid-server.service | 20 ++ scripts/run_local_test.py | 4 +- veilid-cli/src/client_api_connection.rs | 23 ++- veilid-cli/src/command_processor.rs | 6 + veilid-cli/src/ui.rs | 30 ++- veilid-core/src/api_logger.rs | 4 +- veilid-core/src/attachment_manager.rs | 13 +- veilid-core/src/intf/native/network/mod.rs | 9 +- .../native/network/network_class_discovery.rs | 8 +- .../intf/native/network/start_protocols.rs | 10 +- veilid-core/src/network_manager.rs | 115 ++++++++--- veilid-core/src/receipt_manager.rs | 4 - veilid-core/src/routing_table/debug.rs | 40 ++-- veilid-core/src/routing_table/mod.rs | 27 ++- .../src/tests/common/test_veilid_config.rs | 10 +- veilid-core/src/veilid_api/debug.rs | 18 +- veilid-core/src/veilid_api/mod.rs | 130 ++++++++++-- veilid-core/src/veilid_config.rs | 15 +- veilid-flutter/example/lib/config.dart | 182 ++++++++-------- veilid-flutter/lib/veilid.dart | 94 +++++---- veilid-server/Cargo.toml | 7 + veilid-server/proto/veilid-client.capnp | 18 +- veilid-server/src/client_api.rs | 28 ++- veilid-server/src/cmdline.rs | 91 ++++++-- veilid-server/src/main.rs | 34 ++- veilid-server/src/server.rs | 72 +++++-- veilid-server/src/settings.rs | 194 ++++++++++-------- veilid-server/src/unix.rs | 102 ++++++++- veilid-server/src/veilid_logs.rs | 88 ++++++++ 42 files changed, 1329 insertions(+), 368 deletions(-) create mode 100755 package/cargo_version.sh create mode 100755 package/debian/earthly_make_veilid_cli_deb.sh create mode 100755 package/debian/earthly_make_veilid_server_deb.sh create mode 100644 package/debian/veilid-cli/DEBIAN/control create mode 100755 package/debian/veilid-cli/DEBIAN/postinst create mode 100644 package/debian/veilid-server/DEBIAN/control create mode 100755 package/debian/veilid-server/DEBIAN/postinst create mode 100755 package/debian/veilid-server/DEBIAN/postrm create mode 100755 package/debian/veilid-server/DEBIAN/prerm create mode 100644 package/linux/veilid-server.conf create mode 100755 package/replace_variable.sh create mode 100644 package/systemd/veilid-server.service diff --git a/Cargo.lock b/Cargo.lock index dfac024d..a75b0c23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Earthfile b/Earthfile index 990ca6ce..3ea58d52 100644 --- a/Earthfile +++ b/Earthfile @@ -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 \ No newline at end of file diff --git a/package/cargo_version.sh b/package/cargo_version.sh new file mode 100755 index 00000000..00da8f5d --- /dev/null +++ b/package/cargo_version.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +INPUTFILE=$1 +cat $1 | grep version | head -n 1 | cut -d\" -f 2 diff --git a/package/debian/earthly_make_veilid_cli_deb.sh b/package/debian/earthly_make_veilid_cli_deb.sh new file mode 100755 index 00000000..f5bba4a3 --- /dev/null +++ b/package/debian/earthly_make_veilid_cli_deb.sh @@ -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 \ No newline at end of file diff --git a/package/debian/earthly_make_veilid_server_deb.sh b/package/debian/earthly_make_veilid_server_deb.sh new file mode 100755 index 00000000..cea3337b --- /dev/null +++ b/package/debian/earthly_make_veilid_server_deb.sh @@ -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 \ No newline at end of file diff --git a/package/debian/veilid-cli/DEBIAN/control b/package/debian/veilid-cli/DEBIAN/control new file mode 100644 index 00000000..92222ba3 --- /dev/null +++ b/package/debian/veilid-cli/DEBIAN/control @@ -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 + \ No newline at end of file diff --git a/package/debian/veilid-cli/DEBIAN/postinst b/package/debian/veilid-cli/DEBIAN/postinst new file mode 100755 index 00000000..0f9b3ef7 --- /dev/null +++ b/package/debian/veilid-cli/DEBIAN/postinst @@ -0,0 +1,6 @@ +#!/bin/sh + +set -e + +# Set permissions +chmod 755 /usr/bin/veilid-cli diff --git a/package/debian/veilid-server/DEBIAN/control b/package/debian/veilid-server/DEBIAN/control new file mode 100644 index 00000000..53d2d3de --- /dev/null +++ b/package/debian/veilid-server/DEBIAN/control @@ -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 diff --git a/package/debian/veilid-server/DEBIAN/postinst b/package/debian/veilid-server/DEBIAN/postinst new file mode 100755 index 00000000..ae2a332d --- /dev/null +++ b/package/debian/veilid-server/DEBIAN/postinst @@ -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 diff --git a/package/debian/veilid-server/DEBIAN/postrm b/package/debian/veilid-server/DEBIAN/postrm new file mode 100755 index 00000000..b7401ae4 --- /dev/null +++ b/package/debian/veilid-server/DEBIAN/postrm @@ -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 diff --git a/package/debian/veilid-server/DEBIAN/prerm b/package/debian/veilid-server/DEBIAN/prerm new file mode 100755 index 00000000..9355ce1a --- /dev/null +++ b/package/debian/veilid-server/DEBIAN/prerm @@ -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 diff --git a/package/linux/veilid-server.conf b/package/linux/veilid-server.conf new file mode 100644 index 00000000..b47692c3 --- /dev/null +++ b/package/linux/veilid-server.conf @@ -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 diff --git a/package/replace_variable.sh b/package/replace_variable.sh new file mode 100755 index 00000000..5cef662a --- /dev/null +++ b/package/replace_variable.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +INPUTFILE=$1 +VARIABLE_NAME=$2 +VARIABLE_VALUE=$3 + +sed -i "s/\$$VARIABLE_NAME/$VARIABLE_VALUE/g" "$INPUTFILE" \ No newline at end of file diff --git a/package/systemd/veilid-server.service b/package/systemd/veilid-server.service new file mode 100644 index 00000000..ea9e5eeb --- /dev/null +++ b/package/systemd/veilid-server.service @@ -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 \ No newline at end of file diff --git a/scripts/run_local_test.py b/scripts/run_local_test.py index e0ce3717..3a3216ab 100755 --- a/scripts/run_local_test.py +++ b/scripts/run_local_test.py @@ -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") diff --git a/veilid-cli/src/client_api_connection.rs b/veilid-cli/src/client_api_connection.rs index 33bf1810..2a2f073f 100644 --- a/veilid-cli/src/client_api_connection.rs +++ b/veilid-cli/src/client_api_connection.rs @@ -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(()) } diff --git a/veilid-cli/src/command_processor.rs b/veilid-cli/src/command_processor.rs index aee951cf..bfc51bc7 100644 --- a/veilid-cli/src/command_processor.rs +++ b/veilid-cli/src/command_processor.rs @@ -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); } diff --git a/veilid-cli/src/ui.rs b/veilid-cli/src/ui.rs index 38eb05a4..5227fa0f 100644 --- a/veilid-cli/src/ui.rs +++ b/veilid-cli/src/ui.rs @@ -49,6 +49,8 @@ pub type UICallback = Box; struct UIState { attachment_state: Dirty, + network_started: Dirty, + network_down_up: Dirty<(f32, f32)>, connection_state: Dirty, } @@ -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); diff --git a/veilid-core/src/api_logger.rs b/veilid-core/src/api_logger.rs index d2fe2e59..8cffe706 100644 --- a/veilid-core/src/api_logger.rs +++ b/veilid-core/src/api_logger.rs @@ -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, - }) + })) } } } diff --git a/veilid-core/src/attachment_manager.rs b/veilid-core/src/attachment_manager.rs index f0839a76..dfc5f4e1 100644 --- a/veilid-core/src/attachment_manager.rs +++ b/veilid-core/src/attachment_manager.rs @@ -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(), + } + } } diff --git a/veilid-core/src/intf/native/network/mod.rs b/veilid-core/src/intf/native/network/mod.rs index d814dd3e..18646b87 100644 --- a/veilid-core/src/intf/native/network/mod.rs +++ b/veilid-core/src/intf/native/network/mod.rs @@ -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() } } diff --git a/veilid-core/src/intf/native/network/network_class_discovery.rs b/veilid-core/src/intf/native/network/network_class_discovery.rs index 117095aa..fe6638f7 100644 --- a/veilid-core/src/intf/native/network/network_class_discovery.rs +++ b/veilid-core/src/intf/native/network/network_class_discovery.rs @@ -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; - self.inner.lock().network_class = network_class; - - log_net!(debug "network class set to {:?}", network_class); + if network_class != old_network_class { + self.inner.lock().network_class = network_class; + log_net!(debug "network class changed to {:?}", network_class); + } // send updates to everyone self.routing_table().send_node_info_updates(); diff --git a/veilid-core/src/intf/native/network/start_protocols.rs b/veilid-core/src/intf/native/network/start_protocols.rs index 0982a950..81c2a139 100644 --- a/veilid-core/src/intf/native/network/start_protocols.rs +++ b/veilid-core/src/intf/native/network/start_protocols.rs @@ -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 = 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 = HashSet::new(); diff --git a/veilid-core/src/network_manager.rs b/veilid-core/src/network_manager.rs index b278272b..2b3b4d77 100644 --- a/veilid-core/src/network_manager.rs +++ b/veilid-core/src/network_manager.rs @@ -90,6 +90,7 @@ pub enum SendDataKind { struct NetworkManagerInner { routing_table: Option, components: Option, + update_callback: Option, stats: NetworkManagerStats, client_whitelist: LruCache, relay_node: Option, @@ -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; + { + 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,35 +1173,41 @@ 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(); + { + let inner = &mut *self.inner.lock(); - // Roll the low level network transfer stats for our address - inner - .stats - .self_stats - .transfer_stats_accounting - .roll_transfers(last_ts, cur_ts, &mut inner.stats.self_stats.transfer_stats); + // Roll the low level network transfer stats for our address + inner + .stats + .self_stats + .transfer_stats_accounting + .roll_transfers(last_ts, cur_ts, &mut inner.stats.self_stats.transfer_stats); - // Roll all per-address transfers - let mut dead_addrs: HashSet = HashSet::new(); - for (addr, stats) in &mut inner.stats.per_address_stats { - stats.transfer_stats_accounting.roll_transfers( - last_ts, - cur_ts, - &mut stats.transfer_stats, - ); + // Roll all per-address transfers + let mut dead_addrs: HashSet = HashSet::new(); + for (addr, stats) in &mut inner.stats.per_address_stats { + stats.transfer_stats_accounting.roll_transfers( + last_ts, + cur_ts, + &mut stats.transfer_stats, + ); - // While we're here, lets see if this address has timed out - if cur_ts - stats.last_seen_ts >= IPADDR_MAX_INACTIVE_DURATION_US { - // it's dead, put it in the dead list - dead_addrs.insert(*addr); + // While we're here, lets see if this address has timed out + if cur_ts - stats.last_seen_ts >= IPADDR_MAX_INACTIVE_DURATION_US { + // it's dead, put it in the dead list + dead_addrs.insert(*addr); + } + } + + // Remove the dead addresses from our tables + for da in &dead_addrs { + inner.stats.per_address_stats.remove(da); } } - // Remove the dead addresses from our tables - 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 diff --git a/veilid-core/src/receipt_manager.rs b/veilid-core/src/receipt_manager.rs index 1bb635a1..5925a37c 100644 --- a/veilid-core/src/receipt_manager.rs +++ b/veilid-core/src/receipt_manager.rs @@ -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(()) diff --git a/veilid-core/src/routing_table/debug.rs b/veilid-core/src/routing_table/debug.rs index 629586b4..2dc2e26c 100644 --- a/veilid-core/src/routing_table/debug.rs +++ b/veilid-core/src/routing_table/debug.rs @@ -22,20 +22,36 @@ impl RoutingTable { out } - pub fn debug_info_dialinfo(&self) -> String { - let ldis = self.dial_info_details(RoutingDomain::LocalNetwork); - let gdis = self.dial_info_details(RoutingDomain::PublicInternet); - let mut out = String::new(); + pub fn debug_info_dialinfo(&self, txt_format: bool) -> String { + if txt_format { + let mut out = String::new(); - out += "Local Network Dial Info Details:\n"; - for (n, ldi) in ldis.iter().enumerate() { - out += &format!(" {:>2}: {:?}\n", n, ldi); + 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(); + + out += "Local Network Dial Info Details:\n"; + for (n, ldi) in ldis.iter().enumerate() { + out += &format!(" {:>2}: {:?}\n", n, ldi); + } + out += "Public Internet Dial Info Details:\n"; + for (n, gdi) in gdis.iter().enumerate() { + out += &format!(" {:>2}: {:?}\n", n, gdi); + } + out } - out += "Public Internet Dial Info Details:\n"; - for (n, gdi) in gdis.iter().enumerate() { - out += &format!(" {:>2}: {:?}\n", n, gdi); - } - out } pub fn debug_info_entries(&self, limit: usize, min_state: BucketEntryState) -> String { let inner = self.inner.lock(); diff --git a/veilid-core/src/routing_table/mod.rs b/veilid-core/src/routing_table/mod.rs index 557bf658..c04978da 100644 --- a/veilid-core/src/routing_table/mod.rs +++ b/veilid-core/src/routing_table/mod.rs @@ -771,17 +771,36 @@ impl RoutingTable { } } + async fn resolve_bootstrap(&self, bootstrap: Vec) -> Result, String> { + let mut out = Vec::::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> = 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(); diff --git a/veilid-core/src/tests/common/test_veilid_config.rs b/veilid-core/src/tests/common/test_veilid_config.rs index adc78e49..796c956c 100644 --- a/veilid-core/src/tests/common/test_veilid_config.rs +++ b/veilid-core/src/tests/common/test_veilid_config.rs @@ -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::::new())), + "network.bootstrap_nodes" => Ok(Box::new(Vec::::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::::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::::new()); + assert_eq!(inner.network.bootstrap_nodes, Vec::::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); diff --git a/veilid-core/src/veilid_api/debug.rs b/veilid-core/src/veilid_api/debug.rs index e136bc07..1b179ce7 100644 --- a/veilid-core/src/veilid_api/debug.rs +++ b/veilid-core/src/veilid_api/debug.rs @@ -79,10 +79,16 @@ impl VeilidAPI { Ok(routing_table.debug_info_buckets(min_state)) } - async fn debug_dialinfo(&self, _args: String) -> Result { + async fn debug_dialinfo(&self, args: String) -> Result { + let args: Vec = 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 { @@ -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 { 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 { if matches!( - self.get_state().await?.attachment, + self.get_state().await?.attachment.state, AttachmentState::Detaching ) { return Err(VeilidAPIError::Internal { diff --git a/veilid-core/src/veilid_api/mod.rs b/veilid-core/src/veilid_api/mod.rs index b8c646c2..91f744dd 100644 --- a/veilid-core/src/veilid_api/mod.rs +++ b/veilid-core/src/veilid_api/mod.rs @@ -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 { 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()?; diff --git a/veilid-core/src/veilid_config.rs b/veilid-core/src/veilid_config.rs index 0b2d49e5..ea3d0c71 100644 --- a/veilid-core/src/veilid_config.rs +++ b/veilid-core/src/veilid_config.rs @@ -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, + pub bootstrap_nodes: Vec, 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()?; diff --git a/veilid-flutter/example/lib/config.dart b/veilid-flutter/example/lib/config.dart index 4ab95f3f..e1d3754c 100644 --- a/veilid-flutter/example/lib/config.dart +++ b/veilid-flutter/example/lib/config.dart @@ -38,104 +38,102 @@ Future getDefaultVeilidConfig() async { delete: false, ), network: VeilidConfigNetwork( + connectionInitialTimeoutMs: 2000, + connectionInactivityTimeoutMs: 60000, + maxConnectionsPerIp4: 8, + maxConnectionsPerIp6Prefix: 8, + maxConnectionsPerIp6PrefixSize: 56, + maxConnectionFrequencyPerMin: 8, + clientWhitelistTimeoutMs: 300000, + reverseConnectionReceiptTimeMs: 5000, + holePunchReceiptTimeMs: 5000, + nodeId: "", + nodeIdSecret: "", + bootstrap: [], + bootstrapNodes: [], + routingTable: VeilidConfigRoutingTable( + limitOverAttached: 64, + limitFullyAttached: 32, + limitAttachedStrong: 16, + limitAttachedGood: 8, + limitAttachedWeak: 4, + ), + rpc: VeilidConfigRPC( + concurrency: 0, + queueSize: 1024, + maxTimestampBehindMs: 10000, + maxTimestampAheadMs: 10000, + timeoutMs: 10000, + maxRouteHopCount: 7, + ), + dht: VeilidConfigDHT( + resolveNodeTimeoutMs: null, + resolveNodeCount: 20, + resolveNodeFanout: 3, + maxFindNodeCount: 20, + getValueTimeoutMs: null, + getValueCount: 20, + getValueFanout: 3, + setValueTimeoutMs: null, + setValueCount: 20, + setValueFanout: 5, + minPeerCount: 20, + minPeerRefreshTimeMs: 2000, + validateDialInfoReceiptTimeMs: 5000, + ), + upnp: true, + natpmp: true, + enableLocalPeerScope: false, + restrictedNatRetries: 3, + tls: VeilidConfigTLS( + certificatePath: "", + privateKeyPath: "", connectionInitialTimeoutMs: 2000, - connectionInactivityTimeoutMs: 60000, - maxConnectionsPerIp4: 8, - maxConnectionsPerIp6Prefix: 8, - maxConnectionsPerIp6PrefixSize: 56, - maxConnectionFrequencyPerMin: 8, - clientWhitelistTimeoutMs: 300000, - nodeId: "", - nodeIdSecret: "", - bootstrap: [], - routingTable: VeilidConfigRoutingTable( - limitOverAttached: 64, - limitFullyAttached: 32, - limitAttachedStrong: 16, - limitAttachedGood: 8, - limitAttachedWeak: 4, - ), - rpc: VeilidConfigRPC( - concurrency: 0, - queueSize: 1024, - maxTimestampBehindMs: 10000, - maxTimestampAheadMs: 10000, - timeoutMs: 10000, - maxRouteHopCount: 7, - ), - dht: VeilidConfigDHT( - resolveNodeTimeoutMs: null, - resolveNodeCount: 20, - resolveNodeFanout: 3, - maxFindNodeCount: 20, - getValueTimeoutMs: null, - getValueCount: 20, - getValueFanout: 3, - setValueTimeoutMs: null, - setValueCount: 20, - setValueFanout: 5, - minPeerCount: 20, - minPeerRefreshTimeMs: 2000, - validateDialInfoReceiptTimeMs: 5000, - ), - upnp: true, - natpmp: true, - enableLocalPeerScope: false, - restrictedNatRetries: 3, - tls: VeilidConfigTLS( - certificatePath: "", - privateKeyPath: "", - connectionInitialTimeoutMs: 2000, - ), - application: VeilidConfigApplication( - https: VeilidConfigHTTPS( - enabled: false, - listenAddress: "", - path: "", - url: null, - ), - http: VeilidConfigHTTP( - enabled: false, - listenAddress: "", - path: "", - url: null, - )), - protocol: VeilidConfigProtocol( - udp: VeilidConfigUDP( - enabled: !kIsWeb, - socketPoolSize: 0, + ), + application: VeilidConfigApplication( + https: VeilidConfigHTTPS( + enabled: false, listenAddress: "", - publicAddress: null, - ), - tcp: VeilidConfigTCP( - connect: !kIsWeb, - listen: !kIsWeb, - maxConnections: 32, - listenAddress: "", - publicAddress: null, - ), - ws: VeilidConfigWS( - connect: true, - listen: !kIsWeb, - maxConnections: 16, - listenAddress: "", - path: "ws", + path: "", url: null, ), - wss: VeilidConfigWSS( - connect: true, - listen: false, - maxConnections: 16, + http: VeilidConfigHTTP( + enabled: false, listenAddress: "", - path: "ws", + path: "", url: null, - ), + )), + protocol: VeilidConfigProtocol( + udp: VeilidConfigUDP( + enabled: !kIsWeb, + socketPoolSize: 0, + listenAddress: "", + publicAddress: null, ), - leases: VeilidConfigLeases( - maxServerSignalLeases: 256, - maxServerRelayLeases: 8, - maxClientSignalLeases: 2, - maxClientRelayLeases: 2, - )), + tcp: VeilidConfigTCP( + connect: !kIsWeb, + listen: !kIsWeb, + maxConnections: 32, + listenAddress: "", + publicAddress: null, + ), + ws: VeilidConfigWS( + connect: true, + listen: !kIsWeb, + maxConnections: 16, + listenAddress: "", + path: "ws", + url: null, + ), + wss: VeilidConfigWSS( + connect: true, + listen: false, + maxConnections: 16, + listenAddress: "", + path: "ws", + url: null, + ), + ), + ), ); } diff --git a/veilid-flutter/lib/veilid.dart b/veilid-flutter/lib/veilid.dart index 906e581c..3789d32b 100644 --- a/veilid-flutter/lib/veilid.dart +++ b/veilid-flutter/lib/veilid.dart @@ -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 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 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 bootstrap; + List 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 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 json) + : state = attachmentStateFromJson(json['state']); +} + +////////////////////////////////////// +/// VeilidStateNetwork + +class VeilidStateNetwork { + final bool started; + + VeilidStateNetwork(this.started); + + VeilidStateNetwork.fromJson(Map 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 json) - : attachment = attachmentStateFromJson(json['attachment']); + : attachment = VeilidStateAttachment.fromJson(json['attachment']), + network = VeilidStateNetwork.fromJson(json['network']); } ////////////////////////////////////// diff --git a/veilid-server/Cargo.toml b/veilid-server/Cargo.toml index fe9efebb..5baeaddc 100644 --- a/veilid-server/Cargo.toml +++ b/veilid-server/Cargo.toml @@ -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" diff --git a/veilid-server/proto/veilid-client.capnp b/veilid-server/proto/veilid-client.capnp index 2e38175e..0b8544ae 100644 --- a/veilid-server/proto/veilid-client.capnp +++ b/veilid-server/proto/veilid-client.capnp @@ -11,19 +11,27 @@ enum AttachmentState { detaching @7; } -struct Attachment { - state @0 :AttachmentState; +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 {} diff --git a/veilid-server/src/client_api.rs b/veilid-server/src/client_api.rs index 364ee24a..043c84d1 100644 --- a/veilid-server/src/client_api.rs +++ b/veilid-server/src/client_api.rs @@ -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 --------------------------------- diff --git a/veilid-server/src/cmdline.rs b/veilid-server/src/cmdline.rs index 8fe1d8b8..572ac7b0 100644 --- a/veilid-server/src/cmdline.rs +++ b/veilid-server/src/cmdline.rs @@ -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 { let matches = Command::new("veilid-server") @@ -44,8 +45,8 @@ fn do_clap_matches(default_config_path: &OsStr) -> Result Result Result 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 = 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 = 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 diff --git a/veilid-server/src/main.rs b/veilid-server/src/main.rs index de48254f..061f3b63 100644 --- a/veilid-server/src/main.rs +++ b/veilid-server/src/main.rs @@ -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 }) } diff --git a/veilid-server/src/server.rs b/veilid-server/src/server.rs index 5f624b2a..9c5db8d9 100644 --- a/veilid-server/src/server.rs +++ b/veilid-server/src/server.rs @@ -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>> = 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 } diff --git a/veilid-server/src/settings.rs b/veilid-server/src/settings.rs index 4702a7f9..9bf21181 100644 --- a/veilid-server/src/settings.rs +++ b/veilid-server/src/settings.rs @@ -16,12 +16,16 @@ use veilid_core::xx::*; pub fn load_default_config() -> Result { 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, + pub bootstrap: Vec, + pub bootstrap_nodes: Vec, 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, + pub chroot: Option, + pub working_directory: Option, + pub user: Option, + pub group: Option, + pub stdout_file: Option, + pub stderr_file: Option, +} + #[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); } } diff --git a/veilid-server/src/unix.rs b/veilid-server/src/unix.rs index 742f1671..01c7e915 100644 --- a/veilid-server/src/unix.rs +++ b/veilid-server/src/unix.rs @@ -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 + }) } diff --git a/veilid-server/src/veilid_logs.rs b/veilid-server/src/veilid_logs.rs index ed5e82b2..eb994609 100644 --- a/veilid-server/src/veilid_logs.rs +++ b/veilid-server/src/veilid_logs.rs @@ -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, } +cfg_if! { + if #[cfg(target_os = "linux")] { + use systemd_journal_logger::JournalLog; + pub struct SystemLogger { + level_filter: LevelFilter, + config: Config, + journal_log: JournalLog, + } + + impl SystemLogger { + pub fn new(level_filter: LevelFilter, config: Config) -> Box { + 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) -> Box { + Box::new(*self) + } + } + } +} + impl VeilidLogs { pub fn setup_normal_logs(settings: Settings) -> Result { 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 {