1use std::{
2 fs, io,
3 os::unix::{
4 io::{IntoRawFd, RawFd},
5 prelude::AsRawFd,
6 },
7};
8
9use 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)]
16pub struct FileDesc {
17 fd: RawFd,
18 close_on_drop: bool,
19}
20
21impl 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
54impl 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
67impl 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`.
74pub 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