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