diff --git a/hypha/Cargo.toml b/hypha/Cargo.toml index 5787e33..9ad3d6f 100644 --- a/hypha/Cargo.toml +++ b/hypha/Cargo.toml @@ -44,6 +44,10 @@ version = "0.14.2" version = "1.6.1" features = ["v4"] +[dependencies.sys-mount] +default-features = false +version = "2.1.1" + [lib] path = "src/lib.rs" @@ -52,5 +56,5 @@ name = "hyphactl" path = "bin/controller.rs" [[bin]] -name = "hyphad" +name = "hyphactr" path = "bin/container.rs" diff --git a/hypha/bin/container.rs b/hypha/bin/container.rs index dc97f6d..a7e29a6 100644 --- a/hypha/bin/container.rs +++ b/hypha/bin/container.rs @@ -1,7 +1,9 @@ +use hypha::container::init::ContainerInit; use hypha::error::Result; fn main() -> Result<()> { env_logger::init(); - + let mut container = ContainerInit::new(); + container.init()?; Ok(()) } diff --git a/hypha/bin/controller.rs b/hypha/bin/controller.rs index a8e0cfe..e345046 100644 --- a/hypha/bin/controller.rs +++ b/hypha/bin/controller.rs @@ -28,6 +28,8 @@ enum Commands { cpus: u32, #[arg(short, long, default_value_t = 512)] mem: u64, + #[arg(long)] + config_bundle: Option, }, Destroy { #[arg(short, long)] @@ -64,10 +66,18 @@ fn main() -> Result<()> { image, cpus, mem, + config_bundle, } => { let kernel = map_kernel_path(&store_path, kernel); let initrd = map_initrd_path(&store_path, initrd); - let domid = controller.launch(&kernel, &initrd, &image, cpus, mem)?; + let domid = controller.launch( + &kernel, + &initrd, + config_bundle.as_deref(), + &image, + cpus, + mem, + )?; println!("launched domain: {}", domid); } @@ -92,7 +102,12 @@ fn main() -> Result<()> { ]; table.push_row_string(&row)?; } - println!("{}", table.to_string()); + + if table.num_records() == 1 { + println!("no containers have been launched"); + } else { + println!("{}", table.to_string()); + } } } Ok(()) diff --git a/hypha/src/container/init.rs b/hypha/src/container/init.rs new file mode 100644 index 0000000..bd0553c --- /dev/null +++ b/hypha/src/container/init.rs @@ -0,0 +1,48 @@ +use crate::error::Result; +use std::fs; +use std::path::Path; +use sys_mount::{FilesystemType, Mount, MountFlags}; + +const IMAGE_BLOCK_DEVICE_PATH: &str = "/dev/xvda"; +const CONFIG_BLOCK_DEVICE_PATH: &str = "/dev/xvdb"; + +const IMAGE_MOUNT_PATH: &str = "/image"; +const CONFIG_MOUNT_PATH: &str = "/config"; + +pub struct ContainerInit {} + +impl Default for ContainerInit { + fn default() -> Self { + Self::new() + } +} + +impl ContainerInit { + pub fn new() -> ContainerInit { + ContainerInit {} + } + + pub fn init(&mut self) -> Result<()> { + self.prepare_mounts()?; + Ok(()) + } + + fn prepare_mounts(&mut self) -> Result<()> { + let image_mount_path = Path::new(IMAGE_MOUNT_PATH); + let config_mount_path = Path::new(CONFIG_MOUNT_PATH); + self.mount_squashfs(Path::new(IMAGE_BLOCK_DEVICE_PATH), image_mount_path)?; + self.mount_squashfs(Path::new(CONFIG_BLOCK_DEVICE_PATH), config_mount_path)?; + Ok(()) + } + + fn mount_squashfs(&mut self, from: &Path, to: &Path) -> Result<()> { + if !to.is_dir() { + fs::create_dir(to)?; + } + Mount::builder() + .fstype(FilesystemType::Manual("squashfs")) + .flags(MountFlags::RDONLY) + .mount(from, to)?; + Ok(()) + } +} diff --git a/hypha/src/container/mod.rs b/hypha/src/container/mod.rs index 8b13789..43763f1 100644 --- a/hypha/src/container/mod.rs +++ b/hypha/src/container/mod.rs @@ -1 +1 @@ - +pub mod init; diff --git a/hypha/src/ctl/cfgblk.rs b/hypha/src/ctl/cfgblk.rs index c91e444..65228f9 100644 --- a/hypha/src/ctl/cfgblk.rs +++ b/hypha/src/ctl/cfgblk.rs @@ -8,12 +8,17 @@ use uuid::Uuid; pub struct ConfigBlock<'a> { pub image_info: &'a ImageInfo, + pub config_bundle: Option<&'a str>, pub file: PathBuf, pub dir: PathBuf, } impl ConfigBlock<'_> { - pub fn new<'a>(uuid: &Uuid, image_info: &'a ImageInfo) -> Result> { + pub fn new<'a>( + uuid: &Uuid, + image_info: &'a ImageInfo, + config_bundle: Option<&'a str>, + ) -> Result> { let mut dir = std::env::temp_dir().clone(); dir.push(format!("hypha-cfg-{}", uuid)); fs::create_dir_all(&dir)?; @@ -21,12 +26,17 @@ impl ConfigBlock<'_> { file.push("config.squashfs"); Ok(ConfigBlock { image_info, + config_bundle, file, dir, }) } pub fn build(&self) -> Result<()> { + let config_bundle_content = match self.config_bundle { + None => None, + Some(path) => Some(fs::read(path)?), + }; let manifest = self.image_info.config.to_string()?; let mut writer = FilesystemWriter::default(); writer.push_dir( @@ -48,6 +58,18 @@ impl ConfigBlock<'_> { mtime: 0, }, )?; + if let Some(config_bundle_content) = config_bundle_content.as_ref() { + writer.push_file( + config_bundle_content.as_slice(), + "/bundle", + NodeHeader { + permissions: 384, + uid: 0, + gid: 0, + mtime: 0, + }, + )?; + } let mut file = File::create(&self.file)?; writer.write(&mut file)?; Ok(()) diff --git a/hypha/src/ctl/mod.rs b/hypha/src/ctl/mod.rs index 0b65886..e311209 100644 --- a/hypha/src/ctl/mod.rs +++ b/hypha/src/ctl/mod.rs @@ -63,6 +63,7 @@ impl Controller { &mut self, kernel_path: &str, initrd_path: &str, + config_bundle_path: Option<&str>, image: &str, vcpus: u32, mem: u64, @@ -70,7 +71,7 @@ impl Controller { let uuid = Uuid::new_v4(); let name = format!("hypha-{uuid}"); let image_info = self.compile(image)?; - let cfgblk = ConfigBlock::new(&uuid, &image_info)?; + let cfgblk = ConfigBlock::new(&uuid, &image_info, config_bundle_path)?; cfgblk.build()?; let image_squashfs_path = image_info @@ -110,6 +111,7 @@ impl Controller { writable: false, }, ], + filesystems: vec![], extra_keys: vec![ ("hypha/uuid".to_string(), uuid.to_string()), ( diff --git a/hypha/src/error.rs b/hypha/src/error.rs index 1d25351..ed8ac5c 100644 --- a/hypha/src/error.rs +++ b/hypha/src/error.rs @@ -35,6 +35,15 @@ impl Error for HyphaError { } } +#[macro_export] +macro_rules! hypha_err { + ($($arg:tt)*) => {{ + use $crate::error::HyphaError; + let text = std::fmt::format(format_args!($($arg)*)); + Err(HyphaError::new(text.as_str())) + }} +} + impl From for HyphaError { fn from(value: std::io::Error) -> Self { HyphaError::new(value.to_string().as_str()) diff --git a/xenclient/examples/boot.rs b/xenclient/examples/boot.rs index 3819a1d..8929c84 100644 --- a/xenclient/examples/boot.rs +++ b/xenclient/examples/boot.rs @@ -21,6 +21,7 @@ fn main() -> Result<(), XenClientError> { initrd_path: initrd_path.as_str(), cmdline: "debug elevator=noop", disks: vec![], + filesystems: vec![], extra_keys: vec![], }; let domid = client.create(&config)?; diff --git a/xenclient/src/lib.rs b/xenclient/src/lib.rs index 2f332ca..d061c0d 100644 --- a/xenclient/src/lib.rs +++ b/xenclient/src/lib.rs @@ -97,6 +97,11 @@ pub struct DomainDisk<'a> { pub writable: bool, } +pub struct DomainFilesystem<'a> { + pub path: &'a str, + pub tag: &'a str, +} + pub struct DomainConfig<'a> { pub backend_domid: u32, pub name: &'a str, @@ -106,6 +111,7 @@ pub struct DomainConfig<'a> { pub initrd_path: &'a str, pub cmdline: &'a str, pub disks: Vec>, + pub filesystems: Vec>, pub extra_keys: Vec<(String, String)>, } @@ -403,6 +409,16 @@ impl XenClient { disk, )?; } + for (index, filesystem) in config.filesystems.iter().enumerate() { + self.fs_9p_device_add( + &dom_path, + &backend_dom_path, + config.backend_domid, + domid, + index, + filesystem, + )?; + } self.call.unpause_domain(domid)?; Ok(()) } @@ -497,6 +513,43 @@ impl XenClient { Ok(()) } + fn fs_9p_device_add( + &mut self, + dom_path: &str, + backend_dom_path: &str, + backend_domid: u32, + domid: u32, + index: usize, + filesystem: &DomainFilesystem, + ) -> Result<(), XenClientError> { + let id = 90 + index as u64; + let backend_items: Vec<(&str, String)> = vec![ + ("frontend-id", domid.to_string()), + ("online", "1".to_string()), + ("state", "1".to_string()), + ("path", filesystem.path.to_string()), + ("security-model", "none".to_string()), + ]; + + let frontend_items: Vec<(&str, String)> = vec![ + ("backend-id", backend_domid.to_string()), + ("state", "1".to_string()), + ("tag", filesystem.tag.to_string()), + ]; + + self.device_add( + "9pfs", + id, + dom_path, + backend_dom_path, + backend_domid, + domid, + frontend_items, + backend_items, + )?; + Ok(()) + } + #[allow(clippy::too_many_arguments)] fn device_add( &mut self,