Private
Public Access
1
0

xpc: kpcli: clean up client interface

This commit is contained in:
2025-08-23 19:41:12 -07:00
parent 8ff95f4bf9
commit 885c96172d
2 changed files with 46 additions and 38 deletions

View File

@@ -9,7 +9,8 @@ mod dbus;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
mod xpc; mod xpc;
#[async_trait] #[cfg_attr(target_os = "macos", async_trait(?Send))]
#[cfg_attr(not(target_os = "macos"), async_trait)]
pub trait DaemonInterface { pub trait DaemonInterface {
async fn print_version(&mut self) -> Result<()>; async fn print_version(&mut self) -> Result<()>;
async fn print_conversations(&mut self) -> Result<()>; async fn print_conversations(&mut self) -> Result<()>;
@@ -40,7 +41,8 @@ impl StubDaemonInterface {
} }
} }
#[async_trait] #[cfg_attr(target_os = "macos", async_trait(?Send))]
#[cfg_attr(not(target_os = "macos"), async_trait)]
impl DaemonInterface for StubDaemonInterface { impl DaemonInterface for StubDaemonInterface {
async fn print_version(&mut self) -> Result<()> { async fn print_version(&mut self) -> Result<()> {
Err(anyhow::anyhow!( Err(anyhow::anyhow!(

View File

@@ -111,49 +111,55 @@ impl XpcDaemonInterface {
pub fn new() -> Result<Self> { pub fn new() -> Result<Self> {
Ok(Self) Ok(Self)
} }
fn build_service_name() -> Result<CString> {
let service_name = SERVICE_NAME.trim_end_matches('\0');
Ok(CString::new(service_name)?)
}
fn build_request(method: &str, args: Option<HashMap<CString, Message>>) -> HashMap<CString, Message> {
let mut request = HashMap::new();
request.insert(CString::new("method").unwrap(), Message::String(CString::new(method).unwrap()));
if let Some(arguments) = args {
request.insert(CString::new("arguments").unwrap(), Message::Dictionary(arguments));
}
request
}
async fn call_method(&self, client: &mut XPCClient, method: &str, args: Option<HashMap<CString, Message>>) -> anyhow::Result<HashMap<CString, Message>> {
let request = Self::build_request(method, args);
client.send_message(Message::Dictionary(request));
match client.next().await {
Some(Message::Dictionary(map)) => Ok(map),
Some(other) => Err(anyhow::anyhow!("Unexpected XPC reply: {:?}", other)),
None => Err(anyhow::anyhow!("No reply received from XPC daemon")),
}
}
fn get_string<'a>(map: &'a HashMap<CString, Message>, key: &str) -> Option<&'a CStr> {
map.get(&CString::new(key).ok()?).and_then(|v| match v { Message::String(s) => Some(s.as_c_str()), _ => None })
}
} }
#[async_trait] #[async_trait(?Send)]
impl DaemonInterface for XpcDaemonInterface { impl DaemonInterface for XpcDaemonInterface {
async fn print_version(&mut self) -> Result<()> { async fn print_version(&mut self) -> Result<()> {
// Build service name CString (trim trailing NUL from const) // Build service name and connect
let service_name = SERVICE_NAME.trim_end_matches('\0'); let mach_port_name = Self::build_service_name()?;
let mach_port_name = CString::new(service_name)?;
// Open an XPC connection to the daemon service
let mut client = XPCClient::connect(&mach_port_name); let mut client = XPCClient::connect(&mach_port_name);
// Send a GetVersion request as a dictionary message: { method: "GetVersion" } // Call generic method and parse reply
{ let map = self.call_method(&mut client, GET_VERSION_METHOD, None).await?;
let mut request = HashMap::new(); if let Some(ver) = Self::get_string(&map, "version") {
request.insert( println!("Server version: {}", ver.to_string_lossy());
CString::new("method").unwrap(), Ok(())
Message::String(CString::new(GET_VERSION_METHOD).unwrap()), } else if let Some(ty) = Self::get_string(&map, "type") {
); println!("XPC replied with type: {}", ty.to_string_lossy());
client.send_message(Message::Dictionary(request)); Ok(())
} else {
Err(anyhow::anyhow!("Unexpected XPC reply payload for GetVersion"))
} }
// Await a single reply and print the version
match client.next().await {
Some(Message::Dictionary(map)) => {
if let Some(Message::String(ver)) = map.get(&CString::new("version").unwrap()) {
println!("Server version: {}", ver.to_string_lossy());
} else if let Some(Message::String(ty)) = map.get(&CString::new("type").unwrap())
{
println!("XPC replied with type: {}", ty.to_string_lossy());
} else {
eprintln!("Unexpected XPC reply payload for GetVersion");
}
}
Some(other) => {
eprintln!("Unexpected XPC reply: {:?}", other);
}
None => {
eprintln!("No reply received from XPC daemon");
}
}
Ok(())
} }
// Remaining methods unimplemented on macOS // Remaining methods unimplemented on macOS