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}