Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion src/umockdev-ioctl.vala
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,9 @@ public class IoctlClient : GLib.Object {
}

if (!handled && args[0] == 1) {
/* No specific handler for this ioctl. First try stateless ioctls
* (like USB ioctls), then fall back to executing on the real fd
* for terminal ioctls on PTY-backed devices. */
IoctlTree.Tree tree = null;
IoctlData? data = null;
ulong size = IoctlTree.data_size_by_id(_request);
Expand All @@ -594,7 +597,21 @@ public class IoctlClient : GLib.Object {
my_errno = Posix.errno;
Posix.errno = 0;

if (ret != -1) {
/* For termios ioctls (TCGETS, etc.), try executing on the real fd (PTY).
* Check the ioctl number (low byte) and type to identify termios
* ioctls, but exclude non-termios ioctls like FIONREAD (0x541B). */
ulong nr = _request & 0xFF;
bool is_termios_ioctl = ((char) type == 'T') &&
((nr >= 0x01 && nr <= 0x08) || // TCGETS, TCSETS, etc.
(nr >= 0x13 && nr <= 0x16) || // TCGETS2, TCSETS2, TCSETSW2, TCSETSF2
(nr >= 0x2A && nr <= 0x2C)); // TCSETS2 (with size)
if (ret == -1 && my_errno == Posix.ENOTTY && is_termios_ioctl) {
try {
ret = execute(out my_errno);
} catch (IOError e) {
/* If real ioctl also fails, keep the ENOTTY error */
}
} else if (ret != -1) {
my_errno = 0;
}

Expand Down
20 changes: 20 additions & 0 deletions tests/test-umockdev-vala.vala
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,26 @@ E: SUBSYSTEM=usb
Posix.close (fd);
Posix.errno = 0;

// Test that TCGETS2 works on an emulated TTY device (backed by a real PTY)
tb_add_from_string (tb, """P: /devices/serial/ttyTest
N: ttyTest
E: DEVNAME=/dev/ttyTest
E: SUBSYSTEM=tty
""");

fd = Posix.open ("/dev/ttyTest", Posix.O_RDWR | Posix.O_NONBLOCK, 0);
assert_cmpint (fd, CompareOperator.GE, 0);
Posix.errno = 0;
// Note: We can't use the Linux.Termios.TCGETS2 constant here due to a Vala compiler limitation.
// It is defined in sys/ioctl.h using _IOR(struct termios2), and Vala tries to apply
// sizeof() to the incomplete termios2 struct.
long tio_data = 0;
assert_cmpint (Posix.ioctl (fd, 0x5413, &tio_data), CompareOperator.EQ, 0);
assert_cmpint (Posix.errno, CompareOperator.EQ, 0);

Posix.close (fd);
Posix.errno = 0;

// unknown ioctls do work on non-emulated devices
fd = Posix.open ("/dev/stdout", Posix.O_WRONLY, 0);
assert_cmpint (fd, CompareOperator.GE, 0);
Expand Down