Advance Red Bear runtime services and tools
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use std::sync::Mutex;
|
||||
use std::{process::Command, sync::Mutex};
|
||||
|
||||
use zbus::{fdo, interface, zvariant::OwnedObjectPath};
|
||||
|
||||
@@ -27,17 +27,55 @@ impl LoginSeat {
|
||||
.lock()
|
||||
.map_err(|_| fdo::Error::Failed(String::from("seat VT state is poisoned")))
|
||||
}
|
||||
|
||||
fn request_vt_switch(program: &str, vt: u32) -> fdo::Result<()> {
|
||||
let output = Command::new(program)
|
||||
.args(["-A", &vt.to_string()])
|
||||
.output()
|
||||
.map_err(|err| fdo::Error::Failed(format!("failed to run {program} -A {vt}: {err}")))?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let detail = if !stderr.trim().is_empty() {
|
||||
stderr.trim().to_string()
|
||||
} else if !stdout.trim().is_empty() {
|
||||
stdout.trim().to_string()
|
||||
} else {
|
||||
format!("exit status {}", output.status)
|
||||
};
|
||||
return Err(fdo::Error::Failed(format!(
|
||||
"{program} -A {vt} failed: {detail}"
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn current_session_id(&self) -> String {
|
||||
self.runtime
|
||||
.read()
|
||||
.map(|runtime| runtime.session_id.clone())
|
||||
.unwrap_or_else(|_| String::from("c1"))
|
||||
}
|
||||
}
|
||||
|
||||
#[interface(name = "org.freedesktop.login1.Seat")]
|
||||
impl LoginSeat {
|
||||
fn switch_to(&mut self, vt: u32) -> fdo::Result<()> {
|
||||
Self::request_vt_switch("inputd", vt)?;
|
||||
|
||||
let mut last_requested_vt = self.last_requested_vt()?;
|
||||
*last_requested_vt = vt;
|
||||
eprintln!(
|
||||
"redbear-sessiond: SwitchTo requested for seat {} -> vt {vt} (delegated to inputd -A externally)",
|
||||
self.id
|
||||
);
|
||||
|
||||
let mut runtime = self
|
||||
.runtime
|
||||
.write()
|
||||
.map_err(|_| fdo::Error::Failed(String::from("seat runtime state is poisoned")))?;
|
||||
runtime.vt = vt;
|
||||
runtime.active = true;
|
||||
|
||||
eprintln!("redbear-sessiond: SwitchTo requested for seat {} -> vt {vt}", self.id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -48,24 +86,12 @@ impl LoginSeat {
|
||||
|
||||
#[zbus(property(emits_changed_signal = "const"), name = "ActiveSession")]
|
||||
fn active_session(&self) -> (String, OwnedObjectPath) {
|
||||
(
|
||||
self.runtime
|
||||
.read()
|
||||
.map(|runtime| runtime.session_id.clone())
|
||||
.unwrap_or_else(|_| String::from("c1")),
|
||||
self.session_path.clone(),
|
||||
)
|
||||
(self.current_session_id(), self.session_path.clone())
|
||||
}
|
||||
|
||||
#[zbus(property(emits_changed_signal = "const"), name = "Sessions")]
|
||||
fn sessions(&self) -> Vec<(String, OwnedObjectPath)> {
|
||||
vec![(
|
||||
self.runtime
|
||||
.read()
|
||||
.map(|runtime| runtime.session_id.clone())
|
||||
.unwrap_or_else(|_| String::from("c1")),
|
||||
self.session_path.clone(),
|
||||
)]
|
||||
vec![(self.current_session_id(), self.session_path.clone())]
|
||||
}
|
||||
|
||||
#[zbus(property(emits_changed_signal = "const"), name = "CanGraphical")]
|
||||
@@ -83,3 +109,39 @@ impl LoginSeat {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::runtime_state::shared_runtime;
|
||||
|
||||
#[test]
|
||||
fn request_vt_switch_accepts_successful_command() {
|
||||
LoginSeat::request_vt_switch("/bin/true", 3).expect("true should succeed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_vt_switch_rejects_failed_command() {
|
||||
let err = LoginSeat::request_vt_switch("/bin/false", 3).expect_err("false should fail");
|
||||
match err {
|
||||
fdo::Error::Failed(message) => {
|
||||
assert!(message.contains("/bin/false -A 3 failed"));
|
||||
}
|
||||
other => panic!("expected failed error, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn active_session_reflects_runtime_vt_after_update() {
|
||||
let session_path = OwnedObjectPath::try_from(String::from("/org/freedesktop/login1/session/c1"))
|
||||
.expect("session path should parse");
|
||||
let runtime = shared_runtime();
|
||||
{
|
||||
let mut guard = runtime.write().expect("runtime lock should remain healthy");
|
||||
guard.vt = 7;
|
||||
}
|
||||
let seat = LoginSeat::new(session_path, runtime);
|
||||
assert_eq!(seat.active_session().0, "c1");
|
||||
assert_eq!(seat.id(), "seat0");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user