hypha: setup image as disk

This commit is contained in:
Alex Zenla 2024-01-18 06:15:42 -08:00
parent 80311db549
commit 649a0c303d
No known key found for this signature in database
GPG Key ID: 067B238899B51269
6 changed files with 324 additions and 87 deletions

View File

@ -1,6 +1,7 @@
use clap::Parser; use clap::Parser;
use hypha::ctl::Controller; use hypha::ctl::Controller;
use hypha::error::{HyphaError, Result}; use hypha::error::{HyphaError, Result};
use std::path::PathBuf;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(version, about)] #[command(version, about)]
@ -20,23 +21,28 @@ struct ControllerArgs {
#[arg(short, long, default_value_t = 512)] #[arg(short, long, default_value_t = 512)]
mem: u64, mem: u64,
#[arg(short = 'C', long, default_value = "auto")] #[arg(short, long, default_value = "auto")]
cache: String, store: String,
} }
fn main() -> Result<()> { fn main() -> Result<()> {
env_logger::init(); env_logger::init();
let args = ControllerArgs::parse(); let args = ControllerArgs::parse();
let cache_path = if args.cache == "auto" { let store_path = if args.store == "auto" {
default_cache_path() default_store_path()
.ok_or_else(|| HyphaError::new("unable to determine default cache path")) .ok_or_else(|| HyphaError::new("unable to determine default store path"))
} else { } else {
Ok(args.cache) Ok(PathBuf::from(args.store))
}?; }?;
let store_path = store_path
.to_str()
.map(|x| x.to_string())
.ok_or_else(|| HyphaError::new("unable to convert store path to string"))?;
let mut controller = Controller::new( let mut controller = Controller::new(
cache_path, store_path,
args.kernel, args.kernel,
args.initrd, args.initrd,
args.image, args.image,
@ -48,9 +54,13 @@ fn main() -> Result<()> {
Ok(()) Ok(())
} }
fn default_cache_path() -> Option<String> { fn default_store_path() -> Option<PathBuf> {
let user_dirs = directories::UserDirs::new()?; let user_dirs = directories::UserDirs::new()?;
let mut path = user_dirs.home_dir().to_path_buf(); let mut path = user_dirs.home_dir().to_path_buf();
path.push(".hypha/cache"); if path == PathBuf::from("/root") {
Some(path.to_str()?.to_string()) path.push("/var/lib/hypha")
} else {
path.push(".hypha");
}
Some(path)
} }

View File

@ -1,11 +1,11 @@
use crate::error::Result; use crate::error::{HyphaError, Result};
use crate::image::cache::ImageCache; use crate::image::cache::ImageCache;
use crate::image::{ImageCompiler, ImageInfo}; use crate::image::{ImageCompiler, ImageInfo};
use ocipkg::ImageName; use ocipkg::ImageName;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use uuid::Uuid; use uuid::Uuid;
use xenclient::{DomainConfig, XenClient}; use xenclient::{DomainConfig, DomainDisk, XenClient};
pub struct Controller { pub struct Controller {
image_cache: ImageCache, image_cache: ImageCache,
@ -19,17 +19,18 @@ pub struct Controller {
impl Controller { impl Controller {
pub fn new( pub fn new(
cache_path: String, store_path: String,
kernel_path: String, kernel_path: String,
initrd_path: String, initrd_path: String,
image: String, image: String,
vcpus: u32, vcpus: u32,
mem: u64, mem: u64,
) -> Result<Controller> { ) -> Result<Controller> {
fs::create_dir_all(&cache_path)?; let mut image_cache_path = PathBuf::from(store_path);
image_cache_path.push("cache");
fs::create_dir_all(&image_cache_path)?;
let client = XenClient::open()?; let client = XenClient::open()?;
let mut image_cache_path = PathBuf::from(cache_path);
image_cache_path.push("image"); image_cache_path.push("image");
fs::create_dir_all(&image_cache_path)?; fs::create_dir_all(&image_cache_path)?;
let image_cache = ImageCache::new(&image_cache_path)?; let image_cache = ImageCache::new(&image_cache_path)?;
@ -53,14 +54,24 @@ impl Controller {
pub fn launch(&mut self) -> Result<u32> { pub fn launch(&mut self) -> Result<u32> {
let uuid = Uuid::new_v4(); let uuid = Uuid::new_v4();
let name = format!("hypha-{uuid}"); let name = format!("hypha-{uuid}");
let _image_info = self.compile()?; let image_info = self.compile()?;
let squashfs_path = image_info
.squashfs
.to_str()
.ok_or_else(|| HyphaError::new("failed to convert squashfs path to string"))?;
let config = DomainConfig { let config = DomainConfig {
backend_domid: 0,
name: &name, name: &name,
max_vcpus: self.vcpus, max_vcpus: self.vcpus,
mem_mb: self.mem, mem_mb: self.mem,
kernel_path: self.kernel_path.as_str(), kernel_path: self.kernel_path.as_str(),
initrd_path: self.initrd_path.as_str(), initrd_path: self.initrd_path.as_str(),
cmdline: "debug elevator=noop", cmdline: "elevator=noop",
disks: vec![DomainDisk {
vdev: "xvda",
pdev: squashfs_path,
writable: false,
}],
}; };
Ok(self.client.create(&config)?) Ok(self.client.create(&config)?)
} }

View File

@ -13,12 +13,14 @@ fn main() -> Result<(), XenClientError> {
let initrd_path = args.get(2).expect("argument not specified"); let initrd_path = args.get(2).expect("argument not specified");
let mut client = XenClient::open()?; let mut client = XenClient::open()?;
let config = DomainConfig { let config = DomainConfig {
backend_domid: 0,
name: "xenclient-test", name: "xenclient-test",
max_vcpus: 1, max_vcpus: 1,
mem_mb: 512, mem_mb: 512,
kernel_path: kernel_image_path.as_str(), kernel_path: kernel_image_path.as_str(),
initrd_path: initrd_path.as_str(), initrd_path: initrd_path.as_str(),
cmdline: "debug elevator=noop", cmdline: "debug elevator=noop",
disks: vec![],
}; };
let domid = client.create(&config)?; let domid = client.create(&config)?;
println!("created domain {}", domid); println!("created domain {}", domid);

View File

@ -16,7 +16,9 @@ use xencall::sys::CreateDomain;
use xencall::{XenCall, XenCallError}; use xencall::{XenCall, XenCallError};
use xenevtchn::EventChannelError; use xenevtchn::EventChannelError;
use xenstore::bus::XsdBusError; use xenstore::bus::XsdBusError;
use xenstore::client::{XsPermissions, XsdClient, XsdInterface}; use xenstore::client::{
XsPermission, XsdClient, XsdInterface, XS_PERM_NONE, XS_PERM_READ, XS_PERM_READ_WRITE,
};
pub struct XenClient { pub struct XenClient {
store: XsdClient, store: XsdClient,
@ -78,13 +80,21 @@ impl From<EventChannelError> for XenClientError {
} }
} }
pub struct DomainDisk<'a> {
pub vdev: &'a str,
pub pdev: &'a str,
pub writable: bool,
}
pub struct DomainConfig<'a> { pub struct DomainConfig<'a> {
pub backend_domid: u32,
pub name: &'a str, pub name: &'a str,
pub max_vcpus: u32, pub max_vcpus: u32,
pub mem_mb: u64, pub mem_mb: u64,
pub kernel_path: &'a str, pub kernel_path: &'a str,
pub initrd_path: &'a str, pub initrd_path: &'a str,
pub cmdline: &'a str, pub cmdline: &'a str,
pub disks: Vec<DomainDisk<'a>>,
} }
impl XenClient { impl XenClient {
@ -100,55 +110,71 @@ impl XenClient {
..Default::default() ..Default::default()
}; };
let domid = self.call.create_domain(domain)?; let domid = self.call.create_domain(domain)?;
let backend_dom_path = self.store.get_domain_path(0)?;
let dom_path = self.store.get_domain_path(domid)?; let dom_path = self.store.get_domain_path(domid)?;
let uuid_string = Uuid::from_bytes(domain.handle).to_string(); let uuid_string = Uuid::from_bytes(domain.handle).to_string();
let vm_path = format!("/vm/{}", uuid_string); let vm_path = format!("/vm/{}", uuid_string);
let libxl_path = format!("/libxl/{}", domid);
let ro_perm = XsPermissions { id: 0, perms: 0 }; let ro_perm = &[
let rw_perm = XsPermissions { id: 0, perms: 0 }; XsPermission {
let no_perm = XsPermissions { id: 0, perms: 0 }; id: 0,
perms: XS_PERM_NONE,
},
XsPermission {
id: domid,
perms: XS_PERM_READ,
},
];
let rw_perm = &[XsPermission {
id: domid,
perms: XS_PERM_READ_WRITE,
}];
let no_perm = &[XsPermission {
id: 0,
perms: XS_PERM_NONE,
}];
{ {
let mut tx = self.store.transaction()?; let mut tx = self.store.transaction()?;
tx.rm(dom_path.as_str())?; tx.rm(dom_path.as_str())?;
tx.mknod(dom_path.as_str(), &ro_perm)?; tx.mknod(dom_path.as_str(), ro_perm)?;
tx.rm(vm_path.as_str())?; tx.rm(vm_path.as_str())?;
tx.mknod(vm_path.as_str(), &ro_perm)?; tx.mknod(vm_path.as_str(), ro_perm)?;
tx.rm(libxl_path.as_str())?; tx.mknod(vm_path.as_str(), no_perm)?;
tx.mknod(vm_path.as_str(), &no_perm)?; tx.mknod(format!("{}/device", vm_path).as_str(), no_perm)?;
tx.mknod(format!("{}/device", vm_path).as_str(), &no_perm)?;
tx.write_string(format!("{}/vm", dom_path).as_str(), &vm_path)?; tx.write_string(format!("{}/vm", dom_path).as_str(), &vm_path)?;
tx.mknod(format!("{}/cpu", dom_path).as_str(), &ro_perm)?; tx.mknod(format!("{}/cpu", dom_path).as_str(), ro_perm)?;
tx.mknod(format!("{}/memory", dom_path).as_str(), &ro_perm)?; tx.mknod(format!("{}/memory", dom_path).as_str(), ro_perm)?;
tx.mknod(format!("{}/control", dom_path).as_str(), &ro_perm)?; tx.mknod(format!("{}/control", dom_path).as_str(), ro_perm)?;
tx.mknod(format!("{}/control/shutdown", dom_path).as_str(), &rw_perm)?; tx.mknod(format!("{}/control/shutdown", dom_path).as_str(), rw_perm)?;
tx.mknod( tx.mknod(
format!("{}/control/feature-poweroff", dom_path).as_str(), format!("{}/control/feature-poweroff", dom_path).as_str(),
&rw_perm, rw_perm,
)?; )?;
tx.mknod( tx.mknod(
format!("{}/control/feature-reboot", dom_path).as_str(), format!("{}/control/feature-reboot", dom_path).as_str(),
&rw_perm, rw_perm,
)?; )?;
tx.mknod( tx.mknod(
format!("{}/control/feature-suspend", dom_path).as_str(), format!("{}/control/feature-suspend", dom_path).as_str(),
&rw_perm, rw_perm,
)?; )?;
tx.mknod(format!("{}/control/sysrq", dom_path).as_str(), &rw_perm)?; tx.mknod(format!("{}/control/sysrq", dom_path).as_str(), rw_perm)?;
tx.mknod(format!("{}/data", dom_path).as_str(), &rw_perm)?; tx.mknod(format!("{}/data", dom_path).as_str(), rw_perm)?;
tx.mknod(format!("{}/drivers", dom_path).as_str(), &rw_perm)?; tx.mknod(format!("{}/drivers", dom_path).as_str(), rw_perm)?;
tx.mknod(format!("{}/feature", dom_path).as_str(), &rw_perm)?; tx.mknod(format!("{}/feature", dom_path).as_str(), rw_perm)?;
tx.mknod(format!("{}/attr", dom_path).as_str(), &rw_perm)?; tx.mknod(format!("{}/attr", dom_path).as_str(), rw_perm)?;
tx.mknod(format!("{}/error", dom_path).as_str(), &rw_perm)?; tx.mknod(format!("{}/error", dom_path).as_str(), rw_perm)?;
tx.write_string( tx.write_string(
format!("{}/uuid", vm_path).as_str(), format!("{}/uuid", vm_path).as_str(),
@ -156,7 +182,6 @@ impl XenClient {
)?; )?;
tx.write_string(format!("{}/name", dom_path).as_str(), config.name)?; tx.write_string(format!("{}/name", dom_path).as_str(), config.name)?;
tx.write_string(format!("{}/name", vm_path).as_str(), config.name)?; tx.write_string(format!("{}/name", vm_path).as_str(), config.name)?;
tx.write_string(format!("{}/type", libxl_path).as_str(), "pv")?;
tx.commit()?; tx.commit()?;
} }
@ -222,53 +247,192 @@ impl XenClient {
&xenstore_mfn.to_string(), &xenstore_mfn.to_string(),
)?; )?;
for i in 0..config.max_vcpus { for i in 0..config.max_vcpus {
tx.write_string( let path = format!("{}/cpu/{}", dom_path, i);
format!("{}/cpu/{}/availability", dom_path, i).as_str(), tx.mkdir(&path)?;
"online", tx.set_perms(&path, ro_perm)?;
)?; let path = format!("{}/cpu/{}/availability", dom_path, i);
tx.write_string(&path, "online")?;
tx.set_perms(&path, ro_perm)?;
} }
tx.commit()?; tx.commit()?;
} }
if !self
self.console_device_add(&dom_path.to_string(), domid, console_evtchn, console_mfn)?; .store
self.store .introduce_domain(domid, xenstore_mfn, xenstore_evtchn)?
.introduce_domain(domid, xenstore_mfn, xenstore_evtchn)?; {
return Err(XenClientError::new("failed to introduce domain"));
}
self.console_device_add(
&dom_path,
&backend_dom_path,
config.backend_domid,
domid,
console_evtchn,
console_mfn,
)?;
for (index, disk) in config.disks.iter().enumerate() {
self.disk_device_add(
&dom_path,
&backend_dom_path,
config.backend_domid,
domid,
index,
disk,
)?;
}
self.call.unpause_domain(domid)?; self.call.unpause_domain(domid)?;
Ok(domid) Ok(domid)
} }
fn disk_device_add(
&mut self,
dom_path: &str,
backend_dom_path: &str,
backend_domid: u32,
domid: u32,
index: usize,
disk: &DomainDisk,
) -> Result<(), XenClientError> {
let id = (202 << 8) | (index << 4) as u64;
let backend_items: Vec<(&str, String)> = vec![
("frontend-id", domid.to_string()),
("params", disk.pdev.to_string()),
("script", "/etc/xen/scripts/block".to_string()),
("online", "1".to_string()),
("removable", "0".to_string()),
("bootable", "1".to_string()),
("state", "1".to_string()),
("dev", disk.vdev.to_string()),
("type", "phy".to_string()),
("mode", if disk.writable { "w" } else { "r" }.to_string()),
("device-type", "disk".to_string()),
("discard-enable", "0".to_string()),
("specification", "xen".to_string()),
];
let frontend_items: Vec<(&str, String)> = vec![
("backend-id", backend_domid.to_string()),
("state", "1".to_string()),
("virtual-device", id.to_string()),
("device-type", "disk".to_string()),
("trusted", "1".to_string()),
("protocol", "x86_64-abi".to_string()),
];
self.device_add(
"vbd",
id,
dom_path,
backend_dom_path,
backend_domid,
domid,
frontend_items,
backend_items,
)?;
Ok(())
}
fn console_device_add( fn console_device_add(
&mut self, &mut self,
dom_path: &String, dom_path: &str,
backend_dom_path: &str,
backend_domid: u32,
domid: u32, domid: u32,
port: u32, port: u32,
mfn: u64, mfn: u64,
) -> Result<(), XenClientError> { ) -> Result<(), XenClientError> {
let frontend_path = format!("{}/console", dom_path); let backend_entries = vec![
let backend_path = format!("{}/backend/console/{}/{}", dom_path, domid, 0); ("frontend-id", domid.to_string()),
let mut tx = self.store.transaction()?; ("online", "1".to_string()),
tx.write_string( ("state", "1".to_string()),
format!("{}/frontend-id", backend_path).as_str(), ("protocol", "vt100".to_string()),
&domid.to_string(), ];
)?;
tx.write_string(format!("{}/online", backend_path).as_str(), "1")?;
tx.write_string(format!("{}/state", backend_path).as_str(), "1")?;
tx.write_string(format!("{}/protocol", backend_path).as_str(), "vt100")?;
tx.write_string(format!("{}/backend-id", frontend_path).as_str(), "0")?; let frontend_entries = vec![
tx.write_string(format!("{}/limit", frontend_path).as_str(), "1048576")?; ("backend-id", backend_domid.to_string()),
tx.write_string(format!("{}/type", frontend_path).as_str(), "xenconsoled")?; ("limit", "1048576".to_string()),
tx.write_string(format!("{}/output", frontend_path).as_str(), "pty")?; ("type", "xenconsoled".to_string()),
tx.write_string(format!("{}/tty", frontend_path).as_str(), "")?; ("output", "pty".to_string()),
tx.write_string( ("tty", "".to_string()),
format!("{}/port", frontend_path).as_str(), ("port", port.to_string()),
&port.to_string(), ("ring-ref", mfn.to_string()),
)?; ];
tx.write_string(
format!("{}/ring-ref", frontend_path).as_str(), self.device_add(
&mfn.to_string(), "console",
0,
dom_path,
backend_dom_path,
backend_domid,
domid,
frontend_entries,
backend_entries,
)?; )?;
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn device_add(
&mut self,
typ: &str,
id: u64,
dom_path: &str,
backend_dom_path: &str,
backend_domid: u32,
domid: u32,
frontend_items: Vec<(&str, String)>,
backend_items: Vec<(&str, String)>,
) -> Result<(), XenClientError> {
let console_zero = typ == "console" && id == 0;
let frontend_path = if console_zero {
format!("{}/console", dom_path)
} else {
format!("{}/device/{}/{}", dom_path, typ, id)
};
let backend_path = format!("{}/backend/{}/{}/{}", backend_dom_path, typ, domid, id);
let mut backend_items: Vec<(&str, String)> = backend_items.clone();
let mut frontend_items: Vec<(&str, String)> = frontend_items.clone();
backend_items.push(("frontend", frontend_path.clone()));
frontend_items.push(("backend", backend_path.clone()));
let frontend_perms = &[
XsPermission {
id: domid,
perms: XS_PERM_NONE,
},
XsPermission {
id: backend_domid,
perms: XS_PERM_READ,
},
];
let backend_perms = &[
XsPermission {
id: backend_domid,
perms: XS_PERM_NONE,
},
XsPermission {
id: domid,
perms: XS_PERM_READ,
},
];
let mut tx = self.store.transaction()?;
tx.mknod(&frontend_path, frontend_perms)?;
for (p, value) in &frontend_items {
let path = format!("{}/{}", frontend_path, *p);
tx.write_string(&path, value)?;
if !console_zero {
tx.set_perms(&path, frontend_perms)?;
}
}
tx.mknod(&backend_path, backend_perms)?;
for (p, value) in &backend_items {
let path = format!("{}/{}", backend_path, *p);
tx.write_string(&path, value)?;
}
tx.commit()?; tx.commit()?;
Ok(()) Ok(())
} }

View File

@ -120,13 +120,7 @@ impl XsdResponse {
} }
pub fn parse_bool(&self) -> Result<bool, XsdBusError> { pub fn parse_bool(&self) -> Result<bool, XsdBusError> {
if self.payload.is_empty() { Ok(true)
Err(XsdBusError::new(
"Expected bool payload to be at least one byte.",
))
} else {
Ok(self.payload[0] == 0)
}
} }
} }

View File

@ -1,19 +1,39 @@
use crate::bus::{XsdBusError, XsdSocket}; use crate::bus::{XsdBusError, XsdSocket};
use crate::sys::{ use crate::sys::{
XSD_DIRECTORY, XSD_GET_DOMAIN_PATH, XSD_INTRODUCE, XSD_MKDIR, XSD_READ, XSD_RM, XSD_DIRECTORY, XSD_GET_DOMAIN_PATH, XSD_INTRODUCE, XSD_MKDIR, XSD_READ, XSD_RM, XSD_SET_PERMS,
XSD_TRANSACTION_END, XSD_TRANSACTION_START, XSD_WRITE, XSD_TRANSACTION_END, XSD_TRANSACTION_START, XSD_WRITE,
}; };
use log::trace;
use std::ffi::CString; use std::ffi::CString;
pub const XS_PERM_NONE: u32 = 0x00;
pub const XS_PERM_READ: u32 = 0x01;
pub const XS_PERM_WRITE: u32 = 0x02;
pub const XS_PERM_READ_WRITE: u32 = XS_PERM_READ | XS_PERM_WRITE;
pub struct XsdClient { pub struct XsdClient {
pub socket: XsdSocket, pub socket: XsdSocket,
} }
pub struct XsPermissions { #[derive(Debug, Copy, Clone)]
pub struct XsPermission {
pub id: u32, pub id: u32,
pub perms: u32, pub perms: u32,
} }
impl XsPermission {
pub fn encode(&self) -> Result<String, XsdBusError> {
let c = match self.perms {
XS_PERM_READ_WRITE => 'b',
XS_PERM_WRITE => 'w',
XS_PERM_READ => 'r',
XS_PERM_NONE => 'n',
_ => return Err(XsdBusError::new("invalid permissions")),
};
Ok(format!("{}{}", c, self.id))
}
}
pub trait XsdInterface { pub trait XsdInterface {
fn list(&mut self, path: &str) -> Result<Vec<String>, XsdBusError>; fn list(&mut self, path: &str) -> Result<Vec<String>, XsdBusError>;
fn read(&mut self, path: &str) -> Result<Vec<u8>, XsdBusError>; fn read(&mut self, path: &str) -> Result<Vec<u8>, XsdBusError>;
@ -22,9 +42,12 @@ pub trait XsdInterface {
fn write_string(&mut self, path: &str, data: &str) -> Result<bool, XsdBusError>; fn write_string(&mut self, path: &str, data: &str) -> Result<bool, XsdBusError>;
fn mkdir(&mut self, path: &str) -> Result<bool, XsdBusError>; fn mkdir(&mut self, path: &str) -> Result<bool, XsdBusError>;
fn rm(&mut self, path: &str) -> Result<bool, XsdBusError>; fn rm(&mut self, path: &str) -> Result<bool, XsdBusError>;
fn set_perms(&mut self, path: &str, perms: &[XsPermission]) -> Result<bool, XsdBusError>;
fn mknod(&mut self, path: &str, _perm: &XsPermissions) -> Result<bool, XsdBusError> { fn mknod(&mut self, path: &str, perms: &[XsPermission]) -> Result<bool, XsdBusError> {
self.write_string(path, "") let result1 = self.write_string(path, "")?;
let result2 = self.set_perms(path, perms)?;
Ok(result1 && result2)
} }
} }
@ -35,16 +58,19 @@ impl XsdClient {
} }
fn list(&mut self, tx: u32, path: &str) -> Result<Vec<String>, XsdBusError> { fn list(&mut self, tx: u32, path: &str) -> Result<Vec<String>, XsdBusError> {
trace!("list tx={tx} path={path}");
let response = self.socket.send_single(tx, XSD_DIRECTORY, path)?; let response = self.socket.send_single(tx, XSD_DIRECTORY, path)?;
response.parse_string_vec() response.parse_string_vec()
} }
fn read(&mut self, tx: u32, path: &str) -> Result<Vec<u8>, XsdBusError> { fn read(&mut self, tx: u32, path: &str) -> Result<Vec<u8>, XsdBusError> {
trace!("read tx={tx} path={path}");
let response = self.socket.send_single(tx, XSD_READ, path)?; let response = self.socket.send_single(tx, XSD_READ, path)?;
Ok(response.payload) Ok(response.payload)
} }
fn write(&mut self, tx: u32, path: &str, data: Vec<u8>) -> Result<bool, XsdBusError> { fn write(&mut self, tx: u32, path: &str, data: Vec<u8>) -> Result<bool, XsdBusError> {
trace!("write tx={tx} path={path} data={:?}", data);
let mut buffer = Vec::new(); let mut buffer = Vec::new();
let path = CString::new(path)?; let path = CString::new(path)?;
buffer.extend_from_slice(path.as_bytes_with_nul()); buffer.extend_from_slice(path.as_bytes_with_nul());
@ -54,14 +80,34 @@ impl XsdClient {
} }
fn mkdir(&mut self, tx: u32, path: &str) -> Result<bool, XsdBusError> { fn mkdir(&mut self, tx: u32, path: &str) -> Result<bool, XsdBusError> {
trace!("mkdir tx={tx} path={path}");
self.socket.send_single(tx, XSD_MKDIR, path)?.parse_bool() self.socket.send_single(tx, XSD_MKDIR, path)?.parse_bool()
} }
fn rm(&mut self, tx: u32, path: &str) -> Result<bool, XsdBusError> { fn rm(&mut self, tx: u32, path: &str) -> Result<bool, XsdBusError> {
trace!("rm tx={tx} path={path}");
self.socket.send_single(tx, XSD_RM, path)?.parse_bool() self.socket.send_single(tx, XSD_RM, path)?.parse_bool()
} }
fn set_perms(
&mut self,
tx: u32,
path: &str,
perms: &[XsPermission],
) -> Result<bool, XsdBusError> {
trace!("set_perms tx={tx} path={path} perms={:?}", perms);
let mut items: Vec<String> = Vec::new();
items.push(path.to_string());
for perm in perms {
items.push(perm.encode()?);
}
let items_str: Vec<&str> = items.iter().map(|x| x.as_str()).collect();
let response = self.socket.send_multiple(tx, XSD_SET_PERMS, &items_str)?;
response.parse_bool()
}
pub fn transaction(&mut self) -> Result<XsdTransaction, XsdBusError> { pub fn transaction(&mut self) -> Result<XsdTransaction, XsdBusError> {
trace!("transaction start");
let response = self.socket.send_single(0, XSD_TRANSACTION_START, "")?; let response = self.socket.send_single(0, XSD_TRANSACTION_START, "")?;
let str = response.parse_string()?; let str = response.parse_string()?;
let tx = str.parse::<u32>()?; let tx = str.parse::<u32>()?;
@ -79,18 +125,19 @@ impl XsdClient {
&mut self, &mut self,
domid: u32, domid: u32,
mfn: u64, mfn: u64,
eventchn: u32, evtchn: u32,
) -> Result<String, XsdBusError> { ) -> Result<bool, XsdBusError> {
trace!("introduce domain domid={domid} mfn={mfn} evtchn={evtchn}");
let response = self.socket.send_multiple( let response = self.socket.send_multiple(
0, 0,
XSD_INTRODUCE, XSD_INTRODUCE,
&[ &[
domid.to_string().as_str(), domid.to_string().as_str(),
mfn.to_string().as_str(), mfn.to_string().as_str(),
eventchn.to_string().as_str(), evtchn.to_string().as_str(),
], ],
)?; )?;
response.parse_string() response.parse_bool()
} }
} }
@ -127,6 +174,10 @@ impl XsdInterface for XsdClient {
fn rm(&mut self, path: &str) -> Result<bool, XsdBusError> { fn rm(&mut self, path: &str) -> Result<bool, XsdBusError> {
self.rm(0, path) self.rm(0, path)
} }
fn set_perms(&mut self, path: &str, perms: &[XsPermission]) -> Result<bool, XsdBusError> {
self.set_perms(0, path, perms)
}
} }
impl XsdInterface for XsdTransaction<'_> { impl XsdInterface for XsdTransaction<'_> {
@ -157,12 +208,17 @@ impl XsdInterface for XsdTransaction<'_> {
fn rm(&mut self, path: &str) -> Result<bool, XsdBusError> { fn rm(&mut self, path: &str) -> Result<bool, XsdBusError> {
self.client.rm(self.tx, path) self.client.rm(self.tx, path)
} }
fn set_perms(&mut self, path: &str, perms: &[XsPermission]) -> Result<bool, XsdBusError> {
self.client.set_perms(self.tx, path, perms)
}
} }
impl XsdTransaction<'_> { impl XsdTransaction<'_> {
pub fn end(&mut self, abort: bool) -> Result<bool, XsdBusError> { pub fn end(&mut self, abort: bool) -> Result<bool, XsdBusError> {
let abort_str = if abort { "F" } else { "T" }; let abort_str = if abort { "F" } else { "T" };
trace!("transaction end abort={abort_str}");
self.client self.client
.socket .socket
.send_single(self.tx, XSD_TRANSACTION_END, abort_str)? .send_single(self.tx, XSD_TRANSACTION_END, abort_str)?