11 个版本
0.5.1 | 2024 年 4 月 15 日 |
---|---|
0.5.0 | 2023 年 12 月 21 日 |
0.4.2 | 2023 年 11 月 17 日 |
0.4.1 | 2023 年 8 月 13 日 |
0.1.0 | 2021 年 5 月 29 日 |
#24 in macOS 和 iOS API
90 每月下载量
61KB
1K SLoC
xpc-sys
用于方便地在 Rust 中处理 XPC 的各种实用工具。
入门指南
将 Rust/XPC 对象之间进行转换使用在 Apple 开发者上记录的 xpc.h 函数 通过 From
特性。以下将更详细地描述复杂类型,如数组和共享内存对象。
Rust | XPC |
---|---|
i64 | _xpc_type_int64 |
u64 | _xpc_type_uint64 |
f64 | _xpc_type_double |
bool | _xpc_bool_true/false |
Into | _xpc_type_string |
HashMap<Into, Into> | _xpc_type_dictionary |
Vec<Into> | _xpc_type_array |
std::os::unix::prelude::RawFd | _xpc_type_fd |
(MachPortType::Send, mach_port_t) | _xpc_type_mach_send |
(MachPortType::Recv, mach_port_t) | _xpc_type_mach_recv |
XPCShmem | _xpc_type_shmem |
使用 From<T>
为任何东西创建 XPC 对象。请确保为文件描述符和 Mach 端口使用正确的类型
let mut message: HashMap<&str, XPCObject> = HashMap::new();
message.insert(
"domain-port",
XPCObject::from((MachPortType::Send, unsafe {
get_bootstrap_port() as mach_port_t
})),
);
通过 TryXPCValue
特性将 XPC 对象转换为值。它通过 xpc_get_type()
检查你的对象类型,并在使用错误类型时提供清晰的错误
#[test]
fn deserialize_as_wrong_type() {
let an_i64: XPCObject = XPCObject::from(42 as i64);
let as_u64: Result<u64, XPCError> = an_i64.xpc_value();
assert_eq!(
as_u64.err().unwrap(),
XPCValueError("Cannot get int64 as uint64".to_string())
);
}
对象生命周期
XPCObject 封装了一个 xpc_object_t
pub struct XPCObject(pub xpc_object_t, pub XPCType);
当它被丢弃时,将调用 xpc_release
。
QueryBuilder
虽然我们可以从 HashMap<&str, XPCObject>
转换到 XPCObject
,但这可能有点冗长。一个 QueryBuilder
特性公开了一些构建方法,使构建 XPC 字典变得更容易(无需所有 into()
,并进行了额外的错误检查)。
编写 launchctl list
的查询
let LIST_SERVICES: XPCDictionary = XPCDictionary::new()
// "list com.apple.Spotlight" (if specified)
// .entry("name", "com.apple.Spotlight");
.entry("subsystem", 3 as u64)
.entry("handle", 0 as u64)
.entry("routine", 815 as u64)
.entry("legacy", true);
let reply: Result<XPCDictionary, XPCError> = XPCDictionary::new()
// LIST_SERVICES is a proto
.extend(&LIST_SERVICES)
// Specify the domain type, or fall back on requester domain
.with_domain_type_or_default(Some(domain_type))
.entry_if_present("name", name)
.pipe_routine_with_error_handling();
除了检查 errno
是否为 0,pipe_routine_with_error_handling
还会在响应字典中查找可能的 error
和 errors
键,并提供一个包含 xpc_strerror
内容的 Err()
。
XPC 字典
使用 XPCObject
类型将 HashMap
转换为 xpc_object_t
let mut message: HashMap<&str, XPCObject> = HashMap::new();
message.insert("type", XPCObject::from(1 as u64));
message.insert("handle", XPCObject::from(0 as u64));
message.insert("subsystem", XPCObject::from(3 as u64));
message.insert("routine", XPCObject::from(815 as u64));
message.insert("legacy", XPCObject::from(true));
let xpc_object: XPCObject = message.into();
调用 xpc_pipe_routine
并接收 Result<XPCObject, XPCError>
let xpc_object: XPCObject = message.into();
match xpc_object.pipe_routine() {
Ok(xpc_object) => { /* do stuff and things */ },
Err(XPCError::PipeError(err)) => { /* err is a string w/strerror(errno) */ }
}
响应可能是一个 XPC 字典 -- 返回到 HashMap
let xpc_object: XPCObject = message.into();
let response: Result<XPCDictionary, XPCError> = xpc_object
.pipe_routine()
.and_then(|r| r.try_into());
let XPCDictionary(hm) = response.unwrap();
let whatever = hm.get("...");
响应字典可以嵌套,因此 XPCDictionary
包含一个用于此场景的辅助器
let xpc_object: XPCObject = message.into();
// A string: either "Aqua", "StandardIO", "Background", "LoginWindow", "System"
let response: Result<String, XPCError> = xpc_object
.pipe_routine()
.and_then(|r: XPCObject| r.try_into());
.and_then(|d: XPCDictionary| d.get(&["service", "LimitLoadToSessionType"])
.and_then(|lltst: XPCObject| lltst.xpc_value());
或者,从响应中检索 service
键(一个子 XPC 字典)
let xpc_object: XPCObject = message.into();
// A string: either "Aqua", "StandardIO", "Background", "LoginWindow", "System"
let response: Result<XPCDictionary, XPCError> = xpc_object
.pipe_routine()
.and_then(|r: XPCObject| r.try_into());
.and_then(|d: XPCDictionary| d.get_as_dictionary(&["service"]);
let XPCDictionary(hm) = response.unwrap();
let whatever = hm.get("...");
XPC 数组
可以从 Vec<XPCObject>
或 Vec<Into<XPCObject>>
let xpc_array = XPCObject::from(vec![XPCObject::from("eins"), XPCObject::from("zwei"), XPCObject::from("polizei")]);
let xpc_array = XPCObject::from(vec!["eins", "zwei", "polizei"]);
使用 xpc_value
返回到 Vec
let rs_vec: Vec<XPCObject> = xpc_array.xpc_value().unwrap();
XPC Shmem
通过提供大小和 vm_allocate/mmap 标志来创建 XPC 共享内存对象。使用 vm_allocate
创建内存区域,当 XPCShmem
释放时使用 vm_deallocate
。
let shmem = XPCShmem::new_task_self(
0x1400000,
i32::try_from(MAP_SHARED).expect("Must conv flags"),
)?;
// Use as _xpc_type_shmem argument in XPCDictionary
let response = XPCDictionary::new()
.extend(&DUMPSTATE)
.entry("shmem", shmem.xpc_object.clone())
.pipe_routine_with_error_handling()?;
要使用 shmem 区域,请使用 slice_from_raw_parts
let bytes: &[u8] = unsafe {
&*slice_from_raw_parts(shmem.region as *mut u8, size)
};
// Make a string from bytes in the shmem
let mut hey_look_a_string = String::new();
bytes.read_to_string(buf);
致谢
非常感谢这些开源项目和一般资源
- block Obj-C block 支持,对于任何需要
xpc_*_applier_t
的 XPC 函数都是必要的 - Cursive
- tokio
- plist
- notify
- bitflags
- libc
- lazy_static
- xcrun
- Apple Developer XPC services
- Apple Developer XPC API reference
- MOXIL / launjctl
- geosnow - 与 macOS 的 sandbox 共度的一个漫长夜晚
- launchd 的片段 - @5aelo
- 审计令牌解释(例如 ASID)
- objc.io XPC 指南
- Fortinet XPC RE 文章
- 在注释中找到的各种源链接,从 Chrome 的 sandbox 到具有私有 API 函数定义的其他头文件。
- 毕竟,这是 Apple 的 launchd :>)
其他所有内容(C)David Stancu 及贡献者 2021
依赖关系
~0.2–2MB
~42K SLoC