Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions platforms/tauri/scripts/install-systemd.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/bin/bash
# install-systemd.sh — Install NetBoozt headless DNS failover as a systemd user service.
#
# Usage: ./install-systemd.sh
# Requires: systemd user session, NetBoozt binary at ~/.local/bin/NetBoozt
#
# By LOUST (www.loust.pro)

set -e

BIN_PATH="${HOME}/.local/bin/NetBoozt"
SERVICE_FILE="${HOME}/.config/systemd/user/netboozt-dns.service"

echo "==> NetBoozt DNS Failover — systemd user service installer"

# Check binary exists
if [[ ! -x "$BIN_PATH" ]]; then
echo "ERROR: NetBoozt binary not found at $BIN_PATH"
echo "Build it first: cargo build --release --bin netboozt-headless"
exit 1
fi

# Check binary is actually the headless one (basic sanity)
if ! "$BIN_PATH" --help >/dev/null 2>&1; then
echo "WARNING: Binary at $BIN_PATH does not respond to --help"
fi

# Ensure systemd user dir exists
mkdir -p "${HOME}/.config/systemd/user"

# Install unit file
echo "Installing $SERVICE_FILE ..."
cp "$(dirname "$0")/netboozt-dns.service" "$SERVICE_FILE"
chmod 644 "$SERVICE_FILE"

# Reload systemd, enable and start
echo "==> Reloading systemd user daemon..."
systemctl --user daemon-reload

echo "==> Enabling netboozt-dns.service ..."
systemctl --user enable netboozt-dns.service

echo "==> Starting netboozt-dns.service ..."
systemctl --user start netboozt-dns.service

# Verify
if systemctl --user is-active --quiet netboozt-dns.service; then
echo ""
echo "✅ netboozt-dns.service is active."
echo ""
echo "Useful commands:"
echo " systemctl --user status netboozt-dns.service # view status"
echo " journalctl --user -u netboozt-dns.service # view logs"
echo " systemctl --user stop netboozt-dns.service # stop"
echo " systemctl --user restart netboozt-dns.service # restart"
else
echo ""
echo "⚠️ Service started but may not be fully active yet. Check:"
echo " systemctl --user status netboozt-dns.service"
echo " journalctl --user -u netboozt-dns.service"
fi
27 changes: 27 additions & 0 deletions platforms/tauri/scripts/netboozt-dns.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[Unit]
Description=NetBoozt DNS Failover — headless DNS intelligence daemon
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
# Runs the GUI binary in headless mode (DNS intelligence only, no window)
ExecStart=/home/lou/.local/bin/NetBoozt --headless
Restart=on-failure
RestartSec=10
# Graceful stop
TimeoutStopSec=30

# Security: run as user, no new privileges
NoNewPrivileges=true

# Environment
Environment="RUST_LOG=info"

# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=netboozt-dns

[Install]
WantedBy=default.target
36 changes: 36 additions & 0 deletions platforms/tauri/scripts/uninstall-systemd.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/bash
# uninstall-systemd.sh — Remove the NetBoozt headless DNS failover systemd user service.
#
# Usage: ./uninstall-systemd.sh
#
# By LOUST (www.loust.pro)

set -e

SERVICE_FILE="${HOME}/.config/systemd/user/netboozt-dns.service"

echo "==> NetBoozt DNS Failover — systemd user service uninstaller"

# Stop and disable
echo "==> Stopping netboozt-dns.service ..."
systemctl --user stop netboozt-dns.service 2>/dev/null || true

echo "==> Disabling netboozt-dns.service ..."
systemctl --user disable netboozt-dns.service 2>/dev/null || true

# Reload systemd
echo "==> Reloading systemd user daemon ..."
systemctl --user daemon-reload

# Remove unit file
if [[ -f "$SERVICE_FILE" ]]; then
echo "Removing $SERVICE_FILE ..."
rm -f "$SERVICE_FILE"
else
echo "Service file not found at $SERVICE_FILE (already removed or never installed)"
fi

echo ""
echo "✅ netboozt-dns.service uninstalled."
echo ""
echo "Note: The NetBoozt binary at ~/.local/bin/NetBoozt was not modified."
5 changes: 5 additions & 0 deletions platforms/tauri/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ tauri-plugin-fs = "2"
tauri-plugin-dialog = "2"
tauri-plugin-shell = "2"
tauri-plugin-notification = "2"
signal-hook = "0.3"

# Windows-specific
[target.'cfg(windows)'.dependencies]
Expand All @@ -43,3 +44,7 @@ windows = { version = "0.52", features = [
[features]
# This feature is used for production builds or when a dev server is not specified
custom-protocol = ["tauri/custom-protocol"]

[[bin]]
name = "netboozt-headless"
path = "src/bin/netboozt-headless.rs"
50 changes: 50 additions & 0 deletions platforms/tauri/src-tauri/src/bin/netboozt-headless.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//! `netboozt-headless` — headless DNS failover daemon.
//!
//! Starts the DNS Intelligence background service and waits for SIGINT/SIGTERM
//! to shut down gracefully. Designed to run as a systemd user service
//! (`netboozt-dns.service`) so DNS failover survives lid-close / logout on Linux.
//!
//! Usage:
//! netboozt-headless
//!
//! The binary reads no configuration — it uses `DnsIntelConfig::default()`.
//! Override env vars (future): `NETBOOZT_CHECK_INTERVAL`, `NETBOOZT_PARALLEL_WORKERS`.

use std::process;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;

fn main() {
// Initialize logger
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info"))
.format_timestamp_millis()
.init();

log::info!("netboozt-headless v{} starting", env!("CARGO_PKG_VERSION"));

// Shared flag for graceful shutdown — Arc allows both handlers to reference it
let running = Arc::new(AtomicBool::new(true));
let running_sigint = running.clone();
let running_sigterm = running.clone();

// Start DNS Intelligence service
netboozt::start_dns_intelligence();

// Register signal handlers for graceful shutdown using signal-hook
signal_hook::flag::register(signal_hook::consts::SIGINT, running_sigint)
.expect("Failed to register SIGINT handler");
signal_hook::flag::register(signal_hook::consts::SIGTERM, running_sigterm)
.expect("Failed to register SIGTERM handler");

// Wait until signaled to stop
while running.load(Ordering::SeqCst) {
std::thread::sleep(Duration::from_secs(1));
}

// Graceful shutdown
log::info!("Stopping DNS Intelligence service...");
netboozt::stop_dns_intelligence();
log::info!("netboozt-headless stopped");
process::exit(0);
}
12 changes: 12 additions & 0 deletions platforms/tauri/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//! NetBoozt Library
//!
//! Re-exports services for use by binaries (main app + headless daemon).
//!
//! By LOUST (www.loust.pro)

mod services;

pub use services::{
diagnostics, dns, dns_intelligence, get_dns_intelligence, notifications,
start_dns_intelligence, stop_dns_intelligence, DnsIntelSummary, DnsMetrics, FailoverEvent,
};
26 changes: 0 additions & 26 deletions platforms/tauri/src-tauri/src/services/dns_intelligence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,11 +295,7 @@ impl DnsIntelligence {
s.spawn(move || {
chunk
.iter()
<<<<<<< HEAD
.map(|(addr, _)| {
=======
.map(|(addr, _, _)| {
>>>>>>> 1863ed6 (feat(dns): add tier labels and parallel Workers config to DNS metrics)
let (success, ping_ms, resolve_ms) = Self::check_single_dns(addr);
(addr.to_string(), success, ping_ms, resolve_ms)
})
Expand Down Expand Up @@ -888,11 +884,7 @@ mod tests {

let results: Vec<String> = DNS_SERVERS
.chunks(chunk_size)
<<<<<<< HEAD
.flat_map(|chunk| chunk.iter().map(|(addr, _)| addr.to_string()))
=======
.flat_map(|chunk| chunk.iter().map(|(addr, _, _)| addr.to_string()))
>>>>>>> 1863ed6 (feat(dns): add tier labels and parallel Workers config to DNS metrics)
.collect();

assert_eq!(results.len(), DNS_SERVERS.len());
Expand All @@ -904,11 +896,7 @@ mod tests {
let metrics: Arc<RwLock<HashMap<String, DnsMetrics>>> = Arc::new(RwLock::new(
DNS_SERVERS
.iter()
<<<<<<< HEAD
.map(|(addr, name)| (addr.to_string(), DnsMetrics::new(addr, name)))
=======
.map(|(addr, name, tier)| (addr.to_string(), DnsMetrics::new(addr, name, *tier)))
>>>>>>> 1863ed6 (feat(dns): add tier labels and parallel Workers config to DNS metrics)
.collect(),
));
let history: Arc<Mutex<Vec<HistoryEntry>>> = Arc::new(Mutex::new(Vec::new()));
Expand All @@ -926,11 +914,7 @@ mod tests {
let metrics: Arc<RwLock<HashMap<String, DnsMetrics>>> = Arc::new(RwLock::new(
DNS_SERVERS
.iter()
<<<<<<< HEAD
.map(|(addr, name)| (addr.to_string(), DnsMetrics::new(addr, name)))
=======
.map(|(addr, name, tier)| (addr.to_string(), DnsMetrics::new(addr, name, *tier)))
>>>>>>> 1863ed6 (feat(dns): add tier labels and parallel Workers config to DNS metrics)
.collect(),
));
let history: Arc<Mutex<Vec<HistoryEntry>>> = Arc::new(Mutex::new(Vec::new()));
Expand Down Expand Up @@ -967,11 +951,7 @@ mod tests {
/// Verify that consecutive_failures increments on failure and resets on success.
#[test]
fn test_consecutive_failures_behavior() {
<<<<<<< HEAD
let mut metrics = DnsMetrics::new("9.9.9.9", "Quad9");
=======
let mut metrics = DnsMetrics::new("9.9.9.9", "Quad9", 3);
>>>>>>> 1863ed6 (feat(dns): add tier labels and parallel Workers config to DNS metrics)
assert_eq!(metrics.consecutive_failures, 0);

// Simulate a failed check
Expand All @@ -993,14 +973,8 @@ mod tests {
#[test]
fn test_dns_servers_count() {
assert_eq!(DNS_SERVERS.len(), 11);
<<<<<<< HEAD
// AdGuard is present
assert!(DNS_SERVERS.contains(&("94.140.14.14", "AdGuard")));
assert!(DNS_SERVERS.contains(&("94.140.15.15", "AdGuard Secondary")));
=======
// AdGuard is present (3-tuple: addr, name, tier=5)
assert!(DNS_SERVERS.contains(&("94.140.14.14", "AdGuard", 5)));
assert!(DNS_SERVERS.contains(&("94.140.15.15", "AdGuard Secondary", 5)));
>>>>>>> 1863ed6 (feat(dns): add tier labels and parallel Workers config to DNS metrics)
}
}
Loading