Move fallbackdm into own repository.

This commit is contained in:
Johannes Leupolz 2026-01-28 20:52:33 +00:00
parent e5bb81f86e
commit 8d1627ee42
5 changed files with 3 additions and 302 deletions

View file

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

View file

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

View file

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

View file

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

View file

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