feat: detect file changes

This commit is contained in:
2026-07-07 19:21:47 +02:00
parent d2f03dcd2f
commit df017de29d
4 changed files with 259 additions and 16 deletions
Generated
+185 -5
View File
@@ -190,7 +190,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys",
"windows-sys 0.61.2",
]
[[package]]
@@ -199,6 +199,15 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "fsevent-sys"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
dependencies = [
"libc",
]
[[package]]
name = "generic-array"
version = "0.14.7"
@@ -244,6 +253,26 @@ dependencies = [
"hashbrown 0.16.1",
]
[[package]]
name = "inotify"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd854a95a4ac672fed8c054136039fd32c22cf039ff09ead7280afe920486483"
dependencies = [
"bitflags",
"inotify-sys",
"libc",
]
[[package]]
name = "inotify-sys"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c033f80b2c113cdf91ab7a33faa9cbc014726dcad99880c8609af2a370edf37d"
dependencies = [
"libc",
]
[[package]]
name = "itoa"
version = "1.0.18"
@@ -261,6 +290,26 @@ dependencies = [
"serde",
]
[[package]]
name = "kqueue"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "273c0752728918e0ac4976f2b275b6fefb9ecd400585dec929419f3844cd87b5"
dependencies = [
"kqueue-sys",
"libc",
]
[[package]]
name = "kqueue-sys"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087"
dependencies = [
"bitflags",
"libc",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
@@ -310,8 +359,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys",
"windows-sys 0.61.2",
]
[[package]]
@@ -321,19 +371,47 @@ dependencies = [
"anyhow",
"config",
"dotenv",
"notify",
"serde",
"tokio",
"tracing",
"tracing-subscriber",
]
[[package]]
name = "notify"
version = "8.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3"
dependencies = [
"bitflags",
"fsevent-sys",
"inotify",
"kqueue",
"libc",
"log",
"mio",
"notify-types",
"walkdir",
"windows-sys 0.60.2",
]
[[package]]
name = "notify-types"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a"
dependencies = [
"bitflags",
]
[[package]]
name = "nu-ansi-term"
version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys",
"windows-sys 0.61.2",
]
[[package]]
@@ -498,6 +576,15 @@ dependencies = [
"ordered-multimap",
]
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
@@ -611,7 +698,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51"
dependencies = [
"libc",
"windows-sys",
"windows-sys 0.61.2",
]
[[package]]
@@ -657,7 +744,7 @@ dependencies = [
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys",
"windows-sys 0.61.2",
]
[[package]]
@@ -805,18 +892,46 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "winapi-util"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.61.2"
@@ -826,6 +941,71 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [
"windows-link",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "winnow"
version = "1.0.3"
+1
View File
@@ -7,6 +7,7 @@ edition = "2024"
anyhow = "1.0.103"
config = "0.15.25"
dotenv = "0.15.0"
notify = "8.2.0"
serde = { version = "1.0.228", features = ["derive"] }
tokio = { version = "1.52.3", features = ["full"] }
tracing = "0.1.44"
+54 -2
View File
@@ -1,9 +1,13 @@
use std::{path::Path, sync::Arc, time::Duration};
use anyhow::{Result, ensure};
use notify::{EventKind, INotifyWatcher, Watcher};
use serde::Deserialize;
use tokio::sync::{RwLock, mpsc};
#[derive(Deserialize)]
#[allow(clippy::upper_case_acronyms)]
pub enum EntryType {
pub enum RecordType {
A,
AAAA,
MX,
@@ -12,7 +16,7 @@ pub enum EntryType {
#[derive(Deserialize)]
pub struct Entry {
#[serde(rename = "type")]
pub entry_type: EntryType,
pub rtype: RecordType,
pub name: String,
pub address: String,
#[serde(skip)]
@@ -77,3 +81,51 @@ pub fn load() -> Result<ConfigData> {
entries: file_config.entries,
})
}
pub fn watch(config: Arc<RwLock<ConfigData>>) -> Result<INotifyWatcher> {
let (tx, mut rx) = mpsc::channel(100);
let mut watcher = notify::recommended_watcher(move |event| {
let _ = tx.blocking_send(event);
})?;
watcher.configure(notify::Config::default().with_poll_interval(Duration::from_secs(10)))?;
watcher.watch(
Path::new("nanodns.toml"),
notify::RecursiveMode::NonRecursive,
)?;
tokio::spawn(async move {
loop {
let Some(event) = rx.recv().await else {
tracing::info!("watch channel closed");
break;
};
match event {
Ok(event) => {
if let EventKind::Modify(_) = event.kind {
let new_config = match load() {
Ok(new_config) => new_config,
Err(err) => {
tracing::error!("Failed to load new config: {err}");
break;
}
};
tracing::info!("Detected config change");
{
let mut config = config.write().await;
*config = new_config;
}
tracing::info!("Config change applied");
}
}
Err(err) => {
tracing::error!("error in watch channel: {err}");
break;
}
}
}
});
Ok(watcher)
}
+19 -9
View File
@@ -10,11 +10,11 @@ mod ext;
mod logging;
mod macros;
use std::{io::Cursor, net::SocketAddr};
use std::{io::Cursor, net::SocketAddr, sync::Arc};
use anyhow::Result;
use dotenv::dotenv;
use tokio::net::UdpSocket;
use tokio::{net::UdpSocket, sync::RwLock};
use tracing::{Instrument, Level};
use crate::{
@@ -27,15 +27,18 @@ async fn main() -> Result<()> {
dotenv().ok();
logging::setup();
let config = config::load()?;
let port = config.port;
let config = Arc::new(RwLock::new(config));
let _watcher = config::watch(config.clone())?;
tracing::info!("start server on port {}", config.port);
let sock = UdpSocket::bind(("0.0.0.0", config.port)).await?;
tracing::info!("start server on port {}", port);
let sock = UdpSocket::bind(("0.0.0.0", port)).await?;
let mut buffer = [0u8; 512];
loop {
let (size, addr) = sock.recv_from(&mut buffer).await?;
let mut cursor = Cursor::new(&buffer[..size]);
let result = handle_datagram(&config, &sock, &addr, &mut cursor).await;
let result = handle_datagram(config.clone(), &sock, &addr, &mut cursor).await;
if let Err(err) = result {
tracing::error!("failed to handle datagram: {err}");
}
@@ -43,7 +46,7 @@ async fn main() -> Result<()> {
}
async fn handle_datagram(
config: &config::ConfigData,
config: Arc<RwLock<config::ConfigData>>,
sock: &UdpSocket,
addr: &SocketAddr,
cursor: &mut Cursor<&[u8]>,
@@ -80,7 +83,7 @@ async fn handle_datagram(
match question.qtype {
RECORD_TYPE_A => {
handle_type_a_question(config, &question, &mut response);
handle_type_a_question(config.clone(), &question, &mut response).await;
}
qt => {
tracing::error!("unsupported question type, requested: {qt}");
@@ -100,8 +103,15 @@ async fn handle_datagram(
result
}
fn handle_type_a_question(config: &ConfigData, question: &Question, response: &mut Message) {
let address = config.get_ipv4_address_by_domain(&question.domain);
async fn handle_type_a_question(
config: Arc<RwLock<ConfigData>>,
question: &Question,
response: &mut Message,
) {
let address = {
let config = config.read().await;
config.get_ipv4_address_by_domain(&question.domain)
};
match address {
Some(address) => {
let answer = Answer {