New improved realms daemon implementation
This commit is contained in:
parent
24f786cf75
commit
11b3e8a016
677
Cargo.lock
generated
677
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -7,10 +7,12 @@
|
||||
<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"
|
||||
|
@ -12,7 +12,9 @@ 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),
|
||||
@ -22,7 +24,9 @@ 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()),
|
||||
|
@ -194,7 +194,13 @@ impl RealmManager {
|
||||
return Ok(());
|
||||
}
|
||||
info!("Starting realm {}", realm.name());
|
||||
self._start_realm(realm, &mut HashSet::new())?;
|
||||
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()));
|
||||
|
||||
if !Realms::is_some_realm_current() {
|
||||
self.inner_mut().realms.set_realm_current(realm)
|
||||
@ -292,6 +298,7 @@ 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) {
|
||||
@ -300,8 +307,12 @@ impl RealmManager {
|
||||
}
|
||||
|
||||
realm.set_active(false);
|
||||
self.systemd.stop_realm(realm)?;
|
||||
if let Err(err) = self.systemd.stop_realm(realm) {
|
||||
self.inner().events.send_event(RealmEvent::Stopped(realm.clone()));
|
||||
return Err(err);
|
||||
}
|
||||
realm.cleanup_rootfs();
|
||||
self.inner().events.send_event(RealmEvent::Stopped(realm.clone()));
|
||||
|
||||
if realm.is_current() {
|
||||
self.choose_some_current_realm();
|
||||
|
@ -169,6 +169,20 @@ 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()
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ homepage = "https://subgraph.com"
|
||||
[dependencies]
|
||||
libcitadel = { path = "../libcitadel" }
|
||||
rand = "0.8"
|
||||
zvariant = "2.7.0"
|
||||
zvariant = "4.2.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
zbus = "=2.0.0-beta.5"
|
||||
zbus = "4.4.0"
|
||||
gtk = { version = "0.14.0", features = ["v3_24"] }
|
||||
|
@ -1,10 +1,10 @@
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use zbus::dbus_proxy;
|
||||
use zvariant::derive::Type;
|
||||
use zvariant::Type;
|
||||
use serde::{Serialize,Deserialize};
|
||||
|
||||
use zbus::proxy;
|
||||
use zbus::blocking::Connection;
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
#[derive(Deserialize,Serialize,Type)]
|
||||
@ -85,10 +85,12 @@ impl RealmConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[dbus_proxy(
|
||||
#[proxy(
|
||||
default_service = "com.subgraph.realms",
|
||||
interface = "com.subgraph.realms.Manager",
|
||||
default_path = "/com/subgraph/realms"
|
||||
default_path = "/com/subgraph/realms",
|
||||
gen_blocking = true,
|
||||
gen_async = false,
|
||||
)]
|
||||
pub trait RealmsManager {
|
||||
fn get_current(&self) -> zbus::Result<String>;
|
||||
@ -102,7 +104,7 @@ pub trait RealmsManager {
|
||||
|
||||
impl RealmsManagerProxy<'_> {
|
||||
pub fn connect() -> Result<Self> {
|
||||
let connection = zbus::Connection::new_system()?;
|
||||
let connection = Connection::system()?;
|
||||
|
||||
let proxy = RealmsManagerProxy::new(&connection)
|
||||
.map_err(|_| Error::ManagerConnect)?;
|
||||
|
@ -6,8 +6,11 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
libcitadel = { path = "../libcitadel" }
|
||||
zbus = "=2.0.0-beta.5"
|
||||
zvariant = "2.7.0"
|
||||
async-io = "2.3.2"
|
||||
blocking = "1.6.1"
|
||||
event-listener = "5.3.1"
|
||||
zbus = "4.4.0"
|
||||
zvariant = "4.2.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_repr = "0.1.8"
|
||||
|
||||
|
@ -1,15 +1,16 @@
|
||||
use zbus::{Connection, ObjectServer};
|
||||
use async_io::block_on;
|
||||
use zbus::blocking::Connection;
|
||||
use zbus::SignalContext;
|
||||
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, realms_server: RealmsManagerServer) -> Self {
|
||||
EventHandler { connection, realms_server }
|
||||
pub fn new(connection: Connection) -> Self {
|
||||
EventHandler { connection }
|
||||
}
|
||||
|
||||
pub fn handle_event(&self, ev: &RealmEvent) {
|
||||
@ -25,44 +26,49 @@ 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_server<F>(&self, func: F) -> zbus::Result<()>
|
||||
fn with_signal_context<F>(&self, func: F) -> zbus::Result<()>
|
||||
where
|
||||
F: Fn(&RealmsManagerServer) -> zbus::Result<()>,
|
||||
F: Fn(&SignalContext) -> zbus::Result<()>,
|
||||
{
|
||||
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))
|
||||
let object_server = self.connection.object_server();
|
||||
let iface = object_server.interface::<_, RealmsManagerServer>(REALMS_SERVER_OBJECT_PATH)?;
|
||||
|
||||
let ctx = iface.signal_context();
|
||||
func(ctx)
|
||||
}
|
||||
|
||||
fn on_started(&self, realm: &Realm) -> zbus::Result<()> {
|
||||
let pid_ns = realm.pid_ns().unwrap_or(0);
|
||||
let status = realm_status(realm);
|
||||
self.with_server(|server| server.realm_started(realm.name(), pid_ns, status))
|
||||
self.with_signal_context(|ctx| block_on(RealmsManagerServer::realm_started(ctx, realm.name(), pid_ns, status)))
|
||||
}
|
||||
|
||||
fn on_stopped(&self, realm: &Realm) -> zbus::Result<()> {
|
||||
let status = realm_status(realm);
|
||||
self.with_server(|server| server.realm_stopped(realm.name(), status))
|
||||
self.with_signal_context(|ctx| block_on(RealmsManagerServer::realm_stopped(ctx, 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_server(|server| server.realm_new(realm.name(), &description, status))
|
||||
self.with_signal_context(|ctx| block_on(RealmsManagerServer::realm_new(ctx, realm.name(), &description, status)))
|
||||
}
|
||||
|
||||
fn on_removed(&self, realm: &Realm) -> zbus::Result<()> {
|
||||
self.with_server(|server| server.realm_removed(realm.name()))
|
||||
self.with_signal_context(|ctx| block_on(RealmsManagerServer::realm_removed(ctx, realm.name())))
|
||||
}
|
||||
|
||||
fn on_current(&self, realm: Option<&Realm>) -> zbus::Result<()> {
|
||||
self.with_server(|server| {
|
||||
self.with_signal_context(|ctx| {
|
||||
match realm {
|
||||
Some(realm) => server.realm_current(realm.name(), realm_status(realm)),
|
||||
None => server.realm_current("", 0),
|
||||
Some(realm) => block_on(RealmsManagerServer::realm_current(ctx, realm.name(), realm_status(realm))),
|
||||
None => block_on(RealmsManagerServer::realm_current(ctx, "", 0)),
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,14 +1,18 @@
|
||||
#[macro_use] extern crate libcitadel;
|
||||
|
||||
use zbus::{Connection, fdo};
|
||||
|
||||
use libcitadel::{Logger, LogLevel, Result};
|
||||
|
||||
use crate::realms_manager::RealmsManagerServer;
|
||||
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};
|
||||
|
||||
mod realms_manager;
|
||||
mod events;
|
||||
|
||||
mod next;
|
||||
|
||||
fn main() {
|
||||
if let Err(e) = run_realm_manager() {
|
||||
@ -16,24 +20,43 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
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 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 run_realm_manager() -> Result<()> {
|
||||
Logger::set_log_level(LogLevel::Verbose);
|
||||
|
||||
let connection = create_system_connection()
|
||||
let testing = env::args().skip(1).any(|s| s == "--testing");
|
||||
|
||||
let connection = Connection::system()
|
||||
.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(())
|
||||
}
|
||||
|
201
realmsd/src/next/config.rs
Normal file
201
realmsd/src/next/config.rs
Normal file
@ -0,0 +1,201 @@
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
103
realmsd/src/next/manager.rs
Normal file
103
realmsd/src/next/manager.rs
Normal file
@ -0,0 +1,103 @@
|
||||
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()
|
||||
}
|
||||
|
||||
}
|
8
realmsd/src/next/mod.rs
Normal file
8
realmsd/src/next/mod.rs
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
mod manager;
|
||||
mod config;
|
||||
mod realm;
|
||||
mod realmfs;
|
||||
|
||||
pub use manager::RealmsManagerServer2;
|
||||
pub const REALMS2_SERVER_OBJECT_PATH: &str = "/com/subgraph/Realms2";
|
374
realmsd/src/next/realm.rs
Normal file
374
realmsd/src/next/realm.rs
Normal file
@ -0,0 +1,374 @@
|
||||
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(())
|
||||
}
|
||||
}
|
120
realmsd/src/next/realmfs.rs
Normal file
120
realmsd/src/next/realmfs.rs
Normal file
@ -0,0 +1,120 @@
|
||||
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
|
||||
}
|
||||
}
|
@ -1,11 +1,14 @@
|
||||
use libcitadel::{RealmManager, Realm, OverlayType, Result, PidLookupResult};
|
||||
use std::sync::Arc;
|
||||
use zbus::{dbus_interface, ObjectServer,Connection};
|
||||
use zvariant::derive::Type;
|
||||
use zbus::blocking::Connection;
|
||||
use zvariant::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;
|
||||
|
||||
@ -39,6 +42,7 @@ impl From<PidLookupResult> for RealmFromCitadelPid {
|
||||
#[derive(Clone)]
|
||||
pub struct RealmsManagerServer {
|
||||
manager: Arc<RealmManager>,
|
||||
quit_event: Arc<Event>,
|
||||
}
|
||||
|
||||
const BOOL_CONFIG_VARS: &[&str] = &[
|
||||
@ -121,40 +125,40 @@ fn configure_realm(manager: &RealmManager, realm: &Realm, variable: &str, value:
|
||||
|
||||
impl RealmsManagerServer {
|
||||
|
||||
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 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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#[dbus_interface(name = "com.subgraph.realms.Manager")]
|
||||
#[interface(name = "com.subgraph.realms.Manager")]
|
||||
impl RealmsManagerServer {
|
||||
|
||||
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) {
|
||||
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) {
|
||||
warn!("set_current_realm({}) failed: {}", name, err);
|
||||
}
|
||||
}
|
||||
}).await
|
||||
}
|
||||
|
||||
fn get_current(&self) -> String {
|
||||
match self.manager.current_realm() {
|
||||
async fn get_current(&self) -> String {
|
||||
let manager = self.manager.clone();
|
||||
unblock(move || {
|
||||
match manager.current_realm() {
|
||||
Some(realm) => realm.name().to_string(),
|
||||
None => String::new(),
|
||||
}
|
||||
}).await
|
||||
}
|
||||
|
||||
fn list(&self) -> Vec<RealmItem> {
|
||||
@ -249,8 +253,12 @@ impl RealmsManagerServer {
|
||||
});
|
||||
}
|
||||
|
||||
fn realm_from_citadel_pid(&self, pid: u32) -> RealmFromCitadelPid {
|
||||
self.manager.realm_by_pid(pid).into()
|
||||
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_config(&self, name: &str) -> RealmConfig {
|
||||
@ -261,7 +269,7 @@ impl RealmsManagerServer {
|
||||
RealmConfig::new_from_realm(&realm)
|
||||
}
|
||||
|
||||
fn realm_set_config(&self, name: &str, vars: Vec<(String,String)>) {
|
||||
async fn realm_set_config(&self, name: &str, vars: Vec<(String,String)>) {
|
||||
let realm = match self.manager.realm_by_name(name) {
|
||||
Some(r) => r,
|
||||
None => {
|
||||
@ -270,8 +278,12 @@ impl RealmsManagerServer {
|
||||
},
|
||||
};
|
||||
|
||||
for var in &vars {
|
||||
configure_realm(&self.manager, &realm, &var.0, &var.1);
|
||||
for var in vars {
|
||||
let manager = self.manager.clone();
|
||||
let realm = realm.clone();
|
||||
unblock( move || {
|
||||
configure_realm(&manager, &realm, &var.0, &var.1);
|
||||
}).await;
|
||||
}
|
||||
}
|
||||
|
||||
@ -279,13 +291,18 @@ impl RealmsManagerServer {
|
||||
Realm::is_valid_name(name) && self.manager.realm_by_name(name).is_some()
|
||||
}
|
||||
|
||||
fn create_realm(&self, name: &str) -> bool {
|
||||
if let Err(err) = self.manager.new_realm(name) {
|
||||
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) {
|
||||
warn!("Error creating realm ({}): {}", name, err);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}).await
|
||||
}
|
||||
|
||||
fn list_realm_f_s(&self) -> Vec<String> {
|
||||
@ -299,23 +316,23 @@ impl RealmsManagerServer {
|
||||
|
||||
}
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
pub fn realm_started(&self, realm: &str, pid_ns: u64, status: u8) -> zbus::Result<()> { Ok(()) }
|
||||
#[zbus(signal)]
|
||||
pub async fn realm_started(ctx: &SignalContext<'_>, realm: &str, pid_ns: u64, 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_stopped(ctx: &SignalContext<'_>, realm: &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_new(ctx: &SignalContext<'_>, realm: &str, description: &str, status: u8) -> zbus::Result<()>;
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
pub fn realm_removed(&self, realm: &str) -> zbus::Result<()> { Ok(()) }
|
||||
#[zbus(signal)]
|
||||
pub async fn realm_removed(ctx: &SignalContext<'_>, realm: &str) -> zbus::Result<()>;
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
pub fn realm_current(&self, realm: &str, status: u8) -> zbus::Result<()> { Ok(()) }
|
||||
#[zbus(signal)]
|
||||
pub async fn realm_current(ctx: &SignalContext<'_>, realm: &str, status: u8) -> zbus::Result<()>;
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
pub fn service_started(&self) -> zbus::Result<()> { Ok(()) }
|
||||
#[zbus(signal)]
|
||||
pub async fn service_started(ctx: &SignalContext<'_>) -> zbus::Result<()>;
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user