Mercurial > crates > systemd-socket
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 |