interprocess_docfix/os/unix/udsocket/path.rs
1use super::{
2 imports::*,
3 util::{empty_cstr, empty_cstring, eunreachable},
4 MAX_UDSOCKET_PATH_LEN,
5};
6use std::{
7 borrow::{Cow, ToOwned},
8 convert::TryFrom,
9 ffi::{CStr, CString, NulError, OsStr, OsString},
10 io,
11 mem::{replace, size_of_val, zeroed},
12 ops::Deref,
13 path::{Path, PathBuf},
14 ptr,
15};
16
17/// Represents a name for a Unix domain socket.
18///
19/// The main purpose for this enumeration is to conditionally support the dedicated socket namespace on systems which implement it – for that, the `Namespaced` variant is used. Depending on your system, you might not be seeing it, which is when you'd need the `File` fallback variant, which works on all POSIX-compliant systems.
20///
21/// ## `Namespaced`
22/// This variant refers to sockets in a dedicated socket namespace, which is fully isolated from the main filesystem and closes sockets automatically when the server which opened the socket shuts down. **This variant is only implemented on Linux, which is why it is not available on other POSIX-conformant systems at compile time, resulting in a compile-time error if usage is attempted.**
23///
24/// ## `File`
25/// All sockets identified this way are located on the main filesystem and exist as persistent files until deletion, preventing servers from using the same socket without deleting it from the filesystem first. This variant is available on all POSIX-compilant systems.
26#[derive(Clone, Debug, PartialEq, Eq)]
27pub enum UdSocketPath<'a> {
28 /// An unnamed socket, identified only by its file descriptor. This is an invalid path value for creating sockets – all attempts to use such a value will result in an error.
29 Unnamed,
30 /// Identifies a socket which is located in the filesystem tree, existing as a file. See the [enum-level documentation] for more.
31 ///
32 /// [enum-level documentation]: #file " "
33 File(Cow<'a, CStr>),
34 /// Identifies a socket in the dedicated socket namespace, where it exists until the server closes it rather than persisting as a file. See the [enum-level documentation] for more.
35 ///
36 /// [enum-level documentation]: #namespaced " "
37 #[cfg(uds_linux_namespace)]
38 #[cfg_attr( // uds_linux_namespace template
39 feature = "doc_cfg",
40 doc(cfg(any(target_os = "linux", target_os = "android")))
41 )]
42 Namespaced(Cow<'a, CStr>),
43}
44impl<'a> UdSocketPath<'a> {
45 /// Returns the path as a [`CStr`]. The resulting value does not include any indication of whether it's a namespaced socket name or a filesystem path.
46 pub fn as_cstr(&'a self) -> &'a CStr {
47 match self {
48 Self::File(cow) => cow.deref(),
49 #[cfg(uds_linux_namespace)]
50 Self::Namespaced(cow) => cow.deref(),
51 Self::Unnamed => empty_cstr(),
52 }
53 }
54 /// Returns the path as an [`OsStr`]. The resulting value does not include any indication of whether it's a namespaced socket name or a filesystem path.
55 pub fn as_osstr(&'a self) -> &'a OsStr {
56 OsStr::from_bytes(self.as_cstr().to_bytes())
57 }
58 /// Returns the path as a [`CString`]. The resulting value does not include any indication of whether it's a namespaced socket name or a filesystem path.
59 pub fn into_cstring(self) -> CString {
60 match self {
61 Self::File(cow) => cow.into_owned(),
62 #[cfg(uds_linux_namespace)]
63 Self::Namespaced(cow) => cow.into_owned(),
64 Self::Unnamed => empty_cstring(),
65 }
66 }
67 /// Returns the path as an [`OsString`]. The resulting value does not include any indication of whether it's a namespaced socket name or a filesystem path.
68 pub fn into_osstring(self) -> OsString {
69 OsString::from_vec(self.into_cstring().into_bytes())
70 }
71
72 /// Ensures that the path is stored as an owned `CString` in place, and returns whether that required cloning or not. If `self` was not referring to any socket ([`Unnamed` variant]), the value is set to an empty `CString` (only nul terminator) of type [`File`].
73 ///
74 /// [`Unnamed` variant]: #variant.Unnamed " "
75 /// [`File`]: #file " "
76 pub fn make_owned(&mut self) -> bool {
77 let required_cloning = !self.is_owned();
78 *self = self.to_owned();
79 required_cloning
80 }
81 /// Converts to a `UdSocketPath<'static>` which stores the path as an owned `CString`, cloning if necessary.
82 // TODO implement ToOwned instead of Clone in 2.0.0
83 pub fn to_owned(&self) -> UdSocketPath<'static> {
84 match self {
85 Self::File(f) => UdSocketPath::File(Cow::Owned(f.as_ref().to_owned())),
86 #[cfg(uds_linux_namespace)]
87 Self::Namespaced(n) => UdSocketPath::Namespaced(Cow::Owned(n.as_ref().to_owned())),
88 Self::Unnamed => UdSocketPath::Unnamed,
89 }
90 }
91 /// Borrows into another `UdSocketPath<'_>` instance. If borrowed here, reborrows; if owned here, returns a fresh borrow.
92 pub fn borrow(&self) -> UdSocketPath<'_> {
93 match self {
94 UdSocketPath::File(f) => UdSocketPath::File(Cow::Borrowed(f.as_ref())),
95 #[cfg(uds_linux_namespace)]
96 UdSocketPath::Namespaced(n) => UdSocketPath::Namespaced(Cow::Borrowed(n.as_ref())),
97 UdSocketPath::Unnamed => UdSocketPath::Unnamed,
98 }
99 }
100
101 /// Returns a mutable reference to the underlying `CString`, cloning the borrowed path if it wasn't owned before.
102 pub fn get_cstring_mut(&mut self) -> &mut CString {
103 self.make_owned();
104 self.try_get_cstring_mut().unwrap_or_else(|| unsafe {
105 // SAFETY: the call to make_owned ensured that there is a CString
106 std::hint::unreachable_unchecked()
107 })
108 }
109 /// Returns a mutable reference to the underlying `CString` if it's available as owned, otherwise returns `None`.
110 pub fn try_get_cstring_mut(&mut self) -> Option<&mut CString> {
111 let cow = match self {
112 Self::File(cow) => cow,
113 #[cfg(uds_linux_namespace)]
114 Self::Namespaced(cow) => cow,
115 Self::Unnamed => return None,
116 };
117 match cow {
118 Cow::Owned(cstring) => Some(cstring),
119 Cow::Borrowed(..) => None,
120 }
121 }
122
123 /// Returns `true` if the path to the socket is stored as an owned `CString`, i.e. if `into_cstring` doesn't require cloning the path; `false` otherwise.
124 // Cannot use `matches!` due to #[cfg(...)]
125 #[allow(clippy::match_like_matches_macro)]
126 pub const fn is_owned(&self) -> bool {
127 match self {
128 Self::File(Cow::Borrowed(..)) => true,
129 #[cfg(uds_linux_namespace)]
130 Self::Namespaced(Cow::Borrowed(..)) => true,
131 _ => false,
132 }
133 }
134
135 #[cfg(unix)]
136 pub(super) fn write_sockaddr_un_to_self(&mut self, addr: &sockaddr_un, addrlen: usize) {
137 let sun_path_length = (addrlen as isize) - (size_of_val(&addr.sun_family) as isize);
138 let sun_path_length = match usize::try_from(sun_path_length) {
139 Ok(val) => val,
140 Err(..) => {
141 *self = Self::Unnamed;
142 return;
143 }
144 };
145 if let Some(cstring) = self.try_get_cstring_mut() {
146 let cstring = replace(cstring, empty_cstring());
147 let mut vec = cstring.into_bytes_with_nul();
148 let mut _namespaced = false;
149 unsafe {
150 #[cfg(uds_linux_namespace)]
151 let (src_ptr, path_length) = if addr.sun_path[0] == 0 {
152 _namespaced = true;
153 (
154 addr.sun_path.as_ptr().offset(1) as *const u8,
155 sun_path_length - 1,
156 )
157 } else {
158 (addr.sun_path.as_ptr() as *const u8, sun_path_length)
159 };
160 #[cfg(not(uds_linux_namespace))]
161 let (src_ptr, path_length) =
162 { (addr.sun_path.as_ptr() as *const u8, sun_path_length) };
163 // Fill the space for the name and the nul terminator with nuls
164 vec.resize(path_length, 0);
165 ptr::copy_nonoverlapping(src_ptr, vec.as_mut_ptr(), path_length);
166 };
167 // If the system added a nul byte as part of the length, remove the one we added ourselves.
168 if vec.last() == Some(&0) && vec[vec.len() - 2] == 0 {
169 vec.pop();
170 }
171 let new_cstring = CString::new(vec).unwrap_or_else(eunreachable);
172 #[cfg(uds_linux_namespace)]
173 let path_to_write = if _namespaced {
174 UdSocketPath::Namespaced(Cow::Owned(new_cstring))
175 } else {
176 UdSocketPath::File(Cow::Owned(new_cstring))
177 };
178 #[cfg(not(uds_linux_namespace))]
179 let path_to_write = UdSocketPath::File(Cow::Owned(new_cstring));
180 *self = path_to_write;
181 // Implicitly drops the empty CString we wrote in the beginning
182 } else {
183 let mut _namespaced = false;
184 let mut vec = unsafe {
185 let (src_ptr, path_length) = if addr.sun_path[0] == 0 {
186 (
187 addr.sun_path.as_ptr().offset(1) as *const u8,
188 sun_path_length - 1,
189 )
190 } else {
191 (addr.sun_path.as_ptr() as *const u8, sun_path_length)
192 };
193 let mut vec = vec![0; path_length];
194 ptr::copy_nonoverlapping(src_ptr, vec.as_mut_ptr(), path_length);
195 vec
196 };
197 // If the system added a nul byte as part of the length, remove it.
198 if vec.last() == Some(&0) {
199 vec.pop();
200 }
201 let cstring = CString::new(vec).unwrap_or_else(eunreachable);
202 #[cfg(uds_linux_namespace)]
203 let path_to_write = if _namespaced {
204 UdSocketPath::Namespaced(Cow::Owned(cstring))
205 } else {
206 UdSocketPath::File(Cow::Owned(cstring))
207 };
208 #[cfg(not(uds_linux_namespace))]
209 let path_to_write = UdSocketPath::File(Cow::Owned(cstring));
210 *self = path_to_write;
211 }
212 }
213 /// Returns `addr_len` to pass to `bind`/`connect`.
214 #[cfg(unix)]
215 pub(super) fn write_self_to_sockaddr_un(&self, addr: &mut sockaddr_un) -> io::Result<()> {
216 let is_namespaced;
217 let len_of_self = self.as_cstr().to_bytes_with_nul().len();
218 match self {
219 UdSocketPath::File(..) => {
220 is_namespaced = false;
221 if len_of_self > MAX_UDSOCKET_PATH_LEN {
222 return Err(io::Error::new(
223 io::ErrorKind::InvalidInput,
224 format!(
225 "socket path should not be longer than {} bytes",
226 MAX_UDSOCKET_PATH_LEN
227 ),
228 ));
229 }
230 }
231 #[cfg(uds_linux_namespace)]
232 UdSocketPath::Namespaced(..) => {
233 is_namespaced = true;
234 if len_of_self > (MAX_UDSOCKET_PATH_LEN - 1) {
235 return Err(io::Error::new(
236 io::ErrorKind::InvalidInput,
237 format!(
238 "namespaced socket name should not be longer than {} bytes",
239 MAX_UDSOCKET_PATH_LEN - 1
240 ),
241 ));
242 }
243 }
244 UdSocketPath::Unnamed => {
245 return Err(io::Error::new(
246 io::ErrorKind::InvalidInput,
247 "must provide a name for the socket",
248 ))
249 }
250 }
251
252 unsafe {
253 ptr::copy_nonoverlapping(
254 self.as_cstr().as_ptr(),
255 if is_namespaced {
256 addr.sun_path.as_mut_ptr().offset(1)
257 } else {
258 addr.sun_path.as_mut_ptr()
259 },
260 len_of_self,
261 );
262 }
263 Ok(())
264 }
265}
266impl UdSocketPath<'static> {
267 /// Creates a buffer suitable for usage with [`recv_from`] ([`_ancillary`]/[`_vectored`]/[`_ancillary_vectored`]). The capacity is equal to the [`MAX_UDSOCKET_PATH_LEN`] constant (the nul terminator in the `CString` is included). **The contained value is unspecified – results of reading from the buffer should not be relied upon.**
268 ///
269 /// # Example
270 /// ```
271 /// # #[cfg(unix)] {
272 /// use interprocess::os::unix::udsocket::{UdSocketPath, MAX_UDSOCKET_PATH_LEN};
273 /// use std::borrow::Cow;
274 ///
275 /// let path_buffer = UdSocketPath::buffer();
276 /// match path_buffer {
277 /// UdSocketPath::File(cow) => match cow {
278 /// Cow::Owned(cstring)
279 /// => assert_eq!(cstring.into_bytes_with_nul().capacity(), MAX_UDSOCKET_PATH_LEN),
280 /// Cow::Borrowed(..) => unreachable!(),
281 /// }
282 /// _ => unreachable!(),
283 /// }
284 /// # }
285 /// ```
286 ///
287 /// [`recv_from`]: struct.UdSocket.html#method.recv_from " "
288 /// [`_ancillary`]: struct.UdSocket.html#method.recv_from " "
289 /// [`_vectored`]: struct.UdSocket.html#method.recv_from_vectored " "
290 /// [`_ancillary_vectored`]: struct.UdSocket.html#method.recv_from_ancillary_vectored " "
291 /// [`MAX_UDSOCKET_PATH_LEN`]: constant.MAX_UDSOCKET_PATH_LEN.html " "
292 pub fn buffer() -> Self {
293 Self::File(Cow::Owned(
294 CString::new(vec![0x2F; MAX_UDSOCKET_PATH_LEN - 1])
295 .expect("unexpected nul in newly created Vec, possible heap corruption"),
296 ))
297 }
298
299 /// Constructs a `UdSocketPath::File` value from a `Vec` of bytes, wrapping `CString::new`.
300 pub fn file_from_vec(vec: Vec<u8>) -> Result<Self, NulError> {
301 Ok(Self::File(Cow::Owned(CString::new(vec)?)))
302 }
303 /// Constructs a `UdSocketPath::Namespaced` value from a `Vec` of bytes, wrapping `CString::new`.
304 #[cfg(uds_linux_namespace)]
305 #[cfg_attr( // uds_linux_namespace template
306 feature = "doc_cfg",
307 doc(cfg(any(target_os = "linux", target_os = "android")))
308 )]
309 pub fn namespaced_from_vec(vec: Vec<u8>) -> Result<Self, NulError> {
310 Ok(Self::Namespaced(Cow::Owned(CString::new(vec)?)))
311 }
312}
313impl From<UdSocketPath<'_>> for CString {
314 fn from(path: UdSocketPath<'_>) -> Self {
315 path.into_cstring()
316 }
317}
318impl AsRef<CStr> for UdSocketPath<'_> {
319 fn as_ref(&self) -> &CStr {
320 self.as_cstr()
321 }
322}
323impl From<UdSocketPath<'_>> for OsString {
324 fn from(path: UdSocketPath<'_>) -> Self {
325 path.into_osstring()
326 }
327}
328impl AsRef<OsStr> for UdSocketPath<'_> {
329 fn as_ref(&self) -> &OsStr {
330 self.as_osstr()
331 }
332}
333impl TryFrom<UdSocketPath<'_>> for sockaddr_un {
334 type Error = io::Error;
335 fn try_from(path: UdSocketPath<'_>) -> io::Result<Self> {
336 unsafe {
337 let mut addr: sockaddr_un = zeroed();
338 addr.sun_family = AF_UNIX as _;
339 path.write_self_to_sockaddr_un(&mut addr)?;
340 Ok(addr)
341 }
342 }
343}
344
345/// Trait for types which can be converted to a [path to a Unix domain socket][`UdSocketPath`].
346///
347/// The difference between this trait and [`TryInto`]`<`[`UdSocketPath`]`>` is that the latter does not constrain the error type to be [`io::Error`] and thus is not compatible with many types from the standard library which are widely expected to be convertible to Unix domain socket paths. Additionally, this makes the special syntax for namespaced sockets possible (see below).
348///
349/// ## `@` syntax for namespaced paths
350/// On Linux (since it's the only platform which supports [namespaced socket paths]), an extra syntax feature is implemented for string types which don't have file path semantics, i.e. all standard string types except for [`Path`] and [`PathBuf`]. If the first character in a string is `@`, the path is interpreted as a namespaced socket path rather than a normal file path. Read the `UdSocketPath` documentation for more on what that means. There are several ways to opt out of that behavior if you're referring to a socket at a relative path which starts from a `@`:
351/// - Use [`AsRef`] to convert the string slice type into a [`Path`] which has file path semantics and therefore does not have the `@` syntax enabled, if your string type is [`str`] or [`OsStr`]
352/// - Prefix the path with `./`, which carries the same meaning from the perspective of the OS but bypasses the `@` check
353/// - If your string type is [`CStr`] or [`CString`], explicitly construct `UdSocketPath`'s `File` variant with a [`Cow`] wrapping your string value
354///
355/// # Example
356/// The following example uses the `UdStreamListener::bind` method, but `UdStream::connect` and `UdSocket::bind`/`UdSocket::connect` accept the same argument types too.
357/// ```no_run
358/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
359/// # #[cfg(unix)] {
360/// use interprocess::os::unix::udsocket::{UdStreamListener, UdSocketPath};
361/// use std::{ffi::{CStr, CString}, path::{Path, PathBuf}, borrow::Cow};
362///
363/// // 1. Use a string literal
364/// let listener = UdStreamListener::bind("/tmp/example1.sock")?;
365/// // If we're on Linux, we can also use the abstract socket namespace which exists separately from
366/// // the filesystem thanks to the special @ sign syntax which works with all string types
367/// let listener_namespaced = UdStreamListener::bind("@namespaced_socket_1")?;
368///
369/// // 2. Use an owned string
370/// let listener = UdStreamListener::bind("/tmp/example2.sock".to_string())?;
371/// // Same story with the namespaced socket here
372/// let listener_namespaced = UdStreamListener::bind("@namespaced_socket_2")?;
373///
374/// // 3. Use a path slice or an owned path
375/// let listener_by_path = UdStreamListener::bind(Path::new("/tmp/exmaple3a.sock"))?;
376/// let listener_by_pathbuf = UdStreamListener::bind(PathBuf::from("/tmp/example3b.sock"))?;
377/// // The @ syntax doesn't work with Path and PathBuf, since those are explicitly paths at the type
378/// // level, rather than strings with contextual meaning. Using AsRef to convert an &str slice or
379/// // an &OsStr slice into a &Path slice is the recommended way to disable the @ syntax.
380///
381/// // 4. Use manual creation
382/// let cstring = CString::new("/tmp/example4a.sock".to_string().into_bytes())?;
383/// let path_to_socket = UdSocketPath::File(Cow::Owned(cstring));
384/// let listener = UdStreamListener::bind(path_to_socket);
385///
386/// let cstr = CStr::from_bytes_with_nul("/tmp/example4b.sock\0".as_bytes())?;
387/// let path_to_socket = UdSocketPath::File(Cow::Borrowed(cstr));
388/// let listener = UdStreamListener::bind(path_to_socket);
389/// # }
390/// # Ok(()) }
391/// ```
392///
393/// [`UdSocketPath`]: enum.UdSocketPath.html " "
394/// [`io::Error`]: https://doc.rust-lang.org/std/io/struct.Error.html " "
395/// [`TryInto`]: https://doc.rust-lang.org/std/convert/trait.TryInto.html " "
396/// [`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html " "
397/// [namespaced socket paths]: struct.UdSocketPath.html#namespaced " "
398/// [`Path`]: https://doc.rust-lang.org/std/path/struct.Path.html " "
399/// [`PathBuf`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html " "
400/// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html " "
401/// [`CStr`]: https://doc.rust-lang.org/std/ffi/struct.CStr.html " "
402/// [`CString`]: https://doc.rust-lang.org/std/ffi/struct.CString.html " "
403/// [`Cow`]: https://doc.rust-lang.org/std/borrow/enum.Cow.html " "
404/// [`str`]: https://doc.rust-lang.org/stable/std/primitive.str.html
405pub trait ToUdSocketPath<'a> {
406 /// Performs the conversion from `self` to a Unix domain socket path.
407 #[allow(clippy::wrong_self_convention)]
408 fn to_socket_path(self) -> io::Result<UdSocketPath<'a>>;
409}
410impl<'a> ToUdSocketPath<'a> for UdSocketPath<'a> {
411 /// Accepts explicit `UdSocketPath`s in relevant constructors.
412 fn to_socket_path(self) -> io::Result<UdSocketPath<'a>> {
413 Ok(self)
414 }
415}
416impl<'a> ToUdSocketPath<'a> for &'a UdSocketPath<'a> {
417 /// Reborrows an explicit `UdSocketPath` for a smaller lifetime.
418 fn to_socket_path(self) -> io::Result<UdSocketPath<'a>> {
419 Ok(self.borrow())
420 }
421}
422impl<'a> ToUdSocketPath<'a> for &'a CStr {
423 /// Converts a borrowed [`CStr`] to a borrowed `UdSocketPath` with the same lifetime. On platforms which don't support [namespaced socket paths], the variant is always [`File`]; on Linux, which supports namespaced sockets, an extra check for the `@` character is performed. See the trait-level documentation for more.
424 ///
425 /// [`CStr`]: https://doc.rust-lang.org/std/ffi/struct.CStr.html " "
426 /// [`File`]: enum.UdSocketPath.html#file " "
427 /// [namespaced socket paths]: enum.UdSocketPath.html#namespaced " "
428 fn to_socket_path(self) -> io::Result<UdSocketPath<'a>> {
429 // 0x40 is the ASCII code for @, and since UTF-8 is ASCII-compatible, it would work too
430 #[cfg(uds_linux_namespace)]
431 if self.to_bytes().first() == Some(&0x40) {
432 let without_at_sign = &self.to_bytes_with_nul()[1..];
433 let without_at_sign = unsafe {
434 // SAFETY: it's safe to assume that the second byte comes before the nul
435 // terminator or is that nul terminator itself if the first one is an @ sign
436 CStr::from_bytes_with_nul_unchecked(without_at_sign)
437 };
438 // Use early return to simplify the conditional inclusion for the @ syntax check.
439 return Ok(UdSocketPath::Namespaced(Cow::Borrowed(without_at_sign)));
440 }
441 Ok(UdSocketPath::File(Cow::Borrowed(self)))
442 }
443}
444impl ToUdSocketPath<'static> for CString {
445 /// Converts an owned [`CString`] to a borrowed `UdSocketPath` with the same lifetime. On platforms which don't support [namespaced socket paths], the variant is always [`File`]; on Linux, which supports namespaced sockets, an extra check for the `@` character is performed. See the trait-level documentation for more.
446 ///
447 /// [`CString`]: https://doc.rust-lang.org/std/ffi/struct.CString.html " "
448 /// [`File`]: enum.UdSocketPath.html#file " "
449 /// [namespaced socket paths]: enum.UdSocketPath.html#namespaced " "
450 fn to_socket_path(self) -> io::Result<UdSocketPath<'static>> {
451 #[cfg(uds_linux_namespace)]
452 if self.as_bytes().first() == Some(&0x40) {
453 let without_at_sign = {
454 let mut without_at_sign = self.into_bytes();
455 without_at_sign.remove(0);
456 unsafe {
457 // SAFETY: see CStr impl for why this is safe in both impls
458 CString::from_vec_unchecked(without_at_sign)
459 }
460 };
461 // As in the CStr impl, we're using an early return to simplify conditional compilation
462 return Ok(UdSocketPath::Namespaced(Cow::Owned(without_at_sign)));
463 }
464 Ok(UdSocketPath::File(Cow::Owned(self)))
465 }
466}
467impl<'a> ToUdSocketPath<'a> for &'a OsStr {
468 /// Converts a borrowed [`OsStr`] to a borrowed `UdSocketPath` with the same lifetime. On platforms which don't support [namespaced socket paths], the variant is always [`File`]; on Linux, which supports namespaced sockets, an extra check for the `@` character is performed. See the trait-level documentation for more.
469 ///
470 /// If the provided string is not nul-terminated, a nul terminator is automatically added by copying the string into owned storage and adding a nul byte on its end.
471 ///
472 /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html " "
473 /// [`File`]: enum.UdSocketPath.html#file " "
474 /// [namespaced socket paths]: enum.UdSocketPath.html#namespaced " "
475 fn to_socket_path(self) -> io::Result<UdSocketPath<'a>> {
476 #[cfg(uds_linux_namespace)]
477 if self.as_bytes().first() == Some(&0x40) {
478 if self.as_bytes().last() != Some(&0) {
479 let mut owned = self.to_owned().into_vec();
480 owned.remove(0);
481 return Ok(UdSocketPath::Namespaced(Cow::Owned(CString::new(owned)?)));
482 } else {
483 let without_at_sign = self.as_bytes().split_at(1).0;
484 let cstr = CStr::from_bytes_with_nul(without_at_sign)
485 .map_err(|x| io::Error::new(io::ErrorKind::InvalidInput, x))?;
486 return Ok(UdSocketPath::Namespaced(Cow::Borrowed(cstr)));
487 }
488 }
489 if self.as_bytes().last() != Some(&0) {
490 Ok(UdSocketPath::File(Cow::Owned(CString::new(
491 self.to_owned().into_vec(),
492 )?)))
493 } else {
494 let cstr = CStr::from_bytes_with_nul(self.as_bytes())
495 .map_err(|x| io::Error::new(io::ErrorKind::InvalidInput, x))?;
496 Ok(UdSocketPath::File(Cow::Borrowed(cstr)))
497 }
498 }
499}
500impl ToUdSocketPath<'static> for OsString {
501 /// Converts a borrowed [`OsString`] to an owned `UdSocketPath`. On platforms which don't support [namespaced socket paths], the variant is always [`File`]; on Linux, which supports namespaced sockets, an extra check for the `@` character is performed. See the trait-level documentation for more.
502 ///
503 /// If the provided string is not nul-terminated, a nul terminator is automatically added by copying the string into owned storage and adding a nul byte on its end.
504 ///
505 /// [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html " "
506 /// [`File`]: enum.UdSocketPath.html#file " "
507 /// [namespaced socket paths]: enum.UdSocketPath.html#namespaced " "
508 fn to_socket_path(self) -> io::Result<UdSocketPath<'static>> {
509 #[cfg(uds_linux_namespace)]
510 if self.as_os_str().as_bytes().first() == Some(&0x40) {
511 let mut without_at_sign = self.into_vec();
512 without_at_sign.remove(0);
513 return Ok(UdSocketPath::Namespaced(Cow::Owned(CString::new(
514 without_at_sign,
515 )?)));
516 }
517 Ok(UdSocketPath::File(Cow::Owned(CString::new(
518 self.into_vec(),
519 )?)))
520 }
521}
522impl<'a> ToUdSocketPath<'a> for &'a Path {
523 /// Converts a borrowed [`Path`] to a borrowed [`UdSocketPath::File`] with the same lifetime.
524 ///
525 /// If the provided string is not nul-terminated, a nul terminator is automatically added by copying the string into owned storage and adding a nul byte on its end.
526 ///
527 /// [`Path`]: https://doc.rust-lang.org/std/path/struct.Path.html " "
528 /// [`UdSocketPath::File`]: struct.UdSocketPath.html#file " "
529 fn to_socket_path(self) -> io::Result<UdSocketPath<'a>> {
530 if self.as_os_str().as_bytes().last() != Some(&0) {
531 let osstring = self.to_owned().into_os_string().into_vec();
532 let cstring = CString::new(osstring)?;
533 Ok(UdSocketPath::File(Cow::Owned(cstring)))
534 } else {
535 let cstr = CStr::from_bytes_with_nul(self.as_os_str().as_bytes())
536 .map_err(|x| io::Error::new(io::ErrorKind::InvalidInput, x))?;
537 Ok(UdSocketPath::File(Cow::Borrowed(cstr)))
538 }
539 }
540}
541impl ToUdSocketPath<'static> for PathBuf {
542 /// Converts an owned [`PathBuf`] to an owned [`UdSocketPath::File`].
543 ///
544 /// If the provided string is not nul-terminated, a nul terminator is automatically added by copying the string into owned storage and adding a nul byte on its end.
545 ///
546 /// [`PathBuf`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html " "
547 /// [`UdSocketPath::File`]: struct.UdSocketPath.html#file " "
548 fn to_socket_path(self) -> io::Result<UdSocketPath<'static>> {
549 let cstring = CString::new(self.into_os_string().into_vec())?;
550 Ok(UdSocketPath::File(Cow::Owned(cstring)))
551 }
552}
553impl<'a> ToUdSocketPath<'a> for &'a str {
554 /// Converts a borrowed [`str`] to a borrowed `UdSocketPath` with the same lifetime. On platforms which don't support [namespaced socket paths], the variant is always [`File`]; on Linux, which supports namespaced sockets, an extra check for the `@` character is performed. See the trait-level documentation for more.
555 ///
556 /// If the provided string is not nul-terminated, a nul terminator is automatically added by copying the string into owned storage and adding a nul byte on its end. This is done to support normal string literals, since adding `\0` at the end of every single socket name string is tedious and unappealing.
557 ///
558 /// [`str`]: https://doc.rust-lang.org/std/primitive.str.html " "
559 /// [`File`]: enum.UdSocketPath.html#file " "
560 /// [namespaced socket paths]: enum.UdSocketPath.html#namespaced " "
561 fn to_socket_path(self) -> io::Result<UdSocketPath<'a>> {
562 // Use chars().next() instead of raw indexing to account for UTF-8 with BOM
563 #[cfg(uds_linux_namespace)]
564 if self.starts_with('@') {
565 if !self.ends_with('\0') {
566 let mut owned = self.to_owned();
567 owned.remove(0);
568 return Ok(UdSocketPath::Namespaced(Cow::Owned(CString::new(owned)?)));
569 } else {
570 let without_at_sign = self.split_at(1).0;
571 let cstr = CStr::from_bytes_with_nul(without_at_sign.as_bytes())
572 .map_err(|x| io::Error::new(io::ErrorKind::InvalidInput, x))?;
573 return Ok(UdSocketPath::Namespaced(Cow::Borrowed(cstr)));
574 }
575 }
576 if !self.ends_with('\0') {
577 Ok(UdSocketPath::File(Cow::Owned(CString::new(
578 self.to_owned(),
579 )?)))
580 } else {
581 let cstr = CStr::from_bytes_with_nul(self.as_bytes())
582 .map_err(|x| io::Error::new(io::ErrorKind::InvalidInput, x))?;
583 Ok(UdSocketPath::File(Cow::Borrowed(cstr)))
584 }
585 }
586}
587impl ToUdSocketPath<'static> for String {
588 /// Converts an owned [`String`] to an owned `UdSocketPath`. On platforms which don't support [namespaced socket paths], the variant is always [`File`]; on Linux, which supports namespaced sockets, an extra check for the `@` character is performed. See the trait-level documentation for more.
589 ///
590 /// If the provided string is not nul-terminated, a nul terminator is automatically added by copying the string into owned storage and adding a nul byte on its end.
591 ///
592 /// [`String`]: https://doc.rust-lang.org/std/string/struct.String.html " "
593 /// [`File`]: enum.UdSocketPath.html#file " "
594 /// [namespaced socket paths]: enum.UdSocketPath.html#namespaced " "
595 fn to_socket_path(self) -> io::Result<UdSocketPath<'static>> {
596 #[cfg(uds_linux_namespace)]
597 if self.starts_with('@') {
598 let mut without_at_sign = self;
599 without_at_sign.remove(0);
600 return Ok(UdSocketPath::Namespaced(Cow::Owned(CString::new(
601 without_at_sign.into_bytes(),
602 )?)));
603 }
604 Ok(UdSocketPath::File(Cow::Owned(CString::new(
605 self.into_bytes(),
606 )?)))
607 }
608}