forked from brl/citadel-tools
Compare commits
2 Commits
master
...
reproducib
| Author | SHA1 | Date | |
|---|---|---|---|
| 11c1c792db | |||
| df8e0be407 |
@@ -159,7 +159,7 @@ impl <'a> RealmFSInfoRender <'a> {
|
||||
fn render_image(&mut self) {
|
||||
fn sizes(r: &RealmFS) -> Result<(usize,usize)> {
|
||||
let free = r.free_size_blocks()?;
|
||||
let allocated = r.allocated_size_blocks();
|
||||
let allocated = r.allocated_size_blocks()?;
|
||||
Ok((free,allocated))
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ fn main() {
|
||||
} else if exe == Path::new("/usr/bin/citadel-image") {
|
||||
image::main();
|
||||
} else if exe == Path::new("/usr/bin/citadel-realmfs") {
|
||||
realmfs::main(args);
|
||||
realmfs::main();
|
||||
} else if exe == Path::new("/usr/bin/citadel-update") {
|
||||
update::main(args);
|
||||
} else if exe == Path::new("/usr/libexec/citadel-desktop-sync") {
|
||||
@@ -58,7 +58,7 @@ fn dispatch_command(args: Vec<String>) {
|
||||
"boot" => boot::main(rebuild_args("citadel-boot", args)),
|
||||
"install" => install::main(rebuild_args("citadel-install", args)),
|
||||
"image" => image::main(),
|
||||
"realmfs" => realmfs::main(rebuild_args("citadel-realmfs", args)),
|
||||
"realmfs" => realmfs::main(),
|
||||
"update" => update::main(rebuild_args("citadel-update", args)),
|
||||
"mkimage" => mkimage::main(rebuild_args("citadel-mkimage", args)),
|
||||
"sync" => sync::main(rebuild_args("citadel-desktop-sync", args)),
|
||||
|
||||
@@ -6,7 +6,7 @@ use libcitadel::util::is_euid_root;
|
||||
use libcitadel::ResizeSize;
|
||||
use std::process::exit;
|
||||
|
||||
pub fn main(args: Vec<String>) {
|
||||
pub fn main() {
|
||||
|
||||
Logger::set_log_level(LogLevel::Debug);
|
||||
|
||||
@@ -65,7 +65,11 @@ is the final absolute size of the image.")
|
||||
.help("Path or name of RealmFS image to deactivate")
|
||||
.required(true)))
|
||||
|
||||
.get_matches_from(args);
|
||||
|
||||
.arg(Arg::new("image")
|
||||
.help("Name of or path to RealmFS image to display information about")
|
||||
.required(true))
|
||||
.get_matches();
|
||||
|
||||
let result = match matches.subcommand() {
|
||||
Some(("resize", m)) => resize(m),
|
||||
|
||||
@@ -57,8 +57,6 @@ impl RealmFS {
|
||||
// Name used to retrieve key by 'description' from kernel key storage
|
||||
pub const USER_KEYNAME: &'static str = "realmfs-user";
|
||||
|
||||
const BLOCK_SIZE: u64 = 4096;
|
||||
|
||||
/// Locate a RealmFS image by name in the default location using the standard name convention
|
||||
pub fn load_by_name(name: &str) -> Result<Self> {
|
||||
Self::validate_name(name)?;
|
||||
@@ -315,7 +313,7 @@ impl RealmFS {
|
||||
|
||||
info!("forking RealmFS image '{}' to new name '{}'", self.name(), new_name);
|
||||
|
||||
let mut forked = match self.fork_to_path(new_name, &new_path, keys) {
|
||||
let forked = match self.fork_to_path(new_name, &new_path, keys) {
|
||||
Ok(forked) => forked,
|
||||
Err(err) => {
|
||||
if new_path.exists() {
|
||||
@@ -325,10 +323,7 @@ impl RealmFS {
|
||||
}
|
||||
};
|
||||
|
||||
self.with_manager(|m| {
|
||||
m.realmfs_added(&forked);
|
||||
forked.set_manager(m);
|
||||
});
|
||||
self.with_manager(|m| m.realmfs_added(&forked));
|
||||
Ok(forked)
|
||||
}
|
||||
|
||||
@@ -373,11 +368,11 @@ impl RealmFS {
|
||||
pub fn file_nblocks(&self) -> Result<usize> {
|
||||
let meta = self.path.metadata()
|
||||
.map_err(context!("failed to read metadata from realmfs image file {:?}", self.path))?;
|
||||
let len = meta.len();
|
||||
if len % Self::BLOCK_SIZE != 0 {
|
||||
let len = meta.len() as usize;
|
||||
if len % 4096 != 0 {
|
||||
bail!("realmfs image file '{}' has size which is not a multiple of block size", self.path.display());
|
||||
}
|
||||
let nblocks = (len / Self::BLOCK_SIZE) as usize;
|
||||
let nblocks = len / 4096;
|
||||
if nblocks < (self.metainfo().nblocks() + 1) {
|
||||
bail!("realmfs image file '{}' has shorter length than nblocks field of image header", self.path.display());
|
||||
}
|
||||
@@ -411,20 +406,18 @@ impl RealmFS {
|
||||
}
|
||||
|
||||
pub fn free_size_blocks(&self) -> Result<usize> {
|
||||
let sb = Superblock::load(self.path(), Self::BLOCK_SIZE)?;
|
||||
let sb = Superblock::load(self.path(), 4096)?;
|
||||
Ok(sb.free_block_count() as usize)
|
||||
}
|
||||
|
||||
pub fn allocated_size_blocks(&self) -> usize {
|
||||
self.metainfo().nblocks()
|
||||
pub fn allocated_size_blocks(&self) -> Result<usize> {
|
||||
let meta = self.path().metadata()
|
||||
.map_err(context!("failed to read metadata from realmfs image file {:?}", self.path()))?;
|
||||
Ok(meta.blocks() as usize / 8)
|
||||
}
|
||||
|
||||
/// Activate this RealmFS image if not yet activated.
|
||||
pub fn activate(&self) -> Result<()> {
|
||||
// Ensure that mountpoint matches header information of image
|
||||
if let Err(err) = self.check_stale_header(false) {
|
||||
warn!("error reloading stale image header: {}", err);
|
||||
}
|
||||
self.mountpoint().activate(self)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,17 @@ impl RealmFSSet {
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
/*
|
||||
let entries = fs::read_dir(RealmFS::BASE_PATH)
|
||||
.map_err(context!("error reading realmfs directory {}", RealmFS::BASE_PATH))?;
|
||||
for entry in entries {
|
||||
let entry = entry.map_err(context!("error reading directory entry"))?;
|
||||
if let Some(realmfs) = Self::entry_to_realmfs(&entry) {
|
||||
v.push(realmfs)
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
@@ -36,8 +47,6 @@ impl RealmFSSet {
|
||||
let name = filename.trim_end_matches("-realmfs.img");
|
||||
if RealmFS::is_valid_name(name) && RealmFS::named_image_exists(name) {
|
||||
return RealmFS::load_by_name(name).ok();
|
||||
} else {
|
||||
warn!("Rejecting realmfs '{}' as invalid name or invalid image", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,10 +108,7 @@ impl RealmFSUpdate {
|
||||
|
||||
LoopDevice::with_loop(self.target(), Some(BLOCK_SIZE), false, |loopdev| {
|
||||
self.resize_device(loopdev)
|
||||
})?;
|
||||
self.apply_update()?;
|
||||
self.cleanup();
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn mount_update_image(&mut self) -> Result<()> {
|
||||
@@ -217,29 +214,22 @@ impl RealmFSUpdate {
|
||||
self.set_target_len(nblocks)
|
||||
}
|
||||
|
||||
fn remount_read_only(&mut self) {
|
||||
if self.mountpath.exists() {
|
||||
if let Err(err) = cmd!("/usr/bin/mount", "-o remount,ro {}", self.mountpath.display()) {
|
||||
warn!("Failed to remount read-only directory {}: {}", self.mountpath.display(), err);
|
||||
} else {
|
||||
info!("Directory {} remounted as read-only", self.mountpath.display());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn shutdown_container(&mut self) {
|
||||
fn shutdown_container(&mut self) -> Result<()> {
|
||||
if let Some(update) = self.container.take() {
|
||||
if let Err(err) = update.stop_container() {
|
||||
warn!("Error shutting down update container: {}", err);
|
||||
}
|
||||
update.stop_container()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cleanup(&mut self) {
|
||||
// if a container was started, stop it
|
||||
self.shutdown_container();
|
||||
if let Err(err) = self.shutdown_container() {
|
||||
warn!("Error shutting down update container: {}", err);
|
||||
}
|
||||
|
||||
self.unmount_update_image();
|
||||
if self.mountpath.exists() {
|
||||
self.unmount_update_image();
|
||||
}
|
||||
|
||||
if self.target().exists() {
|
||||
if let Err(err) = fs::remove_file(self.target()) {
|
||||
@@ -314,10 +304,6 @@ impl RealmFSUpdate {
|
||||
}
|
||||
|
||||
pub fn commit_update(&mut self) -> Result<()> {
|
||||
// First shutdown container so writable mount can be removed in apply_update()
|
||||
self.shutdown_container();
|
||||
// Ensure no further writes
|
||||
self.remount_read_only();
|
||||
let result = self.apply_update();
|
||||
self.cleanup();
|
||||
result
|
||||
|
||||
@@ -28,7 +28,13 @@ impl Verity {
|
||||
let image = image.as_ref();
|
||||
let output = output.as_ref();
|
||||
// Don't use absolute path to veritysetup so that the build will correctly find the version from cryptsetup-native
|
||||
let output = cmd_with_output!("veritysetup", "format {} {}", image.display(), output.display())?;
|
||||
let output = cmd_with_output!(
|
||||
"veritysetup",
|
||||
"format --uuid=11298b29-ee7b-41b9-bf5e-2c4686d52c9e --salt=94dc0d754adb61f0221d2078713ffe1a0af0914d8f653fd61cd4f3362111720f {} {}",
|
||||
image.display(),
|
||||
output.display()
|
||||
)?;
|
||||
|
||||
Ok(VerityOutput::parse(&output))
|
||||
}
|
||||
|
||||
@@ -51,8 +57,10 @@ impl Verity {
|
||||
bail!("actual file size ({}) does not match expected size ({})", len, expected);
|
||||
}
|
||||
let vout = LoopDevice::with_loop(self.path(), Some(4096), true, |loopdev| {
|
||||
let output = cmd_with_output!(Self::VERITYSETUP, "--data-blocks={} --salt={} format {} {}",
|
||||
nblocks, salt, loopdev, verityfile.display())?;
|
||||
let output = cmd_with_output!(Self::VERITYSETUP, "--data-blocks={} --uuid=11298b29-ee7b-41b9-bf5e-2c4686d52c9e
|
||||
--salt=94dc0d754adb61f0221d2078713ffe1a0af0914d8f653fd61cd4f3362111720f format {} {}",
|
||||
nblocks, loopdev, verityfile.display())?;
|
||||
|
||||
Ok(VerityOutput::parse(&output))
|
||||
})?;
|
||||
let mut input = File::open(&verityfile)
|
||||
|
||||
@@ -7,12 +7,13 @@ use serde_repr::Serialize_repr;
|
||||
use zbus::blocking::fdo::DBusProxy;
|
||||
use zbus::blocking::Connection;
|
||||
use zbus::names::BusName;
|
||||
use zbus::zvariant::{ObjectPath, Type};
|
||||
use zbus::zvariant::Type;
|
||||
use zbus::{fdo, interface};
|
||||
use libcitadel::{PidLookupResult, Realm, RealmManager};
|
||||
use libcitadel::terminal::Base16Scheme;
|
||||
use libcitadel::{PidLookupResult, RealmManager};
|
||||
use crate::next::config::RealmConfigVars;
|
||||
use crate::next::state::RealmsManagerState;
|
||||
use crate::next::realm::RealmItemState;
|
||||
|
||||
use super::realmfs::RealmFSState;
|
||||
|
||||
pub fn failed<T>(message: String) -> fdo::Result<T> {
|
||||
Err(fdo::Error::Failed(message))
|
||||
@@ -42,10 +43,10 @@ impl From<PidLookupResult> for RealmFromCitadelPid {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RealmsManagerServer2 {
|
||||
state: RealmsManagerState,
|
||||
realms: RealmItemState,
|
||||
realmfs_state: RealmFSState,
|
||||
manager: Arc<RealmManager>,
|
||||
quit_event: Arc<Event>,
|
||||
}
|
||||
@@ -54,9 +55,11 @@ pub struct RealmsManagerServer2 {
|
||||
impl RealmsManagerServer2 {
|
||||
|
||||
fn new(connection: Connection, manager: Arc<RealmManager>, quit_event: Arc<Event>) -> Self {
|
||||
let state = RealmsManagerState::new(connection.clone());
|
||||
let realms = RealmItemState::new(connection.clone());
|
||||
let realmfs_state = RealmFSState::new(connection.clone());
|
||||
RealmsManagerServer2 {
|
||||
state,
|
||||
realms,
|
||||
realmfs_state,
|
||||
manager,
|
||||
quit_event,
|
||||
}
|
||||
@@ -70,12 +73,13 @@ impl RealmsManagerServer2 {
|
||||
let args = sig.args()?;
|
||||
match &args.name {
|
||||
BusName::Unique(unique_name) if args.new_owner().is_none() => {
|
||||
self.state.client_disconnected(unique_name);
|
||||
self.realmfs_state.client_disconnected(unique_name);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
}
|
||||
|
||||
fn listen_name_owner_changed(&self, connection: &Connection) {
|
||||
@@ -90,28 +94,12 @@ impl RealmsManagerServer2 {
|
||||
|
||||
pub fn load(connection: &Connection, manager: Arc<RealmManager>, quit_event: Arc<Event>) -> zbus::Result<Self> {
|
||||
let server = Self::new(connection.clone(), manager.clone(), quit_event);
|
||||
server.state.load(&manager)?;
|
||||
server.realms.load_realms(&manager)?;
|
||||
server.realmfs_state.load(&manager)?;
|
||||
server.realms.populate_realmfs(&server.realmfs_state)?;
|
||||
server.listen_name_owner_changed(connection);
|
||||
Ok(server)
|
||||
}
|
||||
|
||||
fn setup_new_realm(manager: &RealmManager, realm: Realm, realmfs_name: &str) {
|
||||
if let Some(realmfs) = manager.realmfs_by_name(&realmfs_name) {
|
||||
realm.with_mut_config(|c| c.realmfs = Some(realmfs.name().to_string()));
|
||||
} else {
|
||||
warn!("Cannot set RealmFS '{}' on realm because it does not exist", realmfs_name);
|
||||
}
|
||||
let config = realm.config();
|
||||
if let Err(err) = config.write() {
|
||||
warn!("error writing config file for new realm: {}", err);
|
||||
}
|
||||
let scheme_name = config.terminal_scheme().unwrap_or("default-dark");
|
||||
if let Some(scheme) = Base16Scheme::by_name(scheme_name) {
|
||||
if let Err(e) = scheme.apply_to_realm(&manager, &realm) {
|
||||
warn!("error writing scheme files: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +108,7 @@ impl RealmsManagerServer2 {
|
||||
|
||||
async fn get_current(&self) -> u32 {
|
||||
|
||||
self.state.get_current()
|
||||
self.realms.get_current()
|
||||
.map(|r| r.index())
|
||||
.unwrap_or(0)
|
||||
}
|
||||
@@ -132,13 +120,11 @@ impl RealmsManagerServer2 {
|
||||
}).await
|
||||
}
|
||||
|
||||
async fn create_realm(&self, name: &str, realmfs: &str) -> fdo::Result<()> {
|
||||
async fn create_realm(&self, name: &str) -> fdo::Result<()> {
|
||||
let manager = self.manager.clone();
|
||||
let name = name.to_string();
|
||||
let realmfs_name = realmfs.to_string();
|
||||
unblock(move || {
|
||||
let realm = manager.new_realm(&name).map_err(|err| fdo::Error::Failed(err.to_string()))?;
|
||||
RealmsManagerServer2::setup_new_realm(&manager, realm, &realmfs_name);
|
||||
let _ = manager.new_realm(&name).map_err(|err| fdo::Error::Failed(err.to_string()))?;
|
||||
Ok(())
|
||||
}).await
|
||||
}
|
||||
@@ -157,16 +143,8 @@ impl RealmsManagerServer2 {
|
||||
}).await
|
||||
}
|
||||
|
||||
async fn fork_realmfs(&self, name: &str, new_name: &str) -> fdo::Result<ObjectPath<'_>> {
|
||||
let state = self.state.clone();
|
||||
let name = name.to_string();
|
||||
let new_name = new_name.to_string();
|
||||
unblock(move || {
|
||||
state.fork_realmfs(&name, &new_name)
|
||||
}).await
|
||||
}
|
||||
|
||||
async fn get_global_config(&self) -> RealmConfigVars {
|
||||
RealmConfigVars::new_global()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,7 +4,5 @@ mod config;
|
||||
mod realm;
|
||||
mod realmfs;
|
||||
|
||||
mod state;
|
||||
|
||||
pub use manager::RealmsManagerServer2;
|
||||
pub const REALMS2_SERVER_OBJECT_PATH: &str = "/com/subgraph/Realms2";
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
use std::collections::HashMap;
|
||||
use crate::next::config::{RealmConfig, RealmConfigVars};
|
||||
use crate::next::REALMS2_SERVER_OBJECT_PATH;
|
||||
use blocking::unblock;
|
||||
use libcitadel::Realm;
|
||||
use std::convert::TryInto;
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::process::Command;
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU32, Ordering};
|
||||
use std::sync::Arc;
|
||||
use async_io::block_on;
|
||||
use zbus::{fdo, interface, Connection};
|
||||
use blocking::unblock;
|
||||
use zbus::zvariant::{OwnedObjectPath, Value};
|
||||
use zbus::{interface, fdo};
|
||||
use zbus::blocking::Connection;
|
||||
use zbus::names::{BusName, InterfaceName};
|
||||
use zbus::zvariant::Value;
|
||||
use crate::next::state::RealmFSNameToId;
|
||||
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 {
|
||||
@@ -21,13 +22,12 @@ pub struct RealmItem {
|
||||
config: RealmConfig,
|
||||
in_run_transition: Arc<AtomicBool>,
|
||||
realmfs_index: Arc<AtomicU32>,
|
||||
realmfs_name_to_id: RealmFSNameToId,
|
||||
last_timestamp: Arc<AtomicI64>,
|
||||
}
|
||||
|
||||
#[derive(Copy,Clone)]
|
||||
#[repr(u32)]
|
||||
pub enum RealmRunStatus {
|
||||
enum RealmRunStatus {
|
||||
Stopped = 0,
|
||||
Starting,
|
||||
Running,
|
||||
@@ -48,14 +48,13 @@ impl RealmRunStatus {
|
||||
}
|
||||
|
||||
impl RealmItem {
|
||||
pub(crate) fn new_from_realm(index: u32, realm: Realm, realmfs_name_to_id: RealmFSNameToId) -> 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 = realmfs_name_to_id.lookup(realm.config().realmfs());
|
||||
let realmfs_index = Arc::new(AtomicU32::new(realmfs_index));
|
||||
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_name_to_id, realmfs_index, last_timestamp }
|
||||
RealmItem { path, index, realm, config, in_run_transition, realmfs_index, last_timestamp }
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &str {
|
||||
@@ -70,26 +69,10 @@ impl RealmItem {
|
||||
self.in_run_transition.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn last_timestamp(&self) -> i64 {
|
||||
self.last_timestamp.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn set_last_timestamp(&self, ts: i64) {
|
||||
self.last_timestamp.store(ts, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn get_run_status(&self) -> RealmRunStatus {
|
||||
fn get_run_status(&self) -> RealmRunStatus {
|
||||
RealmRunStatus::for_realm(&self.realm, self.in_run_transition())
|
||||
}
|
||||
|
||||
pub fn realm(&self) -> &Realm {
|
||||
&self.realm
|
||||
}
|
||||
|
||||
pub fn set_in_run_transition(&self, in_run_transition: bool) {
|
||||
self.in_run_transition.store(in_run_transition, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
async fn do_start(&mut self) -> fdo::Result<()> {
|
||||
if !self.realm.is_active() {
|
||||
let realm = self.realm.clone();
|
||||
@@ -115,20 +98,6 @@ impl RealmItem {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn emit_property_changed(&self, connection: &Connection, propname: &str, value: Value<'_>) -> fdo::Result<()> {
|
||||
let iface_name = InterfaceName::from_str_unchecked("com.subgraph.realms.Realm");
|
||||
let changed = HashMap::from([(propname.to_string(), value)]);
|
||||
let inval: &[&str] = &[];
|
||||
block_on(
|
||||
connection.emit_signal(
|
||||
None::<BusName<'_>>,
|
||||
self.path(),
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"PropertiesChanged",
|
||||
&(iface_name, changed, inval)))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[interface(
|
||||
@@ -172,20 +141,9 @@ impl RealmItem {
|
||||
self.config.config_vars()
|
||||
}
|
||||
|
||||
async fn set_config(&mut self,
|
||||
#[zbus(connection)]
|
||||
connection: &Connection,
|
||||
vars: Vec<(String, String)>) -> fdo::Result<()> {
|
||||
async fn set_config(&mut self, vars: Vec<(String, String)>) -> fdo::Result<()> {
|
||||
for (var, val) in &vars {
|
||||
self.config.set_var(var, val)?;
|
||||
if var == "realmfs" {
|
||||
let index = self.realmfs_name_to_id.lookup(val);
|
||||
if index != self.realmfs_index.load(Ordering::Relaxed) {
|
||||
self.realmfs_index.store(index, Ordering::Relaxed);
|
||||
self.emit_property_changed(connection, "RealmFS", Value::U32(index))?;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
let config = self.config.clone();
|
||||
@@ -245,4 +203,193 @@ impl RealmItem {
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use crate::next::REALMS2_SERVER_OBJECT_PATH;
|
||||
use libcitadel::{RealmFS, RealmFSUpdate, ResizeSize};
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
use blocking::unblock;
|
||||
use zbus::blocking::Connection;
|
||||
use zbus::message::Header;
|
||||
use zbus::names::UniqueName;
|
||||
use zbus::zvariant::{ObjectPath, OwnedObjectPath};
|
||||
use zbus::{fdo, interface};
|
||||
use zbus::object_server::SignalEmitter;
|
||||
use libcitadel::{RealmFS, RealmManager,RealmFSUpdate};
|
||||
use crate::next::REALMS2_SERVER_OBJECT_PATH;
|
||||
|
||||
struct UpdateState(Option<(UniqueName<'static>, RealmFSUpdate)>);
|
||||
|
||||
@@ -40,8 +40,12 @@ impl UpdateState {
|
||||
}
|
||||
}
|
||||
|
||||
fn take_update(&mut self) -> Option<(RealmFSUpdate)> {
|
||||
self.0.take().map(|(_,update)| update)
|
||||
fn commit_update(&mut self) {
|
||||
if let Some((_name, mut update)) = self.0.take() {
|
||||
if let Err(err) = update.commit_update() {
|
||||
warn!("Error committing RealmFS update: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,12 +60,13 @@ pub struct RealmFSItem {
|
||||
|
||||
impl RealmFSItem {
|
||||
|
||||
fn update_state(&self) -> MutexGuard<'_, UpdateState> {
|
||||
fn update_state(&self) -> MutexGuard<UpdateState> {
|
||||
self.update_state.lock().unwrap()
|
||||
}
|
||||
|
||||
|
||||
pub fn client_disconnected(&mut self, name: &UniqueName) {
|
||||
fn client_disconnected(&mut self, name: &UniqueName) {
|
||||
//debug!("disconnect {} {}", self.object_path, name);
|
||||
let mut state = self.update_state();
|
||||
|
||||
if state.matches(name) {
|
||||
@@ -79,15 +84,11 @@ impl RealmFSItem {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn realmfs(&self) -> &RealmFS {
|
||||
&self.realmfs
|
||||
}
|
||||
|
||||
pub fn index(&self) -> u32 {
|
||||
self.index
|
||||
}
|
||||
|
||||
pub fn object_path(&self) -> ObjectPath<'_> {
|
||||
pub fn object_path(&self) -> ObjectPath {
|
||||
self.object_path.as_ref()
|
||||
}
|
||||
}
|
||||
@@ -101,88 +102,39 @@ impl RealmFSItem {
|
||||
&mut self,
|
||||
#[zbus(header)]
|
||||
hdr: Header<'_>,
|
||||
#[zbus(signal_emitter)]
|
||||
emitter: SignalEmitter<'_>,
|
||||
shared_directory: bool,
|
||||
) -> fdo::Result<String> {
|
||||
let mut update_container = String::new();
|
||||
{
|
||||
let mut state = self.update_state();
|
||||
|
||||
if state.is_active() {
|
||||
return Err(fdo::Error::Failed("An update is already in progress".to_owned()));
|
||||
}
|
||||
let mut state = self.update_state();
|
||||
|
||||
let sender = match hdr.sender() {
|
||||
Some(sender) => sender,
|
||||
None => return Err(fdo::Error::Failed("No sender in prepare_update()".into())),
|
||||
};
|
||||
|
||||
let mut update = self.realmfs.update()
|
||||
.map_err(|err| fdo::Error::Failed(err.to_string()))?;
|
||||
|
||||
update.prepare_update(shared_directory)
|
||||
.map_err(|err| fdo::Error::Failed(err.to_string()))?;
|
||||
|
||||
|
||||
update_container.push_str(update.name());
|
||||
|
||||
debug!("Update from {}, container: {}", sender, update_container);
|
||||
state.activate(sender.to_owned(), update);
|
||||
if state.is_active() {
|
||||
return Err(fdo::Error::Failed("An update is already in progress".to_owned()));
|
||||
}
|
||||
self.is_update_in_progress_changed(&emitter).await?;
|
||||
|
||||
let sender = match hdr.sender() {
|
||||
Some(sender) => sender,
|
||||
None => todo!(),
|
||||
};
|
||||
|
||||
let mut update = self.realmfs.update()
|
||||
.map_err(|err| fdo::Error::Failed(err.to_string()))?;
|
||||
|
||||
update.prepare_update(shared_directory)
|
||||
.map_err(|err| fdo::Error::Failed(err.to_string()))?;
|
||||
|
||||
let update_container = update.name().to_string();
|
||||
debug!("Update from {}, container: {}", sender, update_container);
|
||||
state.activate(sender.to_owned(), update);
|
||||
Ok(update_container)
|
||||
}
|
||||
|
||||
async fn commit_update(&mut self,
|
||||
#[zbus(signal_emitter)]
|
||||
emitter: SignalEmitter<'_>
|
||||
) -> fdo::Result<()> {
|
||||
|
||||
let mut update = match self.update_state().take_update() {
|
||||
None => {
|
||||
warn!("CommitUpdate called when no update in progress");
|
||||
return Ok(());
|
||||
},
|
||||
Some(update) => update,
|
||||
};
|
||||
|
||||
unblock(move || {
|
||||
if let Err(err) = update.commit_update() {
|
||||
warn!("Error committing RealmFS update: {}", err);
|
||||
}
|
||||
}).await;
|
||||
|
||||
self.is_update_in_progress_changed(&emitter).await?;
|
||||
self.free_space_changed(&emitter).await?;
|
||||
async fn commit_update(&mut self) -> fdo::Result<()> {
|
||||
self.update_state().commit_update();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn abandon_update(&mut self,
|
||||
#[zbus(signal_emitter)]
|
||||
emitter: SignalEmitter<'_>) -> fdo::Result<()> {
|
||||
async fn abandon_update(&mut self) -> fdo::Result<()> {
|
||||
self.update_state().cleanup_update();
|
||||
self.is_update_in_progress_changed(&emitter).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn resize_grow_by(&mut self,
|
||||
#[zbus(signal_emitter)]
|
||||
emitter: SignalEmitter<'_>,
|
||||
nblocks: u64) -> fdo::Result<()> {
|
||||
let nblocks = nblocks as usize;
|
||||
let current = self.realmfs.allocated_size_blocks();
|
||||
let new_size = current + nblocks;
|
||||
let realmfs = self.realmfs.clone();
|
||||
|
||||
unblock(move || {
|
||||
realmfs.resize_grow_to(ResizeSize::blocks(new_size))
|
||||
.map_err(|err| fdo::Error::Failed(err.to_string()))
|
||||
}).await?;
|
||||
|
||||
self.allocated_space_changed(&emitter).await?;
|
||||
self.free_space_changed(&emitter).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -200,12 +152,6 @@ impl RealmFSItem {
|
||||
fn in_use(&self) -> bool {
|
||||
self.realmfs.is_activated()
|
||||
}
|
||||
|
||||
#[zbus(property, name = "IsUpdateInProgress")]
|
||||
fn is_update_in_progress(&self) -> bool {
|
||||
self.update_state().is_active()
|
||||
}
|
||||
|
||||
#[zbus(property, name = "Mountpoint")]
|
||||
fn mountpoint(&self) -> String {
|
||||
self.realmfs.mountpoint().to_string()
|
||||
@@ -225,7 +171,81 @@ impl RealmFSItem {
|
||||
|
||||
#[zbus(property, name = "AllocatedSpace")]
|
||||
fn allocated_space(&self) -> fdo::Result<u64> {
|
||||
let blocks = self.realmfs.allocated_size_blocks();
|
||||
let blocks = self.realmfs.allocated_size_blocks()
|
||||
.map_err(|err| fdo::Error::Failed(err.to_string()))?;
|
||||
Ok(blocks as u64 * BLOCK_SIZE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RealmFSState(Arc<Mutex<Inner>>);
|
||||
|
||||
impl RealmFSState {
|
||||
pub fn new(connection: Connection) -> Self {
|
||||
RealmFSState(Arc::new(Mutex::new(Inner::new(connection))))
|
||||
}
|
||||
|
||||
fn inner(&self) -> MutexGuard<Inner> {
|
||||
self.0.lock().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn load(&self, manager: &RealmManager) -> zbus::Result<()> {
|
||||
self.inner().load(manager)
|
||||
}
|
||||
|
||||
pub fn realmfs_by_name(&self, name: &str) -> Option<RealmFSItem> {
|
||||
self.inner().realmfs_by_name(name)
|
||||
}
|
||||
|
||||
pub fn client_disconnected(&self, client_name: &UniqueName) {
|
||||
let mut lock = self.inner();
|
||||
for (_,v) in &mut lock.items {
|
||||
v.client_disconnected(client_name);
|
||||
}
|
||||
println!("client disconnected: {client_name}")
|
||||
}
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
connection: Connection,
|
||||
next_index: u32,
|
||||
items: HashMap<String, RealmFSItem>,
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
fn new(connection: Connection) -> Self {
|
||||
Inner {
|
||||
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(())
|
||||
}
|
||||
|
||||
fn realmfs_by_name(&self, name: &str) -> Option<RealmFSItem> {
|
||||
let res = self.items.get(name).cloned();
|
||||
if res.is_none() {
|
||||
warn!("Failed to find RealmFS with name '{}'", name);
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,261 +0,0 @@
|
||||
use crate::next::manager::failed;
|
||||
use crate::next::realm::RealmItem;
|
||||
use crate::next::realmfs::RealmFSItem;
|
||||
use libcitadel::{Realm, RealmEvent, RealmFS, RealmManager, Result};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
use zbus::blocking::Connection;
|
||||
use zbus::fdo;
|
||||
use zbus::names::UniqueName;
|
||||
use zbus::zvariant::{ObjectPath, Value};
|
||||
|
||||
/// Maintains a mapping of RealmFS names to the DBus object
|
||||
/// index values for the corresponding RealmFS objects.
|
||||
///
|
||||
/// This is used in the Realm objects to look up the correct
|
||||
/// realmfs object index for a realmfs name in the realm configuration.
|
||||
///
|
||||
#[derive(Clone)]
|
||||
pub struct RealmFSNameToId(Arc<Mutex<HashMap<String, u32>>>);
|
||||
impl RealmFSNameToId {
|
||||
pub fn new() -> Self {
|
||||
Self(Arc::new(Mutex::new(HashMap::new())))
|
||||
}
|
||||
|
||||
pub fn add(&mut self, name: &str, id: u32) {
|
||||
self.0.lock().unwrap().insert(name.to_string(), id);
|
||||
}
|
||||
|
||||
pub fn lookup(&self, name: &str) -> u32 {
|
||||
match self.0.lock().unwrap().get(name) {
|
||||
None => {
|
||||
warn!("Failed to map realmfs name '{}' to an object index", name);
|
||||
0
|
||||
}
|
||||
Some(&idx) => idx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RealmsManagerState(Arc<Mutex<StateInner>>);
|
||||
|
||||
impl RealmsManagerState {
|
||||
|
||||
pub fn new(connection: Connection) -> Self {
|
||||
Self(Arc::new(Mutex::new(StateInner::new(connection))))
|
||||
}
|
||||
|
||||
pub fn load(&self, manager: &RealmManager) -> zbus::Result<()> {
|
||||
self.inner().load(manager)?;
|
||||
|
||||
self.add_event_handler(manager)
|
||||
.map_err(|err| zbus::Error::Failure(err.to_string()))
|
||||
}
|
||||
|
||||
fn inner(&self) -> MutexGuard<'_, StateInner> {
|
||||
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!("Failed to handle event {}: {:?}", ev, err);
|
||||
}
|
||||
});
|
||||
manager.start_event_task()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_starting(&self, realm: &Realm) -> fdo::Result<()>{
|
||||
self.inner().realm_status_changed(realm, Some(true))
|
||||
}
|
||||
fn on_started(&self, realm: &Realm) -> fdo::Result<()>{
|
||||
self.inner().realm_status_changed(realm, Some(false))
|
||||
}
|
||||
fn on_stopping(&self, realm: &Realm) -> fdo::Result<()> {
|
||||
self.inner().realm_status_changed(realm, Some(true))
|
||||
}
|
||||
fn on_stopped(&self, realm: &Realm) -> fdo::Result<()>{
|
||||
self.inner().realm_status_changed(realm, Some(false))
|
||||
}
|
||||
fn on_new(&self, realm: &Realm) -> fdo::Result<()>{
|
||||
self.inner().add_realm(realm.clone())
|
||||
}
|
||||
fn on_removed(&self, realm: &Realm) -> fdo::Result<()>{
|
||||
self.inner().remove_realm(realm)
|
||||
}
|
||||
fn on_current(&self, realm: Option<&Realm>) -> fdo::Result<()> {
|
||||
self.inner().set_current_realm(realm)
|
||||
}
|
||||
|
||||
fn handle_event(&self, ev: &RealmEvent) -> fdo::Result<()> {
|
||||
match ev {
|
||||
RealmEvent::Started(realm) => self.on_started(realm)?,
|
||||
RealmEvent::Stopped(realm) => self.on_stopped(realm)?,
|
||||
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(realm) => self.on_starting(realm)?,
|
||||
RealmEvent::Stopping(realm) => self.on_stopping(realm)?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
pub fn get_current(&self) -> Option<RealmItem> {
|
||||
self.inner().current_realm.clone()
|
||||
}
|
||||
pub fn client_disconnected(&self, client_name: &UniqueName) {
|
||||
self.inner().client_disconnected(client_name);
|
||||
}
|
||||
pub fn fork_realmfs(&self, name: &str, new_name: &str) -> fdo::Result<ObjectPath<'static>> {
|
||||
self.inner().fork_realmfs(name, new_name)
|
||||
}
|
||||
}
|
||||
struct StateInner {
|
||||
connection: Connection,
|
||||
next_realm_index: u32,
|
||||
realm_items: HashMap<String, RealmItem>,
|
||||
current_realm: Option<RealmItem>,
|
||||
next_realmfs_index: u32,
|
||||
realmfs_items: HashMap<String, RealmFSItem>,
|
||||
realmfs_name_to_id: RealmFSNameToId,
|
||||
}
|
||||
|
||||
impl StateInner {
|
||||
fn new(connection: Connection) -> StateInner {
|
||||
StateInner {
|
||||
connection,
|
||||
next_realm_index: 1,
|
||||
realm_items: HashMap::new(),
|
||||
current_realm: None,
|
||||
next_realmfs_index: 1,
|
||||
realmfs_items: HashMap::new(),
|
||||
realmfs_name_to_id: RealmFSNameToId::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn load(&mut self, manager: &RealmManager) -> fdo::Result<()> {
|
||||
for realmfs in manager.realmfs_list() {
|
||||
self.add_realmfs(realmfs);
|
||||
}
|
||||
for realm in manager.realm_list() {
|
||||
self.add_realm(realm)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_realm(&mut self, realm: Realm) -> fdo::Result<()> {
|
||||
if self.realm_items.contains_key(realm.name()) {
|
||||
warn!("Attempted to add duplicate realm '{}'", realm.name());
|
||||
return Ok(())
|
||||
}
|
||||
info!("Adding realm-{} with obj index {}", realm.name(), self.next_realm_index);
|
||||
|
||||
let key = realm.name().to_string();
|
||||
let item = RealmItem::new_from_realm(self.next_realm_index, realm, self.realmfs_name_to_id.clone());
|
||||
self.connection.object_server().at(item.path(), item.clone())?;
|
||||
self.realm_items.insert(key, item);
|
||||
self.next_realm_index += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove_realm(&mut self, realm: &Realm) -> fdo::Result<()> {
|
||||
if let Some(item) = self.realm_items.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 add_realmfs(&mut self, realmfs: RealmFS) -> Option<RealmFSItem> {
|
||||
if !self.realmfs_items.contains_key(realmfs.name()) {
|
||||
info!("Adding realmfs-{} with object index {}", realmfs.name(), self.next_realmfs_index);
|
||||
let name = realmfs.name().to_string();
|
||||
let item = RealmFSItem::new_from_realmfs(self.next_realmfs_index, realmfs);
|
||||
if let Err(err) = self.connection.object_server().at(item.object_path(), item.clone()) {
|
||||
warn!("Failed to publish object at path {}: {} ", item.object_path(), err);
|
||||
} else {
|
||||
self.realmfs_items.insert(name.clone(), item);
|
||||
self.realmfs_name_to_id.add(&name, self.next_realmfs_index);
|
||||
self.next_realmfs_index += 1;
|
||||
}
|
||||
self.realmfs_by_name(&name)
|
||||
} else {
|
||||
warn!("Attempted to add duplicate realmfs '{}'", realmfs.name());
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn realmfs_by_name(&self, name: &str) -> Option<RealmFSItem> {
|
||||
let res = self.realmfs_items.get(name).cloned();
|
||||
if res.is_none() {
|
||||
warn!("Failed to find RealmFS with name '{}'", name);
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn fork_realmfs(&mut self, name: &str, new_name: &str) -> fdo::Result<ObjectPath<'static>> {
|
||||
let item = match self.realmfs_by_name(name) {
|
||||
None => return Err(fdo::Error::Failed(format!("Could not fork {}-realmfs, realmfs not found", name))),
|
||||
Some(item) => item,
|
||||
};
|
||||
|
||||
let new_realmfs = item.realmfs().fork(new_name).
|
||||
map_err(|err| fdo::Error::Failed(format!("Failed to fork realmfs-{} to '{}': {}", name, new_name, err)))?;
|
||||
|
||||
match self.add_realmfs(new_realmfs) {
|
||||
None => Err(fdo::Error::Failed(format!("Failed adding new realmfs while forking realmfs-{} to {}", name, new_name))),
|
||||
Some(new_item) => {
|
||||
let path = new_item.object_path().to_owned();
|
||||
Ok(path)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn realm_by_name(&self, name: &str) -> Option<&RealmItem> {
|
||||
let res = self.realm_items.get(name);
|
||||
|
||||
if res.is_none() {
|
||||
warn!("Failed to find realm with name '{}'", name);
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn client_disconnected(&mut self, client_name: &UniqueName) {
|
||||
for v in self.realmfs_items.values_mut() {
|
||||
v.client_disconnected(client_name);
|
||||
}
|
||||
}
|
||||
|
||||
fn realm_status_changed(&self, realm: &Realm, transition: Option<bool>) -> fdo::Result<()> {
|
||||
if let Some(realm) = self.realm_by_name(realm.name()) {
|
||||
if let Some(transition) = transition {
|
||||
realm.set_in_run_transition(transition);
|
||||
}
|
||||
realm.emit_property_changed(self.connection.inner(), "RunStatus", Value::U32(realm.get_run_status() as u32))?;
|
||||
let timestamp = realm.realm().timestamp();
|
||||
if timestamp != realm.last_timestamp() {
|
||||
realm.set_last_timestamp(timestamp);
|
||||
realm.emit_property_changed(self.connection.inner(), "Timestamp", Value::U64(timestamp as u64))?;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
failed(format!("Unknown realm {}", realm.name()))
|
||||
}
|
||||
}
|
||||
|
||||
fn set_current_realm(&mut self, realm: Option<&Realm>) -> fdo::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(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user