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:
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user