From 04bd88f17948eac3ec7e39e273486fa5fcfabe38 Mon Sep 17 00:00:00 2001 From: Johannes Leupolz Date: Tue, 23 Dec 2025 20:26:07 +0000 Subject: [PATCH] Check for status of tty1 during startup --- docs/DESIGN.md | 20 +++++------- vuinputd/src/main.rs | 15 +++++++++ vuinputd/src/vt_tools.rs | 67 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 13 deletions(-) create mode 100644 vuinputd/src/vt_tools.rs diff --git a/docs/DESIGN.md b/docs/DESIGN.md index 433ec10..5f58dac 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -499,7 +499,7 @@ As a result: * input devices may interact with the VT layer in unintended ways When a graphical session is active, these issues do not occur: -the compositor, via `systemd-logind`, owns a VT, switches it to `KD_GRAPHICS`, and the VT keyboard handler is disabled automatically. +the compositor, via `systemd-logind`, owns a VT, switches it to `KD_GRAPHICS`, and the VT keyboard handler is **suppressed**. The missing piece is a **well-defined fallback** for the “no graphical session” case. @@ -511,12 +511,12 @@ The missing piece is a **well-defined fallback** for the “no graphical session It runs only when **no other graphical session is active** on the seat and exists solely to: -* open a regular logind session +* open a regular logind session **of class `greeter**` * occupy the assigned VT -* let logind switch the VT to `KD_GRAPHICS` +* let logind switch the VT to `KD_GRAPHICS` **and mute the keyboard handler** `fallbackdm` itself does **not** manipulate VTs, perform `KDSETMODE` ioctls, or access `/dev/tty` directly. -All VT and seat handling is delegated to `systemd-logind`. +All VT and seat handling is delegated to `systemd-logind` **via a standard PAM session**. --- @@ -524,15 +524,13 @@ All VT and seat handling is delegated to `systemd-logind`. * `fallbackdm` starts as a normal session on the seat * while active: - * the VT is in `KD_GRAPHICS` - * VT keyboard handling and `getty` input are suppressed + * **the kernel VT keyboard handler is muted (equivalent to `K_OFF` or `KDSKBMUTE`)** + * `getty` input is suppressed * when a real graphical session (greeter or compositor) starts: - * logind deactivates `fallbackdm` * VT ownership is transferred automatically * when the graphical session ends: - * `fallbackdm` may be restarted to reclaim the fallback role `fallbackdm` is non-interactive by design but may display **minimal status information** in the future. @@ -546,7 +544,6 @@ This design: * reuses existing, well-tested logind behavior * avoids duplicating VT and seat logic * guarantees compatibility with: - * Wayland compositors * graphical login managers * multi-seat setups @@ -560,13 +557,10 @@ Conceptually, `fallbackdm` acts as a **headless placeholder graphical session** * **Direct VT management (KDSETMODE, VT ioctls)** Rejected due to complexity, fragility, and duplication of logind functionality. - * **Filtering input at the evdev / uinput layer** Rejected as insufficient: it does not address VT keyboard handling or `getty` behavior. - * **Disabling gettys or VT switching globally** - Rejected to preserve emergency local access and standard Linux behavior. - + Rejected to preserve emergency local access and standard Linux behavior. **The logind-managed approach allows physical VT switching to remain functional for debugging.** * **Depending on a full display manager** Rejected, as this may require dummy display devices in headless configurations and adds unnecessary complexity. diff --git a/vuinputd/src/main.rs b/vuinputd/src/main.rs index bdf3ef6..c9601ca 100644 --- a/vuinputd/src/main.rs +++ b/vuinputd/src/main.rs @@ -16,10 +16,13 @@ // distinguish between cleanup jobs that must not be cancelled and other jobs (especially background jobs) // naming: dev_path vs dev_node. I guess I mean the same. // Send warning, if udev monitor does not exist +// Filter out Ctrl+Alt+Fx. "sysrq" keys or the low-level VT switching combos. use ::cuse_lowlevel::*; use log::info; use std::ffi::CString; +use std::fs::OpenOptions; +use std::os::fd::AsRawFd; use std::os::raw::c_char; use std::sync::atomic::AtomicU64; use std::sync::Mutex; @@ -40,6 +43,7 @@ use crate::process_tools::*; pub mod actions; pub mod jobs; +pub mod vt_tools; use clap::Parser; @@ -72,6 +76,12 @@ struct Args { help = "Path to /proc//ns used as the namespace source (e.g. /proc/1234/ns or /proc/self/ns)" )] pub target_namespace: Option, + + #[arg( + long = "vt-guard", + help = "Prevent leakage of uinput to VT by, sending K_OFF to /dev/tty0" + )] + pub vt_guard: bool, } fn validate_args(args: &Args) -> Result<(), String> { @@ -132,7 +142,12 @@ fn main() -> std::io::Result<()> { std::process::exit(error_code); } + if args.vt_guard { + vt_tools::mute_keyboard()?; + } + check_permissions().expect("failed to read the capabilities of the vuinputd process"); + vt_tools::check_vt_status(); initialize_vuinput_state(); VUINPUT_COUNTER.set(AtomicU64::new(3)).expect( diff --git a/vuinputd/src/vt_tools.rs b/vuinputd/src/vt_tools.rs new file mode 100644 index 0000000..60be9f5 --- /dev/null +++ b/vuinputd/src/vt_tools.rs @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +// +// Author: Johannes Leupolz + +use std::fs::OpenOptions; +use std::io; +use std::os::unix::io::AsRawFd; +use log::{error, info, warn}; + +use libc::ioctl; + +// see include/uapi/linux/kd.h +const KDSKBMODE: u64 = 0x4B45; // sets current keyboard mode +const KDGKBMODE: u64 = 0x4B44; // gets current keyboard mode + +const K_OFF: u64 = 0x04; + +pub fn check_vt_status() { + match OpenOptions::new().read(true).open("/dev/tty1") { + Err(err) if err.kind() == io::ErrorKind::NotFound => { + info!("/dev/tty1 not present — no VT-related input problem"); + } + Err(err) => { + error!("failed to open /dev/tty1: {}", err); + } + Ok(tty) => { + let fd = tty.as_raw_fd(); + let mut mode: u64 = 0; + + let rc = unsafe { ioctl(fd, KDGKBMODE, &mut mode) }; + if rc < 0 { + error!( + "KDGKBMODE ioctl failed: {}", + io::Error::last_os_error() + ); + return; + } + + if mode == K_OFF { + info!("tty1 keyboard mode is K_OFF — VT input is disabled"); + } else { + warn!( + "tty1 keyboard mode is active (mode={}) — VT may consume input", + mode + ); + } + } + } +} + +pub fn mute_keyboard() -> std::io::Result<()> { + // 1. Open the TTY (usually TTY1 for a DM) + let file = OpenOptions::new() + .read(true) + .write(true) + .open("/dev/tty1")?; + let fd = file.as_raw_fd(); + + // 2. Mute the keyboard + unsafe { + if libc::ioctl(fd, KDSKBMODE, K_OFF) < 0 { + panic!("Failed to mute keyboard. Are you root?"); + } + } + println!("Keyboard muted."); + Ok(()) +}