]> git.proxmox.com Git - pve-xtermjs.git/commitdiff
termproxy: avoid expensive proxmox-sys crate, copy over PTY module
authorThomas Lamprecht <t.lamprecht@proxmox.com>
Mon, 23 Oct 2023 08:27:19 +0000 (10:27 +0200)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Mon, 23 Oct 2023 08:42:45 +0000 (10:42 +0200)
This is only used here anyway, and proxmox-sys is one of those monster
crates still awaiting to be split up in more sensible pieces.

This halves (non-release) build times here from 8.5 to 4.1 seconds.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
termproxy/Cargo.toml
termproxy/src/main.rs
termproxy/src/pty.rs [new file with mode: 0644]

index 61da2d2683f20d3379bb294dbaac3d270ce31056..d2cfc08d574db4e1090dbf6a3add337be625fec6 100644 (file)
@@ -16,9 +16,10 @@ lto = true
 
 [dependencies]
 anyhow = "1"
+libc = "0.2.107"
 mio = { version = "0.8", features = [ "net", "os-ext" ] }
+nix = "0.26.1"
 pico-args = "0.4"
 proxmox-io = "1"
 proxmox-lang = "1.1"
-proxmox-sys = "0.5"
 ureq = { version = "2.4", default-features = false, features = [ "gzip" ] }
index cb72ef61d04c4942fdda7de3e4752dca291ab47f..c674993ef7a19035423528dbbb255e71017c4df8 100644 (file)
@@ -14,11 +14,13 @@ use mio::{Events, Interest, Poll, Token};
 
 use proxmox_io::ByteBuffer;
 use proxmox_lang::error::io_err_other;
-use proxmox_sys::linux::pty::{make_controlling_terminal, PTY};
 
 mod cli;
 use crate::cli::{Options, PortOrFd};
 
+mod pty;
+use crate::pty::{make_controlling_terminal, PTY};
+
 const MSG_TYPE_DATA: u8 = 0;
 const MSG_TYPE_RESIZE: u8 = 1;
 //const MSG_TYPE_PING: u8 = 2;
diff --git a/termproxy/src/pty.rs b/termproxy/src/pty.rs
new file mode 100644 (file)
index 0000000..660d3ab
--- /dev/null
@@ -0,0 +1,134 @@
+//! Helper for creating a pseudo-terminal
+//!
+//! see [PTY](struct.PTY.html) for an example on how to use it
+
+use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
+
+use nix::fcntl::OFlag;
+use nix::pty::{grantpt, posix_openpt, ptsname_r, unlockpt, PtyMaster};
+use nix::sys::stat::Mode;
+use nix::unistd::{dup2, setsid};
+use nix::{ioctl_write_int_bad, ioctl_write_ptr_bad, Result};
+
+ioctl_write_int_bad!(set_controlling_tty, libc::TIOCSCTTY);
+ioctl_write_ptr_bad!(set_size, libc::TIOCSWINSZ, nix::pty::Winsize);
+
+/// Represents a PTY
+///
+/// Implements Read and Write (from std::io) so one can simply use it
+/// to read and write the terminal of a child process
+///
+/// Example:
+/// ```
+/// # use proxmox_sys::linux::pty::*;
+/// # use std::process::Command;
+/// # use nix::Result;
+/// fn fork() -> Result<u64> {
+///     // Code that forks and returs the pid/0
+/// # Ok(1)
+/// }
+///
+/// fn exec(cmd: &str) -> Result<()> {
+///     // Code that execs the cmd
+/// #    Ok(())
+/// }
+///
+/// fn main() -> Result<()> {
+///     let (mut pty, secondary) = PTY::new()?;
+///
+///     let child = fork()?;
+///     if child == 0 {
+///         make_controlling_terminal(&secondary)?;
+///         exec("/some/binary")?;
+///     }
+///
+///     // read/write or set size of the terminal
+///     pty.set_size(80, 20);
+///
+///     Ok(())
+///  }
+/// ```
+pub struct PTY {
+    primary: PtyMaster,
+}
+
+/// Used to make a new process group of the current process,
+/// and make the given terminal its controlling terminal
+pub fn make_controlling_terminal(terminal: &str) -> Result<()> {
+    setsid()?; // make new process group
+    let mode = Mode::S_IRUSR
+        | Mode::S_IWUSR
+        | Mode::S_IRGRP
+        | Mode::S_IWGRP
+        | Mode::S_IROTH
+        | Mode::S_IWOTH; // 0666
+    let secondary_fd = nix::fcntl::open(terminal, OFlag::O_RDWR | OFlag::O_NOCTTY, mode)
+        .map(|fd| unsafe { OwnedFd::from_raw_fd(fd) })?;
+    let s_raw_fd = secondary_fd.as_raw_fd();
+    unsafe { set_controlling_tty(s_raw_fd, 0) }?;
+    dup2(s_raw_fd, 0)?;
+    dup2(s_raw_fd, 1)?;
+    dup2(s_raw_fd, 2)?;
+
+    if s_raw_fd <= 2 {
+        std::mem::forget(secondary_fd); // don't call drop handler
+    }
+
+    Ok(())
+}
+
+impl PTY {
+    /// Creates a new PTY by opening /dev/ptmx and returns
+    /// a new PTY and the path to the secondary terminal on success.
+    pub fn new() -> Result<(Self, String)> {
+        let primary =
+            posix_openpt(OFlag::O_RDWR | OFlag::O_NOCTTY | OFlag::O_NONBLOCK | OFlag::O_CLOEXEC)?;
+        grantpt(&primary)?;
+        unlockpt(&primary)?;
+        let secondary = ptsname_r(&primary)?; // linux specific
+        Ok((Self { primary }, secondary))
+    }
+
+    /// Uses the ioctl 'TIOCSWINSZ' on the terminal fd to set the terminals
+    /// columns and rows
+    pub fn set_size(&mut self, col: u16, row: u16) -> Result<()> {
+        let size = nix::pty::Winsize {
+            ws_row: row,
+            ws_col: col,
+            ws_xpixel: 0,
+            ws_ypixel: 0,
+        };
+
+        unsafe { set_size(self.primary.as_raw_fd(), &size) }?;
+
+        Ok(())
+    }
+}
+
+impl std::io::Read for PTY {
+    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+        Ok(nix::unistd::read(self.primary.as_raw_fd(), buf)?)
+    }
+}
+
+impl std::io::Write for PTY {
+    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+        Ok(nix::unistd::write(self.primary.as_raw_fd(), buf)?)
+    }
+
+    fn flush(&mut self) -> std::io::Result<()> {
+        Ok(())
+    }
+}
+
+impl AsRawFd for PTY {
+    fn as_raw_fd(&self) -> RawFd {
+        self.primary.as_raw_fd()
+    }
+}
+
+impl AsFd for PTY {
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
+    }
+}