1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
//! A Trait and two Impls dealing with auto-closing `RawFd` file handles with a //! sensible `Clone` trait implementations. //! //! - [`DuplicatingFD`](struct.DuplicatingFD.html) holds a raw handle directly and duplicates itself by calls to //! [dup(2)](https://man7.org/linux/man-pages/man2/dup.2.html) - //! that is each instance has its own handle that is individually closed on end-of-life of that //! instance. May panic on `clone()` call due to underlying OS error. //! //! - [`SharedFD`](struct.SharedFD.html) holds a raw handle in `std::sync::Arc` and duplicates itself by //! duplicating said `Arc` - that is each instance shares a single handle, which is closed after //! the last instance reaches end-of-life. //! //! # Common functionality //! //! Both implementations... //! - implement `AsRawFd` and `Clone` traits. //! - have a [`wrap(fd)`](trait.ManagedFD.html#tymethod.wrap) constructor that simply packs `fd` in //! managed shell and takes ownership (you shouldn't use `fd` afterwards and *definitely* not //! `close()` it). //! - have a [`dup_wrap(fd)`](trait.ManagedFD.html#tymethod.dup_wrap) constructor that packs //! [dup(2)](https://man7.org/linux/man-pages/man2/dup.2.html) //! copy of `fd` in managed shell. It doesn't take the ownership of the original `fd`, which you //! should dispose-of properly. //! - have a [`dup()`](trait.ManagedFD.html#tymethod.dup) method that clones handle accordingly, //! returning eventual errors. //! //! # Multi-access //! Both are **not** multi-access safe, with `SharedFD` being even less safe. //! //! - Each of the related `DuplicatingFD` instances has _its own_ read/write pointer (still stepping on //! each other's toes during writes) //! - All the related `SharedFD` instances have a _single, shared_ read/write pointer. //! use std::io; use std::os::unix::io::AsRawFd; use std::os::unix::io::RawFd; use std::sync::Arc; /// Trait `ManagedFD` describes a managed `std::os::unix::io::RawFd`, with primary functionality of /// auto-closing on drop and performing sensible `clone()`/`dup()` operations. /// /// Warning: `Clone` trait has no way to convey errors, so implementations are forced to `panic!()`. pub trait ManagedFD where Self: AsRawFd + Clone, { /// Wrap `fd` in `ManagedFD`. You should not use naked handle afterwards, in particular *don't /// close it*. fn wrap(fd: RawFd) -> Self; /// Wrap a [dup(2)](https://man7.org/linux/man-pages/man2/dup.2.html) copy of `fd` in `ManagedFD`. /// You should dispose the original `fd` properly at your discretion. fn dup_wrap(fd: RawFd) -> io::Result<Self>; /// Create a duplicate of handle in such a way, that dropping of one instance has no influence /// on the other ones. /// /// It lets you define (required) Clone trait as simply /// ```ignore /// impl Clone for MyImpl { /// fn clone(&self) -> Self { /// self.dup().unwrap() /// } /// } /// ``` fn dup(&self) -> io::Result<Self>; } /// Intermediate auto-closing handle. Does not implement `clone()`, but can create itself off a /// `dup(2)` clone. struct AutoClosingFD(RawFd); impl AutoClosingFD{ #[inline] fn wrap(fd: RawFd) -> Self { AutoClosingFD(fd) } #[inline] fn dup_wrap(fd: RawFd) -> io::Result<Self> { let new_handle = unsafe { libc::dup(fd) }; if new_handle == -1 { Err(io::Error::last_os_error()) } else { Ok(Self::wrap(new_handle)) } } } impl Drop for AutoClosingFD{ fn drop(&mut self) { if self.0 >= 0 { unsafe { libc::close(self.0) }; self.0 = -1; } } } impl AsRawFd for AutoClosingFD { fn as_raw_fd(&self) -> RawFd { self.0 } } /// Implements `Clone` trait that calls [dup(2)](https://man7.org/linux/man-pages/man2/dup.2.html) on /// underlying handle and returns new instance wrapping dup-ped handle. /// /// **Warning**: `clone()` will panic if `dup(2)` returns error. /// /// Each `clone()`/`dup()` of `DuplicatingFD` contains a different handle. /// /// # Example /// ``` /// use pakr_managedrawfd::*; /// use std::os::unix::io::AsRawFd; /// /// let stdout_handle = std::io::stdout().lock().as_raw_fd(); /// /// // We don't want myH to close the real stdout, therefore dup_wrap; /// let myDupH = DuplicatingFD::dup_wrap(stdout_handle).unwrap(); /// /// // ... myDupH shall have handle other than stdout. /// assert_ne!(myDupH.as_raw_fd(),stdout_handle); /// /// // Clone it /// let myOtherDupH = myDupH.clone(); /// /// // ... myOtherDupH shall have yet another handle, other than both myDupH and stdout. /// assert_ne!(myOtherDupH.as_raw_fd(),stdout_handle); /// assert_ne!(myOtherDupH.as_raw_fd(),myDupH.as_raw_fd()); /// /// ``` pub struct DuplicatingFD(AutoClosingFD); impl ManagedFD for DuplicatingFD { fn wrap(fd: RawFd) -> Self { DuplicatingFD(AutoClosingFD::wrap(fd)) } fn dup_wrap(fd: RawFd) -> io::Result<Self> { Ok(DuplicatingFD(AutoClosingFD::dup_wrap(fd)?)) } fn dup(&self) -> io::Result<Self> { let new_handle = unsafe { libc::dup(self.as_raw_fd()) }; if new_handle == -1 { Err(io::Error::last_os_error()) } else { Ok(Self::wrap(new_handle)) } } } impl Clone for DuplicatingFD { fn clone(&self) -> Self { self.dup().unwrap() } fn clone_from(&mut self, source: &Self) { assert!(source.as_raw_fd()>=0); assert!(self.as_raw_fd()>=0); if source.as_raw_fd() != self.as_raw_fd() { unsafe { libc::close(self.as_raw_fd()) }; let rc = unsafe { libc::dup2(source.as_raw_fd(), self.as_raw_fd()) }; if rc == -1 { panic!(io::Error::last_os_error()); } } } } impl AsRawFd for DuplicatingFD { #[inline] fn as_raw_fd(&self) -> RawFd { self.0.as_raw_fd() } } /// Implements `Clone` trait that creates new `SharedFD` with `Arc::clone` of the /// embedded handle. /// /// Each `clone()`/`dup()` of `SharedFD` contains the same handle. /// /// # Example /// ``` /// use pakr_managedrawfd::*; /// use std::os::unix::io::AsRawFd; /// /// let stdout_handle = std::io::stdout().lock().as_raw_fd(); /// /// // We don't want myShH to close the real stdout, therefore dup_wrap; /// let myShH = SharedFD::dup_wrap(stdout_handle).unwrap(); /// /// // ... myShH shall have handle other than stdout. /// assert_ne!(myShH.as_raw_fd(),stdout_handle); /// /// // Clone it /// let myOtherShH = myShH.clone(); /// /// // ... myOtherShH shall have the same handle as myShH. /// assert_eq!(myOtherShH.as_raw_fd(),myShH.as_raw_fd()); /// /// ``` pub struct SharedFD(Arc<AutoClosingFD>); impl ManagedFD for SharedFD { fn wrap(fd: RawFd) -> Self { SharedFD(Arc::new(AutoClosingFD::wrap(fd))) } fn dup_wrap(fd: RawFd) -> io::Result<Self> { Ok(SharedFD(Arc::new(AutoClosingFD::dup_wrap(fd)?))) } fn dup(&self) -> io::Result<Self> { Ok(SharedFD(self.0.clone())) } } impl AsRawFd for SharedFD { fn as_raw_fd(&self) -> RawFd { self.0.as_raw_fd() } } impl Clone for SharedFD { fn clone(&self) -> Self { self.dup().unwrap() } }