From e087ff8b717e27cd765400de4ee081d8ceff4746 Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Wed, 15 Jul 2026 22:12:37 +0900 Subject: [PATCH] ptyd: expose PTS number via dup(master, ptsname) for getty slave open --- ptyd/src/main.rs | 1 + ptyd/src/ptsname.rs | 86 +++++++++++++++++++++++++++++++++++++++++++++ ptyd/src/scheme.rs | 3 ++ 3 files changed, 90 insertions(+) create mode 100644 ptyd/src/ptsname.rs diff --git a/ptyd/src/main.rs b/ptyd/src/main.rs index 94a13b38a1..c25e68703b 100644 --- a/ptyd/src/main.rs +++ b/ptyd/src/main.rs @@ -7,6 +7,7 @@ use syscall::data::TimeSpec; mod controlterm; mod pgrp; +mod ptsname; mod pty; mod resource; mod scheme; diff --git a/ptyd/src/ptsname.rs b/ptyd/src/ptsname.rs new file mode 100644 index 0000000000..95f8a4aa65 --- /dev/null +++ b/ptyd/src/ptsname.rs @@ -0,0 +1,86 @@ +use std::cell::RefCell; +use std::rc::Weak; + +use syscall::error::{Error, Result, EBADF, EINVAL, EPIPE}; +use syscall::flag::{EventFlags, F_GETFL, F_SETFL, O_ACCMODE}; + +use crate::pty::Pty; +use crate::resource::Resource; + +/// Read-only resource exposing the PTY's numeric id. +/// +/// relibc's `ptsname`/`ptsname_r` resolve the subterminal path via the +/// `TIOCGPTN` ioctl, which does `dup(master, "ptsname")` and reads a +/// `c_int` (4 bytes) — the PTS number — then formats `/scheme/pty/`. +/// Without this handler ptyd's `dup` returned `EINVAL` for "ptsname", so +/// `ptsname` failed, getty resolved a bogus slave path, and it panicked +/// with "failed to open slave stdin: No such file or directory". Because +/// `Pty::id` is also the master's scheme handle id, returning it here makes +/// the subsequent `/scheme/pty/` slave open resolve to the right master. +pub struct PtyPtsname { + pty: Weak>, + flags: usize, +} + +impl PtyPtsname { + pub fn new(pty: Weak>, flags: usize) -> Self { + PtyPtsname { pty, flags } + } +} + +impl Resource for PtyPtsname { + fn pty(&self) -> Weak> { + self.pty.clone() + } + + fn flags(&self) -> usize { + self.flags + } + + fn path(&mut self, buf: &mut [u8]) -> Result { + if let Some(pty_lock) = self.pty.upgrade() { + pty_lock.borrow_mut().path(buf) + } else { + Err(Error::new(EPIPE)) + } + } + + fn read(&mut self, buf: &mut [u8]) -> Result { + if let Some(pty_lock) = self.pty.upgrade() { + let id = pty_lock.borrow().id as i32; + let bytes = id.to_ne_bytes(); + let n = core::cmp::min(buf.len(), bytes.len()); + buf[..n].copy_from_slice(&bytes[..n]); + Ok(n) + } else { + Ok(0) + } + } + + fn write(&mut self, _buf: &[u8]) -> Result { + Err(Error::new(EINVAL)) + } + + fn sync(&mut self) -> Result<()> { + Ok(()) + } + + fn fcntl(&mut self, cmd: usize, arg: usize) -> Result { + match cmd { + F_GETFL => Ok(self.flags), + F_SETFL => { + self.flags = (self.flags & O_ACCMODE) | (arg & !O_ACCMODE); + Ok(0) + } + _ => Err(Error::new(EINVAL)), + } + } + + fn fevent(&mut self) -> Result { + Err(Error::new(EBADF)) + } + + fn events(&mut self) -> EventFlags { + EventFlags::empty() + } +} diff --git a/ptyd/src/scheme.rs b/ptyd/src/scheme.rs index 3f3f0c7d30..530cc4b3c0 100644 --- a/ptyd/src/scheme.rs +++ b/ptyd/src/scheme.rs @@ -12,6 +12,7 @@ use syscall::schemev2::NewFdFlags; use crate::controlterm::PtyControlTerm; use crate::pgrp::PtyPgrp; +use crate::ptsname::PtyPtsname; use crate::pty::Pty; use crate::resource::Resource; use crate::subterm::PtySubTerm; @@ -126,6 +127,8 @@ impl SchemeSync for PtyScheme { Box::new(PtyTermios::new(old_resource.pty(), old_resource.flags())) } else if buf == b"winsize" { Box::new(PtyWinsize::new(old_resource.pty(), old_resource.flags())) + } else if buf == b"ptsname" { + Box::new(PtyPtsname::new(old_resource.pty(), old_resource.flags())) } else { return Err(Error::new(EINVAL)); }