From cc59fe4996cac64a8a54ec21fda76a7c2103a590 Mon Sep 17 00:00:00 2001 From: James Magahern Date: Sun, 24 Aug 2025 23:38:14 -0700 Subject: [PATCH] xpc: better file descriptor handling --- kordophoned/src/xpc/agent.rs | 35 +---------------------------------- kordophoned/src/xpc/rpc.rs | 28 +++++++++++++++++++--------- 2 files changed, 20 insertions(+), 43 deletions(-) diff --git a/kordophoned/src/xpc/agent.rs b/kordophoned/src/xpc/agent.rs index 409eb04..9a7040b 100644 --- a/kordophoned/src/xpc/agent.rs +++ b/kordophoned/src/xpc/agent.rs @@ -113,46 +113,13 @@ impl XpcAgent { let response = rt_conn.block_on(super::rpc::dispatch(&agent_conn, &conns_for_handler, client, &map)); let reply = xpc_sys::xpc_dictionary_create_reply(msg); if !reply.is_null() { - use std::ffi::CString as StdCString; - use std::os::fd::AsRawFd; - - // Precompute optional fd_path instruction from the message map - let mut maybe_fd_path: Option = None; - if let Message::Dictionary(ref resp_map) = response { - let attach = super::util::dict_get_str(resp_map, "attach_fd").unwrap_or_default() == "true"; - if attach { - maybe_fd_path = super::util::dict_get_str(resp_map, "fd_path"); - } - } - let payload = message_to_xpc_object(response); let apply_block = ConcreteBlock::new(move |key: *const c_char, value: xpc_sys::xpc_object_t| { xpc_sys::xpc_dictionary_set_value(reply, key, value); }) .copy(); - - xpc_sys::xpc_dictionary_apply(payload, apply_block.deref() as *const _ as *mut _); - // Optional FD attachment if requested by response - if let Some(fd_path) = maybe_fd_path { - match std::fs::OpenOptions::new().read(true).open(&fd_path) { - Ok(file) => { - let raw_fd = file.as_raw_fd(); - unsafe { - let fd_obj = xpc_sys::xpc_fd_create(raw_fd); - let key = StdCString::new("fd").unwrap(); - xpc_sys::xpc_dictionary_set_value(reply, key.as_ptr(), fd_obj); - // fd_obj is retained by reply; release our reference - xpc_sys::xpc_release(fd_obj); - } - // Keep file alive until after send - std::mem::forget(file); - } - Err(e) => { - log::warn!(target: LOG_TARGET, "Failed to open fd_path '{}': {}", fd_path, e); - } - } - } + xpc_sys::xpc_dictionary_apply(payload, apply_block.deref() as *const _ as *mut _); xpc_sys::xpc_connection_send_message(client, reply); xpc_sys::xpc_release(payload); diff --git a/kordophoned/src/xpc/rpc.rs b/kordophoned/src/xpc/rpc.rs index bcdc4f1..1a599bf 100644 --- a/kordophoned/src/xpc/rpc.rs +++ b/kordophoned/src/xpc/rpc.rs @@ -303,7 +303,7 @@ pub async fn dispatch( } } - // OpenAttachmentFd (attach file descriptor in reply) + // OpenAttachmentFd (return file descriptor in reply) "OpenAttachmentFd" => { let args = match get_dictionary_field(root, "arguments") { Some(a) => a, @@ -322,15 +322,25 @@ pub async fn dispatch( .await { Ok(attachment) => { - let path = attachment.get_path_for_preview(preview); - let mut reply: XpcMap = HashMap::new(); - - // The agent resolves fd_path to a file descriptor and returns it in the reply - dict_put_str(&mut reply, "type", "OpenAttachmentFdResponse"); - dict_put_str(&mut reply, "fd_path", &path.to_string_lossy()); - dict_put_str(&mut reply, "attach_fd", "true"); + use std::os::fd::AsRawFd; - Message::Dictionary(reply) + let path = attachment.get_path_for_preview(preview); + match std::fs::OpenOptions::new().read(true).open(&path) { + Ok(file) => { + let fd = file.as_raw_fd(); + + // Keep file alive until after conversion to XPC + std::mem::forget(file); + + // Return file descriptor in reply + let mut reply: XpcMap = HashMap::new(); + dict_put_str(&mut reply, "type", "OpenAttachmentFdResponse"); + reply.insert(cstr("fd"), Message::Fd(fd)); + + Message::Dictionary(reply) + } + Err(e) => make_error_reply("OpenFailed", &format!("{}", e)), + } } Err(e) => make_error_reply("DaemonError", &format!("{}", e)), }