diff --git a/Cargo.lock b/Cargo.lock index e13a4d7b..1b4d4b49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,7 +11,9 @@ dependencies = [ "chrono", "clap", "futures-util", + "libc", "log", + "nix", "resolv-conf", "signal-hook", "syslog", @@ -88,6 +90,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + [[package]] name = "cfg-if" version = "1.0.0" @@ -429,6 +437,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "mio" version = "0.8.2" @@ -461,6 +478,19 @@ dependencies = [ "smallvec", ] +[[package]] +name = "nix" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "memoffset", +] + [[package]] name = "ntapi" version = "0.3.7" diff --git a/Cargo.toml b/Cargo.toml index 690ac868..8d248634 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,8 @@ signal-hook = "0.3.13" tokio = { version = "1.19.2", features = ["tokio-macros", "full"] } async-broadcast = "0.4.0" resolv-conf = "0.7.0" +nix = "0.23.0" +libc = "0.2" [build-dependencies] chrono = "*" diff --git a/src/commands/run.rs b/src/commands/run.rs index 7f284372..b5e50074 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -1,8 +1,14 @@ //! Runs the aardvark dns server with provided config use crate::server::serve; use clap::Parser; -use log::debug; +use nix::unistd; +use nix::unistd::{dup2, fork, ForkResult}; +use std::fs::File; +use std::fs::OpenOptions; use std::io::Error; +use std::io::Write; +use std::os::unix::io::AsRawFd; +use std::os::unix::io::FromRawFd; #[derive(Parser, Debug)] pub struct Run {} @@ -19,18 +25,64 @@ impl Run { port: u32, filter_search_domain: String, ) -> Result<(), Error> { - debug!( - "Setting up aardvark server with input directory as {:?}", - input_dir - ); + // create a temporary path for unix socket + // so parent can communicate with child and + // only exit when child is ready to serve. + let (ready_pipe_read, ready_pipe_write) = nix::unistd::pipe()?; - if let Err(er) = serve::serve(&input_dir, port, &filter_search_domain) { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - format!("Error starting server {}", er), - )); + // fork and verify if server is running + // and exit parent + // setsid() ensures that there is no controlling terminal on the child process + match unsafe { fork() } { + Ok(ForkResult::Parent { child, .. }) => { + log::debug!("starting aardvark on a child with pid {}", child); + // verify aardvark here and block till will start + unistd::read(ready_pipe_read, &mut [0_u8; 1])?; + unistd::close(ready_pipe_read)?; + unistd::close(ready_pipe_write)?; + Ok(()) + } + Ok(ForkResult::Child) => { + // remove any controlling terminals + // but don't hardstop if this fails + let _ = unsafe { libc::setsid() }; // check https://docs.rs/libc + // close fds -> stdout, stdin and stderr + let dev_null = OpenOptions::new() + .read(true) + .write(true) + .open("/dev/null") + .map_err(|e| std::io::Error::new(e.kind(), format!("/dev/null: {}", e))); + // redirect stdout, stdin and stderr to /dev/null + if let Ok(dev_null) = dev_null { + let fd = dev_null.as_raw_fd(); + let _ = dup2(fd, 0); + let _ = dup2(fd, 1); + let _ = dup2(fd, 2); + if fd < 2 { + std::mem::forget(dev_null); + } + } + + let mut f = unsafe { File::from_raw_fd(ready_pipe_write) }; + write!(&mut f, "ready")?; + unistd::close(ready_pipe_read)?; + unistd::close(ready_pipe_write)?; + if let Err(er) = serve::serve(&input_dir, port, &filter_search_domain) { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("Error starting server {}", er), + )); + } + Ok(()) + } + Err(err) => { + log::debug!("fork failed with error {}", err); + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("fork failed with error: {}", err), + )); + } } - Ok(()) } } diff --git a/src/main.rs b/src/main.rs index 52261338..c3fa95df 100644 --- a/src/main.rs +++ b/src/main.rs @@ -71,6 +71,7 @@ fn main() { let filter_search_domain = opts .filter_search_domain .unwrap_or_else(|| String::from(".dns.podman")); + let result = match opts.subcmd { SubCommand::Run(run) => run.exec(dir, port, filter_search_domain), SubCommand::Version(version) => version.exec(),