1 | use std::{
|
2 | fs, io,
|
3 | os::unix::{
|
4 | io::{IntoRawFd, RawFd},
|
5 | prelude::AsRawFd,
|
6 | },
|
7 | };
|
8 |
|
9 | use libc::size_t;
|
10 |
|
11 | /// A file descriptor wrapper.
|
12 | ///
|
13 | /// It allows to retrieve raw file descriptor, write to the file descriptor and
|
14 | /// mainly it closes the file descriptor once dropped.
|
15 | #[derive (Debug)]
|
16 | pub struct FileDesc {
|
17 | fd: RawFd,
|
18 | close_on_drop: bool,
|
19 | }
|
20 |
|
21 | impl FileDesc {
|
22 | /// Constructs a new `FileDesc` with the given `RawFd`.
|
23 | ///
|
24 | /// # Arguments
|
25 | ///
|
26 | /// * `fd` - raw file descriptor
|
27 | /// * `close_on_drop` - specify if the raw file descriptor should be closed once the `FileDesc` is dropped
|
28 | pub fn new(fd: RawFd, close_on_drop: bool) -> FileDesc {
|
29 | FileDesc { fd, close_on_drop }
|
30 | }
|
31 |
|
32 | pub fn read(&self, buffer: &mut [u8], size: usize) -> io::Result<usize> {
|
33 | let result = unsafe {
|
34 | libc::read(
|
35 | self.fd,
|
36 | buffer.as_mut_ptr() as *mut libc::c_void,
|
37 | size as size_t,
|
38 | )
|
39 | };
|
40 |
|
41 | if result < 0 {
|
42 | Err(io::Error::last_os_error())
|
43 | } else {
|
44 | Ok(result as usize)
|
45 | }
|
46 | }
|
47 |
|
48 | /// Returns the underlying file descriptor.
|
49 | pub fn raw_fd(&self) -> RawFd {
|
50 | self.fd
|
51 | }
|
52 | }
|
53 |
|
54 | impl Drop for FileDesc {
|
55 | fn drop(&mut self) {
|
56 | if self.close_on_drop {
|
57 | // Note that errors are ignored when closing a file descriptor. The
|
58 | // reason for this is that if an error occurs we don't actually know if
|
59 | // the file descriptor was closed or not, and if we retried (for
|
60 | // something like EINTR), we might close another valid file descriptor
|
61 | // opened after we closed ours.
|
62 | let _ = unsafe { libc::close(self.fd) };
|
63 | }
|
64 | }
|
65 | }
|
66 |
|
67 | impl AsRawFd for FileDesc {
|
68 | fn as_raw_fd(&self) -> RawFd {
|
69 | self.raw_fd()
|
70 | }
|
71 | }
|
72 |
|
73 | /// Creates a file descriptor pointing to the standard input or `/dev/tty`.
|
74 | pub fn tty_fd() -> io::Result<FileDesc> {
|
75 | let (fd: i32, close_on_drop: bool) = if unsafe { libc::isatty(fd:libc::STDIN_FILENO) == 1 } {
|
76 | (libc::STDIN_FILENO, false)
|
77 | } else {
|
78 | (
|
79 | fsFile::OpenOptions::new()
|
80 | .read(true)
|
81 | .write(true)
|
82 | .open(path:"/dev/tty" )?
|
83 | .into_raw_fd(),
|
84 | true,
|
85 | )
|
86 | };
|
87 |
|
88 | Ok(FileDesc::new(fd, close_on_drop))
|
89 | }
|
90 | |