Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
xpc-sys
Various utilities for conveniently dealing with XPC in Rust.
Getting Started
Conversions to/from Rust/XPC objects uses the xpc.h functions documented on Apple Developer using the From trait. Complex types such as arrays and shared memory objects described in greater detail below.
| 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 |
Make XPC objects for anything with From<T>. Make sure to use the correct type for file descriptors and Mach ports:
let mut message: = new;
message.insert;
Go from an XPC object to value via the TryXPCValue trait. It checks your object's type via xpc_get_type() and yields a clear error if you're using the wrong type:
Object lifecycle
XPCObject wraps a xpc_object_t:
;
When it is dropped, xpc_release is called.
NOTE: When using Objective-C blocks with the block crate (e.g. looping over an array), make sure to invoke xpc_retain() on any object you wish to keep after the block is dropped, or else the XPC objects in the block will be dropped as well! See the XPCDictionary implementation for more details. xpc-sys handles this for you for its conversions.
QueryBuilder
While we can go from HashMap<&str, XPCObject> to XPCObject, it can be a little verbose. A QueryBuilder trait exposes some builder methods to make building an XPC dictionary a little easier (without all of the into()s, and some additional error checking).
To write the query for launchctl list:
let LIST_SERVICES: XPCDictionary = new
// "list com.apple.Spotlight" (if specified)
// .entry("name", "com.apple.Spotlight");
.entry
.entry
.entry
.entry;
let reply: = new
// LIST_SERVICES is a proto
.extend
// Specify the domain type, or fall back on requester domain
.with_domain_type_or_default
.entry_if_present
.pipe_routine_with_error_handling;
In addition to checking errno is 0, pipe_routine_with_error_handling also looks for possible error and errors keys in the response dictionary and provides an Err() with xpc_strerror contents.
XPC Dictionary
Go from a HashMap to xpc_object_t with the XPCObject type:
let mut message: = new;
message.insert;
message.insert;
message.insert;
message.insert;
message.insert;
let xpc_object: XPCObject = message.into;
Call xpc_pipe_routine and receive Result<XPCObject, XPCError>:
let xpc_object: XPCObject = message.into;
match xpc_object.pipe_routine
The response is likely an XPC dictionary -- go back to a HashMap:
let xpc_object: XPCObject = message.into;
let response: = xpc_object
.pipe_routine
.and_then;
let XPCDictionary = response.unwrap;
let whatever = hm.get;
Response dictionaries can be nested, so XPCDictionary has a helper included for this scenario:
let xpc_object: XPCObject = message.into;
// A string: either "Aqua", "StandardIO", "Background", "LoginWindow", "System"
let response: = xpc_object
.pipe_routine
.and_then;
.and_then
Or, retrieve the service key (a child XPC Dictionary) from this response:
let xpc_object: XPCObject = message.into;
// A string: either "Aqua", "StandardIO", "Background", "LoginWindow", "System"
let response: = xpc_object
.pipe_routine
.and_then;
.and_then
XPC Array
An XPC array can be made from either Vec<XPCObject> or Vec<Into<XPCObject>>:
let xpc_array = from;
let xpc_array = from;
Go back to Vec using xpc_value:
let rs_vec: = xpc_array.xpc_value.unwrap;
XPC Shmem
Make XPC shared memory objects by providing a size and vm_allocate/mmap flags. vm_allocate is used to create the memory region, and vm_deallocate when XPCShmem is dropped.
let shmem = new_task_self?;
// Use as _xpc_type_shmem argument in XPCDictionary
let response = new
.extend
.entry
.pipe_routine_with_error_handling?;
To work with the shmem region, use slice_from_raw_parts:
let bytes: & = unsafe ;
// Make a string from bytes in the shmem
let mut hey_look_a_string = Stringnew;
bytes.read_to_string;
Credits
A big thanks to these open source projects and general resources:
- block Obj-C block support, necessary for any XPC function taking
xpc_*_applier_t - Cursive TUI
- tokio ASIO
- plist Parsing & validation for XML and binary plists
- notify fsnotify
- bitflags
- libc
- lazy_static
- xcrun
- Apple Developer XPC services
- Apple Developer XPC API reference
- MOXIL / launjctl
- geosnow - A Long Evening With macOS' sandbox
- Bits of launchd - @5aelo
- Audit tokens explained (e.g. ASID)
- objc.io XPC guide
- The various source links found in comments, from Chrome's sandbox and other headers with definitions for private API functions.
- Last but not least, this is Apple's launchd after all, right :>)? I did not know systemd was inspired by launchd until I read this HN comment, which sent me down this eventual rabbit hole :)
Everything else (C) David Stancu & Contributors 2021