Mercurial > crates > systemd-socket
annotate 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 |
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 //! | |
45 //! * `serde` - implements `serde::Deserialize` for `SocketAddr` | |
46 //! * `parse_arg` - implements `parse_arg::ParseArg` for `SocketAddr` | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
47 //! * `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
|
48 //! * `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
|
49 //! * `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
|
50 //! |
0edcde404b02
Added information about MSRV
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
2
diff
changeset
|
51 //! ## MSRV |
0edcde404b02
Added information about MSRV
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
2
diff
changeset
|
52 //! |
0edcde404b02
Added information about MSRV
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
2
diff
changeset
|
53 //! 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
|
54 //! 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
|
55 |
0 | 56 |
57 #![deny(missing_docs)] | |
58 | |
59 pub mod error; | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
60 mod resolv_addr; |
0 | 61 |
62 use std::convert::{TryFrom, TryInto}; | |
63 use std::fmt; | |
64 use std::ffi::{OsStr, OsString}; | |
65 use crate::error::*; | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
66 use crate::resolv_addr::ResolvAddr; |
0 | 67 |
6
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
68 #[cfg(not(linux))] |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
69 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
|
70 |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
71 #[cfg(linux)] |
0 | 72 pub(crate) mod systemd_sockets { |
73 use std::fmt; | |
74 use std::sync::Mutex; | |
75 use libsystemd::activation::FileDescriptor; | |
76 use libsystemd::errors::Error as LibSystemdError; | |
77 use libsystemd::errors::Result as LibSystemdResult; | |
78 | |
79 #[derive(Debug)] | |
80 pub(crate) struct Error(&'static Mutex<LibSystemdError>); | |
81 | |
82 impl fmt::Display for Error { | |
83 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
84 fmt::Display::fmt(&*self.0.lock().expect("mutex poisoned"), f) | |
85 } | |
86 } | |
87 | |
88 // No source we can't keep the mutex locked | |
89 impl std::error::Error for Error {} | |
90 | |
91 pub(crate) fn take(name: &str) -> Result<Option<FileDescriptor>, Error> { | |
92 match &*SYSTEMD_SOCKETS { | |
93 Ok(sockets) => Ok(sockets.take(name)), | |
94 Err(error) => Err(Error(error)) | |
95 } | |
96 } | |
97 | |
98 struct SystemdSockets(std::sync::Mutex<std::collections::HashMap<String, FileDescriptor>>); | |
99 | |
100 impl SystemdSockets { | |
101 fn new() -> LibSystemdResult<Self> { | |
102 // MUST BE true FOR SAFETY!!! | |
103 let map = libsystemd::activation::receive_descriptors_with_names(/*unset env = */ true)?.into_iter().map(|(fd, name)| (name, fd)).collect(); | |
104 Ok(SystemdSockets(Mutex::new(map))) | |
105 } | |
106 | |
107 fn take(&self, name: &str) -> Option<FileDescriptor> { | |
108 // MUST remove THE SOCKET FOR SAFETY!!! | |
109 self.0.lock().expect("poisoned mutex").remove(name) | |
110 } | |
111 } | |
112 | |
113 lazy_static::lazy_static! { | |
114 // We don't panic in order to let the application handle the error later | |
115 static ref SYSTEMD_SOCKETS: Result<SystemdSockets, Mutex<LibSystemdError>> = SystemdSockets::new().map_err(Mutex::new); | |
116 } | |
117 } | |
118 | |
119 /// Socket address that can be an ordinary address or a systemd socket | |
120 /// | |
121 /// This is the core type of this crate that abstracts possible addresses. | |
122 /// It can be (fallibly) converted from various types of strings or deserialized with `serde`. | |
123 /// After it's created, it can be bound as `TcpListener` from `std` or even `tokio` or `async_std` | |
124 /// if the appropriate feature is enabled. | |
125 /// | |
126 /// Optional dependencies on `parse_arg` and `serde` make it trivial to use with | |
127 /// [`configure_me`](https://crates.io/crates/configure_me). | |
128 #[derive(Debug)] | |
129 #[cfg_attr(feature = "serde", derive(serde_crate::Deserialize), serde(crate = "serde_crate", try_from = "serde_str_helpers::DeserBorrowStr"))] | |
130 pub struct SocketAddr(SocketAddrInner); | |
131 | |
132 impl SocketAddr { | |
133 /// Creates `std::net::TcpListener` | |
134 /// | |
135 /// This method either `binds` the socket, if the address was provided or uses systemd socket | |
136 /// if the socket name was provided. | |
137 pub fn bind(self) -> Result<std::net::TcpListener, BindError> { | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
138 match self.0 { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
139 SocketAddrInner::Ordinary(addr) => match std::net::TcpListener::bind(addr) { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
140 Ok(socket) => Ok(socket), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
141 Err(error) => Err(BindErrorInner::BindFailed { addr, error, }.into()), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
142 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
143 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
|
144 Ok(socket) => Ok(socket), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
145 Err(error) => Err(BindErrorInner::BindOrResolvFailed { addr, error, }.into()), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
146 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
147 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
|
148 } |
0 | 149 } |
150 | |
151 /// Creates `tokio::net::TcpListener` | |
152 /// | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
153 /// To be specific, it binds the socket or converts systemd socket to `tokio` 0.2 socket. |
0 | 154 /// |
155 /// This method either `binds` the socket, if the address was provided or uses systemd socket | |
156 /// if the socket name was provided. | |
157 #[cfg(feature = "tokio_0_2")] | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
158 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
|
159 match self.0 { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
160 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
|
161 Ok(socket) => Ok(socket), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
162 Err(error) => Err(TokioBindError::Bind(BindErrorInner::BindFailed { addr, error, }.into())), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
163 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
164 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
|
165 Ok(socket) => Ok(socket), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
166 Err(error) => Err(TokioBindError::Bind(BindErrorInner::BindOrResolvFailed { addr, error, }.into())), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
167 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
168 SocketAddrInner::Systemd(socket_name) => { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
169 let (socket, addr) = Self::get_systemd(socket_name)?; |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
170 socket.try_into().map_err(|error| TokioConversionError { addr, error, }.into()) |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
171 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
172 } |
0 | 173 } |
174 | |
175 /// Creates `tokio::net::TcpListener` | |
176 /// | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
177 /// To be specific, it binds the socket or converts systemd socket to `tokio` 0.3 socket. |
0 | 178 /// |
179 /// This method either `binds` the socket, if the address was provided or uses systemd socket | |
180 /// if the socket name was provided. | |
181 #[cfg(feature = "tokio_0_3")] | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
182 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
|
183 match self.0 { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
184 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
|
185 Ok(socket) => Ok(socket), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
186 Err(error) => Err(TokioBindError::Bind(BindErrorInner::BindFailed { addr, error, }.into())), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
187 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
188 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
|
189 Ok(socket) => Ok(socket), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
190 Err(error) => Err(TokioBindError::Bind(BindErrorInner::BindOrResolvFailed { addr, error, }.into())), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
191 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
192 SocketAddrInner::Systemd(socket_name) => { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
193 let (socket, addr) = Self::get_systemd(socket_name)?; |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
194 socket.try_into().map_err(|error| TokioConversionError { addr, error, }.into()) |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
195 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
196 } |
0 | 197 } |
198 | |
199 /// Creates `async_std::net::TcpListener` | |
200 /// | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
201 /// To be specific, it binds the socket or converts systemd socket to `async_std` socket. |
0 | 202 /// |
203 /// This method either `binds` the socket, if the address was provided or uses systemd socket | |
204 /// if the socket name was provided. | |
205 #[cfg(feature = "async-std")] | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
206 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
|
207 match self.0 { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
208 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
|
209 Ok(socket) => Ok(socket), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
210 Err(error) => Err(BindErrorInner::BindFailed { addr, error, }.into()), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
211 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
212 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
|
213 Ok(socket) => Ok(socket), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
214 Err(error) => Err(BindErrorInner::BindOrResolvFailed { addr, error, }.into()), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
215 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
216 SocketAddrInner::Systemd(socket_name) => { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
217 let (socket, _) = Self::get_systemd(socket_name)?; |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
218 Ok(socket.into()) |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
219 }, |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
220 } |
0 | 221 } |
222 | |
223 // We can't impl<T: Deref<Target=str> + Into<String>> TryFrom<T> for SocketAddr because of orphan | |
224 // rules. | |
225 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) { | |
6
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
227 #[cfg(linux)] |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
228 { |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
229 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
|
230 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
|
231 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
|
232 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
|
233 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
|
234 } |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
235 } |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
236 #[cfg(not(linux))] |
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 Err(ParseErrorInner::SystemdUnsupported(string.into()).into()) |
0 | 239 } |
240 } else { | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
241 match string.parse() { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
242 Ok(addr) => Ok(SocketAddr(SocketAddrInner::Ordinary(addr))), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
243 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
|
244 } |
0 | 245 } |
246 } | |
247 | |
6
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
248 #[cfg(linux)] |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
249 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
|
250 use libsystemd::activation::IsType; |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
251 use std::os::unix::io::{FromRawFd, IntoRawFd}; |
0 | 252 |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
253 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
|
254 // 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
|
255 // 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
|
256 // be duplicated. |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
257 unsafe { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
258 // match instead of combinators to avoid cloning socket_name |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
259 match socket { |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
260 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
|
261 Some(_) => Err(BindErrorInner::NotInetSocket(socket_name).into()), |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
262 None => Err(BindErrorInner::MissingDescriptor(socket_name).into()) |
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
263 } |
0 | 264 } |
265 } | |
6
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
266 |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
267 // This approach makes the rest of the code much simpler as it doesn't require sprinkling it |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
268 // with #[cfg(linux)] yet still statically guarantees it won't execute. |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
269 #[cfg(not(linux))] |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
270 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
|
271 match socket_name {} |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
272 } |
0 | 273 } |
274 | |
275 /// Displays the address in format that can be parsed again. | |
276 /// | |
277 /// **Important: While I don't expect this impl to change, don't rely on it!** | |
278 /// It should be used mostly for debugging/logging. | |
279 impl fmt::Display for SocketAddr { | |
280 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
281 fmt::Display::fmt(&self.0, f) | |
282 } | |
283 } | |
284 | |
285 impl fmt::Display for SocketAddrInner { | |
286 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
287 match self { | |
288 SocketAddrInner::Ordinary(addr) => fmt::Display::fmt(addr, f), | |
289 SocketAddrInner::Systemd(addr) => fmt::Display::fmt(addr, f), | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
290 SocketAddrInner::WithHostname(addr) => fmt::Display::fmt(addr, f), |
0 | 291 } |
292 } | |
293 } | |
294 | |
295 // PartialEq for testing, I'm not convinced it should be exposed | |
296 #[derive(Debug, PartialEq)] | |
297 enum SocketAddrInner { | |
298 Ordinary(std::net::SocketAddr), | |
4
66c0e10c89fc
Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
3
diff
changeset
|
299 WithHostname(resolv_addr::ResolvAddr), |
6
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
300 #[cfg(linux)] |
0 | 301 Systemd(String), |
6
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
302 #[cfg(not(linux))] |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
303 #[allow(dead_code)] |
a7893294e9b2
Make the crate compilable on non-linux systems
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
4
diff
changeset
|
304 Systemd(Never), |
0 | 305 } |
306 | |
307 const SYSTEMD_PREFIX: &str = "systemd://"; | |
308 | |
309 impl std::str::FromStr for SocketAddr { | |
310 type Err = ParseError; | |
311 | |
312 fn from_str(s: &str) -> Result<Self, Self::Err> { | |
313 SocketAddr::try_from_generic(s) | |
314 } | |
315 } | |
316 | |
317 impl<'a> TryFrom<&'a str> for SocketAddr { | |
318 type Error = ParseError; | |
319 | |
320 fn try_from(s: &'a str) -> Result<Self, Self::Error> { | |
321 SocketAddr::try_from_generic(s) | |
322 } | |
323 } | |
324 | |
325 impl TryFrom<String> for SocketAddr { | |
326 type Error = ParseError; | |
327 | |
328 fn try_from(s: String) -> Result<Self, Self::Error> { | |
329 SocketAddr::try_from_generic(s) | |
330 } | |
331 } | |
332 | |
333 impl<'a> TryFrom<&'a OsStr> for SocketAddr { | |
334 type Error = ParseOsStrError; | |
335 | |
336 fn try_from(s: &'a OsStr) -> Result<Self, Self::Error> { | |
337 s.to_str().ok_or(ParseOsStrError::InvalidUtf8)?.try_into().map_err(Into::into) | |
338 } | |
339 } | |
340 | |
341 impl TryFrom<OsString> for SocketAddr { | |
342 type Error = ParseOsStrError; | |
343 | |
344 fn try_from(s: OsString) -> Result<Self, Self::Error> { | |
345 s.into_string().map_err(|_| ParseOsStrError::InvalidUtf8)?.try_into().map_err(Into::into) | |
346 } | |
347 } | |
348 | |
349 #[cfg(feature = "serde")] | |
350 impl<'a> TryFrom<serde_str_helpers::DeserBorrowStr<'a>> for SocketAddr { | |
351 type Error = ParseError; | |
352 | |
353 fn try_from(s: serde_str_helpers::DeserBorrowStr<'a>) -> Result<Self, Self::Error> { | |
354 SocketAddr::try_from_generic(std::borrow::Cow::from(s)) | |
355 } | |
356 } | |
357 | |
358 #[cfg(feature = "parse_arg")] | |
359 impl parse_arg::ParseArg for SocketAddr { | |
360 type Error = ParseOsStrError; | |
361 | |
362 fn describe_type<W: fmt::Write>(mut writer: W) -> fmt::Result { | |
363 std::net::SocketAddr::describe_type(&mut writer)?; | |
364 write!(writer, " or a systemd socket name prefixed with systemd://") | |
365 } | |
366 | |
367 fn parse_arg(arg: &OsStr) -> Result<Self, Self::Error> { | |
368 arg.try_into() | |
369 } | |
370 | |
371 fn parse_owned_arg(arg: OsString) -> Result<Self, Self::Error> { | |
372 arg.try_into() | |
373 } | |
374 } | |
375 | |
376 #[cfg(test)] | |
377 mod tests { | |
378 use super::{SocketAddr, SocketAddrInner}; | |
379 | |
380 #[test] | |
381 fn parse_ordinary() { | |
382 assert_eq!("127.0.0.1:42".parse::<SocketAddr>().unwrap().0, SocketAddrInner::Ordinary(([127, 0, 0, 1], 42).into())); | |
383 } | |
384 | |
385 #[test] | |
386 fn parse_systemd() { | |
387 assert_eq!("systemd://foo".parse::<SocketAddr>().unwrap().0, SocketAddrInner::Systemd("systemd://foo".to_owned())); | |
388 } | |
2
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
389 |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
390 #[test] |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
391 #[should_panic] |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
392 fn parse_systemd_fail_control() { |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
393 "systemd://foo\n".parse::<SocketAddr>().unwrap(); |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
394 } |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
395 |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
396 #[test] |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
397 #[should_panic] |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
398 fn parse_systemd_fail_colon() { |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
399 "systemd://foo:".parse::<SocketAddr>().unwrap(); |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
400 } |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
401 |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
402 #[test] |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
403 #[should_panic] |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
404 fn parse_systemd_fail_non_ascii() { |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
405 "systemd://fooĆ”".parse::<SocketAddr>().unwrap(); |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
406 } |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
407 |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
408 #[test] |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
409 #[should_panic] |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
410 fn parse_systemd_fail_too_long() { |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
411 "systemd://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".parse::<SocketAddr>().unwrap(); |
cabc4aafdd85
Added length check and a few tests
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
0
diff
changeset
|
412 } |
0 | 413 } |