From 710282674a016df76f952c3e299db7d4a2688418 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Mon, 8 Jan 2024 12:43:16 -0800 Subject: [PATCH] Initial commit. --- .gitignore | 2 + Cargo.toml | 5 ++ README.md | 3 + xsd/Cargo.toml | 19 ++++++ xsd/examples/list.rs | 31 +++++++++ xsd/src/bus.rs | 149 +++++++++++++++++++++++++++++++++++++++++++ xsd/src/client.rs | 23 +++++++ xsd/src/lib.rs | 3 + xsd/src/sys.rs | 93 +++++++++++++++++++++++++++ 9 files changed, 328 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 xsd/Cargo.toml create mode 100644 xsd/examples/list.rs create mode 100644 xsd/src/bus.rs create mode 100644 xsd/src/client.rs create mode 100644 xsd/src/lib.rs create mode 100644 xsd/src/sys.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..04ed257 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ + "xsd" +] +resolver = "2" diff --git a/README.md b/README.md new file mode 100644 index 0000000..f095f1e --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# xen-rs + +Xen bindings for Rust. diff --git a/xsd/Cargo.toml b/xsd/Cargo.toml new file mode 100644 index 0000000..fd3d6b2 --- /dev/null +++ b/xsd/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "xsd" +version = "0.0.1" +edition = "2021" +resolver = "2" + +[lib] +path = "src/lib.rs" + +[dependencies] +libc = "0.2" + +[dependencies.bytemuck] +version = "1.14.0" +features = ["derive"] + +[[example]] +name = "xsd-list" +path = "examples/list.rs" diff --git a/xsd/examples/list.rs b/xsd/examples/list.rs new file mode 100644 index 0000000..155618c --- /dev/null +++ b/xsd/examples/list.rs @@ -0,0 +1,31 @@ +use xsd::bus::XsdBusError; +use xsd::client::XsdClient; + +fn list_recursive(client: &mut XsdClient, level: usize, path: &str) -> Result<(), XsdBusError> { + let children = match client.list(path) { + Ok(children) => children, + Err(error) => return if error.to_string() == "EINVAL" { + Ok(()) + } else { + Err(error) + } + }; + + for child in children { + let full = format!("{}/{}", if path == "/" { + "" + } else { + path + }, child); + let value = client.read(full.as_str())?; + println!("{}{} = {:?}", " ".repeat(level), child, String::from_utf8(value)?); + list_recursive(client, level + 1, full.as_str())?; + } + Ok(()) +} + +fn main() -> Result<(), XsdBusError> { + let mut client = XsdClient::new()?; + list_recursive(&mut client, 0, "/")?; + Ok(()) +} diff --git a/xsd/src/bus.rs b/xsd/src/bus.rs new file mode 100644 index 0000000..3374e11 --- /dev/null +++ b/xsd/src/bus.rs @@ -0,0 +1,149 @@ +use std::error::Error; +use std::ffi::{CString, FromVecWithNulError, NulError}; +use std::fs::metadata; +use std::io::{Read, Write}; +use std::mem::size_of; +use std::net::Shutdown; +use std::os::unix::net::UnixStream; +use std::str::Utf8Error; +use std::string::FromUtf8Error; +use crate::sys::{XSD_ERROR, XsdMessageHeader}; + +const XEN_BUS_PATHS: &'static [&'static str] = &[ + "/var/run/xenstored/socket" +]; + +fn find_bus_path() -> Option { + for path in XEN_BUS_PATHS { + match metadata(path) { + Ok(_) => return Some(String::from(*path)), + Err(_) => continue + } + } + None +} + +#[derive(Debug)] +pub struct XsdBusError { + message: String +} + +impl XsdBusError { + pub fn new(msg: &str) -> XsdBusError { + return XsdBusError {message: msg.to_string()}; + } +} + +impl std::fmt::Display for XsdBusError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f,"{}", self.message) + } +} + +impl Error for XsdBusError { + fn description(&self) -> &str { + &self.message + } +} + +impl From for XsdBusError { + fn from(value: std::io::Error) -> Self { + XsdBusError::new(value.to_string().as_str()) + } +} + +impl From for XsdBusError { + fn from(_: NulError) -> Self { + XsdBusError::new("Unable to coerce data into a C string.") + } +} + +impl From for XsdBusError { + fn from(_: FromVecWithNulError) -> Self { + XsdBusError::new("Unable to coerce data into a C string.") + } +} + +impl From for XsdBusError { + fn from(_: Utf8Error) -> Self { + XsdBusError::new("Unable to coerce data into a UTF8 string.") + } +} + +impl From for XsdBusError { + fn from(_: FromUtf8Error) -> Self { + XsdBusError::new("Unable to coerce data into a UTF8 string.") + } +} + +pub struct XsdSocket { + handle: UnixStream +} + +#[derive(Debug)] +pub struct XsdResponse { + pub header: XsdMessageHeader, + pub payload: Vec +} + +impl XsdResponse { + pub fn parse_string_vec(&self) -> Result, XsdBusError> { + let mut strings: Vec = Vec::new(); + let mut buffer: Vec = Vec::new(); + for b in &self.payload { + if *b == 0 { + let string = String::from_utf8(buffer.clone())?; + strings.push(string); + buffer.clear(); + continue; + } + buffer.push(b.clone()); + } + Ok(strings) + } +} + +impl XsdSocket { + pub fn dial() -> Result { + let path = match find_bus_path() { + Some(path) => path, + None => return Err(XsdBusError::new("Failed to find valid bus path.")) + }; + let stream = UnixStream::connect(path)?; + Ok(XsdSocket { handle: stream }) + } + + pub fn send(&mut self, tx: u32, typ: u32, buf: &[u8]) -> Result { + let header = XsdMessageHeader { + typ, + req: 0, + tx, + len: buf.len() as u32 + }; + self.handle.write_all(bytemuck::bytes_of(&header))?; + self.handle.write_all(buf)?; + let mut result_buf = vec![0u8; size_of::()]; + self.handle.read(result_buf.as_mut_slice())?; + let result_header = bytemuck::from_bytes::(&result_buf); + let mut payload = vec![0u8; result_header.len as usize]; + self.handle.read_exact(payload.as_mut_slice())?; + if result_header.typ == XSD_ERROR { + let error = CString::from_vec_with_nul(payload)?; + return Err(XsdBusError::new(error.to_str()?)); + } + let response = XsdResponse { header: header.clone(), payload }; + Ok(response) + } + + pub fn send_single(&mut self, tx: u32, typ: u32, string: &str) -> Result { + let path = CString::new(string)?; + let buf = path.as_bytes_with_nul(); + Ok(self.send(tx, typ, buf)?) + } +} + +impl Drop for XsdSocket { + fn drop(&mut self) { + self.handle.shutdown(Shutdown::Both).unwrap() + } +} diff --git a/xsd/src/client.rs b/xsd/src/client.rs new file mode 100644 index 0000000..f289f38 --- /dev/null +++ b/xsd/src/client.rs @@ -0,0 +1,23 @@ +use crate::bus::{XsdBusError, XsdSocket}; +use crate::sys::{XSD_DIRECTORY, XSD_READ}; + +pub struct XsdClient { + socket: XsdSocket, +} + +impl XsdClient { + pub fn new() -> Result { + let socket = XsdSocket::dial()?; + Ok(XsdClient { socket }) + } + + pub fn list(&mut self, path: &str) -> Result, XsdBusError> { + let response = self.socket.send_single(0, XSD_DIRECTORY, path)?; + Ok(response.parse_string_vec()?) + } + + pub fn read(&mut self, path: &str) -> Result, XsdBusError> { + let response = self.socket.send_single(0, XSD_READ, path)?; + Ok(response.payload) + } +} diff --git a/xsd/src/lib.rs b/xsd/src/lib.rs new file mode 100644 index 0000000..4c7a6de --- /dev/null +++ b/xsd/src/lib.rs @@ -0,0 +1,3 @@ +pub mod sys; +pub mod bus; +pub mod client; diff --git a/xsd/src/sys.rs b/xsd/src/sys.rs new file mode 100644 index 0000000..4db5c8d --- /dev/null +++ b/xsd/src/sys.rs @@ -0,0 +1,93 @@ +/// Handwritten protocol definitions for XenStore. +/// Used xen/include/public/io/xs_wire.h as a reference. +use libc; +use bytemuck::{Pod, Zeroable}; + +#[derive(Copy, Clone, Pod, Zeroable, Debug)] +#[repr(C)] +pub struct XsdMessageHeader { + pub typ: u32, + pub req: u32, + pub tx: u32, + pub len: u32 +} + +pub const XSD_CONTROL: u32 = 0; +pub const XSD_DIRECTORY: u32 = 1; +pub const XSD_READ: u32 = 2; +pub const XSD_GET_PERMS: u32 = 3; +pub const XSD_WATCH: u32 = 4; +pub const XSD_UNWATCH: u32 = 5; +pub const XSD_TRANSACTION_START: u32 = 6; +pub const XSD_TRANSACTION_END: u32 = 7; +pub const XSD_INTRODUCE: u32 = 8; +pub const XSD_RELEASE: u32 = 9; +pub const XSD_GET_DOMAIN_PATH: u32 = 10; +pub const XSD_WRITE: u32 = 11; +pub const XSD_MKDIR: u32 = 12; +pub const XSD_RM: u32 = 13; +pub const XSD_SET_PERMS: u32 = 14; +pub const XSD_WATCH_EVENT: u32 = 15; +pub const XSD_ERROR: u32 = 16; +pub const XSD_IS_DOMAIN_INTRODUCED: u32 = 17; +pub const XSD_RESUME: u32 = 18; +pub const XSD_SET_TARGET: u32 = 19; +pub const XSD_RESET_WATCHES: u32 = XSD_SET_TARGET + 2; +pub const XSD_DIRECTORY_PART: u32 = 20; +pub const XSD_TYPE_COUNT: u32 = 21; +pub const XSD_INVALID: u32 = 0xffff; + +pub const XSD_WRITE_NONE: &str = "NONE"; +pub const XSD_WRITE_CREATE: &str = "CREATE"; +pub const XSD_WRITE_CREATE_EXCL: &str = "CREATE|EXCL"; + +#[repr(C)] +pub struct XsError<'a> { + num: i32, + error: &'a str +} + +pub const XSD_ERROR_EINVAL: XsError = XsError { num: libc::EINVAL, error: "EINVAL" }; +pub const XSD_ERROR_EACCES: XsError = XsError { num: libc::EACCES, error: "EACCES" }; +pub const XSD_ERROR_EEXIST: XsError = XsError { num: libc::EEXIST, error: "EEXIST" }; +pub const XSD_ERROR_EISDIR: XsError = XsError { num: libc::EISDIR, error: "EISDIR" }; +pub const XSD_ERROR_ENOENT: XsError = XsError { num: libc::ENOENT, error: "ENOENT" }; +pub const XSD_ERROR_ENOMEM: XsError = XsError { num: libc::ENOMEM, error: "ENOMEM" }; +pub const XSD_ERROR_ENOSPC: XsError = XsError { num: libc::ENOSPC, error: "ENOSPC" }; +pub const XSD_ERROR_EIO: XsError = XsError { num: libc::EIO, error: "EIO" }; +pub const XSD_ERROR_ENOTEMPTY: XsError = XsError { num: libc::ENOTEMPTY, error: "ENOTEMPTY" }; +pub const XSD_ERROR_ENOSYS: XsError = XsError { num: libc::ENOSYS, error: "ENOSYS" }; +pub const XSD_ERROR_EROFS: XsError = XsError { num: libc::EROFS, error: "EROFS" }; +pub const XSD_ERROR_EBUSY: XsError = XsError { num: libc::EBUSY, error: "EBUSY" }; +pub const XSD_ERROR_EAGAIN: XsError = XsError { num: libc::EAGAIN, error: "EAGAIN" }; +pub const XSD_ERROR_EISCONN: XsError = XsError { num: libc::EISCONN, error: "EISCONN" }; +pub const XSD_ERROR_E2BIG: XsError = XsError { num: libc::E2BIG, error: "E2BIG" }; +pub const XSD_ERROR_EPERM: XsError = XsError { num: libc::EPERM, error: "EPERM" }; + +pub const XSD_WATCH_PATH: u32 = 0; +pub const XSD_WATCH_TOKEN: u32 = 1; + +#[repr(C)] +pub struct XenDomainInterface { + req: [i8; 1024], + rsp: [i8; 1024], + req_cons: u32, + req_prod: u32, + rsp_cons: u32, + rsp_prod: u32, + server_features: u32, + connection: u32, + error: u32 +} + +pub const XS_PAYLOAD_MAX: u32 = 4096; +pub const XS_ABS_PATH_MAX: u32 = 3072; +pub const XS_REL_PATH_MAX: u32 = 2048; +pub const XS_SERVER_FEATURE_RECONNECTION: u32 = 1; +pub const XS_SERVER_FEATURE_ERROR: u32 = 2; +pub const XS_CONNECTED: u32 = 0; +pub const XS_RECONNECT: u32 = 1; +pub const XS_ERROR_NONE: u32 = 0; +pub const XS_ERROR_COMM: u32 = 1; +pub const XS_ERROR_RINGIDX: u32 = 2; +pub const XS_ERROR_PROTO: u32 = 3;