Skip to content

TIOCGPTPEER PTY Fallback for Restricted Android Kernels#20

Merged
bajrangCoder merged 6 commits intobajrangCoder:mainfrom
Ebola-Chan-bot:main
Mar 10, 2026
Merged

TIOCGPTPEER PTY Fallback for Restricted Android Kernels#20
bajrangCoder merged 6 commits intobajrangCoder:mainfrom
Ebola-Chan-bot:main

Conversation

@Ebola-Chan-bot
Copy link
Copy Markdown
Contributor

Summary

The standard openpty() call might fail because SELinux blocks open("/dev/pts/N") under certain environments. This PR adds a TIOCGPTPEER ioctl fallback path so the terminal can create PTY sessions on these restricted kernels. It also switches portable-pty from a pinned wezterm git revision to the crates.io release.

Changes

1. New file: src/terminal/pty_fallback.rs (+351 lines)

Why: portable_pty::native_pty_system().openpty() calls open("/dev/pts/N") internally, which might be denied by SELinux in proot environments. The TIOCGPTPEER ioctl obtains the slave fd directly from the master fd, completely bypassing /dev/pts directory access.

Implementation:

  • OwnedFd — lightweight RAII fd wrapper implementing Read / Write, maps EIO to EOF
  • FallbackMasterPty — implements the portable_pty::MasterPty trait (resize / get_size / try_clone_reader / take_writer)
  • FallbackMasterWriter — sends EOT on drop to match portable-pty behaviour
  • close_fds_above_stderr() — closes leaked fds after fork
  • fallback_open_and_spawn() — public entry point: /dev/ptmxgrantptunlockptTIOCGPTPEER → set winsize → fork + setsid + TIOCSCTTY → exec child

2. Refactored src/terminal/handlers.rs (create_terminal)

Why: The original code performed all session setup (reader/writer/scrollback/exit waiter) inside a deeply nested match openpty { Ok(pair) => match spawn { Ok(child) => ... } } block. Adding a fallback path would make this nesting unmanageable.

What changed:

  • PTY creation is now a two-step try: standard path first, then fallback_open_and_spawn on failure
  • Both paths return a unified (Box<dyn MasterPty + Send>, Box<dyn Child + Send + Sync>)
  • Session setup code (reader/writer/scrollback/exit waiter) is extracted into a shared section after the PTY creation, eliminating duplication
  • native_pty_system() initialization moved closer to its usage point

3. New dependencies: anyhow + libc (Cargo.toml)

Dependency Version Purpose
anyhow 1 Error handling in pty_fallback.rs (bail! macro)
libc 0.2 Direct calls to ioctl / grantpt / setsid and other POSIX APIs

4. portable-pty source change

- portable-pty = { git = "https://github.com/wez/wezterm", package = "portable-pty", rev = "1e941fca5c17..." }
+ portable-pty = "0.9.0"

Why: The previous dependency pulled from a pinned revision of the entire wezterm monorepo, resulting in slow fetches and a bloated Cargo.lock. The crates.io 0.9.0 release provides the same API surface with faster builds and fewer transitive dependencies.

Copilot AI review requested due to automatic review settings March 4, 2026 09:49
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a Linux TIOCGPTPEER-based PTY creation/spawn fallback for environments where openpty() fails (e.g., SELinux denying /dev/pts/N), and updates portable-pty to the crates.io release to reduce dependency bloat and fetch time.

Changes:

  • Introduces src/terminal/pty_fallback.rs implementing a PTY + spawn fallback using /dev/ptmx + TIOCGPTPEER.
  • Refactors create_terminal to try standard portable-pty first, then fall back on failure with shared session setup.
  • Updates dependencies (portable-pty = 0.9.0) and adds anyhow + libc.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/terminal/pty_fallback.rs New Linux-only PTY fallback implementation using TIOCGPTPEER and Command spawn.
src/terminal/mod.rs Wires the new pty_fallback module into the terminal module.
src/terminal/handlers.rs Refactors terminal creation to use a two-stage (standard → fallback) PTY creation path.
Cargo.toml Adds anyhow + libc, switches portable-pty to crates.io.
Cargo.lock Lockfile update reflecting new dependency source and resolver updates.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/terminal/pty_fallback.rs Outdated
Comment thread src/terminal/pty_fallback.rs Outdated
Comment thread src/terminal/pty_fallback.rs Outdated
Comment thread src/terminal/pty_fallback.rs Outdated
Comment thread src/terminal/mod.rs
Comment thread src/terminal/handlers.rs Outdated
Comment thread src/terminal/handlers.rs
Comment thread src/terminal/pty_fallback.rs Outdated
Comment thread src/terminal/pty_fallback.rs Outdated
埃博拉酱 added 2 commits March 4, 2026 18:04
Add pty_fallback.rs implementing TIOCGPTPEER ioctl to obtain slave PTY fd
directly from master fd, bypassing /dev/pts directory access blocked by
SELinux in proot environments (e.g. HarmonyOS + Zhuo Yi Tong).

Refactor create_terminal in handlers.rs: extract session setup into a
shared path after a two-step PTY creation (standard openpty first,
then TIOCGPTPEER fallback on failure).

Add dependencies: anyhow (error handling), libc (POSIX syscalls).
- Set FD_CLOEXEC on dup'd fds in OwnedFd::try_clone() to prevent fd leaks
- Retry read/write on EINTR for robust PTY I/O
- Replace close_fds_above_stderr() with async-signal-safe implementation
  (close_range syscall + getrlimit loop, no heap allocation in pre_exec)
- Wrap slave_fd in OwnedFd RAII to ensure cleanup on all error paths
- Separate openpty from spawn_command in handlers.rs to avoid unnecessary
  fallback when spawn fails (e.g. missing program)
- Log non-fatal TIOCSWINSZ failure instead of ignoring silently
- Add #[cfg] rejection comment in mod.rs (Linux-only by design)
@bajrangCoder bajrangCoder self-requested a review March 4, 2026 13:07
@bajrangCoder bajrangCoder self-assigned this Mar 4, 2026
Comment thread src/terminal/handlers.rs
Comment thread src/terminal/pty_fallback.rs
Kill and wait the spawned child when shared terminal session setup fails after spawn so partially initialized sessions do not leak background processes.

Also tighten the pre-exec file descriptor cleanup fallback by using RLIMIT-derived bounds when available and a high conservative upper bound when they are not, preventing descriptors above 1023 from surviving when close_range cannot be used.
@bajrangCoder bajrangCoder merged commit 521ffd5 into bajrangCoder:main Mar 10, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants