Mercurial > crates > systemd-socket
annotate 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 |
rev | line source |
---|---|
0 | 1 //! A convenience crate for optionally supporting systemd socket activation. |
2 //! | |
3 //! ## About | |
4 //! | |
5 //! The goal of this crate is to make socket activation with systemd in your project trivial. | |
6 //! It provides a replacement for `std::net::SocketAddr` that allows parsing the bind address from string just like the one from `std` | |
7 //! but on top of that also allows `systemd://socket_name` format that tells it to use systemd activation with given socket name. | |
8 //! Then it provides a method to bind the address which will return the socket from systemd if available. | |
9 //! | |
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. | |
12 //! You only need to change the code to use `SocketAddr::bind()` instead of `TcpListener::bind()` for binding. | |
13 //! | |
6
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
14 //! You also don't need to worry about conditional compilation to ensure OS compatibility. |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
15 //! This crate handles that for you by disabling systemd on non-linux systems. |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
16 //! |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
17 //! Further, the crate also provides methods for binding `tokio` 0.2, 0.3, and `async_std` sockets if the appropriate features are |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
18 //! activated. |
0 | 19 //! |
20 //! ## Example | |
21 //! | |
22 //! ```no_run | |
23 //! use systemd_socket::SocketAddr; | |
24 //! use std::convert::TryFrom; | |
25 //! use std::io::Write; | |
26 //! | |
27 //! let mut args = std::env::args_os(); | |
28 //! let program_name = args.next().expect("unknown program name"); | |
29 //! let socket_addr = args.next().expect("missing socket address"); | |
30 //! let socket_addr = SocketAddr::try_from(socket_addr).expect("failed to parse socket address"); | |
31 //! let socket = socket_addr.bind().expect("failed to bind socket"); | |
32 //! | |
33 //! loop { | |
34 //! let _ = socket | |
35 //! .accept() | |
36 //! .expect("failed to accept connection") | |
37 //! .0 | |
38 //! .write_all(b"Hello world!") | |
39 //! .map_err(|err| eprintln!("Failed to send {}", err)); | |
40 //! } | |
41 //! ``` | |
42 //! | |
43 //! ## Features | |
44 //! | |
13
f740dadd2948
Added enable_systemd feature
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
11
diff
changeset
|
45 //! * `enable_systemd` - on by default, the existence of this feature can allow your users to turn |
f740dadd2948
Added enable_systemd feature
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
11
diff
changeset
|
46 //! off systemd support if they don't need it. Note that it's already disabled on non-linux |
f740dadd2948
Added enable_systemd feature
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
11
diff
changeset
|
47 //! systems, so you don't need to care about that. |
0 | 48 //! * `serde` - implements `serde::Deserialize` for `SocketAddr` |
49 //! * `parse_arg` - implements `parse_arg::ParseArg` for `SocketAddr` | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
50 //! * `tokio_0_2` - adds `bind_tokio_0_2` method to `SocketAddr` |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
51 //! * `tokio_0_3` - adds `bind_tokio_0_3` method to `SocketAddr` |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
52 //! * `async_std` - adds `bind_async_std` method to `SocketAddr` |
3
0edcde404b02
Added information about MSRV
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
2
diff
changeset
|
53 //! |
0edcde404b02
Added information about MSRV
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
2
diff
changeset
|
54 //! ## MSRV |
0edcde404b02
Added information about MSRV
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
2
diff
changeset
|
55 //! |
0edcde404b02
Added information about MSRV
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
2
diff
changeset
|
56 //! This crate must always compile with the latest Rust available in the latest Debian stable. |
0edcde404b02
Added information about MSRV
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
2
diff
changeset
|
57 //! That is currently Rust 1.41.1. (Debian 10 - Buster) |
0edcde404b02
Added information about MSRV
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
2
diff
changeset
|
58 |
0 | 59 |
60 #![deny(missing_docs)] | |
61 | |
62 pub mod error; | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
63 mod resolv_addr; |
0 | 64 |
65 use std::convert::{TryFrom, TryInto}; | |
66 use std::fmt; | |
67 use std::ffi::{OsStr, OsString}; | |
68 use crate::error::*; | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
69 use crate::resolv_addr::ResolvAddr; |
0 | 70 |
13
f740dadd2948
Added enable_systemd feature
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
11
diff
changeset
|
71 #[cfg(not(all(linux, feature = "enable_systemd")))] |
6
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
72 use std::convert::Infallible as Never; |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
73 |
13
f740dadd2948
Added enable_systemd feature
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
11
diff
changeset
|
74 #[cfg(all(linux, feature = "enable_systemd"))] |
0 | 75 pub(crate) mod systemd_sockets { |
76 use std::fmt; | |
77 use std::sync::Mutex; | |
78 use libsystemd::activation::FileDescriptor; | |
79 use libsystemd::errors::Error as LibSystemdError; | |
80 use libsystemd::errors::Result as LibSystemdResult; | |
81 | |
82 #[derive(Debug)] | |
83 pub(crate) struct Error(&'static Mutex<LibSystemdError>); | |
84 | |
85 impl fmt::Display for Error { | |
86 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
87 fmt::Display::fmt(&*self.0.lock().expect("mutex poisoned"), f) | |
88 } | |
89 } | |
90 | |
91 // No source we can't keep the mutex locked | |
92 impl std::error::Error for Error {} | |
93 | |
94 pub(crate) fn take(name: &str) -> Result<Option<FileDescriptor>, Error> { | |
95 match &*SYSTEMD_SOCKETS { | |
96 Ok(sockets) => Ok(sockets.take(name)), | |
97 Err(error) => Err(Error(error)) | |
98 } | |
99 } | |
100 | |
101 struct SystemdSockets(std::sync::Mutex<std::collections::HashMap<String, FileDescriptor>>); | |
102 | |
103 impl SystemdSockets { | |
104 fn new() -> LibSystemdResult<Self> { | |
105 // MUST BE true FOR SAFETY!!! | |
106 let map = libsystemd::activation::receive_descriptors_with_names(/*unset env = */ true)?.into_iter().map(|(fd, name)| (name, fd)).collect(); | |
107 Ok(SystemdSockets(Mutex::new(map))) | |
108 } | |
109 | |
110 fn take(&self, name: &str) -> Option<FileDescriptor> { | |
111 // MUST remove THE SOCKET FOR SAFETY!!! | |
112 self.0.lock().expect("poisoned mutex").remove(name) | |
113 } | |
114 } | |
115 | |
116 lazy_static::lazy_static! { | |
117 // We don't panic in order to let the application handle the error later | |
118 static ref SYSTEMD_SOCKETS: Result<SystemdSockets, Mutex<LibSystemdError>> = SystemdSockets::new().map_err(Mutex::new); | |
119 } | |
120 } | |
121 | |
122 /// Socket address that can be an ordinary address or a systemd socket | |
123 /// | |
124 /// This is the core type of this crate that abstracts possible addresses. | |
125 /// It can be (fallibly) converted from various types of strings or deserialized with `serde`. | |
126 /// After it's created, it can be bound as `TcpListener` from `std` or even `tokio` or `async_std` | |
127 /// if the appropriate feature is enabled. | |
128 /// | |
129 /// Optional dependencies on `parse_arg` and `serde` make it trivial to use with | |
130 /// [`configure_me`](https://crates.io/crates/configure_me). | |
131 #[derive(Debug)] | |
132 #[cfg_attr(feature = "serde", derive(serde_crate::Deserialize), serde(crate = "serde_crate", try_from = "serde_str_helpers::DeserBorrowStr"))] | |
133 pub struct SocketAddr(SocketAddrInner); | |
134 | |
135 impl SocketAddr { | |
136 /// Creates `std::net::TcpListener` | |
137 /// | |
138 /// This method either `binds` the socket, if the address was provided or uses systemd socket | |
139 /// if the socket name was provided. | |
140 pub fn bind(self) -> Result<std::net::TcpListener, BindError> { | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
141 match self.0 { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
142 SocketAddrInner::Ordinary(addr) => match std::net::TcpListener::bind(addr) { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
143 Ok(socket) => Ok(socket), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
144 Err(error) => Err(BindErrorInner::BindFailed { addr, error, }.into()), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
145 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
146 SocketAddrInner::WithHostname(addr) => match std::net::TcpListener::bind(addr.as_str()) { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
147 Ok(socket) => Ok(socket), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
148 Err(error) => Err(BindErrorInner::BindOrResolvFailed { addr, error, }.into()), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
149 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
150 SocketAddrInner::Systemd(socket_name) => Self::get_systemd(socket_name).map(|(socket, _)| socket), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
151 } |
0 | 152 } |
153 | |
154 /// Creates `tokio::net::TcpListener` | |
155 /// | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
156 /// To be specific, it binds the socket or converts systemd socket to `tokio` 0.2 socket. |
0 | 157 /// |
158 /// This method either `binds` the socket, if the address was provided or uses systemd socket | |
159 /// if the socket name was provided. | |
160 #[cfg(feature = "tokio_0_2")] | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
161 pub async fn bind_tokio_0_2(self) -> Result<tokio_0_2::net::TcpListener, TokioBindError> { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
162 match self.0 { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
163 SocketAddrInner::Ordinary(addr) => match tokio_0_2::net::TcpListener::bind(addr).await { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
164 Ok(socket) => Ok(socket), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
165 Err(error) => Err(TokioBindError::Bind(BindErrorInner::BindFailed { addr, error, }.into())), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
166 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
167 SocketAddrInner::WithHostname(addr) => match tokio_0_2::net::TcpListener::bind(addr.as_str()).await { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
168 Ok(socket) => Ok(socket), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
169 Err(error) => Err(TokioBindError::Bind(BindErrorInner::BindOrResolvFailed { addr, error, }.into())), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
170 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
171 SocketAddrInner::Systemd(socket_name) => { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
172 let (socket, addr) = Self::get_systemd(socket_name)?; |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
173 socket.try_into().map_err(|error| TokioConversionError { addr, error, }.into()) |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
174 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
175 } |
0 | 176 } |
177 | |
178 /// Creates `tokio::net::TcpListener` | |
179 /// | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
180 /// To be specific, it binds the socket or converts systemd socket to `tokio` 0.3 socket. |
0 | 181 /// |
182 /// This method either `binds` the socket, if the address was provided or uses systemd socket | |
183 /// if the socket name was provided. | |
184 #[cfg(feature = "tokio_0_3")] | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
185 pub async fn bind_tokio_0_3(self) -> Result<tokio_0_3::net::TcpListener, TokioBindError> { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
186 match self.0 { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
187 SocketAddrInner::Ordinary(addr) => match tokio_0_3::net::TcpListener::bind(addr).await { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
188 Ok(socket) => Ok(socket), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
189 Err(error) => Err(TokioBindError::Bind(BindErrorInner::BindFailed { addr, error, }.into())), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
190 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
191 SocketAddrInner::WithHostname(addr) => match tokio_0_3::net::TcpListener::bind(addr.as_str()).await { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
192 Ok(socket) => Ok(socket), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
193 Err(error) => Err(TokioBindError::Bind(BindErrorInner::BindOrResolvFailed { addr, error, }.into())), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
194 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
195 SocketAddrInner::Systemd(socket_name) => { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
196 let (socket, addr) = Self::get_systemd(socket_name)?; |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
197 socket.try_into().map_err(|error| TokioConversionError { addr, error, }.into()) |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
198 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
199 } |
0 | 200 } |
201 | |
202 /// Creates `async_std::net::TcpListener` | |
203 /// | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
204 /// To be specific, it binds the socket or converts systemd socket to `async_std` socket. |
0 | 205 /// |
206 /// This method either `binds` the socket, if the address was provided or uses systemd socket | |
207 /// if the socket name was provided. | |
208 #[cfg(feature = "async-std")] | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
209 pub async fn bind_async_std(self) -> Result<async_std::net::TcpListener, BindError> { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
210 match self.0 { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
211 SocketAddrInner::Ordinary(addr) => match async_std::net::TcpListener::bind(addr).await { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
212 Ok(socket) => Ok(socket), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
213 Err(error) => Err(BindErrorInner::BindFailed { addr, error, }.into()), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
214 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
215 SocketAddrInner::WithHostname(addr) => match async_std::net::TcpListener::bind(addr.as_str()).await { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
216 Ok(socket) => Ok(socket), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
217 Err(error) => Err(BindErrorInner::BindOrResolvFailed { addr, error, }.into()), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
218 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
219 SocketAddrInner::Systemd(socket_name) => { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
220 let (socket, _) = Self::get_systemd(socket_name)?; |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
221 Ok(socket.into()) |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
222 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
223 } |
0 | 224 } |
225 | |
226 // We can't impl<T: Deref<Target=str> + Into<String>> TryFrom<T> for SocketAddr because of orphan | |
227 // rules. | |
228 fn try_from_generic<'a, T>(string: T) -> Result<Self, ParseError> where T: 'a + std::ops::Deref<Target=str> + Into<String> { | |
229 if string.starts_with(SYSTEMD_PREFIX) { | |
13
f740dadd2948
Added enable_systemd feature
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
11
diff
changeset
|
230 #[cfg(all(linux, feature = "enable_systemd"))] |
6
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
231 { |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
232 let name_len = string.len() - SYSTEMD_PREFIX.len(); |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
233 match string[SYSTEMD_PREFIX.len()..].chars().enumerate().find(|(_, c)| !c.is_ascii() || *c < ' ' || *c == ':') { |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
234 None if name_len <= 255 => Ok(SocketAddr(SocketAddrInner::Systemd(string.into()))), |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
235 None => Err(ParseErrorInner::LongSocketName { string: string.into(), len: name_len }.into()), |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
236 Some((pos, c)) => Err(ParseErrorInner::InvalidCharacter { string: string.into(), c, pos, }.into()), |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
237 } |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
238 } |
13
f740dadd2948
Added enable_systemd feature
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
11
diff
changeset
|
239 #[cfg(not(all(linux, feature = "enable_systemd")))] |
6
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
240 { |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
241 Err(ParseErrorInner::SystemdUnsupported(string.into()).into()) |
0 | 242 } |
243 } else { | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
244 match string.parse() { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
245 Ok(addr) => Ok(SocketAddr(SocketAddrInner::Ordinary(addr))), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
246 Err(_) => Ok(SocketAddr(SocketAddrInner::WithHostname(ResolvAddr::try_from_generic(string).map_err(ParseErrorInner::ResolvAddr)?))), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
247 } |
0 | 248 } |
249 } | |
250 | |
13
f740dadd2948
Added enable_systemd feature
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
11
diff
changeset
|
251 #[cfg(all(linux, feature = "enable_systemd"))] |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
252 fn get_systemd(socket_name: String) -> Result<(std::net::TcpListener, SocketAddrInner), BindError> { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
253 use libsystemd::activation::IsType; |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
254 use std::os::unix::io::{FromRawFd, IntoRawFd}; |
0 | 255 |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
256 let socket = systemd_sockets::take(&socket_name[SYSTEMD_PREFIX.len()..]).map_err(BindErrorInner::ReceiveDescriptors)?; |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
257 // Safety: The environment variable is unset, so that no other calls can get the |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
258 // descriptors. The descriptors are taken from the map, not cloned, so they can't |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
259 // be duplicated. |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
260 unsafe { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
261 // match instead of combinators to avoid cloning socket_name |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
262 match socket { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
263 Some(socket) if socket.is_inet() => Ok((std::net::TcpListener::from_raw_fd(socket.into_raw_fd()), SocketAddrInner::Systemd(socket_name))), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
264 Some(_) => Err(BindErrorInner::NotInetSocket(socket_name).into()), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
265 None => Err(BindErrorInner::MissingDescriptor(socket_name).into()) |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
266 } |
0 | 267 } |
268 } | |
6
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
269 |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
270 // This approach makes the rest of the code much simpler as it doesn't require sprinkling it |
13
f740dadd2948
Added enable_systemd feature
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
11
diff
changeset
|
271 // with #[cfg(all(linux, feature = "enable_systemd"))] yet still statically guarantees it won't execute. |
6
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
272 #[cfg(not(linux))] |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
273 fn get_systemd(socket_name: Never) -> Result<(std::net::TcpListener, SocketAddrInner), BindError> { |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
274 match socket_name {} |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
275 } |
0 | 276 } |
277 | |
278 /// Displays the address in format that can be parsed again. | |
279 /// | |
280 /// **Important: While I don't expect this impl to change, don't rely on it!** | |
281 /// It should be used mostly for debugging/logging. | |
282 impl fmt::Display for SocketAddr { | |
283 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
284 fmt::Display::fmt(&self.0, f) | |
285 } | |
286 } | |
287 | |
288 impl fmt::Display for SocketAddrInner { | |
289 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
290 match self { | |
291 SocketAddrInner::Ordinary(addr) => fmt::Display::fmt(addr, f), | |
292 SocketAddrInner::Systemd(addr) => fmt::Display::fmt(addr, f), | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
293 SocketAddrInner::WithHostname(addr) => fmt::Display::fmt(addr, f), |
0 | 294 } |
295 } | |
296 } | |
297 | |
298 // PartialEq for testing, I'm not convinced it should be exposed | |
299 #[derive(Debug, PartialEq)] | |
300 enum SocketAddrInner { | |
301 Ordinary(std::net::SocketAddr), | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
302 WithHostname(resolv_addr::ResolvAddr), |
13
f740dadd2948
Added enable_systemd feature
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
11
diff
changeset
|
303 #[cfg(all(linux, feature = "enable_systemd"))] |
0 | 304 Systemd(String), |
13
f740dadd2948
Added enable_systemd feature
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
11
diff
changeset
|
305 #[cfg(not(all(linux, feature = "enable_systemd")))] |
6
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
306 #[allow(dead_code)] |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
307 Systemd(Never), |
0 | 308 } |
309 | |
310 const SYSTEMD_PREFIX: &str = "systemd://"; | |
311 | |
11
13e2a5545167
Implement From conversions
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
9
diff
changeset
|
312 impl<I: Into<std::net::IpAddr>> From<(I, u16)> for SocketAddr { |
13e2a5545167
Implement From conversions
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
9
diff
changeset
|
313 fn from(value: (I, u16)) -> Self { |
13e2a5545167
Implement From conversions
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
9
diff
changeset
|
314 SocketAddr(SocketAddrInner::Ordinary(value.into())) |
13e2a5545167
Implement From conversions
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
9
diff
changeset
|
315 } |
13e2a5545167
Implement From conversions
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
9
diff
changeset
|
316 } |
13e2a5545167
Implement From conversions
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
9
diff
changeset
|
317 |
13e2a5545167
Implement From conversions
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
9
diff
changeset
|
318 impl From<std::net::SocketAddrV4> for SocketAddr { |
13e2a5545167
Implement From conversions
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
9
diff
changeset
|
319 fn from(value: std::net::SocketAddrV4) -> Self { |
13e2a5545167
Implement From conversions
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
9
diff
changeset
|
320 SocketAddr(SocketAddrInner::Ordinary(value.into())) |
13e2a5545167
Implement From conversions
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
9
diff
changeset
|
321 } |
13e2a5545167
Implement From conversions
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
9
diff
changeset
|
322 } |
13e2a5545167
Implement From conversions
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
9
diff
changeset
|
323 |
13e2a5545167
Implement From conversions
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
9
diff
changeset
|
324 impl From<std::net::SocketAddrV6> for SocketAddr { |
13e2a5545167
Implement From conversions
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
9
diff
changeset
|
325 fn from(value: std::net::SocketAddrV6) -> Self { |
13e2a5545167
Implement From conversions
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
9
diff
changeset
|
326 SocketAddr(SocketAddrInner::Ordinary(value.into())) |
13e2a5545167
Implement From conversions
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
9
diff
changeset
|
327 } |
13e2a5545167
Implement From conversions
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
9
diff
changeset
|
328 } |
13e2a5545167
Implement From conversions
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
9
diff
changeset
|
329 |
0 | 330 impl std::str::FromStr for SocketAddr { |
331 type Err = ParseError; | |
332 | |
333 fn from_str(s: &str) -> Result<Self, Self::Err> { | |
334 SocketAddr::try_from_generic(s) | |
335 } | |
336 } | |
337 | |
338 impl<'a> TryFrom<&'a str> for SocketAddr { | |
339 type Error = ParseError; | |
340 | |
341 fn try_from(s: &'a str) -> Result<Self, Self::Error> { | |
342 SocketAddr::try_from_generic(s) | |
343 } | |
344 } | |
345 | |
346 impl TryFrom<String> for SocketAddr { | |
347 type Error = ParseError; | |
348 | |
349 fn try_from(s: String) -> Result<Self, Self::Error> { | |
350 SocketAddr::try_from_generic(s) | |
351 } | |
352 } | |
353 | |
354 impl<'a> TryFrom<&'a OsStr> for SocketAddr { | |
355 type Error = ParseOsStrError; | |
356 | |
357 fn try_from(s: &'a OsStr) -> Result<Self, Self::Error> { | |
358 s.to_str().ok_or(ParseOsStrError::InvalidUtf8)?.try_into().map_err(Into::into) | |
359 } | |
360 } | |
361 | |
362 impl TryFrom<OsString> for SocketAddr { | |
363 type Error = ParseOsStrError; | |
364 | |
365 fn try_from(s: OsString) -> Result<Self, Self::Error> { | |
366 s.into_string().map_err(|_| ParseOsStrError::InvalidUtf8)?.try_into().map_err(Into::into) | |
367 } | |
368 } | |
369 | |
370 #[cfg(feature = "serde")] | |
371 impl<'a> TryFrom<serde_str_helpers::DeserBorrowStr<'a>> for SocketAddr { | |
372 type Error = ParseError; | |
373 | |
374 fn try_from(s: serde_str_helpers::DeserBorrowStr<'a>) -> Result<Self, Self::Error> { | |
9
4fb70ca820a6
Upgrade serde_str_helpers
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
7
diff
changeset
|
375 SocketAddr::try_from_generic(s) |
0 | 376 } |
377 } | |
378 | |
379 #[cfg(feature = "parse_arg")] | |
380 impl parse_arg::ParseArg for SocketAddr { | |
381 type Error = ParseOsStrError; | |
382 | |
383 fn describe_type<W: fmt::Write>(mut writer: W) -> fmt::Result { | |
384 std::net::SocketAddr::describe_type(&mut writer)?; | |
385 write!(writer, " or a systemd socket name prefixed with systemd://") | |
386 } | |
387 | |
388 fn parse_arg(arg: &OsStr) -> Result<Self, Self::Error> { | |
389 arg.try_into() | |
390 } | |
391 | |
392 fn parse_owned_arg(arg: OsString) -> Result<Self, Self::Error> { | |
393 arg.try_into() | |
394 } | |
395 } | |
396 | |
397 #[cfg(test)] | |
398 mod tests { | |
399 use super::{SocketAddr, SocketAddrInner}; | |
400 | |
401 #[test] | |
402 fn parse_ordinary() { | |
403 assert_eq!("127.0.0.1:42".parse::<SocketAddr>().unwrap().0, SocketAddrInner::Ordinary(([127, 0, 0, 1], 42).into())); | |
404 } | |
405 | |
406 #[test] | |
13
f740dadd2948
Added enable_systemd feature
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
11
diff
changeset
|
407 #[cfg(all(linux, feature = "enable_systemd"))] |
0 | 408 fn parse_systemd() { |
409 assert_eq!("systemd://foo".parse::<SocketAddr>().unwrap().0, SocketAddrInner::Systemd("systemd://foo".to_owned())); | |
410 } | |
2
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
411 |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
412 #[test] |
13
f740dadd2948
Added enable_systemd feature
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
11
diff
changeset
|
413 #[cfg(not(all(linux, feature = "enable_systemd")))] |
7
9731ff589d9c
Fix tests on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
6
diff
changeset
|
414 #[should_panic] |
9731ff589d9c
Fix tests on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
6
diff
changeset
|
415 fn parse_systemd() { |
9731ff589d9c
Fix tests on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
6
diff
changeset
|
416 "systemd://foo".parse::<SocketAddr>().unwrap(); |
9731ff589d9c
Fix tests on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
6
diff
changeset
|
417 } |
9731ff589d9c
Fix tests on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
6
diff
changeset
|
418 |
9731ff589d9c
Fix tests on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
6
diff
changeset
|
419 #[test] |
2
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
420 #[should_panic] |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
421 fn parse_systemd_fail_control() { |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
422 "systemd://foo\n".parse::<SocketAddr>().unwrap(); |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
423 } |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
424 |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
425 #[test] |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
426 #[should_panic] |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
427 fn parse_systemd_fail_colon() { |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
428 "systemd://foo:".parse::<SocketAddr>().unwrap(); |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
429 } |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
430 |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
431 #[test] |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
432 #[should_panic] |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
433 fn parse_systemd_fail_non_ascii() { |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
434 "systemd://fooĆ”".parse::<SocketAddr>().unwrap(); |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
435 } |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
436 |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
437 #[test] |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
438 #[should_panic] |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
439 fn parse_systemd_fail_too_long() { |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
440 "systemd://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".parse::<SocketAddr>().unwrap(); |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
441 } |
0 | 442 } |