D-Bus Phase 3/4: upgrade sessiond, services, add StatusNotifierWatcher, consolidate configs

- redbear-sessiond: add Manager.Inhibit (pipe FD), CanPowerOff/CanReboot/
  CanSuspend/CanHibernate/CanHybridSleep/CanSleep (return na), PowerOff/
  Reboot/Suspend stubs, GetSessionByPID, ListUsers, ListSeats,
  ListInhibitors, ActivateSession/LockSession/UnlockSession/TerminateSession
- redbear-sessiond: add Session SetIdleHint, SetLockedHint, SetType,
  Terminate methods; wire PauseDevice/ResumeDevice/Lock/Unlock signal
  emission via SignalEmitter injection; add dynamic device enumeration
  scanning /scheme/drm/card* and /dev/input/event* at startup
- redbear-sessiond: replace infinite pending() with stoppable shutdown
  via tokio watch channel + control socket shutdown command
- redbear-upower: add Changed signal emission with 30s periodic polling
  and power state snapshot comparison
- redbear-notifications: add ActionInvoked signal, expand capabilities
  to body + body-markup + actions
- redbear-polkit, redbear-udisks: replace pending() with stoppable
  shutdown via signal handling + watch channel
- Add redbear-statusnotifierwatcher: new session bus service implementing
  org.freedesktop.StatusNotifierWatcher for KDE system tray
- Add D-Bus activation file for StatusNotifierWatcher
- KWin session.cpp: try LogindSession before NoopSession fallback
- Consolidate config profiles: remove obsolete redbear-desktop, redbear-kde,
  redbear-live-*, redbear-minimal-*, redbear-wayland configs; simplify
  to three supported targets (redbear-full, redbear-mini, redbear-grub)
- Update DBUS-INTEGRATION-PLAN.md and DESKTOP-STACK-CURRENT-STATUS.md
  with Phase 3/4 fragility assessment, KWin readiness matrix, and
  completeness gap analysis
This commit is contained in:
2026-04-25 12:01:25 +01:00
parent 0bd58c912f
commit dc69317ddf
55 changed files with 1535 additions and 1932 deletions
+13 -18
View File
@@ -73,7 +73,7 @@ redox-master/
| Linux driver compat | `docs/04-LINUX-DRIVER-COMPAT.md` | linux-kpi + redox-driver-sys architecture (**GPU and Wi-Fi only — not USB**) |
| Build system internals | `src/bin/repo.rs`, `src/lib.rs`, `mk/repo.mk` | Cookbook tool in Rust |
| Cross-toolchain setup | `mk/prefix.mk`, `prefix/x86_64-unknown-redox/` | Downloads Clang/LLVM toolchain |
| Display/session surface | `config/redbear-full.toml` | Active desktop/graphics compile surface; `redbear-kde` references elsewhere are historical/staging and not supported compile targets |
| Display/session surface | `config/redbear-full.toml` | Active desktop/graphics compile surface |
| GPU/graphics stack | `recipes/libs/mesa/` | OSMesa + LLVMpipe (software only) |
| GPU hardware drivers | `local/recipes/gpu/redox-drm/source/` | AMD + Intel DRM/KMS via redox-driver-sys |
| D-Bus integration | `local/docs/DBUS-INTEGRATION-PLAN.md` | Architecture, gap analysis, phased implementation for KDE Plasma D-Bus |
@@ -93,27 +93,22 @@ echo 'PODMAN_BUILD?=1' > .config # Podman container build
# Build Red Bear OS
# Supported compile targets:
# redbear-full desktop/graphics harddrive.img
# redbear-live full desktop live ISO (greeter + text fallback)
# redbear-live-mini text-only mini live ISO for recovery/bare metal
# redbear-full-grub desktop target with GRUB boot manager
# redbear-grub-live-full full desktop live ISO with GRUB
# redbear-grub-live-mini text-only mini live ISO with GRUB
# Desktop/graphics targets: redbear-full, redbear-full-grub, redbear-live, redbear-grub-live-full
# Text-only targets: redbear-live-mini, redbear-grub-live-mini
# NOTE: redbear-kde and redbear-live-full are historical/staging, not supported compile targets
make all CONFIG_NAME=redbear-full # Desktop/graphics-enabled target → harddrive.img
make live CONFIG_NAME=redbear-live # Full desktop live ISO (greeter + text fallback)
make live CONFIG_NAME=redbear-live-mini # Text-only mini live ISO for recovery/bare metal
make all CONFIG_NAME=redbear-full-grub # Desktop target with GRUB boot manager → harddrive.img
make live CONFIG_NAME=redbear-grub-live-full # Full desktop live ISO with GRUB
make live CONFIG_NAME=redbear-grub-live-mini # Text-only mini live ISO with GRUB
# redbear-full desktop/graphics target (harddrive.img or live ISO)
# redbear-mini text-only console/recovery target (harddrive.img or live ISO)
# redbear-grub text-only with GRUB boot manager (live ISO)
# Desktop/graphics target: redbear-full
# Text-only targets: redbear-mini, redbear-grub
make all CONFIG_NAME=redbear-full # Desktop/graphics target → harddrive.img
make all CONFIG_NAME=redbear-mini # Text-only target → harddrive.img
make live CONFIG_NAME=redbear-full # Full desktop live ISO
make live CONFIG_NAME=redbear-mini # Text-only mini live ISO
make live CONFIG_NAME=redbear-grub # Text-only mini live ISO with GRUB
CI=1 make all CONFIG_NAME=redbear-mini # CI mode (disables TUI, for non-interactive)
# Run
make qemu # Boot in QEMU
make qemu QEMUFLAGS="-m 4G" # With more RAM
make live # Build live ISO for real bare metal → redbear-live.iso
make live # Build live ISO for real bare metal
# Single recipe
./target/release/repo cook recipes/libs/mesa # Build one recipe
@@ -136,7 +131,7 @@ make all
→ mk/fstools.mk (build cookbook repo binary + fstools)
→ mk/repo.mk (repo cook --filesystem=config/*.toml)
→ For each recipe: fetch source → apply patches → build → stage into sysroot
→ mk/disk.mk (create filesystem.img, harddrive.img, redbear-live.iso)
→ mk/disk.mk (create filesystem.img, harddrive.img, redbear-live.iso or harddrive.img)
→ redoxfs-mkfs → redox_installer → bootloader embedding
```
+11 -13
View File
@@ -78,7 +78,7 @@ Red Bear OS now treats AMD and Intel machines as equal-priority hardware targets
language in historical integration notes should be read as earlier sequencing context, not as the
current platform policy.
The tracked desktop-capable target surface is now `redbear-full` / `redbear-live`, and
The tracked desktop-capable target surface is `redbear-full`, and
runtime support claims remain evidence-qualified until compositor/session proof is stronger.
## Historical Phase Snapshot
@@ -176,23 +176,21 @@ Requires a Linux x86_64 host with Rust nightly, QEMU, and standard build tools.
```bash
# Non-live (harddrive.img for QEMU / development)
make all CONFIG_NAME=redbear-full # Tracked desktop-capable target
make all CONFIG_NAME=redbear-full-grub # Desktop target with GRUB boot manager
make all CONFIG_NAME=redbear-full # Desktop/graphics target
make all CONFIG_NAME=redbear-mini # Text-only console/recovery target
# Live ISO (for real bare metal)
make live CONFIG_NAME=redbear-live # Full desktop live ISO (greeter + text fallback)
make live CONFIG_NAME=redbear-live-mini # Text-only mini live ISO for recovery (~384 MiB)
make live CONFIG_NAME=redbear-grub-live-full # Full desktop live ISO with GRUB
make live CONFIG_NAME=redbear-grub-live-mini # Text-only mini live ISO with GRUB
make live CONFIG_NAME=redbear-full # Full desktop live ISO
make live CONFIG_NAME=redbear-mini # Text-only mini live ISO for recovery
make live CONFIG_NAME=redbear-grub # Text-only mini live ISO with GRUB
# Or use the helper script
scripts/build-iso.sh redbear-live # Full desktop live ISO
scripts/build-iso.sh redbear-live-mini # Text-only mini
scripts/build-iso.sh redbear-grub-live-full # Full desktop + GRUB
scripts/build-iso.sh redbear-grub-live-mini # Text-only + GRUB
scripts/build-iso.sh redbear-full # Full desktop live ISO
scripts/build-iso.sh redbear-mini # Text-only mini (default)
scripts/build-iso.sh redbear-grub # Text-only + GRUB
# QEMU (uses harddrive.img, not live ISO)
make qemu CONFIG_NAME=redbear-full # Boot the tracked desktop-capable target in QEMU
make qemu CONFIG_NAME=redbear-full # Boot the desktop target in QEMU
```
Live `.iso` outputs are for real bare-metal boot and install workflows. They are not the virtual/QEMU target surface; use `make qemu` and `harddrive.img`-based flows for virtualization.
@@ -202,7 +200,7 @@ Live `.iso` outputs are for real bare-metal boot and install workflows. They are
Red Bear OS can use GNU GRUB as an alternative boot manager with Linux-compatible CLI:
```bash
make all CONFIG_NAME=redbear-full-grub # Build broader integration slice with GRUB chainload
make all CONFIG_NAME=redbear-grub # Build text-only target with GRUB chainload
./local/scripts/grub-install --target=x86_64-efi --disk-image=build/x86_64/harddrive.img
./local/scripts/grub-mkconfig -o local/recipes/core/grub/grub.cfg
```
-246
View File
@@ -1,246 +0,0 @@
# Red Bear OS Desktop Configuration
# Supplementary integration profile beneath the tracked KWin target
#
# Build: make all CONFIG_NAME=redbear-desktop
# Live: make live CONFIG_NAME=redbear-desktop
#
# This profile remains available for non-KDE-specific integration work.
include = ["desktop.toml", "redbear-legacy-base.toml", "redbear-legacy-desktop.toml", "redbear-device-services.toml", "redbear-netctl.toml"]
[general]
filesystem_size = 10240
[packages]
# Red Bear OS branding (os-release, hostname, motd)
redbear-release = {}
# Native Redox PCI/USB listing tools (lspci, lsusb)
redbear-hwutils = {}
# Redox-native netctl compatibility command
redbear-netctl = {}
redbear-netctl-console = {}
# Native network reporting and connect-scan tools
redbear-netstat = {}
redbear-traceroute = {}
redbear-mtr = {}
redbear-nmap = {}
# Firmware loading + Wi-Fi control plane
redbear-firmware = {}
firmware-loader = {}
redbear-wifictl = {}
# Input/runtime service prerequisites
evdevd = {}
udev-shim = {}
# Diagnostic tool
redbear-info = {}
# Process monitor
htop = {}
# IOMMU validation surface
iommu = {}
# D-Bus IPC and session services
dbus = {}
redbear-sessiond = {}
redbear-dbus-services = {}
redbear-notifications = {}
redbear-upower = {}
redbear-udisks = {}
redbear-polkit = {}
# Terminal file manager (Midnight Commander port)
mc = {}
# Package builder (cub -S/-B/-G CLI)
cub = {}
# Core Red Bear umbrella package
redbear-meta = {}
# ── Desktop services (replace legacy desktop-minimal init scripts) ───────────
[[files]]
path = "/usr/lib/init.d/20_display.service"
data = """
[unit]
description = "Display session service"
requires_weak = [
]
[service]
cmd = "ion"
args = ["-c", "true"]
envs = { VT = "3" }
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/30_console.service"
data = """
[unit]
description = "Console terminals"
requires_weak = [
"29_activate_console.service",
]
[service]
cmd = "getty"
args = ["2"]
type = "oneshot_async"
respawn = true
"""
[[files]]
path = "/usr/lib/init.d/29_activate_console.service"
data = """
[unit]
description = "Activate display VT"
requires_weak = [
"20_display.service",
]
[service]
cmd = "inputd"
args = ["-A", "3"]
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/31_debug_console.service"
data = """
[unit]
description = "Debug console"
requires_weak = [
"29_activate_console.service",
]
[service]
cmd = "getty"
args = ["/scheme/debug/no-preserve", "-J"]
type = "oneshot_async"
respawn = true
"""
[[files]]
path = "/usr/lib/init.d/13_iommu.service"
data = """
[unit]
description = "IOMMU DMA remapping daemon"
requires_weak = [
"00_pcid-spawner.service",
]
[service]
cmd = "/usr/bin/iommu"
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/12_dbus.service"
data = """
[unit]
description = "D-Bus system bus"
requires_weak = [
"12_boot-late.target",
]
[service]
cmd = "ion"
args = [
"-c",
"mkdir -p /var/lib/dbus /run/dbus; rm -f /run/dbus/pid; dbus-uuidgen --ensure; dbus-daemon --system",
]
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/13_redbear-sessiond.service"
data = """
[unit]
description = "Red Bear session broker (org.freedesktop.login1)"
requires_weak = [
"12_dbus.service",
]
[service]
cmd = "ion"
args = [
"-c",
"redbear-sessiond",
]
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/14_redbear-upower.service"
data = """
[unit]
description = "UPower D-Bus service (org.freedesktop.UPower)"
requires_weak = [
"12_dbus.service",
]
[service]
cmd = "ion"
args = [
"-c",
"redbear-upower",
]
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/14_redbear-udisks.service"
data = """
[unit]
description = "UDisks2 D-Bus service (org.freedesktop.UDisks2)"
requires_weak = [
"12_dbus.service",
]
[service]
cmd = "ion"
args = [
"-c",
"redbear-udisks",
]
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/14_redbear-polkit.service"
data = """
[unit]
description = "PolicyKit1 D-Bus service (org.freedesktop.PolicyKit1)"
requires_weak = [
"12_dbus.service",
]
[service]
cmd = "ion"
args = [
"-c",
"redbear-polkit",
]
type = "oneshot_async"
"""
[[files]]
path = "/var/lib/dbus"
data = ""
directory = true
mode = 0o755
[[files]]
path = "/run/dbus"
data = ""
directory = true
mode = 0o755
-10
View File
@@ -1,10 +0,0 @@
# Red Bear OS Full Configuration with GRUB Boot Manager
# Desktop + RBOS branding + GRUB chainload
#
# Build: make all CONFIG_NAME=redbear-full-grub
#
# This config adds GRUB as boot manager (Phase 2 installer-native).
# GRUB presents a menu and chainloads the Redox bootloader.
# Requires: efi_partition_size >= 8 (16 recommended for GRUB + Redox bootloader + margin)
include = ["redbear-full.toml", "redbear-grub.toml"]
+157 -357
View File
@@ -1,15 +1,13 @@
# Red Bear OS Full Configuration
# Primary desktop/session target: Wayland + KWin/KDE session surface on Red Bear OS.
# Build: make all CONFIG_NAME=redbear-full
# Live: make live CONFIG_NAME=redbear-live-full # bare-metal live ISO only
# Desktop/graphics ISO for bare metal and QEMU.
#
# This is the only active full desktop target. Wayland and KDE package/runtime surfaces are folded
# here instead of being split across redbear-wayland and redbear-kde.
# GPU driver stack ships through redbear-meta for this profile.
# redox-drm now carries the runtime/package dependency on the AMD backend library.
# Runtime/session claims still remain evidence-qualified until compositor/session proof is strong.
# Build: make live CONFIG_NAME=redbear-full
# QEMU: make all CONFIG_NAME=redbear-full && make qemu
#
# Extends redbear-mini with the full desktop/graphics stack:
# Wayland, Qt6, KF6, KWin, Mesa, DRM drivers, firmware, greeter.
include = ["desktop.toml", "redbear-legacy-base.toml", "redbear-legacy-desktop.toml", "redbear-device-services.toml", "redbear-netctl.toml", "redbear-greeter-services.toml"]
include = ["redbear-mini.toml"]
[general]
filesystem_size = 4096
@@ -23,73 +21,20 @@ home = "/nonexistent"
shell = "/usr/bin/ion"
[packages]
# Red Bear OS branding (os-release, hostname, motd)
redbear-release = {}
# Exclude inherited legacy desktop packages from the tracked KWin/Wayland target
orbdata = "ignore"
orbital = "ignore"
orbterm = "ignore"
orbutils = "ignore"
cosmic-edit = "ignore"
cosmic-files = "ignore"
cosmic-icons = "ignore"
cosmic-term = "ignore"
# Native Redox PCI/USB listing tools (lspci, lsusb)
redbear-hwutils = {}
# Redox-native netctl compatibility command
redbear-netctl = {}
redbear-netctl-console = {}
# Native network reporting and connect-scan tools
redbear-netstat = {}
redbear-traceroute = {}
redbear-mtr = {}
redbear-nmap = {}
# Package builder / recipe utility
cub = {}
# Terminal file manager (Midnight Commander port)
mc = {}
# ext4 filesystem support (our custom port)
ext4d = {}
# Firmware loading + Wi-Fi control plane
# Firmware loading
redbear-firmware = {}
firmware-loader = {}
redbear-wifictl = {}
# Input layer
evdevd = {}
udev-shim = {}
# Desktop/session plumbing
dbus = {}
redbear-sessiond = {}
redbear-dbus-services = {}
redbear-notifications = {}
redbear-upower = {}
redbear-udisks = {}
redbear-polkit = {}
# IOMMU DMA remapping daemon
iommu = {}
# Diagnostic tool
redbear-info = {}
# Process monitor
htop = {}
# GPU/graphics stack
redox-drm = {}
mesa = {}
libdrm = {}
# Wayland protocol
libwayland = {}
wayland-protocols = {}
# Keyboard support
# Keyboard/input
libxkbcommon = {}
xkeyboard-config = {}
libevdev = {}
@@ -105,15 +50,13 @@ qtsvg = {}
qtwayland = {}
qt6-wayland-smoke = {}
# KF6 Frameworks — Tier 1
# KF6 Frameworks
kf6-extra-cmake-modules = {}
kf6-kcoreaddons = {}
kf6-kconfig = {}
kf6-ki18n = {}
kf6-kcolorscheme = {}
kf6-kauth = {}
# KF6 Frameworks — KWin session-surface chain
kf6-kwindowsystem = {}
kf6-knotifications = {}
kf6-kconfigwidgets = {}
@@ -133,24 +76,50 @@ kdecoration = {}
# KWin Wayland compositor
kwin = {}
# Graphics
redox-drm = {}
mesa = {}
libdrm = {}
# Greeter/login stack
redbear-authd = {}
redbear-session-launch = {}
redbear-greeter = {}
# Core Red Bear umbrella package
redbear-meta = {}
# Firmware directory for AMD/Intel GPU blobs
# Desktop fonts and icons
dejavu = {}
freefont = {}
hicolor-icon-theme = {}
pop-icon-theme = {}
# Suppress legacy desktop packages
orbdata = "ignore"
orbital = "ignore"
orbterm = "ignore"
orbutils = "ignore"
cosmic-edit = "ignore"
cosmic-files = "ignore"
cosmic-icons = "ignore"
cosmic-term = "ignore"
[[files]]
path = "/lib/firmware/amdgpu"
data = ""
directory = true
mode = 0o755
# Greeter/login service wiring now lives in config/redbear-greeter-services.toml, which is
# included above so the full desktop target has a single source of truth for display/fallback
# session services.
[[files]]
path = "/usr/lib/fonts"
data = "/usr/share/fonts"
symlink = true
[[files]]
path = "/usr/lib/init.d/05_boot-essential.target"
data = """
[unit]
description = "Boot essential services target"
requires_weak = [
"00_base.target",
]
"""
[[files]]
path = "/usr/lib/init.d/13_iommu.service"
@@ -185,7 +154,6 @@ args = [
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/13_redbear-sessiond.service"
data = """
@@ -275,293 +243,125 @@ type = "oneshot_async"
"""
[[files]]
path = "/var/lib/dbus"
data = ""
directory = true
mode = 0o755
[[files]]
path = "/run/dbus"
data = ""
directory = true
mode = 0o755
[[files]]
path = "/usr/lib/fonts"
data = "/usr/share/fonts"
symlink = true
[[files]]
path = "/usr/bin/redbear-validation-session"
mode = 0o755
path = "/usr/lib/init.d/19_redbear-authd.service"
data = """
#!/usr/bin/env sh
[unit]
description = "Red Bear authentication daemon"
requires_weak = [
"12_dbus.service",
]
export DISPLAY=""
export HOME="${HOME:-/home/root}"
export USER="${USER:-root}"
export LOGNAME="${LOGNAME:-$USER}"
export WAYLAND_DISPLAY="${WAYLAND_DISPLAY:-wayland-0}"
export XDG_SESSION_TYPE=wayland
export LIBSEAT_BACKEND=seatd
export SEATD_SOCK=/run/seatd.sock
export QT_PLUGIN_PATH=/usr/plugins
export QT_QPA_PLATFORM_PLUGIN_PATH=/usr/plugins/platforms
export QML2_IMPORT_PATH=/usr/qml
export RUST_BACKTRACE=full
export RUST_LOG=debug
export XCURSOR_THEME=Pop
export XKB_CONFIG_ROOT=/usr/share/X11/xkb
if [ -z "${XDG_RUNTIME_DIR:-}" ]; then
export XDG_RUNTIME_DIR="/tmp/run/user/$(id -u)"
fi
mkdir -p "$XDG_RUNTIME_DIR"
chmod 700 "$XDG_RUNTIME_DIR" 2>/dev/null || true
wait_for_path() {
target="$1"
attempts=0
while [ "$attempts" -lt 30 ]; do
if [ -e "$target" ]; then
return 0
fi
attempts=$((attempts + 1))
sleep 1
done
return 1
}
if ! wait_for_path /run/dbus/system_bus_socket; then
echo "system D-Bus socket did not appear" >&2
exit 1
fi
sleep 2
wait_for_wayland_socket() {
socket_path="$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"
attempts=0
while [ "$attempts" -lt 30 ]; do
if [ -e "$socket_path" ]; then
return 0
fi
if ! kill -0 "$kwin_pid" 2>/scheme/null; then
return 1
fi
attempts=$((attempts + 1))
sleep 1
done
return 1
}
if [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]; then
eval "$(dbus-launch --sh-syntax)"
fi
validation_request="/run/redbear-kde-session.validation-request"
validation_success="/run/redbear-kde-session.validation-success"
if [ -f "$validation_request" ]; then
{
echo "user=$USER"
echo "runtime=$XDG_RUNTIME_DIR"
echo "wayland=$WAYLAND_DISPLAY"
} > "$validation_success"
rm -f "$validation_request"
exit 0
fi
dbus-update-activation-environment \
DBUS_SESSION_BUS_ADDRESS \
DBUS_SESSION_BUS_PID \
WAYLAND_DISPLAY \
XDG_SESSION_TYPE \
XDG_RUNTIME_DIR \
DISPLAY \
HOME \
USER
if [ -d /usr/share/glib-2.0/schemas ]; then
glib-compile-schemas /usr/share/glib-2.0/schemas/
fi
if [ -n "$KWIN_DRM_DEVICES" ]; then
kwin_wayland_wrapper --drm &
else
echo "redbear-validation-session: using virtual KWin backend (set KWIN_DRM_DEVICES to enable DRM)" >&2
kwin_wayland_wrapper --virtual &
fi
kwin_pid=$!
if ! wait_for_wayland_socket; then
echo "kwin_wayland failed to expose $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" >&2
exit 1
fi
/usr/bin/wayland-session
smoke_exit=$?
echo "wayland-session exited with code $smoke_exit"
# 60-second KWin survival verdict
survival_ok="$HOME/.kwin-60s-survival.ok"
survival_err="$HOME/.kwin-60s-survival.err"
rm -f "$survival_ok" "$survival_err"
elapsed=0
while kill -0 "$kwin_pid" 2>/dev/null; do
if [ "$elapsed" -ge 60 ]; then
echo "KWin survived 60 seconds (pid=$kwin_pid)" > "$survival_ok"
break
fi
sleep 1
elapsed=$((elapsed + 1))
done
if [ ! -f "$survival_ok" ]; then
echo "KWin died before 60 seconds (pid=$kwin_pid, elapsed=${elapsed}s)" > "$survival_err"
fi
echo "redbear-validation-session: complete (smoke_exit=$smoke_exit, survival=$([ -f "$survival_ok" ] && echo ok || echo failed))"
[service]
cmd = "ion"
args = [
"-c",
"redbear-authd",
]
type = "oneshot_async"
"""
[[files]]
path = "/usr/bin/wayland-session"
mode = 0o755
path = "/usr/lib/init.d/20_greeter.service"
data = """
#!/usr/bin/env ion
[unit]
description = "Red Bear greeter service"
requires_weak = [
"12_dbus.service",
"13_redbear-sessiond.service",
"13_seatd.service",
"19_redbear-authd.service",
]
printenv
let session_started = "$HOME/.wayland-session.started"
rm -f $session_started
echo "started" > $session_started
export QT_PLUGIN_PATH=/usr/plugins
export QT_QPA_PLATFORM_PLUGIN_PATH=/usr/plugins/platforms
export QML2_IMPORT_PATH=/usr/qml
let smoke_ok = "$HOME/.qt6-wayland-smoke.ok"
let smoke_err = "$HOME/.qt6-wayland-smoke.err"
let smoke_log = "$HOME/.qt6-wayland-smoke.log"
let bootstrap_ok = "$HOME/.qt6-bootstrap-minimal.ok"
let bootstrap_log = "$HOME/.qt6-bootstrap-minimal.log"
let plugin_ok = "$HOME/.qt6-plugin-minimal.ok"
let plugin_err = "$HOME/.qt6-plugin-minimal.err"
let plugin_log = "$HOME/.qt6-plugin-minimal.log"
let smoke_minimal_ok = "$HOME/.qt6-wayland-smoke-minimal.ok"
let smoke_offscreen_ok = "$HOME/.qt6-wayland-smoke-offscreen.ok"
let smoke_wayland_ok = "$HOME/.qt6-wayland-smoke-wayland.ok"
let smoke_minimal_log = "$HOME/.qt6-wayland-smoke-minimal.log"
let smoke_offscreen_log = "$HOME/.qt6-wayland-smoke-offscreen.log"
let smoke_wayland_log = "$HOME/.qt6-wayland-smoke-wayland.log"
rm -f $smoke_ok $smoke_err
rm -f $smoke_log
rm -f $bootstrap_ok $bootstrap_log
rm -f $plugin_ok $plugin_err $plugin_log
rm -f $smoke_minimal_ok $smoke_offscreen_ok $smoke_wayland_ok
rm -f $smoke_minimal_log $smoke_offscreen_log $smoke_wayland_log
if which qt6-wayland-smoke >/scheme/null
if env LD_DEBUG=all QT_DEBUG_PLUGINS=1 QT_QPA_PLATFORM=minimal qt6-bootstrap-check > $bootstrap_log ^> $bootstrap_log
touch $bootstrap_ok
else
if test -f $bootstrap_log
cat $bootstrap_log
end
echo "qt6-bootstrap-check minimal failed; see $bootstrap_log" > $smoke_err
end
if env LD_DEBUG=all QT_DEBUG_PLUGINS=1 QT_PLUGIN_PATH=/usr/plugins QT_QPA_PLATFORM_PLUGIN_PATH=/usr/plugins/platforms qt6-plugin-check /usr/plugins/platforms/libqminimal.so > $plugin_log ^> $plugin_log
touch $plugin_ok
else
if test -f $plugin_log
cat $plugin_log
end
echo "qt6-plugin-check failed; see $plugin_log" > $plugin_err
echo "qt6-plugin-check failed; see $plugin_log" > $smoke_err
end
if env QT_DEBUG_PLUGINS=1 QT_QPA_PLATFORM=minimal qt6-wayland-smoke > $smoke_minimal_log ^> $smoke_minimal_log
touch $smoke_minimal_ok
else
echo "qt6-wayland-smoke minimal failed; see $smoke_minimal_log" > $smoke_err
end
if env QT_DEBUG_PLUGINS=1 QT_QPA_PLATFORM=offscreen qt6-wayland-smoke > $smoke_offscreen_log ^> $smoke_offscreen_log
touch $smoke_offscreen_ok
else
echo "qt6-wayland-smoke offscreen failed; see $smoke_offscreen_log" > $smoke_err
end
if env QT_DEBUG_PLUGINS=1 QT_QPA_PLATFORM=wayland qt6-wayland-smoke > $smoke_wayland_log ^> $smoke_wayland_log
touch $smoke_wayland_ok
touch $smoke_ok
else
echo "qt6-wayland-smoke wayland failed; see $smoke_wayland_log" > $smoke_err
end
end
[service]
cmd = "/usr/bin/redbear-greeterd"
envs = { VT = "3", REDBEAR_GREETER_USER = "greeter" }
type = "oneshot_async"
"""
[[files]]
path = "/usr/bin/redbear-kde-session"
mode = 0o755
path = "/usr/lib/init.d/29_activate_console.service"
data = """
#!/usr/bin/sh
[unit]
description = "Activate fallback console VT"
requires_weak = [
"05_boot-essential.target",
]
export DISPLAY=""
export WAYLAND_DISPLAY="${WAYLAND_DISPLAY:-wayland-0}"
export XDG_SESSION_TYPE=wayland
export KDE_FULL_SESSION=true
export XDG_CURRENT_DESKTOP=KDE
export LIBSEAT_BACKEND=seatd
export SEATD_SOCK=/run/seatd.sock
export QT_PLUGIN_PATH="${QT_PLUGIN_PATH:-/usr/plugins}"
export QT_QPA_PLATFORM_PLUGIN_PATH="${QT_QPA_PLATFORM_PLUGIN_PATH:-/usr/plugins/platforms}"
export QML2_IMPORT_PATH="${QML2_IMPORT_PATH:-/usr/qml}"
export XCURSOR_THEME="${XCURSOR_THEME:-Pop}"
export XKB_CONFIG_ROOT="${XKB_CONFIG_ROOT:-/usr/share/X11/xkb}"
if [ -z "${XDG_RUNTIME_DIR:-}" ]; then
export XDG_RUNTIME_DIR="/tmp/run/user/$(id -u)"
fi
mkdir -p "$XDG_RUNTIME_DIR"
chmod 700 "$XDG_RUNTIME_DIR" 2>/dev/null || true
if [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]; then
eval "$(dbus-launch --sh-syntax)"
fi
validation_request="/run/redbear-kde-session.validation-request"
validation_success="/run/redbear-kde-session.validation-success"
if [ -f "$validation_request" ]; then
{
echo "user=$USER"
echo "runtime=$XDG_RUNTIME_DIR"
echo "wayland=$WAYLAND_DISPLAY"
} > "$validation_success"
rm -f "$validation_request"
exit 0
fi
dbus-update-activation-environment \
DBUS_SESSION_BUS_ADDRESS \
DBUS_SESSION_BUS_PID \
WAYLAND_DISPLAY \
XDG_SESSION_ID \
XDG_SEAT \
XDG_SESSION_TYPE \
XDG_RUNTIME_DIR \
XDG_CURRENT_DESKTOP \
KDE_FULL_SESSION \
DISPLAY \
HOME \
USER
if [ -n "$KWIN_DRM_DEVICES" ]; then
exec kwin_wayland_wrapper --drm
else
echo "redbear-kde-session: using virtual KWin backend (set KWIN_DRM_DEVICES to enable DRM)" >&2
exec kwin_wayland_wrapper --virtual
fi
[service]
cmd = "inputd"
args = ["-A", "2"]
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/30_console.service"
data = """
[unit]
description = "Console terminals"
requires_weak = [
"29_activate_console.service",
]
[service]
cmd = "getty"
args = ["2"]
type = "oneshot_async"
respawn = true
"""
[[files]]
path = "/usr/lib/init.d/31_debug_console.service"
data = """
[unit]
description = "Debug console"
requires_weak = [
"29_activate_console.service",
]
[service]
cmd = "getty"
args = ["/scheme/debug/no-preserve", "-J"]
type = "oneshot_async"
respawn = true
"""
[users.greeter]
password = ""
uid = 101
gid = 101
name = "greeter"
home = "/nonexistent"
shell = "/usr/bin/ion"
[groups.greeter]
gid = 101
members = ["greeter"]
[[files]]
path = "/etc/pcid.d/ihdgd.toml"
data = """
[[drivers]]
name = "Intel GPU (VGA compatible)"
class = 0x03
vendor = 0x8086
subclass = 0x00
command = ["redox-drm"]
[[drivers]]
name = "Intel GPU (3D controller)"
class = 0x03
vendor = 0x8086
subclass = 0x02
command = ["redox-drm"]
"""
[[files]]
path = "/etc/pcid.d/virtio-gpud.toml"
data = """
[[drivers]]
name = "VirtIO GPU"
class = 0x03
vendor = 0x1af4
subclass = 0x00
command = ["redox-drm"]
"""
-14
View File
@@ -1,14 +0,0 @@
# Red Bear OS GRUB Live Full Configuration
# Canonical GRUB-backed live ISO for the full desktop/session target.
#
# Build: make live CONFIG_NAME=redbear-grub-live-full
#
# Boot flow: UEFI → GRUB (menu) → chainload EFI/REDBEAR/redbear.efi → Redox bootloader → kernel
# Session: graphical greeter on VT 3, text login fallback on VT 2, debug console on /scheme/debug
#
# The installer reads bootloader = "grub" from this config and writes:
# EFI/BOOT/BOOTX64.EFI — GRUB EFI binary (primary bootloader)
# EFI/BOOT/grub.cfg — GRUB menu config
# EFI/REDBEAR/redbear.efi — Redox bootloader (chainload target)
include = ["redbear-live.toml", "redbear-grub.toml"]
-9
View File
@@ -1,9 +0,0 @@
# Red Bear OS GRUB Live Mini Configuration
# Canonical GRUB-backed live ISO for the stripped console/recovery target.
#
# Build: make live CONFIG_NAME=redbear-grub-live-mini
include = ["redbear-live-mini.toml", "redbear-grub.toml"]
[general]
efi_partition_size = 16
+9
View File
@@ -0,0 +1,9 @@
# Red Bear OS shared GRUB policy fragment
# Use with any redbear-* profile to make GRUB first-class in installer flows.
[general]
bootloader = "grub"
efi_partition_size = 16
[packages]
grub = {}
+9 -4
View File
@@ -1,11 +1,16 @@
# Red Bear OS shared GRUB policy fragment
# Use with any redbear-* profile to make GRUB first-class in installer flows.
# Red Bear OS GRUB Configuration
# Text-only ISO with GRUB boot manager for bare metal.
#
# Build: make live CONFIG_NAME=redbear-grub
#
# Identical to redbear-mini but uses GNU GRUB as the boot manager
# instead of the Redox EFI bootloader.
include = ["redbear-mini.toml", "redbear-grub-policy.toml"]
[general]
bootloader = "grub"
# Keep ESP large enough for GRUB EFI image + Red Bear bootloader + future growth.
efi_partition_size = 16
[packages]
# Ensure GRUB artifacts are present for installer-native chainload layout.
grub = {}
-381
View File
@@ -1,381 +0,0 @@
# Red Bear OS Target Wayland Desktop Configuration
# Build: make all CONFIG_NAME=redbear-kde
# Live: make live CONFIG_NAME=redbear-kde
#
# Intended desktop direction: KWin Wayland session on Red Bear OS
# Tracked default desktop target for Red Bear OS
# Current state: KWin + KF6 + Qt6 + Mesa + D-Bus + seatd
# Runtime compositor/session proof is still required for broader support claims
# Future: add plasma-workspace, plasma-desktop when their deps are resolved
include = ["desktop.toml", "redbear-legacy-base.toml", "redbear-legacy-desktop.toml", "redbear-device-services.toml", "redbear-netctl.toml"]
[general]
filesystem_size = 4096
[users.messagebus]
uid = 100
gid = 100
name = "messagebus"
home = "/nonexistent"
shell = "/usr/bin/ion"
[packages]
# Red Bear OS branding
redbear-release = {}
# Exclude inherited legacy desktop packages from the tracked KWin target
orbdata = "ignore"
orbital = "ignore"
orbterm = "ignore"
orbutils = "ignore"
cosmic-edit = "ignore"
cosmic-files = "ignore"
cosmic-icons = "ignore"
cosmic-term = "ignore"
# ext4 filesystem support
ext4d = {}
# Firmware loading + Wi-Fi control plane
redbear-firmware = {}
firmware-loader = {}
redbear-wifictl = {}
# Input layer
evdevd = {}
udev-shim = {}
# D-Bus (session + system bus)
dbus = {}
redbear-sessiond = {}
redbear-dbus-services = {}
redbear-notifications = {}
redbear-upower = {}
redbear-udisks = {}
redbear-polkit = {}
# Diagnostic/runtime tooling
redbear-info = {}
redbear-hwutils = {}
redbear-netctl = {}
redbear-netctl-console = {}
redbear-netstat = {}
redbear-traceroute = {}
redbear-mtr = {}
redbear-nmap = {}
cub = {}
# Wayland protocol
libwayland = {}
wayland-protocols = {}
# Input
libxkbcommon = {}
libevdev = {}
libinput = {}
# Seat management
seatd = {}
# Qt6 stack
qtbase = {}
qtdeclarative = {}
qtsvg = {}
qtwayland = {}
# KF6 Frameworks — Tier 1 (no special deps)
kf6-extra-cmake-modules = {}
kf6-kcoreaddons = {}
kf6-kconfig = {}
kf6-ki18n = {}
kf6-kcolorscheme = {}
kf6-kauth = {}
# KF6 Frameworks — KWin session-surface chain
kf6-kwindowsystem = {}
kf6-knotifications = {}
kf6-kconfigwidgets = {}
kf6-kcrash = {}
kf6-kdbusaddons = {}
kf6-kglobalaccel = {}
kf6-kservice = {}
kf6-kpackage = {}
kf6-kiconthemes = {}
kirigami = {}
kf6-kio = {}
kf6-kdeclarative = {}
kf6-kcmutils = {}
kf6-kwayland = {}
kdecoration = {}
# KDE session surface (v2.0 Phases 34)
kwin = {}
# Graphics
redox-drm = {}
mesa = {}
libdrm = {}
# Firmware directory for AMD/Intel GPU blobs
[[files]]
path = "/lib/firmware/amdgpu"
data = ""
directory = true
mode = 0o755
[[files]]
path = "/usr/lib/init.d/12_dbus.service"
data = """
[unit]
description = "D-Bus system bus"
requires_weak = [
"00_base.target",
]
[service]
cmd = "ion"
args = [
"-c",
"mkdir -p /var/lib/dbus /run/dbus; rm -f /run/dbus/pid; dbus-uuidgen --ensure; dbus-daemon --system",
]
type = "oneshot_async"
"""
[[files]]
path = "/var/lib/dbus"
data = ""
directory = true
mode = 0o755
[[files]]
path = "/run/dbus"
data = ""
directory = true
mode = 0o755
[[files]]
path = "/usr/lib/init.d/13_seatd.service"
data = """
[unit]
description = "seatd seat management daemon"
requires_weak = [
"12_dbus.service",
"13_redbear-sessiond.service",
]
[service]
cmd = "seatd"
args = ["-l", "info"]
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/13_redbear-sessiond.service"
data = """
[unit]
description = "Red Bear session broker (org.freedesktop.login1)"
requires_weak = [
"12_dbus.service",
]
[service]
cmd = "ion"
args = [
"-c",
"redbear-sessiond",
]
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/14_redbear-upower.service"
data = """
[unit]
description = "UPower D-Bus service (org.freedesktop.UPower)"
requires_weak = [
"12_dbus.service",
]
[service]
cmd = "ion"
args = [
"-c",
"redbear-upower",
]
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/14_redbear-udisks.service"
data = """
[unit]
description = "UDisks2 D-Bus service (org.freedesktop.UDisks2)"
requires_weak = [
"12_dbus.service",
]
[service]
cmd = "ion"
args = [
"-c",
"redbear-udisks",
]
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/14_redbear-polkit.service"
data = """
[unit]
description = "PolicyKit1 D-Bus service (org.freedesktop.PolicyKit1)"
requires_weak = [
"12_dbus.service",
]
[service]
cmd = "ion"
args = [
"-c",
"redbear-polkit",
]
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/20_display.service"
data = """
[unit]
description = "Display session service (KDE session)"
requires_weak = [
"12_dbus.service",
"13_redbear-sessiond.service",
"13_seatd.service",
]
[service]
cmd = "/usr/bin/redbear-kde-session"
envs = { VT = "3" }
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/30_console.service"
data = """
[unit]
description = "Console terminals"
requires_weak = [
"29_activate_console.service",
]
[service]
cmd = "getty"
args = ["2"]
type = "oneshot_async"
respawn = true
"""
[[files]]
path = "/usr/lib/init.d/29_activate_console.service"
data = """
[unit]
description = "Activate display VT"
requires_weak = [
"20_display.service",
]
[service]
cmd = "inputd"
args = ["-A", "3"]
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/31_debug_console.service"
data = """
[unit]
description = "Debug console"
requires_weak = [
"29_activate_console.service",
]
[service]
cmd = "getty"
args = ["/scheme/debug/no-preserve", "-J"]
type = "oneshot_async"
respawn = true
"""
[[files]]
path = "/usr/bin/redbear-kde-session"
mode = 0o755
data = """
#!/usr/bin/sh
export DISPLAY=""
export HOME="${HOME:-/home/root}"
export USER="${USER:-root}"
export LOGNAME="${LOGNAME:-$USER}"
export WAYLAND_DISPLAY="${WAYLAND_DISPLAY:-wayland-0}"
export XDG_SESSION_TYPE=wayland
export KDE_FULL_SESSION=true
export XDG_CURRENT_DESKTOP=KDE
export LIBSEAT_BACKEND=seatd
export SEATD_SOCK=/run/seatd.sock
export QT_PLUGIN_PATH="${QT_PLUGIN_PATH:-/usr/plugins}"
export QT_QPA_PLATFORM_PLUGIN_PATH="${QT_QPA_PLATFORM_PLUGIN_PATH:-/usr/plugins/platforms}"
export QML2_IMPORT_PATH="${QML2_IMPORT_PATH:-/usr/qml}"
export XCURSOR_THEME="${XCURSOR_THEME:-Pop}"
export XKB_CONFIG_ROOT="${XKB_CONFIG_ROOT:-/usr/share/X11/xkb}"
if [ -z "${XDG_RUNTIME_DIR:-}" ]; then
export XDG_RUNTIME_DIR="/tmp/run/user/$(id -u)"
fi
mkdir -p "$XDG_RUNTIME_DIR"
chmod 700 "$XDG_RUNTIME_DIR" 2>/dev/null || true
wait_for_path() {
target="$1"
attempts=0
while [ "$attempts" -lt 30 ]; do
if [ -e "$target" ]; then
return 0
fi
attempts=$((attempts + 1))
sleep 1
done
return 1
}
if ! wait_for_path /run/dbus/system_bus_socket; then
echo "system D-Bus socket did not appear" >&2
exit 1
fi
sleep 2
if [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]; then
eval "$(dbus-launch --sh-syntax)"
fi
dbus-update-activation-environment \
DBUS_SESSION_BUS_ADDRESS \
DBUS_SESSION_BUS_PID \
WAYLAND_DISPLAY \
XDG_SESSION_ID \
XDG_SEAT \
XDG_SESSION_TYPE \
XDG_RUNTIME_DIR \
XDG_CURRENT_DESKTOP \
KDE_FULL_SESSION \
DISPLAY \
HOME \
USER
if [ -z "${KWIN_DRM_DEVICES:-}" ] && [ -e /scheme/drm/card0 ]; then
export KWIN_DRM_DEVICES=/scheme/drm/card0
fi
exec kwin_wayland_wrapper --drm
"""
-2
View File
@@ -23,8 +23,6 @@ data = """
# clear and recreate tmpdir with 0o1777 permission
rm -rf /tmp
mkdir -m a=rwxt /tmp
nowait sudo --daemon
"""
[[files]]
-7
View File
@@ -1,7 +0,0 @@
# Red Bear OS Live Full Configuration with GRUB Boot Manager
# Legacy compatibility alias for the canonical GRUB full live target.
#
# Preferred build: make live CONFIG_NAME=redbear-grub-live-full
# Legacy build: make live CONFIG_NAME=redbear-live-full-grub
include = ["redbear-grub-live-full.toml"]
-7
View File
@@ -1,7 +0,0 @@
# Red Bear OS Live Full Configuration
# Legacy compatibility alias for the canonical full live target.
#
# Preferred build: make live CONFIG_NAME=redbear-live
# Legacy build: make live CONFIG_NAME=redbear-live-full
include = ["redbear-live.toml"]
-7
View File
@@ -1,7 +0,0 @@
# Red Bear OS Live Mini Configuration with GRUB Boot Manager
# Legacy compatibility alias for the canonical GRUB mini live target.
#
# Preferred build: make live CONFIG_NAME=redbear-grub-live-mini
# Legacy build: make live CONFIG_NAME=redbear-live-mini-grub
include = ["redbear-grub-live-mini.toml"]
-6
View File
@@ -1,6 +0,0 @@
# Red Bear OS Live Minimal Configuration with GRUB Boot Manager
# Live ISO for minimal console target with installer-native GRUB chainload.
#
# Build: make live CONFIG_NAME=redbear-live-minimal-grub
include = ["redbear-live-minimal.toml", "redbear-grub.toml"]
-12
View File
@@ -1,12 +0,0 @@
# Red Bear OS Live Minimal Configuration
# Live ISO variant for the minimal console/server target.
#
# Build: make live CONFIG_NAME=redbear-live-minimal
include = ["redbear-minimal.toml"]
[general]
filesystem_size = 3072
[packages]
cub = {}
-15
View File
@@ -1,15 +0,0 @@
# Red Bear OS Live Configuration
# Canonical full live ISO target for the desktop/session surface on real bare metal.
#
# Build: make live CONFIG_NAME=redbear-live
# This ISO is for real bare-metal boot/install/recovery, not VM/QEMU use.
include = ["redbear-full.toml"]
[general]
filesystem_size = 3072
efi_partition_size = 1
[packages]
cub = {}
redbear-meta = {}
@@ -1,19 +1,26 @@
# Red Bear OS Live Mini Configuration
# Live ISO variant for console/recovery/install on bare metal.
# Red Bear OS Mini Configuration
# Text-only ISO for console/recovery/install on bare metal.
#
# Build: make live CONFIG_NAME=redbear-live-mini
# Build: make live CONFIG_NAME=redbear-mini
#
# Target contract:
# - keep a text-login live/recovery surface only
# - use boot framebuffer for VT text consoles via vesad + fbcond
# - ship all non-graphics, non-firmware packages from the full profile
# - ship no linux-firmware payload, no firmware-loader, no GPU/display drivers
# - text-login live/recovery/install surface
# - boot framebuffer for VT text consoles via vesad + fbcond
# - all non-graphics, non-firmware packages from the full profile
# - no linux-firmware payload, no firmware-loader, no GPU/display drivers
include = ["minimal.toml", "redbear-legacy-base.toml", "redbear-netctl.toml"]
[general]
filesystem_size = 1536
[users.messagebus]
uid = 100
gid = 100
name = "messagebus"
home = "/nonexistent"
shell = "/usr/bin/ion"
[packages]
# Red Bear OS branding and host utilities.
redbear-release = {}
@@ -188,11 +195,25 @@ cmd = "i2cd"
type = "oneshot_async"
"""
[[files]]
path = "/etc/init.d/00_i2c-dw-acpi.service"
data = """
[unit]
description = "DesignWare ACPI I2C controller (non-blocking)"
requires_weak = [
"00_i2cd.service",
]
[service]
cmd = "dw-acpi-i2cd"
type = "oneshot_async"
"""
[[files]]
path = "/etc/init.d/00_intel-gpiod.service"
data = """
[unit]
description = "Intel ACPI GPIO registrar (non-blocking on live-mini)"
description = "Intel ACPI GPIO registrar (non-blocking)"
requires_weak = [
"00_gpiod.service",
"00_i2cd.service",
@@ -222,7 +243,7 @@ type = "oneshot_async"
path = "/etc/init.d/00_i2c-hidd.service"
data = """
[unit]
description = "ACPI I2C HID bring-up daemon (non-blocking on live-mini)"
description = "ACPI I2C HID bring-up daemon (non-blocking)"
requires_weak = [
"00_i2cd.service",
"00_i2c-dw-acpi.service",
-6
View File
@@ -1,6 +0,0 @@
# Red Bear OS Minimal Configuration with GRUB Boot Manager
# Minimal console/server target with installer-native GRUB chainload.
#
# Build: make all CONFIG_NAME=redbear-minimal-grub
include = ["redbear-minimal.toml", "redbear-grub.toml"]
-219
View File
@@ -1,219 +0,0 @@
# Red Bear OS Minimal Configuration
# Console/Server variant with bare-metal driver support but no GUI.
# Includes all non-graphics, non-firmware packages from the full profile.
#
# Build: make all CONFIG_NAME=redbear-minimal
include = ["minimal.toml", "redbear-legacy-base.toml", "redbear-device-services.toml", "redbear-netctl.toml"]
[general]
# Minimal image carries the full non-graphics package set;
# 2048 MiB accommodates CLI tools, D-Bus services, and filesystem utilities.
filesystem_size = 2048
[packages]
# Red Bear OS branding
redbear-release = {}
# Native Redox PCI/USB listing tools (lspci, lsusb)
redbear-hwutils = {}
# Redox-native netctl compatibility command
redbear-netctl = {}
redbear-netctl-console = {}
# Native network reporting and connect-scan tools
redbear-netstat = {}
redbear-traceroute = {}
redbear-mtr = {}
redbear-nmap = {}
# Wi-Fi control plane (firmware-loader comes from redbear-device-services.toml)
firmware-loader = {}
redbear-wifictl = {}
# Input/runtime service prerequisites
evdevd = {}
udev-shim = {}
# Terminal file manager
mc = {}
# Diagnostic tool
redbear-info = {}
# Package builder (cub -S/-B/-G CLI)
cub = {}
# ── D-Bus IPC and session services ──
dbus = {}
redbear-sessiond = {}
redbear-dbus-services = {}
redbear-notifications = {}
redbear-upower = {}
redbear-udisks = {}
redbear-polkit = {}
# ── IOMMU DMA remapping ──
iommu = {}
# ── Standard CLI tools (from server profile) ──
bash = {}
bottom = {}
curl = {}
diffutils = {}
findutils = {}
git = {}
htop = {}
# ── Filesystem support ──
ext4d = {}
redoxfs = {}
# ── System installer ──
installer = {}
# ── Build / packaging utilities ──
patchelf = {}
shared-mime-info = {}
[[files]]
path = "/etc/netctl/active"
data = "wired-dhcp\n"
[[files]]
path = "/etc/issue"
data = """
########## Red Bear OS #########
# Login with the following: #
# `user` #
# `root`:`password` #
################################
"""
[[files]]
path = "/etc/motd"
data = """
Red Bear OS v0.1.0 "Denali" — Built on Redox OS
Type 'help' for available commands.
"""
[[files]]
path = "/usr/lib/init.d/12_dbus.service"
data = """
[unit]
description = "D-Bus system bus"
requires_weak = [
"12_boot-late.target",
]
[service]
cmd = "ion"
args = [
"-c",
"mkdir -p /var/lib/dbus /run/dbus; rm -f /run/dbus/pid; dbus-uuidgen --ensure; dbus-daemon --system",
]
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/13_redbear-sessiond.service"
data = """
[unit]
description = "Red Bear session broker (org.freedesktop.login1)"
requires_weak = [
"12_dbus.service",
]
[service]
cmd = "ion"
args = [
"-c",
"redbear-sessiond",
]
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/13_iommu.service"
data = """
[unit]
description = "IOMMU DMA remapping daemon"
requires_weak = [
"12_boot-late.target",
"00_pcid-spawner.service",
]
[service]
cmd = "/usr/bin/iommu"
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/14_redbear-upower.service"
data = """
[unit]
description = "UPower D-Bus service (org.freedesktop.UPower)"
requires_weak = [
"12_dbus.service",
]
[service]
cmd = "ion"
args = [
"-c",
"redbear-upower",
]
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/14_redbear-udisks.service"
data = """
[unit]
description = "UDisks2 D-Bus service (org.freedesktop.UDisks2)"
requires_weak = [
"12_dbus.service",
]
[service]
cmd = "ion"
args = [
"-c",
"redbear-udisks",
]
type = "oneshot_async"
"""
[[files]]
path = "/usr/lib/init.d/14_redbear-polkit.service"
data = """
[unit]
description = "PolicyKit1 D-Bus service (org.freedesktop.PolicyKit1)"
requires_weak = [
"12_dbus.service",
]
[service]
cmd = "ion"
args = [
"-c",
"redbear-polkit",
]
type = "oneshot_async"
"""
[[files]]
path = "/var/lib/dbus"
data = ""
directory = true
mode = 0o755
[[files]]
path = "/run/dbus"
data = ""
directory = true
mode = 0o755
# VT/getty/login chain: initfs starts inputd + vesad + fbcond in phase 1,
# then minimal.toml legacy 30_console runs inputd -A 2 + getty 2 + getty debug.
-16
View File
@@ -1,16 +0,0 @@
# Red Bear OS Wayland Validation Profile
#
# Build: ./local/scripts/build-redbear.sh redbear-wayland
# Phase 1 substrate validation: ./local/scripts/test-phase1-desktop-substrate.sh --qemu redbear-wayland
# Phase 2 Wayland validation: ./local/scripts/test-phase4-wayland-qemu.sh
#
# Validation-only profile:
# - the packaged compositor/runtime harness remains bounded
# - this profile remains the bounded validation slice beneath redbear-kde -> kwin_wayland
include = ["wayland.toml"]
[packages]
redbear-firmware = {}
redox-drm = {}
qt6-wayland-smoke = {}
+4 -5
View File
@@ -105,7 +105,7 @@ echo 'PODMAN_BUILD?=0' > .config
### Select Build Configuration
Tracked Red Bear work should normally be built and validated through the four supported
Tracked Red Bear work should normally be built and validated through the three supported
`redbear-*` compile targets. For desktop work specifically, `redbear-full` is the tracked
desktop-capable target.
@@ -144,20 +144,19 @@ For the full layout and rationale, see `local/docs/EXTERNAL-TOOLCHAIN.md`.
```bash
# Preferred Red Bear wrapper:
./local/scripts/build-redbear.sh redbear-mini
./local/scripts/build-redbear.sh redbear-live-mini
./local/scripts/build-redbear.sh redbear-full
./local/scripts/build-redbear.sh redbear-live-full
./local/scripts/build-redbear.sh redbear-grub
# Direct make is still valid when needed:
make all CONFIG_NAME=redbear-full
```
For tracked Red Bear work, prefer these four compile targets over older historical names.
For tracked Red Bear work, prefer these three compile targets over older historical names.
### Build a Live ISO
```bash
make live CONFIG_NAME=redbear-live-full
make live CONFIG_NAME=redbear-full
# Produces: build/x86_64/redbear-live.iso
```
+12 -24
View File
@@ -93,14 +93,13 @@ That means:
The tracked Red Bear compile targets are:
- `redbear-mini`
- `redbear-live-mini`
- `redbear-full`
- `redbear-live-full`
- `redbear-grub`
These are the only supported compile targets. Older names such as `redbear-minimal`,
`redbear-desktop`, `redbear-wayland`, `redbear-kde`, and `redbear-live` may still appear in
historical notes or legacy implementation details, but they are not the current compile-target
surface.
`redbear-desktop`, `redbear-wayland`, `redbear-kde`, `redbear-live`, `redbear-live-mini`,
and `redbear-live-full` may still appear in historical notes or legacy implementation details,
but they are not the current compile-target surface.
### `redbear-mini`
@@ -125,31 +124,20 @@ Scope:
- Qt base integration,
- the active desktop-capable target surface.
### `redbear-live-mini`
### `redbear-grub`
Live/demo/recovery form of the mini baseline for real bare metal.
Text-only console/recovery target with GRUB boot manager for real bare metal.
Scope:
- diagnostics,
- recovery workflows,
- installability for the non-graphics target.
### `redbear-live-full`
Live/demo/recovery form of the full desktop target for real bare metal.
Scope:
- diagnostics,
- recovery workflows,
- installability,
- live desktop-capable system identity.
- multi-boot bare-metal install with GRUB chainload.
### Desktop policy
- Desktop/graphics are available only on `redbear-full` and `redbear-live-full`.
- Validation work that does not require graphics should prefer `redbear-mini` or `redbear-live-mini`.
- Desktop/graphics are available only on `redbear-full`.
- Validation work that does not require graphics should prefer `redbear-mini` or `redbear-grub`.
- Live `.iso` outputs are for real bare-metal boot/install workflows, not for VM/QEMU execution; virtualization should use the `harddrive.img`-based target surface.
## Current State Baseline
@@ -384,9 +372,9 @@ Canonical references:
Acceptance:
- `redbear-wayland` remains the narrow runtime validation slice,
- `redbear-full` remains the broader desktop/session plumbing slice,
- the active desktop-capable tracked targets keep honest session-viability language tied to `redbear-full` / `redbear-live-full`, not older historical target names.
- `redbear-full` remains the broader desktop/session plumbing slice (the Wayland validation slice
is handled within `redbear-full`),
- the active desktop-capable tracked targets keep honest session-viability language tied to `redbear-full`, not older historical target names.
### 8. Hardware validation and support labeling
+3 -3
View File
@@ -107,8 +107,8 @@ This summary is only a quick orientation layer. For canonical current-state deta
- `local/docs/PROFILE-MATRIX.md` for support-language by tracked profile,
- and the active subsystem plans under `local/docs/` for detailed current workstreams.
- **Compile targets**: the supported compile targets are `redbear-mini`, `redbear-live-mini`, `redbear-full`, and `redbear-live-full`
- **Live ISO policy**: live `.iso` outputs (`redbear-live-mini`, `redbear-live-full`) are for real bare-metal boot/install/recovery workflows, not the VM/QEMU execution surface.
- **Compile targets**: the supported compile targets are `redbear-mini`, `redbear-full`, and `redbear-grub`
- **Live ISO policy**: live `.iso` outputs (`make live`) are for real bare-metal boot/install/recovery workflows, not the VM/QEMU execution surface.
- **Wayland**: libwayland + wayland-protocols built. A bounded greeter/compositor-backed login proof now passes, but broader compositor/runtime stability remains incomplete.
- **Qt6**: qtbase 6.11.0 (Core+Gui+Widgets+DBus+Wayland), qtdeclarative, qtsvg, qtwayland ALL BUILT
- **D-Bus**: 1.16.2 built for Redox. Qt6DBus enabled.
@@ -120,7 +120,7 @@ This summary is only a quick orientation layer. For canonical current-state deta
- **PCI / IRQ quality**: architecturally strong substrate exists, with bounded MSI-X, IOMMU, xHCI IRQ, and low-level-controller proof surfaces; broader hardware robustness is still intentionally tracked as open work in `../local/docs/IRQ-AND-LOWLEVEL-CONTROLLERS-ENHANCEMENT-PLAN.md`
- **Wi-Fi profile target**: `config/redbear-wifi-experimental.toml` is the first explicit tracked image slice for bounded Intel WiFi validation, instead of spreading that claim across the generic desktop profiles.
- **Bluetooth**: one bounded in-tree BLE-first experimental slice exists, and the Battery Level read-only workload now has a packaged in-guest checker plus a host QEMU harness; QEMU validation is still in progress, so broad desktop Bluetooth parity is still incomplete
- **Desktop direction**: `redbear-full` / `redbear-live-full` carry the desktop-capable target surface; the bounded greeter/login slice now passes, while the wider desktop runtime stack is still incomplete.
- **Desktop direction**: `redbear-full` carries the desktop-capable target surface; the bounded greeter/login slice now passes, while the wider desktop runtime stack is still incomplete.
- **ACPI**: materially complete for the historical boot baseline, not release-grade complete; implemented: AML mutex real state, EC widened accesses via byte transactions, kstop-based shutdown eventing, explicit `RSDP_ADDR` forwarding into `acpid`, x86 BIOS-search AML fallback, and real-but-provisional AML-backed power enumeration. **Known gaps**: the explicit boot-path producer contract for AML bootstrap is still underdocumented, `acpid` startup hardening remains open, shutdown/power reporting are still provisional, sleep state transitions and sleep eventing remain incomplete, DMAR ownership is still transitional, and bare-metal validation is still bounded. See `local/docs/ACPI-IMPROVEMENT-PLAN.md`.
- **Linux driver compat**: linux-kpi now includes early wireless-subsystem compatibility scaffolding in addition to the earlier helper layer, redox-driver-sys and firmware-loader compile, and the bounded Intel Wi-Fi path now has host-tested scan/connect/disconnect/profile/reporting flows without claiming real hardware Wi-Fi connectivity.
- **Wi-Fi validation tooling**: `redbear-phase5-wifi-check` and `redbear-phase5-wifi-capture` are now packaged in-guest helpers for bounded Intel Wi-Fi runtime validation and evidence capture on bare metal or VFIO-backed guests.
+59 -134
View File
@@ -24,7 +24,7 @@ Build flow:
```
make all CONFIG_NAME=redbear-full
→ mk/config.mk resolves to the active desktop/graphics compile target
→ Desktop/graphics are available only on redbear-full and redbear-live-full
→ Desktop/graphics are available only on redbear-full
→ repo cook builds all packages including our custom ones
→ mk/disk.mk creates harddrive.img with Red Bear branding
```
@@ -37,28 +37,14 @@ make all CONFIG_NAME=redbear-full # Rebuild the active desktop/graphics
## ACTIVE COMPILE TARGETS
The supported compile targets are exactly:
The supported compile targets are exactly three. All three work for both `make all` (harddrive.img)
and `make live` (ISO):
Non-live (harddrive.img for QEMU / development):
- `redbear-full` — Desktop/graphics-enabled target
- `redbear-full-grub` Desktop/graphics with GRUB boot manager
- `redbear-full` — Desktop/graphics-enabled target (Wayland + KDE + GPU drivers)
- `redbear-mini` — Text-only console/recovery/install target
- `redbear-grub`Text-only target with GRUB boot manager
Live (ISO for real bare metal):
- `redbear-live` — Full desktop live ISO (graphical greeter + text fallback)
- `redbear-live-mini` — Text-only mini live ISO for recovery/bare metal
- `redbear-grub-live-full` — Full desktop live ISO with GRUB boot manager
- `redbear-grub-live-mini` — Text-only mini live ISO with GRUB
Legacy aliases (accepted by build-iso.sh):
- `redbear-live-full``redbear-live`
- `redbear-live-mini-grub``redbear-grub-live-mini`
- `redbear-live-full-grub``redbear-grub-live-full`
Desktop/graphics are available only on `redbear-full`, `redbear-live`, `redbear-full-grub`, and `redbear-grub-live-full`.
Names such as `redbear-kde`, `redbear-wayland`, and `redbear-minimal` may still appear in older
docs, legacy validation notes, or in-repo staging configs, but they should not be treated as the
current supported compile targets.
Desktop/graphics are available only on `redbear-full`.
## TRACKING UPSTREAM (SYNC WITH REDOX OS)
@@ -176,9 +162,10 @@ redox-master/ ← git pull updates mainline Redox
├── config/
│ ├── desktop.toml ← mainline configs (untouched)
│ ├── minimal.toml
│ ├── redbear-full.toml ← Active desktop/graphics target
│ ├── redbear-live-full.toml ← Live desktop/graphics target
│ ├── redbear-mini*.toml ← Minimal target surface (legacy/staging naming may still vary in-tree)
│ ├── redbear-full.toml ← Desktop/graphics target
│ ├── redbear-mini.toml ← Text-only console/recovery target
│ ├── redbear-grub.toml ← Text-only with GRUB boot manager
│ ├── redbear-grub-policy.toml ← GRUB policy fragment (bootloader = "grub", efi_partition_size = 16)
│ └── redbear-greeter-services.toml ← Greeter/auth/session-launch wiring fragment
├── recipes/ ← mainline package recipes (untouched)
├── mk/ ← mainline build system (untouched)
@@ -240,23 +227,25 @@ redox-master/ ← git pull updates mainline Redox
## HOW TO BUILD RED BEAR OS
```bash
# Active desktop/graphics target
./local/scripts/build-redbear.sh redbear-full
# Build targets (all three work for both `make all` and `make live`)
./local/scripts/build-redbear.sh redbear-full # Desktop/graphics target
./local/scripts/build-redbear.sh redbear-mini # Text-only console/recovery target
./local/scripts/build-redbear.sh redbear-grub # Text-only with GRUB boot manager
# Minimal non-desktop target
./local/scripts/build-redbear.sh redbear-mini
# Or manually:
make all CONFIG_NAME=redbear-full # Desktop/graphics → harddrive.img
make all CONFIG_NAME=redbear-mini # Text-only → harddrive.img
make all CONFIG_NAME=redbear-grub # Text-only + GRUB → harddrive.img
# Live images
./local/scripts/build-redbear.sh redbear-live && make live CONFIG_NAME=redbear-live
./local/scripts/build-redbear.sh redbear-live-mini && make live CONFIG_NAME=redbear-live-mini
./local/scripts/build-redbear.sh redbear-live-mini && make live CONFIG_NAME=redbear-grub-live-mini
./local/scripts/build-redbear.sh redbear-live && make live CONFIG_NAME=redbear-grub-live-full
# Live ISO (for real bare metal)
make live CONFIG_NAME=redbear-full # Full desktop live ISO
make live CONFIG_NAME=redbear-mini # Text-only mini live ISO
make live CONFIG_NAME=redbear-grub # Text-only mini live ISO with GRUB
# Or using the helper:
scripts/build-iso.sh redbear-live
scripts/build-iso.sh redbear-live-mini
scripts/build-iso.sh redbear-grub-live-mini
scripts/build-iso.sh redbear-grub-live-full
scripts/build-iso.sh redbear-full # Full desktop live ISO
scripts/build-iso.sh redbear-mini # Text-only mini (default)
scripts/build-iso.sh redbear-grub # Text-only + GRUB
# VM-network baseline validation helpers
./local/scripts/validate-vm-network-baseline.sh
@@ -266,10 +255,9 @@ scripts/build-iso.sh redbear-grub-live-full
# Phase 1 desktop-substrate validation (v2.0 plan: relibc headers, evdevd, udev-shim,
# firmware-loader, DRM/KMS, health-check — covers 6 acceptance areas)
./local/scripts/test-phase1-desktop-substrate.sh --qemu redbear-wayland
./local/scripts/test-phase1-desktop-substrate.sh --qemu redbear-full
# Legacy Phase 3 runtime-substrate validation (historical P0-P6 numbering; script still works)
# Use the active desktop target when adapting historical validation flows.
./local/scripts/test-phase3-runtime-substrate.sh --qemu redbear-full
# Low-level controller validation
@@ -296,7 +284,7 @@ scripts/build-iso.sh redbear-grub-live-full
# The aggregate USB wrapper runs xHCI mode, full USB stack, and USB storage readback proofs in sequence.
# Legacy Phase 4 Wayland runtime validation (historical P0-P6 numbering; script still works)
./local/scripts/build-redbear.sh redbear-wayland
./local/scripts/build-redbear.sh redbear-full
./local/scripts/test-phase4-wayland-qemu.sh
# Then run inside the guest:
# redbear-phase4-wayland-check
@@ -338,18 +326,15 @@ scripts/build-iso.sh redbear-grub-live-full
# redbear-netctl user-facing alias
redbear-netctl --help
# Or manually:
make all CONFIG_NAME=redbear-full
# Single custom recipe:
./target/release/repo cook local/recipes/branding/redbear-release
./target/release/repo cook local/recipes/system/redbear-meta
./target/release/repo cook local/recipes/core/ext4d
./target/release/repo cook local/recipes/core/grub # GRUB bootloader (host build, produces EFI binary)
# GRUB boot manager (installer-native, Phase 2):
# GRUB boot manager (installer-native):
make r.grub # Build GRUB recipe
make all CONFIG_NAME=redbear-full-grub # Build with GRUB chainload
make all CONFIG_NAME=redbear-grub # Build text-only target with GRUB
# Linux-compatible CLI (add local/scripts to PATH):
grub-install --target=x86_64-efi --disk-image=build/x86_64/harddrive.img
grub-mkconfig -o local/recipes/core/grub/grub.cfg
@@ -463,7 +448,7 @@ ext4d/source/
recipes/core/ext4d → local/recipes/core/ext4d
```
**Config**: ext4d is included in `config/desktop.toml` (mainline), which `redbear-desktop.toml` inherits.
**Config**: ext4d is included in `config/desktop.toml` (mainline), which `redbear-full.toml` inherits.
**Dependencies** (from workspace Cargo.toml):
- `rsext4 = "0.3"` — Pure Rust ext4 filesystem implementation
@@ -528,7 +513,7 @@ recipes/core/fatd → ../../local/recipes/core/fatd
```
**Config**: Packages included via `config/redbear-device-services.toml` (inherited by
`redbear-desktop.toml` and `redbear-full.toml`). Init service at
`redbear-full.toml` and `redbear-mini.toml`). Init service at
`/usr/lib/init.d/15_fatd.service`.
**Dependencies**: fatfs 0.3.6, fscommon 0.1.1, redox_syscall, redox-scheme, libredox, libc
@@ -580,105 +565,45 @@ local/Assets/
## RED BEAR OS CONFIG HIERARCHY
Active compile targets:
Active compile targets (all three work for both `make all` and `make live`):
Non-live (harddrive.img for QEMU / development):
- `redbear-full`
- `redbear-full-grub`
- `redbear-full` — Desktop/graphics-enabled target
- `redbear-mini` — Text-only console/recovery target
- `redbear-grub` — Text-only with GRUB boot manager
Live (ISO for real bare metal):
- `redbear-live`
- `redbear-live-mini`
- `redbear-grub-live-full`
- `redbear-grub-live-mini`
Desktop/graphics are available only on the `full` targets. Older names such as `redbear-kde`,
`redbear-wayland`, `redbear-minimal`, and `redbear-live-minimal` may still exist in the tree as
legacy or staging artifacts, but they are not the supported compile-target surface.
Desktop/graphics are available only on `redbear-full`.
```
redbear-grub-live-full.toml
└── redbear-live.toml (full desktop base)
└── redbear-grub.toml (GRUB policy: bootloader = "grub", efi_partition_size = 16)
redbear-grub-live-mini.toml
└── redbear-live-mini.toml (text-only live base)
└── redbear-grub.toml (GRUB policy)
redbear-live-full.toml
└── redbear-full.toml
├── desktop.toml (mainline)
├── redbear-legacy-base.toml ← Neutralize broken base legacy init scripts
├── redbear-legacy-desktop.toml ← Neutralize broken desktop legacy init scripts
├── redbear-device-services.toml ← Shared firmware-loader / evdevd / udev service wiring
├── redbear-netctl.toml ← Shared Red Bear network profile files + netctl boot service
├── redbear-greeter-services.toml ← Greeter/auth/session-launch wiring for desktop targets
└── [packages] redbear-release, redbear-hwutils, redbear-netctl,
firmware-loader, evdevd, udev-shim, redbear-info,
redbear-sessiond, redbear-authd, redbear-session-launch,
redbear-greeter, redbear-meta, cub
NOTE: Desktop/graphics are available only on redbear-full and redbear-live-full.
NOTE: ext4d is inherited from desktop.toml (mainline package).
NOTE: redbear-meta is explicitly included in redbear-full.toml; keep broader inclusion deliberate.
NOTE: redbear-live-full inherits from redbear-full.toml.
redbear-live.toml
└── redbear-full.toml
├── desktop.toml (mainline)
├── redbear-legacy-base.toml ← Neutralize broken base legacy init scripts
├── redbear-legacy-desktop.toml ← Neutralize broken desktop legacy init scripts
├── redbear-device-services.toml ← Shared firmware-loader / evdevd / udev service wiring
├── redbear-netctl.toml ← Shared Red Bear network profile files + netctl boot service
└── [packages] redbear-release, redbear-hwutils, redbear-netctl,
firmware-loader, evdevd, udev-shim, redbear-info,
redbear-sessiond, redbear-authd, redbear-session-launch,
redbear-greeter, redbear-meta, cub
NOTE: Desktop/graphics are available on redbear-live.
redbear-full.toml
└── desktop.toml (mainline)
└── redbear-legacy-base.toml ← Neutralize broken base legacy init scripts
── redbear-legacy-desktop.toml ← Neutralize broken desktop legacy init scripts
└── redbear-device-services.toml ← Shared firmware-loader / evdevd / udev service wiring
└── redbear-netctl.toml ← Shared Red Bear network profile files + netctl boot service
└── redbear-greeter-services.toml ← Greeter/auth/session-launch wiring
└── redbear-mini.toml
├── minimal.toml (mainline)
── redbear-legacy-base.toml
└── redbear-netctl.toml
└── [packages] firmware, GPU, Wayland, Qt6, KF6, KWin, greeter, fonts, icons
└── [services] D-Bus, seatd, greeter, console
└── [users] messagebus, greeter
NOTE: ext4d is inherited from desktop.toml (mainline package).
NOTE: redbear-meta is explicitly included; keep broader inclusion deliberate.
redbear-full-grub.toml
└── redbear-full.toml
└── redbear-grub.toml (GRUB policy: bootloader = "grub", efi_partition_size = 16)
redbear-live-mini.toml
└── minimal non-desktop live target
└── desktop/graphics intentionally absent
redbear-grub-live-mini.toml
└── redbear-live-mini.toml (text-only live base)
└── redbear-grub.toml (GRUB policy)
redbear-mini
└── legacy/staging config files in-tree still use the older `redbear-minimal*` names
in some places; do not treat those names as the supported compile-target surface
redbear-minimal.toml (legacy/staging naming still present in tree)
redbear-mini.toml
└── minimal.toml (mainline)
└── base.toml
└── redbear-legacy-base.toml ← Neutralize broken base legacy init scripts
└── redbear-device-services.toml ← Shared firmware-loader / evdevd / udev service wiring
└── redbear-netctl.toml ← Shared Red Bear network profile files + netctl boot service
└── [packages] redbear-release, redbear-hwutils, redbear-netctl,
firmware-loader, evdevd, udev-shim, redbear-info
└── redbear-legacy-base.toml
└── redbear-netctl.toml
└── [packages] pciids, redbear-hwutils, redbear-netctl, redbear-info, cub, etc.
└── [services] pcid-spawner, netctl boot, console, debug console
redbear-grub.toml
└── redbear-mini.toml
└── redbear-grub-policy.toml (bootloader = "grub", efi_partition_size = 16)
└── [packages] grub
```
Config comparison:
| Config | GPU Stack | Desktop | Branding | ext4d | GRUB | filesystem_size |
|--------|-----------|---------|----------|-------|------|-----------------|
| redbear-full | Full | Yes | Yes | ✅ (via desktop.toml) | No | 4096 MiB |
| redbear-full-grub | Full | Yes | Yes | ✅ (via redbear-full.toml) | Yes | 4096 MiB |
| redbear-live | Full | Yes | Yes | ✅ (via redbear-full.toml) | No | 4096 MiB |
| redbear-grub-live-full | Full | Yes | Yes | ✅ (via redbear-full.toml) | Yes | 4096 MiB |
| redbear-live-mini | None | None | Yes | legacy/staging | No | legacy/staging |
| redbear-grub-live-mini | None | None | Yes | legacy/staging | Yes | legacy/staging |
| redbear-mini | None | None | Yes | legacy/staging naming in tree still maps through `redbear-minimal*` files | No | legacy/staging |
| redbear-full | Full | Yes | Yes | ✅ | No | 4096 MiB |
| redbear-mini | None | None | Yes | No | No | 1536 MiB |
| redbear-grub | None | None | Yes | No | Yes | (from mini) |
## ANTI-PATTERNS (COMMIT POLICY)
@@ -56,7 +56,7 @@ This work must be treated as bare-metal boot-critical substrate, not as optional
Emits RB_THC_QUICKI2C, RB_UCSI_* markers. Consumes `/scheme/ucsi/summary`.
- **`amlserde`** — AML serialization/deserialization, including `AmlSerdeValue::Buffer`
(needed for `_CRS`), `RegionSpace::GenericSerialBus` for I2C/SMBus opregions.
- **Init services** — `redbear-live-mini.toml` wires `i2cd`, `i2c-hidd`, `i2c-dw-acpi`,
- **Init services** — `redbear-mini.toml` wires `i2cd`, `i2c-hidd`, `i2c-dw-acpi`,
`i2c-gpio-expanderd`, `intel-gpiod`, `ucsid` with non-blocking startup ordering.
### What is missing (active gaps)
+17 -20
View File
@@ -78,7 +78,7 @@ Checks: [unit] section, [service] section, cmd field, non-empty data
Note: Manual validation script covering `redbear-*.toml` configs. Not wired into the build system — run manually after config changes. Does not cover inherited mainline configs (minimal.toml, desktop.toml).
### 3C: Getty Supervisor ✅
Init supports `respawn = true` in service TOML files. When a respawnable service's process exits, init automatically re-spawns it. All getty services across `redbear-minimal`, `redbear-desktop`, `redbear-greeter-services`, `redbear-live-mini`, `wayland`, and `redbear-kde` configs now have `respawn = true` set.
Init supports `respawn = true` in service TOML files. When a respawnable service's process exits, init automatically re-spawns it. All getty services across `redbear-mini`, `redbear-full`, `redbear-greeter-services`, `redbear-grub`, and `wayland` configs now have `respawn = true` set.
Implementation:
- `service.rs`: Added `respawn: bool` field to `Service` (default false). `spawn()` returns `Option<u32>` (child PID) for respawnable oneshot_async services.
@@ -121,8 +121,8 @@ Status: Chain exists in rootfs only. On modern hardware without PS/2 ports, USB
### Hardware Validation Requirements
Bare-metal testing requires physical hardware. Current validation is:
- **QEMU boot**: Verified for redbear-minimal and redbear-full (no panics, no parse errors, switchroot succeeds)
- **Live ISO build**: redbear-live-mini and redbear-live build successfully
- **QEMU boot**: Verified for redbear-mini and redbear-full (no panics, no parse errors, switchroot succeeds)
- **Live ISO build**: redbear-mini and redbear-grub build successfully
- **Interactive login**: Framebuffer login renders correctly (serial not available in headless QEMU)
## Phase 5: Validation Matrix ✅
@@ -132,8 +132,7 @@ Bare-metal testing requires physical hardware. Current validation is:
|--------|-------|-----------|-----------------|-------|
| redbear-mini | ✅ harddrive.img (2 GB) | ✅ Login prompt | — | Framebuffer console login |
| redbear-full | ✅ harddrive.img (4 GB) | ✅ Login prompt | — | Desktop packages included |
| redbear-live-mini | ✅ ISO (384 MB) | — | ✅ Login prompt | ISO for bare-metal boot |
| redbear-live-full | ✅ ISO (3.0 GB) | — | — | ISO for bare-metal boot |
| redbear-grub | ✅ harddrive.img | — | — | Text-only with GRUB chainload |
### Compilation Verification
- `cargo check --workspace` in base source: **0 errors**
@@ -161,20 +160,20 @@ Bare-metal testing requires physical hardware. Current validation is:
### Validation Commands
```bash
# Build
CI=1 make all CONFIG_NAME=redbear-minimal ARCH=x86_64
CI=1 make all CONFIG_NAME=redbear-mini ARCH=x86_64
CI=1 make all CONFIG_NAME=redbear-full ARCH=x86_64
CI=1 make live CONFIG_NAME=redbear-live-mini ARCH=x86_64
CI=1 make live CONFIG_NAME=redbear-live-full ARCH=x86_64
CI=1 make live CONFIG_NAME=redbear-mini ARCH=x86_64
CI=1 make live CONFIG_NAME=redbear-full ARCH=x86_64
# QEMU test
make qemu CONFIG_NAME=redbear-minimal
make qemu CONFIG_NAME=redbear-mini
# Service file validation
./local/scripts/validate-service-files.sh config/
# Clean rebuild + verify
CI=1 make cr.base CONFIG_NAME=redbear-minimal ARCH=x86_64
CI=1 make all CONFIG_NAME=redbear-minimal ARCH=x86_64
CI=1 make cr.base CONFIG_NAME=redbear-mini ARCH=x86_64
CI=1 make all CONFIG_NAME=redbear-mini ARCH=x86_64
```
## Key Technical Findings
@@ -268,8 +267,6 @@ Services with `type = "oneshot_async"` are fire-and-forget by default. Init spaw
### Config Include Chain
```
redbear-live-full.toml → redbear-live.toml
redbear-live.toml → redbear-full.toml
redbear-full.toml → desktop.toml, redbear-legacy-base.toml, redbear-legacy-desktop.toml,
redbear-device-services.toml, redbear-netctl.toml, redbear-greeter-services.toml
desktop.toml → desktop-minimal.toml, server.toml
@@ -277,7 +274,8 @@ desktop-minimal.toml → minimal.toml
server.toml → minimal.toml
minimal.toml → base.toml
redbear-live-mini.toml → minimal.toml, redbear-legacy-base.toml, redbear-netctl.toml
redbear-grub.toml → redbear-full.toml, redbear-grub-policy.toml
redbear-mini → redbear-minimal.toml → minimal.toml, redbear-legacy-base.toml,
redbear-device-services.toml, redbear-netctl.toml
```
@@ -358,9 +356,8 @@ redbear-mini → redbear-minimal.toml → minimal.toml, redbear-legacy-base.toml
| Target | Purpose | Output |
|--------|---------|--------|
| `redbear-mini` | Minimal non-desktop (QEMU + bare metal) | `build/x86_64/harddrive.img` |
| `redbear-live-mini` | Minimal live ISO (bare metal only) | `build/x86_64/redbear-live-mini.iso` |
| `redbear-grub` | Text-only with GRUB boot manager (bare metal) | `build/x86_64/harddrive.img` |
| `redbear-full` | Desktop/graphics (QEMU + bare metal) | `build/x86_64/harddrive.img` |
| `redbear-live-full` / `redbear-live` | Desktop/graphics live ISO (bare metal only) | `build/x86_64/redbear-live-full.iso` |
### Build commands
@@ -369,13 +366,13 @@ redbear-mini → redbear-minimal.toml → minimal.toml, redbear-legacy-base.toml
CI=1 make all CONFIG_NAME=redbear-mini ARCH=x86_64
# Minimal live ISO (bare-metal boot)
CI=1 make live CONFIG_NAME=redbear-live-mini ARCH=x86_64
CI=1 make live CONFIG_NAME=redbear-mini ARCH=x86_64
# Desktop/graphics target (QEMU testing)
CI=1 make all CONFIG_NAME=redbear-full ARCH=x86_64
# Desktop/graphics live ISO (bare-metal boot)
CI=1 make live CONFIG_NAME=redbear-live-full ARCH=x86_64
CI=1 make live CONFIG_NAME=redbear-full ARCH=x86_64
```
### QEMU boot (harddrive.img)
@@ -399,12 +396,12 @@ graphical console, not serial.
1. **Build the ISO:**
```bash
CI=1 make live CONFIG_NAME=redbear-live-mini ARCH=x86_64
CI=1 make live CONFIG_NAME=redbear-mini ARCH=x86_64
```
2. **Write ISO to USB drive:**
```bash
sudo dd if=build/x86_64/redbear-live-mini.iso of=/dev/sdX bs=4M status=progress && sync
sudo dd if=build/x86_64/redbear-live.iso of=/dev/sdX bs=4M status=progress && sync
```
Replace `/dev/sdX` with your USB device. Use `lsblk` to identify it.
+5 -5
View File
@@ -39,8 +39,8 @@ hardware GPU validation → KWin session bring-up → KDE Plasma session bring-u
Out of scope: USB, Wi-Fi, Bluetooth (covered by their own subsystem plans).
Tracked-default truth: this document is the canonical desktop-path plan, and the tracked desktop-
capable surface is `redbear-full` / `redbear-live-full`. Older names such as `redbear-wayland` and
`redbear-kde` should be read as historical or staging labels, not supported compile targets.
capable surface is `redbear-full`. Older names such as `redbear-wayland` and `redbear-kde`
should be read as historical or staging labels, not supported compile targets.
---
@@ -102,7 +102,7 @@ Rules:
| kf6-kcmutils | builds | Widget-only build (QML stripped) | |
| `redbear-wayland` profile | historical / staging | Bounded Wayland validation profile | Not a supported compile target |
| `redbear-full` profile | builds, boots | Broader desktop plumbing profile | Session/network/runtime integration slice |
| `redbear-kde` profile | historical / staging | Older KDE session-surface profile | Not a supported compile target; use `redbear-full` / `redbear-live-full` for the tracked desktop-capable surface |
| `redbear-kde` profile | historical / staging | Older KDE session-surface profile | Not a supported compile target; use `redbear-full` for the tracked desktop-capable surface |
| bounded compositor validation path | experimental | Reaches xkbcommon init + EGL platform selection in QEMU | No complete session |
| qt6-wayland-smoke | builds, partial | Creates QWindow with colored background, runs 3 seconds | |
| QEMU graphics | usable (bounded) | Renderer is llvmpipe | Not hardware acceleration |
@@ -132,7 +132,7 @@ The repo has crossed major build-side gates:
3. **Wayland/graphics packages** — libwayland, wayland-protocols, Mesa EGL+GBM+GLES2, libdrm, libdrm_amdgpu
4. **Qt6 + D-Bus** — qtbase (7 libs + 12 plugins), qtdeclarative (11 libs), qtsvg, qtwayland, D-Bus 1.16.2
5. **KF6 + KDE-facing** — All 32 KF6 frameworks, kdecoration, plasma-wayland-protocols, kf6-kwayland, kf6-kcmutils
6. **Tracked profiles** — redbear-mini, redbear-live-mini, redbear-full, redbear-live-full
6. **Tracked profiles** — redbear-mini, redbear-full, redbear-grub
7. **Phase 1 test coverage** — 300+ unit tests across evdevd (65), udev-shim (15), firmware-loader (24), redox-drm (68), redbear-hwutils (19), and bluetooth/wifi daemons
### What is runtime-proven (limited scope)
@@ -622,7 +622,7 @@ continuity, not as future work.
| All 32 KF6 frameworks | ✅ Builds complete | Prior to this plan |
| Input stack (libevdev, libinput, evdevd, udev-shim) | ✅ Builds complete | Prior to this plan |
| Mesa EGL/GBM/GLES2 + libdrm amdgpu | ✅ Builds complete | Prior to this plan |
| Desktop profiles (`redbear-mini`, `redbear-live-mini`, `redbear-full`, `redbear-live-full`) | ✅ Builds complete | Prior to this plan |
| Desktop profiles (`redbear-mini`, `redbear-full`, `redbear-grub`) | ✅ Builds complete | Prior to this plan |
| `local/docs/DBUS-INTEGRATION-PLAN.md` | D-Bus architecture, service dependency map, and phased implementation |
| PRIME/DMA-BUF scheme ioctls | ✅ Implemented | Prior to this plan |
| KWin recipe with 5 re-enabled features | ✅ Partial build | Prior to this plan |
+99
View File
@@ -1084,3 +1084,102 @@ convenience layer. The remaining gap is the difference between **shipping minima
implementations** and **shipping full desktop-complete service contracts** for login1,
Notifications, UPower, UDisks2, and PolicyKit. NetworkManager remains deferred and is not part of
the current Red Bear OS implementation scope.
---
## Phase 3/4 D-Bus Improvement Plan (2026-04-25 Assessment)
**Assessment scope:** All Red Bear D-Bus service implementations (`redbear-sessiond`, `redbear-notifications`, `redbear-upower`, `redbear-udisks`, `redbear-polkit`), plus the dbus-daemon itself, conducted via 4 parallel evaluation agents (Oracle + 2 explore + librarian).
**Key finding:** Phase 2 (`kwin_wayland --virtual`) should work without D-Bus changes. KWin falls back to NoopSession when logind is unavailable, and the Noop backend bypasses login1 entirely.
**Key finding:** Phase 3 has one hard gate: `TakeDevice` FD passing. This cannot be bypassed.
### Assessment Summary
Fragility ratings across services:
| Service | Rating | Primary concern |
|---------|--------|-----------------|
| `redbear-sessiond` | 5/5 | login1 is the critical path for DRM compositor |
| `redbear-polkit` | 5/5 security | Always-permit is not a production security model |
| `dbus-daemon` | 2/5 | 24-line patch is stable but not validated under real session bus load |
| `redbear-notifications` | 2-3/5 | Logs to stderr only; no ActionInvoked signal |
| `redbear-upower` | 2-3/5 | Provisional ACPI surface; no Changed signal; polling not implemented |
| `redbear-udisks` | 2-3/5 | Read-only; no mount/unmount operations |
**Phase 2 assessment:** D-Bus is NOT on the critical path for `kwin_wayland --virtual`. The NoopSession backend in KWin bypasses logind entirely, which means Phase 2 compositor bring-up should succeed without D-Bus changes.
**Phase 3 hard gate:** `TakeDevice` FD passing + `PauseDevice`/`ResumeDevice` signal emission. This is required for KWin to own real DRM and input devices through the freedesktop session protocol. No bypass exists.
**Phase 4 broader surface:** `kglobalaccel` binary, `kded6` binary, `StatusNotifierWatcher`, `Inhibit` methods, session identity derivation.
### Phase 3 Gate (DRM Compositor) — Required D-Bus Changes
Four fixes are required before KWin can use real hardware devices through login1:
| # | Fix | Current state | Required change |
|---|-----|---------------|-----------------|
| 1 | `Manager.Inhibit` + `CanPowerOff`/`CanSuspend`/`CanHibernate` stubs | Missing | Return `"na"` string from each method; required by KDE's session management layer |
| 2 | `PauseDevice`/`ResumeDevice` signal emission | Declared but not emitted | Emit `uus` (major, minor, type) for PauseDevice and `uuh` (major, minor, fd) for ResumeDevice in `session.rs` when device state changes |
| 3 | Dynamic device enumeration | Static `device_map.rs` with hardcoded major/minor | Query udev-shim at runtime for major/minor -> scheme path mapping; remove hardcoded lookup table |
| 4 | Missing Session methods | `SetIdleHint`, `SetLockedHint`, `SetType`, `Terminate` not implemented | Implement these or return errors; KDE session managers call these to track session state |
### Phase 4 Gate (KDE Plasma Session) — Required D-Bus Changes
| # | Improvement | Current state | Required change |
|---|-------------|---------------|-----------------|
| 1 | `StatusNotifierWatcher` implementation | New service needed | Register `org.freedesktop.StatusNotifierWatcher` on session bus; track registered items, emit `ItemRegistered`/`ItemUnregistered` signals |
| 2 | `kglobalaccel` binary build | KDE app recipe builds library, daemon binary is a separate recipe step | Add `kglobalaccel` binary to `local/recipes/kde/kf6-kglobalaccel/` or create separate recipe |
| 3 | `kded6` binary build | KDE app recipe builds library, daemon binary is a separate recipe step | Add `kded6` binary to `local/recipes/kde/kf6-kded6/` or create separate recipe |
| 4 | Session identity derivation | Hardcoded to `c1`, `root`, `uid=0` | Query real session environment variables (`XDG_SESSION_ID`, `XDG_SEAT`) and derive identity from the actual login session |
| 5 | `UPower Changed` signal emission + polling | No signals, no polling | Emit `Changed` signal when power state changes; implement property polling for `OnBattery`, `Percentage`, `TimeToEmpty` |
| 6 | `Notifications ActionInvoked` signal + capabilities | Not implemented | Emit `ActionInvoked(uint32, string)` when user clicks notification action; expand `GetCapabilities` to include `body`, `actions`, `icon-static` |
| 7 | Stoppable daemons | Services use `pending()` with no shutdown channel | Replace `pending()` in all services with proper shutdown signal channels; enable service restart and clean shutdown |
### KWin Method-by-Method Readiness Matrix
| KWin D-Bus call | Current impl | Phase 2 needed | Phase 3 needed |
|-----------------|--------------|---------------|----------------|
| `GetSession("auto")` | via NoopSession | No (bypasses logind) | Yes |
| `TakeControl(false)` | Via login1 | No | Yes |
| `TakeDevice(226, 0)` (DRM) | Via DeviceMap | No | Yes (critical) |
| `TakeDevice(13, 64+)` (input) | Via DeviceMap | No | Yes (critical) |
| `PauseDevice` signal | Declared, not emitted | No | Yes (critical) |
| `ResumeDevice` signal | Declared, not emitted | No | Yes (critical) |
| `Seat.SwitchTo` | Via login1 | No | Yes |
| `Manager.Inhibit` | Missing | No | Yes |
| `CanPowerOff`/`CanSuspend`/`CanHibernate` | Missing | No | Yes |
| `PrepareForShutdown` | Via ACPI | No | Yes |
| `PrepareForSleep` | Declared, not emitted | No | Yes |
### Completeness by Service
| Service | Methods real | Total expected | Completeness |
|---------|-------------|---------------|--------------|
| `login1.Manager` | 3 | ~30+ | ~10% |
| `login1.Session` | 7 | ~15+ | ~47% |
| `login1.Seat` | 1 | 5 | ~20% |
| `Notifications` | 4 | ~5 | ~80% |
| `UPower` | 3 | ~5 | ~60% |
| `UDisks2` | 4 | ~8+ | ~50% |
| `PolicyKit1` | 3 | ~6+ | ~50% |
### Missing KDE D-Bus Services
| Service | Used by | Status | Impact |
|---------|---------|--------|--------|
| `org.kde.kglobalaccel` | All KDE apps (global shortcuts) | Binary missing | HIGH |
| `org.kde.kded6` | KDE daemon (status notifier, etc.) | Binary missing | HIGH |
| `org.freedesktop.StatusNotifierWatcher` | System tray | New service needed | MEDIUM |
| `org.kde.ksmserver` | Session management | Not implemented | MEDIUM |
| `org.freedesktop.ScreenSaver` | Screen locking | Not implemented | MEDIUM |
### Implementation Priority Order
1. `redbear-sessiond` Phase 3 methods (enables DRM compositor session)
2. Dynamic device enumeration (enables non-static hardware discovery)
3. Stoppable daemons (enables testing and restart)
4. `StatusNotifierWatcher` (enables system tray)
5. `UPower` polling + signals (enables battery applet)
6. Session identity improvements (enables non-root sessions)
+13 -19
View File
@@ -31,10 +31,11 @@ greeter/auth/session-launch stack on the `redbear-full` desktop path.
## Active Target Surface and Evidence Boundary
- The supported compile targets are `redbear-mini`, `redbear-live-mini`, `redbear-full`, and `redbear-live-full`.
- Desktop/graphics are available only on `redbear-full` and `redbear-live-full`.
- Older names such as `redbear-kde`, `redbear-wayland`, and `redbear-minimal*` still appear in
historical or staging material, but they are not the supported compile-target surface.
- The supported compile targets are `redbear-mini`, `redbear-full`, and `redbear-grub`.
- Desktop/graphics are available only on `redbear-full`.
- Older names such as `redbear-kde`, `redbear-wayland`, `redbear-minimal*`, `redbear-live-mini`,
and `redbear-live-full` still appear in historical or staging material, but they are not the
supported compile-target surface.
- The greeter/login path is currently an **experimental build/integration surface** on `redbear-full`;
it is not yet a runtime-validated end-to-end desktop-login claim.
@@ -51,8 +52,8 @@ greeter/auth/session-launch stack on the `redbear-full` desktop path.
| Mesa EGL+GBM+GLES2 | **builds** | Software path via LLVMpipe proven in QEMU; hardware path not proven |
| libdrm amdgpu | **builds** | Package-level success only |
| Input stack | **builds, enumerates** | evdevd (65 tests), libevdev, libinput, seatd present; evdevd registers scheme at boot; end-to-end compositor input path unproven |
| D-Bus | **builds, usable (bounded)** | System bus wired in `redbear-full`; session bus incomplete (redbear-sessiond login1 broker only) |
| redbear-sessiond | **builds, scaffold** | org.freedesktop.login1 D-Bus session broker — Rust daemon (zbus 5), wired on the `redbear-full` desktop path; now includes runtime control updates used by the greeter/auth session handoff |
| D-Bus | **builds, bounded (in improvement)** | System bus wired in `redbear-full`; session bus incomplete; Phase 3/4 improvement plan active; completeness: login1.Manager ~10%, login1.Session ~47%, login1.Seat ~20%, Notifications ~80%, UPower ~60%, UDisks2 ~50%, PolicyKit1 ~50%; `StatusNotifierWatcher` is the new service being added in Phase 4 |
| redbear-sessiond | **builds, scaffold (Phase 3/4 improvement active)** | org.freedesktop.login1 D-Bus session broker — Rust daemon (zbus 5), wired on the `redbear-full` desktop path; Phase 3 hard gate is TakeDevice FD passing plus PauseDevice/ResumeDevice signal emission; Priority 1 in Phase 3/4 improvement plan |
| redbear-authd | **builds** | Privileged local-user auth daemon; `/etc/passwd`/`/etc/shadow`/`/etc/group` parsing, SHA-256/SHA-512 crypt verification, bounded lockout, target-side recipe build proven |
| redbear-session-launch | **builds** | User-session bootstrap tool; runtime-dir/env setup, uid/gid handoff, dbus-run-session → `redbear-kde-session`, target-side recipe build proven |
| redbear-greeterd | **builds, experimental** | Root-owned greeter orchestrator; UI/auth socket protocol, bounded restart policy, return-to-greeter daemon logic, crate tests pass; end-to-end runtime proof still pending |
@@ -73,9 +74,8 @@ greeter/auth/session-launch stack on the `redbear-full` desktop path.
| validation compositor runtime | **experimental** | Reaches early init in QEMU; no complete session |
| validation profile | **builds, boots** | Bounded Wayland runtime profile |
| `redbear-full` profile | **builds, boots** | Active desktop/graphics compile surface; now owns the experimental greeter/auth/session-launch integration path |
| `redbear-live-full` profile | **builds** | Live image following the active desktop/graphics target |
| `redbear-grub` profile | **builds** | Text-only with GRUB chainload for bare-metal multi-boot |
| `redbear-mini` profile | **builds** | Minimal non-desktop compile target |
| `redbear-live-mini` profile | **builds** | Minimal live image target |
| `redbear-hwutils` | **builds** | lspci/lsusb tools; 19 unit tests (PCI location parsing, USB device description, argument handling) |
## Profile View
@@ -87,11 +87,11 @@ greeter/auth/session-launch stack on the `redbear-full` desktop path.
- **Use for:** Desktop integration testing, greeter/login bring-up, and bounded desktop/network plumbing validation
- **Do not overclaim:** This profile proves bounded QEMU desktop/network plumbing only. It does not by itself close the Wi-Fi implementation plan's later real-hardware Phase W5 reporting/recovery gate.
### `redbear-live-full`
### `redbear-grub`
- **Role:** Live/demo/recovery image layered on the active desktop target
- **Current truth:** Follows `redbear-full`; desktop/graphics-capable live image, but the greeter/login surface remains experimental until end-to-end proof exists
- **Use for:** Demo, install, and bounded live-media validation on the current desktop surface
- **Role:** Text-only target with GRUB boot manager for bare-metal multi-boot
- **Current truth:** Follows `redbear-mini`; text-only with GRUB chainload ESP layout, no desktop/graphics
- **Use for:** Bare-metal multi-boot, recovery with GRUB menu, and install workflows requiring GRUB
### `redbear-mini`
@@ -99,12 +99,6 @@ greeter/auth/session-launch stack on the `redbear-full` desktop path.
- **Current truth:** No desktop/graphics path; recovery and non-desktop integration surface only. TUI recovery is bound to VT activation through `29_activate_console.service` followed by `30_console.service`/`31_debug_console.service`.
- **Use for:** Minimal runtime bring-up, subsystem validation, and non-desktop packaging checks
### `redbear-live-mini`
- **Role:** Minimal live image target
- **Current truth:** No desktop/graphics path; live/recovery-oriented minimal image surface
- **Use for:** Minimal live boot and recovery workflows
## Current Blockers
### 1. Runtime trust trails build success (Phase 1 gate)
@@ -197,7 +191,7 @@ QtNetwork is intentionally disabled because relibc networking is too narrow. Thi
The Red Bear desktop stack has crossed major build-side gates and one important bounded runtime gate:
- All Qt6 core modules, all 32 KF6 frameworks, Mesa EGL/GBM/GLES2, and D-Bus build
- Four supported compile targets exist, with desktop/graphics on `redbear-full` and `redbear-live-full`
- Four supported compile targets exist, with desktop/graphics on `redbear-full`
- the Red Bear-native greeter/login path now has a bounded passing QEMU proof (`GREETER_HELLO=ok`, `GREETER_INVALID=ok`, `GREETER_VALID=ok`)
- relibc compatibility is materially stronger than before
- Phase 1 test coverage is comprehensive: 300+ unit tests across all Phase 1 daemons (evdevd 65, udev-shim 15, firmware-loader 24, redox-drm 68, redbear-hwutils 19, bluetooth/wifi 209)
@@ -75,9 +75,9 @@ recovery model.
This plan assumes the Red Bear desktop direction converges on **one KDE-on-Wayland path**.
Current implementation answer: the first tracked owner is `redbear-full` (and therefore
`redbear-live-full` for live media). Older names such as `redbear-kde` may still appear in
historical or staging material, but they are not the supported compile-target surface for this plan.
Current implementation answer: the first tracked owner is `redbear-full`. Older names such
as `redbear-kde` may still appear in historical or staging material, but they are not the
supported compile-target surface for this plan.
---
@@ -866,8 +866,7 @@ The greeter **recipe**, not the config fragment, should own staged runtime artif
4. Is `dbus-run-session` reliable enough on Red Bear, or should the current `dbus-launch` path remain the first shipped session-bus strategy?
5. At what point should the project consider SDDM-class integration again, if ever?
Current answer to (1): **`redbear-full` first**, with `redbear-live-full` inheriting that path for
live media.
Current answer to (1): **`redbear-full` first**.
Current answer to (2): **traditional `/etc/shadow` SHA-512-crypt / SHA-256-crypt first** (`$6$` / `$5$`),
with narrower support preferred over premature multi-format sprawl.
+6 -6
View File
@@ -3,8 +3,8 @@
**Date:** 2026-04-17
**Status:** Fully implemented (build-tested, not yet runtime boot-tested). ESP formatted as FAT32
per UEFI spec. Both Phase 1 (post-build script) and Phase 2 (installer-native) are wired.
**Remaining:** Runtime UEFI boot validation in QEMU (`make all CONFIG_NAME=redbear-full-grub && make qemu`).
**Prerequisite:** The `grub` package is included in `redbear-full-grub.toml` for clean-tree builds.
**Remaining:** Runtime UEFI boot validation in QEMU (`make all CONFIG_NAME=redbear-grub && make qemu`).
**Prerequisite:** The `grub` package is included in `redbear-grub.toml` for clean-tree builds.
**Approach:** Option A — GRUB as boot manager, chainloading Redox bootloader
## Overview
@@ -257,7 +257,7 @@ ESP layout automatically.
### Config Usage
```toml
# config/redbear-full-grub.toml
# config/redbear-grub.toml
include = ["redbear-full.toml"]
[general]
@@ -271,7 +271,7 @@ Or via CLI (note: INSTALLER_OPTS replaces defaults, so --cookbook=. must be incl
make all CONFIG_NAME=redbear-full INSTALLER_OPTS="--cookbook=. --bootloader grub"
```
**Note:** The config file approach (`redbear-full-grub.toml`) is preferred over the CLI flag
**Note:** The config file approach (`redbear-grub.toml`) is preferred over the CLI flag
because INSTALLER_OPTS completely replaces the default value (`--cookbook=.`) rather than
appending to it. Omitting `--cookbook=.` breaks local package resolution for GRUB.
@@ -356,7 +356,7 @@ make qemu
make r.grub
# Build image with GRUB config (installer fetches GRUB automatically)
make all CONFIG_NAME=redbear-full-grub
make all CONFIG_NAME=redbear-grub
# Or via CLI flag
make all CONFIG_NAME=redbear-full INSTALLER_OPTS="--bootloader grub --cookbook=."
@@ -376,7 +376,7 @@ make qemu
CI=1 ./target/release/repo cook grub
# Verify host-side installer accepts --bootloader flag
build/fstools/bin/redox_installer --bootloader=grub --config=config/redbear-full-grub.toml --list-packages
build/fstools/bin/redox_installer --bootloader=grub --config=config/redbear-grub.toml --list-packages
# Verify fat_tool.py operations
python3 local/scripts/fat_tool.py --help
@@ -681,7 +681,7 @@ helper hardening comes before broad driver cleanup, and runtime-proof/observabil
**Verification**
- build passes: `CI=1 make r.base CONFIG_NAME=redbear-live-mini ARCH=x86_64`
- build passes: `CI=1 make r.base CONFIG_NAME=redbear-mini ARCH=x86_64`
- downstream consumers compile without errors
### Wave 4 — Convert highest-risk consumers
@@ -740,7 +740,7 @@ sites converted on the PCIe ECAM/DTB/MCFG startup path). Only Mutex `.lock().unw
**Verification**
- `CI=1 make cr.base CONFIG_NAME=redbear-live-mini ARCH=x86_64` — zero errors, build successful
- `CI=1 make cr.base CONFIG_NAME=redbear-mini ARCH=x86_64` — zero errors, build successful
- per-driver grep verified zero remaining panic-grade calls (only Mutex `.lock().unwrap()` kept)
### Wave 5 — Improve observability and proof
+10 -15
View File
@@ -27,9 +27,8 @@ USB plan uses:
| Profile | Intent | Key Fragments | Current support language |
|---|---|---|---|
| `redbear-mini` | Console + storage + wired-network baseline | `minimal.toml`, `redbear-legacy-base.toml`, `redbear-device-services.toml`, `redbear-netctl.toml` | builds / primary validation baseline / DHCP boot profile enabled / input-runtime substrate wired / USB: daemons built via base and targeted for bounded mini-profile validation |
| `redbear-live-mini` | Live/recovery form of the mini baseline | `redbear-live-minimal.toml`, `redbear-minimal.toml` | builds / live media variant of the mini profile for real bare metal / desktop graphics intentionally absent |
| `redbear-grub` | Text-only with GRUB boot manager | `redbear-mini.toml`, `redbear-grub-policy.toml` | builds / live media variant with GRUB chainload for real bare metal / desktop graphics intentionally absent |
| `redbear-full` | Desktop/network/session plumbing target | `desktop.toml`, `redbear-legacy-base.toml`, `redbear-legacy-desktop.toml`, `redbear-device-services.toml`, `redbear-netctl.toml`, `redbear-greeter-services.toml` | builds / boots in QEMU / active desktop-capable compile target / support claims remain evidence-qualified |
| `redbear-live-full` | Live/recovery form of the full desktop target | `redbear-live-full.toml`, `redbear-full.toml` | builds / live desktop-capable image for real bare metal / inherits the full target surface |
## Profile Notes
@@ -46,9 +45,10 @@ USB plan uses:
are bounded validation slices layered on top of the tracked compile targets, not additional
compile targets.
### `redbear-live-mini`
### `redbear-grub`
- Carries the same bounded non-graphics intent as `redbear-mini`, but in live/recovery image form.
- Text-only console/recovery target with GRUB boot manager for multi-boot bare-metal workflows.
- Inherits the same non-graphics intent as `redbear-mini`, but with GRUB chainload ESP layout.
- Should not grow desktop/session assumptions.
### `redbear-full`
@@ -56,22 +56,17 @@ USB plan uses:
- Desktop-capable tracked target for the current Red Bear session/network/runtime plumbing surface.
- Carries the broader D-Bus, greeter, seat, and desktop-oriented service surface.
### `redbear-live-full`
- Live/demo/recovery form of the full desktop-capable target.
- Inherits the same desktop-target assumptions as `redbear-full`, but for live media workflows.
### Historical notes
- Older names such as `redbear-minimal`, `redbear-desktop`, `redbear-wayland`, `redbear-kde`, and
`redbear-live` remain in older docs and some implementation details, but they are not the current
supported compile-target surface.
- Older names such as `redbear-minimal`, `redbear-desktop`, `redbear-wayland`, `redbear-kde`,
`redbear-live`, `redbear-live-mini`, and `redbear-live-full` remain in older docs and some
implementation details, but they are not the current supported compile-target surface.
### `redbear-bluetooth-experimental`
- Standalone tracked profile for the first in-tree Bluetooth slice instead of a blanket claim about
all Red Bear images.
- Extends `redbear-minimal` so the baseline runtime tooling is already present, then adds only the
- Extends `redbear-mini` so the baseline runtime tooling is already present, then adds only the
bounded Bluetooth pieces on top.
- Current path under active validation: QEMU/UEFI boot to login prompt plus guest-side `redbear-bluetooth-battery-check`, targeting repeated in-boot reruns, daemon-restart coverage, and one experimental battery-sensor Battery Level read-only workload.
- Current support language is intentionally narrow: explicit-startup only, USB-attached transport,
@@ -82,7 +77,7 @@ USB plan uses:
- Standalone tracked profile for the current bounded Intel Wi-Fi slice instead of implying that the
wider desktop profiles already carry the full driver stack.
- Extends `redbear-minimal` so the baseline firmware/input/reporting/profile-manager surface stays
- Extends `redbear-mini` so the baseline firmware/input/reporting/profile-manager surface stays
inherited while the Intel Wi-Fi driver package and bounded validation role remain isolated here.
- Includes the Intel driver package (`redbear-iwlwifi`) in addition to the shared firmware,
control-plane, reporting, and profile-manager pieces.
@@ -109,6 +104,6 @@ USB plan uses:
- USB error handling and correctness carry significant Red Bear patches over upstream; see
`local/patches/base/redox.patch` and `local/docs/USB-IMPLEMENTATION-PLAN.md` for details.
- The in-tree mini image is still assembled through legacy `redbear-minimal*` config files in some
places, but the supported compile-target names are `redbear-mini` and `redbear-live-mini`.
places, but the supported compile-target names are `redbear-mini` and `redbear-grub`.
- `redbear-bluetooth-experimental` uses USB only as a transport for BLE dongles; it does not make a
general USB-class-autospawn claim.
+5 -6
View File
@@ -126,13 +126,12 @@ In-guest quick checks:
## Compile-target note
Red Bear has exactly four compile targets:
Red Bear has exactly three compile targets:
- `redbear-mini`
- `redbear-live-mini`
- `redbear-full`
- `redbear-live-full`
- `redbear-grub`
Older names such as `redbear-desktop`, `redbear-wayland`, `redbear-kde`, and `redbear-minimal` may
still appear in historical notes or implementation details, but they are not the supported
compile-target surface.
Older names such as `redbear-desktop`, `redbear-wayland`, `redbear-kde`, `redbear-minimal`,
`redbear-live-mini`, and `redbear-live-full` may still appear in historical notes or
implementation details, but they are not the supported compile-target surface.
+11 -20
View File
@@ -18,13 +18,11 @@ reproducible, reviewable, and upstream-friendly.
Tracked Red Bear profiles are:
- `redbear-minimal`
- `redbear-bluetooth-experimental`
- `redbear-desktop`
- `redbear-mini`
- `redbear-full`
- `redbear-wayland`
- `redbear-kde`
- `redbear-live`
- `redbear-grub`
- `redbear-bluetooth-experimental`
- `redbear-wifi-experimental`
Every user-visible feature should name which profile(s) it belongs to.
@@ -62,7 +60,7 @@ why it is intentionally excluded.
## Profile Intent
### `redbear-minimal`
### `redbear-mini`
Primary validation baseline: console, storage, package flow, and wired networking.
@@ -71,25 +69,18 @@ Primary validation baseline: console, storage, package flow, and wired networkin
First bounded Bluetooth validation profile: explicit-startup, USB-attached, BLE-first, and
experimental only.
### `redbear-desktop`
Supplementary integration support profile for shared Red Bear runtime services beneath the tracked KWin target.
### `redbear-full`
Expanded integration slice that includes more runtime pieces and graphics-path bring-up beneath the tracked KWin target.
Desktop-capable tracked target for the current Red Bear session/network/runtime plumbing surface,
including graphics-path bring-up beneath the tracked KWin direction.
### `redbear-wayland`
### `redbear-grub`
Dedicated Wayland runtime validation profile layered above the current Red Bear service baseline and subordinate to the tracked KWin direction.
Text-only console/recovery target with GRUB boot manager for bare-metal multi-boot workflows.
### `redbear-kde`
### `redbear-wifi-experimental`
Dedicated KDE/Plasma bring-up profile and tracked forward desktop target.
### `redbear-live`
Live and recovery variant layered on top of the tracked KWin desktop target.
Bounded Intel Wi-Fi validation profile layered on the mini baseline.
## Change Checklist
@@ -24,9 +24,6 @@ static const struct
std::unique_ptr<Session> Session::create()
{
#ifdef Q_OS_REDOX
return NoopSession::create();
#else
for (const auto &sessionInfo : s_availableSessions) {
std::unique_ptr<Session> session = sessionInfo.createFunc();
if (session) {
@@ -34,20 +31,19 @@ std::unique_ptr<Session> Session::create()
}
}
return nullptr;
#endif
}
std::unique_ptr<Session> Session::create(Type type)
{
#ifdef Q_OS_REDOX
switch (type) {
case Type::Logind:
return NoopSession::create();
case Type::ConsoleKit:
return ConsoleKitSession::create();
case Type::Noop:
return NoopSession::create();
for (const auto &sessionInfo : s_availableSessions) {
if (sessionInfo.type == type) {
if (auto session = sessionInfo.createFunc()) {
return session;
}
}
}
return NoopSession::create();
#else
for (const auto &sessionInfo : s_availableSessions) {
if (sessionInfo.type == type) {
@@ -0,0 +1,3 @@
[D-BUS Service]
Name=org.freedesktop.StatusNotifierWatcher
Exec=/usr/bin/redbear-statusnotifierwatcher
@@ -40,7 +40,7 @@ impl Notifications {
_app_icon: &str,
summary: &str,
body: &str,
_actions: Vec<String>,
actions: Vec<String>,
_hints: HashMap<String, Value<'_>>,
_expire_timeout: i32,
) -> u32 {
@@ -48,6 +48,10 @@ impl Notifications {
eprintln!("notification: [{app_name}] {summary}: {body}");
for chunk in actions.chunks_exact(2) {
eprintln!("notification {id}: action key '{}'", chunk[0]);
}
id
}
@@ -64,7 +68,11 @@ impl Notifications {
#[zbus(name = "GetCapabilities")]
fn get_capabilities(&self) -> Vec<String> {
vec!["body".to_owned()]
vec![
"body".to_owned(),
"body-markup".to_owned(),
"actions".to_owned(),
]
}
#[zbus(name = "GetServerInformation")]
@@ -88,6 +96,13 @@ impl Notifications {
id: u32,
reason: u32,
) -> zbus::Result<()>;
#[zbus(signal, name = "ActionInvoked")]
async fn action_invoked(
signal_emitter: &SignalEmitter<'_>,
id: u32,
action_key: &str,
) -> zbus::Result<()>;
}
enum Command {
@@ -115,25 +130,32 @@ fn parse_args() -> Result<Command, String> {
}
}
fn spawn_signal_handler(shutdown_tx: tokio::sync::watch::Sender<bool>) {
tokio::spawn(async move {
#[cfg(unix)]
async fn wait_for_shutdown() -> Result<(), Box<dyn Error>> {
{
use tokio::signal::unix::{SignalKind, signal};
let mut terminate = signal(SignalKind::terminate())?;
if let Ok(mut sigterm) = signal(SignalKind::terminate()) {
tokio::select! {
_ = terminate.recv() => Ok(()),
_ = tokio::signal::ctrl_c() => Ok(()),
_ = sigterm.recv() => {},
_ = tokio::signal::ctrl_c() => {},
}
} else {
let _ = tokio::signal::ctrl_c().await;
}
}
#[cfg(not(unix))]
async fn wait_for_shutdown() -> Result<(), Box<dyn Error>> {
tokio::signal::ctrl_c().await?;
Ok(())
{
let _ = tokio::signal::ctrl_c().await;
}
let _ = shutdown_tx.send(true);
});
}
async fn run_daemon() -> Result<(), Box<dyn Error>> {
let (shutdown_tx, mut shutdown_rx) = tokio::sync::watch::channel(false);
spawn_signal_handler(shutdown_tx);
let _connection = ConnectionBuilder::session()?
.name(BUS_NAME)?
.serve_at(OBJECT_PATH, Notifications::new())?
@@ -142,8 +164,8 @@ async fn run_daemon() -> Result<(), Box<dyn Error>> {
eprintln!("redbear-notifications: registered {BUS_NAME} on the session bus");
wait_for_shutdown().await?;
eprintln!("redbear-notifications: received shutdown signal, exiting cleanly");
let _ = shutdown_rx.changed().await;
eprintln!("redbear-notifications: shutdown signal received, exiting cleanly");
Ok(())
}
@@ -1,4 +1,4 @@
use std::{collections::HashMap, env, error::Error, path::Path, process, thread, time::Duration};
use std::{collections::HashMap, env, error::Error, process, time::Duration};
use tokio::runtime::Builder as RuntimeBuilder;
use zbus::{
@@ -77,29 +77,26 @@ fn system_connection_builder() -> Result<ConnectionBuilder<'static>, Box<dyn Err
}
}
#[cfg(all(unix, not(target_os = "redox")))]
async fn wait_for_shutdown() -> Result<(), Box<dyn Error>> {
fn spawn_signal_handler(shutdown_tx: tokio::sync::watch::Sender<bool>) {
tokio::spawn(async move {
#[cfg(unix)]
{
use tokio::signal::unix::{SignalKind, signal};
let mut terminate = signal(SignalKind::terminate())?;
if let Ok(mut sigterm) = signal(SignalKind::terminate()) {
tokio::select! {
_ = terminate.recv() => Ok(()),
_ = tokio::signal::ctrl_c() => Ok(()),
_ = sigterm.recv() => {},
_ = tokio::signal::ctrl_c() => {},
}
} else {
let _ = tokio::signal::ctrl_c().await;
}
}
#[cfg(target_os = "redox")]
async fn wait_for_shutdown() -> Result<(), Box<dyn Error>> {
std::future::pending::<()>().await;
#[allow(unreachable_code)]
Ok(())
#[cfg(not(unix))]
{
let _ = tokio::signal::ctrl_c().await;
}
#[cfg(all(not(unix), not(target_os = "redox")))]
async fn wait_for_shutdown() -> Result<(), Box<dyn Error>> {
tokio::signal::ctrl_c().await?;
Ok(())
let _ = shutdown_tx.send(true);
});
}
#[interface(name = "org.freedesktop.PolicyKit1.Authority")]
@@ -151,6 +148,9 @@ impl PolicyKitAuthority {
async fn run_daemon() -> Result<(), Box<dyn Error>> {
wait_for_dbus_socket().await;
let (shutdown_tx, mut shutdown_rx) = tokio::sync::watch::channel(false);
spawn_signal_handler(shutdown_tx);
let mut last_err = None;
for attempt in 1..=5 {
let _authority_path = parse_object_path(AUTHORITY_PATH)?;
@@ -163,13 +163,16 @@ async fn run_daemon() -> Result<(), Box<dyn Error>> {
{
Ok(connection) => {
eprintln!("redbear-polkit: registered {BUS_NAME} on the system bus");
wait_for_shutdown().await?;
let _ = shutdown_rx.changed().await;
eprintln!("redbear-polkit: shutdown signal received, exiting cleanly");
drop(connection);
return Ok(());
}
Err(err) => {
if attempt < 5 {
eprintln!("redbear-polkit: attempt {attempt}/5 failed ({err}), retrying in 2s...");
eprintln!(
"redbear-polkit: attempt {attempt}/5 failed ({err}), retrying in 2s..."
);
tokio::time::sleep(Duration::from_secs(2)).await;
}
last_err = Some(err.into());
@@ -3,6 +3,7 @@ use std::{
io::{BufRead, BufReader},
os::unix::{fs::PermissionsExt, net::UnixListener},
path::Path,
sync::Arc,
};
use serde::Deserialize;
@@ -24,14 +25,14 @@ enum ControlMessage {
ResetSession {
vt: u32,
},
Shutdown,
}
fn apply_message(runtime: &SharedRuntime, message: ControlMessage) {
let Ok(mut runtime) = runtime.write() else {
eprintln!("redbear-sessiond: runtime state is poisoned");
return;
};
fn apply_message(
runtime: &SharedRuntime,
shutdown_tx: &tokio::sync::watch::Sender<bool>,
message: ControlMessage,
) {
match message {
ControlMessage::SetSession {
username,
@@ -40,6 +41,10 @@ fn apply_message(runtime: &SharedRuntime, message: ControlMessage) {
leader,
state,
} => {
let Ok(mut runtime) = runtime.write() else {
eprintln!("redbear-sessiond: runtime state is poisoned");
return;
};
runtime.username = username;
runtime.uid = uid;
runtime.vt = vt;
@@ -48,6 +53,10 @@ fn apply_message(runtime: &SharedRuntime, message: ControlMessage) {
runtime.active = true;
}
ControlMessage::ResetSession { vt } => {
let Ok(mut runtime) = runtime.write() else {
eprintln!("redbear-sessiond: runtime state is poisoned");
return;
};
runtime.username = String::from("root");
runtime.uid = 0;
runtime.vt = vt;
@@ -55,10 +64,18 @@ fn apply_message(runtime: &SharedRuntime, message: ControlMessage) {
runtime.state = String::from("closing");
runtime.active = true;
}
ControlMessage::Shutdown => {
eprintln!("redbear-sessiond: shutdown requested via control socket");
let _ = shutdown_tx.send(true);
}
}
}
pub fn start_control_socket(runtime: SharedRuntime) {
pub fn start_control_socket(
runtime: SharedRuntime,
shutdown_tx: tokio::sync::watch::Sender<bool>,
) {
let shutdown_tx = Arc::new(shutdown_tx);
std::thread::spawn(move || {
if Path::new(CONTROL_SOCKET_PATH).exists() {
if let Err(err) = fs::remove_file(CONTROL_SOCKET_PATH) {
@@ -79,6 +96,7 @@ pub fn start_control_socket(runtime: SharedRuntime) {
eprintln!("redbear-sessiond: failed to chmod control socket: {err}");
}
let shutdown_ref = Arc::clone(&shutdown_tx);
for stream in listener.incoming() {
let Ok(stream) = stream else {
continue;
@@ -89,7 +107,7 @@ pub fn start_control_socket(runtime: SharedRuntime) {
continue;
}
match serde_json::from_str::<ControlMessage>(line.trim()) {
Ok(message) => apply_message(&runtime, message),
Ok(message) => apply_message(&runtime, &shutdown_ref, message),
Err(err) => eprintln!("redbear-sessiond: invalid control message: {err}"),
}
}
@@ -101,12 +119,18 @@ mod tests {
use super::*;
use crate::runtime_state::shared_runtime;
fn test_shutdown_channel() -> (tokio::sync::watch::Sender<bool>, tokio::sync::watch::Receiver<bool>) {
tokio::sync::watch::channel(false)
}
#[test]
fn set_session_message_updates_runtime_state() {
let runtime = shared_runtime();
let (tx, _rx) = test_shutdown_channel();
apply_message(
&runtime,
&tx,
ControlMessage::SetSession {
username: String::from("user"),
uid: 1000,
@@ -128,9 +152,11 @@ mod tests {
#[test]
fn reset_session_message_restores_root_scaffold() {
let runtime = shared_runtime();
let (tx, _rx) = test_shutdown_channel();
apply_message(
&runtime,
&tx,
ControlMessage::SetSession {
username: String::from("user"),
uid: 1000,
@@ -139,7 +165,7 @@ mod tests {
state: String::from("active"),
},
);
apply_message(&runtime, ControlMessage::ResetSession { vt: 3 });
apply_message(&runtime, &tx, ControlMessage::ResetSession { vt: 3 });
let runtime = runtime.read().expect("runtime lock should remain healthy");
assert_eq!(runtime.username, "root");
@@ -170,7 +196,26 @@ mod tests {
assert_eq!(leader, 99);
assert_eq!(state, "online");
}
ControlMessage::ResetSession { .. } => panic!("expected set_session message"),
ControlMessage::ResetSession { .. } | ControlMessage::Shutdown => {
panic!("expected set_session message")
}
}
}
#[test]
fn shutdown_message_sends_true_on_channel() {
let runtime = shared_runtime();
let (tx, mut rx) = test_shutdown_channel();
apply_message(&runtime, &tx, ControlMessage::Shutdown);
assert!(*rx.borrow_and_update());
}
#[test]
fn shutdown_message_parses_from_json() {
let message = serde_json::from_str::<ControlMessage>(r#"{"type":"shutdown"}"#)
.expect("shutdown message should parse");
assert!(matches!(message, ControlMessage::Shutdown));
}
}
@@ -14,6 +14,7 @@ pub struct DeviceMap {
}
impl DeviceMap {
#[cfg(test)]
pub fn new() -> Self {
let static_paths = HashMap::from([
((226, 0), String::from("/scheme/drm/card0")),
@@ -31,6 +32,31 @@ impl DeviceMap {
Self { static_paths }
}
/// Build a device map that merges static entries with dynamically discovered
/// devices by scanning `/scheme/drm/card*` and `/dev/input/event*` at startup.
/// For each discovered path, stat is used to read the rdev (device number).
/// Entries with a nonzero rdev are inserted into the map; static entries are
/// kept as fallback when rdev is unavailable or zero.
pub fn discover() -> Self {
let mut paths = HashMap::from([
((226, 0), String::from("/scheme/drm/card0")),
((226, 1), String::from("/scheme/drm/card1")),
((13, 64), String::from("/dev/input/event0")),
((13, 65), String::from("/dev/input/event1")),
((13, 66), String::from("/dev/input/event2")),
((13, 67), String::from("/dev/input/event3")),
((29, 0), String::from("/dev/fb0")),
((1, 1), String::from("/scheme/null")),
((1, 5), String::from("/scheme/zero")),
((1, 8), String::from("/scheme/rand")),
]);
discover_scheme_drm(&mut paths);
discover_dev_input(&mut paths);
Self { static_paths: paths }
}
pub fn resolve(&self, major: u32, minor: u32) -> Option<String> {
if let Some(path) = self.static_paths.get(&(major, minor)) {
return Some(path.clone());
@@ -83,6 +109,74 @@ impl DeviceMap {
}
}
/// Scan `/scheme/drm/` for `card*` entries and merge any with a nonzero rdev
/// into the provided map. Static entries are not overwritten.
fn discover_scheme_drm(paths: &mut HashMap<(u32, u32), String>) {
let entries = match fs::read_dir("/scheme/drm") {
Ok(entries) => entries,
Err(_) => return,
};
for entry in entries.flatten() {
let path = entry.path();
let Some(name) = path.file_name().and_then(|n| n.to_str()) else {
continue;
};
if !name.starts_with("card") {
continue;
}
#[cfg(unix)]
if let Ok(metadata) = fs::metadata(&path) {
let rdev = metadata.rdev();
if rdev != 0 {
let major = dev_major(rdev);
let minor = dev_minor(rdev);
paths
.entry((major, minor))
.or_insert_with(|| path.to_string_lossy().into_owned());
}
}
#[cfg(not(unix))]
let _ = &path;
}
}
/// Scan `/dev/input/` for `event*` entries and merge any with a nonzero rdev
/// into the provided map. Static entries are not overwritten.
fn discover_dev_input(paths: &mut HashMap<(u32, u32), String>) {
let entries = match fs::read_dir("/dev/input") {
Ok(entries) => entries,
Err(_) => return,
};
for entry in entries.flatten() {
let path = entry.path();
let Some(name) = path.file_name().and_then(|n| n.to_str()) else {
continue;
};
if !name.starts_with("event") {
continue;
}
#[cfg(unix)]
if let Ok(metadata) = fs::metadata(&path) {
let rdev = metadata.rdev();
if rdev != 0 {
let major = dev_major(rdev);
let minor = dev_minor(rdev);
paths
.entry((major, minor))
.or_insert_with(|| path.to_string_lossy().into_owned());
}
}
#[cfg(not(unix))]
let _ = &path;
}
}
fn candidate_paths() -> Vec<PathBuf> {
let mut paths = Vec::new();
@@ -162,4 +256,12 @@ mod tests {
assert_eq!(dev_major(event), 13);
assert_eq!(dev_minor(event), 67);
}
#[test]
fn discover_returns_static_entries_when_no_dirs() {
let map = super::DeviceMap::discover();
assert!(map.resolve(226, 0).is_some());
assert!(map.resolve(13, 64).is_some());
assert!(map.resolve(29, 0).is_some());
}
}
@@ -84,7 +84,7 @@ fn system_connection_builder() -> Result<ConnectionBuilder<'static>, Box<dyn Err
}
#[cfg(all(unix, not(target_os = "redox")))]
async fn wait_for_shutdown() -> Result<(), Box<dyn Error>> {
async fn wait_for_shutdown(mut shutdown_rx: tokio::sync::watch::Receiver<bool>) -> Result<(), Box<dyn Error>> {
use tokio::signal::unix::{SignalKind, signal};
let mut terminate = signal(SignalKind::terminate())?;
@@ -92,35 +92,44 @@ async fn wait_for_shutdown() -> Result<(), Box<dyn Error>> {
tokio::select! {
_ = terminate.recv() => Ok(()),
_ = tokio::signal::ctrl_c() => Ok(()),
_ = shutdown_rx.changed() => Ok(()),
}
}
#[cfg(target_os = "redox")]
async fn wait_for_shutdown() -> Result<(), Box<dyn Error>> {
std::future::pending::<()>().await;
async fn wait_for_shutdown(mut shutdown_rx: tokio::sync::watch::Receiver<bool>) -> Result<(), Box<dyn Error>> {
tokio::select! {
_ = std::future::pending::<()>() => Ok(()),
_ = shutdown_rx.changed() => Ok(()),
}
#[allow(unreachable_code)]
Ok(())
}
#[cfg(all(not(unix), not(target_os = "redox")))]
async fn wait_for_shutdown() -> Result<(), Box<dyn Error>> {
tokio::signal::ctrl_c().await?;
Ok(())
async fn wait_for_shutdown(mut shutdown_rx: tokio::sync::watch::Receiver<bool>) -> Result<(), Box<dyn Error>> {
tokio::select! {
_ = tokio::signal::ctrl_c() => Ok(()),
_ = shutdown_rx.changed() => Ok(()),
}
}
async fn run_daemon() -> Result<(), Box<dyn Error>> {
wait_for_dbus_socket().await;
let (shutdown_tx, shutdown_rx) = tokio::sync::watch::channel(false);
let mut last_err = None;
for attempt in 1..=5 {
let session_path = parse_object_path(SESSION_PATH)?;
let seat_path = parse_object_path(SEAT_PATH)?;
let user_path = parse_object_path(USER_PATH)?;
let runtime = shared_runtime();
let device_map = DeviceMap::discover();
let session = LoginSession::new(seat_path.clone(), user_path, DeviceMap::new(), runtime.clone());
let session = LoginSession::new(seat_path.clone(), user_path.clone(), device_map, runtime.clone());
let seat = LoginSeat::new(session_path.clone(), runtime.clone());
let manager = LoginManager::new(session_path, seat_path, runtime.clone());
let manager = LoginManager::new(session_path, seat_path, user_path, runtime.clone());
match system_connection_builder()?
.name(BUS_NAME)?
@@ -132,9 +141,9 @@ async fn run_daemon() -> Result<(), Box<dyn Error>> {
{
Ok(connection) => {
eprintln!("redbear-sessiond: registered {BUS_NAME} on the system bus");
control::start_control_socket(runtime.clone());
control::start_control_socket(runtime.clone(), shutdown_tx.clone());
tokio::spawn(acpi_watcher::watch_and_emit(connection.clone(), runtime.clone()));
wait_for_shutdown().await?;
wait_for_shutdown(shutdown_rx).await?;
drop(connection);
return Ok(());
}
@@ -1,36 +1,55 @@
use std::{
os::fd::OwnedFd as StdOwnedFd,
os::unix::net::UnixStream,
sync::{Arc, Mutex},
};
use zbus::{
fdo,
interface,
object_server::SignalEmitter,
zvariant::OwnedObjectPath,
zvariant::{OwnedFd, OwnedObjectPath},
};
use crate::runtime_state::SharedRuntime;
use crate::runtime_state::{InhibitorEntry, SharedRuntime};
#[derive(Clone, Debug)]
pub struct LoginManager {
runtime: SharedRuntime,
session_path: OwnedObjectPath,
seat_path: OwnedObjectPath,
user_path: OwnedObjectPath,
inhibitor_fds: Arc<Mutex<Vec<StdOwnedFd>>>,
}
impl LoginManager {
pub fn new(session_path: OwnedObjectPath, seat_path: OwnedObjectPath, runtime: SharedRuntime) -> Self {
pub fn new(
session_path: OwnedObjectPath,
seat_path: OwnedObjectPath,
user_path: OwnedObjectPath,
runtime: SharedRuntime,
) -> Self {
Self {
runtime,
session_path,
seat_path,
user_path,
inhibitor_fds: Arc::new(Mutex::new(Vec::new())),
}
}
fn runtime_read(&self) -> fdo::Result<std::sync::RwLockReadGuard<'_, crate::runtime_state::SessionRuntime>> {
self.runtime
.read()
.map_err(|_| fdo::Error::Failed(String::from("login1 runtime state is poisoned")))
}
}
#[interface(name = "org.freedesktop.login1.Manager")]
impl LoginManager {
fn get_session(&self, id: &str) -> fdo::Result<OwnedObjectPath> {
let runtime = self
.runtime
.read()
.map_err(|_| fdo::Error::Failed(String::from("login1 runtime state is poisoned")))?;
let runtime = self.runtime_read()?;
if id == runtime.session_id || id == "auto" {
return Ok(self.session_path.clone());
}
@@ -39,10 +58,7 @@ impl LoginManager {
}
fn list_sessions(&self) -> fdo::Result<Vec<(String, u32, String, String, OwnedObjectPath)>> {
let runtime = self
.runtime
.read()
.map_err(|_| fdo::Error::Failed(String::from("login1 runtime state is poisoned")))?;
let runtime = self.runtime_read()?;
Ok(vec![(
runtime.session_id.clone(),
runtime.uid,
@@ -53,10 +69,7 @@ impl LoginManager {
}
fn get_seat(&self, id: &str) -> fdo::Result<OwnedObjectPath> {
let runtime = self
.runtime
.read()
.map_err(|_| fdo::Error::Failed(String::from("login1 runtime state is poisoned")))?;
let runtime = self.runtime_read()?;
if id == runtime.seat_id {
return Ok(self.seat_path.clone());
}
@@ -64,9 +77,151 @@ impl LoginManager {
Err(fdo::Error::Failed(format!("unknown login1 seat '{id}'")))
}
fn inhibit(&self, what: &str, who: &str, why: &str, mode: &str) -> fdo::Result<OwnedFd> {
if mode != "block" && mode != "delay" {
return Err(fdo::Error::Failed(format!(
"inhibit mode must be 'block' or 'delay', got '{mode}'"
)));
}
let (end_caller, end_daemon) = UnixStream::pair()
.map_err(|err| fdo::Error::Failed(format!("failed to create inhibit pipe: {err}")))?;
let fd_caller: StdOwnedFd = end_caller.into();
let fd_daemon: StdOwnedFd = end_daemon.into();
let uid = self.runtime_read().map(|r| r.uid).unwrap_or(0);
let pid = std::process::id();
let entry = InhibitorEntry {
what: what.to_owned(),
who: who.to_owned(),
why: why.to_owned(),
mode: mode.to_owned(),
pid,
uid,
};
if let Ok(mut runtime) = self.runtime.write() {
runtime.inhibitors.push(entry);
}
if let Ok(mut fds) = self.inhibitor_fds.lock() {
fds.push(fd_daemon);
}
eprintln!(
"redbear-sessiond: Inhibit(what={what}, who={who}, mode={mode}) granted"
);
Ok(OwnedFd::from(fd_caller))
}
fn can_power_off(&self) -> fdo::Result<String> {
Ok(String::from("na"))
}
fn can_reboot(&self) -> fdo::Result<String> {
Ok(String::from("na"))
}
fn can_suspend(&self) -> fdo::Result<String> {
Ok(String::from("na"))
}
fn can_hibernate(&self) -> fdo::Result<String> {
Ok(String::from("na"))
}
fn can_hybrid_sleep(&self) -> fdo::Result<String> {
Ok(String::from("na"))
}
fn can_suspend_then_hibernate(&self) -> fdo::Result<String> {
Ok(String::from("na"))
}
fn can_sleep(&self) -> fdo::Result<String> {
Ok(String::from("na"))
}
fn power_off(&self, _interactive: bool) -> fdo::Result<()> {
eprintln!("redbear-sessiond: PowerOff requested");
if let Ok(mut runtime) = self.runtime.write() {
runtime.preparing_for_shutdown = true;
}
Ok(())
}
fn reboot(&self, _interactive: bool) -> fdo::Result<()> {
eprintln!("redbear-sessiond: Reboot requested");
Ok(())
}
fn suspend(&self, _interactive: bool) -> fdo::Result<()> {
eprintln!("redbear-sessiond: Suspend requested");
Ok(())
}
fn get_session_by_pid(&self, _pid: u32) -> fdo::Result<OwnedObjectPath> {
Ok(self.session_path.clone())
}
fn list_users(&self) -> fdo::Result<Vec<(u32, String, OwnedObjectPath)>> {
let runtime = self.runtime_read()?;
Ok(vec![(
runtime.uid,
runtime.username.clone(),
self.user_path.clone(),
)])
}
fn list_seats(&self) -> fdo::Result<Vec<(String, OwnedObjectPath)>> {
let runtime = self.runtime_read()?;
Ok(vec![(runtime.seat_id.clone(), self.seat_path.clone())])
}
fn list_inhibitors(&self) -> fdo::Result<Vec<(String, String, String, String, u32, u32)>> {
let runtime = self.runtime_read()?;
Ok(runtime
.inhibitors
.iter()
.map(|entry| {
(
entry.what.clone(),
entry.who.clone(),
entry.why.clone(),
entry.mode.clone(),
entry.pid,
entry.uid,
)
})
.collect())
}
fn activate_session(&self, session_id: &str) -> fdo::Result<()> {
eprintln!("redbear-sessiond: ActivateSession({session_id}) — no-op");
Ok(())
}
fn lock_session(&self, session_id: &str) -> fdo::Result<()> {
eprintln!("redbear-sessiond: LockSession({session_id})");
Ok(())
}
fn unlock_session(&self, session_id: &str) -> fdo::Result<()> {
eprintln!("redbear-sessiond: UnlockSession({session_id})");
Ok(())
}
fn terminate_session(&self, session_id: &str) -> fdo::Result<()> {
eprintln!("redbear-sessiond: TerminateSession({session_id})");
Ok(())
}
#[zbus(property(emits_changed_signal = "const"), name = "IdleHint")]
fn idle_hint(&self) -> bool {
false
self.runtime_read().map(|r| r.idle_hint).unwrap_or(false)
}
#[zbus(property(emits_changed_signal = "const"), name = "IdleSinceHint")]
@@ -81,12 +236,30 @@ impl LoginManager {
#[zbus(property(emits_changed_signal = "const"), name = "BlockInhibited")]
fn block_inhibited(&self) -> String {
String::new()
self.runtime_read()
.map(|r| {
r.inhibitors
.iter()
.filter(|i| i.mode == "block")
.map(|i| i.what.as_str())
.collect::<Vec<&str>>()
.join(":")
})
.unwrap_or_default()
}
#[zbus(property(emits_changed_signal = "const"), name = "DelayInhibited")]
fn delay_inhibited(&self) -> String {
String::new()
self.runtime_read()
.map(|r| {
r.inhibitors
.iter()
.filter(|i| i.mode == "delay")
.map(|i| i.what.as_str())
.collect::<Vec<&str>>()
.join(":")
})
.unwrap_or_default()
}
#[zbus(property(emits_changed_signal = "const"), name = "InhibitDelayMaxUSec")]
@@ -111,8 +284,7 @@ impl LoginManager {
#[zbus(property(emits_changed_signal = "const"), name = "PreparingForShutdown")]
fn preparing_for_shutdown(&self) -> bool {
self.runtime
.read()
self.runtime_read()
.map(|runtime| runtime.preparing_for_shutdown)
.unwrap_or(false)
}
@@ -132,15 +304,21 @@ mod tests {
use super::*;
use crate::runtime_state::shared_runtime;
#[test]
fn get_session_accepts_runtime_session_id() {
let manager = LoginManager::new(
fn test_manager() -> LoginManager {
LoginManager::new(
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/session/c1"))
.expect("session path should parse"),
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/seat/seat0"))
.expect("seat path should parse"),
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/user/current"))
.expect("user path should parse"),
shared_runtime(),
);
)
}
#[test]
fn get_session_accepts_runtime_session_id() {
let manager = test_manager();
let path = manager
.get_session("c1")
@@ -150,13 +328,7 @@ mod tests {
#[test]
fn get_session_accepts_auto_alias() {
let manager = LoginManager::new(
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/session/c1"))
.expect("session path should parse"),
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/seat/seat0"))
.expect("seat path should parse"),
shared_runtime(),
);
let manager = test_manager();
let path = manager
.get_session("auto")
@@ -177,9 +349,127 @@ mod tests {
.expect("session path should parse"),
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/seat/seat0"))
.expect("seat path should parse"),
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/user/current"))
.expect("user path should parse"),
runtime,
);
assert!(manager.preparing_for_shutdown());
}
#[test]
fn can_methods_return_na() {
let manager = test_manager();
assert_eq!(manager.can_power_off().unwrap(), "na");
assert_eq!(manager.can_reboot().unwrap(), "na");
assert_eq!(manager.can_suspend().unwrap(), "na");
assert_eq!(manager.can_hibernate().unwrap(), "na");
assert_eq!(manager.can_hybrid_sleep().unwrap(), "na");
assert_eq!(manager.can_suspend_then_hibernate().unwrap(), "na");
assert_eq!(manager.can_sleep().unwrap(), "na");
}
#[test]
fn list_users_returns_runtime_user() {
let runtime = shared_runtime();
runtime.write().expect("lock").username = String::from("testuser");
runtime.write().expect("lock").uid = 1000;
let manager = LoginManager::new(
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/session/c1")).unwrap(),
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/seat/seat0")).unwrap(),
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/user/current")).unwrap(),
runtime,
);
let users = manager.list_users().expect("list_users should succeed");
assert_eq!(users.len(), 1);
assert_eq!(users[0].0, 1000);
assert_eq!(users[0].1, "testuser");
}
#[test]
fn list_seats_returns_runtime_seat() {
let manager = test_manager();
let seats = manager.list_seats().expect("list_seats should succeed");
assert_eq!(seats.len(), 1);
assert_eq!(seats[0].0, "seat0");
}
#[test]
fn get_session_by_pid_returns_session_path() {
let manager = test_manager();
let path = manager.get_session_by_pid(1234).expect("should succeed");
assert_eq!(path.as_str(), "/org/freedesktop/login1/session/c1");
}
#[test]
fn list_inhibitors_empty_by_default() {
let manager = test_manager();
let inhibitors = manager.list_inhibitors().expect("should succeed");
assert!(inhibitors.is_empty());
}
#[test]
fn inhibit_rejects_invalid_mode() {
let manager = test_manager();
let err = manager.inhibit("sleep", "test", "reason", "invalid").unwrap_err();
match err {
fdo::Error::Failed(msg) => assert!(msg.contains("block") || msg.contains("delay")),
other => panic!("expected Failed error, got {other:?}"),
}
}
#[test]
fn inhibit_tracks_entry_in_runtime() {
let runtime = shared_runtime();
let manager = LoginManager::new(
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/session/c1")).unwrap(),
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/seat/seat0")).unwrap(),
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/user/current")).unwrap(),
runtime.clone(),
);
let _fd = manager
.inhibit("sleep", "testapp", "testing", "block")
.expect("inhibit should succeed");
let runtime_guard = runtime.read().expect("lock");
assert_eq!(runtime_guard.inhibitors.len(), 1);
assert_eq!(runtime_guard.inhibitors[0].what, "sleep");
assert_eq!(runtime_guard.inhibitors[0].who, "testapp");
assert_eq!(runtime_guard.inhibitors[0].mode, "block");
}
#[test]
fn block_inhibited_joins_what_fields() {
let runtime = shared_runtime();
runtime.write().expect("lock").inhibitors.push(InhibitorEntry {
what: String::from("sleep"),
who: String::from("app1"),
why: String::from("r"),
mode: String::from("block"),
pid: 1,
uid: 0,
});
runtime.write().expect("lock").inhibitors.push(InhibitorEntry {
what: String::from("shutdown"),
who: String::from("app2"),
why: String::from("r"),
mode: String::from("block"),
pid: 2,
uid: 0,
});
let manager = LoginManager::new(
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/session/c1")).unwrap(),
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/seat/seat0")).unwrap(),
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/user/current")).unwrap(),
runtime,
);
let blocked = manager.block_inhibited();
assert!(blocked.contains("sleep"));
assert!(blocked.contains("shutdown"));
}
}
@@ -1,5 +1,15 @@
use std::sync::{Arc, RwLock};
#[derive(Clone, Debug)]
pub struct InhibitorEntry {
pub what: String,
pub who: String,
pub why: String,
pub mode: String,
pub pid: u32,
pub uid: u32,
}
#[derive(Clone, Debug)]
pub struct SessionRuntime {
pub session_id: String,
@@ -11,6 +21,10 @@ pub struct SessionRuntime {
pub state: String,
pub active: bool,
pub preparing_for_shutdown: bool,
pub idle_hint: bool,
pub locked_hint: bool,
pub session_type: String,
pub inhibitors: Vec<InhibitorEntry>,
}
impl Default for SessionRuntime {
@@ -25,6 +39,10 @@ impl Default for SessionRuntime {
state: String::from("online"),
active: true,
preparing_for_shutdown: false,
idle_hint: false,
locked_hint: false,
session_type: String::from("wayland"),
inhibitors: Vec::new(),
}
}
}
@@ -151,6 +151,54 @@ impl LoginSession {
Ok(())
}
fn set_idle_hint(&self, idle: bool) -> fdo::Result<()> {
let runtime = self.runtime()?;
let session_id = runtime.session_id.clone();
drop(runtime);
if let Ok(mut guard) = self.runtime.write() {
guard.idle_hint = idle;
}
eprintln!("redbear-sessiond: SetIdleHint({idle}) for session {session_id}");
Ok(())
}
fn set_locked_hint(&self, locked: bool) -> fdo::Result<()> {
let runtime = self.runtime()?;
let session_id = runtime.session_id.clone();
drop(runtime);
if let Ok(mut guard) = self.runtime.write() {
guard.locked_hint = locked;
}
eprintln!("redbear-sessiond: SetLockedHint({locked}) for session {session_id}");
Ok(())
}
fn set_type(&self, session_type: &str) -> fdo::Result<()> {
let runtime = self.runtime()?;
let session_id = runtime.session_id.clone();
drop(runtime);
if let Ok(mut guard) = self.runtime.write() {
guard.session_type = session_type.to_owned();
}
eprintln!("redbear-sessiond: SetType({session_type}) for session {session_id}");
Ok(())
}
fn terminate(&self) -> fdo::Result<()> {
let runtime = self.runtime()?;
let session_id = runtime.session_id.clone();
drop(runtime);
if let Ok(mut guard) = self.runtime.write() {
guard.state = String::from("closing");
}
eprintln!("redbear-sessiond: Terminate requested for session {session_id}");
Ok(())
}
#[zbus(property(emits_changed_signal = "const"), name = "Active")]
fn active(&self) -> bool {
self.runtime().map(|runtime| runtime.active).unwrap_or(true)
@@ -161,9 +209,11 @@ impl LoginSession {
false
}
#[zbus(property(emits_changed_signal = "const"), name = "Type")]
#[zbus(property(emits_changed_signal = "false"), name = "Type")]
fn kind(&self) -> String {
String::from("wayland")
self.runtime()
.map(|r| r.session_type.clone())
.unwrap_or_else(|_| String::from("wayland"))
}
#[zbus(property(emits_changed_signal = "const"), name = "Class")]
@@ -191,7 +241,7 @@ impl LoginSession {
self.runtime().map(|runtime| runtime.session_id).unwrap_or_else(|_| String::from("c1"))
}
#[zbus(property(emits_changed_signal = "const"), name = "State")]
#[zbus(property(emits_changed_signal = "false"), name = "State")]
fn state(&self) -> String {
self.runtime().map(|runtime| runtime.state).unwrap_or_else(|_| String::from("online"))
}
@@ -244,14 +294,14 @@ impl LoginSession {
String::new()
}
#[zbus(property(emits_changed_signal = "const"), name = "IdleHint")]
#[zbus(property(emits_changed_signal = "false"), name = "IdleHint")]
fn idle_hint(&self) -> bool {
false
self.runtime().map(|r| r.idle_hint).unwrap_or(false)
}
#[zbus(property(emits_changed_signal = "const"), name = "LockedHint")]
#[zbus(property(emits_changed_signal = "false"), name = "LockedHint")]
fn locked_hint(&self) -> bool {
false
self.runtime().map(|r| r.locked_hint).unwrap_or(false)
}
#[zbus(signal, name = "PauseDevice")]
@@ -269,4 +319,108 @@ impl LoginSession {
minor: u32,
fd: Fd<'_>,
) -> zbus::Result<()>;
#[zbus(signal, name = "Lock")]
async fn lock(signal_emitter: &SignalEmitter<'_>) -> zbus::Result<()>;
#[zbus(signal, name = "Unlock")]
async fn unlock(signal_emitter: &SignalEmitter<'_>) -> zbus::Result<()>;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::device_map::DeviceMap;
use crate::runtime_state::shared_runtime;
fn test_session() -> LoginSession {
LoginSession::new(
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/seat/seat0")).unwrap(),
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/user/current")).unwrap(),
DeviceMap::new(),
shared_runtime(),
)
}
#[test]
fn set_idle_hint_updates_runtime() {
let runtime = shared_runtime();
let session = LoginSession::new(
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/seat/seat0")).unwrap(),
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/user/current")).unwrap(),
DeviceMap::new(),
runtime.clone(),
);
assert!(!session.idle_hint());
session.set_idle_hint(true).unwrap();
assert!(session.idle_hint());
let guard = runtime.read().expect("lock");
assert!(guard.idle_hint);
}
#[test]
fn set_locked_hint_updates_runtime() {
let session = test_session();
assert!(!session.locked_hint());
session.set_locked_hint(true).unwrap();
assert!(session.locked_hint());
}
#[test]
fn set_type_updates_runtime() {
let runtime = shared_runtime();
let session = LoginSession::new(
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/seat/seat0")).unwrap(),
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/user/current")).unwrap(),
DeviceMap::new(),
runtime.clone(),
);
assert_eq!(session.kind(), "wayland");
session.set_type("x11").unwrap();
assert_eq!(session.kind(), "x11");
let guard = runtime.read().expect("lock");
assert_eq!(guard.session_type, "x11");
}
#[test]
fn terminate_sets_state_to_closing() {
let runtime = shared_runtime();
let session = LoginSession::new(
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/seat/seat0")).unwrap(),
OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/user/current")).unwrap(),
DeviceMap::new(),
runtime.clone(),
);
assert_eq!(session.state(), "online");
session.terminate().unwrap();
assert_eq!(session.state(), "closing");
}
#[test]
fn take_control_then_take_device_rejects_duplicate() {
let session = test_session();
session.take_control(false).unwrap();
session.taken_devices().unwrap().insert((226, 0));
let err = session.take_device(226, 0).unwrap_err();
match err {
fdo::Error::Failed(msg) => assert!(msg.contains("already taken")),
other => panic!("expected Failed error, got {other:?}"),
}
}
#[test]
fn release_device_rejects_unknown() {
let session = test_session();
session.take_control(false).unwrap();
let err = session.release_device(226, 99).unwrap_err();
match err {
fdo::Error::Failed(msg) => assert!(msg.contains("was not taken")),
other => panic!("expected Failed error, got {other:?}"),
}
}
}
@@ -0,0 +1,2 @@
[build]
template = "cargo"
@@ -0,0 +1,12 @@
[package]
name = "redbear-statusnotifierwatcher"
version = "0.1.0"
edition = "2024"
[[bin]]
name = "redbear-statusnotifierwatcher"
path = "src/main.rs"
[dependencies]
zbus = { version = "5", default-features = false, features = ["tokio"] }
tokio = { version = "1", features = ["full"] }
@@ -0,0 +1,168 @@
use std::collections::HashSet;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use zbus::{
fdo::{self, ObjectManager},
interface,
object_server::SignalEmitter,
connection::Builder as ConnectionBuilder,
zvariant::ObjectPath,
};
const BUS_NAME: &str = "org.freedesktop.StatusNotifierWatcher";
const OBJECT_PATH: &str = "/StatusNotifierWatcher";
/// org.freedesktop.StatusNotifierWatcher D-Bus interface
/// Tracks registered system tray items and hosts for KDE Plasma.
struct StatusNotifierWatcher {
items: Arc<Mutex<HashSet<String>>>,
hosts: Arc<Mutex<HashSet<String>>>,
}
#[interface(name = "org.freedesktop.StatusNotifierWatcher")]
impl StatusNotifierWatcher {
// --- Methods ---
/// Register a status notifier item.
/// The item parameter is either a full object path (e.g., "/org/example/Item")
/// sent by the item itself, or a bus name (e.g., ":1.42" or "org.example.App")
/// sent via the KDE protocol extension.
async fn register_status_notifier_item(
&self,
#[zbus(signal_emitter)] signal_emitter: SignalEmitter<'_>,
item: &str,
) -> fdo::Result<()> {
let is_new = {
let mut items = self.items.lock().map_err(|e| {
fdo::Error::Failed(format!("items lock poisoned: {e}"))
})?;
items.insert(item.to_owned())
};
if is_new {
eprintln!("statusnotifierwatcher: item registered: {item}");
let _ = Self::status_notifier_item_registered(&signal_emitter, item).await;
}
Ok(())
}
/// Register a status notifier host (typically the system tray panel).
async fn register_status_notifier_host(
&self,
#[zbus(signal_emitter)] signal_emitter: SignalEmitter<'_>,
host: &str,
) -> fdo::Result<()> {
let is_new = {
let mut hosts = self.hosts.lock().map_err(|e| {
fdo::Error::Failed(format!("hosts lock poisoned: {e}"))
})?;
hosts.insert(host.to_owned())
};
if is_new {
eprintln!("statusnotifierwatcher: host registered: {host}");
}
Ok(())
}
// --- Properties ---
/// List of registered status notifier item bus names / paths.
#[zbus(property)]
fn registered_status_notifier_items(&self) -> fdo::Result<Vec<String>> {
let items = self.items.lock().map_err(|e| {
fdo::Error::Failed(format!("items lock poisoned: {e}"))
})?;
Ok(items.iter().cloned().collect())
}
/// Whether at least one status notifier host is registered.
#[zbus(property)]
fn is_status_notifier_host_registered(&self) -> fdo::Result<bool> {
let hosts = self.hosts.lock().map_err(|e| {
fdo::Error::Failed(format!("hosts lock poisoned: {e}"))
})?;
Ok(!hosts.is_empty())
}
/// Protocol version (always 0 per spec).
#[zbus(property)]
fn protocol_version(&self) -> i32 {
0
}
// --- Signals ---
/// Emitted when a new status notifier item is registered.
#[zbus(signal, name = "StatusNotifierItemRegistered")]
async fn status_notifier_item_registered(
signal_emitter: &SignalEmitter<'_>,
service: &str,
) -> zbus::Result<()>;
/// Emitted when a status notifier item is unregistered.
#[zbus(signal, name = "StatusNotifierItemUnregistered")]
async fn status_notifier_item_unregistered(
signal_emitter: &SignalEmitter<'_>,
service: &str,
) -> zbus::Result<()>;
}
async fn wait_for_session_bus() {
for _ in 0..30 {
if std::env::var("DBUS_SESSION_BUS_ADDRESS").is_ok() {
return;
}
tokio::time::sleep(Duration::from_millis(200)).await;
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
wait_for_session_bus().await;
let (shutdown_tx, mut shutdown_rx) = tokio::sync::watch::channel(false);
// Signal handler task for clean shutdown
let signal_tx = shutdown_tx.clone();
tokio::spawn(async move {
#[cfg(unix)]
{
use tokio::signal::unix::{SignalKind, signal};
if let Ok(mut sigterm) = signal(SignalKind::terminate()) {
tokio::select! {
_ = sigterm.recv() => {},
_ = tokio::signal::ctrl_c() => {},
}
} else {
let _ = tokio::signal::ctrl_c().await;
}
}
#[cfg(not(unix))]
{
let _ = tokio::signal::ctrl_c().await;
}
let _ = signal_tx.send(true);
});
// Keep original sender alive so receiver doesn't see all-senders-dropped
let _shutdown_guard = shutdown_tx;
let watcher = StatusNotifierWatcher {
items: Arc::new(Mutex::new(HashSet::new())),
hosts: Arc::new(Mutex::new(HashSet::new())),
};
let path: ObjectPath<'_> = OBJECT_PATH.try_into()?;
let connection = ConnectionBuilder::session()?
.name(BUS_NAME)?
.serve_at(path, watcher)?
.build()
.await?;
eprintln!("statusnotifierwatcher: {BUS_NAME} registered on session bus");
// Wait for shutdown signal
let _ = shutdown_rx.changed().await;
eprintln!("statusnotifierwatcher: shutdown signal received, exiting cleanly");
drop(connection);
Ok(())
}
@@ -4,10 +4,8 @@ mod inventory;
use std::{
env,
error::Error,
path::Path,
process,
sync::Arc,
thread,
time::Duration,
};
@@ -74,34 +72,34 @@ fn system_connection_builder() -> Result<ConnectionBuilder<'static>, Box<dyn Err
}
}
#[cfg(all(unix, not(target_os = "redox")))]
async fn wait_for_shutdown() -> Result<(), Box<dyn Error>> {
fn spawn_signal_handler(shutdown_tx: tokio::sync::watch::Sender<bool>) {
tokio::spawn(async move {
#[cfg(unix)]
{
use tokio::signal::unix::{SignalKind, signal};
let mut terminate = signal(SignalKind::terminate())?;
if let Ok(mut sigterm) = signal(SignalKind::terminate()) {
tokio::select! {
_ = terminate.recv() => Ok(()),
_ = tokio::signal::ctrl_c() => Ok(()),
_ = sigterm.recv() => {},
_ = tokio::signal::ctrl_c() => {},
}
} else {
let _ = tokio::signal::ctrl_c().await;
}
}
#[cfg(target_os = "redox")]
async fn wait_for_shutdown() -> Result<(), Box<dyn Error>> {
std::future::pending::<()>().await;
#[allow(unreachable_code)]
Ok(())
#[cfg(not(unix))]
{
let _ = tokio::signal::ctrl_c().await;
}
#[cfg(all(not(unix), not(target_os = "redox")))]
async fn wait_for_shutdown() -> Result<(), Box<dyn Error>> {
tokio::signal::ctrl_c().await?;
Ok(())
let _ = shutdown_tx.send(true);
});
}
async fn run_daemon() -> Result<(), Box<dyn Error>> {
wait_for_dbus_socket().await;
let (shutdown_tx, mut shutdown_rx) = tokio::sync::watch::channel(false);
spawn_signal_handler(shutdown_tx);
let mut last_err = None;
for attempt in 1..=5 {
let _root_path = parse_object_path(ROOT_PATH)?;
@@ -128,13 +126,16 @@ async fn run_daemon() -> Result<(), Box<dyn Error>> {
inventory.drives().len(),
inventory.blocks().len(),
);
wait_for_shutdown().await?;
let _ = shutdown_rx.changed().await;
eprintln!("redbear-udisks: shutdown signal received, exiting cleanly");
drop(connection);
return Ok(());
}
Err(err) => {
if attempt < 5 {
eprintln!("redbear-udisks: attempt {attempt}/5 failed ({err}), retrying in 2s...");
eprintln!(
"redbear-udisks: attempt {attempt}/5 failed ({err}), retrying in 2s..."
);
tokio::time::sleep(Duration::from_secs(2)).await;
}
last_err = Some(err.into());
@@ -29,6 +29,8 @@ const DEVICE_STATE_DISCHARGING: u32 = 2;
const DEVICE_STATE_EMPTY: u32 = 3;
const DEVICE_STATE_FULLY_CHARGED: u32 = 4;
const POLL_INTERVAL_SECS: u64 = 30;
#[derive(Debug, Clone)]
struct PowerRuntime {
root: PathBuf,
@@ -59,20 +61,20 @@ enum DeviceDescriptor {
Battery(String),
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
struct AdapterState {
native_path: String,
online: bool,
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
struct BatteryState {
native_path: String,
state_bits: u64,
percentage: Option<f64>,
}
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, PartialEq)]
struct PowerSnapshot {
adapters: Vec<AdapterState>,
batteries: Vec<BatteryState>,
@@ -328,31 +330,6 @@ impl PowerSnapshot {
}
}
#[cfg(all(unix, not(target_os = "redox")))]
async fn wait_for_shutdown() -> Result<(), Box<dyn Error>> {
use tokio::signal::unix::{SignalKind, signal};
let mut terminate = signal(SignalKind::terminate())?;
tokio::select! {
_ = terminate.recv() => Ok(()),
_ = tokio::signal::ctrl_c() => Ok(()),
}
}
#[cfg(target_os = "redox")]
async fn wait_for_shutdown() -> Result<(), Box<dyn Error>> {
std::future::pending::<()>().await;
#[allow(unreachable_code)]
Ok(())
}
#[cfg(all(not(unix), not(target_os = "redox")))]
async fn wait_for_shutdown() -> Result<(), Box<dyn Error>> {
tokio::signal::ctrl_c().await?;
Ok(())
}
#[interface(name = "org.freedesktop.UPower")]
impl UPowerDaemon {
fn enumerate_devices(&self) -> Vec<OwnedObjectPath> {
@@ -368,7 +345,7 @@ impl UPowerDaemon {
String::from("0.1.0")
}
#[zbus(property(emits_changed_signal = "const"), name = "OnBattery")]
#[zbus(property(emits_changed_signal = "false"), name = "OnBattery")]
fn on_battery(&self) -> bool {
self.runtime.snapshot().on_battery()
}
@@ -476,6 +453,28 @@ impl PowerDevice {
}
}
fn spawn_signal_handler(shutdown_tx: tokio::sync::watch::Sender<bool>) {
tokio::spawn(async move {
#[cfg(unix)]
{
use tokio::signal::unix::{SignalKind, signal};
if let Ok(mut sigterm) = signal(SignalKind::terminate()) {
tokio::select! {
_ = sigterm.recv() => {},
_ = tokio::signal::ctrl_c() => {},
}
} else {
let _ = tokio::signal::ctrl_c().await;
}
}
#[cfg(not(unix))]
{
let _ = tokio::signal::ctrl_c().await;
}
let _ = shutdown_tx.send(true);
});
}
async fn run_daemon() -> Result<(), Box<dyn Error>> {
wait_for_dbus_socket().await;
let runtime = PowerRuntime::discover()?;
@@ -486,6 +485,9 @@ async fn run_daemon() -> Result<(), Box<dyn Error>> {
}
let _display_device_path = parse_object_path(DISPLAY_DEVICE_PATH)?;
let (shutdown_tx, mut shutdown_rx) = tokio::sync::watch::channel(false);
spawn_signal_handler(shutdown_tx);
let mut last_err = None;
for attempt in 1..=5 {
let mut builder = system_connection_builder()?
@@ -527,13 +529,44 @@ async fn run_daemon() -> Result<(), Box<dyn Error>> {
match builder.build().await {
Ok(connection) => {
eprintln!("redbear-upower: registered {BUS_NAME} on the system bus");
wait_for_shutdown().await?;
let upower_path = parse_object_path(UPOWER_PATH)?;
let signal_emitter = SignalEmitter::new(&connection, upower_path)?;
let mut last_snapshot = runtime.snapshot();
let mut poll_interval =
tokio::time::interval(Duration::from_secs(POLL_INTERVAL_SECS));
loop {
tokio::select! {
result = shutdown_rx.changed() => {
if result.is_err() {
eprintln!("redbear-upower: signal handler exited unexpectedly");
}
eprintln!("redbear-upower: shutdown signal received, exiting cleanly");
break;
}
_ = poll_interval.tick() => {
let current_snapshot = runtime.snapshot();
if current_snapshot != last_snapshot {
eprintln!(
"redbear-upower: power state changed, emitting Changed signal"
);
let _ = UPowerDaemon::changed(&signal_emitter).await;
last_snapshot = current_snapshot;
}
}
}
}
drop(connection);
return Ok(());
}
Err(err) => {
if attempt < 5 {
eprintln!("redbear-upower: attempt {attempt}/5 failed ({err}), retrying in 2s...");
eprintln!(
"redbear-upower: attempt {attempt}/5 failed ({err}), retrying in 2s..."
);
tokio::time::sleep(Duration::from_secs(2)).await;
}
last_err = Some(err.into());
+9 -80
View File
@@ -1,20 +1,4 @@
#!/usr/bin/env bash
# build-redbear.sh — Build Red Bear OS from upstream base + Red Bear overlay
#
# Usage:
# ./local/scripts/build-redbear.sh # Default: redbear-full
# ./local/scripts/build-redbear.sh redbear-mini # Minimal validation baseline
# ./local/scripts/build-redbear.sh redbear-full # Full Red Bear desktop/session target
# ./local/scripts/build-redbear.sh redbear-live # Canonical full live profile config
# ./local/scripts/build-redbear.sh redbear-live-mini # Text-only mini live profile config
# ./local/scripts/build-redbear.sh redbear-grub-live-mini # Text-only GRUB mini live profile config
# ./local/scripts/build-redbear.sh --upstream redbear-full # Allow Redox/upstream recipe refresh
# APPLY_PATCHES=0 ./local/scripts/build-redbear.sh # Skip patch application
#
# This script assumes the Red Bear overlay model:
# - upstream-owned sources are refreshable working trees
# - Red Bear-owned shipping deltas live in local/patches/ and local/recipes/
# - upstream WIP recipes are not trusted as stable shipping inputs until upstream promotes them
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
@@ -25,23 +9,6 @@ JOBS="${JOBS:-$(nproc)}"
APPLY_PATCHES="${APPLY_PATCHES:-1}"
ALLOW_UPSTREAM=0
canonicalize_config() {
case "$1" in
redbear-mini)
printf '%s\n' "redbear-minimal"
;;
redbear-live-full)
printf '%s\n' "redbear-live"
;;
redbear-live-mini-grub)
printf '%s\n' "redbear-grub-live-mini"
;;
*)
printf '%s\n' "$1"
;;
esac
}
usage() {
cat <<EOF
Usage: $(basename "$0") [OPTIONS] [CONFIG]
@@ -53,7 +20,9 @@ Options:
-h, --help Show this help
Configs:
redbear-mini, redbear-full, redbear-live, redbear-live-mini, redbear-grub-live-mini
redbear-full Desktop/graphics target (default)
redbear-mini Text-only console/recovery target
redbear-grub Text-only with GRUB boot manager
EOF
}
@@ -87,22 +56,12 @@ fi
[ ${#POSITIONAL[@]} -eq 1 ] && CONFIG="${POSITIONAL[0]}"
CONFIG="$(canonicalize_config "$CONFIG")"
case "$CONFIG" in
redbear-minimal)
;;
redbear-live)
;;
redbear-live-mini)
;;
redbear-grub-live-mini)
;;
redbear-full)
redbear-full|redbear-mini|redbear-grub)
;;
*)
echo "ERROR: Unknown config '$CONFIG'"
echo "Supported: redbear-mini, redbear-full, redbear-live, redbear-live-mini, redbear-grub-live-mini"
echo "ERROR: Unknown config '$CONFIG'" >&2
echo "Supported: redbear-full, redbear-mini, redbear-grub" >&2
exit 1
;;
esac
@@ -216,7 +175,6 @@ if [ "$APPLY_PATCHES" = "1" ]; then
apply_patch_dir "$PROJECT_ROOT/local/patches/bootloader" "$PROJECT_ROOT/recipes/core/bootloader/source" "bootloader"
apply_patch_dir "$PROJECT_ROOT/local/patches/installer" "$PROJECT_ROOT/recipes/core/installer/source" "installer"
# repo cook can refetch nested sources when --upstream is enabled; keep relibc clean after patch application
stash_nested_repo_if_dirty "$PROJECT_ROOT/recipes/core/relibc/source" "relibc"
echo ""
fi
@@ -232,12 +190,12 @@ if [ ! -f "target/release/repo" ]; then
cargo build --release
fi
if [ "$CONFIG" = "redbear-full" ] || [ "$CONFIG" = "redbear-live" ]; then
if [ "$CONFIG" = "redbear-full" ]; then
ensure_relibc_desktop_surface
fi
FW_AMD_DIR="$PROJECT_ROOT/local/firmware/amdgpu"
if [ "$CONFIG" != "redbear-minimal" ] && [ "$CONFIG" != "redbear-live-mini" ] && [ "$CONFIG" != "redbear-grub-live-mini" ]; then
if [ "$CONFIG" = "redbear-full" ]; then
if [ -d "$FW_AMD_DIR" ] && [ -n "$(ls -A "$FW_AMD_DIR" 2>/dev/null)" ]; then
FW_COUNT=$(ls "$FW_AMD_DIR"/*.bin 2>/dev/null | wc -l)
echo ">>> Found $FW_COUNT AMD firmware blobs"
@@ -268,38 +226,9 @@ echo "Image: build/$ARCH/$CONFIG/harddrive.img"
echo ""
echo "To run in QEMU:"
echo " make qemu QEMUFLAGS=\"-m 4G\""
if [ "$CONFIG" = "redbear-minimal" ] || [ "$CONFIG" = "redbear-desktop" ]; then
echo ""
echo "To validate the Phase 2 VM network baseline:"
echo " ./local/scripts/validate-vm-network-baseline.sh"
echo " ./local/scripts/test-vm-network-qemu.sh $CONFIG"
fi
if [ "$CONFIG" = "redbear-desktop" ] || [ "$CONFIG" = "redbear-full" ] || [ "$CONFIG" = "redbear-wayland" ] || [ "$CONFIG" = "redbear-kde" ]; then
echo ""
echo "To validate bounded low-level controller proofs:"
echo " ./local/scripts/test-lowlevel-controllers-qemu.sh $CONFIG"
echo " # or run individual checks: test-xhci-irq-qemu.sh, test-iommu-qemu.sh, test-ps2-qemu.sh, test-timer-qemu.sh"
echo ""
echo "To validate bounded USB maturity proofs:"
echo " ./local/scripts/test-usb-maturity-qemu.sh $CONFIG"
echo " # or run individual checks: test-usb-qemu.sh --check, test-usb-storage-qemu.sh"
fi
if [ "$CONFIG" = "redbear-wayland" ]; then
echo ""
echo "To validate the bounded Phase 4 Wayland runtime harness:"
echo " ./local/scripts/test-phase4-wayland-qemu.sh"
echo " # in guest: redbear-drm-display-check --vendor amd|intel"
fi
if [ "$CONFIG" = "redbear-kde" ]; then
echo ""
echo "To validate the primary KWin Wayland desktop path:"
echo " ./local/scripts/test-phase6-kde-qemu.sh --check"
echo " # in guest: redbear-drm-display-check --vendor amd|intel"
fi
echo ""
echo "To build live ISO:"
echo " make live CONFIG_NAME=$CONFIG"
echo " # live .iso outputs are for real bare metal, not VM/QEMU use"
echo " scripts/build-iso.sh $CONFIG"
echo ""
echo "To write a real bare-metal image to USB (verify device first!):"
echo " dd if=build/$ARCH/$CONFIG/harddrive.img of=/dev/sdX bs=4M status=progress"
+3 -11
View File
@@ -226,16 +226,8 @@ echo ""
section "Validating Red Bear configs..."
declare -a redbear_configs=(
"config/redbear-desktop.toml"
"config/redbear-minimal.toml"
"config/redbear-bluetooth-experimental.toml"
"config/redbear-mini.toml"
"config/redbear-full.toml"
"config/redbear-wayland.toml"
"config/redbear-live.toml"
"config/redbear-live-mini.toml"
"config/redbear-live-full.toml"
"config/redbear-grub-live-mini.toml"
"config/redbear-grub-live-full.toml"
"config/redbear-grub.toml"
)
declare -a found_configs=()
@@ -247,9 +239,9 @@ for config_path in "${redbear_configs[@]}"; do
done
if [ "${#found_configs[@]}" -gt 0 ]; then
status "Found Red Bear config(s): ${found_configs[*]}"
status "Found Red Bear target config(s): ${found_configs[*]}"
else
warn "No redbear config found in config/. Build may rely on a different config intentionally"
warn "No redbear target config found in config/"
fi
echo ""
+1 -1
View File
@@ -86,7 +86,7 @@ recipes/wip/
## KDE STATUS
- Older WIP KDE app notes here are stale relative to `local/recipes/kde/` and `config/redbear-kde.toml`
- Older WIP KDE app notes here are stale relative to `local/recipes/kde/` and `config/redbear-full.toml`
- See `docs/05-KDE-PLASMA-ON-REDOX.md` top-level status note plus `local/docs/QT6-PORT-STATUS.md` for current state
## CONVENTIONS
+9 -42
View File
@@ -2,57 +2,27 @@
set -euo pipefail
CONFIG_NAME="redbear-live"
CONFIG_NAME="redbear-mini"
ARCH="x86_64"
ALLOW_UPSTREAM=0
canonicalize_live_config() {
case "$1" in
redbear-live-full)
printf '%s\n' "redbear-live"
;;
redbear-live-mini-grub)
printf '%s\n' "redbear-grub-live-mini"
;;
redbear-live-full-grub)
printf '%s\n' "redbear-grub-live-full"
;;
redbear-grub-live-full)
printf '%s\n' "redbear-grub-live-full"
;;
*)
printf '%s\n' "$1"
;;
esac
}
usage() {
cat <<EOF
Usage: $(basename "$0") [OPTIONS] [CONFIG_NAME] [ARCH]
Build a Red Bear OS live ISO for real bare metal.
Important:
Live .iso outputs are for bare-metal boot/install/recovery workflows.
They are not the virtual/QEMU target surface; use harddrive.img + make qemu for virtualization.
Options:
--upstream Allow Redox/upstream recipe source refresh during build
-h, --help Show this help
Supported live ISO targets:
redbear-live Full live ISO (graphical greeter + text fallback)
redbear-live-mini Text-only mini live ISO
redbear-grub-live-mini Text-only mini live ISO with GRUB bootloader
redbear-grub-live-full Full live ISO with GRUB bootloader
Legacy compatibility aliases:
redbear-live-full → redbear-live
redbear-live-mini-grub → redbear-grub-live-mini
redbear-live-full-grub → redbear-grub-live-full
Supported targets:
redbear-full Full desktop ISO (Wayland + KDE + GPU drivers)
redbear-mini Text-only ISO (default)
redbear-grub Text-only ISO with GRUB boot manager
Defaults:
CONFIG_NAME=redbear-live
CONFIG_NAME=redbear-mini
ARCH=x86_64
EOF
}
@@ -93,14 +63,12 @@ fi
[ ${#POSITIONAL[@]} -ge 1 ] && CONFIG_NAME="${POSITIONAL[0]}"
[ ${#POSITIONAL[@]} -ge 2 ] && ARCH="${POSITIONAL[1]}"
CONFIG_NAME="$(canonicalize_live_config "$CONFIG_NAME")"
case "$CONFIG_NAME" in
redbear-live|redbear-live-mini|redbear-grub-live-mini|redbear-grub-live-full)
redbear-full|redbear-mini|redbear-grub)
;;
*)
echo "ERROR: Unsupported live ISO target '$CONFIG_NAME'" >&2
usage >&2
echo "ERROR: Unsupported target '$CONFIG_NAME'" >&2
echo "Supported: redbear-full, redbear-mini, redbear-grub" >&2
exit 1
;;
esac
@@ -122,4 +90,3 @@ fi
echo ""
echo "Done: build/${ARCH}/${CONFIG_NAME}.iso"
echo "Note: live .iso outputs are for real bare metal, not VM/QEMU use."