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