| 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 | |