KDE Plasma crashed. Not a gentle “the desktop is frozen” crash. A “switched to TTY, got nothing, hard reboot” crash. The kind where you hold the power button and have a brief conversation with yourself about whether your system is coming back.
It came back. Then I spent the rest of the afternoon learning why it crashed and discovering that my init system had opinions I didn’t know about.
The Scene
November 30, 2025. Running Gentoo with OpenRC — which I’d been doing for weeks, coming from years of systemd-based distros. KDE Plasma on bleeding-edge packages because that’s the Gentoo way. Things crash sometimes. Expected. Normal.
What wasn’t normal: complete terminal lockout. No TTY switch. No SSH. The system was running but unreachable in every way that mattered. Hard reboot was the only option.
After the reboot, I started reading logs. And found this, repeating constantly:
kf.solid.backends.udisks2: Failed enumerating UDisks2 objects:
"org.freedesktop.DBus.Error.Spawn.ExecFailed"
"Failed to execute program org.freedesktop.UDisks2: Permission denied"
KDE Solid — the hardware detection framework that handles USB drives, disk mounting, device notifications — was trying to talk to UDisks2. UDisks2 wasn’t answering. This error was firing constantly, on a loop, consuming resources and destabilizing Plasma until the whole desktop gave up.
“Permission denied.” That phrase sent me down the wrong path for twenty minutes, thinking it was a permissions problem. It wasn’t.
The Missing Services
rc-status
UDisks2 wasn’t in the output. Neither was Polkit. Neither was UPower.
Not “stopped.” Not “crashed.” Not listed at all. These services were installed — the binaries were on disk, the init scripts existed — but they weren’t enabled. Nobody had told OpenRC to run them at boot, so OpenRC very reasonably did not run them at boot.
On every systemd distro I’d ever used, these services were just… there. Running. I never thought about them because I never had to. They auto-started via D-Bus activation: an application requests org.freedesktop.UDisks2, systemd notices the request, starts the service automatically, and the application gets its response.
OpenRC doesn’t do that. D-Bus tries to activate the service, OpenRC says “that’s not my job,” and the application gets “Permission denied” instead of a running service.
systemd flow:
Application -> D-Bus -> systemd auto-starts service -> success
OpenRC flow:
Application -> D-Bus -> "start this" -> OpenRC says no -> Permission denied
Every KDE component that depends on UDisks2, Polkit, or UPower was getting permission errors. Constantly. Hundreds of them. Until Plasma’s event loop choked and the desktop died.
The error message said “Permission denied.” The actual problem was “service not running.” Two completely different things, wearing the same error code. Helpful.
The Fix (And The Fix That Wouldn’t Fix)
Started each service manually:
sudo rc-service udisks start
sudo rc-service polkit start
sudo rc-service upower start
Enabled them at boot:
sudo rc-update add udisks default
sudo rc-update add polkit default
sudo rc-update add upower default
UDisks started. UPower started. Polkit did not start.
* Starting polkit ...
* start-stop-daemon: fopen `/run/polkit-1/polkitd.pid': No such file or directory
Of course. /run/.
The /run/ Problem
Here’s something I knew intellectually but had never been bitten by: /run/ is a tmpfs filesystem. RAM-based. Volatile. It gets cleared on every reboot. Every directory in it, gone. Every PID file, gone. Every socket, gone.
Polkit needed /run/polkit-1/ to exist so it could write its PID file. That directory was gone. Destroyed by the reboot. And nothing was recreating it because — say it with me — OpenRC doesn’t auto-create runtime directories unless you tell it to.
systemd handles this through RuntimeDirectory= in unit files. The init system creates the directory before starting the service, every boot, automatically. OpenRC expects you to handle it in the init script.
The fix:
# /etc/init.d/polkit
start_pre() {
checkpath --directory --owner root:root --mode 0755 /run/polkit-1
}
start_pre() runs before the service starts. checkpath creates the directory if it doesn’t exist. Now every boot, the init script creates /run/polkit-1/, then starts Polkit, and the PID file has somewhere to live.
One function. Four lines. Thirty minutes of debugging a “file not found” error on a directory that exists half the time.
The Bluetooth Detour
While I was in the logs fixing things, I noticed this in dmesg:
bluetooth hci0: Direct firmware load for rtl_bt/rtl8761bu_fw.bin failed with error -2
Missing Realtek Bluetooth firmware. Not related to the Plasma crash, but broken nonetheless. The kind of thing you’d never notice until you try to pair a Bluetooth device and wonder why nothing shows up.
sudo emerge -av sys-kernel/linux-firmware
Note: it’s sys-kernel/linux-firmware, not sys-firmware/linux-firmware. Gentoo package naming. The category matters. Tab completion will save your life.
After the firmware install:
sudo rc-service bluetooth start
sudo rc-update add bluetooth default
Bluetooth worked. One more service I hadn’t known I needed to explicitly enable.
The Wireplumber and UPower Crashes
The log reading wasn’t done. Further down:
wireplumber[4065]: segfault at 8 ... in libglib-2.0.so.0.8400.4
upowerd[3448]: trap int3 ... in libglib-2.0.so.0.8400.4
Both crashing in glib. The int3 is a CPU debug breakpoint — either a failed assertion in the code or memory corruption that happened to hit a byte that looks like a breakpoint instruction. Neither explanation is good news.
Rebuilt glib and the affected services:
sudo glib-compile-schemas /usr/share/glib-2.0/schemas/
sudo gio-querymodules /usr/lib64/gio/modules/
sudo emerge -av1 dev-libs/glib sys-power/upower media-video/wireplumber
Wireplumber stabilized. Audio came back clean. UPower still crashes occasionally, but on a desktop that’s plugged into the wall, UPower is mainly for battery management on laptops. Its crashes are annoying, not catastrophic. I’ll fight that one another day.
The Complete Service List
After an afternoon of enabling services I didn’t know I needed, here’s what my default runlevel looks like:
rc-update show default | grep -E "dbus|elogind|polkit|udisks|upower|snapper|bluetooth"
dbus — Everything needs this. The inter-process communication backbone. Without it, nothing talks to anything.
elogind — Session management. Handles user sessions, seat management, and the stuff that makes multi-user desktops work. The systemd-logind equivalent for non-systemd systems.
polkit — Authentication and authorization. When KDE asks “do you want to mount this USB drive?” Polkit is what verifies you’re allowed to say yes.
udisks — Disk management. KDE Solid talks to this constantly. Without it, Plasma doesn’t know your drives exist and throws errors until it crashes. Ask me how I know.
upower — Power management. Technically optional on a desktop. Practically required if you want KDE to stop complaining about power state queries.
snapperd — Btrfs snapshot daemon. Not related to the crash, but critical for my sanity. Automatic pre/post-emerge snapshots.
bluetooth — The Bluetooth stack. Needs firmware AND the service. Both.
Seven services that systemd would have managed invisibly. Seven services I had to discover, enable, and in one case fix the init script for. OpenRC made me learn what each one does. Whether that’s a feature or a bug depends on how you feel about learning things at 4 PM on a Saturday when your desktop won’t stop crashing.
The Commands I Now Know By Heart
# Check what's running
rc-status
# Check a specific service
rc-service <service> status
# Start a service
sudo rc-service <service> start
# Enable at boot
sudo rc-update add <service> default
# List all enabled services
rc-update show default
The muscle memory from systemd is strong. I still type systemctl status at least once a week. The command doesn’t exist. The error message is brutal in its simplicity: bash: systemctl: command not found. Every time, a tiny reminder that I chose this.
Never use on OpenRC: systemctl, journalctl, hostnamectl, timedatectl, or any systemd command. They don’t exist. The shell will judge you.
The Actual Lesson
I’d been running Gentoo with OpenRC for weeks. Coming from Fedora, Ubuntu, openSUSE — all systemd. The muscle memory was wrong, but worse, the mental model was wrong.
systemd is automatic. Services auto-start via socket activation, D-Bus activation, dependency resolution. The system figures out what needs to run and runs it. You configure the desired state; systemd achieves it.
OpenRC is explicit. Nothing starts unless you tell it to. Nothing creates runtime directories unless you add start_pre(). Nothing auto-activates on D-Bus request. You are responsible for understanding every service your desktop needs and enabling each one manually.
Neither philosophy is better. systemd’s automation means things just work — until they don’t, and you have no idea why because the auto-start chain is five units deep and the journal is 40,000 lines long. OpenRC’s explicitness means you understand exactly what’s running — after you’ve spent an afternoon discovering what you forgot to enable.
The “Permission denied” error was the most valuable crash I’ve had on Gentoo. It forced me to understand the D-Bus service activation model, the difference between init system philosophies, the volatile nature of /run/, and the complete list of services KDE needs to not fall over.
On systemd, I used KDE for years without knowing UDisks2 existed. On OpenRC, I know exactly what it does, why it matters, and what happens when it’s not running. That knowledge is permanent. The afternoon of debugging was temporary.
OpenRC made me earn my desktop. And I know every service that keeps it running, which is more than I could say after five years on systemd.
That’s the whole point.