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> <busconfig>
<policy user="root"> <policy user="root">
<allow own="com.subgraph.realms"/> <allow own="com.subgraph.realms"/>
<allow own="com.subgraph.Realms2"/>
</policy> </policy>
<policy context="default"> <policy context="default">
<allow send_destination="com.subgraph.realms"/> <allow send_destination="com.subgraph.realms"/>
<allow send_destination="com.subgraph.Realms2"/>
<allow send_destination="com.subgraph.realms" <allow send_destination="com.subgraph.realms"
send_interface="org.freedesktop.DBus.Properties"/> send_interface="org.freedesktop.DBus.Properties"/>
<allow send_destination="com.subgraph.realms" <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}; use inotify::{Inotify, WatchMask, WatchDescriptor, Event};
pub enum RealmEvent { pub enum RealmEvent {
Starting(Realm),
Started(Realm), Started(Realm),
Stopping(Realm),
Stopped(Realm), Stopped(Realm),
New(Realm), New(Realm),
Removed(Realm), Removed(Realm),
@ -24,9 +22,7 @@ pub enum RealmEvent {
impl Display for RealmEvent { impl Display for RealmEvent {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
RealmEvent::Starting(ref realm) => write!(f, "RealmStarting({})", realm.name()),
RealmEvent::Started(ref realm) => write!(f, "RealmStarted({})", 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::Stopped(ref realm) => write!(f, "RealmStopped({})", realm.name()),
RealmEvent::New(ref realm) => write!(f, "RealmNew({})", realm.name()), RealmEvent::New(ref realm) => write!(f, "RealmNew({})", realm.name()),
RealmEvent::Removed(ref realm) => write!(f, "RealmRemoved({})", realm.name()), RealmEvent::Removed(ref realm) => write!(f, "RealmRemoved({})", realm.name()),

View File

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

View File

@ -169,20 +169,6 @@ impl Realm {
self.inner.write().unwrap() 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 { pub fn is_active(&self) -> bool {
self.inner_mut().is_active() self.inner_mut().is_active()
} }

View File

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

View File

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

View File

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

View File

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

View File

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