Implement pipes...

...also, add stub messages everywhere.

launchd now works again as of this commit.
This commit is contained in:
Ariel Abreu 2021-04-10 20:25:39 -04:00
parent 86a317edbf
commit 90b934b336
No known key found for this signature in database
GPG Key ID: BB20848279B910AC
30 changed files with 1415 additions and 129 deletions

View File

@ -1,9 +1,12 @@
#ifndef XPC_LAUNCHD_H_
#define XPC_LAUNCHD_H_
#include <xpc/xpc.h>
#include <xpc/launchd_defs.h>
#include <launch.h>
#include <xpc/private/pipe.h>
#ifdef __cplusplus
extern "C" {
#endif
@ -14,15 +17,8 @@ extern "C" {
#define EXSRCH 3
#define EXMAX EXSRCH
XPC_IGNORE_DUPLICATE_PROTOCOL_PUSH;
XPC_DECL(xpc_pipe);
XPC_IGNORE_DUPLICATE_PROTOCOL_POP;
const char *xpc_strerror(int error);
xpc_object_t xpc_copy_entitlement_for_token(const char *, audit_token_t *);
int xpc_pipe_routine_reply(xpc_pipe_t pipe);
int xpc_pipe_try_receive(mach_port_t, xpc_object_t *, mach_port_t *,
boolean_t (*)(mach_msg_header_t *, mach_msg_header_t *), mach_msg_size_t, int);
kern_return_t xpc_call_wakeup(mach_port_t, int);
void xpc_dictionary_get_audit_token(xpc_object_t, audit_token_t *);
void xpc_dictionary_set_mach_recv(xpc_object_t, const char *, mach_port_t);

View File

@ -6,6 +6,8 @@
// `notify_client.c` includes `xpc/private.h` and expects it to define `xpc_copy_entitlement_for_token`, which we have in `launchd.h`
#include <xpc/launchd.h>
#include <xpc/private/pipe.h>
#include <xpc/private/endpoint.h>
#ifdef __cplusplus
extern "C" {
@ -46,10 +48,6 @@ XPC_TYPE(_xpc_type_file_transfer);
int _xpc_runtime_is_app_sandboxed();
void xpc_pipe_invalidate(xpc_pipe_t pipe);
xpc_pipe_t xpc_pipe_create(const char* name, int flags);
xpc_object_t _od_rpc_call(const char *procname, xpc_object_t payload, xpc_pipe_t (*get_pipe)(bool));
xpc_object_t xpc_create_with_format(const char * format, ...);
@ -59,18 +57,11 @@ xpc_object_t xpc_create_reply_with_format(xpc_object_t original, const char * fo
xpc_object_t xpc_create_from_plist(void *data, size_t size);
void xpc_dictionary_get_audit_token(xpc_object_t, audit_token_t *);
int xpc_pipe_routine(xpc_pipe_t pipe, xpc_object_t payload, xpc_object_t* reply);
void xpc_connection_set_target_uid(xpc_connection_t connection, uid_t uid);
void xpc_connection_set_instance(xpc_connection_t connection, uuid_t uid);
void xpc_dictionary_set_mach_send(xpc_object_t object, const char* key, mach_port_t port);
// Completely random. Not sure what the "actual" one is
#define XPC_PIPE_PRIVILEGED 7
#define XPC_PIPE_USE_SYNC_IPC_OVERRIDE 8
#define XPC_PIPE_FLAG_PRIVILEGED XPC_PIPE_PRIVILEGED
xpc_object_t xpc_connection_copy_entitlement_value(xpc_connection_t connection, const char* entitlement);
void xpc_transaction_exit_clean();

View File

@ -0,0 +1,12 @@
#ifndef _XPC_PRIVATE_ENDPOINT_H_
#define _XPC_PRIVATE_ENDPOINT_H_
#include <xpc/xpc.h>
#include <xpc/endpoint.h>
int xpc_endpoint_compare(xpc_endpoint_t xlhs, xpc_endpoint_t xrhs);
mach_port_t xpc_endpoint_copy_listener_port_4sim(xpc_endpoint_t xendpoint);
xpc_endpoint_t xpc_endpoint_create_bs_named(const char* name, uint64_t flags, uint8_t* out_type);
xpc_endpoint_t xpc_endpoint_create_mach_port_4sim(mach_port_t port);
#endif //_XPC_PRIVATE_ENDPOINT_H_

View File

@ -0,0 +1,52 @@
#ifndef _XPC_PRIVATE_PIPE_H_
#define _XPC_PRIVATE_PIPE_H_
// NOTE: i have no clue if this header exists in the real libxpc,
// but it seems logical to keep all the xpc_pipe stuff in one place
// (rather than dumping it all into `private.h`)
#include <xpc/xpc.h>
#ifdef __cplusplus
extern "C" {
#endif
XPC_IGNORE_DUPLICATE_PROTOCOL_PUSH;
XPC_DECL(xpc_pipe);
XPC_IGNORE_DUPLICATE_PROTOCOL_POP;
// verified correct
#define XPC_PIPE_PRIVILEGED (1 << 1)
#define XPC_PIPE_USE_SYNC_IPC_OVERRIDE (1 << 2)
// not sure what these are called or what they do, but they definitely exist
#define XPC_PIPE_SOME_OTHER_FLAG_1 (1 << 3)
#define XPC_PIPE_SOME_OTHER_FLAG_2 (1 << 4)
#define XPC_PIPE_FLAG_PRIVILEGED XPC_PIPE_PRIVILEGED
// not sure about the name, but i'm sure of the value and purpose
#define XPC_PIPE_RECEIVE_FLAG_NONBLOCKING (1 << 0)
xpc_pipe_t xpc_pipe_create(const char* name, uint64_t flags);
xpc_pipe_t xpc_pipe_create_from_port(mach_port_t port, uint64_t flags);
void xpc_pipe_invalidate(xpc_pipe_t xpipe);
int xpc_pipe_routine(xpc_pipe_t xpipe, xpc_object_t xdict, xpc_object_t* reply);
int xpc_pipe_routine_with_flags(xpc_pipe_t xpipe, xpc_object_t xdict, xpc_object_t* reply, uint64_t flags);
int xpc_pipe_routine_reply(xpc_object_t xdict);
int xpc_pipe_routine_async(xpc_pipe_t xpipe, xpc_object_t xdict, mach_port_t local_reply_port);
int xpc_pipe_routine_forward(xpc_pipe_t xpipe, xpc_object_t xdict);
int xpc_pipe_simpleroutine(xpc_pipe_t xpipe, xpc_object_t xdict);
int xpc_pipe_receive(mach_port_t receive_port, xpc_object_t* out_msg, uint64_t flags);
int xpc_pipe_try_receive(mach_port_t port, xpc_object_t* out_object, mach_port_t* out_local_port, boolean_t (*demuxer)(mach_msg_header_t* request, mach_msg_header_t* reply), mach_msg_size_t max_mig_reply_size, uint64_t flags);
int _xpc_pipe_handle_mig(mach_msg_header_t* request, mach_msg_header_t* reply, boolean_t (*demuxer)(mach_msg_header_t* request, mach_msg_header_t* reply));
#ifdef __cplusplus
};
#endif
#endif // _XPC_PRIVATE_PIPE_H_

View File

@ -3,16 +3,56 @@
#import <xpc/objects/base.h>
#include <mach/mach.h>
#include <pthread/pthread.h>
XPC_IGNORE_DUPLICATE_PROTOCOL_PUSH;
XPC_CLASS_DECL(pipe);
XPC_IGNORE_DUPLICATE_PROTOCOL_POP;
typedef boolean_t (*xpc_pipe_mig_demux_f)(mach_msg_header_t* request, mach_msg_header_t* reply);
OS_ENUM(xpc_pipe_state, uint8_t,
xpc_pipe_state_initial,
xpc_pipe_state_active,
xpc_pipe_state_broken,
);
struct xpc_pipe_s {
struct xpc_object_s base;
mach_port_t checkin_port;
mach_port_t send_port;
mach_port_t recv_port;
pthread_rwlock_t state_lock;
xpc_pipe_state_t state;
};
@class XPC_CLASS(dictionary);
@interface XPC_CLASS_INTERFACE(pipe)
/**
* Whether the pipe is broken.
*
* @note The setter for this property is one-way: once it is set to `YES`, it cannot be unset.
*/
@property(assign) BOOL broken;
+ (int)receiveWithPort: (mach_port_t)port incomingMessage: (xpc_object_t*)incomingMessage flags: (uint64_t)flags;
+ (int)tryReceiveWithPort: (mach_port_t)port incomingMessage: (xpc_object_t*)incomingMessage replyPort: (mach_port_t*)replyPort maximumMIGReplySize: (size_t)maximumMIGReplySize flags:(uint64_t)flags demuxer:(xpc_pipe_mig_demux_f)demuxer;
+ (int)sendReply: (XPC_CLASS(dictionary)*)message;
+ (int)demux: (mach_msg_header_t*)request reply: (mach_msg_header_t*)reply demuxer: (xpc_pipe_mig_demux_f)demuxer;
- (instancetype)initForService: (const char*)serviceName withFlags: (uint64_t)flags;
- (instancetype)initWithPort: (mach_port_t)port flags: (uint64_t)flags;
- (int)sendMessage: (XPC_CLASS(dictionary)*)message withSynchronousReply: (xpc_object_t*)reply flags: (uint64_t)flags;
- (int)sendMessage: (XPC_CLASS(dictionary)*)message withReplyPort: (mach_port_t)replyPort;
- (int)forwardMessage: (XPC_CLASS(dictionary)*)message;
- (int)sendMessage: (XPC_CLASS(dictionary)*)message;
- (void)invalidate;
@end
#endif // _XPC_OBJECTS_PIPE_H_

View File

@ -259,4 +259,14 @@ void _xpc_logv(const char* function, const char* file, size_t line, xpc_log_prio
*/
#define xpc_logv(...) _xpc_logv(__PRETTY_FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
/**
* Prints a message indicating that a stub was called.
*/
void _xpc_stub(const char* function, const char* file, size_t line);
/**
* Prints a message indicating that a stub was called. This macro automatically fills in the arguments to `_xpc_stub`.
*/
#define xpc_stub() _xpc_stub(__PRETTY_FUNCTION__, __FILE__, __LINE__)
#endif // _XPC_UTIL_H_

View File

@ -116,37 +116,41 @@ XPC_CLASS_HEADER(activity);
XPC_EXPORT
void xpc_activity_register(const char *identifier, xpc_object_t criteria, xpc_activity_handler_t handler) {
xpc_stub();
};
XPC_EXPORT
xpc_object_t xpc_activity_copy_criteria(xpc_activity_t activity) {
xpc_stub();
return NULL;
};
XPC_EXPORT
void xpc_activity_set_criteria(xpc_activity_t activity, xpc_object_t criteria) {
xpc_stub();
};
XPC_EXPORT
xpc_activity_state_t xpc_activity_get_state(xpc_activity_t activity) {
xpc_stub();
return 0;
};
XPC_EXPORT
bool xpc_activity_set_state(xpc_activity_t activity, xpc_activity_state_t state) {
xpc_stub();
return false;
};
XPC_EXPORT
bool xpc_activity_should_defer(xpc_activity_t activity) {
xpc_stub();
return false;
};
XPC_EXPORT
void xpc_activity_unregister(const char* identifier) {
xpc_stub();
};
//
@ -159,23 +163,26 @@ XPC_EXPORT
struct xpc_activity_eligibility_changed_handler_s* xpc_activity_add_eligibility_changed_handler(xpc_activity_t xactivity, void (^handler)()) {
// the return type is some kind of array or structure that must be freed
// no clue what the handler parameters are
xpc_stub();
return NULL;
};
XPC_EXPORT
dispatch_queue_t xpc_activity_copy_dispatch_queue(xpc_activity_t xactivity) {
xpc_stub();
return NULL;
};
XPC_EXPORT
char* xpc_activity_copy_identifier(xpc_activity_t xactivity) {
// returns a string that must be freed
xpc_stub();
return NULL;
};
XPC_EXPORT
void xpc_activity_debug(const char* identifier, uint64_t flags) {
xpc_stub();
};
XPC_EXPORT
@ -202,22 +209,22 @@ int xpc_activity_get_percentage() {
XPC_EXPORT
void xpc_activity_list(const char* identifier) {
xpc_stub();
};
XPC_EXPORT
void xpc_activity_remove_eligibility_changed_handler(xpc_activity_t xactivity, struct xpc_activity_eligibility_changed_handler_s* handler_context) {
xpc_stub();
};
XPC_EXPORT
void xpc_activity_run(const char* identifier) {
xpc_stub();
};
XPC_EXPORT
void xpc_activity_set_completion_status(xpc_activity_t activity, uint64_t status) {
xpc_stub();
};
XPC_EXPORT

View File

@ -18,96 +18,110 @@ XPC_CLASS_HEADER(bundle);
XPC_EXPORT
xpc_object_t xpc_bundle_copy_info_dictionary(xpc_bundle_t xbundle) {
// doesn't actually copy the dictionary; only retains it
xpc_stub();
return NULL;
};
XPC_EXPORT
char* xpc_bundle_copy_resource_path(xpc_bundle_t xbundle) {
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_bundle_copy_services(xpc_bundle_t xbundle) {
// doesn't actually copy the array; only retains it
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_bundle_t xpc_bundle_create(const char* path, unsigned int flags) {
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_bundle_t xpc_bundle_create_from_origin(unsigned int origin, const char* path) {
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_bundle_t xpc_bundle_create_main(void) {
xpc_stub();
return NULL;
};
XPC_EXPORT
int xpc_bundle_get_error(xpc_bundle_t xbundle) {
// unsure about the return type
xpc_stub();
return -1;
};
XPC_EXPORT
const char* xpc_bundle_get_executable_path(xpc_bundle_t xbundle) {
// does NOT copy the path it returns
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_bundle_get_info_dictionary(xpc_bundle_t xbundle) {
xpc_stub();
return NULL;
};
XPC_EXPORT
const char* xpc_bundle_get_path(xpc_bundle_t xbundle) {
// does NOT copy the path it returns
xpc_stub();
return NULL;
};
XPC_EXPORT
uint64_t xpc_bundle_get_property(xpc_bundle_t xbundle, unsigned int property) {
// unsure about the return type; it can actually return both integers and pointers
xpc_stub();
return 0;
};
XPC_EXPORT
xpc_object_t xpc_bundle_get_xpcservice_dictionary(xpc_bundle_t xbundle) {
// the dictionary is contained within the info dictionary
xpc_stub();
return NULL;
};
XPC_EXPORT
void xpc_bundle_populate(xpc_bundle_t xbundle, xpc_object_t info_dictionary, xpc_object_t services_array) {
xpc_stub();
};
XPC_EXPORT
void xpc_bundle_resolve(xpc_bundle_t xbundle, dispatch_queue_t queue_for_later, void* something, void* something_else) {
// no clue what `something` and `something_else` are
xpc_stub();
};
XPC_EXPORT
void xpc_bundle_resolve_on_queue(xpc_bundle_t xbundle, dispatch_queue_t queue_for_later, dispatch_queue_t queue_for_now, void* something, void* something_else) {
// no clue what `something` and `something_else` are
xpc_stub();
};
XPC_EXPORT
void xpc_bundle_resolve_sync(xpc_bundle_t xbundle) {
xpc_stub();
};
XPC_EXPORT
void xpc_add_bundle(const char* path, unsigned int flags) {
xpc_stub();
};
XPC_EXPORT
void xpc_add_bundles_for_domain(xpc_object_t domain, xpc_object_t bundles) {
xpc_stub();
};

View File

@ -1,4 +1,5 @@
#import <xpc/xpc.h>
#import <xpc/util.h>
XPC_EXPORT const char* XPC_COALITION_INFO_KEY_BUNDLE_IDENTIFIER = "bundle_identifier";
XPC_EXPORT const char* XPC_COALITION_INFO_KEY_CID = "cid";
@ -11,10 +12,12 @@ XPC_EXPORT const char* XPC_COALITION_INFO_KEY_RESOURCE_USAGE_BLOB = "resource-us
XPC_EXPORT
xpc_object_t xpc_coalition_copy_info(uint64_t cid) {
xpc_stub();
return NULL;
};
XPC_EXPORT
int xpc_coalition_history_pipe_async(int flags) {
xpc_stub();
return -1;
};

View File

@ -1013,21 +1013,25 @@ const char* xpc_connection_get_name(xpc_connection_t xconn) {
XPC_EXPORT
uid_t xpc_connection_get_euid(xpc_connection_t xconn) {
xpc_stub();
return UID_MAX;
};
XPC_EXPORT
gid_t xpc_connection_get_egid(xpc_connection_t xconn) {
xpc_stub();
return GID_MAX;
};
XPC_EXPORT
pid_t xpc_connection_get_pid(xpc_connection_t xconn) {
xpc_stub();
return -1;
};
XPC_EXPORT
au_asid_t xpc_connection_get_asid(xpc_connection_t xconn) {
xpc_stub();
return AU_ASSIGN_ASID;
};
@ -1055,12 +1059,12 @@ void xpc_connection_set_finalizer_f(xpc_connection_t xconn, xpc_finalizer_t fina
XPC_EXPORT
void xpc_connection_set_legacy(xpc_connection_t xconn) {
xpc_stub();
};
XPC_EXPORT
void xpc_connection_set_privileged(xpc_connection_t xconn) {
xpc_stub();
};
XPC_EXPORT
@ -1074,16 +1078,17 @@ void xpc_connection_activate(xpc_connection_t xconn) {
XPC_EXPORT
void xpc_connection_set_target_uid(xpc_connection_t xconn, uid_t uid) {
xpc_stub();
};
XPC_EXPORT
void xpc_connection_set_instance(xpc_connection_t xconn, uuid_t uuid) {
xpc_stub();
};
XPC_EXPORT
xpc_object_t xpc_connection_copy_entitlement_value(xpc_connection_t xconn, const char* entitlement) {
xpc_stub();
return NULL;
};
@ -1095,11 +1100,13 @@ XPC_EXPORT
void _xpc_connection_set_event_handler_f(xpc_connection_t xconn, void (*handler)(xpc_object_t event, void* context)) {
// unsure about the parameters to the handler
// maybe the second parameter to the handler is actually the connection object?
xpc_stub();
};
XPC_EXPORT
char* xpc_connection_copy_bundle_id(xpc_connection_t xconn) {
// returns a string that must be freed
xpc_stub();
return NULL;
};
@ -1110,27 +1117,28 @@ xpc_connection_t xpc_connection_create_listener(const char* name, dispatch_queue
XPC_EXPORT
void xpc_connection_enable_sim2host_4sim(xpc_connection_t xconn) {
xpc_stub();
};
XPC_EXPORT
void xpc_connection_enable_termination_imminent_event(xpc_connection_t xconn) {
xpc_stub();
};
XPC_EXPORT
void xpc_connection_get_audit_token(xpc_connection_t xconn, audit_token_t* out_token) {
xpc_stub();
};
XPC_EXPORT
uint8_t xpc_connection_get_bs_type(xpc_connection_t xconn) {
xpc_stub();
return 0;
};
XPC_EXPORT
void xpc_connection_get_instance(xpc_connection_t xconn, uint8_t* out_uuid) {
xpc_stub();
};
XPC_EXPORT
@ -1140,46 +1148,47 @@ bool xpc_connection_is_extension(xpc_connection_t xconn) {
XPC_EXPORT
void xpc_connection_kill(xpc_connection_t xconn, int signal) {
xpc_stub();
};
XPC_EXPORT
void xpc_connection_send_notification(xpc_connection_t xconn, xpc_object_t details) {
xpc_stub();
};
XPC_EXPORT
void xpc_connection_set_bootstrap(xpc_connection_t xconn, xpc_object_t bootstrap) {
xpc_stub();
};
XPC_EXPORT
void xpc_connection_set_bs_type(xpc_connection_t xconn, uint8_t type) {
xpc_stub();
};
XPC_EXPORT
void xpc_connection_set_event_channel(xpc_connection_t xconn, const char* channel_name) {
// parameter 2 is a guess
xpc_stub();
};
XPC_EXPORT
void xpc_connection_set_non_launching(xpc_connection_t xconn, bool non_launching) {
xpc_stub();
};
XPC_EXPORT
void xpc_connection_set_oneshot_instance(xpc_connection_t xconn, const uint8_t* uuid) {
xpc_stub();
};
XPC_EXPORT
void xpc_connection_set_qos_class_fallback(xpc_connection_t xconn, dispatch_qos_class_t qos_class) {
xpc_stub();
};
XPC_EXPORT
void xpc_connection_set_qos_class_floor(xpc_connection_t xconn, dispatch_qos_class_t qos_class, int relative_priority) {
xpc_stub();
};

View File

@ -158,6 +158,7 @@ mach_port_t xpc_endpoint_copy_listener_port_4sim(xpc_endpoint_t xendpoint) {
XPC_EXPORT
xpc_endpoint_t xpc_endpoint_create_bs_named(const char* name, uint64_t flags, uint8_t* out_type) {
// parameter 3's purpose is a guess
xpc_stub();
return NULL;
};

View File

@ -1,4 +1,5 @@
#import <xpc/xpc.h>
#import <xpc/util.h>
// actually, the event publisher is it's own class
typedef xpc_object_t xpc_event_publisher_t;
@ -12,21 +13,24 @@ XPC_EXPORT const char* const _xpc_event_key_stream_name = "XPCEventStreamName";
XPC_EXPORT
void xpc_event_publisher_activate(xpc_event_publisher_t xpub) {
xpc_stub();
};
XPC_EXPORT
xpc_object_t xpc_event_publisher_create(const char* name, void* some_parameter) {
xpc_stub();
return NULL;
};
XPC_EXPORT
int xpc_event_publisher_fire(xpc_event_publisher_t xpub, uint64_t token, xpc_object_t details) {
xpc_stub();
return -1;
};
XPC_EXPORT
int xpc_event_publisher_fire_noboost(xpc_event_publisher_t xpub, uint64_t token, xpc_object_t details) {
xpc_stub();
return -1;
};
@ -34,17 +38,20 @@ XPC_EXPORT
int xpc_event_publisher_fire_with_reply() {
// i didn't feel like determining the parameters for this one
// should be similar to `xpc_event_publisher_fire`
xpc_stub();
return -1;
};
XPC_EXPORT
xpc_object_t xpc_event_publisher_fire_with_reply_sync() {
// should be similar to `xpc_event_publisher_fire_with_reply`
xpc_stub();
return NULL;
};
XPC_EXPORT
au_asid_t xpc_event_publisher_get_subscriber_asid(xpc_event_publisher_t xpub, uint64_t token) {
xpc_stub();
return -1;
};
@ -52,20 +59,24 @@ XPC_EXPORT
void xpc_event_publisher_set_error_handler(xpc_event_publisher_t xpub, void (^handler)()) {
// no clue what the parameters for the handler are
// (probably includes some sort of error parameter, but i have no clue what that is)
xpc_stub();
};
XPC_EXPORT
void xpc_event_publisher_set_handler(xpc_event_publisher_t xpub, void (^handler)()) {
// likewise, no clue what the parameters to the handler are
xpc_stub();
};
XPC_EXPORT
void xpc_event_publisher_set_initial_load_completed_handler_4remoted(xpc_event_publisher_t xpub, void (^handler)()) {
// once again, no clue about the handler parameters
xpc_stub();
};
XPC_EXPORT
int xpc_event_publisher_set_subscriber_keepalive(xpc_event_publisher_t xpub, uint64_t token, bool state) {
xpc_stub();
return -1;
};
@ -85,37 +96,40 @@ int xpc_event_stream_check_in2() {
XPC_EXPORT
int xpc_get_event_name(const char* stream, uint64_t token, char* out_name) {
xpc_stub();
return -1;
};
XPC_EXPORT
xpc_object_t xpc_copy_event(const char* stream, const char* name) {
// returns a dictionary
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_copy_event_entitlements(const char* stream, uint64_t token) {
// returns a dictionary
xpc_stub();
return NULL;
};
XPC_EXPORT
void xpc_set_event(const char* stream, const char* name, xpc_object_t descriptor) {
xpc_stub();
};
XPC_EXPORT
void xpc_set_event_state(const char* stream, uint64_t token, bool state) {
xpc_stub();
};
XPC_EXPORT
void xpc_set_event_stream_handler(const char* connection_name, dispatch_queue_t queue, void (^handler)(xpc_object_t event)) {
xpc_stub();
};
XPC_EXPORT
void xpc_set_event_with_flags(const char* stream, const char* name, xpc_object_t descriptor, uint64_t flags) {
xpc_stub();
};

View File

@ -1,5 +1,6 @@
#import <xpc/objects/file_transfer.h>
#import <xpc/xpc.h>
#import <xpc/util.h>
XPC_CLASS_SYMBOL_DECL(file_transfer);
@ -17,59 +18,70 @@ XPC_CLASS_HEADER(file_transfer);
XPC_EXPORT
void xpc_file_transfer_cancel(xpc_file_transfer_t xtransfer) {
// actually just crashes
xpc_stub();
};
XPC_EXPORT
dispatch_io_t xpc_file_transfer_copy_io(xpc_file_transfer_t xtransfer) {
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_file_transfer_t xpc_file_transfer_create_with_fd(int fd, void (^completionCallback)(void)) {
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_file_transfer_t xpc_file_transfer_create_with_path(const char* path, void (^completionCallback)(void)) {
xpc_stub();
return NULL;
};
XPC_EXPORT
uint64_t xpc_file_transfer_get_size(xpc_file_transfer_t xtransfer) {
// return type is either `uint64_t` or `size_t`
xpc_stub();
return 0;
};
XPC_EXPORT
uint64_t xpc_file_transfer_get_transfer_id(xpc_file_transfer_t xtransfer) {
xpc_stub();
return 0;
};
XPC_EXPORT
void xpc_file_transfer_send_finished(xpc_file_transfer_t xtransfer, bool succeeded) {
// unsure about the second parameter (both the return type and the purpose)
xpc_stub();
};
XPC_EXPORT
void xpc_file_transfer_set_transport_writing_callbacks(xpc_file_transfer_t xtransfer, void (^writeCallback)(void), void (^sendCallback)(void)) {
// parameters 2 & 3 are complete guesses;
// the only thing i know about them is that they're blocks
xpc_stub();
};
XPC_EXPORT
void xpc_file_transfer_write_finished(xpc_file_transfer_t xtransfer, bool succeeded) {
// i'm guessing this function is like `xpc_file_transfer_send_finished`
xpc_stub();
};
XPC_EXPORT
void* xpc_file_transfer_write_to_fd(xpc_file_transfer_t xtransfer, int fd, void (^someCallback)(void)) {
// no clue what the return type is
// also unsure about parameter 3's type
xpc_stub();
return NULL;
};
XPC_EXPORT
void* xpc_file_transfer_write_to_path(xpc_file_transfer_t xtransfer, const char* path, void (^someCallback)(void)) {
// same issues as `xpc_file_transfer_write_to_fd`
xpc_stub();
return NULL;
};

View File

@ -1,10 +1,13 @@
#import <xpc/xpc.h>
void xpc_stub_init(void);
extern void bootstrap_init(void); // in liblaunch
XPC_EXPORT
void _libxpc_initializer(void)
{
xpc_stub_init();
bootstrap_init();
}

View File

@ -1,5 +1,6 @@
#import <xpc/xpc.h>
#import <launch.h>
#import <xpc/util.h>
//
// private C API
@ -13,6 +14,7 @@ xpc_object_t _launch_msg2(xpc_object_t request, int type, uint64_t handle) {
// valid values for `type`: 0, 1, 2, 3
// `handle` can be a normal value or it can be UINT64_MAX (only matters when `type` == )
// returns a uint64 object
xpc_stub();
return NULL;
};
@ -21,11 +23,13 @@ int _launch_service_stats_copy_4ppse_impl(struct some_launch_service_stats_struc
// no clue what the structure layout is
// `type` MUST be 2 or else the function crashes
// the return type seems to be a status code
xpc_stub();
return -1;
};
XPC_EXPORT
int launch_activate_socket(const char* key, int** fds, size_t* count) {
xpc_stub();
return -1;
};
@ -33,67 +37,78 @@ XPC_EXPORT
int launch_add_external_service(int handle, const char* path, xpc_object_t overlay) {
// `overlay` is probably a dictionary
// no clue what type of value `handle` is other than some integer
xpc_stub();
return -1;
};
XPC_EXPORT
int launch_bootout_user_service_4coresim(const char* name) {
xpc_stub();
return -1;
};
XPC_EXPORT
xpc_object_t launch_copy_busy_extension_instances(const char** names, size_t name_count) {
// returns an array
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t launch_copy_endpoints_properties_for_pid(pid_t pid) {
// returns a dictionary
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t launch_copy_extension_properties(xpc_connection_t xconn) {
// returns a dictionary
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t launch_copy_extension_properties_for_pid(pid_t pid) {
// returns a dictionary
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t launch_copy_properties_for_pid_4assertiond(pid_t pid) {
// returns a dictionary
xpc_stub();
return NULL;
};
XPC_EXPORT
int launch_create_persona(uid_t uid, uint64_t flags) {
xpc_stub();
return -1;
};
XPC_EXPORT
int launch_destroy_persona(int handle) {
xpc_stub();
return -1;
};
XPC_EXPORT
int launch_disable_directory(const char* path) {
xpc_stub();
return -1;
};
XPC_EXPORT
int launch_enable_directory(const char* path) {
xpc_stub();
return -1;
};
XPC_EXPORT
void launch_extension_check_in_live_4UIKit(void) {
xpc_stub();
};
XPC_EXPORT const char* launch_extension_property_bundle_id = "XPCExtensionBundleIdentifier";
@ -106,17 +121,20 @@ XPC_EXPORT const char* launch_extension_property_xpc_bundle = "XPCExtensionXPCBu
XPC_EXPORT
int launch_get_service_enabled(const char* name, bool* out_loaded, bool* out_enabled) {
xpc_stub();
return -1;
};
XPC_EXPORT
int launch_get_system_service_enabled(const char* name, bool* out_loaded, bool* out_enabled) {
xpc_stub();
return -1;
};
XPC_EXPORT
char* launch_path_for_user_service_4coresim(const char* name) {
// returns a string that must be freed
xpc_stub();
return NULL;
};
@ -128,46 +146,54 @@ XPC_EXPORT const char* launch_perfcheck_property_endpoints = "XPCServiceEndpoint
XPC_EXPORT
void launch_remove_external_service(const char* name, const char* version, dispatch_queue_t queue, void (^callback)(int error)) {
xpc_stub();
};
XPC_EXPORT
int launch_service_stats_disable_4ppse(void) {
xpc_stub();
return -1;
};
XPC_EXPORT
int launch_service_stats_enable_4ppse(void) {
xpc_stub();
return -1;
};
XPC_EXPORT
bool launch_service_stats_is_enabled_4ppse() {
xpc_stub();
return -1;
};
XPC_EXPORT
int launch_set_service_enabled(const char* name, bool enabled) {
xpc_stub();
return -1;
};
XPC_EXPORT
int launch_set_system_service_enabled(const char* name, bool enabled) {
xpc_stub();
return -1;
};
XPC_EXPORT
char* launch_version_for_user_service_4coresim(const char* name) {
// returns a string that must be freed
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t ld2xpc(launch_data_t data) {
xpc_stub();
return NULL;
};
XPC_EXPORT
kern_return_t xpc_call_wakeup(mach_port_t port, int status) {
xpc_stub();
return -1;
};

View File

@ -1,4 +1,5 @@
#import <xpc/xpc.h>
#import <xpc/util.h>
#import <mach-o/dyld_priv.h>
#define __DISPATCH_INDIRECT__
#import <dispatch/mach_private.h>
@ -7,21 +8,25 @@
XPC_EXPORT
bool _availability_version_check(size_t version_count, dyld_build_version_t* versions) {
// i'm *pretty* sure the second argument is an array of `dyld_build_version_t`
xpc_stub();
return false;
};
XPC_EXPORT
int _system_ios_support_version_copy_string_sysctl(char* out_string) {
xpc_stub();
return -1;
};
XPC_EXPORT
bool _system_version_copy_string_plist(char* out_string) {
xpc_stub();
return false;
};
XPC_EXPORT
bool _system_version_copy_string_sysctl(char* out_string) {
xpc_stub();
return false;
};
@ -30,23 +35,27 @@ XPC_EXPORT uint32_t _system_version_fallback[] = { 10, 15, 0 };
XPC_EXPORT
bool _system_version_parse_string(const char* string, uint32_t* out_version) {
// `version` is an array of 3 `uint32_t`s (or `int`s)
xpc_stub();
return false;
};
XPC_EXPORT
int os_system_version_get_current_version(uint32_t* out_version) {
// parameter 1's type is a good guess, but i'm unsure
xpc_stub();
return -1;
};
XPC_EXPORT
int os_system_version_sim_get_current_host_version(uint32_t* out_version) {
// same as with `os_system_version_get_current_version`
xpc_stub();
return -1;
};
XPC_EXPORT
xpc_object_t _xpc_payload_create_from_mach_msg(dispatch_mach_msg_t msg, int some_flag) {
xpc_stub();
return NULL;
};
@ -54,65 +63,77 @@ XPC_EXPORT
void _xpc_spawnattr_pack_string(char* string_but_with_an_offset_of_AEh, uint32_t* offset, size_t* length, const char* string_to_pack) {
// `string_to_pack` is copied into `string_but_with_an_offset_of_AEh` after offsetting it by 0xae + `*offset`
// the length of `string_to_pack` (including the null terminator) is added to `*offset` and subtracted from `*length`
xpc_stub();
};
XPC_EXPORT
void _xpc_spawnattr_pack_string_fragment(char* string_but_with_an_offset_of_AEh, uint32_t* offset, size_t* length, const char* string_to_pack) {
// same as `_xpc_spawnattr_pack_string`, but doesn't include the null terminator in the math
xpc_stub();
};
XPC_EXPORT
const char* _xpc_spawnattr_unpack_string(const char* string, size_t length, uint32_t offset) {
xpc_stub();
return NULL;
};
XPC_EXPORT
char* _xpc_spawnattr_unpack_strings(char* string_but_with_an_offset_of_AEh, size_t length, uint32_t offset, const char** out_strings, size_t strings_array_size) {
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t place_hold_on_real_loginwindow(void) {
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_copy_entitlement_for_self(const char* name) {
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_copy_entitlement_for_token(const char* name, audit_token_t* token) {
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_copy_entitlements_data_for_token(audit_token_t* token) {
// returns a data object
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_copy_entitlements_for_pid(pid_t pid) {
// returns a dictionary
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_copy_entitlements_for_self(void) {
// returns a dictionary
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_copy_bootstrap(void) {
// returns a dictionary
xpc_stub();
return NULL;
};
XPC_EXPORT
char* xpc_copy_code_signing_identity_for_token(audit_token_t* token) {
// returns a string that must be freed
xpc_stub();
return NULL;
};
@ -120,6 +141,7 @@ XPC_EXPORT
xpc_object_t xpc_copy_domain(void) {
// returns a dictionary with a single entry:
// "pid": <pid of current process>
xpc_stub();
return NULL;
};
@ -128,6 +150,7 @@ xpc_object_t xpc_copy_extension_sdk_entry() {
// not a stub
// just returns NULL
// probably has parameters, but there's no way to tell what they are
xpc_stub();
return NULL;
};
@ -135,16 +158,18 @@ XPC_EXPORT
const char* xpc_exit_reason_get_label(int reason) {
// `reason` is actually an enum value
// this function just maps the values with their names
xpc_stub();
return NULL;
};
XPC_EXPORT
void xpc_generate_audit_token(pid_t i_think_this_is_a_pid, audit_token_t* token) {
xpc_stub();
};
XPC_EXPORT
xpc_endpoint_t xpc_get_attachment_endpoint(void) {
xpc_stub();
return NULL;
};
@ -155,6 +180,7 @@ XPC_EXPORT
struct some_return_remote_hooks_struct* xpc_install_remote_hooks(struct some_remote_hooks_struct* hooks) {
// the return struct is different from the input struct
// it's probably some hooks for the caller to call back into libxpc
xpc_stub();
return NULL;
};
@ -163,6 +189,7 @@ void xpc_set_idle_handler() {
// not a stub
// just does nothing
// probably has parameters, but there's no way to tell what they are
xpc_stub();
};
XPC_EXPORT
@ -170,17 +197,19 @@ bool xpc_test_symbols_exported() {
// not a stub
// just returns false
// probably has parameters, but there's no way to tell what they are
xpc_stub();
return false;
};
XPC_EXPORT
void xpc_track_activity(void) {
xpc_stub();
};
XPC_EXPORT
int xpc_receive_mach_msg(dispatch_mach_msg_t msg, bool end_transaction, voucher_t voucher, xpc_connection_t connection, xpc_object_t* out_object) {
// parameter 2's purpose is a guess
xpc_stub();
return -1;
};
@ -189,42 +218,50 @@ int xpc_receive_remote_msg(void* data, size_t data_length, bool some_flag, void*
// parameter 4 is unknown
// parameter 6 seems to be a callback, but i'm not sure if it's a raw function or a block (probably a block)
// i'm also unsure what the parameters for the callback are
xpc_stub();
return -1;
};
XPC_EXPORT
void* xpc_make_serialization(xpc_object_t object, size_t* out_serialization_length) {
xpc_stub();
return NULL;
};
XPC_EXPORT
void* xpc_make_serialization_with_ool(xpc_object_t object, size_t* out_serialization_length, uint64_t flags) {
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_create_from_serialization(void* data, size_t data_length) {
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_create_from_serialization_with_ool(void* data, size_t data_length, void (^oolCallback)()) {
// same issue with `oolCallback` as in `xpc_receive_remote_msg`
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_create_from_plist(void* data, size_t data_length) {
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_create_from_plist_descriptor(int fd, dispatch_queue_t queue) {
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_create_reply_with_format_and_arguments(xpc_object_t original, const char* format, va_list args) {
xpc_stub();
return NULL;
};
@ -239,6 +276,7 @@ xpc_object_t xpc_create_reply_with_format(xpc_object_t original, const char* for
XPC_EXPORT
xpc_object_t xpc_create_with_format_and_arguments(const char* format, va_list args) {
xpc_stub();
return NULL;
};

View File

@ -1,9 +1,28 @@
#import <xpc/objects/pipe.h>
#import <xpc/objects/dictionary.h>
#import <xpc/xpc.h>
#import <xpc/private.h>
#define __DISPATCH_INDIRECT__
#import <dispatch/mach_private.h>
#import <xpc/util.h>
#import <xpc/serialization.h>
#include <bootstrap_priv.h>
#include <errno.h>
#define DEFAULT_RECEIVE_SIZE 256
//
// implementation notes:
//
// Pipes share the exact same communication protocol as connections, including the checkin requirement,
// but implement a simpler, more synchronously-oriented API.
//
// From Apple's current pipe API, it seems that they can only be used as a client interface, not a server interface.
// There are, however, certain functions in the C API that are essentially class methods (i.e. they don't take a pipe instance as an argument)
// and most of these seem to be oriented for server use.
//
// One interesting "instance method" that stands out in contrast with the connection API is `xpc_pipe_routine_forward`.
// This function allows a service to act as a proxy, and any replies from the handling service are sent directly to the original client.
//
XPC_CLASS_SYMBOL_DECL(pipe);
@ -12,6 +31,747 @@ OS_OBJECT_NONLAZY_CLASS
XPC_CLASS_HEADER(pipe);
- (void)dealloc
{
XPC_THIS_DECL(pipe);
pthread_rwlock_destroy(&this->state_lock);
xpc_mach_port_release_send(this->checkin_port);
xpc_mach_port_release_send(this->send_port);
xpc_mach_port_release_receive(this->recv_port);
[super dealloc];
}
+ (int)handleMachMessageSendReturnCode: (mach_msg_return_t)ret withMessageHeader: (mach_msg_header_t*)header
{
int status = 0;
switch (ret) {
case MACH_SEND_NO_BUFFER: /* fallthrough */
case MACH_SEND_INVALID_DATA: /* fallthrough */
case MACH_SEND_INVALID_HEADER: /* fallthrough */
case MACH_SEND_INVALID_NOTIFY: /* fallthrough */
case MACH_SEND_INVALID_REPLY: /* fallthrough */
case MACH_SEND_INVALID_TRAILER: {
// for these, the message wasn't even touched, so we can safely destroy it
mach_msg_destroy(header);
status = EIO;
} break;
case MACH_SEND_INVALID_DEST: {
mach_msg_destroy(header);
status = EPIPE;
} break;
case MACH_SEND_TIMED_OUT:
case MACH_SEND_INTERRUPTED: {
// for these, the kernel has completely consumed the message, so we don't need to do anything
status = EIO;
} break;
case MACH_SEND_INVALID_MEMORY: /* fallthrough */
case MACH_SEND_INVALID_RIGHT: /* fallthrough */
case MACH_SEND_INVALID_TYPE: /* fallthrough */
case MACH_SEND_MSG_TOO_SMALL: {
// these are hard to clean up because the message may have been partially consumed by the kernel,
// so we just don't clean them up
xpc_log(XPC_LOG_WARNING, "pipe: message could not be cleaned up");
status = EIO;
} break;
case MACH_MSG_SUCCESS: {
status = 0;
} break;
}
return status;
}
+ (int)handleMachMessageReceiveReturnCode: (mach_msg_return_t)ret
{
int status = 0;
switch (ret) {
case MACH_RCV_INVALID_NAME: /* fallthrough */
case MACH_RCV_IN_SET: /* fallthrough */
case MACH_RCV_TIMED_OUT: /* fallthrough */
case MACH_RCV_INTERRUPTED: /* fallthrough */
case MACH_RCV_PORT_DIED: /* fallthrough */
case MACH_RCV_PORT_CHANGED: {
// nothing happened to the message
status = EIO;
} break;
case MACH_RCV_HEADER_ERROR: /* fallthrough */
case MACH_RCV_INVALID_NOTIFY: {
// message was dequeued and destroyed
status = EIO;
} break;
case MACH_RCV_TOO_LARGE: {
status = EAGAIN;
} break;
case MACH_RCV_BODY_ERROR: /* fallthrough */
case MACH_RCV_INVALID_DATA: {
// message was received
status = EIO;
} break;
case MACH_MSG_SUCCESS: {
status = 0;
} break;
}
return status;
}
+ (int)receiveMachMessageWithPort: (mach_port_t)port incomingMessage: (dispatch_mach_msg_t*)incomingMessage flags: (uint64_t)flags
{
int status = 0;
dispatch_mach_msg_t message = NULL;
size_t messageSize = DEFAULT_RECEIVE_SIZE + MAX_TRAILER_SIZE;
mach_msg_header_t* header = NULL;
mach_msg_return_t ret = MACH_MSG_SUCCESS;
retry:
[message release];
message = dispatch_mach_msg_create(NULL, messageSize, DISPATCH_MACH_MSG_DESTRUCTOR_DEFAULT, &header);
if (!message) {
status = ENOMEM;
goto out;
}
ret = mach_msg(header, MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT), 0, messageSize, port, 0, MACH_PORT_NULL);
status = [[self class] handleMachMessageReceiveReturnCode: ret];
if (status == EAGAIN) {
messageSize = header->msgh_size + MAX_TRAILER_SIZE;
goto retry;
} else if (status == 0) {
*incomingMessage = [message retain];
}
out:
[message release];
return status;
}
+ (int)receiveWithPort: (mach_port_t)port incomingMessage: (xpc_object_t*)incomingMessage flags: (uint64_t)flags
{
int status = 0;
dispatch_mach_msg_t message = NULL;
mach_msg_header_t* header = NULL;
mach_msg_return_t ret = MACH_MSG_SUCCESS;
status = [[self class] receiveMachMessageWithPort: port incomingMessage: &message flags: flags];
if (status == 0) {
header = dispatch_mach_msg_get_msg(message, NULL);
if (header->msgh_id == XPC_MSGH_ID_MESSAGE || header->msgh_id == XPC_MSGH_ID_ASYNC_REPLY) {
XPC_CLASS(dictionary)* dict = nil;
dict = [XPC_CLASS(deserializer) process: [message retain]];
if (!dict) {
status = EIO;
goto out;
}
*incomingMessage = dict;
status = 0;
} else {
xpc_log(XPC_LOG_ERROR, "pipe: received invalid message with ID: %u", header->msgh_id);
mach_msg_destroy(header);
status = EIO;
goto out;
}
}
out:
[message release];
return status;
}
+ (int)tryReceiveWithPort: (mach_port_t)port incomingMessage: (xpc_object_t*)incomingMessage replyPort: (mach_port_t*)replyPort maximumMIGReplySize: (size_t)maximumMIGReplySize flags:(uint64_t)flags demuxer:(xpc_pipe_mig_demux_f)demuxer
{
int status = 0;
dispatch_mach_msg_t message = NULL;
dispatch_mach_msg_t reply = NULL;
mach_msg_header_t* header = NULL;
mach_msg_header_t* replyHeader = NULL;
status = [[self class] receiveMachMessageWithPort: port incomingMessage: &message flags: flags];
if (status == 0) {
header = dispatch_mach_msg_get_msg(message, NULL);
if (header->msgh_id == XPC_MSGH_ID_MESSAGE || header->msgh_id == XPC_MSGH_ID_ASYNC_REPLY) {
XPC_CLASS(dictionary)* dict = nil;
dict = [XPC_CLASS(deserializer) process: [message retain]];
if (!dict) {
status = EIO;
goto out;
}
*incomingMessage = dict;
*replyPort = header->msgh_local_port;
status = 0;
} else {
replyHeader = NULL;
reply = dispatch_mach_msg_create(NULL, maximumMIGReplySize, DISPATCH_MACH_MSG_DESTRUCTOR_DEFAULT, &replyHeader);
status = [[self class] demux: header reply: replyHeader demuxer: demuxer];
}
}
out:
[message release];
[reply release];
return status;
}
+ (int)sendReply: (XPC_CLASS(dictionary)*)contents
{
int status = 0;
XPC_CLASS(serializer)* serializer = nil;
dispatch_mach_msg_t message = NULL;
mach_msg_header_t* header = NULL;
size_t messageSize = 0;
mach_msg_return_t ret = MACH_MSG_SUCCESS;
if (!contents.isReply) {
status = EINVAL;
goto out;
}
serializer = [XPC_CLASS(serializer) new];
if (!serializer) {
status = ENOMEM;
goto out;
}
if (![serializer writeObject: contents]) {
status = EINVAL;
goto out;
}
message = [serializer finalizeWithRemotePort: contents.outgoingPort
localPort: MACH_PORT_NULL
asReply: YES
expectingReply: NO];
if (!message) {
// could either be invalid arguments or insufficient memory,
// but at this point, insufficient memory is more likely
status = ENOMEM;
goto out;
}
message = [message retain];
header = dispatch_mach_msg_get_msg(message, &messageSize);
[serializer release];
serializer = nil;
ret = mach_msg(header, MACH_SEND_MSG, messageSize, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
status = [[self class] handleMachMessageSendReturnCode: ret withMessageHeader: header];
out:
[serializer release];
[message release];
return status;
}
+ (int)demux: (mach_msg_header_t*)request reply: (mach_msg_header_t*)reply demuxer: (xpc_pipe_mig_demux_f)demuxer
{
// refer to `dispatch_mach_mig_demux` to see why this method does what it does
int status = 0;
mig_reply_error_t* replyContent = (mig_reply_error_t*)reply;
if (!demuxer(request, reply)) {
mach_msg_destroy(reply);
status = EINVAL;
goto out;
}
switch (replyContent->RetCode) {
case KERN_SUCCESS: {
// everything's good
} break;
case MIG_NO_REPLY: {
// not really an error, just indicates that we should not reply
replyContent->Head.msgh_remote_port = MACH_PORT_NULL;
} break;
default: {
// quoting a comment in `dispatch_mach_mig_demux`:
// > destroy the request - but not the reply port
// > (MIG moved it into the bufReply).
request->msgh_remote_port = MACH_PORT_NULL;
mach_msg_destroy(request);
} break;
}
if (MACH_PORT_VALID(replyContent->Head.msgh_remote_port)) {
mach_msg_return_t ret = mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
status = [[self class] handleMachMessageSendReturnCode: ret withMessageHeader: reply];
if (status == EPIPE) {
// peer died; not a big deal
status = 0;
}
}
out:
return status;
}
- (BOOL)broken
{
XPC_THIS_DECL(pipe);
BOOL broken = YES;
pthread_rwlock_rdlock(&this->state_lock);
broken = this->state == xpc_pipe_state_broken;
pthread_rwlock_unlock(&this->state_lock);
return broken;
}
- (void)setBroken: (BOOL)broken
{
XPC_THIS_DECL(pipe);
if (!broken) {
return;
}
pthread_rwlock_wrlock(&this->state_lock);
this->state = xpc_pipe_state_broken;
pthread_rwlock_unlock(&this->state_lock);
}
- (instancetype)initForService: (const char*)serviceName withFlags: (uint64_t)flags
{
mach_port_t servicePort = MACH_PORT_NULL;
if (bootstrap_look_up2(bootstrap_port, serviceName, &servicePort, 0, (flags & XPC_PIPE_FLAG_PRIVILEGED) ? BOOTSTRAP_PRIVILEGED_SERVER : 0) != BOOTSTRAP_SUCCESS) {
self = [super init];
[self release];
return nil;
}
self = [self initWithPort: servicePort flags: flags];
//xpc_mach_port_release_send(servicePort);
return self;
}
- (instancetype)initWithPort: (mach_port_t)port flags: (uint64_t)flags
{
if (self = [super init]) {
XPC_THIS_DECL(pipe);
pthread_rwlock_init(&this->state_lock, NULL);
/*
if (xpc_mach_port_retain_send(port) != KERN_SUCCESS) {
[self release];
return nil;
}
*/
this->checkin_port = port;
this->send_port = xpc_mach_port_create_send_receive();
this->recv_port = xpc_mach_port_create_receive();
if (!MACH_PORT_VALID(this->checkin_port) || !MACH_PORT_VALID(this->send_port) || !MACH_PORT_VALID(this->recv_port)) {
[self release];
return nil;
}
}
return self;
}
- (int)activateLocked
{
XPC_THIS_DECL(pipe);
XPC_CLASS(serializer)* serializer = [[XPC_CLASS(serializer) alloc] initWithoutHeader];
dispatch_mach_msg_t message = NULL;
mach_msg_header_t* header = NULL;
kern_return_t ret = KERN_SUCCESS;
size_t messageSize = 0;
if (![serializer writePort: this->send_port type: MACH_MSG_TYPE_MOVE_RECEIVE]) {
goto error_out;
}
// we don't actually use this receive port, but the server needs it
if (![serializer writePort: this->recv_port type: MACH_MSG_TYPE_MAKE_SEND]) {
goto error_out;
}
message = [serializer finalizeWithRemotePort: this->checkin_port localPort: MACH_PORT_NULL asReply: NO expectingReply: NO messageID: XPC_MSGH_ID_CHECKIN];
if (!message) {
goto error_out;
}
header = dispatch_mach_msg_get_msg(message, &messageSize);
if (!header) {
goto error_out;
}
ret = mach_msg(header, MACH_SEND_MSG, messageSize, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
if (ret != KERN_SUCCESS) {
goto error_out;
}
// we're now active
this->state = xpc_pipe_state_active;
[serializer release];
return 0;
error_out:
// if we failed to checkin, this pipe is now useless
this->state = xpc_pipe_state_broken;
[serializer release];
return EPIPE;
}
- (int)activate
{
XPC_THIS_DECL(pipe);
xpc_pipe_state_t state = xpc_pipe_state_broken;
// first, try the fast-path (i.e. we've already been activated)
pthread_rwlock_rdlock(&this->state_lock);
state = this->state;
pthread_rwlock_unlock(&this->state_lock);
// if we're not currently in the initial state,
// we're either active (which is what we want) or we're broken (which we can't do anything about),
// in either case, all we can do is return.
if (state != xpc_pipe_state_initial) {
goto out;
}
pthread_rwlock_wrlock(&this->state_lock);
// make sure nobody activated us or broke us while we had the lock dropped
if ((state = this->state) == xpc_pipe_state_initial) {
[self activateLocked];
state = this->state;
}
pthread_rwlock_unlock(&this->state_lock);
out:
if (state == xpc_pipe_state_active) {
return 0;
} else {
return EPIPE;
}
}
- (int)sendMessage: (XPC_CLASS(dictionary)*)contents withSynchronousReply: (xpc_object_t*)reply flags: (uint64_t)flags
{
XPC_THIS_DECL(pipe);
int status = 0;
mach_port_t replyPort = MACH_PORT_NULL;
XPC_CLASS(serializer)* serializer = nil;
dispatch_mach_msg_t message = NULL;
mach_msg_header_t* header = NULL;
size_t messageSize = 0;
BOOL sent = NO;
mach_msg_return_t ret = MACH_MSG_SUCCESS;
status = [self activate];
if (status != 0) {
goto out;
}
replyPort = mig_get_reply_port();
if (!MACH_PORT_VALID(replyPort)) {
status = ENOMEM;
goto out;
}
serializer = [XPC_CLASS(serializer) new];
if (!serializer) {
status = ENOMEM;
goto out;
}
if (![serializer writeObject: contents]) {
status = EINVAL;
goto out;
}
message = [serializer finalizeWithRemotePort: MACH_PORT_VALID(contents.outgoingPort) ? contents.outgoingPort : this->send_port
localPort: replyPort
asReply: contents.isReply
expectingReply: YES];
if (!message) {
// could either be invalid arguments or insufficient memory,
// but at this point, insufficient memory is more likely
status = ENOMEM;
goto out;
}
message = [message retain];
header = dispatch_mach_msg_get_msg(message, &messageSize);
[serializer release];
serializer = nil;
retry:
// do the send and receive at the same time as an optimization
ret = mach_msg(header, (sent ? 0 : MACH_SEND_MSG) | MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT), sent ? 0 : messageSize, messageSize, replyPort, 0, MACH_PORT_NULL);
sent = YES;
status = [[self class] handleMachMessageSendReturnCode: ret withMessageHeader: header];
if (status == 0) {
status = [[self class] handleMachMessageReceiveReturnCode: ret];
if (status == EAGAIN) {
messageSize = header->msgh_size + MAX_TRAILER_SIZE;
[message release];
message = dispatch_mach_msg_create(NULL, messageSize, DISPATCH_MACH_MSG_DESTRUCTOR_DEFAULT, &header);
goto retry;
} else if (status == 0) {
if (header->msgh_id == XPC_MSGH_ID_ASYNC_REPLY) {
XPC_CLASS(dictionary)* dict = nil;
dict = [XPC_CLASS(deserializer) process: [message retain]];
if (!dict) {
status = EIO;
goto out;
}
*reply = dict;
status = 0;
} else {
xpc_log(XPC_LOG_ERROR, "pipe: received invalid reply message with ID: %u", header->msgh_id);
mach_msg_destroy(header);
status = EIO;
goto out;
}
}
}
out:
if (status == EPIPE) {
self.broken = YES;
}
[serializer release];
[message release];
return status;
}
- (int)sendMessage: (XPC_CLASS(dictionary)*)contents withReplyPort: (mach_port_t)replyPort
{
XPC_THIS_DECL(pipe);
int status = 0;
XPC_CLASS(serializer)* serializer = nil;
dispatch_mach_msg_t message = NULL;
mach_msg_header_t* header = NULL;
size_t messageSize = 0;
mach_msg_return_t ret = MACH_MSG_SUCCESS;
status = [self activate];
if (status != 0) {
goto out;
}
serializer = [XPC_CLASS(serializer) new];
if (!serializer) {
status = ENOMEM;
goto out;
}
if (![serializer writeObject: contents]) {
status = EINVAL;
goto out;
}
message = [serializer finalizeWithRemotePort: MACH_PORT_VALID(contents.outgoingPort) ? contents.outgoingPort : this->send_port
localPort: replyPort
asReply: contents.isReply
expectingReply: YES];
if (!message) {
// could either be invalid arguments or insufficient memory,
// but at this point, insufficient memory is more likely
status = ENOMEM;
goto out;
}
message = [message retain];
header = dispatch_mach_msg_get_msg(message, &messageSize);
[serializer release];
serializer = nil;
ret = mach_msg(header, MACH_SEND_MSG, messageSize, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
status = [[self class] handleMachMessageSendReturnCode: ret withMessageHeader: header];
out:
if (status == EPIPE) {
self.broken = YES;
}
[serializer release];
[message release];
return status;
}
- (int)forwardMessage: (XPC_CLASS(dictionary)*)contents
{
XPC_THIS_DECL(pipe);
int status = 0;
XPC_CLASS(serializer)* serializer = nil;
dispatch_mach_msg_t message = NULL;
mach_msg_header_t* header = NULL;
size_t messageSize = 0;
mach_msg_return_t ret = MACH_MSG_SUCCESS;
bool forwardedMessageExpectsReply = contents.expectsReply;
status = [self activate];
if (status != 0) {
goto out;
}
serializer = [XPC_CLASS(serializer) new];
if (!serializer) {
status = ENOMEM;
goto out;
}
if (![serializer writeObject: contents]) {
status = EINVAL;
goto out;
}
message = [serializer finalizeWithRemotePort: this->send_port
localPort: contents.incomingPort
asReply: NO
expectingReply: forwardedMessageExpectsReply];
if (!message) {
// could either be invalid arguments or insufficient memory,
// but at this point, insufficient memory is more likely
status = ENOMEM;
goto out;
}
message = [message retain];
header = dispatch_mach_msg_get_msg(message, &messageSize);
[serializer release];
serializer = nil;
header->msgh_local_port = contents.incomingPort;
header->msgh_bits = (header->msgh_bits & ~MACH_MSGH_BITS_LOCAL_MASK) | ((forwardedMessageExpectsReply ? MACH_MSG_TYPE_MOVE_SEND_ONCE : MACH_MSG_TYPE_COPY_SEND) << 8);
contents.incomingPort = MACH_PORT_NULL;
ret = mach_msg(header, MACH_SEND_MSG, messageSize, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
status = [[self class] handleMachMessageSendReturnCode: ret withMessageHeader: header];
out:
if (status == EPIPE) {
self.broken = YES;
}
[serializer release];
[message release];
return status;
}
- (int)sendMessage: (XPC_CLASS(dictionary)*)contents
{
XPC_THIS_DECL(pipe);
int status = 0;
XPC_CLASS(serializer)* serializer = nil;
dispatch_mach_msg_t message = NULL;
mach_msg_header_t* header = NULL;
size_t messageSize = 0;
mach_msg_return_t ret = MACH_MSG_SUCCESS;
status = [self activate];
if (status != 0) {
goto out;
}
serializer = [XPC_CLASS(serializer) new];
if (!serializer) {
status = ENOMEM;
goto out;
}
if (![serializer writeObject: contents]) {
status = EINVAL;
goto out;
}
message = [serializer finalizeWithRemotePort: MACH_PORT_VALID(contents.outgoingPort) ? contents.outgoingPort : this->send_port
localPort: MACH_PORT_NULL
asReply: contents.isReply
expectingReply: NO];
if (!message) {
// could either be invalid arguments or insufficient memory,
// but at this point, insufficient memory is more likely
status = ENOMEM;
goto out;
}
message = [message retain];
header = dispatch_mach_msg_get_msg(message, &messageSize);
[serializer release];
serializer = nil;
ret = mach_msg(header, MACH_SEND_MSG, messageSize, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
status = [[self class] handleMachMessageSendReturnCode: ret withMessageHeader: header];
out:
if (status == EPIPE) {
self.broken = YES;
}
[serializer release];
[message release];
return status;
}
- (void)invalidate
{
XPC_THIS_DECL(pipe);
self.broken = YES;
xpc_mach_port_release_send(this->checkin_port);
this->checkin_port = MACH_PORT_NULL;
xpc_mach_port_release_send(this->send_port);
this->send_port = MACH_PORT_NULL;
xpc_mach_port_release_receive(this->recv_port);
this->recv_port = MACH_PORT_NULL;
}
@end
//
@ -20,67 +780,103 @@ XPC_CLASS_HEADER(pipe);
XPC_EXPORT
void xpc_pipe_invalidate(xpc_pipe_t xpipe) {
TO_OBJC_CHECKED(pipe, xpipe, pipe) {
return [pipe invalidate];
}
};
XPC_EXPORT
xpc_pipe_t xpc_pipe_create(const char* name, int flags) {
return NULL;
xpc_pipe_t xpc_pipe_create(const char* name, uint64_t flags) {
return [[XPC_CLASS(pipe) alloc] initForService: name withFlags: flags];
};
XPC_EXPORT
xpc_pipe_t xpc_pipe_create_from_port(mach_port_t port, uint64_t flags) {
return [[XPC_CLASS(pipe) alloc] initWithPort: port flags: flags];
};
// actually belongs in libsystem_info
XPC_EXPORT
xpc_object_t _od_rpc_call(const char* procname, xpc_object_t payload, xpc_pipe_t (*get_pipe)(bool)) {
xpc_stub();
return NULL;
};
XPC_EXPORT
int xpc_pipe_routine_reply(xpc_pipe_t xpipe) {
return -1;
int xpc_pipe_routine_reply(xpc_object_t xdict) {
TO_OBJC_CHECKED(dictionary, xdict, dict) {
return [XPC_CLASS(pipe) sendReply: dict];
}
return EINVAL;
};
XPC_EXPORT
int xpc_pipe_routine(xpc_pipe_t xpipe, xpc_object_t payload, xpc_object_t* reply) {
return -1;
int xpc_pipe_routine(xpc_pipe_t xpipe, xpc_object_t xdict, xpc_object_t* reply) {
TO_OBJC_CHECKED(pipe, xpipe, pipe) {
TO_OBJC_CHECKED(dictionary, xdict, dict) {
return [pipe sendMessage: dict withSynchronousReply: reply flags: 0];
}
}
return EINVAL;
};
XPC_EXPORT
int xpc_pipe_try_receive(mach_port_t port, xpc_object_t* out_object, mach_port_t* out_remote_port, boolean_t (*demux)(mach_msg_header_t* request, mach_msg_header_t* reply), mach_msg_size_t max_message_size, int flags) {
return -1;
int xpc_pipe_try_receive(mach_port_t port, xpc_object_t* out_object, mach_port_t* out_local_port, boolean_t (*demuxer)(mach_msg_header_t* request, mach_msg_header_t* reply), mach_msg_size_t max_mig_reply_size, uint64_t flags) {
return [XPC_CLASS(pipe) tryReceiveWithPort: port incomingMessage: out_object replyPort: out_local_port maximumMIGReplySize: max_mig_reply_size flags: flags demuxer: demuxer];
};
XPC_EXPORT
int _xpc_pipe_handle_mig(mach_msg_header_t* request, mach_msg_header_t* reply, bool (*demux)(mach_msg_header_t* request, mach_msg_header_t* reply)) {
return -1;
int _xpc_pipe_handle_mig(mach_msg_header_t* request, mach_msg_header_t* reply, boolean_t (*demuxer)(mach_msg_header_t* request, mach_msg_header_t* reply)) {
return [XPC_CLASS(pipe) demux: request reply: reply demuxer: demuxer];
};
XPC_EXPORT
xpc_pipe_t xpc_pipe_create_from_port(mach_port_t port, mach_port_type_t port_type) {
// that second parameter is just a guess
// a good guess, but a guess nonetheless
return NULL;
int xpc_pipe_receive(mach_port_t receive_port, xpc_object_t* out_msg, uint64_t flags) {
return [XPC_CLASS(pipe) receiveWithPort: receive_port incomingMessage: out_msg flags: flags];
};
XPC_EXPORT
int xpc_pipe_receive(mach_port_t port, xpc_object_t* out_msg, bool some_bool) {
return -1;
int xpc_pipe_routine_async(xpc_pipe_t xpipe, xpc_object_t xdict, mach_port_t local_reply_port) {
TO_OBJC_CHECKED(pipe, xpipe, pipe) {
TO_OBJC_CHECKED(dictionary, xdict, dict) {
return [pipe sendMessage: dict withReplyPort: local_reply_port];
}
}
return EINVAL;
};
XPC_EXPORT
int xpc_pipe_routine_async(xpc_pipe_t xpipe, xpc_object_t payload, int some_flags_probably) {
return -1;
int xpc_pipe_routine_forward(xpc_pipe_t xpipe, xpc_object_t xdict) {
TO_OBJC_CHECKED(pipe, xpipe, pipe) {
TO_OBJC_CHECKED(dictionary, xdict, dict) {
return [pipe forwardMessage: dict];
}
}
return EINVAL;
};
XPC_EXPORT
int xpc_pipe_routine_forward(xpc_pipe_t xpipe, xpc_object_t payload) {
return -1;
int xpc_pipe_routine_with_flags(xpc_pipe_t xpipe, xpc_object_t xdict, xpc_object_t* reply, uint64_t flags) {
TO_OBJC_CHECKED(pipe, xpipe, pipe) {
TO_OBJC_CHECKED(dictionary, xdict, dict) {
return [pipe sendMessage: dict withSynchronousReply: reply flags: flags];
}
}
return EINVAL;
};
XPC_EXPORT
int xpc_pipe_routine_with_flags(xpc_pipe_t xpipe, xpc_object_t payload, xpc_object_t* reply, uint64_t flags) {
return -1;
};
int xpc_pipe_simpleroutine(xpc_pipe_t xpipe, xpc_object_t xdict) {
TO_OBJC_CHECKED(pipe, xpipe, pipe) {
TO_OBJC_CHECKED(dictionary, xdict, dict) {
return [pipe sendMessage: dict];
}
}
XPC_EXPORT
int xpc_pipe_simpleroutine(xpc_pipe_t xpipe, xpc_object_t payload, int some_flags_probably) {
return -1;
return EINVAL;
};

View File

@ -1,30 +1,35 @@
#import <xpc/xpc.h>
#import <xpc/util.h>
XPC_EXPORT
xpc_object_t _xpc_runtime_get_entitlements_data(void) {
// returns a data object
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_object_t _xpc_runtime_get_self_entitlements(void) {
// returns a dictionary (parsed from the plist data from `_xpc_runtime_get_entitlements_data`)
xpc_stub();
return NULL;
};
XPC_EXPORT
bool _xpc_runtime_is_app_sandboxed(void) {
xpc_stub();
return false;
};
XPC_EXPORT
void xpc_main(xpc_connection_handler_t handler) {
xpc_stub();
abort();
};
XPC_EXPORT
void xpc_init_services(void) {
xpc_stub();
};
XPC_EXPORT

View File

@ -18,45 +18,50 @@ XPC_CLASS_HEADER(service);
XPC_EXPORT
void xpc_service_attach(xpc_service_t xservice, bool run, bool kill) {
xpc_stub();
};
XPC_EXPORT
xpc_service_t xpc_service_create(int flags, const char* name, uint64_t handle, dispatch_queue_t queue) {
xpc_stub();
return NULL;
};
XPC_EXPORT
xpc_service_t xpc_service_create_from_specifier(const char* specifier, dispatch_queue_t queue) {
xpc_stub();
return NULL;
};
XPC_EXPORT
const char* xpc_service_get_rendezvous_token(xpc_service_t xservice) {
xpc_stub();
return NULL;
};
XPC_EXPORT
void xpc_service_kickstart(xpc_service_t xservice, bool suspended, bool kill) {
xpc_stub();
};
XPC_EXPORT
void xpc_service_set_attach_handler(xpc_service_t xservice, void (^handler)(xpc_service_instance_t xinstance)) {
xpc_stub();
};
XPC_EXPORT
void _xpc_service_last_xref_cancel(xpc_service_t xservice) {
xpc_stub();
};
XPC_EXPORT
void xpc_handle_service() {
// i have no clue what the types for the 3 arguments to this function are
xpc_stub();
};
XPC_EXPORT
void xpc_handle_subservice() {
// same as for `xpc_handle_service`
xpc_stub();
};

View File

@ -1,6 +1,7 @@
#import <xpc/objects/service_instance.h>
#import <xpc/xpc.h>
#import <xpc/endpoint.h>
#import <xpc/util.h>
XPC_CLASS_SYMBOL_DECL(service_instance);
@ -17,82 +18,91 @@ XPC_CLASS_HEADER(service_instance);
XPC_EXPORT
void xpc_service_instance_dup2(xpc_service_instance_t xinstance, int fd, int new_fd) {
xpc_stub();
};
XPC_EXPORT
void* xpc_service_instance_get_context(xpc_service_instance_t xinstance) {
// unsure about the return type, but given the name, it's the most likely type
xpc_stub();
return NULL;
};
XPC_EXPORT
pid_t xpc_service_instance_get_host_pid(xpc_service_instance_t xinstance) {
xpc_stub();
return -1;
};
XPC_EXPORT
pid_t xpc_service_instance_get_pid(xpc_service_instance_t xinstance) {
xpc_stub();
return -1;
};
XPC_EXPORT
int xpc_service_instance_get_type(xpc_service_instance_t xinstance) {
// unsure about the return type
xpc_stub();
return -1;
};
XPC_EXPORT
bool xpc_service_instance_is_configurable(xpc_service_instance_t xinstance) {
xpc_stub();
return false;
};
XPC_EXPORT
void xpc_service_instance_run(xpc_service_instance_t xinstance) {
xpc_stub();
};
XPC_EXPORT
void xpc_service_instance_set_binpref(xpc_service_instance_t xinstance, int binpref) {
// unsure about `binpref`'s type
xpc_stub();
};
XPC_EXPORT
void xpc_service_instance_set_context(xpc_service_instance_t xinstance, void* context) {
// unsure about `context`'s type, but given the name, it's the most likely type
xpc_stub();
};
XPC_EXPORT
void xpc_service_instance_set_cwd(xpc_service_instance_t xinstance, const char* cwd) {
xpc_stub();
};
XPC_EXPORT
void xpc_service_instance_set_endpoint(xpc_service_instance_t xinstance, xpc_endpoint_t endpoint) {
xpc_stub();
};
XPC_EXPORT
void xpc_service_instance_set_environment(xpc_service_instance_t xinstance, xpc_object_t environment) {
// i'm pretty sure `environment` is a dictionary
xpc_stub();
};
XPC_EXPORT
void xpc_service_instance_set_finalizer_f(xpc_service_instance_t xinstance, void (*finalizer)(void* context)) {
xpc_stub();
};
XPC_EXPORT
void xpc_service_instance_set_jetsam_properties(xpc_service_instance_t xinstance, int flags, int priority, int memlimit) {
xpc_stub();
};
XPC_EXPORT
void xpc_service_instance_set_path(xpc_service_instance_t xinstance, const char* path) {
xpc_stub();
};
XPC_EXPORT
void xpc_service_instance_set_start_suspended(xpc_service_instance_t xinstance) {
// takes no other arguments; supposed to set the "start suspended" internal flag
xpc_stub();
};

View File

@ -17,11 +17,13 @@ XPC_CLASS_HEADER(shmem);
XPC_EXPORT
xpc_object_t xpc_shmem_create(void* region, size_t length) {
xpc_stub();
return NULL;
};
XPC_EXPORT
size_t xpc_shmem_map(xpc_object_t xshmem, void** region) {
xpc_stub();
return 0;
};
@ -31,15 +33,18 @@ size_t xpc_shmem_map(xpc_object_t xshmem, void** region) {
XPC_EXPORT
mach_port_t _xpc_shmem_get_mach_port(xpc_object_t xshmem) {
xpc_stub();
return MACH_PORT_NULL;
};
XPC_EXPORT
xpc_object_t xpc_shmem_create_readonly(const void* region, size_t length) {
xpc_stub();
return NULL;
};
XPC_EXPORT
size_t xpc_shmem_get_length(xpc_object_t xshmem) {
xpc_stub();
return 0;
};

View File

@ -2,6 +2,7 @@
#include <os/object_private.h>
#import <Foundation/NSZone.h>
#import <xpc/xpc.h>
#import <xpc/util.h>
OS_OBJECT_NONLAZY_CLASS
@implementation OS_OBJECT_CLASS(os_transaction)
@ -33,11 +34,13 @@ os_transaction_t os_transaction_create(const char* transaction_name) {
XPC_EXPORT
char* os_transaction_copy_description(os_transaction_t transaction) {
xpc_stub();
return NULL;
};
XPC_EXPORT
int os_transaction_needs_more_time(os_transaction_t transaction) {
xpc_stub();
return -1;
};
@ -46,12 +49,12 @@ int os_transaction_needs_more_time(os_transaction_t transaction) {
XPC_EXPORT
void xpc_transaction_begin(void) {
xpc_stub();
};
XPC_EXPORT
void xpc_transaction_end(void) {
xpc_stub();
};
//
@ -60,15 +63,15 @@ void xpc_transaction_end(void) {
XPC_EXPORT
void xpc_transaction_exit_clean(void) {
xpc_stub();
};
XPC_EXPORT
void xpc_transaction_interrupt_clean_exit(void) {
xpc_stub();
};
XPC_EXPORT
void xpc_transactions_enable(void) {
xpc_stub();
};

View File

@ -38,5 +38,5 @@ struct some_extension_type_struct;
XPC_EXPORT
void xpc_extension_type_init(struct some_extension_type_struct* extension_type, void* some_user_function_probably) {
xpc_stub();
};

View File

@ -5,10 +5,14 @@
#include <sys/syslog.h>
#import <xpc/objects/connection.h>
#include <stdio.h>
#ifndef XPC_LOG_TO_STDOUT_TOO
#define XPC_LOG_TO_STDOUT_TOO 0
#endif
static bool enable_stub_messages = false;
XPC_CLASS(object)* xpc_retain_for_collection(XPC_CLASS(object)* object) {
// connections aren't retained by collections
if ([object isKindOfClass: [XPC_CLASS(connection) class]]) {
@ -229,3 +233,14 @@ void _xpc_log(const char* function, const char* file, size_t line, xpc_log_prior
_xpc_logv(function, file, line, priority, format, args);
va_end(args);
};
void xpc_stub_init(void) {
enable_stub_messages = getenv("STUB_VERBOSE") != NULL;
};
void _xpc_stub(const char* function, const char* file, size_t line) {
if (enable_stub_messages) {
printf("libxpc: stub called %s (at %s:%zu)\n", function, file, line);
fflush(stdout);
}
};

View File

@ -1,5 +1,6 @@
add_darling_executable(libxpc_test_server server.c)
add_darling_executable(libxpc_test_client client.c)
add_darling_executable(libxpc_test_pipe_client pipe_client.c)
target_link_libraries(libxpc_test_server
xpc_static
@ -11,10 +12,16 @@ target_link_libraries(libxpc_test_client
objc
)
target_link_libraries(libxpc_test_pipe_client
xpc_static
objc
)
install(
TARGETS
libxpc_test_server
libxpc_test_client
libxpc_test_pipe_client
DESTINATION
libexec/darling/usr/libexec
)

View File

@ -20,6 +20,7 @@
#define server_error(format, ...) fprintf(stderr, "anonymous server connection: " format "\n", ## __VA_ARGS__)
#include "server_common.h"
#include "client_common.h"
dispatch_semaphore_t waiter;
@ -185,31 +186,13 @@ static xpc_endpoint_t setup_anonymous_server(void) {
};
int main(int argc, char** argv) {
test_service_message_type_t message_type = test_service_message_type_poke;
test_service_message_type_t message_type = determine_message_type(argc, argv);
xpc_connection_t client = xpc_connection_create(TEST_SERVICE_NAME, NULL);
xpc_object_t message = NULL;
bool synchronous_reply = false;
void (^send_with_reply)(void) = NULL;
waiter = dispatch_semaphore_create(0);
if (argc > 1) {
char first = argv[1][0];
if (first == 'p' || first == 'P') {
message_type = test_service_message_type_poke;
} else if (first == 'h' || first == 'H') {
message_type = test_service_message_type_hello;
} else if (first == 'e' || first == 'E') {
message_type = test_service_message_type_echo;
} else if (first == 'f' || first == 'F') {
message_type = test_service_message_type_friendship_invitation;
} else if (first == 'm' || first == 'M') {
message_type = test_service_message_type_meet_a_new_friend;
} else {
client_error("unparsable argument: %s", argv[1]);
exit(1);
}
}
if (argc > 2) {
synchronous_reply = true;
}

View File

@ -0,0 +1,40 @@
#ifndef _XPC_TEST_LAUNCHD_SERVICE_CLIENT_COMMON_H_
#define _XPC_TEST_LAUNCHD_SERVICE_CLIENT_COMMON_H_
#include <stdlib.h>
#include "service.h"
#ifndef client_log
#define client_log(...)
#endif
#ifndef client_error
#define client_error(...)
#endif
static test_service_message_type_t determine_message_type(int argc, char** argv) {
test_service_message_type_t message_type = test_service_message_type_invalid;
if (argc > 1) {
char first = argv[1][0];
if (first == 'p' || first == 'P') {
message_type = test_service_message_type_poke;
} else if (first == 'h' || first == 'H') {
message_type = test_service_message_type_hello;
} else if (first == 'e' || first == 'E') {
message_type = test_service_message_type_echo;
} else if (first == 'f' || first == 'F') {
message_type = test_service_message_type_friendship_invitation;
} else if (first == 'm' || first == 'M') {
message_type = test_service_message_type_meet_a_new_friend;
} else {
client_error("unparsable argument: %s", argv[1]);
exit(1);
}
}
return message_type;
};
#endif // _XPC_TEST_LAUNCHD_SERVICE_CLIENT_COMMON_H_

View File

@ -11,8 +11,5 @@
<key>org.darlinghq.libxpc.test-service</key>
<true/>
</dict>
<!-- redirect output to a logfile because launchd's stdout output is messed up (incorrect line breaks) -->
<key>StandardOutPath</key>
<string>/var/log/libxpc_test_service.log</string>
</dict>
</plist>

View File

@ -0,0 +1,181 @@
#include <xpc/xpc.h>
#include <xpc/endpoint.h>
#include <xpc/private/pipe.h>
#include <xpc/private/endpoint.h>
#include "service.h"
#define client_log(format, ...) printf("piped client: " format "\n", ## __VA_ARGS__)
#define client_error(format, ...) fprintf(stderr, "piped client: " format "\n", ## __VA_ARGS__)
#define reply_log(format, ...) printf("pipe reply handler: " format "\n", ## __VA_ARGS__)
#define reply_error(format, ...) fprintf(stderr, "pipe reply handler: " format "\n", ## __VA_ARGS__)
#include "client_common.h"
// from `util.h`
kern_return_t xpc_mach_port_release_send(mach_port_t port);
static xpc_pipe_t xpc_pipe_create_from_endpoint(xpc_endpoint_t endpoint) {
xpc_pipe_t pipe = NULL;
mach_port_t port = MACH_PORT_NULL;
if (!endpoint || xpc_get_type(endpoint) != (xpc_type_t)XPC_TYPE_ENDPOINT) {
goto out;
}
port = xpc_endpoint_copy_listener_port_4sim(endpoint);
if (!MACH_PORT_VALID(port)) {
goto out;
}
pipe = xpc_pipe_create_from_port(port, 0);
xpc_mach_port_release_send(port); // release it because the pipe already copies it
out:
return pipe;
};
static xpc_pipe_t xpc_dictionary_create_pipe(xpc_object_t xdict, const char* key) {
return xpc_pipe_create_from_endpoint(xpc_dictionary_get_value(xdict, key));
};
static void error_handler(int status) {
client_error("error: %s", strerror(status));
};
static void reply_handler(test_service_message_type_t outgoing_message_type, xpc_object_t object) {
xpc_type_t obj_type = xpc_get_type(object);
if (obj_type == (xpc_type_t)XPC_TYPE_DICTIONARY) {
test_service_message_type_t incoming_message_type = xpc_dictionary_get_uint64(object, MESSAGE_TYPE_KEY);
switch (outgoing_message_type) {
case test_service_message_type_hello: {
if (incoming_message_type != test_service_message_type_hello) {
reply_error("server replied to hello message with invalid message with type: %llu", incoming_message_type);
return;
}
reply_log("server replied to hello message with: %s", xpc_dictionary_get_string(object, HELLO_KEY));
} break;
case test_service_message_type_echo: {
if (incoming_message_type != test_service_message_type_echo) {
reply_error("server replied to echo message with invalid message with type: %llu", incoming_message_type);
return;
}
char* desc = xpc_copy_description(xpc_dictionary_get_value(object, ECHO_KEY));
reply_log("server replied to echo message with: %s", desc);
if (desc) {
free(desc);
}
} break;
case test_service_message_type_meet_a_new_friend: {
xpc_pipe_t new_pipe = NULL;
xpc_object_t message = NULL;
xpc_object_t reply = NULL;
int status = 0;
if (incoming_message_type != test_service_message_type_meet_a_new_friend) {
reply_error("server replied to meet-a-new-friend message with invalid message with type: %llu", incoming_message_type);
return;
}
new_pipe = xpc_dictionary_create_pipe(object, MEET_A_NEW_FRIEND_KEY);
if (!new_pipe) {
reply_log("server didn't have a friend for us to meet :(");
return;
}
message = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_uint64(message, MESSAGE_TYPE_KEY, test_service_message_type_hello);
xpc_dictionary_set_string(message, HELLO_KEY, "Hello from an anonymous piped client!");
status = xpc_pipe_routine(new_pipe, message, &reply);
if (status == 0) {
reply_handler(test_service_message_type_hello, reply);
} else {
error_handler(status);
}
} break;
}
} else {
reply_error("received non-dictionary object in reply handler: %s\n", xpc_copy_description(object));
abort();
}
};
int main(int argc, char** argv) {
test_service_message_type_t message_type = determine_message_type(argc, argv);
xpc_pipe_t pipe = xpc_pipe_create(TEST_SERVICE_NAME, 0);
xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_uint64(message, MESSAGE_TYPE_KEY, message_type);
switch (message_type) {
case test_service_message_type_poke: {
int status = 0;
status = xpc_pipe_simpleroutine(pipe, message);
if (status == 0) {
client_log("poke message sent successfully");
} else {
error_handler(status);
}
} break;
case test_service_message_type_hello: {
xpc_object_t reply = NULL;
int status = 0;
xpc_dictionary_set_string(message, HELLO_KEY, "Hello from the piped client!");
status = xpc_pipe_routine(pipe, message, &reply);
if (status == 0) {
reply_handler(message_type, reply);
} else {
error_handler(status);
}
} break;
case test_service_message_type_echo: {
xpc_object_t reply = NULL;
int status = 0;
xpc_dictionary_set_string(message, ECHO_KEY, "ECHO... Echo... echo.. *whipsers* echo...");
status = xpc_pipe_routine(pipe, message, &reply);
if (status == 0) {
reply_handler(message_type, reply);
} else {
error_handler(status);
}
} break;
case test_service_message_type_meet_a_new_friend: {
xpc_object_t reply = NULL;
int status = 0;
status = xpc_pipe_routine(pipe, message, &reply);
if (status == 0) {
reply_handler(message_type, reply);
} else {
error_handler(status);
}
} break;
default: {
client_error("user requested unknown/unsupported message type: %llu", message_type);
exit(1);
} break;
}
return 0;
};

View File

@ -8,10 +8,11 @@
#include "service.h"
#include "../test-util.h"
#define server_peer_log(format, ...) printf("server peer connection: " format "\n", ## __VA_ARGS__)
#define server_peer_error(format, ...) fprintf(stderr, "server peer connection: " format "\n", ## __VA_ARGS__)
#define server_log(format, ...) printf("server connection: " format "\n", ## __VA_ARGS__)
#define server_error(format, ...) fprintf(stderr, "server connection: " format "\n", ## __VA_ARGS__)
// if we don't flush the output, it doesn't get written, for some reason
#define server_peer_log(format, ...) do { printf("server peer connection: " format "\n", ## __VA_ARGS__); fflush(stdout); } while (0)
#define server_peer_error(format, ...) do { fprintf(stderr, "server peer connection: " format "\n", ## __VA_ARGS__); fflush(stderr); } while (0)
#define server_log(format, ...) do { printf("server connection: " format "\n", ## __VA_ARGS__); fflush(stdout); } while (0)
#define server_error(format, ...) do { fprintf(stderr, "server connection: " format "\n", ## __VA_ARGS__); fflush(stderr); } while (0)
#include "server_common.h"
@ -126,8 +127,18 @@ static void handle_server_peer_message(xpc_object_t message) {
}
};
// manually log everything to a file because launchd's default stdout is broken and so is its redirection
static void replace_output_fds(void) {
freopen("/var/log/libxpc_test_service.log", "a", stdout);
freopen("/var/log/libxpc_test_service.log", "a", stderr);
};
int main(int arc, char** argv) {
xpc_connection_t server = xpc_connection_create_mach_service(TEST_SERVICE_NAME, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);
xpc_connection_t server = NULL;
replace_output_fds();
server = xpc_connection_create_mach_service(TEST_SERVICE_NAME, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);
xpc_connection_set_event_handler(server, ^(xpc_object_t object) {
xpc_type_t obj_type = xpc_get_type(object);