Check for status of tty1 during startup

This commit is contained in:
Johannes Leupolz 2025-12-23 20:26:07 +00:00
parent 77c3452bbb
commit 04bd88f179
3 changed files with 89 additions and 13 deletions

View file

@ -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.

View file

@ -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/<pid>/ns used as the namespace source (e.g. /proc/1234/ns or /proc/self/ns)"
)]
pub target_namespace: Option<String>,
#[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(

67
vuinputd/src/vt_tools.rs Normal file
View file

@ -0,0 +1,67 @@
// SPDX-License-Identifier: MIT
//
// Author: Johannes Leupolz <dev@leupolz.eu>
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(())
}