mirror of
https://github.com/darlinghq/darling-libxpc.git
synced 2024-11-23 03:39:40 +00:00
Implement pipes...
...also, add stub messages everywhere. launchd now works again as of this commit.
This commit is contained in:
parent
86a317edbf
commit
90b934b336
@ -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);
|
||||
|
@ -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();
|
||||
|
12
include/xpc/private/endpoint.h
Normal file
12
include/xpc/private/endpoint.h
Normal 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_
|
52
include/xpc/private/pipe.h
Normal file
52
include/xpc/private/pipe.h
Normal 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_
|
@ -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_
|
||||
|
@ -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_
|
||||
|
@ -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
|
||||
|
22
src/bundle.m
22
src/bundle.m
@ -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();
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
24
src/event.m
24
src/event.m
@ -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();
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
42
src/misc.m
42
src/misc.m
@ -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;
|
||||
};
|
||||
|
||||
|
854
src/pipe.m
854
src/pipe.m
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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();
|
||||
};
|
||||
|
15
src/util.m
15
src/util.m
@ -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);
|
||||
}
|
||||
};
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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;
|
||||
}
|
||||
|
40
test/launchd-service/client_common.h
Normal file
40
test/launchd-service/client_common.h
Normal 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_
|
@ -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>
|
||||
|
181
test/launchd-service/pipe_client.c
Normal file
181
test/launchd-service/pipe_client.c
Normal 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;
|
||||
};
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user