comparison src/lib.rs @ 6:a7893294e9b2

Make the crate compilable on non-linux systems This makes the crate compile on other operating systems. Since systemd is only supported on Linux, it simply disables systemd features on other systems. The API is still the same, just parsing `systemd://` string will return an error.
author Martin Habovstiak <martin.habovstiak@gmail.com>
date Fri, 27 Nov 2020 16:15:57 +0100
parents 66c0e10c89fc
children 9731ff589d9c
comparison
equal deleted inserted replaced
5:27456533853e 6:a7893294e9b2
8 //! Then it provides a method to bind the address which will return the socket from systemd if available. 8 //! Then it provides a method to bind the address which will return the socket from systemd if available.
9 //! 9 //!
10 //! The provided type supports conversions from various types of strings and also `serde` and `parse_arg` via feature flag. 10 //! The provided type supports conversions from various types of strings and also `serde` and `parse_arg` via feature flag.
11 //! Thanks to this the change to your code should be minimal - parsing will continue to work, it'll just allow a new format. 11 //! Thanks to this the change to your code should be minimal - parsing will continue to work, it'll just allow a new format.
12 //! You only need to change the code to use `SocketAddr::bind()` instead of `TcpListener::bind()` for binding. 12 //! You only need to change the code to use `SocketAddr::bind()` instead of `TcpListener::bind()` for binding.
13 //!
14 //! You also don't need to worry about conditional compilation to ensure OS compatibility.
15 //! This crate handles that for you by disabling systemd on non-linux systems.
13 //! 16 //!
14 //! Further, the crate also provides methods for binding `tokio` 0.2, 0.3, and `async_std` sockets if the appropriate features are 17 //! Further, the crate also provides methods for binding `tokio` 0.2, 0.3, and `async_std` sockets if the appropriate features are
15 //! activated. 18 //! activated.
16 //! 19 //!
17 //! ## Example 20 //! ## Example
60 use std::fmt; 63 use std::fmt;
61 use std::ffi::{OsStr, OsString}; 64 use std::ffi::{OsStr, OsString};
62 use crate::error::*; 65 use crate::error::*;
63 use crate::resolv_addr::ResolvAddr; 66 use crate::resolv_addr::ResolvAddr;
64 67
68 #[cfg(not(linux))]
69 use std::convert::Infallible as Never;
70
71 #[cfg(linux)]
65 pub(crate) mod systemd_sockets { 72 pub(crate) mod systemd_sockets {
66 use std::fmt; 73 use std::fmt;
67 use std::sync::Mutex; 74 use std::sync::Mutex;
68 use libsystemd::activation::FileDescriptor; 75 use libsystemd::activation::FileDescriptor;
69 use libsystemd::errors::Error as LibSystemdError; 76 use libsystemd::errors::Error as LibSystemdError;
215 222
216 // We can't impl<T: Deref<Target=str> + Into<String>> TryFrom<T> for SocketAddr because of orphan 223 // We can't impl<T: Deref<Target=str> + Into<String>> TryFrom<T> for SocketAddr because of orphan
217 // rules. 224 // rules.
218 fn try_from_generic<'a, T>(string: T) -> Result<Self, ParseError> where T: 'a + std::ops::Deref<Target=str> + Into<String> { 225 fn try_from_generic<'a, T>(string: T) -> Result<Self, ParseError> where T: 'a + std::ops::Deref<Target=str> + Into<String> {
219 if string.starts_with(SYSTEMD_PREFIX) { 226 if string.starts_with(SYSTEMD_PREFIX) {
220 let name_len = string.len() - SYSTEMD_PREFIX.len(); 227 #[cfg(linux)]
221 match string[SYSTEMD_PREFIX.len()..].chars().enumerate().find(|(_, c)| !c.is_ascii() || *c < ' ' || *c == ':') { 228 {
222 None if name_len <= 255 => Ok(SocketAddr(SocketAddrInner::Systemd(string.into()))), 229 let name_len = string.len() - SYSTEMD_PREFIX.len();
223 None => Err(ParseErrorInner::LongSocketName { string: string.into(), len: name_len }.into()), 230 match string[SYSTEMD_PREFIX.len()..].chars().enumerate().find(|(_, c)| !c.is_ascii() || *c < ' ' || *c == ':') {
224 Some((pos, c)) => Err(ParseErrorInner::InvalidCharacter { string: string.into(), c, pos, }.into()), 231 None if name_len <= 255 => Ok(SocketAddr(SocketAddrInner::Systemd(string.into()))),
232 None => Err(ParseErrorInner::LongSocketName { string: string.into(), len: name_len }.into()),
233 Some((pos, c)) => Err(ParseErrorInner::InvalidCharacter { string: string.into(), c, pos, }.into()),
234 }
235 }
236 #[cfg(not(linux))]
237 {
238 Err(ParseErrorInner::SystemdUnsupported(string.into()).into())
225 } 239 }
226 } else { 240 } else {
227 match string.parse() { 241 match string.parse() {
228 Ok(addr) => Ok(SocketAddr(SocketAddrInner::Ordinary(addr))), 242 Ok(addr) => Ok(SocketAddr(SocketAddrInner::Ordinary(addr))),
229 Err(_) => Ok(SocketAddr(SocketAddrInner::WithHostname(ResolvAddr::try_from_generic(string).map_err(ParseErrorInner::ResolvAddr)?))), 243 Err(_) => Ok(SocketAddr(SocketAddrInner::WithHostname(ResolvAddr::try_from_generic(string).map_err(ParseErrorInner::ResolvAddr)?))),
230 } 244 }
231 } 245 }
232 } 246 }
233 247
248 #[cfg(linux)]
234 fn get_systemd(socket_name: String) -> Result<(std::net::TcpListener, SocketAddrInner), BindError> { 249 fn get_systemd(socket_name: String) -> Result<(std::net::TcpListener, SocketAddrInner), BindError> {
235 use libsystemd::activation::IsType; 250 use libsystemd::activation::IsType;
236 use std::os::unix::io::{FromRawFd, IntoRawFd}; 251 use std::os::unix::io::{FromRawFd, IntoRawFd};
237 252
238 let socket = systemd_sockets::take(&socket_name[SYSTEMD_PREFIX.len()..]).map_err(BindErrorInner::ReceiveDescriptors)?; 253 let socket = systemd_sockets::take(&socket_name[SYSTEMD_PREFIX.len()..]).map_err(BindErrorInner::ReceiveDescriptors)?;
246 Some(_) => Err(BindErrorInner::NotInetSocket(socket_name).into()), 261 Some(_) => Err(BindErrorInner::NotInetSocket(socket_name).into()),
247 None => Err(BindErrorInner::MissingDescriptor(socket_name).into()) 262 None => Err(BindErrorInner::MissingDescriptor(socket_name).into())
248 } 263 }
249 } 264 }
250 } 265 }
266
267 // This approach makes the rest of the code much simpler as it doesn't require sprinkling it
268 // with #[cfg(linux)] yet still statically guarantees it won't execute.
269 #[cfg(not(linux))]
270 fn get_systemd(socket_name: Never) -> Result<(std::net::TcpListener, SocketAddrInner), BindError> {
271 match socket_name {}
272 }
251 } 273 }
252 274
253 /// Displays the address in format that can be parsed again. 275 /// Displays the address in format that can be parsed again.
254 /// 276 ///
255 /// **Important: While I don't expect this impl to change, don't rely on it!** 277 /// **Important: While I don't expect this impl to change, don't rely on it!**
273 // PartialEq for testing, I'm not convinced it should be exposed 295 // PartialEq for testing, I'm not convinced it should be exposed
274 #[derive(Debug, PartialEq)] 296 #[derive(Debug, PartialEq)]
275 enum SocketAddrInner { 297 enum SocketAddrInner {
276 Ordinary(std::net::SocketAddr), 298 Ordinary(std::net::SocketAddr),
277 WithHostname(resolv_addr::ResolvAddr), 299 WithHostname(resolv_addr::ResolvAddr),
300 #[cfg(linux)]
278 Systemd(String), 301 Systemd(String),
302 #[cfg(not(linux))]
303 #[allow(dead_code)]
304 Systemd(Never),
279 } 305 }
280 306
281 const SYSTEMD_PREFIX: &str = "systemd://"; 307 const SYSTEMD_PREFIX: &str = "systemd://";
282 308
283 impl std::str::FromStr for SocketAddr { 309 impl std::str::FromStr for SocketAddr {