Skip to content

ioctl: Forward standard termios calls to real pty#279

Merged
martinpitt merged 3 commits intomainfrom
termio-ioctl
Feb 21, 2026
Merged

ioctl: Forward standard termios calls to real pty#279
martinpitt merged 3 commits intomainfrom
termio-ioctl

Conversation

@martinpitt
Copy link
Copy Markdown
Owner

@martinpitt martinpitt commented Feb 16, 2026

Handle ioctls TCGETS{,2} and friends on emulated TTY devices by forwarding them to the underlying PTY. Our ioctl handler previously always returned ENOTTY for these, unless there was an explicit handler.

This fixes tests with uutils (Rust coreutils)' stty implementation, the current Ubuntu development series just switched to that [1].

GNU coreutils avoids that problem:

  1. Opens (emulated) /dev/ttyUSB1 as fd 3
  2. dup2(3,0) = 0
  3. close(3)
  4. ioctl(0, TCGETS2, ...) = 0

i.e. it bypasses the emulation and does the ioctl on the real underlying PTY.

Rust coreutils does not do the dup() step, but calls TCGETS2 on the /dev/ttyUSB1 directly, so it previously got ENOTTY and failed.

This is unfortunately rather messy: The most obvious solution of using the Linux::Termios::TC* constants does not work, as Vala cannot evaluate the incomplete struct termios2; and the TC*2 variants don't exist on ppc64le, but Vala does not have conditional compilation. So add a little C helper library for these.

[1] https://launchpad.net/ubuntu/+source/rust-coreutils/0.6.0-0ubuntu1


Fixes the ubuntu/devel failure which started a few days ago.

@martinpitt
Copy link
Copy Markdown
Owner Author

ppc64le trouble:

not ok /umockdev-testbed-vala/usbfs_ioctl_static - ERROR:../tests/test-umockdev-vala.vala:296:t_usbfs_ioctl_static: assertion failed (ioctl (fd, 0x5413, &tio_data) == 0): (-1 == 0)

will look later.

@martinpitt
Copy link
Copy Markdown
Owner Author

Man, what a mess. This should work now!

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

This PR improves ioctl handling for emulated TTY devices by forwarding standard termios ioctls (e.g., TCGETS/TCSETS/TIOCGWINSZ) to the underlying PTY when no explicit emulation handler exists, fixing failures with Rust coreutils’ stty.

Changes:

  • Add a termios-ioctl fallback path in the default ioctl handler to execute the ioctl on the real (PTY-backed) fd when appropriate.
  • Introduce a small C helper (ioctl_termios.*) to identify termios ioctls and expose constants in a Vala-friendly way.
  • Add a Vala test ensuring a basic termios ioctl (TCGETS) succeeds on an emulated TTY device.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/test-umockdev-vala.vala Adds regression test for TCGETS on PTY-backed emulated TTY.
src/umockdev-ioctl.vala Adds fallback to run real ioctl for termios requests when emulation yields ENOTTY.
src/ioctl_termios.vapi Vala bindings for termios ioctl helper functions.
src/ioctl_termios.h Declares helper functions for identifying termios ioctls / getting TCGETS.
src/ioctl_termios.c Implements termios ioctl detection with arch-conditional TC*2 handling.
meson.build Wires new helper sources into library/program builds and the Vala test build.

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

Comment thread src/umockdev-ioctl.vala Outdated
Comment thread src/umockdev-ioctl.vala Outdated
Handle ioctls TCGETS{,2} and friends on emulated TTY devices by
forwarding them to the underlying PTY. Our ioctl handler previously
always returned `ENOTTY` for these, unless there was an explicit
handler.

This fixes tests with uutils (Rust coreutils)' `stty` implementation,
the current Ubuntu development series just switched to that [1].

GNU coreutils avoids that problem:
 1. Opens (emulated) /dev/ttyUSB1 as fd 3
 2. dup2(3,0) = 0
 3. close(3)
 4. ioctl(0, TCGETS2, ...) = 0

i.e. it bypasses the emulation and does the ioctl on the real underlying
PTY.

Rust coreutils does *not* do the dup() step, but calls TCGETS2 on the
/dev/ttyUSB1 directly, so it previously got ENOTTY and failed.

This is unfortunately rather messy: The most obvious solution of using
the Linux::Termios::TC* constants does not work, as Vala cannot evaluate
the incomplete `struct termios2`; and the TC*2 variants don't exist on
ppc64le, but Vala does not have conditional compilation. So add a little
C helper library for these.

[1] https://launchpad.net/ubuntu/+source/rust-coreutils/0.6.0-0ubuntu1
Per POSIX, errno is only meaningful after a syscall fails. Successful
syscalls preserve the previous errno value. Remove assertions that
check errno == 0 after successful ioctl calls. This prepares the tests
for the next change where we actually fix the emulated ioctls to behave
that way.
Per POSIX, errno is only modified on syscall failure and is preserved
on success. Make our emulated ioctl()s conformant to that.

In the preload (client side), only set errno when ioctl/read/write
fails.

In umockdev-ioctl.vala (server side), clean up the errno handling:

 - Pre-set default errno (ENOENT for type 'E', ENOTTY otherwise)
   before calling tree.execute(), so ioctl handlers can override it
 - Capture errno after tree.execute() to preserve handler-set errors
   (e.g., ENODATA for USBDEVFS_GETDRIVER)
 - For termios ioctls forwarded to real PTY: preserve errno from the
   real ioctl(2) call
 - For successful tree.execute(): send errno=0 to preload, which now
   correctly ignores it

This ensures umockdev's ioctl emulation matches real kernel behavior:
- Successful calls preserve errno (applications can detect success by
  checking return value, not errno)
- Failed calls set appropriate error codes
- termios ioctls forwarded to real PTY inherit the real errno
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

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.


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

@martinpitt martinpitt merged commit 66629bc into main Feb 21, 2026
57 of 58 checks passed
@martinpitt martinpitt deleted the termio-ioctl branch February 21, 2026 02:20
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.

2 participants