pakr_managedrawfd/
lib.rs

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