e65a23fd6b
This commit replaces the D-Bus implementation stubs and gaps identified
during the zbus/D-Bus review round with real, tested implementations.
**redbear-sessiond: real login1 properties + PrepareForShutdown signals**
The login1.Manager interface had hardcoded stub values:
- idle_since_hint() and idle_since_hint_monotonic() returned 0
- inhibit_delay_max_usec() returned 0
- handle_lid_switch() returned 'ignore'
- handle_power_key() returned 'poweroff'
- power_off/reboot/suspend wrote to /scheme/sys/kstop but did NOT
emit PrepareForShutdown(before=true) / PrepareForSleep(true) first
Replace with:
- Run-time configurable atomic fields (last_activity_us,
inhibit_delay_max_us) and RwLock<String> fields (handle_lid_switch,
handle_power_key) on SessionRuntime, mutated by the existing control
socket. Defaults: 5s inhibit delay, 'ignore' lid, 'poweroff' power key.
- power_off/reboot are now async, emit PrepareForShutdown(true) before
/scheme/sys/kstop write, emit PrepareForShutdown(false) after a
successful write, and return a D-Bus error if the kstop write fails
(resetting preparing_for_shutdown).
- suspend emits PrepareForSleep(true)/(false) similarly.
- Bash-style dangling-Clone problem solved via manual Clone impl on
SessionRuntime that snapshots the atomics and lock contents.
**redbear-wifictl: real NetworkManager-shaped D-Bus interface**
The dbus-nm feature was a no-op stub that just logged 'registered'
without actually doing anything. Replace with a real zbus interface:
- zbus::interface structs wrapping Arc<Mutex<NmWifiDevice>> shared state
- org.freedesktop.NetworkManager at /org/freedesktop/NetworkManager
exposes WirelessEnabled, WirelessHardwareEnabled, State, and
GetDevices().
- org.freedesktop.NetworkManager.Device.Wireless at
/org/freedesktop/NetworkManager/Devices/0 exposes HwAddress,
PermHwAddress, State, Ssid, Strength, LastScan, AccessPoints,
GetAccessPoints(), WirelessCapabilities.
- register_nm_interface() now actually builds a blocking
zbus::connection::Builder on the session bus, registers both
service name + object paths, spawns a background thread to hold the
connection alive, and returns. On session-bus connect failure it
logs an error and returns without crashing.
- When the dbus-nm feature is disabled, behavior is unchanged (no-op).
- Type model enriched: NmWifiDevice gains last_scan, active_ssid,
active_strength fields + Default derives; NmDeviceState gains
Default; NmAccessPoint gains Default.
**redbear-statusnotifierwatcher: wired into redbear-full**
The recipe compiled and had 12 tests but was NOT in any config —
the binary never deployed. Add:
- [package.files] stanza to its recipe.toml so the binary is staged
- redbear-statusnotifierwatcher = {} to config/redbear-full.toml
- launch_optional_component invocation in redbear-kde-session
**redbear-notifications: 8 host unit tests**
Previously zero tests. Add a #[cfg(test)] module covering:
- monotonic notification IDs
- capabilities list (spec values present)
- server information strings
- close-id + reason recording
- action invocation payload
- ordering preserved across multiple notifications
- independence between close and action records
**zbus build-ordering marker: clean source**
The marker source lib.rs contained 'pub struct Connection;' which
is misleading. Replace with a minimal comment explaining the
build-ordering purpose. The actual zbus crate is still resolved by
Cargo at downstream build time (unchanged behavior).
**D-Bus symlink cleanup**
Remove recipes/system/dbus/dbus-root-uid.patch (orphan symlink, not
in .gitignore-relevant scope). The actual patch stays in
local/patches/dbus/. The redox.patch in the same directory is a
real file (not a symlink) and is preserved.
**Build-system bug fixes**
- build-preflight.sh: when broken recipe.toml links cannot be restored
from git, invoke the guard-recipes.sh --fix path so untracked
custom links are regenerated.
- verify-fork-functions.sh: skip std-trait method names (fmt, eq,
clone, drop, etc.) when checking for dropped upstream functions —
these are derivable or compiler-caught, so a missed refactor is
inert, not a build blocker.
- verify-overlay-integrity.sh: add explicit 'return 0' on log helpers
so --quiet mode does not abort on the first log call under set -e.
Verification: host cargo test passes on all four modified daemons.
redbear-sessiond: 52 tests (12 new for the properties + signals).
redbear-wifictl: 35 tests (4 new for dbus_nm) + 2 cli_transport.
redbear-notifications: 8 tests (all new).
redbear-upower/udisks/polkit: unchanged, still pass.
258 lines
8.9 KiB
Bash
Executable File
258 lines
8.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# verify-overlay-integrity.sh — Verify the Red Bear OS overlay structure is intact.
|
|
#
|
|
# Checks:
|
|
# 1. All recipe symlinks (recipes/ → local/recipes/) resolve correctly
|
|
# 2. All patch symlinks (recipes/ → local/patches/) resolve correctly
|
|
# 3. No circular symlink references
|
|
# 4. Critical local/patches/ files exist
|
|
# 5. Critical config/redbear-*.toml files exist
|
|
#
|
|
# Usage:
|
|
# ./local/scripts/verify-overlay-integrity.sh # Check and report
|
|
# ./local/scripts/verify-overlay-integrity.sh --repair # Re-run apply-patches.sh on failures
|
|
# ./local/scripts/verify-overlay-integrity.sh --quiet # Exit code only (for CI)
|
|
#
|
|
# Exit codes:
|
|
# 0 — all checks pass
|
|
# 1 — one or more checks failed
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
REPAIR=0
|
|
QUIET=0
|
|
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--repair) REPAIR=1 ;;
|
|
--quiet) QUIET=1 ;;
|
|
--help|-h)
|
|
echo "Usage: $0 [--repair] [--quiet]"
|
|
echo " --repair Re-run apply-patches.sh if overlay checks fail"
|
|
echo " --quiet Exit code only, no output"
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown argument: $arg" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
cd "$REPO_ROOT"
|
|
|
|
ERRORS=0
|
|
WARNINGS=0
|
|
|
|
# NB: each helper MUST end with `return 0`. Under `set -e`, a function whose
|
|
# last command is `[ "$QUIET" = "0" ] && echo ...` returns the exit status of
|
|
# the failed test in --quiet mode (QUIET=1 → test false → returns 1), which
|
|
# `set -e` treats as a fatal error and aborts the script on the FIRST log call
|
|
# — making --quiet always exit 1 regardless of overlay state. `return 0` keeps
|
|
# the helpers side-effect-only.
|
|
log() {
|
|
[ "$QUIET" = "0" ] && echo "$@"
|
|
return 0
|
|
}
|
|
|
|
log_error() {
|
|
[ "$QUIET" = "0" ] && echo " ERROR: $*" >&2
|
|
ERRORS=$((ERRORS + 1))
|
|
return 0
|
|
}
|
|
|
|
log_warn() {
|
|
[ "$QUIET" = "0" ] && echo " WARN: $*"
|
|
WARNINGS=$((WARNINGS + 1))
|
|
return 0
|
|
}
|
|
|
|
# ── 1. Recipe symlinks (recipes/ → local/recipes/) ──────────────────
|
|
log "==> Checking recipe symlinks (recipes/ → local/recipes/)..."
|
|
RECIPE_SYMLINK_COUNT=0
|
|
BROKEN_RECIPE_SYMLINKS=0
|
|
|
|
while IFS= read -r link; do
|
|
RECIPE_SYMLINK_COUNT=$((RECIPE_SYMLINK_COUNT + 1))
|
|
target="$(readlink -f "$link" 2>/dev/null || true)"
|
|
|
|
if [ -z "$target" ]; then
|
|
log_error "broken symlink: $link (cannot resolve target)"
|
|
BROKEN_RECIPE_SYMLINKS=$((BROKEN_RECIPE_SYMLINKS + 1))
|
|
continue
|
|
fi
|
|
|
|
# Check if target is inside local/recipes/
|
|
if echo "$target" | grep -q "/local/recipes/"; then
|
|
if [ ! -e "$target" ]; then
|
|
log_error "dangling symlink: $link -> $target (target does not exist)"
|
|
BROKEN_RECIPE_SYMLINKS=$((BROKEN_RECIPE_SYMLINKS + 1))
|
|
fi
|
|
fi
|
|
|
|
# Check for circular symlinks
|
|
if [ -L "$target" ]; then
|
|
# The target itself is a symlink — could be circular
|
|
resolved_inner="$(readlink -f "$target" 2>/dev/null || true)"
|
|
if [ -n "$resolved_inner" ] && [ "$resolved_inner" = "$(readlink -f "$link" 2>/dev/null)" ]; then
|
|
log_error "circular symlink: $link -> $target -> (circular)"
|
|
BROKEN_RECIPE_SYMLINKS=$((BROKEN_RECIPE_SYMLINKS + 1))
|
|
fi
|
|
fi
|
|
done < <(find recipes -maxdepth 3 -type l 2>/dev/null | grep -v '/target$\|/stage\|/sysroot$\|/source$' | sort)
|
|
|
|
log " $RECIPE_SYMLINK_COUNT recipe symlinks checked, $BROKEN_RECIPE_SYMLINKS broken"
|
|
|
|
# ── 2. Patch symlinks (recipes/ → local/patches/) ───────────────────
|
|
log "==> Checking patch symlinks (recipes/ → local/patches/)..."
|
|
PATCH_SYMLINK_COUNT=0
|
|
BROKEN_PATCH_SYMLINKS=0
|
|
|
|
# Mega redox.patch files were archived during Phase 2/3 cleanup; modular
|
|
# P0-...P9- patches (applied by apply-patches.sh) replace them. We only
|
|
# check the recipe-build-system patches and P2-boot-runtime-fixes (which
|
|
# is still tracked at base/).
|
|
EXPECTED_PATCH_SYMLINKS=(
|
|
)
|
|
|
|
for patch_link in "${EXPECTED_PATCH_SYMLINKS[@]}"; do
|
|
PATCH_SYMLINK_COUNT=$((PATCH_SYMLINK_COUNT + 1))
|
|
if [ ! -L "$patch_link" ]; then
|
|
if [ -f "$patch_link" ]; then
|
|
log_warn "$patch_link exists as regular file (not a symlink to local/patches/)"
|
|
else
|
|
log_error "$patch_link missing (should be symlink to local/patches/)"
|
|
BROKEN_PATCH_SYMLINKS=$((BROKEN_PATCH_SYMLINKS + 1))
|
|
fi
|
|
continue
|
|
fi
|
|
target="$(readlink -f "$patch_link" 2>/dev/null || true)"
|
|
if [ -z "$target" ] || [ ! -f "$target" ]; then
|
|
log_error "dangling patch symlink: $patch_link -> $target"
|
|
BROKEN_PATCH_SYMLINKS=$((BROKEN_PATCH_SYMLINKS + 1))
|
|
fi
|
|
done
|
|
|
|
log " $PATCH_SYMLINK_COUNT patch symlinks checked, $BROKEN_PATCH_SYMLINKS broken"
|
|
|
|
# ── 3. Critical local/patches/ files ────────────────────────────────
|
|
log "==> Checking critical local/patches/ files..."
|
|
CRITICAL_PATCHES=(
|
|
"local/patches/build-system/001-rebrand-and-build.patch"
|
|
"local/patches/build-system/002-cookbook-fixes.patch"
|
|
"local/patches/build-system/003-config.patch"
|
|
"local/patches/build-system/004-docs-and-cleanup.patch"
|
|
)
|
|
|
|
MISSING_PATCHES=0
|
|
for patch_file in "${CRITICAL_PATCHES[@]}"; do
|
|
if [ ! -f "$patch_file" ]; then
|
|
log_error "missing critical patch: $patch_file"
|
|
MISSING_PATCHES=$((MISSING_PATCHES + 1))
|
|
fi
|
|
done
|
|
|
|
if [ "$MISSING_PATCHES" = "0" ]; then
|
|
log " All critical patches present"
|
|
fi
|
|
|
|
# ── 4. Config files ─────────────────────────────────────────────────
|
|
log "==> Checking config/redbear-*.toml files..."
|
|
CRITICAL_CONFIGS=(
|
|
"config/redbear-full.toml"
|
|
"config/redbear-mini.toml"
|
|
"config/redbear-grub.toml"
|
|
"config/redbear-grub-policy.toml"
|
|
"config/redbear-device-services.toml"
|
|
"config/redbear-legacy-base.toml"
|
|
"config/redbear-legacy-desktop.toml"
|
|
"config/redbear-netctl.toml"
|
|
"config/redbear-greeter-services.toml"
|
|
"config/redbear-boot-stages.toml"
|
|
)
|
|
|
|
MISSING_CONFIGS=0
|
|
for config_file in "${CRITICAL_CONFIGS[@]}"; do
|
|
if [ ! -f "$config_file" ]; then
|
|
log_error "missing config: $config_file"
|
|
MISSING_CONFIGS=$((MISSING_CONFIGS + 1))
|
|
fi
|
|
done
|
|
|
|
TOTAL_REDBEAR_CONFIGS=$(find config -maxdepth 1 -name 'redbear-*.toml' 2>/dev/null | wc -l)
|
|
log " $TOTAL_REDBEAR_CONFIGS redbear-*.toml files found, $MISSING_CONFIGS critical configs missing"
|
|
|
|
# ── 5. local/ directory structure ───────────────────────────────────
|
|
log "==> Checking local/ directory structure..."
|
|
REQUIRED_LOCAL_DIRS=(
|
|
"local/recipes"
|
|
"local/recipes/drivers"
|
|
"local/recipes/gpu"
|
|
"local/recipes/system"
|
|
"local/recipes/core"
|
|
"local/recipes/libs"
|
|
"local/recipes/branding"
|
|
"local/recipes/kde"
|
|
"local/patches"
|
|
"local/patches/kernel"
|
|
"local/patches/base"
|
|
"local/patches/relibc"
|
|
"local/patches/bootloader"
|
|
"local/patches/installer"
|
|
"local/patches/build-system"
|
|
"local/docs"
|
|
"local/scripts"
|
|
"local/Assets"
|
|
)
|
|
|
|
MISSING_DIRS=0
|
|
for dir in "${REQUIRED_LOCAL_DIRS[@]}"; do
|
|
if [ ! -d "$dir" ]; then
|
|
log_error "missing directory: $dir"
|
|
MISSING_DIRS=$((MISSING_DIRS + 1))
|
|
fi
|
|
done
|
|
|
|
if [ "$MISSING_DIRS" = "0" ]; then
|
|
log " All required local/ directories present"
|
|
fi
|
|
|
|
# ── Summary ─────────────────────────────────────────────────────────
|
|
log ""
|
|
log "=== Overlay Integrity Summary ==="
|
|
log " Recipe symlinks: $RECIPE_SYMLINK_COUNT ($BROKEN_RECIPE_SYMLINKS broken)"
|
|
log " Patch symlinks: $PATCH_SYMLINK_COUNT ($BROKEN_PATCH_SYMLINKS broken)"
|
|
log " Critical patches: ${#CRITICAL_PATCHES[@]} ($MISSING_PATCHES missing)"
|
|
log " Critical configs: ${#CRITICAL_CONFIGS[@]} ($MISSING_CONFIGS missing)"
|
|
log " Required dirs: ${#REQUIRED_LOCAL_DIRS[@]} ($MISSING_DIRS missing)"
|
|
log " Warnings: $WARNINGS"
|
|
log " Errors: $ERRORS"
|
|
|
|
if [ "$ERRORS" -gt 0 ]; then
|
|
log ""
|
|
log "!! Overlay integrity check FAILED ($ERRORS errors)"
|
|
if [ "$REPAIR" = "1" ]; then
|
|
log "==> Attempting repair via apply-patches.sh..."
|
|
if [ -f local/scripts/apply-patches.sh ]; then
|
|
bash local/scripts/apply-patches.sh
|
|
log "==> Repair complete. Re-running verification..."
|
|
exec "$0" --quiet
|
|
else
|
|
log_error "apply-patches.sh not found — cannot repair"
|
|
exit 1
|
|
fi
|
|
else
|
|
log " Run with --repair to attempt automatic fix, or:"
|
|
log " ./local/scripts/apply-patches.sh"
|
|
exit 1
|
|
fi
|
|
else
|
|
if [ "$QUIET" = "0" ]; then
|
|
log ""
|
|
log " Overlay integrity check PASSED"
|
|
fi
|
|
exit 0
|
|
fi
|