forked from brl/citadel-tools
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>
|
<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"
|
||||||
|
@ -12,7 +12,9 @@ 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),
|
||||||
@ -22,7 +24,9 @@ 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()),
|
||||||
|
@ -194,7 +194,13 @@ impl RealmManager {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
info!("Starting realm {}", realm.name());
|
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() {
|
if !Realms::is_some_realm_current() {
|
||||||
self.inner_mut().realms.set_realm_current(realm)
|
self.inner_mut().realms.set_realm_current(realm)
|
||||||
@ -292,6 +298,7 @@ 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) {
|
||||||
@ -300,8 +307,12 @@ impl RealmManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
realm.set_active(false);
|
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();
|
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();
|
||||||
|
@ -169,6 +169,20 @@ 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()
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ homepage = "https://subgraph.com"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
libcitadel = { path = "../libcitadel" }
|
libcitadel = { path = "../libcitadel" }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
zvariant = "2.7.0"
|
zvariant = "4.2.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
zbus = "=2.0.0-beta.5"
|
zbus = "4.4.0"
|
||||||
gtk = { version = "0.14.0", features = ["v3_24"] }
|
gtk = { version = "0.14.0", features = ["v3_24"] }
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use zbus::dbus_proxy;
|
use zvariant::Type;
|
||||||
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,10 +85,12 @@ impl RealmConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[dbus_proxy(
|
#[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>;
|
||||||
@ -102,7 +104,7 @@ pub trait RealmsManager {
|
|||||||
|
|
||||||
impl RealmsManagerProxy<'_> {
|
impl RealmsManagerProxy<'_> {
|
||||||
pub fn connect() -> Result<Self> {
|
pub fn connect() -> Result<Self> {
|
||||||
let connection = zbus::Connection::new_system()?;
|
let connection = Connection::system()?;
|
||||||
|
|
||||||
let proxy = RealmsManagerProxy::new(&connection)
|
let proxy = RealmsManagerProxy::new(&connection)
|
||||||
.map_err(|_| Error::ManagerConnect)?;
|
.map_err(|_| Error::ManagerConnect)?;
|
||||||
|
@ -6,8 +6,11 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libcitadel = { path = "../libcitadel" }
|
libcitadel = { path = "../libcitadel" }
|
||||||
zbus = "=2.0.0-beta.5"
|
async-io = "2.3.2"
|
||||||
zvariant = "2.7.0"
|
blocking = "1.6.1"
|
||||||
|
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"
|
||||||
|
|
||||||
|
@ -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 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, realms_server: RealmsManagerServer) -> Self {
|
pub fn new(connection: Connection) -> Self {
|
||||||
EventHandler { connection, realms_server }
|
EventHandler { connection }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_event(&self, ev: &RealmEvent) {
|
pub fn handle_event(&self, ev: &RealmEvent) {
|
||||||
@ -25,44 +26,49 @@ 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_server<F>(&self, func: F) -> zbus::Result<()>
|
fn with_signal_context<F>(&self, func: F) -> zbus::Result<()>
|
||||||
where
|
where
|
||||||
F: Fn(&RealmsManagerServer) -> zbus::Result<()>,
|
F: Fn(&SignalContext) -> zbus::Result<()>,
|
||||||
{
|
{
|
||||||
let mut object_server = ObjectServer::new(&self.connection);
|
let object_server = self.connection.object_server();
|
||||||
object_server.at(REALMS_SERVER_OBJECT_PATH, self.realms_server.clone())?;
|
let iface = object_server.interface::<_, RealmsManagerServer>(REALMS_SERVER_OBJECT_PATH)?;
|
||||||
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_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<()> {
|
fn on_stopped(&self, realm: &Realm) -> zbus::Result<()> {
|
||||||
let status = realm_status(realm);
|
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<()> {
|
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_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<()> {
|
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<()> {
|
fn on_current(&self, realm: Option<&Realm>) -> zbus::Result<()> {
|
||||||
self.with_server(|server| {
|
self.with_signal_context(|ctx| {
|
||||||
match realm {
|
match realm {
|
||||||
Some(realm) => server.realm_current(realm.name(), realm_status(realm)),
|
Some(realm) => block_on(RealmsManagerServer::realm_current(ctx, realm.name(), realm_status(realm))),
|
||||||
None => server.realm_current("", 0),
|
None => block_on(RealmsManagerServer::realm_current(ctx, "", 0)),
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
#[macro_use] extern crate libcitadel;
|
#[macro_use] extern crate libcitadel;
|
||||||
|
|
||||||
use zbus::{Connection, fdo};
|
use std::env;
|
||||||
|
use std::sync::Arc;
|
||||||
use libcitadel::{Logger, LogLevel, Result};
|
use event_listener::{Event, Listener};
|
||||||
|
use zbus::blocking::Connection;
|
||||||
use crate::realms_manager::RealmsManagerServer;
|
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 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() {
|
||||||
@ -16,24 +20,43 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_system_connection() -> zbus::Result<Connection> {
|
fn register_realms_manager_server(connection: &Connection, realm_manager: &Arc<RealmManager>, quit_event: &Arc<Event>) -> Result<()> {
|
||||||
let connection = zbus::Connection::new_system()?;
|
let server = RealmsManagerServer::load(&connection, realm_manager.clone(), quit_event.clone())
|
||||||
fdo::DBusProxy::new(&connection)?.request_name("com.subgraph.realms", fdo::RequestNameFlags::AllowReplacement.into())?;
|
.map_err(context!("Loading realms server"))?;
|
||||||
Ok(connection)
|
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<()> {
|
fn run_realm_manager() -> Result<()> {
|
||||||
Logger::set_log_level(LogLevel::Verbose);
|
Logger::set_log_level(LogLevel::Verbose);
|
||||||
|
|
||||||
let connection = create_system_connection()
|
let testing = env::args().skip(1).any(|s| s == "--testing");
|
||||||
.map_err(context!("ZBus Connection error"))?;
|
|
||||||
|
|
||||||
let mut object_server = RealmsManagerServer::register(&connection)?;
|
let connection = Connection::system()
|
||||||
|
.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(())
|
||||||
}
|
}
|
||||||
|
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 libcitadel::{RealmManager, Realm, OverlayType, Result, PidLookupResult};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use zbus::{dbus_interface, ObjectServer,Connection};
|
use zbus::blocking::Connection;
|
||||||
use zvariant::derive::Type;
|
use zvariant::Type;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use serde::{Serialize,Deserialize};
|
use blocking::unblock;
|
||||||
|
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;
|
||||||
|
|
||||||
@ -39,6 +42,7 @@ 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] = &[
|
||||||
@ -121,40 +125,40 @@ fn configure_realm(manager: &RealmManager, realm: &Realm, variable: &str, value:
|
|||||||
|
|
||||||
impl RealmsManagerServer {
|
impl RealmsManagerServer {
|
||||||
|
|
||||||
fn register_events(&self, connection: &Connection) -> Result<()> {
|
pub fn load(connection: &Connection, manager: Arc<RealmManager>, quit_event: Arc<Event>) -> Result<RealmsManagerServer> {
|
||||||
let events = EventHandler::new(connection.clone(), self.clone());
|
let server = RealmsManagerServer { manager, quit_event };
|
||||||
self.manager.add_event_handler(move |ev| events.handle_event(ev));
|
let events = EventHandler::new(connection.clone());
|
||||||
self.manager.start_event_task()
|
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 {
|
impl RealmsManagerServer {
|
||||||
|
|
||||||
fn set_current(&self, name: &str) {
|
async 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) {
|
let manager = self.manager.clone();
|
||||||
warn!("set_current_realm({}) failed: {}", name, err);
|
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 {
|
async fn get_current(&self) -> String {
|
||||||
match self.manager.current_realm() {
|
let manager = self.manager.clone();
|
||||||
Some(realm) => realm.name().to_string(),
|
unblock(move || {
|
||||||
None => String::new(),
|
match manager.current_realm() {
|
||||||
}
|
Some(realm) => realm.name().to_string(),
|
||||||
|
None => String::new(),
|
||||||
|
}
|
||||||
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list(&self) -> Vec<RealmItem> {
|
fn list(&self) -> Vec<RealmItem> {
|
||||||
@ -249,8 +253,12 @@ impl RealmsManagerServer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realm_from_citadel_pid(&self, pid: u32) -> RealmFromCitadelPid {
|
async fn realm_from_citadel_pid(&self, pid: u32) -> RealmFromCitadelPid {
|
||||||
self.manager.realm_by_pid(pid).into()
|
let manager = self.manager.clone();
|
||||||
|
unblock(move || {
|
||||||
|
manager.realm_by_pid(pid).into()
|
||||||
|
|
||||||
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn realm_config(&self, name: &str) -> RealmConfig {
|
fn realm_config(&self, name: &str) -> RealmConfig {
|
||||||
@ -261,7 +269,7 @@ impl RealmsManagerServer {
|
|||||||
RealmConfig::new_from_realm(&realm)
|
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) {
|
let realm = match self.manager.realm_by_name(name) {
|
||||||
Some(r) => r,
|
Some(r) => r,
|
||||||
None => {
|
None => {
|
||||||
@ -270,8 +278,12 @@ impl RealmsManagerServer {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
for var in &vars {
|
for var in vars {
|
||||||
configure_realm(&self.manager, &realm, &var.0, &var.1);
|
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()
|
Realm::is_valid_name(name) && self.manager.realm_by_name(name).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_realm(&self, name: &str) -> bool {
|
async fn create_realm(&self, name: &str) -> bool {
|
||||||
if let Err(err) = self.manager.new_realm(name) {
|
|
||||||
warn!("Error creating realm ({}): {}", name, err);
|
let manager = self.manager.clone();
|
||||||
false
|
let name = name.to_string();
|
||||||
} else {
|
unblock(move || {
|
||||||
true
|
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> {
|
fn list_realm_f_s(&self) -> Vec<String> {
|
||||||
@ -299,23 +316,23 @@ impl RealmsManagerServer {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[dbus_interface(signal)]
|
#[zbus(signal)]
|
||||||
pub fn realm_started(&self, realm: &str, pid_ns: u64, status: u8) -> zbus::Result<()> { Ok(()) }
|
pub async fn realm_started(ctx: &SignalContext<'_>, realm: &str, pid_ns: u64, status: u8) -> zbus::Result<()>;
|
||||||
|
|
||||||
#[dbus_interface(signal)]
|
#[zbus(signal)]
|
||||||
pub fn realm_stopped(&self, realm: &str, status: u8) -> zbus::Result<()> { Ok(()) }
|
pub async fn realm_stopped(ctx: &SignalContext<'_>, realm: &str, status: u8) -> zbus::Result<()>;
|
||||||
|
|
||||||
#[dbus_interface(signal)]
|
#[zbus(signal)]
|
||||||
pub fn realm_new(&self, realm: &str, description: &str, status: u8) -> zbus::Result<()> { Ok(()) }
|
pub async fn realm_new(ctx: &SignalContext<'_>, realm: &str, description: &str, status: u8) -> zbus::Result<()>;
|
||||||
|
|
||||||
#[dbus_interface(signal)]
|
#[zbus(signal)]
|
||||||
pub fn realm_removed(&self, realm: &str) -> zbus::Result<()> { Ok(()) }
|
pub async fn realm_removed(ctx: &SignalContext<'_>, realm: &str) -> zbus::Result<()>;
|
||||||
|
|
||||||
#[dbus_interface(signal)]
|
#[zbus(signal)]
|
||||||
pub fn realm_current(&self, realm: &str, status: u8) -> zbus::Result<()> { Ok(()) }
|
pub async fn realm_current(ctx: &SignalContext<'_>, realm: &str, status: u8) -> zbus::Result<()>;
|
||||||
|
|
||||||
#[dbus_interface(signal)]
|
#[zbus(signal)]
|
||||||
pub fn service_started(&self) -> zbus::Result<()> { Ok(()) }
|
pub async fn service_started(ctx: &SignalContext<'_>) -> zbus::Result<()>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user