Skip to content
Merged
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
8 changes: 7 additions & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ umockdev_lib = shared_library('umockdev',
'src/uevent_sender.c',
'src/ioctl_tree.vapi',
'src/ioctl_tree.c',
'src/ioctl_termios.vapi',
'src/ioctl_termios.c',
'src/utils.c',
'src/debug.c'],
vala_vapi: 'umockdev-1.0.vapi',
Expand Down Expand Up @@ -224,6 +226,8 @@ umockdev_record_exe = executable('umockdev-record',
'src/umockdev-spi.vala',
'src/ioctl_tree.vapi',
'src/ioctl_tree.c',
'src/ioctl_termios.vapi',
'src/ioctl_termios.c',
'src/utils.c',
'src/debug.c'],
dependencies: [glib, gobject, gio_unix, vapi_posix, vapi_config, vapi_ioctl, vapi_selinux, libpcap, selinux],
Expand Down Expand Up @@ -282,7 +286,9 @@ if gudev.found()
depends: [preload_lib])

test('umockdev-vala', executable('test-umockdev-vala',
'tests/test-umockdev-vala.vala',
['tests/test-umockdev-vala.vala',
'src/ioctl_termios.vapi',
'src/ioctl_termios.c'],
include_directories: include_directories('src'),
dependencies: [glib, gobject, gio, gudev, vapi_posix, vapi_assertions, vapi_ioctl, vapi_glibc, vapi_selinux, selinux],
link_with: [umockdev_lib, umockdev_utils_lib],
Expand Down
43 changes: 43 additions & 0 deletions src/ioctl_termios.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* ioctl_termios.c - Helper functions for termios ioctl handling
*
* We can't use the Linux.Termios.TCGETS2 constants here: It is defined in sys/ioctl.h using _IOR(struct termios2),
* and Vala tries to apply sizeof() to the incomplete termios2 struct. Also, the TC*2 variants are not available
* on ppc64le, and Vala does not have conditional compilation.
*/

#include "ioctl_termios.h"
#include <sys/ioctl.h>
#include <asm/termbits.h>
#include <asm/ioctls.h>

bool is_termios_ioctl(unsigned long request)
{
switch (request) {
case TCGETS:
case TCSETS:
case TCSETSW:
case TCSETSF:
case TCGETA:
case TCSETA:
case TCSETAW:
case TCSETAF:
case TIOCGWINSZ:
case TIOCSWINSZ:
#ifdef TCGETS2
case TCGETS2:
case TCSETS2:
case TCSETSW2:
case TCSETSF2:
#endif
return true;
default:
return false;
}
}

unsigned long get_tcgets_ioctl(void)
{
return TCGETS;
}
11 changes: 11 additions & 0 deletions src/ioctl_termios.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* ioctl_termios.h - Helper functions for termios ioctl handling
*/

#pragma once

#include <stdbool.h>

bool is_termios_ioctl(unsigned long request);
unsigned long get_tcgets_ioctl(void);
8 changes: 8 additions & 0 deletions src/ioctl_termios.vapi
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[CCode (cprefix = "", lower_case_cprefix = "", cheader_filename = "ioctl_termios.h")]
namespace IoctlTermios {
[CCode (cname = "is_termios_ioctl")]
public bool is_termios_ioctl(ulong request);

[CCode (cname = "get_tcgets_ioctl")]
public ulong get_tcgets_ioctl();
}
4 changes: 3 additions & 1 deletion src/libumockdev-preload.c
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,9 @@ remote_emulate(int fd, int cmd, long arg1, long arg2)

switch (req.cmd) {
case IOCTL_RES_DONE:
errno = req.arg2;
/* Only set errno on failure (POSIX behavior) */
if (req.arg1 == (unsigned long)-1)
errno = req.arg2;

pthread_mutex_unlock (&fdinfo->sock_lock);
pthread_sigmask(SIG_SETMASK, &sig_restore, NULL);
Expand Down
25 changes: 20 additions & 5 deletions src/umockdev-ioctl.vala
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@


namespace UMockdev {

/**
Expand Down Expand Up @@ -30,7 +29,6 @@ internal bool signal_accumulator_true_handled(GLib.SignalInvocationHint ihint,
return continue_emission;
}


/**
* UMockdevIoctlData:
*
Expand Down Expand Up @@ -568,6 +566,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 @@ -585,16 +586,30 @@ public class IoctlClient : GLib.Object {
return;
}

/* Set default errno for tree.execute if it doesn't set one itself */
if ((char) type == 'E') {
Posix.errno = Posix.ENOENT;
} else {
Posix.errno = Posix.ENOTTY;
}

/* Try handling with ioctl tree (stateless ioctls like USB) */
tree.execute(null, _request, *(void**) _arg.data, out ret);
my_errno = Posix.errno;
Posix.errno = 0;
my_errno = Posix.errno; /* Capture errno (may be set by tree.execute) */

if (ret != -1) {
if (ret == -1) {
/* tree.execute failed - check if we should forward to real PTY for termios */
if (my_errno == Posix.ENOTTY && IoctlTermios.is_termios_ioctl(_request)) {
try {
ret = execute(out my_errno);
/* execute() returns errno from real ioctl on failure,
* or preserved errno on success (POSIX behavior) */
} catch (IOError e) {
/* execute() threw exception - keep ENOTTY */
}
}
} else {
/* tree.execute succeeded - errno not used (preload doesn't set it on success) */
my_errno = 0;
}

Expand Down
Loading