diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-31 16:00:03 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-31 16:00:03 +0300 |
| commit | b778162a9f57b64ce9a067386b7de27d4cbcbafc (patch) | |
| tree | 6bbce8729acba59aebbdaaa78eb2acb37a144b81 /src/pty/fd.rs | |
| parent | 77819797385c5538f624a8881208921342911db0 (diff) | |
refactor: centralize pty fd ownership
refs #357
refs #327
Diffstat (limited to 'src/pty/fd.rs')
| -rw-r--r-- | src/pty/fd.rs | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/src/pty/fd.rs b/src/pty/fd.rs new file mode 100644 index 00000000..a8bf021e --- /dev/null +++ b/src/pty/fd.rs @@ -0,0 +1,109 @@ +#[cfg(unix)] +use std::os::fd::RawFd; + +#[cfg(unix)] +pub(crate) fn duplicate_fd(fd: RawFd) -> std::io::Result<RawFd> { + let duplicated = unsafe { libc::dup(fd) }; + if duplicated < 0 { + return Err(std::io::Error::last_os_error()); + } + Ok(duplicated) +} + +#[cfg(unix)] +pub(crate) fn set_cloexec(fd: RawFd) -> std::io::Result<()> { + let flags = unsafe { libc::fcntl(fd, libc::F_GETFD) }; + if flags < 0 { + return Err(std::io::Error::last_os_error()); + } + if unsafe { libc::fcntl(fd, libc::F_SETFD, flags | libc::FD_CLOEXEC) } < 0 { + return Err(std::io::Error::last_os_error()); + } + Ok(()) +} + +#[cfg(unix)] +pub(crate) fn set_nonblocking(fd: RawFd) -> std::io::Result<()> { + let flags = unsafe { libc::fcntl(fd, libc::F_GETFL) }; + if flags < 0 { + return Err(std::io::Error::last_os_error()); + } + if unsafe { libc::fcntl(fd, libc::F_SETFL, flags | libc::O_NONBLOCK) } < 0 { + return Err(std::io::Error::last_os_error()); + } + Ok(()) +} + +#[cfg(unix)] +pub(crate) fn duplicate_cloexec_fd(fd: RawFd) -> std::io::Result<RawFd> { + let duplicated = duplicate_fd(fd)?; + if let Err(err) = set_cloexec(duplicated) { + let _ = unsafe { libc::close(duplicated) }; + return Err(err); + } + Ok(duplicated) +} + +#[cfg(unix)] +pub(crate) fn poll_read_ready(fd: RawFd, timeout_ms: i32) -> std::io::Result<bool> { + let mut poll_fd = libc::pollfd { + fd, + events: libc::POLLIN, + revents: 0, + }; + loop { + let result = unsafe { libc::poll(&mut poll_fd, 1, timeout_ms) }; + if result < 0 { + let err = std::io::Error::last_os_error(); + if err.kind() == std::io::ErrorKind::Interrupted { + continue; + } + return Err(err); + } + return Ok(result > 0 && (poll_fd.revents & (libc::POLLIN | libc::POLLHUP)) != 0); + } +} + +#[cfg(unix)] +pub(crate) fn poll_write_ready(fd: RawFd, timeout_ms: i32) -> std::io::Result<bool> { + let mut poll_fd = libc::pollfd { + fd, + events: libc::POLLOUT, + revents: 0, + }; + loop { + let result = unsafe { libc::poll(&mut poll_fd, 1, timeout_ms) }; + if result < 0 { + let err = std::io::Error::last_os_error(); + if err.kind() == std::io::ErrorKind::Interrupted { + continue; + } + return Err(err); + } + return Ok(result > 0 && (poll_fd.revents & (libc::POLLOUT | libc::POLLHUP)) != 0); + } +} + +#[cfg(unix)] +pub(crate) fn resize_pty_fd( + fd: RawFd, + rows: u16, + cols: u16, + cell_width_px: u32, + cell_height_px: u32, +) -> std::io::Result<()> { + let size = libc::winsize { + ws_row: rows, + ws_col: cols, + ws_xpixel: (cols as u32) + .saturating_mul(cell_width_px) + .min(u16::MAX as u32) as u16, + ws_ypixel: (rows as u32) + .saturating_mul(cell_height_px) + .min(u16::MAX as u32) as u16, + }; + if unsafe { libc::ioctl(fd, libc::TIOCSWINSZ, &size) } < 0 { + return Err(std::io::Error::last_os_error()); + } + Ok(()) +} |
