1 | use std::io;
|
2 |
|
3 | #[cfg (feature = "libc" )]
|
4 | use libc::size_t;
|
5 | #[cfg (not(feature = "libc" ))]
|
6 | use rustix::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd};
|
7 | #[cfg (feature = "libc" )]
|
8 | use std::{
|
9 | fs,
|
10 | marker::PhantomData,
|
11 | os::unix::{
|
12 | io::{IntoRawFd, RawFd},
|
13 | prelude::AsRawFd,
|
14 | },
|
15 | };
|
16 |
|
17 | /// A file descriptor wrapper.
|
18 | ///
|
19 | /// It allows to retrieve raw file descriptor, write to the file descriptor and
|
20 | /// mainly it closes the file descriptor once dropped.
|
21 | #[derive (Debug)]
|
22 | #[cfg (feature = "libc" )]
|
23 | pub struct FileDesc<'a> {
|
24 | fd: RawFd,
|
25 | close_on_drop: bool,
|
26 | phantom: PhantomData<&'a ()>,
|
27 | }
|
28 |
|
29 | #[cfg (not(feature = "libc" ))]
|
30 | pub enum FileDesc<'a> {
|
31 | Owned(OwnedFd),
|
32 | Borrowed(BorrowedFd<'a>),
|
33 | }
|
34 |
|
35 | #[cfg (feature = "libc" )]
|
36 | impl FileDesc<'_> {
|
37 | /// Constructs a new `FileDesc` with the given `RawFd`.
|
38 | ///
|
39 | /// # Arguments
|
40 | ///
|
41 | /// * `fd` - raw file descriptor
|
42 | /// * `close_on_drop` - specify if the raw file descriptor should be closed once the `FileDesc` is dropped
|
43 | pub fn new(fd: RawFd, close_on_drop: bool) -> FileDesc<'static> {
|
44 | FileDesc {
|
45 | fd,
|
46 | close_on_drop,
|
47 | phantom: PhantomData,
|
48 | }
|
49 | }
|
50 |
|
51 | pub fn read(&self, buffer: &mut [u8]) -> io::Result<usize> {
|
52 | let result = unsafe {
|
53 | libc::read(
|
54 | self.fd,
|
55 | buffer.as_mut_ptr() as *mut libc::c_void,
|
56 | buffer.len() as size_t,
|
57 | )
|
58 | };
|
59 |
|
60 | if result < 0 {
|
61 | Err(io::Error::last_os_error())
|
62 | } else {
|
63 | Ok(result as usize)
|
64 | }
|
65 | }
|
66 |
|
67 | /// Returns the underlying file descriptor.
|
68 | pub fn raw_fd(&self) -> RawFd {
|
69 | self.fd
|
70 | }
|
71 | }
|
72 |
|
73 | #[cfg (not(feature = "libc" ))]
|
74 | impl FileDesc<'_> {
|
75 | pub fn read(&self, buffer: &mut [u8]) -> io::Result<usize> {
|
76 | let fd: BorrowedFd<'_> = match self {
|
77 | FileDesc::Owned(fd: &OwnedFd) => fd.as_fd(),
|
78 | FileDesc::Borrowed(fd: &BorrowedFd<'_>) => fd.as_fd(),
|
79 | };
|
80 | let result: usize = rustix::io::read(fd, buf:buffer)?;
|
81 | Ok(result)
|
82 | }
|
83 |
|
84 | pub fn raw_fd(&self) -> RawFd {
|
85 | match self {
|
86 | FileDesc::Owned(fd: &OwnedFd) => fd.as_raw_fd(),
|
87 | FileDesc::Borrowed(fd: &BorrowedFd<'_>) => fd.as_raw_fd(),
|
88 | }
|
89 | }
|
90 | }
|
91 |
|
92 | #[cfg (feature = "libc" )]
|
93 | impl Drop for FileDesc<'_> {
|
94 | fn drop(&mut self) {
|
95 | if self.close_on_drop {
|
96 | // Note that errors are ignored when closing a file descriptor. The
|
97 | // reason for this is that if an error occurs we don't actually know if
|
98 | // the file descriptor was closed or not, and if we retried (for
|
99 | // something like EINTR), we might close another valid file descriptor
|
100 | // opened after we closed ours.
|
101 | let _ = unsafe { libc::close(self.fd) };
|
102 | }
|
103 | }
|
104 | }
|
105 |
|
106 | impl AsRawFd for FileDesc<'_> {
|
107 | fn as_raw_fd(&self) -> RawFd {
|
108 | self.raw_fd()
|
109 | }
|
110 | }
|
111 |
|
112 | #[cfg (not(feature = "libc" ))]
|
113 | impl AsFd for FileDesc<'_> {
|
114 | fn as_fd(&self) -> BorrowedFd<'_> {
|
115 | match self {
|
116 | FileDesc::Owned(fd: &OwnedFd) => fd.as_fd(),
|
117 | FileDesc::Borrowed(fd: &BorrowedFd<'_>) => fd.as_fd(),
|
118 | }
|
119 | }
|
120 | }
|
121 |
|
122 | #[cfg (feature = "libc" )]
|
123 | /// Creates a file descriptor pointing to the standard input or `/dev/tty`.
|
124 | pub fn tty_fd() -> io::Result<FileDesc<'static>> {
|
125 | let (fd, close_on_drop) = if unsafe { libc::isatty(libc::STDIN_FILENO) == 1 } {
|
126 | (libc::STDIN_FILENO, false)
|
127 | } else {
|
128 | (
|
129 | fs::OpenOptions::new()
|
130 | .read(true)
|
131 | .write(true)
|
132 | .open("/dev/tty" )?
|
133 | .into_raw_fd(),
|
134 | true,
|
135 | )
|
136 | };
|
137 |
|
138 | Ok(FileDesc::new(fd, close_on_drop))
|
139 | }
|
140 |
|
141 | #[cfg (not(feature = "libc" ))]
|
142 | /// Creates a file descriptor pointing to the standard input or `/dev/tty`.
|
143 | pub fn tty_fd() -> io::Result<FileDesc<'static>> {
|
144 | use std::fs::File;
|
145 |
|
146 | let stdin: BorrowedFd<'static> = rustix::stdio::stdin();
|
147 | let fd: FileDesc<'_> = if rustix::termios::isatty(fd:stdin) {
|
148 | FileDesc::Borrowed(stdin)
|
149 | } else {
|
150 | let dev_tty: File = File::options().read(true).write(true).open(path:"/dev/tty" )?;
|
151 | FileDesc::Owned(dev_tty.into())
|
152 | };
|
153 | Ok(fd)
|
154 | }
|
155 | |