Compare commits

..

No commits in common. "master" and "master" have entirely different histories.

16 changed files with 312 additions and 1445 deletions

675
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,12 +7,10 @@
<busconfig>
<policy user="root">
<allow own="com.subgraph.realms"/>
<allow own="com.subgraph.Realms2"/>
</policy>
<policy context="default">
<allow send_destination="com.subgraph.realms"/>
<allow send_destination="com.subgraph.Realms2"/>
<allow send_destination="com.subgraph.realms"
send_interface="org.freedesktop.DBus.Properties"/>
<allow send_destination="com.subgraph.realms"

View File

@ -12,9 +12,7 @@ use dbus::{Connection, BusType, ConnectionItem, Message, Path};
use inotify::{Inotify, WatchMask, WatchDescriptor, Event};
pub enum RealmEvent {
Starting(Realm),
Started(Realm),
Stopping(Realm),
Stopped(Realm),
New(Realm),
Removed(Realm),
@ -24,9 +22,7 @@ pub enum RealmEvent {
impl Display for RealmEvent {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
RealmEvent::Starting(ref realm) => write!(f, "RealmStarting({})", realm.name()),
RealmEvent::Started(ref realm) => write!(f, "RealmStarted({})", realm.name()),
RealmEvent::Stopping(ref realm) => write!(f, "RealmStopping({})", realm.name()),
RealmEvent::Stopped(ref realm) => write!(f, "RealmStopped({})", realm.name()),
RealmEvent::New(ref realm) => write!(f, "RealmNew({})", realm.name()),
RealmEvent::Removed(ref realm) => write!(f, "RealmRemoved({})", realm.name()),

View File

@ -194,13 +194,7 @@ impl RealmManager {
return Ok(());
}
info!("Starting realm {}", realm.name());
self.inner().events.send_event(RealmEvent::Starting(realm.clone()));
if let Err(err) = self._start_realm(realm, &mut HashSet::new()) {
self.inner().events.send_event(RealmEvent::Stopped(realm.clone()));
return Err(err);
}
self.inner().events.send_event(RealmEvent::Started(realm.clone()));
self._start_realm(realm, &mut HashSet::new())?;
if !Realms::is_some_realm_current() {
self.inner_mut().realms.set_realm_current(realm)
@ -298,7 +292,6 @@ impl RealmManager {
}
info!("Stopping realm {}", realm.name());
self.inner().events.send_event(RealmEvent::Stopping(realm.clone()));
if realm.config().flatpak() {
if let Err(err) = self.stop_gnome_software_sandbox(realm) {
@ -307,12 +300,8 @@ impl RealmManager {
}
realm.set_active(false);
if let Err(err) = self.systemd.stop_realm(realm) {
self.inner().events.send_event(RealmEvent::Stopped(realm.clone()));
return Err(err);
}
self.systemd.stop_realm(realm)?;
realm.cleanup_rootfs();
self.inner().events.send_event(RealmEvent::Stopped(realm.clone()));
if realm.is_current() {
self.choose_some_current_realm();

View File

@ -169,20 +169,6 @@ impl Realm {
self.inner.write().unwrap()
}
pub fn start(&self) -> Result<()> {
warn!("Realm({})::start()", self.name());
self.manager().start_realm(self)
}
pub fn stop(&self) -> Result<()> {
self.manager().stop_realm(self)
}
pub fn set_current(&self) -> Result<()> {
self.manager().set_current_realm(self)
}
pub fn is_active(&self) -> bool {
self.inner_mut().is_active()
}

View File

@ -9,7 +9,7 @@ homepage = "https://subgraph.com"
[dependencies]
libcitadel = { path = "../libcitadel" }
rand = "0.8"
zvariant = "4.2.0"
zvariant = "2.7.0"
serde = { version = "1.0", features = ["derive"] }
zbus = "4.4.0"
zbus = "=2.0.0-beta.5"
gtk = { version = "0.14.0", features = ["v3_24"] }

View File

@ -1,10 +1,10 @@
use std::collections::HashMap;
use std::rc::Rc;
use zvariant::Type;
use zbus::dbus_proxy;
use zvariant::derive::Type;
use serde::{Serialize,Deserialize};
use zbus::proxy;
use zbus::blocking::Connection;
use crate::error::{Error, Result};
#[derive(Deserialize,Serialize,Type)]
@ -85,12 +85,10 @@ impl RealmConfig {
}
}
#[proxy(
#[dbus_proxy(
default_service = "com.subgraph.realms",
interface = "com.subgraph.realms.Manager",
default_path = "/com/subgraph/realms",
gen_blocking = true,
gen_async = false,
default_path = "/com/subgraph/realms"
)]
pub trait RealmsManager {
fn get_current(&self) -> zbus::Result<String>;
@ -104,7 +102,7 @@ pub trait RealmsManager {
impl RealmsManagerProxy<'_> {
pub fn connect() -> Result<Self> {
let connection = Connection::system()?;
let connection = zbus::Connection::new_system()?;
let proxy = RealmsManagerProxy::new(&connection)
.map_err(|_| Error::ManagerConnect)?;

View File

@ -6,11 +6,8 @@ edition = "2018"
[dependencies]
libcitadel = { path = "../libcitadel" }
async-io = "2.3.2"
blocking = "1.6.1"
event-listener = "5.3.1"
zbus = "4.4.0"
zvariant = "4.2.0"
zbus = "=2.0.0-beta.5"
zvariant = "2.7.0"
serde = { version = "1.0", features = ["derive"] }
serde_repr = "0.1.8"

View File

@ -1,16 +1,15 @@
use async_io::block_on;
use zbus::blocking::Connection;
use zbus::SignalContext;
use zbus::{Connection, ObjectServer};
use crate::realms_manager::{RealmsManagerServer, REALMS_SERVER_OBJECT_PATH, realm_status};
use libcitadel::{RealmEvent, Realm};
pub struct EventHandler {
connection: Connection,
realms_server: RealmsManagerServer,
}
impl EventHandler {
pub fn new(connection: Connection) -> Self {
EventHandler { connection }
pub fn new(connection: Connection, realms_server: RealmsManagerServer) -> Self {
EventHandler { connection, realms_server }
}
pub fn handle_event(&self, ev: &RealmEvent) {
@ -26,49 +25,44 @@ impl EventHandler {
RealmEvent::New(realm) => self.on_new(realm),
RealmEvent::Removed(realm) => self.on_removed(realm),
RealmEvent::Current(realm) => self.on_current(realm.as_ref()),
RealmEvent::Starting(_) => Ok(()),
RealmEvent::Stopping(_) => Ok(()),
}
}
fn with_signal_context<F>(&self, func: F) -> zbus::Result<()>
fn with_server<F>(&self, func: F) -> zbus::Result<()>
where
F: Fn(&SignalContext) -> zbus::Result<()>,
F: Fn(&RealmsManagerServer) -> zbus::Result<()>,
{
let object_server = self.connection.object_server();
let iface = object_server.interface::<_, RealmsManagerServer>(REALMS_SERVER_OBJECT_PATH)?;
let ctx = iface.signal_context();
func(ctx)
let mut object_server = ObjectServer::new(&self.connection);
object_server.at(REALMS_SERVER_OBJECT_PATH, self.realms_server.clone())?;
object_server.with(REALMS_SERVER_OBJECT_PATH, |iface: &RealmsManagerServer| func(iface))
}
fn on_started(&self, realm: &Realm) -> zbus::Result<()> {
let pid_ns = realm.pid_ns().unwrap_or(0);
let status = realm_status(realm);
self.with_signal_context(|ctx| block_on(RealmsManagerServer::realm_started(ctx, realm.name(), pid_ns, status)))
self.with_server(|server| server.realm_started(realm.name(), pid_ns, status))
}
fn on_stopped(&self, realm: &Realm) -> zbus::Result<()> {
let status = realm_status(realm);
self.with_signal_context(|ctx| block_on(RealmsManagerServer::realm_stopped(ctx, realm.name(), status)))
self.with_server(|server| server.realm_stopped(realm.name(), status))
}
fn on_new(&self, realm: &Realm) -> zbus::Result<()> {
let status = realm_status(realm);
let description = realm.notes().unwrap_or(String::new());
self.with_signal_context(|ctx| block_on(RealmsManagerServer::realm_new(ctx, realm.name(), &description, status)))
self.with_server(|server| server.realm_new(realm.name(), &description, status))
}
fn on_removed(&self, realm: &Realm) -> zbus::Result<()> {
self.with_signal_context(|ctx| block_on(RealmsManagerServer::realm_removed(ctx, realm.name())))
self.with_server(|server| server.realm_removed(realm.name()))
}
fn on_current(&self, realm: Option<&Realm>) -> zbus::Result<()> {
self.with_signal_context(|ctx| {
self.with_server(|server| {
match realm {
Some(realm) => block_on(RealmsManagerServer::realm_current(ctx, realm.name(), realm_status(realm))),
None => block_on(RealmsManagerServer::realm_current(ctx, "", 0)),
Some(realm) => server.realm_current(realm.name(), realm_status(realm)),
None => server.realm_current("", 0),
}
})
}

View File

@ -1,18 +1,14 @@
#[macro_use] extern crate libcitadel;
use std::env;
use std::sync::Arc;
use event_listener::{Event, Listener};
use zbus::blocking::Connection;
use zbus::fdo::ObjectManager;
use libcitadel::{Logger, LogLevel, Result, RealmManager};
use crate::next::{RealmsManagerServer2, REALMS2_SERVER_OBJECT_PATH};
use crate::realms_manager::{RealmsManagerServer, REALMS_SERVER_OBJECT_PATH};
use zbus::{Connection, fdo};
use libcitadel::{Logger, LogLevel, Result};
use crate::realms_manager::RealmsManagerServer;
mod realms_manager;
mod events;
mod next;
fn main() {
if let Err(e) = run_realm_manager() {
@ -20,43 +16,24 @@ fn main() {
}
}
fn register_realms_manager_server(connection: &Connection, realm_manager: &Arc<RealmManager>, quit_event: &Arc<Event>) -> Result<()> {
let server = RealmsManagerServer::load(&connection, realm_manager.clone(), quit_event.clone())
.map_err(context!("Loading realms server"))?;
connection.object_server().at(REALMS_SERVER_OBJECT_PATH, server).map_err(context!("registering realms manager object"))?;
Ok(())
}
fn register_realms2_manager_server(connection: &Connection, realm_manager: &Arc<RealmManager>, quit_event: &Arc<Event>) -> Result<()> {
let server2 = RealmsManagerServer2::load(&connection, realm_manager.clone(), quit_event.clone())
.map_err(context!("Loading realms2 server"))?;
connection.object_server().at(REALMS2_SERVER_OBJECT_PATH, server2).map_err(context!("registering realms manager object"))?;
connection.object_server().at(REALMS2_SERVER_OBJECT_PATH, ObjectManager).map_err(context!("registering ObjectManager"))?;
Ok(())
fn create_system_connection() -> zbus::Result<Connection> {
let connection = zbus::Connection::new_system()?;
fdo::DBusProxy::new(&connection)?.request_name("com.subgraph.realms", fdo::RequestNameFlags::AllowReplacement.into())?;
Ok(connection)
}
fn run_realm_manager() -> Result<()> {
Logger::set_log_level(LogLevel::Verbose);
let testing = env::args().skip(1).any(|s| s == "--testing");
let connection = Connection::system()
let connection = create_system_connection()
.map_err(context!("ZBus Connection error"))?;
let mut object_server = RealmsManagerServer::register(&connection)?;
loop {
if let Err(err) = object_server.try_handle_next() {
warn!("Error handling DBus message: {}", err);
}
}
let realm_manager = RealmManager::load()?;
let quit_event = Arc::new(Event::new());
if testing {
register_realms2_manager_server(&connection, &realm_manager, &quit_event)?;
connection.request_name("com.subgraph.Realms2")
.map_err(context!("acquiring realms manager name"))?;
} else {
register_realms_manager_server(&connection, &realm_manager, &quit_event)?;
register_realms2_manager_server(&connection, &realm_manager, &quit_event)?;
connection.request_name("com.subgraph.realms")
.map_err(context!("acquiring realms manager name"))?;
};
quit_event.listen().wait();
Ok(())
}

View File

@ -1,201 +0,0 @@
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use serde::{Deserialize, Serialize};
use zbus::fdo;
use zvariant::Type;
use libcitadel::{OverlayType, Realm, GLOBAL_CONFIG};
use libcitadel::terminal::Base16Scheme;
use crate::next::manager::failed;
const BOOL_CONFIG_VARS: &[&str] = &[
"use-gpu", "use-wayland", "use-x11", "use-sound",
"use-shared-dir", "use-network", "use-kvm", "use-ephemeral-home",
"use-media-dir", "use-fuse", "use-flatpak", "use-gpu-card0"
];
fn is_bool_config_variable(variable: &str) -> bool {
BOOL_CONFIG_VARS.iter().any(|&s| s == variable)
}
#[derive(Deserialize,Serialize,Type)]
pub struct RealmConfigVars {
items: HashMap<String,String>,
}
impl RealmConfigVars {
fn new() -> Self {
RealmConfigVars { items: HashMap::new() }
}
pub fn new_global() -> Self {
Self::new_from_config(&GLOBAL_CONFIG)
}
fn new_from_realm(realm: &Realm) -> Self {
let config = realm.config();
Self::new_from_config(&config)
}
fn new_from_config(config: &libcitadel::RealmConfig) -> Self {
let mut vars = RealmConfigVars::new();
vars.add_bool("use-gpu", config.gpu());
vars.add_bool("use-gpu-card0", config.gpu_card0());
vars.add_bool("use-wayland", config.wayland());
vars.add_bool("use-x11", config.x11());
vars.add_bool("use-sound", config.sound());
vars.add_bool("use-shared-dir", config.shared_dir());
vars.add_bool("use-network", config.network());
vars.add_bool("use-kvm", config.kvm());
vars.add_bool("use-ephemeral-home", config.ephemeral_home());
vars.add_bool("use-media-dir", config.media_dir());
vars.add_bool("use-fuse", config.fuse());
vars.add_bool("use-flatpak", config.flatpak());
let overlay = match config.overlay() {
OverlayType::None => "none",
OverlayType::TmpFS => "tmpfs",
OverlayType::Storage => "storage",
};
vars.add("overlay", overlay);
let scheme = match config.terminal_scheme() {
Some(name) => name.to_string(),
None => String::new(),
};
vars.add("terminal-scheme", scheme);
vars.add("realmfs", config.realmfs());
vars
}
fn add_bool(&mut self, name: &str, val: bool) {
let valstr = if val { "true".to_string() } else { "false".to_string() };
self.add(name, valstr);
}
fn add<S,T>(&mut self, k: S, v: T) where S: Into<String>, T: Into<String> {
self.items.insert(k.into(), v.into());
}
}
#[derive(Clone)]
pub struct RealmConfig {
realm: Realm,
changed: Arc<AtomicBool>,
}
impl RealmConfig {
pub fn new(realm: Realm) -> Self {
let changed = Arc::new(AtomicBool::new(false));
RealmConfig { realm, changed }
}
fn mark_changed(&self) {
self.changed.store(true, Ordering::Relaxed);
}
fn is_changed(&self) -> bool {
self.changed.load(Ordering::Relaxed)
}
pub fn config_vars(&self) -> RealmConfigVars {
RealmConfigVars::new_from_realm(&self.realm)
}
fn set_bool_var(&mut self, var: &str, value: &str) -> fdo::Result<()> {
let v = match value {
"true" => true,
"false" => false,
_ => return failed(format!("Invalid boolean value '{}' for realm config variable '{}'", value, var)),
};
let mut has_changed = true;
self.realm.with_mut_config(|c| {
match var {
"use-gpu" if c.gpu() != v => c.use_gpu = Some(v),
_ => has_changed = false,
}
});
if has_changed {
self.mark_changed();
}
Ok(())
}
fn set_overlay(&mut self, value: &str) -> fdo::Result<()> {
let val = match value {
"tmpfs" => Some("tmpfs".to_string()),
"storage" => Some("storage".to_string()),
"none" => None,
_ => return failed(format!("Invalid value '{}' for overlay config", value)),
};
if self.realm.config().overlay != val {
self.realm.with_mut_config(|c| {
c.overlay = Some(value.to_string());
});
self.mark_changed();
}
Ok(())
}
fn set_terminal_scheme(&mut self, value: &str) -> fdo::Result<()> {
if Some(value) == self.realm.config().terminal_scheme() {
return Ok(())
}
let scheme = match Base16Scheme::by_name(value) {
Some(scheme) => scheme,
None => return failed(format!("Invalid terminal color scheme '{}'", value)),
};
let manager = self.realm.manager();
if let Err(err) = scheme.apply_to_realm(&manager, &self.realm) {
return failed(format!("Error applying terminal color scheme: {}", err));
}
self.realm.with_mut_config(|c| {
c.terminal_scheme = Some(value.to_string());
});
self.mark_changed();
Ok(())
}
fn set_realmfs(&mut self, value: &str) -> fdo::Result<()> {
let manager = self.realm.manager();
if manager.realmfs_by_name(value).is_none() {
return failed(format!("Failed to set 'realmfs' config for realm-{}: RealmFS named '{}' does not exist", self.realm.name(), value));
}
if self.realm.config().realmfs() != value {
self.realm.with_mut_config(|c| {
c.realmfs = Some(value.to_string())
});
self.mark_changed();
}
Ok(())
}
pub fn save_config(&self) -> fdo::Result<()> {
if self.is_changed() {
self.realm.config()
.write()
.map_err(|err| fdo::Error::Failed(format!("Error writing config file for realm-{}: {}", self.realm.name(), err)))?;
self.changed.store(false, Ordering::Relaxed);
}
Ok(())
}
pub fn set_var(&mut self, var: &str, value: &str) -> fdo::Result<()> {
if is_bool_config_variable(var) {
self.set_bool_var(var, value)
} else if var == "overlay" {
self.set_overlay(value)
} else if var == "terminal-scheme" {
self.set_terminal_scheme(value)
} else if var == "realmfs" {
self.set_realmfs(value)
} else {
failed(format!("Unknown realm configuration variable '{}'", var))
}
}
}

View File

@ -1,103 +0,0 @@
use std::sync::Arc;
use blocking::unblock;
use event_listener::{Event, EventListener};
use serde::Serialize;
use serde_repr::Serialize_repr;
use zbus::blocking::Connection;
use zbus::{fdo, interface};
use zvariant::Type;
use libcitadel::{PidLookupResult, RealmManager};
use crate::next::config::RealmConfigVars;
use crate::next::realm::RealmItemState;
use crate::next::realmfs::RealmFSState;
pub fn failed<T>(message: String) -> fdo::Result<T> {
Err(fdo::Error::Failed(message))
}
#[derive(Serialize_repr, Type, Debug, PartialEq)]
#[repr(u32)]
pub enum PidLookupResultCode {
Unknown = 1,
Realm = 2,
Citadel = 3,
}
#[derive(Debug, Type, Serialize)]
pub struct RealmFromCitadelPid {
code: PidLookupResultCode,
realm: String,
}
impl From<PidLookupResult> for RealmFromCitadelPid {
fn from(result: PidLookupResult) -> Self {
match result {
PidLookupResult::Unknown => RealmFromCitadelPid { code: PidLookupResultCode::Unknown, realm: String::new() },
PidLookupResult::Realm(realm) => RealmFromCitadelPid { code: PidLookupResultCode::Realm, realm: realm.name().to_string() },
PidLookupResult::Citadel => RealmFromCitadelPid { code: PidLookupResultCode::Citadel, realm: String::new() },
}
}
}
pub struct RealmsManagerServer2 {
realms: RealmItemState,
realmfs_state: RealmFSState,
manager: Arc<RealmManager>,
quit_event: Arc<Event>,
}
impl RealmsManagerServer2 {
fn new(connection: Connection, manager: Arc<RealmManager>, quit_event: Arc<Event>) -> Self {
let realms = RealmItemState::new(connection.clone());
let realmfs_state = RealmFSState::new(connection.clone());
RealmsManagerServer2 {
realms,
realmfs_state,
manager,
quit_event,
}
}
pub fn load(connection: &Connection, manager: Arc<RealmManager>, quit_event: Arc<Event>) -> zbus::Result<Self> {
let mut server = Self::new(connection.clone(), manager.clone(), quit_event);
server.realms.load_realms(&manager)?;
server.realmfs_state.load(&manager)?;
server.realms.populate_realmfs(&server.realmfs_state)?;
Ok(server)
}
}
#[interface(name = "com.subgraph.realms.Manager2")]
impl RealmsManagerServer2 {
async fn get_current(&self) -> u32 {
self.realms.get_current()
.map(|r| r.index())
.unwrap_or(0)
}
async fn realm_from_citadel_pid(&self, pid: u32) -> RealmFromCitadelPid {
let manager = self.manager.clone();
unblock(move || {
manager.realm_by_pid(pid).into()
}).await
}
async fn create_realm(&self, name: &str) -> fdo::Result<()> {
let manager = self.manager.clone();
let name = name.to_string();
unblock(move || {
let _ = manager.new_realm(&name).map_err(|err| fdo::Error::Failed(err.to_string()))?;
Ok(())
}).await
}
async fn get_global_config(&self) -> RealmConfigVars {
RealmConfigVars::new_global()
}
}

View File

@ -1,8 +0,0 @@
mod manager;
mod config;
mod realm;
mod realmfs;
pub use manager::RealmsManagerServer2;
pub const REALMS2_SERVER_OBJECT_PATH: &str = "/com/subgraph/Realms2";

View File

@ -1,374 +0,0 @@
use std::collections::HashMap;
use std::convert::TryInto;
use std::sync::{Arc, Mutex, MutexGuard};
use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU32, Ordering};
use blocking::unblock;
use zbus::{interface, fdo};
use zbus::blocking::Connection;
use zbus::names::{BusName, InterfaceName};
use zvariant::{OwnedObjectPath, Value};
use libcitadel::{Realm, RealmEvent, RealmManager, Result};
use crate::next::config::{RealmConfig, RealmConfigVars};
use crate::next::realmfs::RealmFSState;
use crate::next::REALMS2_SERVER_OBJECT_PATH;
#[derive(Clone)]
pub struct RealmItem {
path: String,
index: u32,
realm: Realm,
config: RealmConfig,
in_run_transition: Arc<AtomicBool>,
realmfs_index: Arc<AtomicU32>,
last_timestamp: Arc<AtomicI64>,
}
#[derive(Copy,Clone)]
#[repr(u32)]
enum RealmRunStatus {
Stopped = 0,
Starting,
Running,
Current,
Stopping,
}
impl RealmRunStatus {
fn for_realm(realm: &Realm, in_transition: bool) -> Self {
if in_transition {
if realm.is_active() { Self::Stopping } else { Self::Starting }
} else if realm.is_active() {
if realm.is_current() { Self::Current } else {Self::Running }
} else {
Self::Stopped
}
}
}
impl RealmItem {
pub(crate) fn new_from_realm(index: u32, realm: Realm) -> RealmItem {
let path = format!("{}/Realm{}", REALMS2_SERVER_OBJECT_PATH, index);
let in_run_transition = Arc::new(AtomicBool::new(false));
let config = RealmConfig::new(realm.clone());
let realmfs_index = Arc::new(AtomicU32::new(0));
let last_timestamp = Arc::new(AtomicI64::new(realm.timestamp()));
RealmItem { path, index, realm, config, in_run_transition, realmfs_index, last_timestamp }
}
pub fn path(&self) -> &str {
&self.path
}
pub fn index(&self) -> u32 {
self.index
}
fn in_run_transition(&self) -> bool {
self.in_run_transition.load(Ordering::Relaxed)
}
fn get_run_status(&self) -> RealmRunStatus {
RealmRunStatus::for_realm(&self.realm, self.in_run_transition())
}
async fn do_start(&mut self) -> fdo::Result<()> {
if !self.realm.is_active() {
let realm = self.realm.clone();
let res = unblock(move || realm.start()).await;
if let Err(err) = res {
return Err(fdo::Error::Failed(format!("Failed to start realm: {}", err)));
}
}
Ok(())
}
async fn do_stop(&mut self) -> fdo::Result<()> {
if self.realm.is_active() {
let realm = self.realm.clone();
let res = unblock(move || realm.stop()).await;
if let Err(err) = res {
return Err(fdo::Error::Failed(format!("Failed to stop realm: {}", err)));
}
}
Ok(())
}
}
#[interface(
name = "com.subgraph.realms.Realm"
)]
impl RealmItem {
async fn start(
&mut self,
) -> fdo::Result<()> {
self.do_start().await?;
Ok(())
}
async fn stop(
&mut self,
) -> fdo::Result<()> {
self.do_stop().await?;
Ok(())
}
async fn restart(
&mut self,
) -> fdo::Result<()> {
self.do_stop().await?;
self.do_start().await?;
Ok(())
}
async fn set_current(&mut self) -> fdo::Result<()> {
let realm = self.realm.clone();
let res = unblock(move || realm.set_current()).await;
if let Err(err) = res {
return Err(fdo::Error::Failed(format!("Failed to set realm {} as current: {}", self.realm.name(), err)));
}
Ok(())
}
async fn get_config(&self) -> RealmConfigVars {
self.config.config_vars()
}
async fn set_config(&mut self, vars: Vec<(String, String)>) -> fdo::Result<()> {
for (var, val) in &vars {
self.config.set_var(var, val)?;
}
let config = self.config.clone();
unblock(move || config.save_config()).await
}
#[zbus(property, name = "RunStatus")]
fn run_status(&self) -> u32 {
self.get_run_status() as u32
}
#[zbus(property, name="IsSystemRealm")]
fn is_system_realm(&self) -> bool {
self.realm.is_system()
}
#[zbus(property, name = "Name")]
fn name(&self) -> &str {
self.realm.name()
}
#[zbus(property, name = "Description")]
fn description(&self) -> String {
self.realm.notes()
.unwrap_or(String::new())
}
#[zbus(property, name = "PidNS")]
fn pid_ns(&self) -> u64 {
self.realm.pid_ns().unwrap_or_default()
}
#[zbus(property, name = "RealmFS")]
fn realmfs(&self) -> u32 {
self.realmfs_index.load(Ordering::Relaxed)
}
#[zbus(property, name = "Timestamp")]
fn timestamp(&self) -> u64 {
self.realm.timestamp() as u64
}
}
#[derive(Clone)]
pub struct RealmItemState(Arc<Mutex<Inner>>);
struct Inner {
connection: Connection,
next_index: u32,
realms: HashMap<String, RealmItem>,
current_realm: Option<RealmItem>,
}
impl Inner {
fn new(connection: Connection) -> Self {
Inner {
connection,
next_index: 1,
realms:HashMap::new(),
current_realm: None,
}
}
fn load_realms(&mut self, manager: &RealmManager) -> zbus::Result<()> {
for realm in manager.realm_list() {
self.add_realm(realm)?;
}
Ok(())
}
pub fn populate_realmfs(&mut self, realmfs_state: &RealmFSState) -> zbus::Result<()> {
for item in self.realms.values_mut() {
if let Some(realmfs) = realmfs_state.realmfs_by_name(item.realm.config().realmfs()) {
item.realmfs_index.store(realmfs.index(), Ordering::Relaxed);
}
}
Ok(())
}
fn add_realm(&mut self, realm: Realm) -> zbus::Result<()> {
if self.realms.contains_key(realm.name()) {
warn!("Attempted to add duplicate realm '{}'", realm.name());
return Ok(())
}
let key = realm.name().to_string();
let item = RealmItem::new_from_realm(self.next_index, realm);
self.connection.object_server().at(item.path(), item.clone())?;
self.realms.insert(key, item);
self.next_index += 1;
Ok(())
}
fn remove_realm(&mut self, realm: &Realm) -> zbus::Result<()> {
if let Some(item) = self.realms.remove(realm.name()) {
self.connection.object_server().remove::<RealmItem, &str>(item.path())?;
} else {
warn!("Failed to find realm to remove with name '{}'", realm.name());
}
Ok(())
}
fn emit_property_changed(&self, object_path: OwnedObjectPath, propname: &str, value: Value<'_>) -> zbus::Result<()> {
let iface_name = InterfaceName::from_str_unchecked("com.subgraph.realms.Realm");
let changed = HashMap::from([(propname.to_string(), value)]);
let inval: &[&str] = &[];
self.connection.emit_signal(
None::<BusName<'_>>,
&object_path,
"org.freedesktop.Dbus.Properties",
"PropertiesChanged",
&(iface_name, changed, inval))?;
Ok(())
}
fn realm_status_changed(&self, realm: &Realm, transition: Option<bool>) -> zbus::Result<()> {
if let Some(realm) = self.realm_by_name(realm.name()) {
if let Some(transition) = transition {
realm.in_run_transition.store(transition, Ordering::Relaxed);
}
let object_path = realm.path().try_into().unwrap();
self.emit_property_changed(object_path, "RunStatus", Value::U32(realm.get_run_status() as u32))?;
let timestamp = realm.realm.timestamp();
if realm.last_timestamp.load(Ordering::Relaxed) != realm.realm.timestamp() {
realm.last_timestamp.store(timestamp, Ordering::Relaxed);
let object_path = realm.path().try_into().unwrap();
self.emit_property_changed(object_path, "Timestamp", Value::U64(timestamp as u64))?;
}
}
Ok(())
}
fn realm_by_name(&self, name: &str) -> Option<&RealmItem> {
let res = self.realms.get(name);
if res.is_none() {
warn!("Failed to find realm with name '{}'", name);
}
res
}
fn on_starting(&self, realm: &Realm) -> zbus::Result<()>{
self.realm_status_changed(realm, Some(true))?;
Ok(())
}
fn on_started(&self, realm: &Realm) -> zbus::Result<()>{
self.realm_status_changed(realm, Some(false))
}
fn on_stopping(&self, realm: &Realm) -> zbus::Result<()> {
self.realm_status_changed(realm, Some(true))
}
fn on_stopped(&self, realm: &Realm) -> zbus::Result<()> {
self.realm_status_changed(realm, Some(false))
}
fn on_new(&mut self, realm: &Realm) -> zbus::Result<()> {
self.add_realm(realm.clone())?;
Ok(())
}
fn on_removed(&mut self, realm: &Realm) -> zbus::Result<()> {
self.remove_realm(&realm)?;
Ok(())
}
fn on_current(&mut self, realm: Option<&Realm>) -> zbus::Result<()> {
if let Some(r) = self.current_realm.take() {
self.realm_status_changed(&r.realm, None)?;
}
if let Some(realm) = realm {
self.realm_status_changed(realm, None)?;
if let Some(item) = self.realm_by_name(realm.name()) {
self.current_realm = Some(item.clone());
}
}
Ok(())
}
}
impl RealmItemState {
pub fn new(connection: Connection) -> Self {
RealmItemState(Arc::new(Mutex::new(Inner::new(connection))))
}
pub fn load_realms(&self, manager: &RealmManager) -> zbus::Result<()> {
self.inner().load_realms(manager)?;
self.add_event_handler(manager)
.map_err(|err| zbus::Error::Failure(err.to_string()))?;
Ok(())
}
pub fn populate_realmfs(&self, realmfs_state: &RealmFSState) -> zbus::Result<()> {
self.inner().populate_realmfs(realmfs_state)
}
pub fn get_current(&self) -> Option<RealmItem> {
self.inner().current_realm.clone()
}
fn inner(&self) -> MutexGuard<Inner> {
self.0.lock().unwrap()
}
fn add_event_handler(&self, manager: &RealmManager) -> Result<()> {
let state = self.clone();
manager.add_event_handler(move |ev| {
if let Err(err) = state.handle_event(ev) {
warn!("Error handling {}: {}", ev, err);
}
});
manager.start_event_task()?;
Ok(())
}
fn handle_event(&self, ev: &RealmEvent) -> zbus::Result<()> {
match ev {
RealmEvent::Started(realm) => self.inner().on_started(realm)?,
RealmEvent::Stopped(realm) => self.inner().on_stopped(realm)?,
RealmEvent::New(realm) => self.inner().on_new(realm)?,
RealmEvent::Removed(realm) => self.inner().on_removed(realm)?,
RealmEvent::Current(realm) => self.inner().on_current(realm.as_ref())?,
RealmEvent::Starting(realm) => self.inner().on_starting(realm)?,
RealmEvent::Stopping(realm) => self.inner().on_stopping(realm)?,
};
Ok(())
}
}

View File

@ -1,120 +0,0 @@
use std::collections::HashMap;
use std::convert::TryInto;
use zbus::blocking::Connection;
use zbus::{fdo, interface};
use zvariant::{ObjectPath, OwnedObjectPath};
use libcitadel::{RealmFS, RealmManager};
use crate::next::REALMS2_SERVER_OBJECT_PATH;
const BLOCK_SIZE: u64 = 4096;
#[derive(Clone)]
pub struct RealmFSItem {
object_path: OwnedObjectPath,
index: u32,
realmfs: RealmFS,
}
impl RealmFSItem {
pub(crate) fn new_from_realmfs(index: u32, realmfs: RealmFS) -> RealmFSItem {
let object_path = format!("{}/RealmFS{}", REALMS2_SERVER_OBJECT_PATH, index).try_into().unwrap();
RealmFSItem {
object_path,
index,
realmfs,
}
}
pub fn index(&self) -> u32 {
self.index
}
pub fn object_path(&self) -> ObjectPath {
self.object_path.as_ref()
}
}
#[interface(
name = "com.subgraph.realms.RealmFS"
)]
impl RealmFSItem {
#[zbus(property, name = "Name")]
fn name(&self) -> &str {
self.realmfs.name()
}
#[zbus(property, name = "Activated")]
fn activated(&self) -> bool {
self.realmfs.is_activated()
}
#[zbus(property, name = "InUse")]
fn in_use(&self) -> bool {
self.realmfs.is_activated()
}
#[zbus(property, name = "Mountpoint")]
fn mountpoint(&self) -> String {
self.realmfs.mountpoint().to_string()
}
#[zbus(property, name = "Path")]
fn path(&self) -> String {
format!("{}", self.realmfs.path().display())
}
#[zbus(property, name = "FreeSpace")]
fn free_space(&self) -> fdo::Result<u64> {
let blocks = self.realmfs.free_size_blocks()
.map_err(|err| fdo::Error::Failed(err.to_string()))?;
Ok(blocks as u64 * BLOCK_SIZE)
}
#[zbus(property, name = "AllocatedSpace")]
fn allocated_space(&self) -> fdo::Result<u64> {
let blocks = self.realmfs.allocated_size_blocks()
.map_err(|err| fdo::Error::Failed(err.to_string()))?;
Ok(blocks as u64 * BLOCK_SIZE)
}
}
pub struct RealmFSState {
connection: Connection,
next_index: u32,
items: HashMap<String, RealmFSItem>,
}
impl RealmFSState {
pub fn new(connection: Connection) -> Self {
RealmFSState {
connection,
next_index: 1,
items: HashMap::new(),
}
}
pub fn load(&mut self, manager: &RealmManager) -> zbus::Result<()> {
for realmfs in manager.realmfs_list() {
self.add_realmfs(realmfs)?;
}
Ok(())
}
fn add_realmfs(&mut self, realmfs: RealmFS) -> zbus::Result<()> {
if !self.items.contains_key(realmfs.name()) {
let name = realmfs.name().to_string();
let item = RealmFSItem::new_from_realmfs(self.next_index, realmfs);
self.connection.object_server().at(item.object_path(), item.clone())?;
self.items.insert(name, item);
self.next_index += 1;
} else {
warn!("Attempted to add duplicate realmfs '{}'", realmfs.name());
}
Ok(())
}
pub fn realmfs_by_name(&self, name: &str) -> Option<&RealmFSItem> {
let res = self.items.get(name);
if res.is_none() {
warn!("Failed to find RealmFS with name '{}'", name);
}
res
}
}

View File

@ -1,14 +1,11 @@
use libcitadel::{RealmManager, Realm, OverlayType, Result, PidLookupResult};
use std::sync::Arc;
use zbus::blocking::Connection;
use zvariant::Type;
use zbus::{dbus_interface, ObjectServer,Connection};
use zvariant::derive::Type;
use std::thread;
use std::collections::HashMap;
use blocking::unblock;
use event_listener::{Event, EventListener};
use serde::{Serialize,Deserialize};
use serde_repr::Serialize_repr;
use zbus::{interface, SignalContext};
use crate::events::EventHandler;
use libcitadel::terminal::Base16Scheme;
@ -42,7 +39,6 @@ impl From<PidLookupResult> for RealmFromCitadelPid {
#[derive(Clone)]
pub struct RealmsManagerServer {
manager: Arc<RealmManager>,
quit_event: Arc<Event>,
}
const BOOL_CONFIG_VARS: &[&str] = &[
@ -125,40 +121,40 @@ fn configure_realm(manager: &RealmManager, realm: &Realm, variable: &str, value:
impl RealmsManagerServer {
pub fn load(connection: &Connection, manager: Arc<RealmManager>, quit_event: Arc<Event>) -> Result<RealmsManagerServer> {
let server = RealmsManagerServer { manager, quit_event };
let events = EventHandler::new(connection.clone());
server.manager.add_event_handler(move |ev| events.handle_event(ev));
server.manager.start_event_task()?;
Ok(server)
fn register_events(&self, connection: &Connection) -> Result<()> {
let events = EventHandler::new(connection.clone(), self.clone());
self.manager.add_event_handler(move |ev| events.handle_event(ev));
self.manager.start_event_task()
}
pub fn register(connection: &Connection) -> Result<ObjectServer> {
let manager = RealmManager::load()?;
let iface = RealmsManagerServer { manager };
iface.register_events(connection)?;
let mut object_server = ObjectServer::new(connection);
object_server.at(REALMS_SERVER_OBJECT_PATH, iface).map_err(context!("ZBus error"))?;
Ok(object_server)
}
}
#[interface(name = "com.subgraph.realms.Manager")]
#[dbus_interface(name = "com.subgraph.realms.Manager")]
impl RealmsManagerServer {
async fn set_current(&self, name: &str) {
let manager = self.manager.clone();
let name = name.to_string();
unblock(move || {
if let Some(realm) = manager.realm_by_name(&name) {
if let Err(err) = manager.set_current_realm(&realm) {
fn set_current(&self, name: &str) {
if let Some(realm) = self.manager.realm_by_name(name) {
if let Err(err) = self.manager.set_current_realm(&realm) {
warn!("set_current_realm({}) failed: {}", name, err);
}
}
}).await
}
async fn get_current(&self) -> String {
let manager = self.manager.clone();
unblock(move || {
match manager.current_realm() {
fn get_current(&self) -> String {
match self.manager.current_realm() {
Some(realm) => realm.name().to_string(),
None => String::new(),
}
}).await
}
fn list(&self) -> Vec<RealmItem> {
@ -253,12 +249,8 @@ impl RealmsManagerServer {
});
}
async fn realm_from_citadel_pid(&self, pid: u32) -> RealmFromCitadelPid {
let manager = self.manager.clone();
unblock(move || {
manager.realm_by_pid(pid).into()
}).await
fn realm_from_citadel_pid(&self, pid: u32) -> RealmFromCitadelPid {
self.manager.realm_by_pid(pid).into()
}
fn realm_config(&self, name: &str) -> RealmConfig {
@ -269,7 +261,7 @@ impl RealmsManagerServer {
RealmConfig::new_from_realm(&realm)
}
async fn realm_set_config(&self, name: &str, vars: Vec<(String,String)>) {
fn realm_set_config(&self, name: &str, vars: Vec<(String,String)>) {
let realm = match self.manager.realm_by_name(name) {
Some(r) => r,
None => {
@ -278,12 +270,8 @@ impl RealmsManagerServer {
},
};
for var in vars {
let manager = self.manager.clone();
let realm = realm.clone();
unblock( move || {
configure_realm(&manager, &realm, &var.0, &var.1);
}).await;
for var in &vars {
configure_realm(&self.manager, &realm, &var.0, &var.1);
}
}
@ -291,18 +279,13 @@ impl RealmsManagerServer {
Realm::is_valid_name(name) && self.manager.realm_by_name(name).is_some()
}
async fn create_realm(&self, name: &str) -> bool {
let manager = self.manager.clone();
let name = name.to_string();
unblock(move || {
if let Err(err) = manager.new_realm(&name) {
fn create_realm(&self, name: &str) -> bool {
if let Err(err) = self.manager.new_realm(name) {
warn!("Error creating realm ({}): {}", name, err);
false
} else {
true
}
}).await
}
fn list_realm_f_s(&self) -> Vec<String> {
@ -316,23 +299,23 @@ impl RealmsManagerServer {
}
#[zbus(signal)]
pub async fn realm_started(ctx: &SignalContext<'_>, realm: &str, pid_ns: u64, status: u8) -> zbus::Result<()>;
#[dbus_interface(signal)]
pub fn realm_started(&self, realm: &str, pid_ns: u64, status: u8) -> zbus::Result<()> { Ok(()) }
#[zbus(signal)]
pub async fn realm_stopped(ctx: &SignalContext<'_>, realm: &str, status: u8) -> zbus::Result<()>;
#[dbus_interface(signal)]
pub fn realm_stopped(&self, realm: &str, status: u8) -> zbus::Result<()> { Ok(()) }
#[zbus(signal)]
pub async fn realm_new(ctx: &SignalContext<'_>, realm: &str, description: &str, status: u8) -> zbus::Result<()>;
#[dbus_interface(signal)]
pub fn realm_new(&self, realm: &str, description: &str, status: u8) -> zbus::Result<()> { Ok(()) }
#[zbus(signal)]
pub async fn realm_removed(ctx: &SignalContext<'_>, realm: &str) -> zbus::Result<()>;
#[dbus_interface(signal)]
pub fn realm_removed(&self, realm: &str) -> zbus::Result<()> { Ok(()) }
#[zbus(signal)]
pub async fn realm_current(ctx: &SignalContext<'_>, realm: &str, status: u8) -> zbus::Result<()>;
#[dbus_interface(signal)]
pub fn realm_current(&self, realm: &str, status: u8) -> zbus::Result<()> { Ok(()) }
#[zbus(signal)]
pub async fn service_started(ctx: &SignalContext<'_>) -> zbus::Result<()>;
#[dbus_interface(signal)]
pub fn service_started(&self) -> zbus::Result<()> { Ok(()) }
}