diff --git a/Cargo.lock b/Cargo.lock index 85b21f81..a122c50d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -419,7 +419,7 @@ dependencies = [ "parsec-client-test 0.1.3 (git+https://github.com/parallaxsecond/parsec-client-test?tag=0.1.3)", "parsec-interface 0.1.0 (git+https://github.com/parallaxsecond/parsec-interface-rs?tag=0.1.0)", "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sd-notify 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sd-notify 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", "signal-hook 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "std-semaphore 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -631,7 +631,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "sd-notify" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -928,7 +928,7 @@ dependencies = [ "checksum regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b143cceb2ca5e56d5671988ef8b15615733e7ee16cd348e064333b251b89343f" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" -"checksum sd-notify 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "40b76d9fb089398d0b9c5a365008b3cb3104624fe8143f1a43c9827788a22252" +"checksum sd-notify 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aef40838bbb143707f8309b1e92e6ba3225287592968ba6f6e3b6de4a9816486" "checksum serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "fec2851eb56d010dc9a21b89ca53ee75e6528bab60c11e89d38390904982da9f" "checksum serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "cb4dc18c61206b08dc98216c98faa0232f4337e1e1b8574551d5bad29ea1b425" "checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" diff --git a/Cargo.toml b/Cargo.toml index ecf0bf6c..7699f2bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ uuid = "0.7.4" threadpool = "1.7.1" std-semaphore = "0.1.0" signal-hook = "0.1.10" -sd-notify = { version = "0.1.0", optional = true } +sd-notify = { version = "0.1.1" } toml = "0.4.2" serde = { version = "1.0", features = ["derive"] } env_logger = "0.7.1" @@ -42,4 +42,4 @@ mbed = [] # Feature to compile the PARSEC binary to be executed as a systemd daemon. # This feature is only available on Linux. -systemd-daemon = ["sd-notify"] +systemd-daemon = [] diff --git a/README.md b/README.md index f5bf4dc7..f7063d8c 100644 --- a/README.md +++ b/README.md @@ -128,19 +128,19 @@ You will need to understand the [**wire protocol specification**](docs/wire_prot The software is provided under Apache-2.0. Contributions to this project are accepted under the same license. This project uses the following third party crates: -* serde (Apache-2.0) +* serde (MIT and Apache-2.0) * bindgen (BSD-3-Clause) * cargo\_toml (Apache-2.0) * toml (MIT and Apache-2.0) * rand (MIT and Apache-2.0) * base64 (MIT and Apache-2.0) -* uuid (Apache-2.0) -* threadpool (Apache-2.0) +* uuid (MIT and Apache-2.0) +* threadpool (MIT and Apache-2.0) * std-semaphore (MIT and Apache-2.0) * num\_cpus (MIT and Apache-2.0) * signal-hook (MIT and Apache-2.0) -* sd-notify (Apache-2.0) -* log (Apache-2.0) +* sd-notify (MIT and Apache-2.0) +* log (MIT and Apache-2.0) * env\_logger (MIT and Apache-2.0) This project uses the following third party libraries: diff --git a/src/bin/main.rs b/src/bin/main.rs index a8cf98a6..be23d80e 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -57,7 +57,6 @@ fn main() -> Result<(), Error> { info!("PARSEC is ready."); - #[cfg(feature = "systemd-daemon")] // Notify systemd that the daemon is ready, the start command will block until this point. let _ = sd_notify::notify(true, &[sd_notify::NotifyState::Ready]); diff --git a/src/front/domain_socket.rs b/src/front/domain_socket.rs index a00cefc4..5a38ecb3 100644 --- a/src/front/domain_socket.rs +++ b/src/front/domain_socket.rs @@ -31,7 +31,7 @@ static SOCKET_PATH: &str = "/tmp/security-daemon-socket"; /// /// Only works on Unix systems. pub struct DomainSocketListener { - listener: Option, + listener: UnixListener, timeout: Duration, } @@ -42,36 +42,39 @@ impl DomainSocketListener { /// - if a file/socket exists at the path specified for the socket and `remove_file` /// fails /// - if binding to the socket path fails - fn init(&mut self) { - if cfg!(feature = "systemd-daemon") { - // The PARSEC service is socket activated (see parsec.socket file). - // systemd creates the PARSEC service giving it an initialised socket as the file - // descriptor number 3 (see sd_listen_fds(3) man page). - // If an instance of PARSEC compiled with the "systemd-daemon" feature is run directly - // instead of by systemd, this call will still work but the next accept call on the - // UnixListener will generate a Linux error 9 (Bad file number), as checked below. - unsafe { - self.listener = Some(UnixListener::from_raw_fd(3)); - } - } else { - let socket = Path::new(SOCKET_PATH); + pub fn new(timeout: Duration) -> Self { + // If this PARSEC instance was socket activated (see the `parsec.socket` + // file), the listener will be opened by systemd and passed to the + // process. + // If PARSEC was service activated or not started under systemd, this + // will return `0`. + let listener = + match sd_notify::listen_fds().expect("Could not retrieve listener from systemd") { + 0 => { + let socket = Path::new(SOCKET_PATH); - if socket.exists() { - fs::remove_file(&socket).unwrap(); - } + if socket.exists() { + fs::remove_file(&socket).unwrap(); + } - let listener_val = match UnixListener::bind(SOCKET_PATH) { - Ok(listener) => listener, - Err(err) => panic!(err), - }; + let listener = + UnixListener::bind(SOCKET_PATH).expect("Could not bind listen socket"); + listener + .set_nonblocking(true) + .expect("Could not set the socket as non-blocking"); - // Set the socket as non-blocking. - listener_val - .set_nonblocking(true) - .expect("Could not set the socket as non-blocking"); + listener + } + 1 => { + // No need to set the socket as non-blocking, parsec.service + // already requests that. + let nfd = sd_notify::SD_LISTEN_FDS_START; + unsafe { UnixListener::from_raw_fd(nfd) } + } + _ => panic!("Received too many file descriptors"), + }; - self.listener = Some(listener_val); - } + Self { listener, timeout } } } @@ -81,44 +84,30 @@ impl Listen for DomainSocketListener { } fn accept(&self) -> Option> { - if let Some(listener) = &self.listener { - let stream_result = listener.accept(); - match stream_result { - Ok((stream, _)) => { - if let Err(err) = stream.set_read_timeout(Some(self.timeout)) { - error!("Failed to set read timeout ({})", err); - None - } else if let Err(err) = stream.set_write_timeout(Some(self.timeout)) { - error!("Failed to set write timeout ({})", err); - None - } else if let Err(err) = stream.set_nonblocking(false) { - error!("Failed to set stream as blocking ({})", err); - None - } else { - Some(Box::from(stream)) - } - } - Err(err) => { - if cfg!(feature = "systemd-daemon") { - // When run as a systemd daemon, a file descriptor mapping to the Domain Socket - // should have been passed to this process. - if let Some(os_error) = err.raw_os_error() { - // On Linux, 9 is EBADF (Bad file number) - if os_error == 9 { - panic!("The Unix Domain Socket file descriptor (number 3) should have been given to this process."); - } - } - } - // Check if the error is because no connections are currently present. - if err.kind() != ErrorKind::WouldBlock { - // Only log the real errors. - error!("Failed to connect with a UnixStream ({})", err); - } + let stream_result = self.listener.accept(); + match stream_result { + Ok((stream, _)) => { + if let Err(err) = stream.set_read_timeout(Some(self.timeout)) { + error!("Failed to set read timeout ({})", err); + None + } else if let Err(err) = stream.set_write_timeout(Some(self.timeout)) { + error!("Failed to set write timeout ({})", err); None + } else if let Err(err) = stream.set_nonblocking(false) { + error!("Failed to set stream as blocking ({})", err); + None + } else { + Some(Box::from(stream)) + } + } + Err(err) => { + // Check if the error is because no connections are currently present. + if err.kind() != ErrorKind::WouldBlock { + // Only log the real errors. + error!("Failed to connect with a UnixStream ({})", err); } + None } - } else { - panic!("The Unix Domain Socket has not been initialised."); } } } @@ -139,12 +128,7 @@ impl DomainSocketListenerBuilder { } pub fn build(self) -> DomainSocketListener { - let mut listener = DomainSocketListener { - timeout: self.timeout.expect("FrontEndHandler missing"), - listener: None, - }; - listener.init(); - - listener + let timeout = self.timeout.expect("The listener timeout was not set"); + DomainSocketListener::new(timeout) } }