comparison src/lib.rs @ 13:f740dadd2948

Added enable_systemd feature This feature makes systemd support optional, on by default. While it may seem strange that this feature exists, it makes sense for authors of applications who want to make systemd optional. Thanks to this feature the interface stays the same, it just fails to parse `systemd://` addresses with a helpful error message.
author Martin Habovstiak <martin.habovstiak@gmail.com>
date Thu, 03 Dec 2020 16:34:09 +0100
parents 13e2a5545167
children bc76507dd878
comparison
equal deleted inserted replaced
12:4479770c2275 13:f740dadd2948
40 //! } 40 //! }
41 //! ``` 41 //! ```
42 //! 42 //!
43 //! ## Features 43 //! ## Features
44 //! 44 //!
45 //! * `enable_systemd` - on by default, the existence of this feature can allow your users to turn
46 //! off systemd support if they don't need it. Note that it's already disabled on non-linux
47 //! systems, so you don't need to care about that.
45 //! * `serde` - implements `serde::Deserialize` for `SocketAddr` 48 //! * `serde` - implements `serde::Deserialize` for `SocketAddr`
46 //! * `parse_arg` - implements `parse_arg::ParseArg` for `SocketAddr` 49 //! * `parse_arg` - implements `parse_arg::ParseArg` for `SocketAddr`
47 //! * `tokio_0_2` - adds `bind_tokio_0_2` method to `SocketAddr` 50 //! * `tokio_0_2` - adds `bind_tokio_0_2` method to `SocketAddr`
48 //! * `tokio_0_3` - adds `bind_tokio_0_3` method to `SocketAddr` 51 //! * `tokio_0_3` - adds `bind_tokio_0_3` method to `SocketAddr`
49 //! * `async_std` - adds `bind_async_std` method to `SocketAddr` 52 //! * `async_std` - adds `bind_async_std` method to `SocketAddr`
63 use std::fmt; 66 use std::fmt;
64 use std::ffi::{OsStr, OsString}; 67 use std::ffi::{OsStr, OsString};
65 use crate::error::*; 68 use crate::error::*;
66 use crate::resolv_addr::ResolvAddr; 69 use crate::resolv_addr::ResolvAddr;
67 70
68 #[cfg(not(linux))] 71 #[cfg(not(all(linux, feature = "enable_systemd")))]
69 use std::convert::Infallible as Never; 72 use std::convert::Infallible as Never;
70 73
71 #[cfg(linux)] 74 #[cfg(all(linux, feature = "enable_systemd"))]
72 pub(crate) mod systemd_sockets { 75 pub(crate) mod systemd_sockets {
73 use std::fmt; 76 use std::fmt;
74 use std::sync::Mutex; 77 use std::sync::Mutex;
75 use libsystemd::activation::FileDescriptor; 78 use libsystemd::activation::FileDescriptor;
76 use libsystemd::errors::Error as LibSystemdError; 79 use libsystemd::errors::Error as LibSystemdError;
222 225
223 // We can't impl<T: Deref<Target=str> + Into<String>> TryFrom<T> for SocketAddr because of orphan 226 // We can't impl<T: Deref<Target=str> + Into<String>> TryFrom<T> for SocketAddr because of orphan
224 // rules. 227 // rules.
225 fn try_from_generic<'a, T>(string: T) -> Result<Self, ParseError> where T: 'a + std::ops::Deref<Target=str> + Into<String> { 228 fn try_from_generic<'a, T>(string: T) -> Result<Self, ParseError> where T: 'a + std::ops::Deref<Target=str> + Into<String> {
226 if string.starts_with(SYSTEMD_PREFIX) { 229 if string.starts_with(SYSTEMD_PREFIX) {
227 #[cfg(linux)] 230 #[cfg(all(linux, feature = "enable_systemd"))]
228 { 231 {
229 let name_len = string.len() - SYSTEMD_PREFIX.len(); 232 let name_len = string.len() - SYSTEMD_PREFIX.len();
230 match string[SYSTEMD_PREFIX.len()..].chars().enumerate().find(|(_, c)| !c.is_ascii() || *c < ' ' || *c == ':') { 233 match string[SYSTEMD_PREFIX.len()..].chars().enumerate().find(|(_, c)| !c.is_ascii() || *c < ' ' || *c == ':') {
231 None if name_len <= 255 => Ok(SocketAddr(SocketAddrInner::Systemd(string.into()))), 234 None if name_len <= 255 => Ok(SocketAddr(SocketAddrInner::Systemd(string.into()))),
232 None => Err(ParseErrorInner::LongSocketName { string: string.into(), len: name_len }.into()), 235 None => Err(ParseErrorInner::LongSocketName { string: string.into(), len: name_len }.into()),
233 Some((pos, c)) => Err(ParseErrorInner::InvalidCharacter { string: string.into(), c, pos, }.into()), 236 Some((pos, c)) => Err(ParseErrorInner::InvalidCharacter { string: string.into(), c, pos, }.into()),
234 } 237 }
235 } 238 }
236 #[cfg(not(linux))] 239 #[cfg(not(all(linux, feature = "enable_systemd")))]
237 { 240 {
238 Err(ParseErrorInner::SystemdUnsupported(string.into()).into()) 241 Err(ParseErrorInner::SystemdUnsupported(string.into()).into())
239 } 242 }
240 } else { 243 } else {
241 match string.parse() { 244 match string.parse() {
243 Err(_) => Ok(SocketAddr(SocketAddrInner::WithHostname(ResolvAddr::try_from_generic(string).map_err(ParseErrorInner::ResolvAddr)?))), 246 Err(_) => Ok(SocketAddr(SocketAddrInner::WithHostname(ResolvAddr::try_from_generic(string).map_err(ParseErrorInner::ResolvAddr)?))),
244 } 247 }
245 } 248 }
246 } 249 }
247 250
248 #[cfg(linux)] 251 #[cfg(all(linux, feature = "enable_systemd"))]
249 fn get_systemd(socket_name: String) -> Result<(std::net::TcpListener, SocketAddrInner), BindError> { 252 fn get_systemd(socket_name: String) -> Result<(std::net::TcpListener, SocketAddrInner), BindError> {
250 use libsystemd::activation::IsType; 253 use libsystemd::activation::IsType;
251 use std::os::unix::io::{FromRawFd, IntoRawFd}; 254 use std::os::unix::io::{FromRawFd, IntoRawFd};
252 255
253 let socket = systemd_sockets::take(&socket_name[SYSTEMD_PREFIX.len()..]).map_err(BindErrorInner::ReceiveDescriptors)?; 256 let socket = systemd_sockets::take(&socket_name[SYSTEMD_PREFIX.len()..]).map_err(BindErrorInner::ReceiveDescriptors)?;
263 } 266 }
264 } 267 }
265 } 268 }
266 269
267 // This approach makes the rest of the code much simpler as it doesn't require sprinkling it 270 // 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. 271 // with #[cfg(all(linux, feature = "enable_systemd"))] yet still statically guarantees it won't execute.
269 #[cfg(not(linux))] 272 #[cfg(not(linux))]
270 fn get_systemd(socket_name: Never) -> Result<(std::net::TcpListener, SocketAddrInner), BindError> { 273 fn get_systemd(socket_name: Never) -> Result<(std::net::TcpListener, SocketAddrInner), BindError> {
271 match socket_name {} 274 match socket_name {}
272 } 275 }
273 } 276 }
295 // PartialEq for testing, I'm not convinced it should be exposed 298 // PartialEq for testing, I'm not convinced it should be exposed
296 #[derive(Debug, PartialEq)] 299 #[derive(Debug, PartialEq)]
297 enum SocketAddrInner { 300 enum SocketAddrInner {
298 Ordinary(std::net::SocketAddr), 301 Ordinary(std::net::SocketAddr),
299 WithHostname(resolv_addr::ResolvAddr), 302 WithHostname(resolv_addr::ResolvAddr),
300 #[cfg(linux)] 303 #[cfg(all(linux, feature = "enable_systemd"))]
301 Systemd(String), 304 Systemd(String),
302 #[cfg(not(linux))] 305 #[cfg(not(all(linux, feature = "enable_systemd")))]
303 #[allow(dead_code)] 306 #[allow(dead_code)]
304 Systemd(Never), 307 Systemd(Never),
305 } 308 }
306 309
307 const SYSTEMD_PREFIX: &str = "systemd://"; 310 const SYSTEMD_PREFIX: &str = "systemd://";
399 fn parse_ordinary() { 402 fn parse_ordinary() {
400 assert_eq!("127.0.0.1:42".parse::<SocketAddr>().unwrap().0, SocketAddrInner::Ordinary(([127, 0, 0, 1], 42).into())); 403 assert_eq!("127.0.0.1:42".parse::<SocketAddr>().unwrap().0, SocketAddrInner::Ordinary(([127, 0, 0, 1], 42).into()));
401 } 404 }
402 405
403 #[test] 406 #[test]
404 #[cfg(linux)] 407 #[cfg(all(linux, feature = "enable_systemd"))]
405 fn parse_systemd() { 408 fn parse_systemd() {
406 assert_eq!("systemd://foo".parse::<SocketAddr>().unwrap().0, SocketAddrInner::Systemd("systemd://foo".to_owned())); 409 assert_eq!("systemd://foo".parse::<SocketAddr>().unwrap().0, SocketAddrInner::Systemd("systemd://foo".to_owned()));
407 } 410 }
408 411
409 #[test] 412 #[test]
410 #[cfg(not(linux))] 413 #[cfg(not(all(linux, feature = "enable_systemd")))]
411 #[should_panic] 414 #[should_panic]
412 fn parse_systemd() { 415 fn parse_systemd() {
413 "systemd://foo".parse::<SocketAddr>().unwrap(); 416 "systemd://foo".parse::<SocketAddr>().unwrap();
414 } 417 }
415 418