From 8d1627ee42b01998b72c0dc85fdea4332a648371 Mon Sep 17 00:00:00 2001 From: Johannes Leupolz Date: Wed, 28 Jan 2026 20:52:33 +0000 Subject: [PATCH] Move fallbackdm into own repository. --- docs/DESIGN.md | 2 +- docs/USAGE.md | 4 +- fallbackdm/DESIGN.md | 144 --------------------------- fallbackdm/README.md | 147 ---------------------------- fallbackdm/systemd/pam.d_fallbackdm | 8 -- 5 files changed, 3 insertions(+), 302 deletions(-) delete mode 100644 fallbackdm/DESIGN.md delete mode 100644 fallbackdm/README.md delete mode 100644 fallbackdm/systemd/pam.d_fallbackdm diff --git a/docs/DESIGN.md b/docs/DESIGN.md index d528854..16bc56e 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -549,7 +549,7 @@ To further mitigate these risks, `vuinputd` could be extended to parse the host' ### **Decision** -`fallbackdm` is implemented as a **logind-managed fallback graphical session**. +`fallbackdm` is implemented as a **logind-managed fallback graphical session**. It is available at https://github.com/joleuger/fallbackdm. It runs only when **no other graphical session is active** on the seat and exists solely to: diff --git a/docs/USAGE.md b/docs/USAGE.md index ab940a0..f9b1c96 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -395,7 +395,7 @@ 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`. +A robust solution is to replace the kernel VT text console with a **DRM/KMS-based console** such as `kmscon`. This is very likely the solution that works natively with Fedora 44+ (see [phoronix.com](https://www.phoronix.com/news/Fedora-44-Considers-KMSCON)). #### How it helps @@ -451,7 +451,7 @@ This is done via direct VT ioctls (e.g. `KDSETMODE`), ensuring that: ### Solution 3: fallbackdm (Work in Progress) -`fallbackdm` is an experimental, lightweight **logind-integrated fallback display manager**. +`fallbackdm` is an experimental, lightweight **logind-integrated fallback display manager**. `fallbackdm` is available at https://github.com/joleuger/fallbackdm. #### Intended behavior diff --git a/fallbackdm/DESIGN.md b/fallbackdm/DESIGN.md deleted file mode 100644 index 3c157fb..0000000 --- a/fallbackdm/DESIGN.md +++ /dev/null @@ -1,144 +0,0 @@ -# DESIGN.md -## 1. Design Philosophy: Standardized Infrastructure - -The core design principle of `fallbackdm` is to **utilize established systemd interfaces** to solve the "Empty Seat" problem. - -In a modern Linux ecosystem, hardware resource arbitration has moved away from direct device manipulation by individual applications. `systemd-logind` now acts as the central arbiter for seats, sessions, and terminal states. - -`fallbackdm` is designed to be a "minimalist citizen" of this architecture. Instead of implementing a full graphical stack to "own" the hardware, it leverages the fact that the kernel and `logind` already provide a mechanism to mute VTs and protect input streams. By registering as a formal session controller, `fallbackdm` ensures the system remains in a "graphical-ready" state—silencing the legacy text console—without the overhead of an actual display server. - -## 2. The Mechanism: The `TakeControl` Handshake - -The critical functionality of `fallbackdm`—silencing the kernel terminal to prevent input leakage—is achieved through a single, standardized D-Bus handshake: `TakeControl`. - -This mechanism replaces manual `ioctl` calls. By invoking the `TakeControl` method on the `org.freedesktop.login1.Session` interface, `fallbackdm` triggers a privileged workflow inside `logind` that safely transitions the machine state. - -### 2.1 The Internal Workflow - -When `fallbackdm` calls `TakeControl`, it triggers the following verified code path within `systemd-logind` (as referenced in `logind-session.c`): - -1. **Permission Check:** `logind` verifies the caller owns the session. -2. **Controller Assignment:** `session_set_controller()` marks `fallbackdm` as the active display server. -3. **VT Preparation:** `logind` executes `session_prepare_vt()`, which performs the privileged operations: -* **Mute Input:** Calls `ioctl(vt, KDSKBMODE, K_OFF)`. This effectively disconnects the kernel console from the keyboard, preventing `getty` or the kernel from interpreting keystrokes. -* **Graphics Mode:** Calls `ioctl(vt, KDSETMODE, KD_GRAPHICS)`. This disables the blinking cursor and text rendering. -* **Signal Handling:** Configures the VT to send signals (like `SIGUSR1`) for switching, rather than automatically switching context. - - - -**Result:** `fallbackdm` achieves a "muted" state without ever needing to open a device file or possess `CAP_SYS_TTY_CONFIG` capabilities directly. - -## 3. Industry Precedent: The Standard Stack - -`fallbackdm` does not invent a new protocol; it isolates the infrastructure logic used by modern Linux desktops. To understand why `fallbackdm` works, we must look at how the **Wayland Native** stack (GDM and Mutter) handles seat ownership. - -### 3.1 Foundational Concepts: Seats, Sessions, and Leaders - -On a modern system, hardware access is governed by several distinct layers: - -* **The Seat (e.g., `seat0`):** A collection of hardware (GPU, Keyboard, Mouse). -* **The Session:** An instance of a user (or service) interacting with a seat. -* **The Session Leader:** The primary process responsible for the session. In a graphical world, this is the **Compositor** (Mutter). -* **The Display Manager (GDM):** A supervisor that manages the lifecycle of sessions. - -In the **Wayland Native** workflow, the compositor (Mutter) serves as the "Display Server." It talks directly to the kernel for graphics (DRM/KMS) and input (libinput). However, it does not "steal" these resources; it asks `systemd-logind` for permission. - -### 3.2 Technical Execution: PAM and the Handshake - -The transition from a text-based boot to a graphical environment follows a strict sequence. `fallbackdm` mimics the first half of this cycle: - -#### 1. Simplified Session Registration (The PAM Layer) - -Before a process can "Take Control" of a seat, a session must exist. GDM initiates this via a specialized, minimalist PAM stack (e.g., `gdm-launch-environment.pam`). - -Following the GDM precedent, `fallbackdm` uses a simplified PAM stack because a greeter/placeholder session **has no password and cannot be locked.** This removes the overhead of full `system-auth` account and password modules, focusing strictly on: - -* Setting up the environment (`pam_env.so`). -* Permitting the session entry (`pam_permit.so`). -* Registering the session with `logind` via `pam_systemd.so` as `class=greeter`. - -#### 2. Claiming the Seat (The D-Bus Layer) - -Once registered, the Session Leader (Mutter in a standard setup, `fallbackdm` in ours) must claim the seat. The process calls `TakeControl(force=true)` on its own Session object via D-Bus. `logind` validates that the caller is the registered leader and then performs the privileged "silencing" of the VT. - -### 3.3 Sequence & Implementation Mapping - -```mermaid -sequenceDiagram - participant P as PAM (Simplified Stack) - participant F as fallbackdm (Session Leader) - participant L as systemd-logind - participant K as Kernel (VT Layer) - - Note over P, L: 1. Registration (class=greeter) - P->>L: CreateSession (via pam_systemd) - L-->>P: Session Path / ID - - Note over F, L: 2. The Handshake (Wayland Native Style) - F->>L: D-Bus: TakeControl(force=true) - - Note over L: Validate Caller (session_set_controller) - - Note over L, K: 3. VT Preparation (session_prepare_vt) - L->>K: ioctl(vt, KDSKBMODE, K_OFF) - L->>K: ioctl(vt, KDSETMODE, KD_GRAPHICS) - - Note over K: Keyboard Muted / Console Silenced - - L-->>F: Method Return (Success) - Note over F: Hold Seat (Idle) - -``` - -#### Source References for Verification - -* **GDM (PAM):** `data/gdm-launch-environment.pam` — Demonstrates the minimalist "don't run full account/password stacks" approach for greeters. -* **GDM (PAM):** `src/daemon/gdm-session-worker.c` — Shows how the minimalist pam session is started via `gdm_session_worker_initialize_pam`. -* **systemd-logind:** `src/login/logind-session-dbus.c` — see `method_take_control()`. -* **systemd-logind:** `src/login/logind-session.c` — see `session_prepare_vt()` (where the `K_OFF` and `KD_GRAPHICS` ioctls live). -* **Mutter:** `src/backends/native/meta-launcher.c` — see `meta_launcher_new()` where the D-Bus proxy for the session is created and `TakeControl` is called. -* **GDM:** `daemon/gdm-manager.c` — see `set_up_greeter_session()` which coordinates the PAM transition. - -### 3.4 The Parallel - -* **Standard DM:** `PAM` → `Mutter` → **`TakeControl`** → *Open DRM/Input Devices* -* **fallbackdm:** `PAM` → `fallbackdm` → **`TakeControl`** → *Wait/Idle* - -By stopping after the `TakeControl` handshake, `fallbackdm` provides the exact same system-level protection as a full desktop environment with zero overhead and a vastly smaller attack surface. - - -## 4. Alignment with Systemd Guidelines - -The design strictly adheres to the *[Writing Display Managers](https://systemd.io/WRITING_DISPLAY_MANAGERS/)* specification provided by the systemd project. - -| Systemd Requirement | `fallbackdm` Implementation | -| --- | --- | -| **"Register via PAM"** | `fallbackdm` uses `pam_systemd.so` with `class=greeter` to register a valid session. | -| **"Take possession"** | We use the `TakeControl` D-Bus method to explicitly claim the seat. | -| **"Passive C API"** | We use `sd-login` (or equivalent D-Bus calls) to identify the seat, avoiding manual parsing of `/var/run` or `/proc`. | -| **"Minimal Porting"** | By offloading VT management to `logind`, we achieve the "Minimal porting" goal described in the docs, removing legacy ConsoleKit/ioctl code. | - -## 5. Security & Stability Implications - -### 5.1 Adherence to Least Privilege - -By using `logind` as a proxy for hardware configuration, `fallbackdm` avoids the need for: - -* `CAP_SYS_TTY_CONFIG`: No need to configure TTYs directly. -* Device Node Access: No need to open `/dev/ttyX` or `/dev/input/eventX`. -* Root Privileges: `fallbackdm` can run as a dedicated unprivileged system user, as `logind` validates the `TakeControl` request based on session ownership, not UID 0. - -### 5.2 Graceful Handover - -Because `fallbackdm` is a "polite" session controller: - -* When a real DM (like GDM) starts, it triggers a new session or requests the seat. -* `logind` manages the transition, and `fallbackdm` yields or exits based on standard D-Bus signals (`ReleaseSession`). -* This ensures no "input blips" where the keyboard reverts to text mode during the split-second transition between `fallbackdm` and a real compositor. - - -## 6. Summary - -The design of `fallbackdm` is not a workaround; it is a **canonical implementation of a headless systemd session controller**. - -By leveraging the `TakeControl` API, we utilize the exact mechanism built for this purpose, supported by the kernel and `systemd` developers, and battle-tested by GNOME and KDE. This ensures that the "Input Leakage" problem is solved at the infrastructure level, where it belongs. \ No newline at end of file diff --git a/fallbackdm/README.md b/fallbackdm/README.md deleted file mode 100644 index 1c7afca..0000000 --- a/fallbackdm/README.md +++ /dev/null @@ -1,147 +0,0 @@ -# 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 deleted file mode 100644 index 6edefd3..0000000 --- a/fallbackdm/systemd/pam.d_fallbackdm +++ /dev/null @@ -1,8 +0,0 @@ -# /etc/pam.d/fallbackdm -# https://man.archlinux.org/man/pam_systemd.8.en -# alternative class=lock-screen -# debug=yes -# https://github.com/GNOME/gdm/blob/main/daemon/gdm-local-display-factory.c -auth required pam_permit.so -account required pam_permit.so -session required pam_systemd.so class=greeter type=wayland \ No newline at end of file