Add missing usbmuxd FFI methods

This commit is contained in:
Jackson Coxson
2025-10-18 22:10:14 -06:00
parent ea61459df2
commit c10f4da9f1

View File

@@ -2,6 +2,7 @@
use std::{
ffi::{CStr, CString, c_char},
pin::Pin,
ptr::null_mut,
};
@@ -9,15 +10,19 @@ use crate::{
IdeviceFfiError, IdeviceHandle, IdevicePairingFile, RUNTIME, ffi_err,
util::{SockAddr, c_socket_to_rust, idevice_sockaddr, idevice_socklen_t},
};
use futures::{Stream, StreamExt};
use idevice::{
IdeviceError,
usbmuxd::{UsbmuxdAddr, UsbmuxdConnection, UsbmuxdDevice},
usbmuxd::{UsbmuxdAddr, UsbmuxdConnection, UsbmuxdDevice, UsbmuxdListenEvent},
};
use log::error;
pub struct UsbmuxdConnectionHandle(pub UsbmuxdConnection);
pub struct UsbmuxdAddrHandle(pub UsbmuxdAddr);
pub struct UsbmuxdDeviceHandle(pub UsbmuxdDevice);
pub struct UsbmuxdListenerHandle<'a>(
Pin<Box<dyn Stream<Item = Result<UsbmuxdListenEvent, IdeviceError>> + 'a>>,
);
/// Connects to a usbmuxd instance over TCP
///
@@ -301,6 +306,153 @@ pub unsafe extern "C" fn idevice_usbmuxd_get_pair_record(
}
}
/// Saves the pairing record for a given device UDID.
///
/// # Arguments
/// * `usbmuxd_conn` - A valid connection to usbmuxd.
/// * `device_id` - The muxer ID for the device
/// * `udid` - The UDID of the device.
/// * `pair_record` - The bytes of the pairing record plist to save
/// * `pair_record_len` - the length of the pairing record bytes
///
/// # Returns
/// An `IdeviceFfiError` on error, `null` on success.
///
/// # Safety
/// * `usbmuxd_conn` must be a valid pointer.
/// * `udid` must be a valid, null-terminated C string.
/// * `pair_record` must be a valid, non-null pointer.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn idevice_usbmuxd_save_pair_record(
usbmuxd_conn: *mut UsbmuxdConnectionHandle,
device_id: u32,
udid: *const c_char,
pair_record: *mut u8,
pair_record_len: usize,
) -> *mut IdeviceFfiError {
if usbmuxd_conn.is_null() || pair_record.is_null() {
return ffi_err!(IdeviceError::FfiInvalidArg);
}
let conn = unsafe { &mut (*usbmuxd_conn).0 };
let pair_record =
unsafe { std::slice::from_raw_parts_mut(pair_record, pair_record_len) }.to_vec();
let udid_str = unsafe {
match CStr::from_ptr(udid).to_str() {
Ok(s) => s,
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
}
};
let res = RUNTIME.block_on(async {
conn.save_pair_record(device_id, udid_str, pair_record)
.await
});
match res {
Ok(_) => null_mut(),
Err(e) => ffi_err!(e),
}
}
/// Listens on the socket for connections and disconnections
///
/// # Safety
/// Pass valid pointers. Free the stream with ``idevice_usbmuxd_listener_handle_free``.
/// The stream must outlive the usbmuxd connection, and the usbmuxd connection cannot
/// be used for other requests.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn idevice_usbmuxd_listen(
usbmuxd_conn: *mut UsbmuxdConnectionHandle,
stream_handle: *mut *mut UsbmuxdListenerHandle,
) -> *mut IdeviceFfiError {
if usbmuxd_conn.is_null() || stream_handle.is_null() {
return ffi_err!(IdeviceError::FfiInvalidArg);
}
let conn = unsafe { &mut (*usbmuxd_conn).0 };
let res = RUNTIME.block_on(async { conn.listen().await });
match res {
Ok(s) => {
unsafe { *stream_handle = Box::into_raw(Box::new(UsbmuxdListenerHandle(s))) };
null_mut()
}
Err(e) => ffi_err!(e),
}
}
/// Frees a stream created by ``listen`` or does nothing on null
///
/// # Safety
/// Pass a valid pointer.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn idevice_usbmuxd_listener_handle_free(
stream_handle: *mut UsbmuxdListenerHandle,
) {
if stream_handle.is_null() {
return;
}
let _ = unsafe { Box::from_raw(stream_handle) };
}
/// Gets the next event from the stream.
/// Connect will be set to true if the event is a connection event,
/// and the connection_device will be filled with the device information.
/// If connection is false, the mux ID of the device will be filled.
///
/// # Arguments
/// * `stream_handle` - The handle to the stream returned by listen
/// * `connect` - The bool that will be set
/// * `connection_device` - The pointer that will be filled on a connect event
/// * `disconnection_id` - The mux ID that will be set on a disconnect event
///
/// # Safety
/// Pass valid pointers
#[unsafe(no_mangle)]
pub unsafe extern "C" fn idevice_usbmuxd_listener_next(
stream_handle: *mut UsbmuxdListenerHandle,
connect: *mut bool,
connection_device: *mut *mut UsbmuxdDeviceHandle,
disconnection_id: *mut u32,
) -> *mut IdeviceFfiError {
if stream_handle.is_null()
|| connect.is_null()
|| connection_device.is_null()
|| disconnection_id.is_null()
{
return ffi_err!(IdeviceError::FfiInvalidArg);
}
let stream = unsafe { &mut (*stream_handle).0 };
let res = RUNTIME.block_on(async { stream.next().await });
match res {
Some(res) => match res {
Ok(s) => {
match s {
UsbmuxdListenEvent::Connected(usbmuxd_device) => {
unsafe { *connect = true };
unsafe {
*connection_device =
Box::into_raw(Box::new(UsbmuxdDeviceHandle(usbmuxd_device)))
};
}
UsbmuxdListenEvent::Disconnected(id) => unsafe { *disconnection_id = id },
}
null_mut()
}
Err(e) => ffi_err!(e),
},
None => {
ffi_err!(IdeviceError::Socket(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"end of stream"
)))
}
}
}
/// Reads the BUID (Boot-Unique ID) from usbmuxd.
///
/// The returned string must be freed with `idevice_string_free`.