diff --git a/docs/DESIGN.md b/docs/DESIGN.md index ab21957..433ec10 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -487,59 +487,6 @@ The chosen approach offers the best balance between correctness, portability, an --- -## **3.11 VT Guarding for Headless / No-Compositor Systems** - -### **Decision** - -When **no X11 or Wayland session is active**, `vuinputd` relies on a small, separate **VT-guard daemon** to own the currently active Virtual Terminal (VT) and switch it into **graphics mode (`KD_GRAPHICS`)**. - -If an X11 or Wayland compositor is running, **no VT guard is required**, as the compositor already owns a VT and has disabled the VT keyboard handler. - -The VT-guard: - -* runs in the foreground -* opens the active VT (`/dev/tty`) -* prints an informational notice -* switches the VT to `KD_GRAPHICS` -* registers for VT release notifications -* restores `KD_TEXT` and exits on VT switch - -It does not access evdev, uinput, or interpret key events. - -### **Rationale** - -On systems without a graphical session, keyboard input reaches `getty` via the **VT keyboard handler**, independent of evdev. -Switching the VT to `KD_GRAPHICS` disables this path and effectively starves `getty` on that VT. - -This mirrors compositor behavior (Xorg / Wayland) without requiring rendering, DRM, or input stack integration. - -### **Designed Behavior: VT Switching Is Allowed** - -VT switching (e.g. `Ctrl+Alt+Fn`) is **intentionally not blocked**. - -This preserves a recovery path: - -* users can switch to another VT -* `getty` remains accessible for local login -* physical access always overrides remote control - -On VT switch, the VT-guard restores `KD_TEXT` and exits cleanly. - -### **Constraints and Guarantees** - -The VT-guard guarantees: - -* the active VT does not accept keyboard input -* `getty` is effectively disabled on that VT -* normal console behavior resumes on VT switch - -### **Alternatives Considered** - -* **Blocking VT switching** - Rejected to preserve local recovery. - ---- - ## **3.11 Fallback Graphical Session (`fallbackdm`)** ### **Problem Statement** @@ -620,6 +567,8 @@ Conceptually, `fallbackdm` acts as a **headless placeholder graphical session** * **Disabling gettys or VT switching globally** Rejected to preserve emergency local access and standard Linux behavior. +* **Depending on a full display manager** + Rejected, as this may require dummy display devices in headless configurations and adds unnecessary complexity. --- diff --git a/docs/USAGE.md b/docs/USAGE.md index 005645d..a047248 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -225,7 +225,111 @@ Sample output from `journalctl` showing vuinputd output: --- -## 7. Troubleshooting +## 7. Handling Phantom Input Events Caused by VTs + +On Linux systems without an active graphical session (X11 or Wayland), **virtual terminals (VTs)** remain in text mode (`KD_TEXT`) and continue to process keyboard input via the kernel VT keyboard handler. +This can lead to *phantom input events*, where injected or forwarded input (e.g. via `vuinputd`) unintentionally reaches: + +* `getty` login prompts +* inactive consoles +* kernel VT hotkeys (e.g. `Ctrl+Alt+Fn`) + +The following approaches can be used to prevent or mitigate this behavior. + +### Solution 1: Use KMSCON (DRM/KMS-based console) + +A robust solution is to replace the kernel VT text console with a **DRM/KMS-based console** such as `kmscon`. + +#### How it helps + +* The kernel VT is no longer responsible for input handling +* Keyboard input is processed via evdev, not the VT layer +* Seat assignment is respected: + + * devices on non-default seats (e.g. `seat_vuinput`) are ignored +* Phantom input events do not reach `getty` + +#### Notes + +* Requires DRM/KMS availability +* On most real GPUs, the DRM device remains available even when no monitor is connected and enters a hotplug-waiting state +* For headless systems, a virtual KMS device can be used: + + ```bash + modprobe vkms + ``` + +#### Trade-offs + +* Additional dependencies (DRM, kmscon) +* Not always desired for minimal or embedded systems + +### Solution 2: VT Guard Mode (`--vt-guard`) + +`vuinputd` can be started with the `--vt-guard` flag to explicitly neutralize VT input handling. + +#### How it works + +At startup, `vuinputd` performs a minimal VT operation such as: + +* switching the active VT into graphics mode (`KD_GRAPHICS`), or +* disabling the kernel keyboard processing for that VT + +This is done via direct VT ioctls (e.g. `KDSETMODE`), ensuring that: + +* the kernel VT keyboard handler is inactive +* `getty` does not receive injected input events + +#### Characteristics + +* Very lightweight +* No DRM, compositor, or additional services required +* Effective even on fully headless systems + +#### Caveats + +* Relies on low-level VT ioctls +* Considered **hacky**, but intentionally minimal +* Bypasses higher-level session management + +### Solution 3: fallbackdm (Work in Progress) + +`fallbackdm` is an experimental, lightweight **logind-integrated fallback display manager**. + +#### Intended behavior + +* Starts only when no graphical session is active +* Registers a proper `greeter` session with `systemd-logind` +* Takes ownership of a VT and switches it to `KD_GRAPHICS` +* Prevents `getty` and the VT keyboard handler from receiving input +* Leaves other VTs untouched for emergency local access + +#### Advantages + +* Clean integration with `systemd-logind` +* No direct VT hacks +* Compatible with standard Linux session semantics +* Designed to coexist with real display managers + +#### Status + +* Currently under development +* Intended as the long-term, principled solution + +### Summary + +| Solution | Headless | Lightweight | logind-aware | Recommended for | +| ------------ | --------- | ----------- | ------------ | ---------------------------- | +| KMSCON | ⚠️ (vkms) | ❌ | ✅ | Full console replacement | +| `--vt-guard` | ✅ | ✅ | ❌ | Minimal setups | +| fallbackdm | ✅ | ⚠️ | ✅ | Long-term, clean integration | + +Choose the approach that best fits your system constraints and deployment model. + + +--- + +## 8. Troubleshooting | Symptom | Possible Cause | Fix | | --------------------------- | ------------------------------------ | ------------------------------------------------- | @@ -246,7 +350,7 @@ Dez 14 21:33:17 wohnzimmer vuinputd[2172719]: called `Result::unwrap()` on an `E Ensure /dev and /run are writable in the container. If in doubt, use tmpfs. --- -## 8. Notes and Advanced Topics +## 9. Notes and Advanced Topics * You can safely run **multiple containers**. * Devices are automatically cleaned up when the container stops. @@ -258,7 +362,7 @@ Ensure /dev and /run are writable in the container. If in doubt, use tmpfs. --- -## 9. References +## 10. References * [mkosi manual](https://github.com/systemd/mkosi/blob/main/mkosi/resources/man/mkosi.1.md) * [Docker device rules documentation](https://docs.docker.com/engine/reference/run/#device-cgroup-rule) diff --git a/fallbackdm/README.md b/fallbackdm/README.md new file mode 100644 index 0000000..1c7afca --- /dev/null +++ b/fallbackdm/README.md @@ -0,0 +1,147 @@ +# fallbackdm + +> This crate is WIP and has not released any source, yet. + +**fallbackdm** is a minimal, headless display manager that exists solely to **own a seat and VT when no graphical session is running**. + +It prevents unintended keyboard input from reaching `getty` or the kernel VT layer by registering a proper **greeter session** with `systemd-logind`, activating a VT, and switching it to graphics mode — without starting X11 or Wayland. + +This is primarily useful for **kiosk setups, remote desktop systems, or input-virtualization scenarios** where no local user interaction is intended, but correct VT semantics must still be preserved. + +--- + +## Problem Statement + +On modern Linux systems: + +* Virtual terminals (VTs) still exist and have a kernel keyboard handler +* If **no graphical session is active**, `getty` will attach to a VT +* Input injected via `uinput` or forwarded from remote systems may: + + * Trigger `Ctrl+Alt+Fn` + * Wake or interfere with `getty` + * Cause VT switches or text-mode interaction + +Graphical compositors avoid this by: + +* Registering a session with `systemd-logind` +* Owning a VT +* Switching it to `KD_GRAPHICS` + +But when **no compositor or greeter is running**, nothing owns the VT. + +**fallbackdm fills exactly this gap.** + +--- + +## What fallbackdm Does + +* Registers a **`greeter` session** via PAM + `pam_systemd` +* Acquires a seat using **libseat** +* Activates a VT and switches it to graphics mode +* Keeps the session alive while no real graphical session exists +* Displays nothing and launches no compositor + +Once a real display manager or compositor starts, it naturally replaces `fallbackdm`. + +--- + +## What fallbackdm Does *Not* Do + +* ❌ No X11 +* ❌ No Wayland +* ❌ No greeter UI +* ❌ No input filtering (by design) +* ❌ No Device Ownership Required: Unlike a real compositor, `fallbackdm` does not need to open `/dev/dri/cardX` or `/dev/input/event*` to do its job. It only needs the TTY. This minimizes the attack surface significantly. + +It only ensures **correct session, seat, and VT ownership**. + +--- + +## When You Need This + +You **do not need fallbackdm** if: + +* X11 or Wayland is already running +* A display manager (gdm, sddm, greetd, etc.) is active + +You **do need fallbackdm** if: + +* The system boots without a graphical stack +* Input devices (especially `uinput`) must not reach `getty` +* You rely on logind-correct VT behavior without a real compositor + +--- + +## Architecture Overview + +``` +fallbackdm + ├─ PAM session (class=greeter) + ├─ pam_systemd + ├─ libseat + │ └─ seatd or systemd-logind backend + └─ VT activation + KD_GRAPHICS +``` + +This mirrors what real display managers do — just without launching anything graphical. + +--- + +## PAM Configuration + +Create `/etc/pam.d/fallbackdm`: + +``` +session required pam_systemd.so class=greeter +``` + +This is mandatory. Without it, logind will not track the session. + +--- + +## systemd Service Example + +```ini +[Unit] +Description=Fallback Display Manager +After=systemd-user-sessions.service +ConditionPathExists=!/run/graphical-session-active + +[Service] +ExecStart=/usr/bin/fallbackdm +PAMName=fallbackdm +Restart=always + +[Install] +WantedBy=multi-user.target +``` + +> The condition is optional and can be replaced with more advanced logic later. + +--- + +## Relationship to Other Projects + +* **Display managers (gdm, sddm, greetd)** + Full login stacks with UI and session spawning. + +* **Greeters (gtkgreet, tuigreet)** + UI components launched *by* a display manager. + +* **fallbackdm** + A *headless*, compatibility-focused DM whose only job is to own the seat. + +--- + +## Future Ideas + +* Optional status output on the VT +* Signaling input-forwarding daemons (e.g. `vuinputd`) +* Conditional exit when a real session becomes active + +--- + +## License + +MIT \ No newline at end of file diff --git a/fallbackdm/systemd/pam.d_fallbackdm b/fallbackdm/systemd/pam.d_fallbackdm new file mode 100644 index 0000000..12db36c --- /dev/null +++ b/fallbackdm/systemd/pam.d_fallbackdm @@ -0,0 +1 @@ +session required pam_systemd.so class=greeter