aboutsummaryrefslogtreecommitdiff
path: root/src/pty/fd.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-31 16:00:03 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-31 16:00:03 +0300
commitb778162a9f57b64ce9a067386b7de27d4cbcbafc (patch)
tree6bbce8729acba59aebbdaaa78eb2acb37a144b81 /src/pty/fd.rs
parent77819797385c5538f624a8881208921342911db0 (diff)
refactor: centralize pty fd ownership
refs #357 refs #327
Diffstat (limited to 'src/pty/fd.rs')
-rw-r--r--src/pty/fd.rs109
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(())
+}