1
0
forked from brl/citadel-tools

Realmsd rewritten to use zbus

This commit is contained in:
Bruce Leidl 2021-10-04 06:02:35 -04:00
parent 2e6542e9f7
commit 13516fe024
6 changed files with 478 additions and 507 deletions

View File

@ -6,5 +6,7 @@ edition = "2018"
[dependencies]
libcitadel = { path = "../libcitadel" }
dbus = "0.8"
zbus = "^2.0.0-beta.5"
zvariant = "2.7.0"
serde = { version = "1.0", features = ["derive"] }

View File

@ -1,496 +0,0 @@
use std::fmt;
use std::sync::Arc;
use std::{result, thread};
use dbus::tree::{self, Factory, MTFn, MethodResult, Tree, MethodErr};
use dbus::blocking::LocalConnection;
use dbus::Message;
use libcitadel::{Result, RealmManager, Realm, RealmEvent, OverlayType, RealmFS, terminal};
use std::time::Duration;
type MethodInfo<'a> = tree::MethodInfo<'a, MTFn<TData>, TData>;
// XXX
const UPDATE_TOOL_PATH: &str = "/realms/Shared/citadel-realmfs";
const SUDO_PATH: &str = "/usr/bin/sudo";
const STATUS_REALM_RUNNING: u8 = 1;
const STATUS_REALM_CURRENT: u8 = 2;
const STATUS_REALM_SYSTEM_REALM: u8 = 4;
const OBJECT_PATH: &str = "/com/subgraph/realms";
const INTERFACE_NAME: &str = "com.subgraph.realms.Manager";
const BUS_NAME: &str = "com.subgraph.realms";
pub struct DbusServer {
connection: Arc<LocalConnection>,
manager: Arc<RealmManager>,
events: EventHandler,
}
impl DbusServer {
pub fn connect(manager: Arc<RealmManager>) -> Result<DbusServer> {
let connection = LocalConnection::new_system()
.map_err(|e| format_err!("Failed to connect to DBUS system bus: {}", e))?;
let connection = Arc::new(connection);
let events = EventHandler::new(connection.clone());
let server = DbusServer { events, connection, manager };
Ok(server)
}
fn build_tree(&self) -> Tree<MTFn<TData>, TData> {
let f = Factory::new_fn::<TData>();
let data = TreeData::new(self.manager.clone());
let interface = f.interface(INTERFACE_NAME, ())
// Methods
.add_m(f.method("SetCurrent", (), Self::do_set_current)
.in_arg(("name", "s")))
.add_m(f.method("GetCurrent", (), Self::do_get_current)
.out_arg(("name", "s")))
.add_m(f.method("List", (), Self::do_list)
.out_arg(("realms", "a(sssy)")))
.add_m(f.method("Start", (), Self::do_start)
.in_arg(("name", "s")))
.add_m(f.method("Stop", (), Self::do_stop)
.in_arg(("name", "s")))
.add_m(f.method("Restart", (), Self::do_restart)
.in_arg(("name", "s")))
.add_m(f.method("Terminal", (), Self::do_terminal)
.in_arg(("name", "s")))
.add_m(f.method("Run", (), Self::do_run)
.in_arg(("name", "s"))
.in_arg(("args", "as")))
.add_m(f.method("RealmFromCitadelPid", (), Self::do_pid_to_realm)
.in_arg(("pid", "u"))
.out_arg(("realm", "s")))
.add_m(f.method("RealmConfig", (), Self::do_get_realm_config)
.in_arg(("name", "s"))
.out_arg(("config", "a(ss)")))
.add_m(f.method("ListRealmFS", (), Self::do_list_realmfs)
.out_arg(("realmfs", "as")))
.add_m(f.method("UpdateRealmFS", (), Self::do_update)
.in_arg(("name", "s")))
// Signals
.add_s(f.signal("RealmStarted", ())
.arg(("realm", "s")))
.add_s(f.signal("RealmStopped", ())
.arg(("realm", "s")))
.add_s(f.signal("RealmNew", ())
.arg(("realm", "s")))
.add_s(f.signal("RealmRemoved", ())
.arg(("realm","s")))
.add_s(f.signal("RealmCurrent", ())
.arg(("realm", "s")))
.add_s(f.signal("ServiceStarted", ()));
let obpath = f.object_path(OBJECT_PATH, ())
.introspectable()
.add(interface);
f.tree(data).add(obpath)
}
fn do_list(m: &MethodInfo) -> MethodResult {
let list = m.tree.get_data().realm_list();
Ok(vec![m.msg.method_return().append1(list)])
}
fn do_set_current(m: &MethodInfo) -> MethodResult {
let manager = m.tree.get_data().manager();
let name = m.msg.read1()?;
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);
}
}
Ok(vec![m.msg.method_return()])
}
fn do_get_current(m: &MethodInfo) -> MethodResult {
let manager = m.tree.get_data().manager();
let ret = m.msg.method_return();
let msg = match manager.current_realm() {
Some(realm) => ret.append1(realm.name()),
None => ret.append1(""),
};
Ok(vec![msg])
}
fn do_start(m: &MethodInfo) -> MethodResult {
let name = m.msg.read1()?;
let data = m.tree.get_data().clone();
let realm = data.realm_by_name(name)?;
thread::spawn(move || {
if let Err(e) = data.manager().start_realm(&realm) {
warn!("failed to start realm {}: {}", realm.name(), e);
}
});
Ok(vec![m.msg.method_return()])
}
fn do_stop(m: &MethodInfo) -> MethodResult {
let name = m.msg.read1()?;
let data = m.tree.get_data().clone();
let realm = data.realm_by_name(name)?;
thread::spawn(move || {
if let Err(e) = data.manager().stop_realm(&realm) {
warn!("failed to stop realm {}: {}", realm.name(), e);
}
});
Ok(vec![m.msg.method_return()])
}
fn do_restart(m: &MethodInfo) -> MethodResult {
let name = m.msg.read1()?;
let data = m.tree.get_data().clone();
let realm = data.realm_by_name(name)?;
thread::spawn(move || {
if let Err(e) = data.manager().stop_realm(&realm) {
warn!("failed to stop realm {}: {}", realm.name(), e);
} else if let Err(e) = data.manager().start_realm(&realm) {
warn!("failed to restart realm {}: {}", realm.name(), e);
}
});
Ok(vec![m.msg.method_return()])
}
fn do_terminal(m: &MethodInfo) -> MethodResult {
let name = m.msg.read1()?;
let data = m.tree.get_data().clone();
let realm = data.realm_by_name(name)?;
thread::spawn(move || {
if !realm.is_active() {
if let Err(err) = data.manager().start_realm(&realm) {
warn!("failed to start realm {}: {}", realm.name(), err);
return;
}
}
if let Err(err) = data.manager().launch_terminal(&realm) {
warn!("error launching terminal for realm {}: {}", realm.name(), err);
}
});
Ok(vec![m.msg.method_return()])
}
fn do_update(m: &MethodInfo) -> MethodResult {
let name = m.msg.read1()?;
let data = m.tree.get_data().clone();
let realmfs = data.realmfs_by_name(name)?;
let command = format!("{} {} update {}", SUDO_PATH, UPDATE_TOOL_PATH, realmfs.name());
terminal::spawn_citadel_gnome_terminal(Some(command));
Ok(vec![m.msg.method_return()])
}
fn do_run(m: &MethodInfo) -> MethodResult {
let (name,args) = m.msg.read2::<&str, Vec<String>>()?;
let data = m.tree.get_data().clone();
let realm = data.realm_by_name(name)?;
thread::spawn(move || {
if !realm.is_active() {
if let Err(err) = data.manager().start_realm(&realm) {
warn!("failed to start realm {}: {}", realm.name(), err);
return;
}
}
if let Err(err) = data.manager().run_in_realm(&realm, &args, true) {
warn!("error running {:?} in realm {}: {}", args, realm.name(), err);
}
});
Ok(vec![m.msg.method_return()])
}
fn do_pid_to_realm(m: &MethodInfo) -> MethodResult {
let pid = m.msg.read1::<u32>()?;
let manager = m.tree.get_data().manager();
let ret = m.msg.method_return();
let msg = match manager.realm_by_pid(pid) {
Some(realm) => ret.append1(realm.name()),
None => ret.append1(""),
};
Ok(vec![msg])
}
fn do_get_realm_config(m: &MethodInfo) -> MethodResult {
let name = m.msg.read1()?;
let data = m.tree.get_data().clone();
let config = data.realm_config(name)?;
Ok(vec![m.msg.method_return().append1(config)])
}
fn do_list_realmfs(m: &MethodInfo) -> MethodResult {
let list = m.tree.get_data().realmfs_list();
Ok(vec![m.msg.method_return().append1(list)])
}
pub fn start(&self) -> Result<()> {
let tree = self.build_tree();
if let Err(err) = self.connection.request_name(BUS_NAME, false, true, false) {
bail!("failed to register DBUS name {}: {}", BUS_NAME, err);
}
tree.start_receive(self.connection.as_ref());
self.manager.add_event_handler({
let events = self.events.clone();
move |ev| events.handle_event(ev)
});
if let Err(e) = self.manager.start_event_task() {
warn!("error starting realm manager event task: {}", e);
}
self.send_service_started();
loop {
self.connection
.process(Duration::from_millis(1000))
.map_err(context!("Error handling dbus messages"))?;
}
}
fn send_service_started(&self) {
let signal = Self::create_signal("ServiceStarted");
if self.connection.channel().send(signal).is_err() {
warn!("failed to send ServiceStarted signal");
}
}
fn create_signal(name: &str) -> Message {
let path = dbus::Path::new(OBJECT_PATH).unwrap();
let iface = dbus::strings::Interface::new(INTERFACE_NAME).unwrap();
let member = dbus::strings::Member::new(name).unwrap();
Message::signal(&path, &iface, &member)
}
}
/// Wraps a connection instance and only expose the send() method.
/// Sending a message does not read or write any of the internal
/// Connection object state other than the native handle for the
/// connection. It should be safe to share this across threads as
/// internally libdbus uses a mutex to control concurrent access
/// to the dbus_connection_send() function.
#[derive(Clone)]
struct ConnectionSender(Arc<LocalConnection>);
unsafe impl Send for ConnectionSender {}
unsafe impl Sync for ConnectionSender {}
impl ConnectionSender {
fn new(connection: Arc<LocalConnection>) -> Self {
ConnectionSender(connection)
}
fn send(&self, msg: Message) -> Result<()> {
if let Err(()) = self.0.channel().send(msg) {
bail!("failed to send DBUS message");
}
Ok(())
}
}
#[derive(Clone)]
struct EventHandler {
sender: ConnectionSender,
}
impl EventHandler {
fn new(conn: Arc<LocalConnection>) -> EventHandler {
EventHandler {
sender: ConnectionSender::new(conn),
}
}
fn handle_event(&self, ev: &RealmEvent) {
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()),
}
}
fn on_started(&self, realm: &Realm) {
self.send_realm_signal("RealmStarted", Some(realm));
}
fn on_stopped(&self, realm: &Realm) {
self.send_realm_signal("RealmStopped", Some(realm));
}
fn on_new(&self, realm: &Realm) {
self.send_realm_signal("RealmNew", Some(realm));
}
fn on_removed(&self, realm: &Realm) {
self.send_realm_signal("RealmRemoved", Some(realm));
}
fn on_current(&self, realm: Option<&Realm>) {
self.send_realm_signal("RealmCurrent", realm);
}
fn create_realm_signal(name: &str) -> Message {
let path = dbus::Path::new(OBJECT_PATH).unwrap();
let iface = dbus::strings::Interface::new(INTERFACE_NAME).unwrap();
let member = dbus::strings::Member::new(name).unwrap();
Message::signal(&path, &iface, &member)
}
fn send_realm_signal(&self, sig_name: &str, realm: Option<&Realm>) {
let realm_name = match realm {
Some(r) => r.name(),
None => "",
};
let msg = Self::create_realm_signal(sig_name)
.append1(realm_name);
if let Err(e) = self.sender.send(msg) {
warn!("Could not send signal '{}': {}", sig_name, e);
}
}
}
#[derive(Clone)]
struct TreeData {
manager: Arc<RealmManager>,
}
impl TreeData {
fn new(manager: Arc<RealmManager>) -> TreeData {
TreeData {
manager,
}
}
fn manager(&self) -> &RealmManager {
&self.manager
}
fn realm_by_name(&self, name: &str) -> result::Result<Realm, MethodErr> {
if let Some(realm) = self.manager.realm_by_name(name) {
Ok(realm)
} else {
result::Result::Err(MethodErr::failed(&format!("Cannot find realm {}", name)))
}
}
fn realmfs_by_name(&self, name: &str) -> result::Result<RealmFS, MethodErr> {
if let Some(realmfs) = self.manager.realmfs_by_name(name) {
Ok(realmfs)
} else {
result::Result::Err(MethodErr::failed(&format!("Cannot find realmfs {}", name)))
}
}
fn append_config_flag(list: &mut Vec<(String,String)>, val: bool, name: &str) {
let valstr = if val { "true".to_string() } else { "false".to_string() };
list.push((name.to_string(), valstr));
}
fn realm_config(&self, name: &str) -> result::Result<Vec<(String,String)>, MethodErr> {
let realm = self.realm_by_name(name)?;
let config = realm.config();
let mut list = Vec::new();
Self::append_config_flag(&mut list, config.gpu(), "use-gpu");
Self::append_config_flag(&mut list, config.wayland(), "use-wayland");
Self::append_config_flag(&mut list, config.x11(), "use-x11");
Self::append_config_flag(&mut list, config.sound(), "use-sound");
Self::append_config_flag(&mut list, config.shared_dir(), "use-shared-dir");
Self::append_config_flag(&mut list, config.network(), "use-network");
Self::append_config_flag(&mut list, config.kvm(), "use-kvm");
Self::append_config_flag(&mut list, config.ephemeral_home(), "use-ephemeral-home");
let overlay = match config.overlay() {
OverlayType::None => "none",
OverlayType::TmpFS => "tmpfs",
OverlayType::Storage => "storage",
};
let scheme = match config.terminal_scheme() {
Some(name) => name.to_string(),
None => String::new(),
};
list.push(("realmfs".to_string(), config.realmfs().to_string()));
list.push(("overlay".to_string(), overlay.to_string()));
list.push(("terminal-scheme".to_string(), scheme));
Ok(list)
}
fn realm_element(realm: &Realm) -> (String, String, String, u8) {
let name = realm.name().to_owned();
let desc = Self::realm_description(realm);
let realmfs = realm.config().realmfs().to_owned();
let status = Self::realm_status(realm);
(name, desc, realmfs, status)
}
fn realm_list(&self) -> Vec<(String, String, String, u8)> {
self.manager.realm_list()
.iter()
.map(Self::realm_element)
.collect()
}
fn realm_description(realm: &Realm) -> String {
match realm.notes() {
Some(s) => s,
None => String::new(),
}
}
fn realm_status(realm: &Realm) -> u8 {
let mut status = 0;
if realm.is_active() {
status |= STATUS_REALM_RUNNING;
}
if realm.is_current() {
status |= STATUS_REALM_CURRENT;
}
if realm.is_system() {
status |= STATUS_REALM_SYSTEM_REALM;
}
status
}
fn realmfs_list(&self) -> Vec<String> {
self.manager.realmfs_list()
.into_iter()
.map(|fs| fs.name().to_owned())
.collect()
}
}
impl fmt::Debug for TreeData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<TreeData>")
}
}
#[derive(Copy, Clone, Default, Debug)]
struct TData;
impl tree::DataType for TData {
type Tree = TreeData;
type ObjectPath = ();
type Property = ();
type Interface = ();
type Method = ();
type Signal = ();
}

View File

69
realmsd/src/events.rs Normal file
View File

@ -0,0 +1,69 @@
use zbus::{Connection, ObjectServer};
use crate::realms_manager::{RealmsManagerServer, REALMS_SERVER_OBJECT_PATH, realm_status};
use libcitadel::{RealmEvent, Realm};
pub struct EventHandler {
connection: Connection,
realms_server: RealmsManagerServer,
}
impl EventHandler {
pub fn new(connection: Connection, realms_server: RealmsManagerServer) -> Self {
EventHandler { connection, realms_server }
}
pub fn handle_event(&self, ev: &RealmEvent) {
if let Err(err) = self.dispatch_event(ev) {
warn!("Error emitting signal for realm event {}: {}", ev, err);
}
}
fn dispatch_event(&self, ev: &RealmEvent) -> zbus::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()),
}
}
fn with_server<F>(&self, func: F) -> zbus::Result<()>
where
F: Fn(&RealmsManagerServer) -> zbus::Result<()>,
{
let mut object_server = ObjectServer::new(&self.connection);
object_server.at(REALMS_SERVER_OBJECT_PATH, self.realms_server.clone())?;
object_server.with(REALMS_SERVER_OBJECT_PATH, |iface: &RealmsManagerServer| func(iface))
}
fn on_started(&self, realm: &Realm) -> zbus::Result<()> {
let namespace = realm.pid_namespace().unwrap_or(String::new());
let status = realm_status(realm);
self.with_server(|server| server.realm_started(realm.name(), namespace.as_str(), status))
}
fn on_stopped(&self, realm: &Realm) -> zbus::Result<()> {
let status = realm_status(realm);
self.with_server(|server| server.realm_stopped(realm.name(), status))
}
fn on_new(&self, realm: &Realm) -> zbus::Result<()> {
let status = realm_status(realm);
let description = realm.notes().unwrap_or(String::new());
self.with_server(|server| server.realm_new(realm.name(), &description, status))
}
fn on_removed(&self, realm: &Realm) -> zbus::Result<()> {
self.with_server(|server| server.realm_removed(realm.name()))
}
fn on_current(&self, realm: Option<&Realm>) -> zbus::Result<()> {
self.with_server(|server| {
match realm {
Some(realm) => server.realm_current(realm.name(), realm_status(realm)),
None => server.realm_current("", 0),
}
})
}
}

View File

@ -1,19 +1,39 @@
#[macro_use] extern crate libcitadel;
use libcitadel::{RealmManager, Result, Logger, LogLevel};
mod dbus;
mod devices;
use zbus::{Connection, fdo};
use libcitadel::{Logger, LogLevel, Result};
use crate::realms_manager::RealmsManagerServer;
mod realms_manager;
mod events;
fn main() {
if let Err(e) = run_dbus_server() {
if let Err(e) = run_realm_manager() {
warn!("Error: {}", e);
}
}
fn run_dbus_server() -> Result<()> {
Logger::set_log_level(LogLevel::Verbose);
let manager = RealmManager::load()?;
let server = dbus::DbusServer::connect(manager)?;
server.start()?;
Ok(())
fn create_system_connection() -> zbus::Result<Connection> {
let connection = zbus::Connection::new_system()?;
fdo::DBusProxy::new(&connection)?.request_name("com.subgraph.realms", fdo::RequestNameFlags::AllowReplacement.into())?;
Ok(connection)
}
fn run_realm_manager() -> Result<()> {
Logger::set_log_level(LogLevel::Verbose);
let connection = create_system_connection()
.map_err(context!("ZBus Connection error"))?;
let mut object_server = RealmsManagerServer::register(&connection)?;
loop {
if let Err(err) = object_server.try_handle_next() {
warn!("Error handling DBus message: {}", err);
}
}
}

View File

@ -0,0 +1,376 @@
use libcitadel::{RealmManager, Realm, OverlayType, Result};
use std::sync::Arc;
use zbus::{dbus_interface, ObjectServer,Connection};
use zvariant::derive::Type;
use std::thread;
use std::collections::HashMap;
use serde::{Serialize,Deserialize};
use crate::events::EventHandler;
use libcitadel::terminal::Base16Scheme;
pub const REALMS_SERVER_OBJECT_PATH: &str = "/com/subgraph/realms";
#[derive(Clone)]
pub struct RealmsManagerServer {
manager: Arc<RealmManager>,
}
const BOOL_CONFIG_VARS: &[&str] = &[
"use-gpu", "use-wayland", "use-x11", "use-sound",
"use-shared-dir", "use-network", "use-kvm", "use-ephemeral-home"
];
fn is_bool_config_variable(variable: &str) -> bool {
BOOL_CONFIG_VARS.iter().any(|&s| s == variable)
}
fn save_config(realm: &Realm) {
let path = realm.base_path_file("config");
if let Err(e) = realm.config().write_to(&path) {
warn!("Error writing config file {}: {}", path.display(), e);
}
}
fn configure_realm_boolean_config(realm: &Realm, variable: &str, value: &str) {
let val = match value {
"true" => true,
"false" => false,
_ => {
warn!("Not a valid boolean value '{}'", value);
return;
},
};
realm.with_mut_config(|c| {
match variable {
"use-gpu" if c.gpu() != val => c.use_gpu = Some(val),
"use-wayland" if c.wayland() != val => c.use_wayland = Some(val),
"use-x11" if c.x11() != val => c.use_x11 = Some(val),
"use-sound" if c.sound() != val => c.use_sound = Some(val),
"use-shared-dir" if c.shared_dir() != val => c.use_shared_dir = Some(val),
"use-network" if c.network() != val => c.use_network = Some(val),
"use-kvm" if c.kvm() != val => c.use_kvm = Some(val),
"use-ephemeral-home" if c.ephemeral_home() != val => c.use_ephemeral_home = Some(val),
_ => {},
}
});
save_config(realm);
}
fn configure_realm(realm: &Realm, variable: &str, value: &str) {
if is_bool_config_variable(variable) {
configure_realm_boolean_config(realm, variable, value);
} else if variable == "overlay" {
if value == "tmpfs" || value == "storage" || value == "none" {
realm.with_mut_config(|c| {
c.overlay = Some(value.to_string());
});
save_config(realm);
} else {
warn!("Invalid storage type '{}'", value);
return;
}
} else if variable == "terminal-scheme" {
if Base16Scheme::by_name(value).is_none() {
warn!("No terminal color scheme with name '{}' available", value);
}
realm.with_mut_config(|c| {
c.terminal_scheme = Some(value.to_string());
});
save_config(realm);
} else if variable == "realmfs" {
warn!("Changing realmfs config variable not implemented");
} else {
warn!("Unknown config variable '{}'", variable);
}
}
impl RealmsManagerServer {
fn register_events(&self, connection: &Connection) -> Result<()> {
let events = EventHandler::new(connection.clone(), self.clone());
self.manager.add_event_handler(move |ev| events.handle_event(ev));
self.manager.start_event_task()
}
pub fn 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")]
impl RealmsManagerServer {
fn set_current(&self, name: &str) {
if let Some(realm) = self.manager.realm_by_name(name) {
if let Err(err) = self.manager.set_current_realm(&realm) {
warn!("set_current_realm({}) failed: {}", name, err);
}
}
}
fn get_current(&self) -> String {
match self.manager.current_realm() {
Some(realm) => realm.name().to_string(),
None => String::new(),
}
}
fn list(&self) -> Vec<RealmItem> {
let mut realms = Vec::new();
for r in self.manager.realm_list() {
realms.push(RealmItem::new_from_realm(&r));
}
realms
}
fn start(&self, name: &str) {
let realm = match self.manager.realm_by_name(name) {
Some(r) => r,
None => return,
};
let manager = self.manager.clone();
thread::spawn(move || {
if let Err(e) = manager.start_realm(&realm) {
warn!("failed to start realm {}: {}", realm.name(), e);
}
});
}
fn stop(&self, name: &str) {
let realm = match self.manager.realm_by_name(name) {
Some(r) => r,
None => return,
};
let manager = self.manager.clone();
thread::spawn(move || {
if let Err(e) = manager.stop_realm(&realm) {
warn!("failed to stop realm {}: {}", realm.name(), e);
}
});
}
fn restart(&self, name: &str) {
let realm = match self.manager.realm_by_name(name) {
Some(r) => r,
None => return,
};
let manager = self.manager.clone();
thread::spawn(move || {
if let Err(e) = manager.stop_realm(&realm) {
warn!("failed to stop realm {}: {}", realm.name(), e);
} else if let Err(e) = manager.start_realm(&realm) {
warn!("failed to restart realm {}: {}", realm.name(), e);
}
});
}
fn terminal(&self, name: &str) {
let realm = match self.manager.realm_by_name(name) {
Some(r) => r,
None => return,
};
let manager = self.manager.clone();
thread::spawn(move || {
if !realm.is_active() {
if let Err(err) = manager.start_realm(&realm) {
warn!("failed to start realm {}: {}", realm.name(), err);
return;
}
}
if let Err(err) = manager.launch_terminal(&realm) {
warn!("error launching terminal for realm {}: {}", realm.name(), err);
}
});
}
fn run(&self, name: &str, args: Vec<String>) {
let realm = match self.manager.realm_by_name(name) {
Some(r) => r,
None => return,
};
let manager = self.manager.clone();
thread::spawn(move || {
if !realm.is_active() {
if let Err(err) = manager.start_realm(&realm) {
warn!("failed to start realm {}: {}", realm.name(), err);
return;
}
}
if let Err(err) = manager.run_in_realm(&realm, &args, true) {
warn!("error running {:?} in realm {}: {}", args, realm.name(), err);
}
});
}
fn realm_from_citadel_pid(&self, pid: u32) -> String {
match self.manager.realm_by_pid(pid) {
Some(r) => r.name().to_string(),
None => String::new(),
}
}
fn realm_config(&self, name: &str) -> RealmConfig {
let realm = match self.manager.realm_by_name(name) {
Some(r) => r,
None => return RealmConfig::new(),
};
RealmConfig::new_from_realm(&realm)
}
fn realm_set_config(&self, name: &str, vars: Vec<(String,String)>) {
let realm = match self.manager.realm_by_name(name) {
Some(r) => r,
None => {
warn!("No realm named '{}' found in RealmSetConfig", name);
return;
},
};
for var in &vars {
configure_realm(&realm, &var.0, &var.1);
}
}
fn realm_exists(&self, name: &str) -> bool {
Realm::is_valid_name(name) && self.manager.realm_by_name(name).is_some()
}
fn create_realm(&self, name: &str) -> bool {
if let Err(err) = self.manager.new_realm(name) {
warn!("Error creating realm ({}): {}", name, err);
false
} else {
true
}
}
fn list_realm_f_s(&self) -> Vec<String> {
self.manager.realmfs_list()
.into_iter()
.map(|fs| fs.name().to_owned())
.collect()
}
fn update_realm_f_s(&self, _name: &str) {
}
#[dbus_interface(signal)]
pub fn realm_started(&self, realm: &str, namespace: &str, status: u8) -> zbus::Result<()> { Ok(()) }
#[dbus_interface(signal)]
pub fn realm_stopped(&self, realm: &str, status: u8) -> zbus::Result<()> { Ok(()) }
#[dbus_interface(signal)]
pub fn realm_new(&self, realm: &str, description: &str, status: u8) -> zbus::Result<()> { Ok(()) }
#[dbus_interface(signal)]
pub fn realm_removed(&self, realm: &str) -> zbus::Result<()> { Ok(()) }
#[dbus_interface(signal)]
pub fn realm_current(&self, realm: &str, status: u8) -> zbus::Result<()> { Ok(()) }
#[dbus_interface(signal)]
pub fn service_started(&self) -> zbus::Result<()> { Ok(()) }
}
const STATUS_REALM_RUNNING: u8 = 1;
const STATUS_REALM_CURRENT: u8 = 2;
const STATUS_REALM_SYSTEM_REALM: u8 = 4;
pub fn realm_status(realm: &Realm) -> u8 {
let mut status = 0;
if realm.is_active() {
status |= STATUS_REALM_RUNNING;
}
if realm.is_current() {
status |= STATUS_REALM_CURRENT;
}
if realm.is_system() {
status |= STATUS_REALM_SYSTEM_REALM;
}
status
}
#[derive(Deserialize,Serialize,Type)]
struct RealmItem {
name: String,
description: String,
realmfs: String,
namespace: String,
status: u8,
}
impl RealmItem {
fn new_from_realm(realm: &Realm) -> Self {
let name = realm.name().to_string();
let description = realm.notes().unwrap_or(String::new());
let realmfs = realm.config().realmfs().to_string();
let namespace = realm.pid_namespace().unwrap_or(String::new());
let status = realm_status(realm);
RealmItem { name, description, realmfs, namespace, status }
}
}
#[derive(Deserialize,Serialize,Type)]
struct RealmConfig {
items: HashMap<String,String>,
}
impl RealmConfig {
fn new() -> Self {
RealmConfig { items: HashMap::new() }
}
fn new_from_realm(realm: &Realm) -> Self {
let mut this = RealmConfig { items: HashMap::new() };
let config = realm.config();
this.add_bool("use-gpu", config.gpu());
this.add_bool("use-wayland", config.wayland());
this.add_bool("use-x11", config.x11());
this.add_bool("use-sound", config.sound());
this.add_bool("use-shared-dir", config.shared_dir());
this.add_bool("use-network", config.network());
this.add_bool("use-kvm", config.kvm());
this.add_bool("use-ephemeral-home", config.ephemeral_home());
let overlay = match config.overlay() {
OverlayType::None => "none",
OverlayType::TmpFS => "tmpfs",
OverlayType::Storage => "storage",
};
this.add("overlay", overlay);
let scheme = match config.terminal_scheme() {
Some(name) => name.to_string(),
None => String::new(),
};
this.add("terminal-scheme", scheme);
this.add("realmfs", config.realmfs());
this
}
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());
}
}