Initial rewrite commit

This commit is the first in the rewrite of libxpc. It implements the basic API (that is, the XPC object system and the most basic objects and collections). It also adds some tests for the basic API.

The current test suite uses ctest as the testing framework, but for future tests (such as for connections), it may be necessary to find something a little more robust (or roll our own).

Future commits will implement the rewritten connection system based on top of libdispatch's private Mach API, which is made specifically for this purpose (it even has certain methods marked as `4libxpc`).
This commit is contained in:
Ariel Abreu 2021-03-27 15:14:08 -04:00
parent 3382d940fd
commit 675120a197
No known key found for this signature in database
GPG Key ID: BB20848279B910AC
98 changed files with 5447 additions and 4303 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "test/ctest"]
path = test/ctest
url = https://github.com/bvdberg/ctest.git

View File

@ -1,14 +1,9 @@
project(xpc)
cmake_minimum_required(VERSION 2.4.0)
enable_language(C ASM)
cmake_minimum_required(VERSION 3.12)
if(COMMAND cmake_policy)
cmake_policy(SET CMP0003 NEW)
endif(COMMAND cmake_policy)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse -msse2 -msse3 -w -nostdinc -fblocks")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__DARWIN_UNIX03 -fPIC -w")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse -msse2 -msse3 -nostdinc -fblocks -fvisibility=hidden")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__DARWIN_UNIX03 -fPIC")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib")
include_directories(
@ -16,31 +11,69 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/internal-include
)
add_definitions(-DMACH)
set(xpc_sources
src/activity.c
src/array.c
src/bsm_wrappers.c
src/connection.c
src/dictionary.c
src/error.c
src/init.c
src/misc.c
src/pipe.c
src/private.c
src/reboot3.c
src/serialization.c
src/subr_sbuf.c
src/todo.c
src/transaction.m
src/type.c
add_compile_definitions(
__XPC_BUILDING_XPC__
__XPC_PROJECT_BUILD__
)
add_compile_options(
-Wno-extern-initializer
)
add_compile_options(
-include "${CMAKE_CURRENT_SOURCE_DIR}/internal-include/xpc/prefix.h"
)
set(xpc_sources
src/bsm_wrappers.c
src/init.c
src/reboot3.c
src/serialization.c
src/activity.m
src/array.m
src/base.m
src/bool.m
src/bundle.m
src/coalition.m
src/connection.m
src/data.m
src/date.m
src/dictionary.m
src/double.m
src/endpoint.m
src/error.m
src/event.m
src/fd.m
src/file_transfer.m
src/int64.m
src/launchd.m
src/mach_recv.m
src/mach_send.m
src/misc.m
src/null.m
src/pipe.m
src/pointer.m
src/runtime.m
src/serializer.m
src/service_instance.m
src/service.m
src/shmem.m
src/string.m
src/transaction.m
src/type.m
src/uint64.m
src/uuid.m
src/util.m
)
add_darling_object_library(xpc_obj ${xpc_sources})
set(DYLIB_INSTALL_NAME "/usr/lib/system/libxpc.dylib")
add_circular(xpc FAT
SOURCES
${xpc_sources}
add_circular(xpc
FAT
OBJECTS
$<TARGET_OBJECTS:xpc_obj>
SIBLINGS
system_c
system_kernel
@ -51,6 +84,8 @@ add_circular(xpc FAT
launch
system_info
system_dyld
unwind
compiler_rt
UPWARD
# break an upward dependency on libobjc
# ---
@ -58,10 +93,21 @@ add_circular(xpc FAT
# libSystem guarantees that we'll be initialized after libdispatch, and libdispatch already initializes libobjc
objc
)
#target_link_libraries(xpc system)
install(TARGETS xpc DESTINATION libexec/darling/usr/lib/system)
# NOTE: on macOS, these exported symbols are actually in libxpc itself, and are instead reexported by liblaunch.
# this difference shouldn't matter in practice
target_link_options(xpc PRIVATE
-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/scripts/exported-symbols.exp
-Wl,-reexported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/scripts/reexported-symbols.exp
)
if (ENABLE_TESTS)
add_darling_static_library(xpc_static
FAT
SOURCES
$<TARGET_OBJECTS:xpc_obj>
)
add_subdirectory(test)
endif (ENABLE_TESTS)

View File

@ -4,7 +4,25 @@
#include <os/object_private.h>
#if OS_OBJECT_USE_OBJC
OS_OBJECT_DECL_IMPL_CLASS(os_transaction, OS_OBJECT_CLASS(object));
struct os_transaction_vtable_s {
_OS_OBJECT_CLASS_HEADER();
};
struct os_transaction_s {
_OS_OBJECT_HEADER(
const struct os_transaction_vtable_s* os_obj_isa,
os_obj_ref_cnt,
os_obj_xref_cnt
);
};
@interface OS_OBJECT_CLASS(os_transaction) : OS_OBJECT_CLASS(object)
- (instancetype)initWithName: (const char*)name;
@end
typedef OS_OBJECT_CLASS(os_transaction)* OS_OBJC_INDEPENDENT_CLASS os_transaction_t;
#else
typedef struct os_transaction_s* os_transaction_t;
#endif

View File

@ -2,6 +2,7 @@
#define XPC_LAUNCHD_H_
#include <xpc/xpc.h>
#include <xpc/launchd_defs.h>
#include <launch.h>
#ifdef __cplusplus
extern "C" {
@ -13,9 +14,11 @@ extern "C" {
#define EXSRCH 3
#define EXMAX EXSRCH
XPC_DECL(xpc_pipe);
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_object_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);
@ -24,7 +27,7 @@ void xpc_dictionary_set_mach_recv(xpc_object_t, const char *, mach_port_t);
void xpc_dictionary_set_mach_send(xpc_object_t, const char *, mach_port_t);
mach_port_t xpc_dictionary_copy_mach_send(xpc_object_t, const char *);
xpc_object_t xpc_copy_entitlements_for_pid(pid_t);
xpc_object_t ld2xpc(launch_data_t);
xpc_object_t ld2xpc(launch_data_t data);
int launch_activate_socket(const char* key, int** fds, size_t* count);

View File

@ -13,8 +13,6 @@ extern "C" {
int _xpc_runtime_is_app_sandboxed();
typedef struct _xpc_pipe_s* xpc_pipe_t;
void xpc_pipe_invalidate(xpc_pipe_t pipe);
xpc_pipe_t xpc_pipe_create(const char* name, int flags);
@ -28,8 +26,7 @@ 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_reply(xpc_object_t);
int xpc_pipe_routine(xpc_object_t pipe, void *payload,xpc_object_t *reply);
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);
@ -45,6 +42,70 @@ xpc_object_t xpc_connection_copy_entitlement_value(xpc_connection_t connection,
void xpc_transaction_exit_clean();
void _xpc_string_set_value(xpc_object_t xstring, const char* new_string);
xpc_object_t xpc_string_create_no_copy(const char* string);
typedef void (*xpc_array_applier_f)(size_t index, xpc_object_t value, void* context);
void xpc_array_apply_f(xpc_object_t xarray, void* context, xpc_array_applier_f applier);
void xpc_array_set_mach_send(xpc_object_t xarray, size_t index, mach_port_t value);
mach_port_t xpc_array_copy_mach_send(xpc_object_t xarray, size_t index);
xpc_object_t xpc_array_get_array(xpc_object_t xarray, size_t index);
xpc_object_t xpc_array_get_dictionary(xpc_object_t xarray, size_t index);
xpc_object_t _xpc_dictionary_create_reply_with_port(mach_port_t port);
mach_msg_id_t _xpc_dictionary_extract_reply_msg_id(xpc_object_t xdict);
mach_port_t _xpc_dictionary_extract_reply_port(xpc_object_t xdict);
mach_msg_id_t _xpc_dictionary_get_reply_msg_id(xpc_object_t xdict);
void _xpc_dictionary_set_remote_connection(xpc_object_t xdict, xpc_connection_t xconn);
void _xpc_dictionary_set_reply_msg_id(xpc_object_t xdict, mach_msg_id_t msg_id);
typedef void (*xpc_dictionary_applier_f)(const char* key, xpc_object_t value, void* context);
void xpc_dictionary_apply_f(xpc_object_t xdict, void* context, xpc_dictionary_applier_f applier);
char* xpc_dictionary_copy_basic_description(xpc_object_t xdict);
bool xpc_dictionary_expects_reply(xpc_object_t xdict);
void xpc_dictionary_handoff_reply(xpc_object_t xdict, dispatch_queue_t queue, dispatch_block_t block);
void xpc_dictionary_handoff_reply_f(xpc_object_t xdict, dispatch_queue_t queue, void* context, dispatch_function_t function);
void xpc_dictionary_send_reply(xpc_object_t xdict);
void xpc_dictionary_set_mach_recv(xpc_object_t xdict, const char* key, mach_port_t recv);
mach_port_t _xpc_dictionary_extract_mach_send(xpc_object_t xdict, const char* key);
mach_port_t xpc_dictionary_copy_mach_send(xpc_object_t xdict, const char* key);
mach_port_t xpc_dictionary_extract_mach_recv(xpc_object_t xdict, const char* key);
xpc_object_t xpc_dictionary_get_array(xpc_object_t xdict, const char* key);
xpc_connection_t xpc_dictionary_get_connection(xpc_object_t xdict);
void _xpc_data_set_value(xpc_object_t xdata, const void* bytes, size_t length);
size_t xpc_data_get_inline_max(xpc_object_t xdata);
xpc_object_t _xpc_bool_create_distinct(bool value);
void _xpc_bool_set_value(xpc_object_t xbool, bool value);
const char* xpc_type_get_name(xpc_type_t xtype);
#ifdef __cplusplus
}
#endif

View File

@ -380,15 +380,18 @@ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
XPC_EXPORT XPC_NONNULL1
void
xpc_release(xpc_object_t object);
void
xpc_release_safe(xpc_object_t object);
#if OS_OBJECT_USE_OBJC_RETAIN_RELEASE
#undef xpc_release
#define xpc_release(object) ({ xpc_object_t _o = (object); \
_xpc_object_validate(_o); [_o release]; })
#define xpc_release_safe(object) xpc_release(object)
#endif // OS_OBJECT_USE_OBJC_RETAIN_RELEASE
#define xpc_release_safe(object) do { \
if (object) { \
xpc_release(object); \
object = NULL; \
} \
} while (0);
/*!
* @function xpc_get_type

View File

@ -30,6 +30,7 @@
#include <sys/queue.h>
#include <xpc/xpc.h>
#include <xpc/activity.h>
#include <darling/emulation/simple.h>
#ifdef XPC_DEBUG

View File

@ -0,0 +1,16 @@
#ifndef _XPC_OBJECTS_ACTIVITY_H_
#define _XPC_OBJECTS_ACTIVITY_H_
#import <xpc/objects/base.h>
XPC_CLASS_DECL(activity);
struct xpc_activity_s {
struct xpc_object_s base;
};
@interface XPC_CLASS_INTERFACE(activity)
@end
#endif // _XPC_OBJECTS_ACTIVITY_H_

View File

@ -0,0 +1,33 @@
#ifndef _XPC_OBJECTS_ARRAY_H_
#define _XPC_OBJECTS_ARRAY_H_
#import <xpc/objects/base.h>
#import <Foundation/NSEnumerator.h>
XPC_CLASS_DECL(array);
struct xpc_array_s {
struct xpc_object_s base;
NSUInteger size;
XPC_CLASS(object)** array;
};
@interface XPC_CLASS_INTERFACE(array)
// this API is modeled after NSMutableArray
@property(readonly) NSUInteger count;
- (instancetype)initWithObjects: (XPC_CLASS(object)* const*)objects count: (NSUInteger)count;
- (XPC_CLASS(object)*)objectAtIndex: (NSUInteger)index;
- (void)addObject: (XPC_CLASS(object)*)object;
- (void)replaceObjectAtIndex: (NSUInteger)index withObject: (XPC_CLASS(object)*)object;
- (void)enumerateObjectsUsingBlock: (void (^)(XPC_CLASS(object)* object, NSUInteger index, BOOL* stop))block;
- (XPC_CLASS(object)*)objectAtIndexedSubscript: (NSUInteger)index;
- (void)setObject: (XPC_CLASS(object)*)object atIndexedSubscript: (NSUInteger)index;
- (NSUInteger)countByEnumeratingWithState: (NSFastEnumerationState*)state objects: (id __unsafe_unretained [])objects count: (NSUInteger)count;
@end
#endif // _XPC_OBJECTS_ARRAY_H_

View File

@ -0,0 +1,120 @@
#ifndef _XPC_OBJECTS_BASE_H_
#define _XPC_OBJECTS_BASE_H_
#import <os/object_private.h>
#import <objc/NSObject.h>
#ifndef __XPC_INDIRECT__
OS_OBJECT_DECL(xpc_object);
#endif
#define __XPC_INDIRECT__
#import <xpc/base.h>
#define XPC_CLASS(name) OS_OBJECT_CLASS(xpc_ ## name)
#define XPC_CLASS_DECL(name) OS_OBJECT_DECL_SUBCLASS(xpc_ ## name, xpc_object)
#define XPC_CLASS_INTERFACE(name) XPC_CLASS(name) : XPC_CLASS(object) <XPC_CLASS(name)>
#define XPC_CAST(name, object) ((XPC_CLASS(name)*)object)
#define XPC_CLASS_HEADER(name) \
OS_OBJECT_NONLAZY_CLASS_LOAD \
+ (NSUInteger)_instanceSize \
{ \
return sizeof(struct xpc_ ## name ## _s); \
}
#define XPC_THIS(name) ((struct xpc_ ## name ## _s*)self)
#define XPC_THIS_DECL(name) struct xpc_ ## name ## _s* this = XPC_THIS(name)
#define XPC_WRAPPER_CLASS_DECL(name, type) \
XPC_CLASS_DECL(name); \
struct xpc_ ## name ## _s { \
struct xpc_object_s base; \
type value; \
}; \
@interface XPC_CLASS_INTERFACE(name) \
@property(assign) type value; \
- (instancetype)initWithValue: (type)value; \
@end
#define XPC_WRAPPER_CLASS_IMPL(name, type, format) \
XPC_CLASS_SYMBOL_DECL(name); \
OS_OBJECT_NONLAZY_CLASS \
@implementation XPC_CLASS(name) \
XPC_CLASS_HEADER(name); \
- (char*)xpcDescription \
{ \
char* output = NULL; \
asprintf(&output, "<%s: " format ">", xpc_class_name(self), self.value); \
return output; \
} \
- (type)value \
{ \
XPC_THIS_DECL(name); \
return this->value; \
} \
- (void)setValue: (type)value \
{ \
XPC_THIS_DECL(name); \
this->value = value; \
} \
- (instancetype)initWithValue: (type)value \
{ \
if (self = [super init]) { \
XPC_THIS_DECL(name); \
this->value = value; \
} \
return self; \
} \
- (NSUInteger)hash \
{ \
XPC_THIS_DECL(name); \
return xpc_raw_data_hash(&this->value, sizeof(this->value)); \
} \
@end
// hack to create symbol aliases programmatically, because the `alias` attribute isn't supported on Darwin platforms
#define _CREATE_ALIAS(original, alias) __asm__(".globl " original "; .globl " alias "; .equiv " alias ", " original)
#define XPC_CLASS_SYMBOL(name) _xpc_type_ ## name
#define XPC_CLASS_SYMBOL_DECL(name) \
XPC_EXPORT struct objc_class XPC_CLASS_SYMBOL(name); \
_CREATE_ALIAS(OS_OBJC_CLASS_RAW_SYMBOL_NAME(XPC_CLASS(name)), "_" OS_STRINGIFY(XPC_CLASS_SYMBOL(name)))
#define XPC_OBJC_CLASS(name) ((Class)&XPC_CLASS_SYMBOL(name))
#define XPC_GLOBAL_OBJECT_HEADER(className) \
.os_obj_isa = (const struct xpc_object_vtable_s*)XPC_OBJC_CLASS(className), \
.os_obj_ref_cnt = _OS_OBJECT_GLOBAL_REFCNT, \
.os_obj_xref_cnt = _OS_OBJECT_GLOBAL_REFCNT
//
// base XPC class
//
struct xpc_object_vtable_s {
_OS_OBJECT_CLASS_HEADER();
};
struct xpc_object_s {
_OS_OBJECT_HEADER(
const struct xpc_object_vtable_s* os_obj_isa,
os_obj_ref_cnt,
os_obj_xref_cnt
);
};
XPC_EXPORT
@interface XPC_CLASS(object) : OS_OBJECT_CLASS(object) <XPC_CLASS(object)>
+ (NSUInteger)_instanceSize;
// note that this method returns a string that must be freed
- (char*)xpcDescription;
@end
#endif // _XPC_OBJECTS_BASE_H_

View File

@ -0,0 +1,26 @@
#ifndef _XPC_OBJECTS_BOOL_H_
#define _XPC_OBJECTS_BOOL_H_
#import <xpc/objects/base.h>
// bools are NOT simple wrapper objects because they are global singletons
XPC_CLASS_DECL(bool);
struct xpc_bool_s {
struct xpc_object_s base;
bool value;
};
#undef bool
@interface XPC_CLASS_INTERFACE(bool)
@property(assign) BOOL value;
- (instancetype)initWithValue: (BOOL)value;
+ (instancetype)boolForValue: (BOOL)value;
@end
#define bool _Bool
#endif // _XPC_OBJECTS_BOOL_H_

View File

@ -0,0 +1,16 @@
#ifndef _XPC_OBJECTS_BUNDLE_H_
#define _XPC_OBJECTS_BUNDLE_H_
#import <xpc/objects/base.h>
XPC_CLASS_DECL(bundle);
struct xpc_bundle_s {
struct xpc_object_s base;
};
@interface XPC_CLASS_INTERFACE(bundle)
@end
#endif // _XPC_OBJECTS_BUNDLE_H_

View File

@ -0,0 +1,16 @@
#ifndef _XPC_OBJECTS_CONNECTION_H_
#define _XPC_OBJECTS_CONNECTION_H_
#import <xpc/objects/base.h>
XPC_CLASS_DECL(connection);
struct xpc_connection_s {
struct xpc_object_s base;
};
@interface XPC_CLASS_INTERFACE(connection)
@end
#endif // _XPC_OBJECTS_CONNECTION_H_

View File

@ -0,0 +1,33 @@
#ifndef _XPC_OBJECTS_DATA_H_
#define _XPC_OBJECTS_DATA_H_
#import <xpc/objects/base.h>
#import <dispatch/dispatch.h>
XPC_CLASS_DECL(data);
struct xpc_data_s {
struct xpc_object_s base;
dispatch_data_t data;
};
@interface XPC_CLASS_INTERFACE(data)
// this API is modeled after NSMutableData
// NOTE: deviates from NSData by always returning a pointer to the internal buffer, even if `length` is 0
@property(readonly) const void* bytes;
@property(readonly) NSUInteger length;
- (instancetype)initWithBytes: (const void*)bytes length: (NSUInteger)length;
- (instancetype)initWithDispatchData: (dispatch_data_t)data;
// NOTE: deviates from NSData by returning the number of bytes copied
- (NSUInteger)getBytes: (void*)buffer length: (NSUInteger)length;
// non-NSData method
- (void)replaceBytesWithBytes: (const void*)bytes length: (NSUInteger)length;
@end
#endif // _XPC_OBJECTS_DATA_H_

View File

@ -0,0 +1,8 @@
#ifndef _XPC_OBJECTS_DATE_H_
#define _XPC_OBJECTS_DATE_H_
#import <xpc/objects/base.h>
XPC_WRAPPER_CLASS_DECL(date, int64_t);
#endif // _XPC_OBJECTS_DATE_H_

View File

@ -0,0 +1,54 @@
#ifndef _XPC_OBJECTS_DICTIONARY_H_
#define _XPC_OBJECTS_DICTIONARY_H_
#import <xpc/objects/base.h>
#import <xpc/objects/connection.h>
#include <sys/queue.h>
@class XPC_CLASS(string);
XPC_CLASS_DECL(dictionary);
typedef struct xpc_dictionary_entry_s* xpc_dictionary_entry_t;
struct xpc_dictionary_entry_s {
LIST_ENTRY(xpc_dictionary_entry_s) link;
XPC_CLASS(object)* object;
char name[];
};
struct xpc_dictionary_s {
struct xpc_object_s base;
NSUInteger size;
LIST_HEAD(, xpc_dictionary_entry_s) head;
XPC_CLASS(connection)* associatedConnection;
};
@interface XPC_CLASS_INTERFACE(dictionary)
// this API is modeled after NSMutableDictionary
@property(readonly) NSUInteger count;
// NOTE: i'm not sure if the associated connection should be strongly or weakly associated
@property(strong) XPC_CLASS(connection)* associatedConnection;
- (instancetype)initWithObjects: (XPC_CLASS(object)* const*)objects forKeys: (const char* const*)keys count: (NSUInteger)count;
- (XPC_CLASS(object)*)objectForKey: (const char*)key;
- (void)setObject: (XPC_CLASS(object)*)object forKey: (const char*)key;
- (void)removeObjectForKey: (const char*)key;
- (void)enumerateKeysAndObjectsUsingBlock: (void (^)(const char* key, XPC_CLASS(object)* obj, BOOL* stop))block;
// unfortunately, no keyed subscripts for this class because `const char*`s aren't valid subscripts
// NOTE: consider these as private methods
- (xpc_dictionary_entry_t)entryForKey: (const char*)key;
- (void)addEntry: (xpc_dictionary_entry_t)entry;
- (void)removeEntry: (xpc_dictionary_entry_t)entry;
// useful extensions:
- (XPC_CLASS(string)*)stringForKey: (const char*)key;
@end
#endif // _XPC_OBJECTS_DICTIONARY_H_

View File

@ -0,0 +1,8 @@
#ifndef _XPC_OBJECTS_DOUBLE_H_
#define _XPC_OBJECTS_DOUBLE_H_
#import <xpc/objects/base.h>
XPC_WRAPPER_CLASS_DECL(double, double);
#endif // _XPC_OBJECTS_DOUBLE_H_

View File

@ -0,0 +1,19 @@
#ifndef _XPC_OBJECTS_ENDPOINT_H_
#define _XPC_OBJECTS_ENDPOINT_H_
#import <xpc/objects/base.h>
#import <xpc/objects/connection.h>
XPC_CLASS_DECL(endpoint);
struct xpc_endpoint_s {
struct xpc_object_s base;
};
@interface XPC_CLASS_INTERFACE(endpoint)
- (instancetype)initWithConnection: (XPC_CLASS(connection)*)connection;
@end
#endif // _XPC_OBJECTS_ENDPOINT_H_

View File

@ -0,0 +1,18 @@
#ifndef _XPC_OBJECTS_ERROR_H_
#define _XPC_OBJECTS_ERROR_H_
#import <xpc/objects/dictionary.h>
// errors are unique XPC objects: they're basically dictionaries
OS_OBJECT_DECL_SUBCLASS(xpc_error, xpc_object);
struct xpc_error_s {
struct xpc_dictionary_s base;
};
@interface XPC_CLASS(error) : XPC_CLASS(dictionary) <XPC_CLASS(error)>
@end
#endif // _XPC_OBJECTS_ERROR_H_

View File

@ -0,0 +1,26 @@
#ifndef _XPC_OBJECTS_FD_H_
#define _XPC_OBJECTS_FD_H_
#import <xpc/objects/base.h>
#include <mach/mach_port.h>
XPC_CLASS_DECL(fd);
struct xpc_fd_s {
struct xpc_object_s base;
mach_port_t port;
};
@interface XPC_CLASS_INTERFACE(fd)
@property(readonly) mach_port_t port;
- (instancetype)initWithDescriptor: (int)descriptor;
- (instancetype)initWithPort: (mach_port_t)port;
- (int)instantiateDescriptor;
@end
#endif // _XPC_OBJECTS_FD_H_

View File

@ -0,0 +1,16 @@
#ifndef _XPC_OBJECTS_FILE_TRANSFER_H_
#define _XPC_OBJECTS_FILE_TRANSFER_H_
#import <xpc/objects/base.h>
XPC_CLASS_DECL(file_transfer);
struct xpc_file_transfer_s {
struct xpc_object_s base;
};
@interface XPC_CLASS_INTERFACE(file_transfer)
@end
#endif // _XPC_OBJECTS_FILE_TRANSFER_H_

View File

@ -0,0 +1,8 @@
#ifndef _XPC_OBJECTS_INT64_H_
#define _XPC_OBJECTS_INT64_H_
#import <xpc/objects/base.h>
XPC_WRAPPER_CLASS_DECL(int64, int64_t);
#endif // _XPC_OBJECTS_INT64_H_

View File

@ -0,0 +1,24 @@
#ifndef _XPC_OBJECTS_MACH_RECV_H_
#define _XPC_OBJECTS_MACH_RECV_H_
#import <xpc/objects/base.h>
#import <mach/port.h>
XPC_CLASS_DECL(mach_recv);
struct xpc_mach_recv_s {
struct xpc_object_s base;
};
@interface XPC_CLASS_INTERFACE(mach_recv)
@end
//
// private C API
//
xpc_object_t xpc_mach_recv_create(mach_port_t recv);
mach_port_t xpc_mach_recv_extract_right(xpc_object_t xrecv);
#endif // _XPC_OBJECTS_MACH_RECV_H_

View File

@ -0,0 +1,27 @@
#ifndef _XPC_OBJECTS_MACH_SEND_H_
#define _XPC_OBJECTS_MACH_SEND_H_
#import <xpc/objects/base.h>
#import <mach/mach_port.h>
XPC_CLASS_DECL(mach_send);
struct xpc_mach_send_s {
struct xpc_object_s base;
};
@interface XPC_CLASS_INTERFACE(mach_send)
@end
//
// private C API
//
xpc_object_t xpc_mach_send_create(mach_port_t send);
mach_port_t xpc_mach_send_copy_right(xpc_object_t xsend);
xpc_object_t xpc_mach_send_create_with_disposition(mach_port_t send, unsigned int disposition);
mach_port_t xpc_mach_send_get_right(xpc_object_t xsend);
mach_port_t xpc_mach_send_extract_right(xpc_object_t xsend);
#endif // _XPC_OBJECTS_MACH_SEND_H_

View File

@ -0,0 +1,20 @@
#ifndef _XPC_OBJECTS_NULL_H_
#define _XPC_OBJECTS_NULL_H_
#import <xpc/objects/base.h>
XPC_CLASS_DECL(null);
struct xpc_null_s {
struct xpc_object_s base;
};
@interface XPC_CLASS_INTERFACE(null)
// this API is modeled after NSNull
+ (instancetype)null;
@end
#endif // _XPC_OBJECTS_NULL_H_

View File

@ -0,0 +1,16 @@
#ifndef _XPC_OBJECTS_PIPE_H_
#define _XPC_OBJECTS_PIPE_H_
#import <xpc/objects/base.h>
XPC_CLASS_DECL(pipe);
struct xpc_pipe_s {
struct xpc_object_s base;
};
@interface XPC_CLASS_INTERFACE(pipe)
@end
#endif // _XPC_OBJECTS_PIPE_H_

View File

@ -0,0 +1,8 @@
#ifndef _XPC_OBJECTS_POINTER_H_
#define _XPC_OBJECTS_POINTER_H_
#import <xpc/objects/base.h>
XPC_WRAPPER_CLASS_DECL(pointer, void*);
#endif // _XPC_OBJECTS_POINTER_H_

View File

@ -0,0 +1,16 @@
#ifndef _XPC_OBJECTS_SERIALIZER_H_
#define _XPC_OBJECTS_SERIALIZER_H_
#import <xpc/objects/base.h>
XPC_CLASS_DECL(serializer);
struct xpc_serializer_s {
struct xpc_object_s base;
};
@interface XPC_CLASS_INTERFACE(serializer)
@end
#endif // _XPC_OBJECTS_SERIALIZER_H_

View File

@ -0,0 +1,16 @@
#ifndef _XPC_OBJECTS_SERVICE_H_
#define _XPC_OBJECTS_SERVICE_H_
#import <xpc/objects/base.h>
XPC_CLASS_DECL(service);
struct xpc_service_s {
struct xpc_object_s base;
};
@interface XPC_CLASS_INTERFACE(service)
@end
#endif // _XPC_OBJECTS_SERVICE_H_

View File

@ -0,0 +1,16 @@
#ifndef _XPC_OBJECTS_SERVICE_INSTANCE_H_
#define _XPC_OBJECTS_SERVICE_INSTANCE_H_
#import <xpc/objects/base.h>
XPC_CLASS_DECL(service_instance);
struct xpc_service_instance_s {
struct xpc_object_s base;
};
@interface XPC_CLASS_INTERFACE(service_instance)
@end
#endif // _XPC_OBJECTS_SERVICE_INSTANCE_H_

View File

@ -0,0 +1,16 @@
#ifndef _XPC_OBJECTS_SHMEM_H_
#define _XPC_OBJECTS_SHMEM_H_
#import <xpc/objects/base.h>
XPC_CLASS_DECL(shmem);
struct xpc_shmem_s {
struct xpc_object_s base;
};
@interface XPC_CLASS_INTERFACE(shmem)
@end
#endif // _XPC_OBJECTS_SHMEM_H_

View File

@ -0,0 +1,35 @@
#ifndef _XPC_OBJECTS_STRING_H_
#define _XPC_OBJECTS_STRING_H_
#import <xpc/objects/base.h>
#include <stdarg.h>
XPC_CLASS_DECL(string);
struct xpc_string_s {
struct xpc_object_s base;
NSUInteger byteLength; // acts as an optional cache
BOOL freeWhenDone;
const char* string;
};
@interface XPC_CLASS_INTERFACE(string)
// this API is modeled after NSString
// non-NSString property; length of the string in bytes, not including the null terminator
@property(readonly) NSUInteger byteLength;
@property(readonly) const char* UTF8String;
- (instancetype)initWithUTF8String: (const char*)string;
// non-NSString method
- (instancetype)initWithUTF8StringNoCopy: (const char*)string freeWhenDone: (BOOL)freeIt;
- (instancetype)initWithFormat: (const char*)format, ...;
- (instancetype)initWithFormat: (const char*)format arguments: (va_list)args;
// non-NSString method
- (void)replaceStringWithString: (const char*)string;
@end
#endif // _XPC_OBJECTS_STRING_H_

View File

@ -0,0 +1,8 @@
#ifndef _XPC_OBJECTS_UINT64_H_
#define _XPC_OBJECTS_UINT64_H_
#import <xpc/objects/base.h>
XPC_WRAPPER_CLASS_DECL(uint64, uint64_t);
#endif // _XPC_OBJECTS_UINT64_H_

View File

@ -0,0 +1,22 @@
#ifndef _XPC_OBJECTS_UUID_H_
#define _XPC_OBJECTS_UUID_H_
#import <xpc/objects/base.h>
#include <uuid/uuid.h>
XPC_CLASS_DECL(uuid);
struct xpc_uuid_s {
struct xpc_object_s base;
uuid_t value;
};
@interface XPC_CLASS_INTERFACE(uuid)
@property(readonly) uint8_t* bytes;
- (instancetype)initWithBytes: (const uint8_t*)bytes;
@end
#endif // _XPC_OBJECTS_UUID_H_

View File

@ -0,0 +1,10 @@
#import <os/object.h>
#if __OBJC2__
// Objective-C 2.0 marks `struct objc_class` as unavailable by default.
// we know what we're doing; let's prevent the compiler from complaining.
#define OBJC2_UNAVAILABLE
#endif
#define XPC_TYPE(name) struct objc_class name
#define XPC_DECL(name) OS_OBJECT_DECL_SUBCLASS(name, xpc_object)

View File

@ -0,0 +1,90 @@
#ifndef _XPC_UTIL_H_
#define _XPC_UTIL_H_
#import <xpc/objects/base.h>
#import <xpc/objects/connection.h>
#include <stdlib.h>
#include <stdbool.h>
#include <mach/mach.h>
/**
* Defines `objcName` by casting `origName`, but also checks to ensure it's not `nil`
* and to ensure it is an XPC object of the given `className`.
*
* You should call it with a (possibly braced) statement of what to do when it passes these checks.
* You can optionally append an `else` clause for what to do when it fails these checks.
*
* @code
* xpc_object_t some_input = get_some_input();
* TO_OBJC_CHECKED(string, some_input, some_checked_input) {
* printf("Yay! It's a string! Look: %s\n", some_checked_input.description.UTF8String); // note that this assumes NSString is loaded
* } else {
* printf("Oh no, you messed up and gave me an object that wasn't a string :(\n");
* }
* @endcode
*/
#define TO_OBJC_CHECKED(className, origName, objcName) \
XPC_CLASS(className)* objcName = XPC_CAST(className, origName); \
if (objcName && [objcName isKindOfClass: [XPC_CLASS(className) class]])
/**
* Like `TO_OBJC_CHECKED`, but the condition checks for failure to pass the checks.
*
* @see `TO_OBJC_CHECKED`
*/
#define TO_OBJC_CHECKED_ON_FAIL(className, origName, objcName) \
XPC_CLASS(className)* objcName = XPC_CAST(className, origName); \
if (!objcName || ![objcName isKindOfClass: [XPC_CLASS(className) class]])
/**
* Special `retain` variant for collection classes like dictionaries and arrays.
*
* This is necessary because collections do not retain certain types of objects
* and just store them weakly.
*
* @returns The object passed in, possibly with an increased reference count.
*/
XPC_CLASS(object)* xpc_retain_for_collection(XPC_CLASS(object)* object);
/**
* Special `release` variant for collection classes like dictionaries and arrays.
*
* @see `xpc_retain_for_collection`
*
* @returns The object passed in, possibly with a decreased reference count.
*/
void xpc_release_for_collection(XPC_CLASS(object)* object);
/**
* Maps the given object to a class name.
*
* @returns The mapped class name for the object.
*/
const char* xpc_class_name(XPC_CLASS(object)* object);
/**
* Creates a string that is an indented copy of the given string.
*
* @returns A string that must be freed.
*/
char* xpc_description_indent(const char* description, bool indentFirstLine);
/**
* Produces a hash from the given data.
*
* @returns A hash of the input data.
*/
size_t xpc_raw_data_hash(const void* data, size_t data_length);
/**
* Increments the reference count on the given right for the given port.
*/
kern_return_t xpc_mach_port_retain_right(mach_port_name_t port, mach_port_right_t right);
/**
* Decrements the reference count on the given right for the given port.
*/
kern_return_t xpc_mach_port_release_right(mach_port_name_t port, mach_port_right_t right);
#endif // _XPC_UTIL_H_

View File

@ -1,635 +0,0 @@
#
# the symbols that are commented out are ones we don't have defined yet
#
#_OBJC_CLASS_$_OS_xpc_object
#_OBJC_METACLASS_$_OS_xpc_object
_XPC_ACTIVITY_ALLOW_BATTERY
#_XPC_ACTIVITY_APP_REFRESH
_XPC_ACTIVITY_CHECK_IN
#_XPC_ACTIVITY_COMMUNICATES_WITH_PAIRED_DEVICE
#_XPC_ACTIVITY_CPU_INTENSIVE
_XPC_ACTIVITY_DELAY
#_XPC_ACTIVITY_DESIRED_MOTION_STATE
#_XPC_ACTIVITY_DISK_INTENSIVE
#_XPC_ACTIVITY_DO_IT_LATER
#_XPC_ACTIVITY_DUET_ACTIVITY_SCHEDULER_DATA
#_XPC_ACTIVITY_DUET_ATTRIBUTE_COST
#_XPC_ACTIVITY_DUET_ATTRIBUTE_NAME
#_XPC_ACTIVITY_DUET_ATTRIBUTE_VALUE
#_XPC_ACTIVITY_DUET_RELATED_APPLICATIONS
#_XPC_ACTIVITY_EXCLUSIVE
#_XPC_ACTIVITY_EXPECTED_DURATION
_XPC_ACTIVITY_GRACE_PERIOD
#_XPC_ACTIVITY_GROUP_CONCURRENCY_LIMIT
#_XPC_ACTIVITY_GROUP_NAME
_XPC_ACTIVITY_INTERVAL
_XPC_ACTIVITY_INTERVAL_15_MIN
_XPC_ACTIVITY_INTERVAL_1_DAY
_XPC_ACTIVITY_INTERVAL_1_HOUR
_XPC_ACTIVITY_INTERVAL_1_MIN
_XPC_ACTIVITY_INTERVAL_30_MIN
_XPC_ACTIVITY_INTERVAL_4_HOURS
_XPC_ACTIVITY_INTERVAL_5_MIN
_XPC_ACTIVITY_INTERVAL_7_DAYS
_XPC_ACTIVITY_INTERVAL_8_HOURS
#_XPC_ACTIVITY_MAY_REBOOT_DEVICE
#_XPC_ACTIVITY_MEMORY_INTENSIVE
#_XPC_ACTIVITY_MOTION_STATE_AUTOMOTIVE
#_XPC_ACTIVITY_MOTION_STATE_AUTOMOTIVE_MOVING
#_XPC_ACTIVITY_MOTION_STATE_AUTOMOTIVE_STATIONARY
#_XPC_ACTIVITY_MOTION_STATE_CYCLING
#_XPC_ACTIVITY_MOTION_STATE_RUNNING
#_XPC_ACTIVITY_MOTION_STATE_STATIONARY
#_XPC_ACTIVITY_MOTION_STATE_WALKING
#_XPC_ACTIVITY_NETWORK_TRANSFER_BIDIRECTIONAL
_XPC_ACTIVITY_NETWORK_TRANSFER_DIRECTION
_XPC_ACTIVITY_NETWORK_TRANSFER_DIRECTION_DOWNLOAD
_XPC_ACTIVITY_NETWORK_TRANSFER_DIRECTION_UPLOAD
#_XPC_ACTIVITY_NETWORK_TRANSFER_ENDPOINT
#_XPC_ACTIVITY_NETWORK_TRANSFER_PARAMETERS
#_XPC_ACTIVITY_NETWORK_TRANSFER_SIZE
#_XPC_ACTIVITY_POST_INSTALL
#_XPC_ACTIVITY_POWER_NAP
_XPC_ACTIVITY_PRIORITY
_XPC_ACTIVITY_PRIORITY_MAINTENANCE
_XPC_ACTIVITY_PRIORITY_UTILITY
#_XPC_ACTIVITY_RANDOM_INITIAL_DELAY
_XPC_ACTIVITY_REPEATING
#_XPC_ACTIVITY_REPLY_ENDPOINT
#_XPC_ACTIVITY_REQUIRES_BUDDY_COMPLETE
#_XPC_ACTIVITY_REQUIRES_CLASS_A
#_XPC_ACTIVITY_REQUIRES_CLASS_B
#_XPC_ACTIVITY_REQUIRES_CLASS_C
_XPC_ACTIVITY_REQUIRE_BATTERY_LEVEL
_XPC_ACTIVITY_REQUIRE_HDD_SPINNING
_XPC_ACTIVITY_REQUIRE_INEXPENSIVE_NETWORK_CONNECTIVITY
_XPC_ACTIVITY_REQUIRE_NETWORK_CONNECTIVITY
_XPC_ACTIVITY_REQUIRE_SCREEN_SLEEP
#_XPC_ACTIVITY_REQUIRE_SIGNIFICANT_USER_INACTIVITY
#_XPC_ACTIVITY_SEQUENCE_NUMBER
#_XPC_ACTIVITY_SHOULD_WAKE_DEVICE
#_XPC_ACTIVITY_USER_REQUESTED_BACKUP_TASK
#_XPC_ACTIVITY_USES_DUET_POWER_BUDGETING
#_XPC_COALITION_INFO_KEY_BUNDLE_IDENTIFIER
#_XPC_COALITION_INFO_KEY_CID
#_XPC_COALITION_INFO_KEY_NAME
#_XPC_COALITION_INFO_KEY_RESOURCE_USAGE_BLOB
#__availability_version_check
#__launch_msg2
#__launch_service_stats_copy_4ppse_impl
__libxpc_initializer
__spawn_via_launchd
#__system_ios_support_version_copy_string_sysctl
#__system_version_copy_string_plist
#__system_version_copy_string_sysctl
#__system_version_fallback
#__system_version_parse_string
__vproc_get_last_exit_status
__vproc_grab_subset
__vproc_kickstart_by_label
__vproc_log
__vproc_log_error
__vproc_logv
__vproc_pid_is_managed
__vproc_post_fork_ping
__vproc_send_signal_by_label
__vproc_set_global_on_demand
__vproc_standby_begin
__vproc_standby_count
__vproc_standby_end
__vproc_standby_timeout
__vproc_transaction_begin
__vproc_transaction_count
__vproc_transaction_count_for_pid
__vproc_transaction_end
__vproc_transaction_set_clean_callback
__vproc_transaction_try_exit
__vproc_transactions_enable
__vprocmgr_detach_from_console
__vprocmgr_getsocket
__vprocmgr_init
__vprocmgr_log_drain
__vprocmgr_log_forward
__vprocmgr_move_subset_to_user
__vprocmgr_switch_to_session
#__xpc_bool_create_distinct
__xpc_bool_false
#__xpc_bool_set_value
__xpc_bool_true
#__xpc_connection_set_event_handler_f
#__xpc_data_set_value
#__xpc_dictionary_create_reply_with_port
#__xpc_dictionary_extract_mach_send
#__xpc_dictionary_extract_reply_msg_id
#__xpc_dictionary_extract_reply_port
#__xpc_dictionary_get_reply_msg_id
#__xpc_dictionary_set_remote_connection
#__xpc_dictionary_set_reply_msg_id
#__xpc_double_set_value
__xpc_error_connection_interrupted
__xpc_error_connection_invalid
__xpc_error_key_description
__xpc_error_termination_imminent
__xpc_event_key_name
#__xpc_event_key_stream_name
#__xpc_fd_get_port
#__xpc_int64_set_value
#__xpc_payload_create_from_mach_msg
#__xpc_pipe_handle_mig
#__xpc_runtime_get_entitlements_data
#__xpc_runtime_get_self_entitlements
__xpc_runtime_is_app_sandboxed
#__xpc_service_last_xref_cancel
#__xpc_shmem_get_mach_port
#__xpc_spawnattr_pack_string
#__xpc_spawnattr_pack_string_fragment
#__xpc_spawnattr_unpack_string
#__xpc_spawnattr_unpack_strings
#__xpc_string_set_value
__xpc_type_activity
__xpc_type_array
#__xpc_type_base
__xpc_type_bool
#__xpc_type_bundle
__xpc_type_connection
__xpc_type_data
__xpc_type_date
__xpc_type_dictionary
__xpc_type_double
__xpc_type_endpoint
__xpc_type_error
__xpc_type_fd
#__xpc_type_file_transfer
__xpc_type_int64
#__xpc_type_mach_recv
#__xpc_type_mach_send
__xpc_type_null
#__xpc_type_pipe
__xpc_type_pointer
#__xpc_type_serializer
#__xpc_type_service
#__xpc_type_service_instance
__xpc_type_shmem
__xpc_type_string
__xpc_type_uint64
__xpc_type_uuid
_bootstrap_check_in
_bootstrap_check_in2
_bootstrap_check_in3
_bootstrap_create_server
_bootstrap_create_service
_bootstrap_get_root
_bootstrap_info
_bootstrap_init
_bootstrap_look_up
_bootstrap_look_up2
_bootstrap_look_up3
_bootstrap_look_up_per_user
_bootstrap_lookup_children
_bootstrap_parent
_bootstrap_register
_bootstrap_register2
_bootstrap_status
_bootstrap_strerror
_bootstrap_subset
_bootstrap_unprivileged
_create_and_switch_to_per_session_launchd
_launch_activate_socket
#_launch_add_external_service
#_launch_bootout_user_service_4coresim
#_launch_copy_busy_extension_instances
#_launch_copy_endpoints_properties_for_pid
#_launch_copy_extension_properties
#_launch_copy_extension_properties_for_pid
#_launch_copy_properties_for_pid_4assertiond
#_launch_create_persona
_launch_data_alloc
_launch_data_array_get_count
_launch_data_array_get_index
_launch_data_array_set_index
_launch_data_copy
_launch_data_dict_get_count
_launch_data_dict_insert
_launch_data_dict_iterate
_launch_data_dict_lookup
_launch_data_dict_remove
_launch_data_free
_launch_data_get_bool
_launch_data_get_errno
_launch_data_get_fd
_launch_data_get_integer
_launch_data_get_machport
_launch_data_get_opaque
_launch_data_get_opaque_size
_launch_data_get_real
_launch_data_get_string
_launch_data_get_type
_launch_data_new_bool
_launch_data_new_errno
_launch_data_new_fd
_launch_data_new_integer
_launch_data_new_machport
_launch_data_new_opaque
_launch_data_new_real
_launch_data_new_string
_launch_data_pack
_launch_data_set_bool
_launch_data_set_errno
_launch_data_set_fd
_launch_data_set_integer
_launch_data_set_machport
_launch_data_set_opaque
_launch_data_set_real
_launch_data_set_string
_launch_data_unpack
#_launch_destroy_persona
#_launch_disable_directory
#_launch_enable_directory
#_launch_extension_check_in_live_4UIKit
#_launch_extension_property_bundle_id
#_launch_extension_property_host_bundle_id
#_launch_extension_property_host_pid
#_launch_extension_property_path
#_launch_extension_property_pid
#_launch_extension_property_version
#_launch_extension_property_xpc_bundle
_launch_get_fd
#_launch_get_service_enabled
#_launch_get_system_service_enabled
_launch_msg
#_launch_path_for_user_service_4coresim
#_launch_perfcheck_property_endpoint_active
#_launch_perfcheck_property_endpoint_event
#_launch_perfcheck_property_endpoint_name
#_launch_perfcheck_property_endpoint_needs_activation
#_launch_perfcheck_property_endpoints
#_launch_remove_external_service
#_launch_service_stats_disable_4ppse
#_launch_service_stats_enable_4ppse
#_launch_service_stats_is_enabled_4ppse
#_launch_set_service_enabled
#_launch_set_system_service_enabled
_launch_socket_service_check_in
#_launch_version_for_user_service_4coresim
_launch_wait
_launchd_close
_launchd_fdopen
_launchd_getfd
_launchd_msg_recv
_launchd_msg_send
_load_launchd_jobs_at_loginwindow_prompt
_mpm_uncork_fork
_mpm_wait
_os_system_version_get_current_version
#_os_system_version_sim_get_current_host_version
#_os_transaction_copy_description
_os_transaction_create
#_os_transaction_needs_more_time
#_place_hold_on_real_loginwindow
_reboot2
_reboot3
_vproc_release
_vproc_retain
_vproc_standby_begin
_vproc_standby_end
_vproc_swap_complex
_vproc_swap_integer
_vproc_swap_string
_vproc_transaction_begin
_vproc_transaction_end
_vprocmgr_lookup_vproc
#_xpc_activity_add_eligibility_changed_handler
_xpc_activity_copy_criteria
#_xpc_activity_copy_dispatch_queue
#_xpc_activity_copy_identifier
#_xpc_activity_debug
#_xpc_activity_defer_until_network_change
#_xpc_activity_defer_until_percentage
#_xpc_activity_get_percentage
_xpc_activity_get_state
#_xpc_activity_list
_xpc_activity_register
#_xpc_activity_remove_eligibility_changed_handler
#_xpc_activity_run
#_xpc_activity_set_completion_status
_xpc_activity_set_criteria
#_xpc_activity_set_network_threshold
_xpc_activity_set_state
_xpc_activity_should_defer
_xpc_activity_unregister
#_xpc_add_bundle
#_xpc_add_bundles_for_domain
_xpc_array_append_value
_xpc_array_apply
#_xpc_array_apply_f
#_xpc_array_copy_mach_send
_xpc_array_create
#_xpc_array_create_connection
_xpc_array_dup_fd
#_xpc_array_get_array
_xpc_array_get_bool
_xpc_array_get_count
_xpc_array_get_data
_xpc_array_get_date
#_xpc_array_get_dictionary
_xpc_array_get_double
_xpc_array_get_int64
_xpc_array_get_pointer
_xpc_array_get_string
_xpc_array_get_uint64
_xpc_array_get_uuid
_xpc_array_get_value
_xpc_array_set_bool
_xpc_array_set_connection
_xpc_array_set_data
_xpc_array_set_date
_xpc_array_set_double
_xpc_array_set_fd
_xpc_array_set_int64
#_xpc_array_set_mach_send
_xpc_array_set_pointer
_xpc_array_set_string
_xpc_array_set_uint64
_xpc_array_set_uuid
_xpc_array_set_value
_xpc_atfork_child
_xpc_atfork_parent
_xpc_atfork_prepare
_xpc_bool_create
_xpc_bool_get_value
#_xpc_bundle_copy_info_dictionary
#_xpc_bundle_copy_resource_path
#_xpc_bundle_copy_services
#_xpc_bundle_create
#_xpc_bundle_create_from_origin
#_xpc_bundle_create_main
#_xpc_bundle_get_error
#_xpc_bundle_get_executable_path
#_xpc_bundle_get_info_dictionary
#_xpc_bundle_get_path
#_xpc_bundle_get_property
#_xpc_bundle_get_xpcservice_dictionary
#_xpc_bundle_populate
#_xpc_bundle_resolve
#_xpc_bundle_resolve_on_queue
#_xpc_bundle_resolve_sync
#_xpc_coalition_copy_info
#_xpc_coalition_history_pipe_async
_xpc_connection_activate
_xpc_connection_cancel
#_xpc_connection_copy_bundle_id
_xpc_connection_copy_entitlement_value
_xpc_connection_create
_xpc_connection_create_from_endpoint
#_xpc_connection_create_listener
_xpc_connection_create_mach_service
#_xpc_connection_enable_sim2host_4sim
#_xpc_connection_enable_termination_imminent_event
_xpc_connection_get_asid
_xpc_connection_get_audit_token
#_xpc_connection_get_bs_type
_xpc_connection_get_context
#_xpc_connection_get_egid
_xpc_connection_get_euid
#_xpc_connection_get_instance
_xpc_connection_get_name
_xpc_connection_get_pid
#_xpc_connection_is_extension
#_xpc_connection_kill
_xpc_connection_resume
_xpc_connection_send_barrier
_xpc_connection_send_message
_xpc_connection_send_message_with_reply
_xpc_connection_send_message_with_reply_sync
#_xpc_connection_send_notification
#_xpc_connection_set_bootstrap
#_xpc_connection_set_bs_type
_xpc_connection_set_context
#_xpc_connection_set_event_channel
_xpc_connection_set_event_handler
_xpc_connection_set_finalizer_f
_xpc_connection_set_instance
_xpc_connection_set_legacy
#_xpc_connection_set_non_launching
#_xpc_connection_set_oneshot_instance
_xpc_connection_set_privileged
#_xpc_connection_set_qos_class_fallback
#_xpc_connection_set_qos_class_floor
_xpc_connection_set_target_queue
_xpc_connection_set_target_uid
_xpc_connection_suspend
#_xpc_copy
#_xpc_copy_bootstrap
#_xpc_copy_clean_description
#_xpc_copy_code_signing_identity_for_token
#_xpc_copy_debug_description
_xpc_copy_description
#_xpc_copy_domain
#_xpc_copy_entitlement_for_self
_xpc_copy_entitlement_for_token
#_xpc_copy_entitlements_data_for_token
_xpc_copy_entitlements_for_pid
#_xpc_copy_entitlements_for_self
#_xpc_copy_event
#_xpc_copy_event_entitlements
#_xpc_copy_extension_sdk_entry
#_xpc_copy_short_description
_xpc_create_from_plist
#_xpc_create_from_plist_descriptor
#_xpc_create_from_serialization
#_xpc_create_from_serialization_with_ool
_xpc_create_reply_with_format
#_xpc_create_reply_with_format_and_arguments
_xpc_create_with_format
#_xpc_create_with_format_and_arguments
_xpc_data_create
_xpc_data_create_with_dispatch_data
_xpc_data_get_bytes
_xpc_data_get_bytes_ptr
#_xpc_data_get_inline_max
_xpc_data_get_length
_xpc_date_create
#_xpc_date_create_absolute
_xpc_date_create_from_current
_xpc_date_get_value
#_xpc_date_get_value_absolute
#_xpc_date_is_int64_range
_xpc_dictionary_apply
#_xpc_dictionary_apply_f
#_xpc_dictionary_copy_basic_description
_xpc_dictionary_copy_mach_send
_xpc_dictionary_create
#_xpc_dictionary_create_connection
_xpc_dictionary_create_reply
_xpc_dictionary_dup_fd
#_xpc_dictionary_expects_reply
#_xpc_dictionary_extract_mach_recv
#_xpc_dictionary_get_array
_xpc_dictionary_get_audit_token
_xpc_dictionary_get_bool
#_xpc_dictionary_get_connection
_xpc_dictionary_get_count
_xpc_dictionary_get_data
#_xpc_dictionary_get_date
_xpc_dictionary_get_dictionary
_xpc_dictionary_get_double
_xpc_dictionary_get_int64
_xpc_dictionary_get_pointer
_xpc_dictionary_get_remote_connection
_xpc_dictionary_get_string
_xpc_dictionary_get_uint64
_xpc_dictionary_get_uuid
_xpc_dictionary_get_value
#_xpc_dictionary_handoff_reply
#_xpc_dictionary_handoff_reply_f
#_xpc_dictionary_send_reply
_xpc_dictionary_set_bool
#_xpc_dictionary_set_connection
_xpc_dictionary_set_data
#_xpc_dictionary_set_date
_xpc_dictionary_set_double
_xpc_dictionary_set_fd
_xpc_dictionary_set_int64
_xpc_dictionary_set_mach_recv
_xpc_dictionary_set_mach_send
_xpc_dictionary_set_pointer
_xpc_dictionary_set_string
_xpc_dictionary_set_uint64
_xpc_dictionary_set_uuid
_xpc_dictionary_set_value
_xpc_double_create
_xpc_double_get_value
#_xpc_endpoint_compare
#_xpc_endpoint_copy_listener_port_4sim
_xpc_endpoint_create
#_xpc_endpoint_create_bs_named
#_xpc_endpoint_create_mach_port_4sim
_xpc_equal
#_xpc_event_publisher_activate
#_xpc_event_publisher_create
#_xpc_event_publisher_fire
#_xpc_event_publisher_fire_noboost
#_xpc_event_publisher_fire_with_reply
#_xpc_event_publisher_fire_with_reply_sync
#_xpc_event_publisher_get_subscriber_asid
#_xpc_event_publisher_set_error_handler
#_xpc_event_publisher_set_handler
#_xpc_event_publisher_set_initial_load_completed_handler_4remoted
#_xpc_event_publisher_set_subscriber_keepalive
#_xpc_event_stream_check_in
#_xpc_event_stream_check_in2
#_xpc_exit_reason_get_label
#_xpc_extension_type_init
_xpc_fd_create
_xpc_fd_dup
#_xpc_file_transfer_cancel
#_xpc_file_transfer_copy_io
#_xpc_file_transfer_create_with_fd
#_xpc_file_transfer_create_with_path
#_xpc_file_transfer_get_size
#_xpc_file_transfer_get_transfer_id
#_xpc_file_transfer_send_finished
#_xpc_file_transfer_set_transport_writing_callbacks
#_xpc_file_transfer_write_finished
#_xpc_file_transfer_write_to_fd
#_xpc_file_transfer_write_to_path
#_xpc_generate_audit_token
#_xpc_get_attachment_endpoint
#_xpc_get_class4NSXPC
#_xpc_get_event_name
_xpc_get_type
#_xpc_handle_service
#_xpc_handle_subservice
_xpc_hash
#_xpc_impersonate_user
#_xpc_init_services
#_xpc_inspect_copy_description
#_xpc_inspect_copy_description_local
#_xpc_inspect_copy_short_description
#_xpc_inspect_copy_short_description_local
#_xpc_install_remote_hooks
_xpc_int64_create
_xpc_int64_get_value
#_xpc_is_kind_of_xpc_object4NSXPC
#_xpc_mach_recv_create
#_xpc_mach_recv_extract_right
#_xpc_mach_send_copy_right
#_xpc_mach_send_create
#_xpc_mach_send_create_with_disposition
#_xpc_mach_send_get_right
_xpc_main
#_xpc_make_serialization
#_xpc_make_serialization_with_ool
_xpc_null_create
_xpc_pipe_create
#_xpc_pipe_create_from_port
_xpc_pipe_invalidate
_xpc_pipe_receive
_xpc_pipe_routine
#_xpc_pipe_routine_async
#_xpc_pipe_routine_forward
_xpc_pipe_routine_reply
#_xpc_pipe_routine_with_flags
#_xpc_pipe_simpleroutine
_xpc_pipe_try_receive
_xpc_pointer_create
_xpc_pointer_get_value
#_xpc_receive_mach_msg
#_xpc_receive_remote_msg
_xpc_release
_xpc_retain
#_xpc_service_attach
#_xpc_service_create
#_xpc_service_create_from_specifier
#_xpc_service_get_rendezvous_token
#_xpc_service_instance_dup2
#_xpc_service_instance_get_context
#_xpc_service_instance_get_host_pid
#_xpc_service_instance_get_pid
#_xpc_service_instance_get_type
#_xpc_service_instance_is_configurable
#_xpc_service_instance_run
#_xpc_service_instance_set_binpref
#_xpc_service_instance_set_context
#_xpc_service_instance_set_cwd
#_xpc_service_instance_set_endpoint
#_xpc_service_instance_set_environment
#_xpc_service_instance_set_finalizer_f
#_xpc_service_instance_set_jetsam_properties
#_xpc_service_instance_set_path
#_xpc_service_instance_set_start_suspended
#_xpc_service_kickstart
#_xpc_service_set_attach_handler
#_xpc_set_event
#_xpc_set_event_state
#_xpc_set_event_stream_handler
#_xpc_set_event_with_flags
#_xpc_set_idle_handler
#_xpc_shmem_create
#_xpc_shmem_create_readonly
#_xpc_shmem_get_length
#_xpc_shmem_map
_xpc_strerror
_xpc_string_create
#_xpc_string_create_no_copy
_xpc_string_create_with_format
_xpc_string_create_with_format_and_arguments
_xpc_string_get_length
_xpc_string_get_string_ptr
#_xpc_test_symbols_exported
_xpc_track_activity
_xpc_transaction_begin
_xpc_transaction_end
_xpc_transaction_exit_clean
#_xpc_transaction_interrupt_clean_exit
#_xpc_transactions_enable
#_xpc_type_get_name
_xpc_uint64_create
_xpc_uint64_get_value
_xpc_uuid_create
_xpc_uuid_get_bytes
# not in the official libxpc
# maybe it belongs elsewhere?
_ld2xpc

View File

@ -0,0 +1,112 @@
__spawn_via_launchd
__vproc_get_last_exit_status
__vproc_grab_subset
__vproc_kickstart_by_label
__vproc_log
__vproc_log_error
__vproc_logv
__vproc_pid_is_managed
__vproc_post_fork_ping
__vproc_send_signal_by_label
__vproc_set_global_on_demand
__vproc_standby_begin
__vproc_standby_count
__vproc_standby_end
__vproc_standby_timeout
__vproc_transaction_begin
__vproc_transaction_count
__vproc_transaction_count_for_pid
__vproc_transaction_end
__vproc_transaction_set_clean_callback
__vproc_transaction_try_exit
__vproc_transactions_enable
__vprocmgr_detach_from_console
__vprocmgr_getsocket
__vprocmgr_init
__vprocmgr_log_drain
__vprocmgr_log_forward
__vprocmgr_move_subset_to_user
__vprocmgr_switch_to_session
_bootstrap_check_in
_bootstrap_check_in2
_bootstrap_check_in3
_bootstrap_create_server
_bootstrap_create_service
_bootstrap_get_root
_bootstrap_info
_bootstrap_init
_bootstrap_look_up
_bootstrap_look_up2
_bootstrap_look_up3
_bootstrap_look_up_per_user
_bootstrap_lookup_children
_bootstrap_parent
_bootstrap_register
_bootstrap_register2
_bootstrap_status
_bootstrap_strerror
_bootstrap_subset
_bootstrap_unprivileged
_create_and_switch_to_per_session_launchd
_launch_data_alloc
_launch_data_array_get_count
_launch_data_array_get_index
_launch_data_array_set_index
_launch_data_copy
_launch_data_dict_get_count
_launch_data_dict_insert
_launch_data_dict_iterate
_launch_data_dict_lookup
_launch_data_dict_remove
_launch_data_free
_launch_data_get_bool
_launch_data_get_errno
_launch_data_get_fd
_launch_data_get_integer
_launch_data_get_machport
_launch_data_get_opaque
_launch_data_get_opaque_size
_launch_data_get_real
_launch_data_get_string
_launch_data_get_type
_launch_data_new_bool
_launch_data_new_errno
_launch_data_new_fd
_launch_data_new_integer
_launch_data_new_machport
_launch_data_new_opaque
_launch_data_new_real
_launch_data_new_string
_launch_data_pack
_launch_data_set_bool
_launch_data_set_errno
_launch_data_set_fd
_launch_data_set_integer
_launch_data_set_machport
_launch_data_set_opaque
_launch_data_set_real
_launch_data_set_string
_launch_data_unpack
_launch_get_fd
_launch_msg
_launch_socket_service_check_in
_launch_wait
_launchd_close
_launchd_fdopen
_launchd_getfd
_launchd_msg_recv
_launchd_msg_send
_load_launchd_jobs_at_loginwindow_prompt
_mpm_uncork_fork
_mpm_wait
_reboot2
_vproc_release
_vproc_retain
_vproc_standby_begin
_vproc_standby_end
_vproc_swap_complex
_vproc_swap_integer
_vproc_swap_string
_vproc_transaction_begin
_vproc_transaction_end
_vprocmgr_lookup_vproc

View File

@ -1,144 +0,0 @@
/*
This file is part of Darling.
Copyright (C) 2017 Darling developers
Darling is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Darling is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Darling. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Block.h>
#include <xpc/xpc.h>
#include <xpc/internal.h>
const char *XPC_ACTIVITY_INTERVAL = "Interval";
const char *XPC_ACTIVITY_REPEATING = "Repeating";
const char *XPC_ACTIVITY_DELAY = "Delay";
const char *XPC_ACTIVITY_GRACE_PERIOD = "GracePeriod";
const int64_t XPC_ACTIVITY_INTERVAL_1_MIN = 60;
const int64_t XPC_ACTIVITY_INTERVAL_5_MIN = 300;
const int64_t XPC_ACTIVITY_INTERVAL_15_MIN = 900;
const int64_t XPC_ACTIVITY_INTERVAL_30_MIN = 1800;
const int64_t XPC_ACTIVITY_INTERVAL_1_HOUR = 3600;
const int64_t XPC_ACTIVITY_INTERVAL_4_HOURS = 14400;
const int64_t XPC_ACTIVITY_INTERVAL_8_HOURS = 28800;
const int64_t XPC_ACTIVITY_INTERVAL_1_DAY = 86400;
const int64_t XPC_ACTIVITY_INTERVAL_7_DAYS = 604800;
const char *XPC_ACTIVITY_PRIORITY = "Priority";
const char *XPC_ACTIVITY_PRIORITY_MAINTENANCE = "Maintenance";
const char *XPC_ACTIVITY_PRIORITY_UTILITY = "Utility";
const char *XPC_ACTIVITY_ALLOW_BATTERY = "AllowBattery";
const char *XPC_ACTIVITY_REQUIRE_SCREEN_SLEEP = "RequireScreenSleep";
const char *XPC_ACTIVITY_REQUIRE_BATTERY_LEVEL = "RequireBatteryLevel";
const char *XPC_ACTIVITY_REQUIRE_HDD_SPINNING = "RequireHDDSpinning";
const char *XPC_ACTIVITY_REQUIRE_NETWORK_CONNECTIVITY = "RequireNetworkConnectivity";
const char *XPC_ACTIVITY_REQUIRE_INEXPENSIVE_NETWORK_CONNECTIVITY = "RequireInexpensiveNetworkConnectivity";
const char *XPC_ACTIVITY_NETWORK_TRANSFER_DIRECTION = "NetworkTransferDirection";
const char *XPC_ACTIVITY_NETWORK_TRANSFER_DIRECTION_DOWNLOAD = "Download";
const char *XPC_ACTIVITY_NETWORK_TRANSFER_DIRECTION_UPLOAD = "Upload";
static const struct xpc_object _xpc_activity_check_in = {
.xo_xpc_type = _XPC_TYPE_STRING,
.xo_size = 10, /* strlen("<CHECK-IN>") */
.xo_refcnt = 1,
.xo_u = {
.str = "<CHECK-IN>"
}
};
const xpc_object_t XPC_ACTIVITY_CHECK_IN = &_xpc_activity_check_in;
void
xpc_activity_register(const char *identifier, xpc_object_t criteria,
xpc_activity_handler_t handler)
{
/* TODO: global activity registry, XPC_ACTIVITY_CHECK_IN, actual scheduling */
xpc_u value;
xpc_activity_t activity = _xpc_prim_create(_XPC_TYPE_ACTIVITY, value, 0);
xpc_activity_set_criteria(activity, criteria);
xpc_activity_set_state(activity, XPC_ACTIVITY_STATE_WAIT);
xpc_activity_handler_t handler_copy =
(xpc_activity_handler_t) Block_copy(handler);
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_async(q, ^ {
xpc_activity_set_state(activity, XPC_ACTIVITY_STATE_RUN);
handler_copy(activity);
Block_release(handler_copy);
xpc_release(activity);
});
}
xpc_object_t
xpc_activity_copy_criteria(xpc_activity_t activity)
{
struct xpc_object *xo;
xo = activity;
return xo->xo_activity.criteria;
}
void
xpc_activity_set_criteria(xpc_activity_t activity, xpc_object_t criteria)
{
struct xpc_object *xo;
xo = activity;
xpc_release(xo->xo_activity.criteria);
xpc_retain(criteria);
xo->xo_activity.criteria = criteria;
}
xpc_activity_state_t
xpc_activity_get_state(xpc_activity_t activity)
{
struct xpc_object *xo;
xo = activity;
return xo->xo_activity.state;
}
bool
xpc_activity_set_state(xpc_activity_t activity, xpc_activity_state_t state)
{
struct xpc_object *xo;
xo = activity;
xo->xo_activity.state = state;
return true;
}
bool
xpc_activity_should_defer(xpc_activity_t activity)
{
return false;
}
void
xpc_activity_unregister(const char *identifier)
{
}
void xpc_track_activity(void)
{
}

228
src/activity.m Normal file
View File

@ -0,0 +1,228 @@
#import <xpc/objects/activity.h>
#import <xpc/objects/string.h>
#import <xpc/util.h>
#import <xpc/xpc.h>
#import <xpc/activity.h>
XPC_CLASS_SYMBOL_DECL(activity);
// strangely enough, the variables themselves aren't marked as const
#define KEY_DEF(name, string) XPC_EXPORT const char* XPC_ACTIVITY_ ## name = string
#define INTERVAL_DEF(name, seconds) XPC_EXPORT const int64_t XPC_ACTIVITY_INTERVAL_ ## name = seconds
KEY_DEF(ALLOW_BATTERY, "AllowBattery");
KEY_DEF(APP_REFRESH, "AppRefresh");
KEY_DEF(COMMUNICATES_WITH_PAIRED_DEVICE, "CommunicatesWithPairedDevice");
KEY_DEF(DELAY, "Delay");
KEY_DEF(DO_IT_LATER, "DoItLater");
KEY_DEF(EXCLUSIVE, "Exclusive");
KEY_DEF(EXPECTED_DURATION, "ExpectedDuration");
KEY_DEF(GRACE_PERIOD, "GracePeriod");
KEY_DEF(GROUP_CONCURRENCY_LIMIT, "ActivityGroupConcurrencyLimit");
KEY_DEF(GROUP_NAME, "ActivityGroupName");
KEY_DEF(MAY_REBOOT_DEVICE, "MayRebootDevice");
KEY_DEF(POST_INSTALL, "PostInstall");
KEY_DEF(POWER_NAP, "PowerNap");
KEY_DEF(RANDOM_INITIAL_DELAY, "RandomInitialDelay");
KEY_DEF(REPEATING, "Repeating");
KEY_DEF(REPLY_ENDPOINT, "_ReplyEndpoint");
KEY_DEF(SEQUENCE_NUMBER, "_SequenceNumber");
KEY_DEF(SHOULD_WAKE_DEVICE, "ShouldWakeDevice");
KEY_DEF(USER_REQUESTED_BACKUP_TASK, "UserRequestedBackupTask");
// interval
KEY_DEF(INTERVAL, "Interval");
INTERVAL_DEF(1_MIN, 1 * 60);
INTERVAL_DEF(5_MIN, 5 * 60);
INTERVAL_DEF(15_MIN, 15 * 60);
INTERVAL_DEF(30_MIN, 30 * 60);
INTERVAL_DEF(1_HOUR, 1 * 60 * 60);
INTERVAL_DEF(4_HOURS, 4 * 60 * 60);
INTERVAL_DEF(8_HOURS, 8 * 60 * 60);
INTERVAL_DEF(1_DAY, 1 * 24 * 60 * 60);
INTERVAL_DEF(7_DAYS, 7 * 24 * 60 * 60);
// resource intesive
KEY_DEF(CPU_INTENSIVE, "CPUIntensive");
KEY_DEF(DISK_INTENSIVE, "DiskIntensive");
KEY_DEF(MEMORY_INTENSIVE, "MemoryIntensive");
// priority
KEY_DEF(PRIORITY, "Priority");
KEY_DEF(PRIORITY_MAINTENANCE, "Maintenance");
KEY_DEF(PRIORITY_UTILITY, "Utility");
// motion state
KEY_DEF(DESIRED_MOTION_STATE, "MotionState");
KEY_DEF(MOTION_STATE_AUTOMOTIVE, "Automotive");
KEY_DEF(MOTION_STATE_AUTOMOTIVE_MOVING, "AutomotiveMoving");
KEY_DEF(MOTION_STATE_AUTOMOTIVE_STATIONARY, "AutomotiveStationary");
KEY_DEF(MOTION_STATE_CYCLING, "Cycling");
KEY_DEF(MOTION_STATE_RUNNING, "Running");
KEY_DEF(MOTION_STATE_STATIONARY, "Stationary");
KEY_DEF(MOTION_STATE_WALKING, "Walking");
// network transfer
KEY_DEF(NETWORK_TRANSFER_ENDPOINT, "NetworkEndpoint");
KEY_DEF(NETWORK_TRANSFER_PARAMETERS, "NetworkParameters");
KEY_DEF(NETWORK_TRANSFER_SIZE, "NetworkTransferSize");
KEY_DEF(NETWORK_TRANSFER_DIRECTION, "NetworkTransferDirection");
KEY_DEF(NETWORK_TRANSFER_BIDIRECTIONAL, "Bidirectional");
KEY_DEF(NETWORK_TRANSFER_DIRECTION_DOWNLOAD, "Download");
KEY_DEF(NETWORK_TRANSFER_DIRECTION_UPLOAD, "Upload");
// requirements
KEY_DEF(REQUIRES_BUDDY_COMPLETE, "RequiresBuddyComplete");
KEY_DEF(REQUIRES_CLASS_A, "RequiresClassA");
KEY_DEF(REQUIRES_CLASS_B, "RequiresClassB");
KEY_DEF(REQUIRES_CLASS_C, "RequiresClassC");
KEY_DEF(REQUIRE_BATTERY_LEVEL, "RequireBatteryLevel");
KEY_DEF(REQUIRE_HDD_SPINNING, "RequireHDDSpinning");
KEY_DEF(REQUIRE_INEXPENSIVE_NETWORK_CONNECTIVITY, "RequireInexpensiveNetworkConnectivity");
KEY_DEF(REQUIRE_NETWORK_CONNECTIVITY, "RequireNetworkConnectivity");
KEY_DEF(REQUIRE_SCREEN_SLEEP, "RequireScreenSleep");
KEY_DEF(REQUIRE_SIGNIFICANT_USER_INACTIVITY, "RequireSignificantUserInactivity");
// Duet stuff
KEY_DEF(DUET_ACTIVITY_SCHEDULER_DATA, "DASData");
KEY_DEF(DUET_ATTRIBUTE_COST, "DuetAttributeCost");
KEY_DEF(DUET_ATTRIBUTE_NAME, "DuetAttributeName");
KEY_DEF(DUET_ATTRIBUTE_VALUE, "DuetAttributeValue");
KEY_DEF(DUET_RELATED_APPLICATIONS, "DuetRelatedApplications");
KEY_DEF(USES_DUET_POWER_BUDGETING, "DuetPowerBudgeting");
static const struct xpc_string_s _xpc_activity_check_in = {
.base = {
XPC_GLOBAL_OBJECT_HEADER(string),
},
.byteLength = NSUIntegerMax,
.string = "<CHECK-IN>",
.freeWhenDone = false,
};
XPC_EXPORT
const xpc_object_t XPC_ACTIVITY_CHECK_IN = XPC_GLOBAL_OBJECT(_xpc_activity_check_in);
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(activity)
XPC_CLASS_HEADER(activity);
@end
//
// C API
//
XPC_EXPORT
void xpc_activity_register(const char *identifier, xpc_object_t criteria, xpc_activity_handler_t handler) {
};
XPC_EXPORT
xpc_object_t xpc_activity_copy_criteria(xpc_activity_t activity) {
return NULL;
};
XPC_EXPORT
void xpc_activity_set_criteria(xpc_activity_t activity, xpc_object_t criteria) {
};
XPC_EXPORT
xpc_activity_state_t xpc_activity_get_state(xpc_activity_t activity) {
return 0;
};
XPC_EXPORT
bool xpc_activity_set_state(xpc_activity_t activity, xpc_activity_state_t state) {
return false;
};
XPC_EXPORT
bool xpc_activity_should_defer(xpc_activity_t activity) {
return false;
};
XPC_EXPORT
void xpc_activity_unregister(const char* identifier) {
};
//
// private C API
//
struct xpc_activity_eligibility_changed_handler_s;
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
return NULL;
};
XPC_EXPORT
dispatch_queue_t xpc_activity_copy_dispatch_queue(xpc_activity_t xactivity) {
return NULL;
};
XPC_EXPORT
char* xpc_activity_copy_identifier(xpc_activity_t xactivity) {
// returns a string that must be freed
return NULL;
};
XPC_EXPORT
void xpc_activity_debug(const char* identifier, uint64_t flags) {
};
XPC_EXPORT
int xpc_activity_defer_until_network_change() {
// not a stub
// just returns 0
// no indiciation of parameters, but probably takes an activity object as an argument
return 0;
};
XPC_EXPORT
int xpc_activity_defer_until_percentage() {
// not a stub
// just returns 0
return 0;
};
XPC_EXPORT
int xpc_activity_get_percentage() {
// not a stub
// just returns 0
return 0;
};
XPC_EXPORT
void xpc_activity_list(const char* identifier) {
};
XPC_EXPORT
void xpc_activity_remove_eligibility_changed_handler(xpc_activity_t xactivity, struct xpc_activity_eligibility_changed_handler_s* handler_context) {
};
XPC_EXPORT
void xpc_activity_run(const char* identifier) {
};
XPC_EXPORT
void xpc_activity_set_completion_status(xpc_activity_t activity, uint64_t status) {
};
XPC_EXPORT
void xpc_activity_set_network_threshold() {
// not a stub
// just does nothing
// probably has parameters, but no way to tell
};

View File

@ -1,339 +0,0 @@
/*
* Copyright 2014-2015 iXsystems, Inc.
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <sys/types.h>
#include <mach/mach.h>
#include <xpc/launchd.h>
#include <xpc/internal.h>
xpc_object_t
xpc_array_create(const xpc_object_t *objects, size_t count)
{
struct xpc_object *xo;
size_t i;
xpc_u val; bzero(&val, sizeof(val));
xo = _xpc_prim_create(_XPC_TYPE_ARRAY, val, 0);
for (i = 0; i < count; i++)
xpc_array_append_value(xo, objects[i]);
return (xo);
}
void
xpc_array_set_value(xpc_object_t xarray, size_t index, xpc_object_t value)
{
struct xpc_object *xo, *xotmp, *xotmp2;
struct xpc_array_head *arr;
size_t i;
xo = xarray;
arr = &xo->xo_array;
i = 0;
if (index == XPC_ARRAY_APPEND)
return xpc_array_append_value(xarray, value);
if (index >= (size_t)xo->xo_size)
return;
TAILQ_FOREACH_SAFE(xotmp, arr, xo_link, xotmp2) {
if (i++ == index) {
TAILQ_INSERT_AFTER(arr, (struct xpc_object *)value,
xotmp, xo_link);
TAILQ_REMOVE(arr, xotmp, xo_link);
xpc_retain(value);
xpc_release(xotmp);
break;
}
}
}
void
xpc_array_append_value(xpc_object_t xarray, xpc_object_t value)
{
struct xpc_object *xo;
struct xpc_array_head *arr;
xo = xarray;
arr = &xo->xo_array;
TAILQ_INSERT_TAIL(arr, (struct xpc_object *)value, xo_link);
xpc_retain(value);
++xo->xo_size;
}
xpc_object_t
xpc_array_get_value(xpc_object_t xarray, size_t index)
{
struct xpc_object *xo, *xotmp;
struct xpc_array_head *arr;
size_t i;
xo = xarray;
arr = &xo->xo_array;
i = 0;
if (index > xo->xo_size)
return (NULL);
TAILQ_FOREACH(xotmp, arr, xo_link) {
if (i++ == index)
return (xotmp);
}
return (NULL);
}
size_t
xpc_array_get_count(xpc_object_t xarray)
{
struct xpc_object *xo;
xo = xarray;
return (xo->xo_size);
}
void
xpc_array_set_bool(xpc_object_t xarray, size_t index, bool value)
{
struct xpc_object *xo, *xotmp;
xo = xarray;
xotmp = xpc_bool_create(value);
return (xpc_array_set_value(xarray, index, xotmp));
}
void
xpc_array_set_int64(xpc_object_t xarray, size_t index, int64_t value)
{
struct xpc_object *xo, *xotmp;
xo = xarray;
xotmp = xpc_int64_create(value);
return (xpc_array_set_value(xarray, index, xotmp));
}
void
xpc_array_set_uint64(xpc_object_t xarray, size_t index, uint64_t value)
{
struct xpc_object *xo, *xotmp;
xo = xarray;
xotmp = xpc_uint64_create(value);
return (xpc_array_set_value(xarray, index, xotmp));
}
void
xpc_array_set_double(xpc_object_t xarray, size_t index, double value)
{
struct xpc_object *xo, *xotmp;
xo = xarray;
xotmp = xpc_double_create(value);
return (xpc_array_set_value(xarray, index, xotmp));
}
void
xpc_array_set_date(xpc_object_t xarray, size_t index, int64_t value)
{
struct xpc_object *xo, *xotmp;
xo = xarray;
xotmp = xpc_date_create(value);
return (xpc_array_set_value(xarray, index, xotmp));
}
void
xpc_array_set_data(xpc_object_t xarray, size_t index, const void *data,
size_t length)
{
struct xpc_object *xo, *xotmp;
xo = xarray;
xotmp = xpc_data_create(data, length);
return (xpc_array_set_value(xarray, index, xotmp));
}
void
xpc_array_set_string(xpc_object_t xarray, size_t index, const char *string)
{
struct xpc_object *xo, *xotmp;
xo = xarray;
xotmp = xpc_string_create(string);
return (xpc_array_set_value(xarray, index, xotmp));
}
void
xpc_array_set_uuid(xpc_object_t xarray, size_t index, const uuid_t value)
{
struct xpc_object *xo, *xotmp;
xo = xarray;
xotmp = xpc_uuid_create(value);
return (xpc_array_set_value(xarray, index, xotmp));
}
void
xpc_array_set_fd(xpc_object_t xarray, size_t index, int value)
{
struct xpc_object *xo, *xotmp;
xo = xarray;
xotmp = xpc_fd_create(value);
return (xpc_array_set_value(xarray, index, xotmp));
}
void
xpc_array_set_connection(xpc_object_t xarray, size_t index,
xpc_connection_t value)
{
}
bool
xpc_array_get_bool(xpc_object_t xarray, size_t index)
{
struct xpc_object *xotmp;
xotmp = xpc_array_get_value(xarray, index);
return (xpc_bool_get_value(xotmp));
}
int64_t
xpc_array_get_int64(xpc_object_t xarray, size_t index)
{
struct xpc_object *xotmp;
xotmp = xpc_array_get_value(xarray, index);
return (xpc_int64_get_value(xotmp));
}
uint64_t
xpc_array_get_uint64(xpc_object_t xarray, size_t index)
{
struct xpc_object *xotmp;
xotmp = xpc_array_get_value(xarray, index);
return (xpc_uint64_get_value(xotmp));
}
double
xpc_array_get_double(xpc_object_t xarray, size_t index)
{
struct xpc_object *xotmp;
xotmp = xpc_array_get_value(xarray, index);
return (xpc_double_get_value(xotmp));
}
int64_t
xpc_array_get_date(xpc_object_t xarray, size_t index)
{
struct xpc_object *xotmp;
xotmp = xpc_array_get_value(xarray, index);
return (xpc_date_get_value(xotmp));
}
const void *
xpc_array_get_data(xpc_object_t xarray, size_t index, size_t *length)
{
struct xpc_object *xotmp;
xotmp = xpc_array_get_value(xarray, index);
*length = xpc_data_get_length(xotmp);
return (xpc_data_get_bytes_ptr(xotmp));
}
const uint8_t *
xpc_array_get_uuid(xpc_object_t xarray, size_t index)
{
struct xpc_object *xotmp;
xotmp = xpc_array_get_value(xarray, index);
return (xpc_uuid_get_bytes(xotmp));
}
const char *
xpc_array_get_string(xpc_object_t xarray, size_t index)
{
struct xpc_object *xotmp;
xotmp = xpc_array_get_value(xarray, index);
return (xpc_string_get_string_ptr(xotmp));
}
int
xpc_array_dup_fd(xpc_object_t array, size_t index)
{
/* XXX */
return (-1);
}
xpc_connection_t
xpc_array_get_connection(xpc_object_t array, size_t index)
{
/* XXX */
return (NULL);
}
bool
xpc_array_apply(xpc_object_t xarray, xpc_array_applier_t applier)
{
struct xpc_object *xo, *xotmp;
struct xpc_array_head *arr;
size_t i;
i = 0;
xo = xarray;
arr = &xo->xo_array;
TAILQ_FOREACH(xotmp, arr, xo_link) {
if (!applier(i++, xotmp))
return (false);
}
return (true);
}
void* xpc_array_get_pointer(xpc_object_t xarray, size_t index) {
struct xpc_object* xo = xpc_array_get_value(xarray, index);
if (xo)
return xpc_pointer_get_value(xo);
return NULL;
};
void xpc_array_set_pointer(xpc_object_t xarray, size_t index, void* value) {
struct xpc_object* xo = xpc_pointer_create(value);
xpc_array_set_value(xarray, index, xo);
xpc_release(xo);
};

390
src/array.m Normal file
View File

@ -0,0 +1,390 @@
#import <xpc/objects/array.h>
#import <xpc/util.h>
#import <xpc/private.h>
#import <xpc/connection.h>
#import <xpc/objects/mach_send.h>
#import <xpc/objects/dictionary.h>
XPC_CLASS_SYMBOL_DECL(array);
// the old code for xpc_array used each xpc_object as a linked list node
// and stored a head pointer, but this code uses an actual array because:
// 1. random access is constant time with arrays
// 2. xpc_arrays can only grow by appending values
// 3. xpc_arrays can never shrink or have items deleted
// 4. slightly less memory per object
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(array)
XPC_CLASS_HEADER(array);
- (void)dealloc
{
XPC_THIS_DECL(array);
for (NSUInteger i = 0; i < this->size; ++i) {
xpc_release_for_collection(this->array[i]);
}
free(this->array);
[super dealloc];
}
- (char*)xpcDescription
{
char* output = NULL;
size_t count = self.count;
if (count == 0) {
asprintf(&output, "<%s: %p> []", xpc_class_name(self), self);
return output;
}
char** descriptions = malloc(sizeof(char*) * count);
size_t outputLength = 0;
outputLength += snprintf(NULL, 0, "<%s: %p> [\n", xpc_class_name(self), self);
for (size_t i = 0; i < count; ++i) {
char* description = self[i].xpcDescription;
descriptions[i] = xpc_description_indent(description, true);
free(description);
outputLength += snprintf(NULL, 0, "%s\n", descriptions[i]);
}
outputLength += snprintf(NULL, 0, "]");
output = malloc(outputLength + 1);
size_t offset = 0;
offset += snprintf(output + offset, outputLength + 1 - offset, "<%s: %p> [\n", xpc_class_name(self), self);
for (size_t i = 0; i < count; ++i) {
offset += snprintf(output + offset, outputLength + 1 - offset, "%s\n", descriptions[i]);
free(descriptions[i]);
}
free(descriptions);
offset += snprintf(output + offset, outputLength + 1 - offset, "]");
return output;
}
- (NSUInteger)count
{
XPC_THIS_DECL(array);
return this->size;
}
- (instancetype)initWithObjects: (XPC_CLASS(object)* const*)objects count: (NSUInteger)count
{
if (self = [super init]) {
XPC_THIS_DECL(array);
this->array = malloc(count * sizeof(XPC_CLASS(object)*));
if (!this->array) {
[self release];
return nil;
}
this->size = count;
for (NSUInteger i = 0; i < count; ++i) {
this->array[i] = xpc_retain_for_collection(XPC_CAST(object, objects[i]));
}
}
return self;
}
- (XPC_CLASS(object)*)objectAtIndex: (NSUInteger)index
{
XPC_THIS_DECL(array);
if (index >= this->size) {
return nil;
}
return this->array[index];
}
- (void)addObject: (XPC_CLASS(object)*)object
{
XPC_THIS_DECL(array);
// TODO: check what we should actually do when someone tries to append `nil`
if (object == nil) {
return;
}
XPC_CLASS(object)** expandedArray = realloc(this->array, (this->size + 1) * sizeof(XPC_CLASS(object)*));
if (expandedArray == NULL) {
// we have no way to report errors, so just silently leave everything in its previous state
return;
}
this->array = expandedArray;
this->array[this->size++] = xpc_retain_for_collection(object);
}
- (void)replaceObjectAtIndex: (NSUInteger)index withObject: (XPC_CLASS(object)*)object
{
XPC_THIS_DECL(array);
if (index >= this->size) {
// again, no way to report errors
return;
}
XPC_CLASS(object)* old = this->array[index];
this->array[index] = xpc_retain_for_collection(object);
[old release];
}
- (void)enumerateObjectsUsingBlock: (void (^)(XPC_CLASS(object)* object, NSUInteger index, BOOL* stop))block
{
XPC_THIS_DECL(array);
for (NSUInteger i = 0; i < this->size; ++i) {
BOOL stop = NO;
block(this->array[i], i, &stop);
if (stop) {
break;
}
}
}
- (XPC_CLASS(object)*)objectAtIndexedSubscript: (NSUInteger)index
{
return [self objectAtIndex: index];
}
- (void)setObject: (XPC_CLASS(object)*)object atIndexedSubscript: (NSUInteger)index
{
return [self replaceObjectAtIndex: index withObject: object];
}
- (NSUInteger)countByEnumeratingWithState: (NSFastEnumerationState*)state objects: (id __unsafe_unretained [])objects count: (NSUInteger)count
{
if (state->state == 0) {
XPC_THIS_DECL(array);
// note that this will only detect mutations of adding new objects, not reassigning existing ones
state->mutationsPtr = &this->size;
state->itemsPtr = this->array;
state->state = 1;
return this->size;
} else {
return 0;
}
}
- (NSUInteger)hash
{
XPC_THIS_DECL(array);
NSUInteger result = 0;
for (NSUInteger i = 0; i < this->size; ++i) {
result += [this->array[i] hash];
}
return result;
}
- (instancetype)copy
{
XPC_THIS_DECL(array);
XPC_CLASS(array)* result = [XPC_CLASS(array) new];
for (NSUInteger i = 0; i < this->size; ++i) {
XPC_CLASS(object)* copied = [this->array[i] copy];
[result addObject: copied];
[copied release];
}
return result;
}
@end
//
// C API
//
XPC_EXPORT
xpc_object_t xpc_array_create(const xpc_object_t* objects, size_t count) {
return [[XPC_CLASS(array) alloc] initWithObjects: (XPC_CLASS(object)* const*)objects count: count];
};
XPC_EXPORT
void xpc_array_set_value(xpc_object_t xarray, size_t index, xpc_object_t value) {
if (index == XPC_ARRAY_APPEND) {
return xpc_array_append_value(xarray, value);
}
TO_OBJC_CHECKED(array, xarray, array) {
array[index] = XPC_CAST(object, value);
}
};
XPC_EXPORT
void xpc_array_append_value(xpc_object_t xarray, xpc_object_t value) {
TO_OBJC_CHECKED(array, xarray, array) {
[array addObject: XPC_CAST(object, value)];
}
};
XPC_EXPORT
size_t xpc_array_get_count(xpc_object_t xarray) {
TO_OBJC_CHECKED(array, xarray, array) {
return array.count;
}
return 0;
};
XPC_EXPORT
xpc_object_t xpc_array_get_value(xpc_object_t xarray, size_t index) {
TO_OBJC_CHECKED(array, xarray, array) {
return array[index];
}
return NULL;
};
XPC_EXPORT
bool xpc_array_apply(xpc_object_t xarray, xpc_array_applier_t applier) {
TO_OBJC_CHECKED(array, xarray, array) {
for (NSUInteger i = 0; i < array.count; ++i) {
if (!applier(i, array[i])) {
return false;
}
}
return true;
}
return false;
};
//
// private C API
//
XPC_EXPORT
void xpc_array_apply_f(xpc_object_t xarray, void* context, xpc_array_applier_f applier) {
xpc_array_apply(xarray, ^bool (size_t index, xpc_object_t value) {
applier(index, value, context);
return true;
});
};
//
// setters
//
#define SIMPLE_SETTER(name, type) \
XPC_EXPORT \
void xpc_array_set_ ## name(xpc_object_t xarray, size_t index, type value) { \
TO_OBJC_CHECKED(array, xarray, array) { \
xpc_object_t object = xpc_ ## name ## _create(value); \
xpc_array_set_value(xarray, index, object); \
xpc_release(object); \
} \
};
SIMPLE_SETTER(bool, bool);
SIMPLE_SETTER(int64, int64_t);
SIMPLE_SETTER(uint64, uint64_t);
SIMPLE_SETTER(double, double);
SIMPLE_SETTER(date, int64_t);
SIMPLE_SETTER(string, const char*);
SIMPLE_SETTER(uuid, const uuid_t);
SIMPLE_SETTER(fd, int);
SIMPLE_SETTER(pointer, void*);
SIMPLE_SETTER(mach_send, mach_port_t); // private setter
XPC_EXPORT
void xpc_array_set_data(xpc_object_t xarray, size_t index, const void* bytes, size_t length) {
TO_OBJC_CHECKED(array, xarray, array) {
xpc_object_t object = xpc_data_create(bytes, length);
xpc_array_set_value(xarray, index, object);
xpc_release(object);
}
};
XPC_EXPORT
void xpc_array_set_connection(xpc_object_t xarray, size_t index, xpc_connection_t connection) {
return xpc_array_set_value(xarray, index, connection);
};
//
// getters
//
#define SIMPLE_GETTER(name, type) \
XPC_EXPORT \
type xpc_array_get_ ## name(xpc_object_t xarray, size_t index) { \
xpc_object_t object = xpc_array_get_value(xarray, index); \
return xpc_ ## name ## _get_value(object); \
};
SIMPLE_GETTER(bool, bool);
SIMPLE_GETTER(int64, int64_t);
SIMPLE_GETTER(uint64, uint64_t);
SIMPLE_GETTER(double, double);
SIMPLE_GETTER(date, int64_t);
SIMPLE_GETTER(pointer, void*);
XPC_EXPORT
const void* xpc_array_get_data(xpc_object_t xarray, size_t index, size_t* length) {
xpc_object_t object = xpc_array_get_value(xarray, index);
if (length != NULL) {
*length = xpc_data_get_length(object);
}
return xpc_data_get_bytes_ptr(object);
};
XPC_EXPORT
const char* xpc_array_get_string(xpc_object_t xarray, size_t index) {
xpc_object_t object = xpc_array_get_value(xarray, index);
return xpc_string_get_string_ptr(object);
};
XPC_EXPORT
const uint8_t* xpc_array_get_uuid(xpc_object_t xarray, size_t index) {
xpc_object_t object = xpc_array_get_value(xarray, index);
return xpc_uuid_get_bytes(object);
};
XPC_EXPORT
int xpc_array_dup_fd(xpc_object_t xarray, size_t index) {
xpc_object_t object = xpc_array_get_value(xarray, index);
return xpc_fd_dup(object);
};
XPC_EXPORT
xpc_connection_t xpc_array_create_connection(xpc_object_t xarray, size_t index) {
xpc_object_t object = xpc_array_get_value(xarray, index);
return xpc_connection_create_from_endpoint((xpc_endpoint_t)object);
};
//
// private getters
//
XPC_EXPORT
mach_port_t xpc_array_copy_mach_send(xpc_object_t xarray, size_t index) {
xpc_object_t object = xpc_array_get_value(xarray, index);
return xpc_mach_send_copy_right(object);
};
XPC_EXPORT
xpc_object_t xpc_array_get_array(xpc_object_t xarray, size_t index) {
xpc_object_t object = xpc_array_get_value(xarray, index);
TO_OBJC_CHECKED(array, object, other_array) {
return other_array;
}
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_array_get_dictionary(xpc_object_t xarray, size_t index) {
xpc_object_t object = xpc_array_get_value(xarray, index);
TO_OBJC_CHECKED(dictionary, object, dict) {
return dict;
}
return NULL;
};

138
src/base.m Normal file
View File

@ -0,0 +1,138 @@
#import <xpc/objects/base.h>
#import <Foundation/NSZone.h>
#import <Foundation/NSString.h>
#import <objc/runtime.h>
#import <xpc/xpc.h>
// the symbol alias for the base xpc_object class is named differently
XPC_EXPORT struct objc_class _xpc_type_base;
_CREATE_ALIAS(OS_OBJC_CLASS_RAW_SYMBOL_NAME(XPC_CLASS(object)), "__xpc_type_base");
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(object)
XPC_CLASS_HEADER(object);
+ (instancetype)allocWithZone: (NSZone*)zone
{
return (id)_os_object_alloc_realized([self class], [self _instanceSize]);
}
- (instancetype)init
{
// we CANNOT call `-[super init]`.
// libdispatch makes `init` crash on `OS_object`s.
return self;
}
- (char*)xpcDescription
{
char* string = NULL;
asprintf(&string, "<%s: %p>", class_getName([self class]), self);
return string;
}
- (NSString*)description
{
Class nsstring = objc_lookUpClass("NSString");
if (!nsstring) return nil;
char* desc = self.xpcDescription;
NSString* string = [nsstring stringWithUTF8String: desc];
free(desc);
return string;
}
- (BOOL)isEqual: (id)object
{
if (![object isKindOfClass: [self class]]) {
return NO;
}
return [self hash] == [object hash];
}
- (instancetype)copy
{
return [self retain];
}
@end
//
// C API
//
XPC_EXPORT
xpc_object_t (xpc_retain)(xpc_object_t object) {
return [object retain];
};
XPC_EXPORT
void (xpc_release)(xpc_object_t object) {
return [object release];
};
XPC_EXPORT
xpc_object_t xpc_copy(xpc_object_t object) {
return [object copy];
};
XPC_EXPORT
bool xpc_equal(xpc_object_t object1, xpc_object_t object2) {
return [object1 isEqual: object2];
};
XPC_EXPORT
size_t xpc_hash(xpc_object_t object) {
return [object hash];
};
XPC_EXPORT
char* xpc_copy_description(xpc_object_t object) {
return [XPC_CAST(object, object) xpcDescription];
};
//
// private C API
//
XPC_EXPORT
char* xpc_copy_debug_description(xpc_object_t object) {
// for now
return xpc_copy_description(object);
};
XPC_EXPORT
char* xpc_copy_short_description(xpc_object_t object) {
// for now
return xpc_copy_description(object);
};
XPC_EXPORT
char* xpc_copy_clean_description(xpc_object_t object) {
// for now
return xpc_copy_description(object);
};
XPC_EXPORT
char* xpc_inspect_copy_description(xpc_object_t object, xpc_object_t another_object) {
// returns a string that must be freed
return NULL;
};
XPC_EXPORT
char* xpc_inspect_copy_description_local(xpc_object_t object, xpc_object_t another_object) {
// returns a string that must be freed
return NULL;
};
XPC_EXPORT
char* xpc_inspect_copy_short_description(xpc_object_t object, xpc_object_t another_object) {
// returns a string that must be freed
return NULL;
};
XPC_EXPORT
char* xpc_inspect_copy_short_description_local(xpc_object_t object, xpc_object_t another_object) {
// returns a string that must be freed
return NULL;
};

127
src/bool.m Normal file
View File

@ -0,0 +1,127 @@
#import <xpc/objects/bool.h>
#import <xpc/util.h>
#import <xpc/xpc.h>
#undef bool
// workaround because `xpc/xpc.h` uses `_xpc_bool_s` instead of `xpc_bool_s`
struct _xpc_bool_s {
struct xpc_bool_s the_real_thing;
};
XPC_CLASS_SYMBOL_DECL(bool);
XPC_EXPORT
const struct _xpc_bool_s _xpc_bool_true = {
.the_real_thing = {
.base = {
XPC_GLOBAL_OBJECT_HEADER(bool),
},
.value = true,
},
};
XPC_EXPORT
const struct _xpc_bool_s _xpc_bool_false = {
.the_real_thing = {
.base = {
XPC_GLOBAL_OBJECT_HEADER(bool),
},
.value = false,
},
};
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(bool)
XPC_CLASS_HEADER(bool);
- (char*)xpcDescription
{
char* output = NULL;
asprintf(&output, "<%s: %s>", xpc_class_name(self), self.value ? "TRUE" : "FALSE");
return output;
}
- (BOOL)value
{
XPC_THIS_DECL(bool);
return this->value;
}
- (void)setValue: (BOOL)value
{
XPC_THIS_DECL(bool);
this->value = value;
}
- (instancetype)initWithValue: (BOOL)value
{
if (self = [super init]) {
XPC_THIS_DECL(bool);
this->value = value;
}
return self;
}
+ (instancetype)boolForValue: (BOOL)value
{
if (value) {
return XPC_CAST(bool, &_xpc_bool_true);
} else {
return XPC_CAST(bool, &_xpc_bool_false);
}
}
- (NSUInteger)hash
{
XPC_THIS_DECL(bool);
if (this->value) {
return 0xb001;
} else {
return 0x100b;
}
}
@end
#define bool _Bool
//
// C API
//
XPC_EXPORT
xpc_object_t xpc_bool_create(bool value) {
return [XPC_CLASS(bool) boolForValue: value];
};
XPC_EXPORT
bool xpc_bool_get_value(xpc_object_t xbool) {
#undef bool
TO_OBJC_CHECKED(bool, xbool, boolObj) {
#define bool _Bool
return boolObj.value;
}
return false;
};
//
// private C API
//
XPC_EXPORT
xpc_object_t _xpc_bool_create_distinct(bool value) {
return [[XPC_CLASS(bool) alloc] initWithValue: value];
};
XPC_EXPORT
void _xpc_bool_set_value(xpc_object_t xbool, bool value) {
#undef bool
TO_OBJC_CHECKED(bool, xbool, boolObj) {
#define bool _Bool
boolObj.value = value;
}
};

113
src/bundle.m Normal file
View File

@ -0,0 +1,113 @@
#import <xpc/objects/bundle.h>
#import <xpc/util.h>
#import <xpc/xpc.h>
XPC_CLASS_SYMBOL_DECL(bundle);
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(bundle)
XPC_CLASS_HEADER(bundle);
@end
//
// private C API
//
XPC_EXPORT
xpc_object_t xpc_bundle_copy_info_dictionary(xpc_bundle_t xbundle) {
// doesn't actually copy the dictionary; only retains it
return NULL;
};
XPC_EXPORT
char* xpc_bundle_copy_resource_path(xpc_bundle_t xbundle) {
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_bundle_copy_services(xpc_bundle_t xbundle) {
// doesn't actually copy the array; only retains it
return NULL;
};
XPC_EXPORT
xpc_bundle_t xpc_bundle_create(const char* path, unsigned int flags) {
return NULL;
};
XPC_EXPORT
xpc_bundle_t xpc_bundle_create_from_origin(unsigned int origin, const char* path) {
return NULL;
};
XPC_EXPORT
xpc_bundle_t xpc_bundle_create_main(void) {
return NULL;
};
XPC_EXPORT
int xpc_bundle_get_error(xpc_bundle_t xbundle) {
// unsure about the return type
return -1;
};
XPC_EXPORT
const char* xpc_bundle_get_executable_path(xpc_bundle_t xbundle) {
// does NOT copy the path it returns
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_bundle_get_info_dictionary(xpc_bundle_t xbundle) {
return NULL;
};
XPC_EXPORT
const char* xpc_bundle_get_path(xpc_bundle_t xbundle) {
// does NOT copy the path it returns
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
return 0;
};
XPC_EXPORT
xpc_object_t xpc_bundle_get_xpcservice_dictionary(xpc_bundle_t xbundle) {
// the dictionary is contained within the info dictionary
return NULL;
};
XPC_EXPORT
void xpc_bundle_populate(xpc_bundle_t xbundle, xpc_object_t info_dictionary, xpc_object_t services_array) {
};
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_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_EXPORT
void xpc_bundle_resolve_sync(xpc_bundle_t xbundle) {
};
XPC_EXPORT
void xpc_add_bundle(const char* path, unsigned int flags) {
};
XPC_EXPORT
void xpc_add_bundles_for_domain(xpc_object_t domain, xpc_object_t bundles) {
};

20
src/coalition.m Normal file
View File

@ -0,0 +1,20 @@
#import <xpc/xpc.h>
XPC_EXPORT const char* XPC_COALITION_INFO_KEY_BUNDLE_IDENTIFIER = "bundle_identifier";
XPC_EXPORT const char* XPC_COALITION_INFO_KEY_CID = "cid";
XPC_EXPORT const char* XPC_COALITION_INFO_KEY_NAME = "name";
XPC_EXPORT const char* XPC_COALITION_INFO_KEY_RESOURCE_USAGE_BLOB = "resource-usage-blob";
//
// private C API
//
XPC_EXPORT
xpc_object_t xpc_coalition_copy_info(uint64_t cid) {
return NULL;
};
XPC_EXPORT
int xpc_coalition_history_pipe_async(int flags) {
return -1;
};

View File

@ -1,617 +0,0 @@
/*
* Copyright 2014-2015 iXsystems, Inc.
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <errno.h>
#include <mach/mach.h>
#include <servers/bootstrap.h>
#include <xpc/xpc.h>
#include <machine/atomic.h>
#include <Block.h>
#include <libkern/OSAtomic.h>
#include <xpc/internal.h>
static uint64_t generate_message_id(xpc_connection_t conn) {
uint64_t num = 0;
arc4random_buf(&num, sizeof(num));
return num;
};
static void xpc_connection_recv_message();
static void xpc_send(xpc_connection_t xconn, xpc_object_t message, uint64_t id);
static inline struct xpc_connection* conn_extract(xpc_connection_t obj)
{
struct xpc_object* o = (struct xpc_object*) obj;
return &o->xo_connection;
}
xpc_connection_t
xpc_connection_create(const char *name, dispatch_queue_t targetq)
{
kern_return_t kr;
char *qname;
struct xpc_connection *conn;
xpc_connection_t rv;
xpc_u val = { 0 };
rv = _xpc_prim_create(_XPC_TYPE_CONNECTION, val, 0);
if (rv == NULL)
return NULL;
debugf("xpc_connection_create(%s)\n", name);
conn = conn_extract(rv);
memset(conn, 0, sizeof(struct xpc_connection));
TAILQ_INIT(&conn->xc_peers);
TAILQ_INIT(&conn->xc_pending);
/* Create send queue */
asprintf(&qname, "com.ixsystems.xpc.connection.sendq.%p", conn);
conn->xc_send_queue = dispatch_queue_create(qname, NULL);
free(qname);
/* Create recv queue */
asprintf(&qname, "com.ixsystems.xpc.connection.recvq.%p", conn);
conn->xc_recv_queue = dispatch_queue_create(qname, NULL);
free(qname);
/* Create target queue */
conn->xc_target_queue = targetq ? targetq : dispatch_get_global_queue(DISPATCH_TARGET_QUEUE_DEFAULT, 0);
/* Receive queue is initially suspended */
dispatch_suspend(conn->xc_recv_queue);
/* Create local port */
kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
&conn->xc_local_port);
if (kr != KERN_SUCCESS) {
debugf("Failed to allocate port");
errno = EPERM;
return (NULL);
}
kr = mach_port_insert_right(mach_task_self(), conn->xc_local_port,
conn->xc_local_port, MACH_MSG_TYPE_MAKE_SEND);
if (kr != KERN_SUCCESS) {
debugf("Failed to insert right");
errno = EPERM;
return (NULL);
}
debugf("Connection obj created");
return (rv);
}
void xpc_connection_destroy(xpc_connection_t obj)
{
debugf("xpc_connection_destroy: %p", obj);
struct xpc_connection *conn = conn_extract(obj);
dispatch_release(conn->xc_send_queue);
dispatch_release(conn->xc_recv_queue);
mach_port_deallocate(mach_task_self(), conn->xc_local_port);
if (conn->xc_handler)
Block_release(conn->xc_handler);
}
xpc_connection_t
xpc_connection_create_mach_service(const char *name, dispatch_queue_t targetq,
uint64_t flags)
{
kern_return_t kr;
struct xpc_connection *conn;
xpc_connection_t rv;
debugf("Create mach service %s\n", name);
rv = xpc_connection_create(name, targetq);
if (rv == NULL)
return (NULL);
conn = conn_extract(rv);
conn->xc_flags = flags;
if (flags & XPC_CONNECTION_MACH_SERVICE_LISTENER) {
kr = bootstrap_check_in(bootstrap_port, name,
&conn->xc_local_port);
if (kr != KERN_SUCCESS) {
errno = EBUSY;
xpc_connection_resume(rv);
xpc_release(rv);
return (NULL);
}
return (rv);
}
if (!strcmp(name, "bootstrap")) {
conn->xc_remote_port = bootstrap_port;
return (rv);
}
/* Look up named mach service */
kr = bootstrap_look_up(bootstrap_port, name, &conn->xc_remote_port);
if (kr != KERN_SUCCESS) {
errno = ENOENT;
xpc_connection_resume(rv);
xpc_release(rv);
return (NULL);
}
debugf("success");
return (rv);
}
xpc_connection_t
xpc_connection_create_from_endpoint(xpc_endpoint_t endpoint)
{
kern_return_t kr;
struct xpc_connection *conn;
xpc_connection_t rv;
rv = xpc_connection_create("anonymous", NULL);
if (rv == NULL)
return (NULL);
conn = conn_extract(rv);
conn->xc_remote_port = (mach_port_t)endpoint;
return (rv);
}
void
xpc_connection_set_target_queue(xpc_connection_t xconn,
dispatch_queue_t targetq)
{
struct xpc_connection *conn;
debugf("xpc_connection_set_target_queue(): connection=%p", xconn);
conn = conn_extract(xconn);
conn->xc_target_queue = targetq;
}
void
xpc_connection_set_event_handler(xpc_connection_t xconn,
xpc_handler_t handler)
{
struct xpc_connection *conn;
debugf("xpc_connection_set_event_handler(): connection=%p", xconn);
conn = conn_extract(xconn);
conn->xc_handler = (xpc_handler_t)Block_copy(handler);
}
void
xpc_connection_suspend(xpc_connection_t xconn)
{
struct xpc_connection *conn;
debugf("xpc_connection_suspend(): connection=%p", xconn);
conn = conn_extract(xconn);
dispatch_suspend(conn->xc_recv_source);
}
void
xpc_connection_resume(xpc_connection_t xconn)
{
struct xpc_connection *conn;
conn = conn_extract(xconn);
debugf("xpc_connection_resume(): connection=%p, internally %p", xconn, conn);
/* Create dispatch source for top-level connection */
if (conn->xc_parent == NULL) {
conn->xc_recv_source = dispatch_source_create(
DISPATCH_SOURCE_TYPE_MACH_RECV, conn->xc_local_port, 0,
conn->xc_recv_queue);
dispatch_set_context(conn->xc_recv_source, conn);
dispatch_source_set_event_handler_f(conn->xc_recv_source,
xpc_connection_recv_message);
dispatch_resume(conn->xc_recv_source);
}
dispatch_resume(conn->xc_recv_queue);
}
void
xpc_connection_send_message(xpc_connection_t xconn,
xpc_object_t message)
{
struct xpc_connection *conn;
uint64_t id;
conn = conn_extract(xconn);
xpc_object_t seq_id = xpc_dictionary_get_value(message, XPC_SEQID);
if (seq_id) {
id = xpc_uint64_get_value(seq_id);
} else {
id = generate_message_id(xconn);
}
xpc_retain(message);
dispatch_async(conn->xc_send_queue, ^{
xpc_send(xconn, message, id);
xpc_release(message);
});
}
void
xpc_connection_send_message_with_reply(xpc_connection_t xconn,
xpc_object_t message, dispatch_queue_t targetq, xpc_handler_t handler)
{
struct xpc_connection *conn;
struct xpc_pending_call *call;
uint64_t id;
xpc_object_t seq_id = xpc_dictionary_get_value(message, XPC_SEQID);
if (seq_id) {
id = xpc_uint64_get_value(seq_id);
} else {
id = generate_message_id(xconn);
}
conn = conn_extract(xconn);
call = malloc(sizeof(struct xpc_pending_call));
call->xp_id = id;
call->xp_handler = Block_copy(handler);
call->xp_queue = targetq;
TAILQ_INSERT_TAIL(&conn->xc_pending, call, xp_link);
xpc_retain(message);
dispatch_async(conn->xc_send_queue, ^{
xpc_send(xconn, message, call->xp_id);
xpc_release(message);
});
}
xpc_object_t
xpc_connection_send_message_with_reply_sync(xpc_connection_t conn,
xpc_object_t message)
{
__block xpc_object_t result;
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
xpc_connection_send_message_with_reply(conn, message, NULL,
^(xpc_object_t o) {
debugf("received reply");
result = o;
dispatch_semaphore_signal(sem);
});
debugf("I'm waiting (blocking) for reply");
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
return (result);
}
void
xpc_connection_send_barrier(xpc_connection_t xconn, dispatch_block_t barrier)
{
struct xpc_connection *conn;
conn = conn_extract(xconn);
dispatch_sync(conn->xc_send_queue, barrier);
}
void
xpc_connection_cancel(xpc_connection_t connection)
{
}
const char *
xpc_connection_get_name(xpc_connection_t connection)
{
return ("unknown"); /* ??? */
}
uid_t
xpc_connection_get_euid(xpc_connection_t xconn)
{
struct xpc_connection *conn;
conn = conn_extract(xconn);
return (conn->xc_remote_euid);
}
gid_t
xpc_connection_get_guid(xpc_connection_t xconn)
{
struct xpc_connection *conn;
conn = conn_extract(xconn);
return (conn->xc_remote_guid);
}
pid_t
xpc_connection_get_pid(xpc_connection_t xconn)
{
struct xpc_connection *conn;
conn = conn_extract(xconn);
return (conn->xc_remote_pid);
}
au_asid_t
xpc_connection_get_asid(xpc_connection_t xconn)
{
struct xpc_connection *conn;
conn = conn_extract(xconn);
return (conn->xc_remote_asid);
}
void
xpc_connection_set_context(xpc_connection_t xconn, void *ctx)
{
struct xpc_connection *conn;
conn = conn_extract(xconn);
conn->xc_context = ctx;
}
void *
xpc_connection_get_context(xpc_connection_t xconn)
{
struct xpc_connection *conn;
conn = conn_extract(xconn);
return (conn->xc_context);
}
void
xpc_connection_set_finalizer_f(xpc_connection_t connection,
xpc_finalizer_t finalizer)
{
}
xpc_endpoint_t
xpc_endpoint_create(xpc_connection_t connection)
{
}
void
xpc_main(xpc_connection_handler_t handler)
{
dispatch_main();
}
void
xpc_transaction_begin(void)
{
vproc_transaction_begin(NULL);
}
void
xpc_transaction_end(void)
{
vproc_transaction_end(NULL, NULL);
}
static void xpc_connection_invalidate(xpc_connection_t xconn) {
struct xpc_pending_call* call = NULL;
struct xpc_connection* conn = conn_extract(xconn);
struct xpc_connection* peer = NULL;
// tell all outstanding waiters that the connection is now invalid
while ((call = TAILQ_FIRST(&conn->xc_pending))) {
TAILQ_REMOVE(&conn->xc_pending, call, xp_link);
if (call->xp_handler) {
xpc_handler_t handler = call->xp_handler;
dispatch_async(conn->xc_target_queue, ^{
handler((xpc_object_t)XPC_ERROR_CONNECTION_INVALID);
Block_release(handler);
});
}
free(call);
}
// notify the main event handler
if (conn->xc_handler) {
dispatch_async(conn->xc_target_queue, ^{
conn->xc_handler((xpc_object_t)XPC_ERROR_CONNECTION_INVALID);
});
}
};
static void
xpc_send(xpc_connection_t xconn, xpc_object_t message, uint64_t id)
{
struct xpc_connection *conn;
int status;
debugf("xpc_send(): connection=%p, message=%p (type=%d), id=%d", xconn, message, ((struct xpc_object*) message)->xo_xpc_type, id);
conn = conn_extract(xconn);
status = xpc_pipe_send(message, conn->xc_remote_port,
conn->xc_local_port, id);
if (status != 0) {
debugf("send failed, kr=%d", status);
if (status == -EPIPE) {
xpc_connection_invalidate(xconn);
}
}
}
static void
xpc_connection_set_credentials(struct xpc_connection *conn, audit_token_t *tok)
{
uid_t uid;
gid_t gid;
pid_t pid;
au_asid_t asid;
if (tok == NULL)
return;
audit_token_to_au32(*tok, NULL, &uid, &gid, NULL, NULL, &pid, &asid,
NULL);
conn->xc_remote_euid = uid;
conn->xc_remote_guid = gid;
conn->xc_remote_pid = pid;
conn->xc_remote_asid = asid;
}
static void
xpc_connection_recv_message(void *context)
{
struct xpc_pending_call *call;
struct xpc_connection *conn, *peer;
xpc_object_t result;
mach_port_t remote;
int status;
uint64_t id;
debugf("xpc_recv: connection=%p", context);
conn = context;
status = xpc_pipe_receive(conn->xc_local_port, &remote, &result, &id);
if (status != 0) {
if (status == -EPIPE) {
xpc_connection_invalidate((xpc_connection_t)conn);
}
return;
}
debugf("xpc_recv: message=%p, id=%d, remote=<%d>", result, id, remote);
if (conn->xc_flags & XPC_CONNECTION_MACH_SERVICE_LISTENER) {
TAILQ_FOREACH(peer, &conn->xc_peers, xc_link) {
if (remote == peer->xc_remote_port) {
// first, check if it's a reply to any message with pending replies
TAILQ_FOREACH(call, &peer->xc_pending, xp_link) {
if (call->xp_id == id) {
debugf("Found matching ID, async run handler on queue %p", peer->xc_target_queue);
if (call->xp_handler) {
dispatch_async(peer->xc_target_queue, ^{
debugf("pass to handler");
call->xp_handler(result);
Block_release(call->xp_handler);
TAILQ_REMOVE(&peer->xc_pending, call, xp_link);
free(call);
});
}
return;
}
}
// otherwise, pass it to the main handler
if (peer->xc_handler) {
dispatch_async(peer->xc_target_queue, ^{
peer->xc_handler(result);
});
}
return;
}
}
debugf("new peer on port <%u>", remote);
/* New peer */
xpc_object_t peerx = xpc_connection_create(NULL, NULL);
peer = conn_extract(peerx);
peer->xc_parent = conn;
peer->xc_remote_port = remote;
xpc_connection_set_credentials(peer,
((struct xpc_object *)result)->xo_audit_token);
TAILQ_INSERT_TAIL(&conn->xc_peers, peer, xc_link);
if (conn->xc_handler) {
debugf("Async run conn handler");
dispatch_async(conn->xc_target_queue, ^{
debugf("Sync run conn handler");
conn->xc_handler(peerx);
debugf("Sync conn handler done");
if (peer->xc_handler) {
dispatch_async(peer->xc_target_queue, ^{
debugf("Sync result handler");
peer->xc_handler(result);
debugf("Sync result handler done");
});
}
});
}
} else {
xpc_connection_set_credentials(conn,
((struct xpc_object *)result)->xo_audit_token);
debugf("To run client handler");
TAILQ_FOREACH(call, &conn->xc_pending, xp_link) {
if (call->xp_id == id) {
debugf("Found matching ID, async run handler on queue %p", conn->xc_target_queue);
if (call->xp_handler) {
dispatch_async(conn->xc_target_queue, ^{
debugf("pass to handler");
call->xp_handler(result);
Block_release(call->xp_handler);
TAILQ_REMOVE(&conn->xc_pending, call,
xp_link);
free(call);
});
}
return;
}
}
if (conn->xc_handler) {
debugf("Using xc_handler");
dispatch_async(conn->xc_target_queue, ^{
conn->xc_handler(result);
});
}
}
}
void xpc_connection_get_audit_token(xpc_connection_t connection, audit_token_t* auditToken) {
}
void xpc_connection_set_legacy(xpc_connection_t connection) {
puts("STUB: xpc_connection_set_legacy called");
}
void xpc_connection_set_privileged(xpc_connection_t connection) {
puts("STUB: xpc_connection_set_privileged");
}
void xpc_connection_activate(xpc_connection_t connection) {
debugf("xpc_connection_activate: %p\n", connection);
// according to the man page for `xpc_connection_create_mach_service(3)`, a call to `xpc_connection_resume` for a never-before-activated connection
// is identical to calling `xpc_connection_activate`, so logically, since we've just been using `xpc_connection_resume` in the past, we can just defer to it here
// there's not much documentation for `xpc_connection_activate`, though, but i don't think there's much else to it
xpc_connection_resume(connection);
};

244
src/connection.m Normal file
View File

@ -0,0 +1,244 @@
#import <xpc/objects/connection.h>
#import <xpc/util.h>
#import <xpc/xpc.h>
#import <xpc/connection.h>
XPC_CLASS_SYMBOL_DECL(connection);
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(connection)
XPC_CLASS_HEADER(connection);
@end
//
// C API
//
XPC_EXPORT
xpc_connection_t xpc_connection_create(const char* name, dispatch_queue_t targetq) {
return NULL;
};
XPC_EXPORT
xpc_connection_t xpc_connection_create_mach_service(const char* name, dispatch_queue_t targetq, uint64_t flags) {
return NULL;
};
XPC_EXPORT
xpc_connection_t xpc_connection_create_from_endpoint(xpc_endpoint_t endpoint) {
return NULL;
};
XPC_EXPORT
void xpc_connection_set_target_queue(xpc_connection_t xconn, dispatch_queue_t targetq) {
};
XPC_EXPORT
void xpc_connection_set_event_handler(xpc_connection_t xconn, xpc_handler_t handler) {
};
XPC_EXPORT
void xpc_connection_suspend(xpc_connection_t xconn) {
};
XPC_EXPORT
void xpc_connection_resume(xpc_connection_t xconn) {
};
XPC_EXPORT
void xpc_connection_send_message(xpc_connection_t xconn, xpc_object_t message) {
};
XPC_EXPORT
void xpc_connection_send_barrier(xpc_connection_t xconn, dispatch_block_t barrier) {
};
XPC_EXPORT
void xpc_connection_send_message_with_reply(xpc_connection_t xconn, xpc_object_t message, dispatch_queue_t replyq, xpc_handler_t handler) {
};
XPC_EXPORT
xpc_object_t xpc_connection_send_message_with_reply_sync(xpc_connection_t xconn, xpc_object_t message) {
return NULL;
};
XPC_EXPORT
void xpc_connection_cancel(xpc_connection_t xconn) {
};
XPC_EXPORT
const char* xpc_connection_get_name(xpc_connection_t xconn) {
return NULL;
};
XPC_EXPORT
uid_t xpc_connection_get_euid(xpc_connection_t xconn) {
return UID_MAX;
};
XPC_EXPORT
gid_t xpc_connection_get_egid(xpc_connection_t xconn) {
return GID_MAX;
};
XPC_EXPORT
pid_t xpc_connection_get_pid(xpc_connection_t xconn) {
return -1;
};
XPC_EXPORT
au_asid_t xpc_connection_get_asid(xpc_connection_t xconn) {
return AU_ASSIGN_ASID;
};
XPC_EXPORT
void xpc_connection_set_context(xpc_connection_t xconn, void* context) {
};
XPC_EXPORT
void* xpc_connection_get_context(xpc_connection_t xconn) {
return NULL;
};
XPC_EXPORT
void xpc_connection_set_finalizer_f(xpc_connection_t xconn, xpc_finalizer_t finalizer) {
};
XPC_EXPORT
void xpc_connection_set_legacy(xpc_connection_t xconn) {
};
XPC_EXPORT
void xpc_connection_set_privileged(xpc_connection_t xconn) {
};
XPC_EXPORT
void xpc_connection_activate(xpc_connection_t xconn) {
};
XPC_EXPORT
void xpc_connection_set_target_uid(xpc_connection_t xconn, uid_t uid) {
};
XPC_EXPORT
void xpc_connection_set_instance(xpc_connection_t xconn, uuid_t uuid) {
};
XPC_EXPORT
xpc_object_t xpc_connection_copy_entitlement_value(xpc_connection_t xconn, const char* entitlement) {
return NULL;
};
//
// private C API
//
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_EXPORT
char* xpc_connection_copy_bundle_id(xpc_connection_t xconn) {
// returns a string that must be freed
return NULL;
};
XPC_EXPORT
xpc_connection_t xpc_connection_create_listener(const char* name, dispatch_queue_t queue) {
return NULL;
};
XPC_EXPORT
void xpc_connection_enable_sim2host_4sim(xpc_connection_t xconn) {
};
XPC_EXPORT
void xpc_connection_enable_termination_imminent_event(xpc_connection_t xconn) {
};
XPC_EXPORT
void xpc_connection_get_audit_token(xpc_connection_t xconn, audit_token_t* out_token) {
};
XPC_EXPORT
uint8_t xpc_connection_get_bs_type(xpc_connection_t xconn) {
return 0;
};
XPC_EXPORT
void xpc_connection_get_instance(xpc_connection_t xconn, uint8_t* out_uuid) {
};
XPC_EXPORT
bool xpc_connection_is_extension(xpc_connection_t xconn) {
return false;
};
XPC_EXPORT
void xpc_connection_kill(xpc_connection_t xconn, int signal) {
};
XPC_EXPORT
void xpc_connection_send_notification(xpc_connection_t xconn, xpc_object_t details) {
};
XPC_EXPORT
void xpc_connection_set_bootstrap(xpc_connection_t xconn, xpc_object_t bootstrap) {
};
XPC_EXPORT
void xpc_connection_set_bs_type(xpc_connection_t xconn, uint8_t type) {
};
XPC_EXPORT
void xpc_connection_set_event_channel(xpc_connection_t xconn, const char* channel_name) {
// parameter 2 is a guess
};
XPC_EXPORT
void xpc_connection_set_non_launching(xpc_connection_t xconn, bool non_launching) {
};
XPC_EXPORT
void xpc_connection_set_oneshot_instance(xpc_connection_t xconn, const uint8_t* uuid) {
};
XPC_EXPORT
void xpc_connection_set_qos_class_fallback(xpc_connection_t xconn, dispatch_qos_class_t qos_class) {
};
XPC_EXPORT
void xpc_connection_set_qos_class_floor(xpc_connection_t xconn, dispatch_qos_class_t qos_class, int relative_priority) {
};

141
src/data.m Normal file
View File

@ -0,0 +1,141 @@
#import <xpc/objects/data.h>
#import <xpc/util.h>
#define __DISPATCH_INDIRECT__
#import <dispatch/data_private.h>
#import <xpc/xpc.h>
XPC_CLASS_SYMBOL_DECL(data);
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(data)
XPC_CLASS_HEADER(data);
- (char*)xpcDescription
{
char* output = NULL;
asprintf(&output, "<%s: %p, %zu byte%s>", xpc_class_name(self), self, self.length, self.length == 1 ? "" : "s");
return output;
}
- (void)dealloc
{
XPC_THIS_DECL(data);
dispatch_release(this->data);
[super dealloc];
}
- (const void*)bytes
{
XPC_THIS_DECL(data);
return dispatch_data_get_flattened_bytes_4libxpc(this->data);
}
- (NSUInteger)length
{
XPC_THIS_DECL(data);
return dispatch_data_get_size(this->data);
}
- (instancetype)initWithBytes: (const void*)bytes length: (NSUInteger)length
{
if (self = [super init]) {
XPC_THIS_DECL(data);
this->data = dispatch_data_create(bytes, length, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
}
return self;
}
- (instancetype)initWithDispatchData: (dispatch_data_t)data
{
if (self = [super init]) {
XPC_THIS_DECL(data);
this->data = data;
dispatch_retain(this->data);
}
return self;
}
- (NSUInteger)getBytes: (void*)buffer length: (NSUInteger)length
{
XPC_THIS_DECL(data);
length = MIN(length, self.length);
memcpy(buffer, self.bytes, length);
return length;
}
- (void)replaceBytesWithBytes: (const void*)bytes length: (NSUInteger)length
{
XPC_THIS_DECL(data);
dispatch_release(this->data);
this->data = dispatch_data_create(bytes, length, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
}
- (NSUInteger)hash
{
XPC_THIS_DECL(data);
__block NSUInteger result = 0;
dispatch_data_apply(this->data, ^bool (dispatch_data_t region, size_t offset, const void* buffer, size_t size) {
result += xpc_raw_data_hash(buffer, size);
return true;
});
return result;
}
@end
//
// C API
//
XPC_EXPORT
xpc_object_t xpc_data_create(const void* bytes, size_t length) {
return [[XPC_CLASS(data) alloc] initWithBytes: bytes length: length];
};
XPC_EXPORT
xpc_object_t xpc_data_create_with_dispatch_data(dispatch_data_t ddata) {
return [[XPC_CLASS(data) alloc] initWithDispatchData: ddata];
};
XPC_EXPORT
size_t xpc_data_get_length(xpc_object_t xdata) {
TO_OBJC_CHECKED(data, xdata, data) {
return data.length;
}
return 0;
};
XPC_EXPORT
const void* xpc_data_get_bytes_ptr(xpc_object_t xdata) {
TO_OBJC_CHECKED(data, xdata, data) {
return data.bytes;
}
return NULL;
};
XPC_EXPORT
size_t xpc_data_get_bytes(xpc_object_t xdata, void* buffer, size_t offset, size_t length) {
TO_OBJC_CHECKED(data, xdata, data) {
return [data getBytes: ((char*)buffer + offset) length: length];
}
return 0;
};
//
// private C API
//
XPC_EXPORT
void _xpc_data_set_value(xpc_object_t xdata, const void* bytes, size_t length) {
TO_OBJC_CHECKED(data, xdata, data) {
[data replaceBytesWithBytes: bytes length: length];
}
};
XPC_EXPORT
size_t xpc_data_get_inline_max(xpc_object_t xdata) {
// not a stub
// just returns 0
return 0;
};

55
src/date.m Normal file
View File

@ -0,0 +1,55 @@
#import <xpc/objects/date.h>
#import <xpc/util.h>
#import <xpc/xpc.h>
#import <Foundation/NSDate.h>
#include <time.h>
XPC_WRAPPER_CLASS_IMPL(date, int64_t, "%lld");
//
// C API
//
XPC_EXPORT
xpc_object_t xpc_date_create(int64_t value) {
return [[XPC_CLASS(date) alloc] initWithValue: value];
};
XPC_EXPORT
xpc_object_t xpc_date_create_from_current(void) {
return xpc_date_create((int64_t)clock_gettime_nsec_np(CLOCK_REALTIME));
};
XPC_EXPORT
int64_t xpc_date_get_value(xpc_object_t xdate) {
TO_OBJC_CHECKED(date, xdate, date) {
return date.value;
}
return 0;
};
//
// private C API
//
XPC_EXPORT
xpc_object_t xpc_date_create_absolute(int64_t value) {
return [[XPC_CLASS(date) alloc] initWithValue: (value + NSTimeIntervalSince1970) * NSEC_PER_SEC];
};
XPC_EXPORT
int64_t xpc_date_get_value_absolute(xpc_object_t xdate) {
TO_OBJC_CHECKED(date, xdate, date) {
return (date.value / NSEC_PER_SEC) - NSTimeIntervalSince1970;
}
return 0;
};
XPC_EXPORT
bool xpc_date_is_int64_range(xpc_object_t xdate) {
TO_OBJC_CHECKED(date, xdate, date) {
return true;
}
return false;
};

View File

@ -1,344 +0,0 @@
/*
* Copyright 2014-2015 iXsystems, Inc.
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <sys/types.h>
#include <mach/mach.h>
#include <xpc/launchd.h>
#include <xpc/internal.h>
#include <assert.h>
#include <math.h>
xpc_object_t
xpc_dictionary_create(const char * const *keys, const xpc_object_t *values,
size_t count)
{
struct xpc_object *xo;
size_t i;
xpc_u val = {0};
xo = _xpc_prim_create(_XPC_TYPE_DICTIONARY, val, 0);
for (i = 0; i < count; i++)
xpc_dictionary_set_value(xo, keys[i], values[i]);
return (xo);
}
xpc_object_t
xpc_dictionary_create_reply(xpc_object_t original)
{
struct xpc_object *xo, *xo_orig;
xpc_u val;
if (xpc_get_type(original) != XPC_TYPE_DICTIONARY)
return NULL;
xo_orig = original;
if ((xo_orig->xo_flags & _XPC_FROM_WIRE) == 0)
{
debugf("xpc_dictionary_create_reply: not from wire!");
return (NULL);
}
xo_orig->xo_flags &= ~_XPC_FROM_WIRE;
// possible BUG: i'm not sure if this is how Apple associates the 2 dictionaries or not
// if we want to replicate their format exactly, we need to double-check this
xpc_object_t new_dict = xpc_dictionary_create(NULL, NULL, 0);
xpc_object_t seq_id = xpc_dictionary_get_value(original, XPC_SEQID);
if (seq_id != NULL) {
xpc_dictionary_set_uint64(new_dict, XPC_SEQID, xpc_uint64_get_value(seq_id));
}
return new_dict;
}
void
xpc_dictionary_get_audit_token(xpc_object_t xdict, audit_token_t *token)
{
struct xpc_object *xo;
xo = xdict;
if (xo->xo_audit_token != NULL)
memcpy(token, xo->xo_audit_token, sizeof(*token));
}
void
xpc_dictionary_set_mach_recv(xpc_object_t xdict, const char *key, mach_port_t port)
{
struct xpc_object *xo = xdict;
struct xpc_object *xotmp;
xpc_u val;
val.port = port;
xotmp = _xpc_prim_create(_XPC_TYPE_ENDPOINT, val, 0);
xpc_dictionary_set_value(xdict, key, xotmp);
}
void
xpc_dictionary_set_mach_send(xpc_object_t xdict, const char *key, mach_port_t port)
{
struct xpc_object *xotmp;
xpc_u val;
val.port = port;
xotmp = _xpc_prim_create(_XPC_TYPE_ENDPOINT, val, 0);
xpc_dictionary_set_value(xdict, key, xotmp);
}
mach_port_t
xpc_dictionary_copy_mach_send(xpc_object_t xdict, const char *key)
{
struct xpc_object *xo;
const struct xpc_object *xotmp;
#if 0
xo = xdict;
if (nvlist_exists_type(xo->xo_nv, key, NV_TYPE_ENDPOINT))
return (nvlist_get_number(xo->xo_nv, key));
else if (nvlist_exists_binary(xo->xo_nv, key)) {
xotmp = (void *)nvlist_get_number(xo->xo_nv, key);
return (xotmp->xo_uint);
}
return (0);
#endif
}
void
xpc_dictionary_set_value(xpc_object_t xdict, const char *key, xpc_object_t value)
{
struct xpc_object *xo, *xotmp;
struct xpc_dict_head *head;
struct xpc_dict_pair *pair;
xo = xdict;
head = &xo->xo_dict;
TAILQ_FOREACH(pair, head, xo_link) {
if (!strcmp(pair->key, key)) {
xpc_retain(value);
xpc_release(pair->value);
pair->value = value;
return;
}
}
xo->xo_size++;
pair = malloc(sizeof(struct xpc_dict_pair));
size_t len = strlen(key);
char* str = malloc(len + 1);
strncpy(str, key, len);
str[len] = '\0';
pair->key = str;
pair->value = value;
TAILQ_INSERT_TAIL(&xo->xo_dict, pair, xo_link);
xpc_retain(value);
}
xpc_object_t
xpc_dictionary_get_value(xpc_object_t xdict, const char *key)
{
struct xpc_object *xo;
struct xpc_dict_head *head;
struct xpc_dict_pair *pair;
xo = xdict;
head = &xo->xo_dict;
TAILQ_FOREACH(pair, head, xo_link) {
if (!strcmp(pair->key, key))
return (pair->value);
}
return (NULL);
}
size_t
xpc_dictionary_get_count(xpc_object_t xdict)
{
struct xpc_object *xo;
xo = xdict;
return (xo->xo_size);
}
void
xpc_dictionary_set_bool(xpc_object_t xdict, const char *key, bool value)
{;
struct xpc_object *xo, *xotmp;
xo = xdict;
xotmp = xpc_bool_create(value);
xpc_dictionary_set_value(xdict, key, xotmp);
xpc_release(xotmp);
}
void
xpc_dictionary_set_int64(xpc_object_t xdict, const char *key, int64_t value)
{
struct xpc_object *xo, *xotmp;
xo = xdict;
xotmp = xpc_int64_create(value);
xpc_dictionary_set_value(xdict, key, xotmp);
xpc_release(xotmp);
}
void
xpc_dictionary_set_uint64(xpc_object_t xdict, const char *key, uint64_t value)
{
struct xpc_object *xo, *xotmp;
xo = xdict;
xotmp = xpc_uint64_create(value);
xpc_dictionary_set_value(xdict, key, xotmp);
xpc_release(xotmp);
}
void
xpc_dictionary_set_string(xpc_object_t xdict, const char *key, const char *value)
{
struct xpc_object *xo, *xotmp;
xo = xdict;
xotmp = xpc_string_create(value);
xpc_dictionary_set_value(xdict, key, xotmp);
xpc_release(xotmp);
}
bool xpc_dictionary_get_bool(xpc_object_t xdict, const char* key) {
xpc_object_t xo = xpc_dictionary_get_value(xdict, key);
if (xo)
return xpc_bool_get_value(xo);
return false;
};
int64_t xpc_dictionary_get_int64(xpc_object_t xdict, const char* key) {
xpc_object_t xo = xpc_dictionary_get_value(xdict, key);
if (xo)
return xpc_int64_get_value(xo);
return 0;
};
uint64_t xpc_dictionary_get_uint64(xpc_object_t xdict, const char* key) {
xpc_object_t xo = xpc_dictionary_get_value(xdict, key);
if (xo)
return xpc_uint64_get_value(xo);
return 0;
};
const char* xpc_dictionary_get_string(xpc_object_t xdict, const char* key) {
xpc_object_t xo = xpc_dictionary_get_value(xdict, key);
if (xo)
return xpc_string_get_string_ptr(xo);
return NULL;
}
double xpc_dictionary_get_double(xpc_object_t xdict, const char* key) {
xpc_object_t xo = xpc_dictionary_get_value(xdict, key);
if (xo)
return xpc_double_get_value(xo);
return NAN;
};
bool
xpc_dictionary_apply(xpc_object_t xdict, xpc_dictionary_applier_t applier)
{
struct xpc_object *xo, *xotmp;
struct xpc_dict_head *head;
struct xpc_dict_pair *pair;
xo = xdict;
head = &xo->xo_dict;
TAILQ_FOREACH(pair, head, xo_link) {
if (!applier(pair->key, pair->value))
return (false);
}
return (true);
}
const void* xpc_dictionary_get_data(xpc_object_t xdict, const char* key, size_t* length) {
xpc_object_t xo = xpc_dictionary_get_value(xdict, key);
*length = xpc_data_get_length(xo);
return xpc_data_get_bytes_ptr(xo);
};
void xpc_dictionary_set_data(xpc_object_t xdict, const char* key, const void* bytes, size_t length) {
xpc_object_t xo = xpc_data_create(bytes, length);
xpc_dictionary_set_value(xdict, key, xo);
xpc_release(xo);
};
xpc_connection_t
xpc_dictionary_get_remote_connection(xpc_object_t xdict)
{
return NULL;
}
int
xpc_dictionary_dup_fd(xpc_object_t xdict, const char *key)
{
return -1;
}
void
xpc_dictionary_set_fd(xpc_object_t xdict, const char *key, int fd)
{
}
void xpc_dictionary_set_double(xpc_object_t xdict, const char* key, double value) {
xpc_object_t xo = xpc_double_create(value);
xpc_dictionary_set_value(xdict, key, xo);
xpc_release(xo);
};
const uint8_t *
xpc_dictionary_get_uuid(xpc_object_t xdict, const char *key)
{
return NULL;
}
xpc_object_t xpc_dictionary_get_dictionary(xpc_object_t xdict, const char* key) {
xpc_object_t xo = xpc_dictionary_get_value(xdict, key);
if (xo && xpc_get_type(xo) == XPC_TYPE_DICTIONARY)
return xo;
return NULL;
};
void* xpc_dictionary_get_pointer(xpc_object_t xdict, const char* key) {
xpc_object_t xo = xpc_dictionary_get_value(xdict, key);
if (xo)
return xpc_pointer_get_value(xo);
return NULL;
};
void xpc_dictionary_set_pointer(xpc_object_t xdict, const char* key, void* value) {
xpc_object_t xo = xpc_pointer_create(value);
xpc_dictionary_set_value(xdict, key, xo);
xpc_release(xo);
};

533
src/dictionary.m Normal file
View File

@ -0,0 +1,533 @@
#import <xpc/objects/dictionary.h>
#import <xpc/util.h>
#import <xpc/private.h>
#import <xpc/connection.h>
#import <xpc/objects/string.h>
#import <xpc/objects/mach_send.h>
#import <xpc/objects/mach_recv.h>
#import <xpc/objects/array.h>
XPC_CLASS_SYMBOL_DECL(dictionary);
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(dictionary)
XPC_CLASS_HEADER(dictionary);
- (void)dealloc
{
XPC_THIS_DECL(dictionary);
while (!LIST_EMPTY(&this->head)) {
xpc_dictionary_entry_t entry = LIST_FIRST(&this->head);
[self removeEntry: entry];
xpc_release_for_collection(entry->object);
free(entry);
}
[super dealloc];
}
- (char*)xpcDescription
{
char* output = NULL;
size_t count = self.count;
if (count == 0) {
asprintf(&output, "<%s: %p> {}", xpc_class_name(self), self);
return output;
}
char** descriptions = malloc(sizeof(char*) * count);
size_t outputLength = 0;
outputLength += snprintf(NULL, 0, "<%s: %p> {\n", xpc_class_name(self), self);
XPC_THIS_DECL(dictionary);
xpc_dictionary_entry_t current = LIST_FIRST(&this->head);
for (size_t i = 0; i < count; ++i) {
char* description = current->object.xpcDescription;
descriptions[i] = xpc_description_indent(description, false);
free(description);
outputLength += snprintf(NULL, 0, "\t%s: %s\n", current->name, descriptions[i]);
current = LIST_NEXT(current, link);
}
outputLength += snprintf(NULL, 0, "}");
output = malloc(outputLength + 1);
size_t offset = 0;
offset += snprintf(output + offset, outputLength + 1 - offset, "<%s: %p> {\n", xpc_class_name(self), self);
current = LIST_FIRST(&this->head);
for (size_t i = 0; i < count; ++i) {
offset += snprintf(output + offset, outputLength + 1 - offset, "\t%s: %s\n", current->name, descriptions[i]);
free(descriptions[i]);
current = LIST_NEXT(current, link);
}
free(descriptions);
offset += snprintf(output + offset, outputLength + 1 - offset, "}");
return output;
}
- (instancetype)init
{
if (self = [super init]) {
XPC_THIS_DECL(dictionary);
LIST_INIT(&this->head);
}
return self;
}
- (instancetype)initWithObjects: (XPC_CLASS(object)* const*)objects forKeys: (const char* const*)keys count: (NSUInteger)count
{
if (self = [super init]) {
XPC_THIS_DECL(dictionary);
LIST_INIT(&this->head);
for (NSUInteger i = 0; i < count; ++i) {
[self setObject: objects[i] forKey: keys[i]];
}
}
return self;
}
- (xpc_dictionary_entry_t)entryForKey: (const char*)key
{
XPC_THIS_DECL(dictionary);
xpc_dictionary_entry_t entry = NULL;
LIST_FOREACH(entry, &this->head, link) {
if (strcmp(entry->name, key) == 0) {
return entry;
}
}
return NULL;
}
- (void)addEntry: (xpc_dictionary_entry_t)entry
{
XPC_THIS_DECL(dictionary);
LIST_INSERT_HEAD(&this->head, entry, link);
++this->size;
}
- (void)removeEntry: (xpc_dictionary_entry_t)entry
{
XPC_THIS_DECL(dictionary);
LIST_REMOVE(entry, link);
--this->size;
}
- (NSUInteger)count
{
XPC_THIS_DECL(dictionary);
return this->size;
}
- (XPC_CLASS(connection)*)associatedConnection
{
XPC_THIS_DECL(dictionary);
return this->associatedConnection;
}
- (void)setAssociatedConnection: (XPC_CLASS(connection)*)associatedConnection
{
XPC_THIS_DECL(dictionary);
XPC_CLASS(connection)* old = this->associatedConnection;
this->associatedConnection = [associatedConnection retain];
[old release];
}
- (XPC_CLASS(object)*)objectForKey: (const char*)key
{
xpc_dictionary_entry_t entry = [self entryForKey: key];
if (entry == NULL) {
return nil;
}
return entry->object;
}
- (void)setObject: (XPC_CLASS(object)*)object forKey: (const char*)key
{
if (object == nil) {
return [self removeObjectForKey: key];
}
xpc_dictionary_entry_t entry = [self entryForKey: key];
if (entry != NULL) {
XPC_CLASS(object)* old = entry->object;
entry->object = xpc_retain_for_collection(object);
[old release];
return;
}
size_t keyLength = strlen(key);
entry = malloc(sizeof(struct xpc_dictionary_entry_s) + keyLength + 1);
if (entry == NULL) {
// no way to report errors
return;
}
entry->object = xpc_retain_for_collection(object);
strlcpy(entry->name, key, keyLength + 1);
[self addEntry: entry];
}
- (void)removeObjectForKey: (const char*)key
{
xpc_dictionary_entry_t entry = [self entryForKey: key];
if (entry == NULL) {
return;
}
[self removeEntry: entry];
free(entry);
}
- (void)enumerateKeysAndObjectsUsingBlock: (void (^)(const char* key, XPC_CLASS(object)* obj, BOOL* stop))block
{
XPC_THIS_DECL(dictionary);
xpc_dictionary_entry_t entry = NULL;
LIST_FOREACH(entry, &this->head, link) {
BOOL stop = NO;
block(entry->name, entry->object, &stop);
if (stop) {
return;
}
}
}
- (XPC_CLASS(string)*)stringForKey: (const char*)key
{
XPC_CLASS(object)* object = [self objectForKey: key];
TO_OBJC_CHECKED(string, object, string) {
return string;
}
return nil;
}
- (NSUInteger)hash
{
XPC_THIS_DECL(dictionary);
NSUInteger result = 0;
xpc_dictionary_entry_t entry = NULL;
LIST_FOREACH(entry, &this->head, link) {
result += [entry->object hash];
}
return result;
}
- (instancetype)copy
{
XPC_THIS_DECL(dictionary);
XPC_CLASS(dictionary)* result = [XPC_CLASS(dictionary) new];
xpc_dictionary_entry_t entry = NULL;
LIST_FOREACH(entry, &this->head, link) {
XPC_CLASS(object)* copied = [entry->object copy];
[result setObject: copied forKey: entry->name];
[copied release];
}
return result;
}
@end
//
// C API
//
XPC_EXPORT
xpc_object_t xpc_dictionary_create(const char* const* keys, const xpc_object_t* values, size_t count) {
return [[XPC_CLASS(dictionary) alloc] initWithObjects: (XPC_CLASS(object)* const*)values forKeys: keys count: count];
};
XPC_EXPORT
xpc_object_t xpc_dictionary_create_reply(xpc_object_t original) {
// TODO
return NULL;
};
XPC_EXPORT
void xpc_dictionary_set_value(xpc_object_t xdict, const char* key, xpc_object_t value) {
TO_OBJC_CHECKED(dictionary, xdict, dict) {
[dict setObject: XPC_CAST(object, value) forKey: key];
}
};
XPC_EXPORT
xpc_object_t xpc_dictionary_get_value(xpc_object_t xdict, const char* key) {
TO_OBJC_CHECKED(dictionary, xdict, dict) {
return [dict objectForKey: key];
}
return NULL;
};
XPC_EXPORT
size_t xpc_dictionary_get_count(xpc_object_t xdict) {
TO_OBJC_CHECKED(dictionary, xdict, dict) {
return dict.count;
}
return 0;
};
XPC_EXPORT
bool xpc_dictionary_apply(xpc_object_t xdict, xpc_dictionary_applier_t applier) {
TO_OBJC_CHECKED(dictionary, xdict, dict) {
__block BOOL stoppedEarly = NO;
[dict enumerateKeysAndObjectsUsingBlock: ^(const char* key, XPC_CLASS(object)* value, BOOL* stop) {
if (!applier(key, value)) {
stoppedEarly = *stop = YES;
}
}];
return !stoppedEarly;
}
return false;
};
XPC_EXPORT
xpc_connection_t xpc_dictionary_get_remote_connection(xpc_object_t xdict) {
TO_OBJC_CHECKED(dictionary, xdict, dict) {
return dict.associatedConnection;
}
return NULL;
};
XPC_EXPORT
void xpc_dictionary_get_audit_token(xpc_object_t xdict, audit_token_t* token) {
};
//
// private C API
//
XPC_EXPORT
xpc_object_t _xpc_dictionary_create_reply_with_port(mach_port_t port) {
return NULL;
};
XPC_EXPORT
mach_msg_id_t _xpc_dictionary_extract_reply_msg_id(xpc_object_t xdict) {
return -1;
};
XPC_EXPORT
mach_port_t _xpc_dictionary_extract_reply_port(xpc_object_t xdict) {
return MACH_PORT_NULL;
};
XPC_EXPORT
mach_msg_id_t _xpc_dictionary_get_reply_msg_id(xpc_object_t xdict) {
return -1;
};
XPC_EXPORT
void _xpc_dictionary_set_remote_connection(xpc_object_t xdict, xpc_connection_t xconn) {
};
XPC_EXPORT
void _xpc_dictionary_set_reply_msg_id(xpc_object_t xdict, mach_msg_id_t msg_id) {
};
XPC_EXPORT
void xpc_dictionary_apply_f(xpc_object_t xdict, void* context, xpc_dictionary_applier_f applier) {
xpc_dictionary_apply(xdict, ^bool (const char* key, xpc_object_t value) {
// can't stop it early
applier(key, value, context);
return true;
});
};
XPC_EXPORT
char* xpc_dictionary_copy_basic_description(xpc_object_t xdict) {
// returns a string that must freed
return NULL;
};
XPC_EXPORT
bool xpc_dictionary_expects_reply(xpc_object_t xdict) {
return false;
};
XPC_EXPORT
void xpc_dictionary_handoff_reply(xpc_object_t xdict, dispatch_queue_t queue, dispatch_block_t block) {
};
XPC_EXPORT
void xpc_dictionary_handoff_reply_f(xpc_object_t xdict, dispatch_queue_t queue, void* context, dispatch_function_t function) {
};
XPC_EXPORT
void xpc_dictionary_send_reply(xpc_object_t xdict) {
};
//
// setters
//
#define SIMPLE_SETTER(name, type) \
XPC_EXPORT \
void xpc_dictionary_set_ ## name(xpc_object_t xdict, const char* key, type value) { \
TO_OBJC_CHECKED(dictionary, xdict, dict) { \
xpc_object_t object = xpc_ ## name ## _create(value); \
xpc_dictionary_set_value(xdict, key, object); \
xpc_release(object); \
} \
};
SIMPLE_SETTER(bool, bool);
SIMPLE_SETTER(int64, int64_t);
SIMPLE_SETTER(uint64, uint64_t);
SIMPLE_SETTER(double, double);
SIMPLE_SETTER(date, int64_t);
SIMPLE_SETTER(string, const char*);
SIMPLE_SETTER(uuid, const uuid_t);
SIMPLE_SETTER(fd, int);
SIMPLE_SETTER(pointer, void*);
XPC_EXPORT
void xpc_dictionary_set_data(xpc_object_t xdict, const char* key, const void* bytes, size_t length) {
TO_OBJC_CHECKED(dictionary, xdict, dict) {
xpc_object_t object = xpc_data_create(bytes, length);
xpc_dictionary_set_value(xdict, key, object);
xpc_release(object);
}
};
XPC_EXPORT
void xpc_dictionary_set_connection(xpc_object_t xdict, const char* key, xpc_connection_t connection) {
return xpc_dictionary_set_value(xdict, key, connection);
};
XPC_EXPORT
void xpc_dictionary_set_mach_send(xpc_object_t xdict, const char* key, mach_port_t port) {
xpc_object_t object = xpc_mach_send_create(port);
xpc_dictionary_set_value(xdict, key, object);
xpc_release(object);
};
XPC_EXPORT
void xpc_dictionary_set_mach_recv(xpc_object_t xdict, const char* key, mach_port_t recv) {
xpc_object_t object = xpc_mach_recv_create(recv);
xpc_dictionary_set_value(xdict, key, object);
xpc_release(object);
};
//
// getters
//
#define SIMPLE_GETTER(name, type) \
XPC_EXPORT \
type xpc_dictionary_get_ ## name(xpc_object_t xdict, const char* key) { \
xpc_object_t object = xpc_dictionary_get_value(xdict, key); \
return xpc_ ## name ## _get_value(object); \
};
SIMPLE_GETTER(bool, bool);
SIMPLE_GETTER(int64, int64_t);
SIMPLE_GETTER(uint64, uint64_t);
SIMPLE_GETTER(double, double);
SIMPLE_GETTER(date, int64_t);
SIMPLE_GETTER(pointer, void*);
XPC_EXPORT
const void* xpc_dictionary_get_data(xpc_object_t xdict, const char* key, size_t* length) {
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
if (length != NULL) {
*length = xpc_data_get_length(object);
}
return xpc_data_get_bytes_ptr(object);
};
XPC_EXPORT
const char* xpc_dictionary_get_string(xpc_object_t xdict, const char* key) {
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
return xpc_string_get_string_ptr(object);
};
XPC_EXPORT
const uint8_t* xpc_dictionary_get_uuid(xpc_object_t xdict, const char* key) {
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
return xpc_uuid_get_bytes(object);
};
XPC_EXPORT
int xpc_dictionary_dup_fd(xpc_object_t xdict, const char* key) {
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
return xpc_fd_dup(object);
};
XPC_EXPORT
xpc_connection_t xpc_dictionary_create_connection(xpc_object_t xdict, const char* key) {
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
return xpc_connection_create_from_endpoint((xpc_endpoint_t)object);
};
XPC_EXPORT
xpc_object_t xpc_dictionary_get_dictionary(xpc_object_t xdict, const char* key) {
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
TO_OBJC_CHECKED(dictionary, object, dict) {
return dict;
}
return NULL;
};
XPC_EXPORT
mach_port_t _xpc_dictionary_extract_mach_send(xpc_object_t xdict, const char* key) {
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
TO_OBJC_CHECKED(mach_send, object, send) {
return xpc_mach_send_extract_right(send);
}
return MACH_PORT_NULL;
};
XPC_EXPORT
mach_port_t xpc_dictionary_copy_mach_send(xpc_object_t xdict, const char* key) {
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
TO_OBJC_CHECKED(mach_send, object, send) {
return xpc_mach_send_copy_right(send);
}
return MACH_PORT_NULL;
};
XPC_EXPORT
mach_port_t xpc_dictionary_extract_mach_recv(xpc_object_t xdict, const char* key) {
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
TO_OBJC_CHECKED(mach_recv, object, recv) {
return xpc_mach_recv_extract_right(recv);
}
return MACH_PORT_NULL;
};
XPC_EXPORT
xpc_object_t xpc_dictionary_get_array(xpc_object_t xdict, const char* key) {
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
TO_OBJC_CHECKED(array, object, array) {
return array;
}
return NULL;
};
XPC_EXPORT
xpc_connection_t xpc_dictionary_get_connection(xpc_object_t xdict) {
return xpc_dictionary_get_remote_connection(xdict);
};

35
src/double.m Normal file
View File

@ -0,0 +1,35 @@
#import <xpc/objects/double.h>
#import <xpc/util.h>
#import <xpc/xpc.h>
#include <math.h>
XPC_WRAPPER_CLASS_IMPL(double, double, "%f");
//
// C API
//
XPC_EXPORT
xpc_object_t xpc_double_create(double value) {
return [[XPC_CLASS(double) alloc] initWithValue: value];
};
XPC_EXPORT
double xpc_double_get_value(xpc_object_t xdouble) {
TO_OBJC_CHECKED(double, xdouble, doubleObj) {
return doubleObj.value;
}
return NAN;
};
//
// private C API
//
XPC_EXPORT
void _xpc_double_set_value(xpc_object_t xdouble, double value) {
TO_OBJC_CHECKED(double, xdouble, doubleObj) {
doubleObj.value = value;
}
};

54
src/endpoint.m Normal file
View File

@ -0,0 +1,54 @@
#import <xpc/objects/endpoint.h>
#import <xpc/util.h>
#import <xpc/xpc.h>
XPC_CLASS_SYMBOL_DECL(endpoint);
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(endpoint)
XPC_CLASS_HEADER(endpoint);
- (instancetype)initWithConnection: (XPC_CLASS(connection)*)connection
{
if (self = [super init]) {
#warning TODO: -[XPC_CLASS(endpoint) initWithConnection:]
}
return self;
}
@end
//
// C API
//
XPC_EXPORT
xpc_endpoint_t xpc_endpoint_create(xpc_connection_t xconn) {
return [[XPC_CLASS(endpoint) alloc] initWithConnection: XPC_CAST(connection, xconn)];
};
//
// private C API
//
XPC_EXPORT
int xpc_endpoint_compare(xpc_endpoint_t lhs, xpc_endpoint_t rhs) {
return 0;
};
XPC_EXPORT
mach_port_t xpc_endpoint_copy_listener_port_4sim(xpc_endpoint_t xendpoint) {
return MACH_PORT_NULL;
};
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
return NULL;
};
XPC_EXPORT
xpc_endpoint_t xpc_endpoint_create_mach_port_4sim(mach_port_t port) {
return NULL;
};

View File

@ -1,151 +0,0 @@
/*
This file is part of Darling.
Copyright (C) 2017 Darling developers
Darling is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Darling is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Darling. If not, see <http://www.gnu.org/licenses/>.
*/
#include <xpc/xpc.h>
#include <xpc/internal.h>
/* The only key for XPC_ERROR_* dictionaries */
#define _XPC_ERROR_KEY_DESCRIPTION_STR "XPCErrorDescription"
const char *const _xpc_error_key_description = _XPC_ERROR_KEY_DESCRIPTION_STR;
/*
* XPC_ERROR_* constants are declared to be of type `struct _xpc_dictionary_s`
* in Apple's <xpc/connection.h>, yet we want `struct _xpc_dictionary_s *` to
* be interchangable with `struct xpc_object *`, because both types get passed
* as opaque pointers of type `xpc_object_t` to various XPC functions.
* Thankfully, `struct _xpc_dictionary_s` isn't used anywhere else, so we can
* make it a simple wrapper.
*/
struct _xpc_dictionary_s {
struct xpc_object inner;
};
/*
* We cannot initialize these structures using any function because they are
* declared as `const` in Apple's <xpc/connection.h>
* See <sys/queue.h> for TAILQ_ENTRY & TAILQ_HEAD structure details.
*/
/* XPC_ERROR_CONNECTION_INTERRUPTED */
const struct _xpc_dictionary_s _xpc_error_connection_interrupted;
static const struct xpc_object _xpc_error_connection_interrupted_val = {
.xo_xpc_type = _XPC_TYPE_STRING,
.xo_size = 22, /* strlen("Connection interrupted") */
.xo_refcnt = 1,
.xo_u = {
.str = "Connection interrupted"
}
};
static const struct xpc_dict_pair _xpc_error_connection_interrupted_pair = {
.key = _XPC_ERROR_KEY_DESCRIPTION_STR,
.value = &_xpc_error_connection_interrupted_val,
.xo_link = {
.tqe_next = NULL,
.tqe_prev = &_xpc_error_connection_interrupted.inner.xo_u.dict.tqh_first
}
};
const struct _xpc_dictionary_s _xpc_error_connection_interrupted = {
.inner = {
.xo_xpc_type = _XPC_TYPE_DICTIONARY,
.xo_size = 1,
.xo_refcnt = 1,
.xo_u = {
.dict = {
.tqh_first = &_xpc_error_connection_interrupted_pair,
.tqh_last = &_xpc_error_connection_interrupted_pair.xo_link.tqe_next
}
}
}
};
/* XPC_ERROR_CONNECTION_INVALID */
const struct _xpc_dictionary_s _xpc_error_connection_invalid;
static const struct xpc_object _xpc_error_connection_invalid_val = {
.xo_xpc_type = _XPC_TYPE_STRING,
.xo_size = 18, /* strlen("Connection invalid") */
.xo_refcnt = 1,
.xo_u = {
.str = "Connection invalid"
}
};
static const struct xpc_dict_pair _xpc_error_connection_invalid_pair = {
.key = _XPC_ERROR_KEY_DESCRIPTION_STR,
.value = &_xpc_error_connection_invalid_val,
.xo_link = {
.tqe_next = NULL,
.tqe_prev = &_xpc_error_connection_invalid.inner.xo_u.dict.tqh_first
}
};
const struct _xpc_dictionary_s _xpc_error_connection_invalid = {
.inner = {
.xo_xpc_type = _XPC_TYPE_DICTIONARY,
.xo_size = 1,
.xo_refcnt = 1,
.xo_u = {
.dict = {
.tqh_first = &_xpc_error_connection_invalid_pair,
.tqh_last = &_xpc_error_connection_invalid_pair.xo_link.tqe_next
}
}
}
};
/* XPC_ERROR_TERMINATION_IMMINENT */
const struct _xpc_dictionary_s _xpc_error_termination_imminent;
static const struct xpc_object _xpc_error_termination_imminent_val = {
.xo_xpc_type = _XPC_TYPE_STRING,
.xo_size = 20, /* strlen("Termination imminent") */
.xo_refcnt = 1,
.xo_u = {
.str = "Termination imminent"
}
};
static const struct xpc_dict_pair _xpc_error_termination_imminent_pair = {
.key = _XPC_ERROR_KEY_DESCRIPTION_STR,
.value = &_xpc_error_termination_imminent_val,
.xo_link = {
.tqe_next = NULL,
.tqe_prev = &_xpc_error_termination_imminent.inner.xo_u.dict.tqh_first
}
};
const struct _xpc_dictionary_s _xpc_error_termination_imminent = {
.inner = {
.xo_xpc_type = _XPC_TYPE_DICTIONARY,
.xo_size = 1,
.xo_refcnt = 1,
.xo_u = {
.dict = {
.tqh_first = &_xpc_error_termination_imminent_pair,
.tqh_last = &_xpc_error_termination_imminent_pair.xo_link.tqe_next
}
}
}
};

73
src/error.m Normal file
View File

@ -0,0 +1,73 @@
#import <xpc/objects/error.h>
#import <xpc/objects/string.h>
#import <xpc/xpc.h>
#import <xpc/util.h>
XPC_CLASS_SYMBOL_DECL(error);
#define XPCErrorDescriptionKey "XPCErrorDescription"
const char* const _xpc_error_key_description = XPCErrorDescriptionKey;
// workaround like in `bool.m`
struct _xpc_dictionary_s {
struct xpc_error_s the_real_thing;
};
#define XPC_ERROR_DEFINITION(_name, _description) \
struct xpc_string_s _xpc_error_ ## _name ## _entry_string = { \
.base = { \
XPC_GLOBAL_OBJECT_HEADER(string), \
}, \
.byteLength = NSUIntegerMax, \
.string = _description, \
.freeWhenDone = false \
}; \
extern struct _xpc_dictionary_s _xpc_error_ ## _name; \
struct xpc_dictionary_entry_s _xpc_error_ ## _name ## _entry = { \
.link = { \
.le_next = NULL, \
.le_prev = &LIST_FIRST(&_xpc_error_ ## _name.the_real_thing.base.head), \
}, \
.object = XPC_CAST(string, &_xpc_error_ ## _name ## _entry_string), \
.name = XPCErrorDescriptionKey, \
}; \
XPC_EXPORT struct _xpc_dictionary_s _xpc_error_ ## _name = { \
.the_real_thing = { \
.base = { \
.base = { \
XPC_GLOBAL_OBJECT_HEADER(error), \
}, \
.size = 1, \
.head = { \
.lh_first = &_xpc_error_ ## _name ## _entry, \
}, \
}, \
}, \
};
XPC_ERROR_DEFINITION(connection_interrupted, "Connection interrupted");
XPC_ERROR_DEFINITION(connection_invalid, "Connection invalid");
XPC_ERROR_DEFINITION(termination_imminent, "Termination imminent");
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(error)
XPC_CLASS_HEADER(error);
- (char*)xpcDescription
{
char* output = NULL;
asprintf(&output, "<%s: %s>", xpc_class_name(self), [self stringForKey: XPCErrorDescriptionKey].UTF8String);
return output;
}
@end
//
// C API
//
XPC_EXPORT
const char* xpc_strerror(int error) {
return NULL;
};

121
src/event.m Normal file
View File

@ -0,0 +1,121 @@
#import <xpc/xpc.h>
// actually, the event publisher is it's own class
typedef xpc_object_t xpc_event_publisher_t;
XPC_EXPORT const char* const _xpc_event_key_name = "XPCEventName";
XPC_EXPORT const char* const _xpc_event_key_stream_name = "XPCEventStreamName";
//
// private C API
//
XPC_EXPORT
void xpc_event_publisher_activate(xpc_event_publisher_t xpub) {
};
XPC_EXPORT
xpc_object_t xpc_event_publisher_create(const char* name, void* some_parameter) {
return NULL;
};
XPC_EXPORT
int xpc_event_publisher_fire(xpc_event_publisher_t xpub, uint64_t token, xpc_object_t details) {
return -1;
};
XPC_EXPORT
int xpc_event_publisher_fire_noboost(xpc_event_publisher_t xpub, uint64_t token, xpc_object_t details) {
return -1;
};
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`
return -1;
};
XPC_EXPORT
xpc_object_t xpc_event_publisher_fire_with_reply_sync() {
// should be similar to `xpc_event_publisher_fire_with_reply`
return NULL;
};
XPC_EXPORT
au_asid_t xpc_event_publisher_get_subscriber_asid(xpc_event_publisher_t xpub, uint64_t token) {
return -1;
};
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_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_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_EXPORT
int xpc_event_publisher_set_subscriber_keepalive(xpc_event_publisher_t xpub, uint64_t token, bool state) {
return -1;
};
XPC_EXPORT
int xpc_event_stream_check_in() {
// not a stub
// just returns 0
return 0;
};
XPC_EXPORT
int xpc_event_stream_check_in2() {
// not a stub
// just returns 0
return 0;
};
XPC_EXPORT
int xpc_get_event_name(const char* stream, uint64_t token, char* out_name) {
return -1;
};
XPC_EXPORT
xpc_object_t xpc_copy_event(const char* stream, const char* name) {
// returns a dictionary
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_copy_event_entitlements(const char* stream, uint64_t token) {
// returns a dictionary
return NULL;
};
XPC_EXPORT
void xpc_set_event(const char* stream, const char* name, xpc_object_t descriptor) {
};
XPC_EXPORT
void xpc_set_event_state(const char* stream, uint64_t token, bool state) {
};
XPC_EXPORT
void xpc_set_event_stream_handler(const char* connection_name, dispatch_queue_t queue, void (^handler)(xpc_object_t event)) {
};
XPC_EXPORT
void xpc_set_event_with_flags(const char* stream, const char* name, xpc_object_t descriptor, uint64_t flags) {
};

109
src/fd.m Normal file
View File

@ -0,0 +1,109 @@
#import <xpc/objects/fd.h>
#import <xpc/util.h>
#import <xpc/xpc.h>
#include <sys/fileport.h>
#include <mach/mach.h>
XPC_CLASS_SYMBOL_DECL(fd);
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(fd)
XPC_CLASS_HEADER(fd);
- (void)dealloc
{
XPC_THIS_DECL(fd);
if (this->port != MACH_PORT_NULL) {
xpc_mach_port_release_right(this->port, MACH_PORT_RIGHT_SEND);
}
[super dealloc];
}
- (char*)xpcDescription
{
char* output = NULL;
asprintf(&output, "<%s: port = %d>", xpc_class_name(self), self.port);
return output;
}
- (mach_port_t)port
{
XPC_THIS_DECL(fd);
return this->port;
}
- (instancetype)initWithDescriptor: (int)descriptor
{
if (self = [super init]) {
XPC_THIS_DECL(fd);
if (fileport_makeport(descriptor, &this->port) != KERN_SUCCESS) {
[self release];
return nil;
}
}
return self;
}
- (instancetype)initWithPort: (mach_port_t)port
{
if (self = [super init]) {
XPC_THIS_DECL(fd);
if (xpc_mach_port_retain_right(port, MACH_PORT_RIGHT_SEND) != KERN_SUCCESS) {
[self release];
return nil;
}
this->port = port;
}
return self;
}
- (int)instantiateDescriptor
{
XPC_THIS_DECL(fd);
return fileport_makefd(this->port);
}
- (NSUInteger)hash
{
XPC_THIS_DECL(fd);
return (NSUInteger)this->port;
}
- (instancetype)copy
{
XPC_THIS_DECL(fd);
return [[XPC_CLASS(fd) alloc] initWithPort: this->port];
}
@end
//
// C API
//
XPC_EXPORT
xpc_object_t xpc_fd_create(int fd) {
return [[XPC_CLASS(fd) alloc] initWithDescriptor: fd];
};
XPC_EXPORT
int xpc_fd_dup(xpc_object_t xfd) {
TO_OBJC_CHECKED(fd, xfd, fd) {
return [fd instantiateDescriptor];
}
return -1;
};
//
// private C API
//
XPC_EXPORT
mach_port_t _xpc_fd_get_port(xpc_object_t xfd) {
TO_OBJC_CHECKED(fd, xfd, fd) {
return fd.port;
}
return MACH_PORT_NULL;
};

75
src/file_transfer.m Normal file
View File

@ -0,0 +1,75 @@
#import <xpc/objects/file_transfer.h>
#import <xpc/xpc.h>
XPC_CLASS_SYMBOL_DECL(file_transfer);
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(file_transfer)
XPC_CLASS_HEADER(file_transfer);
@end
//
// private C API
//
XPC_EXPORT
void xpc_file_transfer_cancel(xpc_file_transfer_t xtransfer) {
// actually just crashes
};
XPC_EXPORT
dispatch_io_t xpc_file_transfer_copy_io(xpc_file_transfer_t xtransfer) {
return NULL;
};
XPC_EXPORT
xpc_file_transfer_t xpc_file_transfer_create_with_fd(int fd, void (^completionCallback)(void)) {
return NULL;
};
XPC_EXPORT
xpc_file_transfer_t xpc_file_transfer_create_with_path(const char* path, void (^completionCallback)(void)) {
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`
return 0;
};
XPC_EXPORT
uint64_t xpc_file_transfer_get_transfer_id(xpc_file_transfer_t xtransfer) {
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_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_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_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
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`
return NULL;
};

View File

@ -1,20 +1,25 @@
#import <xpc/xpc.h>
extern void bootstrap_init(void); // in liblaunch
XPC_EXPORT
void _libxpc_initializer(void)
{
bootstrap_init();
}
XPC_EXPORT
void xpc_atfork_child(void)
{
bootstrap_init();
}
XPC_EXPORT
void xpc_atfork_parent(void)
{
}
XPC_EXPORT
void xpc_atfork_prepare(void)
{
}

33
src/int64.m Normal file
View File

@ -0,0 +1,33 @@
#import <xpc/objects/int64.h>
#import <xpc/util.h>
#import <xpc/xpc.h>
XPC_WRAPPER_CLASS_IMPL(int64, int64_t, "%lld");
//
// C API
//
XPC_EXPORT
xpc_object_t xpc_int64_create(int64_t value) {
return [[XPC_CLASS(int64) alloc] initWithValue: value];
};
XPC_EXPORT
int64_t xpc_int64_get_value(xpc_object_t xint) {
TO_OBJC_CHECKED(int64, xint, integer) {
return integer.value;
}
return 0;
};
//
// private C API
//
XPC_EXPORT
void _xpc_int64_set_value(xpc_object_t xint, int64_t value) {
TO_OBJC_CHECKED(int64, xint, integer) {
integer.value = value;
}
};

173
src/launchd.m Normal file
View File

@ -0,0 +1,173 @@
#import <xpc/xpc.h>
#import <launch.h>
//
// private C API
//
struct some_launch_service_stats_struct;
XPC_EXPORT
xpc_object_t _launch_msg2(xpc_object_t request, int type, uint64_t handle) {
// `request` is probably a dictionary
// 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
return NULL;
};
XPC_EXPORT
int _launch_service_stats_copy_4ppse_impl(struct some_launch_service_stats_struct* launch_service_stats, int type) {
// 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
return -1;
};
XPC_EXPORT
int launch_activate_socket(const char* key, int** fds, size_t* count) {
return -1;
};
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
return -1;
};
XPC_EXPORT
int launch_bootout_user_service_4coresim(const char* name) {
return -1;
};
XPC_EXPORT
xpc_object_t launch_copy_busy_extension_instances(const char** names, size_t name_count) {
// returns an array
return NULL;
};
XPC_EXPORT
xpc_object_t launch_copy_endpoints_properties_for_pid(pid_t pid) {
// returns a dictionary
return NULL;
};
XPC_EXPORT
xpc_object_t launch_copy_extension_properties(xpc_connection_t xconn) {
// returns a dictionary
return NULL;
};
XPC_EXPORT
xpc_object_t launch_copy_extension_properties_for_pid(pid_t pid) {
// returns a dictionary
return NULL;
};
XPC_EXPORT
xpc_object_t launch_copy_properties_for_pid_4assertiond(pid_t pid) {
// returns a dictionary
return NULL;
};
XPC_EXPORT
int launch_create_persona(uid_t uid, uint64_t flags) {
return -1;
};
XPC_EXPORT
int launch_destroy_persona(int handle) {
return -1;
};
XPC_EXPORT
int launch_disable_directory(const char* path) {
return -1;
};
XPC_EXPORT
int launch_enable_directory(const char* path) {
return -1;
};
XPC_EXPORT
void launch_extension_check_in_live_4UIKit(void) {
};
XPC_EXPORT const char* launch_extension_property_bundle_id = "XPCExtensionBundleIdentifier";
XPC_EXPORT const char* launch_extension_property_host_bundle_id = "XPCExtensionHostBundleIdentifier";
XPC_EXPORT const char* launch_extension_property_host_pid = "XPCExtensionHostPID";
XPC_EXPORT const char* launch_extension_property_path = "XPCExtensionPath";
XPC_EXPORT const char* launch_extension_property_pid = "XPCExtensionPID";
XPC_EXPORT const char* launch_extension_property_version = "XPCExtensionBundleVersion";
XPC_EXPORT const char* launch_extension_property_xpc_bundle = "XPCExtensionXPCBundle";
XPC_EXPORT
int launch_get_service_enabled(const char* name, bool* out_loaded, bool* out_enabled) {
return -1;
};
XPC_EXPORT
int launch_get_system_service_enabled(const char* name, bool* out_loaded, bool* out_enabled) {
return -1;
};
XPC_EXPORT
char* launch_path_for_user_service_4coresim(const char* name) {
// returns a string that must be freed
return NULL;
};
XPC_EXPORT const char* launch_perfcheck_property_endpoint_active = "XPCServiceEndpointActive";
XPC_EXPORT const char* launch_perfcheck_property_endpoint_event = "XPCServiceEndpointEvent";
XPC_EXPORT const char* launch_perfcheck_property_endpoint_name = "XPCServiceEndpointName";
XPC_EXPORT const char* launch_perfcheck_property_endpoint_needs_activation = "XPCServiceEndpointNeedsActivation";
XPC_EXPORT const char* launch_perfcheck_property_endpoints = "XPCServiceEndpoints";
XPC_EXPORT
void launch_remove_external_service(const char* name, const char* version, dispatch_queue_t queue, void (^callback)(int error)) {
};
XPC_EXPORT
int launch_service_stats_disable_4ppse(void) {
return -1;
};
XPC_EXPORT
int launch_service_stats_enable_4ppse(void) {
return -1;
};
XPC_EXPORT
bool launch_service_stats_is_enabled_4ppse() {
return -1;
};
XPC_EXPORT
int launch_set_service_enabled(const char* name, bool enabled) {
return -1;
};
XPC_EXPORT
int launch_set_system_service_enabled(const char* name, bool enabled) {
return -1;
};
XPC_EXPORT
char* launch_version_for_user_service_4coresim(const char* name) {
// returns a string that must be freed
return NULL;
};
XPC_EXPORT
xpc_object_t ld2xpc(launch_data_t data) {
return NULL;
};
XPC_EXPORT
kern_return_t xpc_call_wakeup(mach_port_t port, int status) {
return -1;
};

25
src/mach_recv.m Normal file
View File

@ -0,0 +1,25 @@
#import <xpc/objects/mach_recv.h>
#import <xpc/xpc.h>
XPC_CLASS_SYMBOL_DECL(mach_recv);
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(mach_recv)
XPC_CLASS_HEADER(mach_recv);
@end
//
// private C API
//
XPC_EXPORT
xpc_object_t xpc_mach_recv_create(mach_port_t recv) {
return NULL;
};
XPC_EXPORT
mach_port_t xpc_mach_recv_extract_right(xpc_object_t xrecv) {
return MACH_PORT_NULL;
};

45
src/mach_send.m Normal file
View File

@ -0,0 +1,45 @@
#import <xpc/objects/mach_send.h>
#import <xpc/xpc.h>
XPC_CLASS_SYMBOL_DECL(mach_send);
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(mach_send)
XPC_CLASS_HEADER(mach_send);
@end
//
// private C API
//
XPC_EXPORT
mach_port_t xpc_mach_send_copy_right(xpc_object_t xsend) {
// retains the send right
return MACH_PORT_NULL;
};
XPC_EXPORT
xpc_object_t xpc_mach_send_create(mach_port_t send) {
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_mach_send_create_with_disposition(mach_port_t send, unsigned int disposition) {
// values for `disposition` are either:
// - 0x11 - no action taken with send
// - 0x13 - send is retained
// - 0x14 - send is `make_send`ed
return NULL;
};
XPC_EXPORT
mach_port_t xpc_mach_send_get_right(xpc_object_t xsend) {
// only returns the send right; doesn't retain it
return MACH_PORT_NULL;
};
mach_port_t xpc_mach_send_extract_right(xpc_object_t xsend) {
return MACH_PORT_NULL;
};

View File

@ -1,656 +0,0 @@
/*
* Copyright 2014-2015 iXsystems, Inc.
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/sbuf.h>
#include <mach/mach.h>
#include <xpc/launchd.h>
#include <libkern/OSAtomic.h>
#include <assert.h>
#include <syslog.h>
#include <stdarg.h>
#include <uuid/uuid.h>
#include <stdio.h>
#include <xpc/internal.h>
#include <libkern/OSByteOrder.h>
#include <sys/sysctl.h>
#define RECV_BUFFER_SIZE 65536
#include <xpc/private.h>
#include <xpc/serialization.h>
#define sbuf_new_auto() sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND)
#define MAX_RECV 8192
#define XPC_RECV_SIZE \
MAX_RECV - \
sizeof(mach_msg_header_t) - \
sizeof(mach_msg_trailer_t) - \
sizeof(uint64_t) - \
sizeof(size_t)
const char *const _xpc_event_key_name = "XPCEventName";
struct xpc_message {
mach_msg_header_t header;
size_t size;
uint64_t id;
char data[0];
mach_msg_trailer_t trailer;
};
struct xpc_recv_message {
mach_msg_header_t header;
size_t size;
uint64_t id;
char data[XPC_RECV_SIZE];
mach_msg_trailer_t trailer;
};
static void xpc_copy_description_level(xpc_object_t obj, struct sbuf *sbuf,
int level);
void
fail_log(const char *exp)
{
debugf("%s", exp);
//sleep(1);
printf("%s", exp);
//abort();
}
static void
xpc_dictionary_destroy(struct xpc_object *dict)
{
struct xpc_dict_head *head;
struct xpc_dict_pair *p, *ptmp;
head = &dict->xo_dict;
TAILQ_FOREACH_SAFE(p, head, xo_link, ptmp) {
TAILQ_REMOVE(head, p, xo_link);
free(p->key);
xpc_release(p->value);
free(p);
}
}
static void
xpc_array_destroy(struct xpc_object *dict)
{
struct xpc_object *p, *ptmp;
struct xpc_array_head *head;
head = &dict->xo_array;
TAILQ_FOREACH_SAFE(p, head, xo_link, ptmp) {
TAILQ_REMOVE(head, p, xo_link);
xpc_release(p);
}
}
void
xpc_object_destroy(struct xpc_object *xo)
{
if (xo->xo_refcnt == _XPC_KEEP_ALIVE)
return;
if (xo->xo_xpc_type == _XPC_TYPE_DICTIONARY)
xpc_dictionary_destroy(xo);
if (xo->xo_xpc_type == _XPC_TYPE_ARRAY)
xpc_array_destroy(xo);
if (xo->xo_xpc_type == _XPC_TYPE_CONNECTION)
xpc_connection_destroy(xo);
if (xo->xo_xpc_type == _XPC_TYPE_STRING)
free(xo->xo_u.str);
if (xo->xo_xpc_type == _XPC_TYPE_DATA)
free(xo->xo_u.ptr);
free(xo);
}
xpc_object_t
xpc_retain(xpc_object_t obj)
{
struct xpc_object *xo = obj;
if (xo->xo_refcnt == _XPC_KEEP_ALIVE)
return obj;
OSAtomicIncrement32(&xo->xo_refcnt);
return (obj);
}
void
xpc_release(xpc_object_t obj)
{
struct xpc_object *xo = obj;
if (xo->xo_refcnt == _XPC_KEEP_ALIVE)
return;
if (OSAtomicDecrement32(&xo->xo_refcnt) > 0)
return;
xpc_object_destroy(xo);
}
// The other one is unsafe?
// This is called by Security and is private
void
xpc_release_safe(xpc_object_t obj)
{
xpc_release(obj);
}
static const char *xpc_errors[] = {
"No Error Found",
"No Memory",
"Invalid Argument",
"No Such Process"
};
const char *
xpc_strerror(int error)
{
if (error > EXMAX || error < 0)
return "BAD ERROR";
return (xpc_errors[error]);
}
char *
xpc_copy_description(xpc_object_t obj)
{
char *result;
struct sbuf *sbuf;
sbuf = sbuf_new_auto();
xpc_copy_description_level(obj, sbuf, 0);
sbuf_finish(sbuf);
result = strdup(sbuf_data(sbuf));
sbuf_delete(sbuf);
return (result);
}
static void
xpc_copy_description_level(xpc_object_t obj, struct sbuf *sbuf, int level)
{
struct xpc_object *xo = obj;
#ifndef __APPLE__
struct uuid *id;
#else
uuid_t id;
#endif
char *uuid_str;
uint32_t uuid_status;
if (obj == NULL) {
sbuf_printf(sbuf, "<null value>\n");
return;
}
sbuf_printf(sbuf, "(%s) ", _xpc_get_type_name(obj));
switch (xo->xo_xpc_type) {
case _XPC_TYPE_DICTIONARY:
sbuf_printf(sbuf, "\n");
xpc_dictionary_apply(xo, ^(const char *k, xpc_object_t v) {
sbuf_printf(sbuf, "%*s\"%s\": ", level * 4, " ", k);
xpc_copy_description_level(v, sbuf, level + 1);
return ((bool)true);
});
break;
case _XPC_TYPE_ARRAY:
sbuf_printf(sbuf, "\n");
xpc_array_apply(xo, ^(size_t idx, xpc_object_t v) {
sbuf_printf(sbuf, "%*s%ld: ", level * 4, " ", idx);
xpc_copy_description_level(v, sbuf, level + 1);
return ((bool)true);
});
break;
case _XPC_TYPE_BOOL:
sbuf_printf(sbuf, "%s\n",
xpc_bool_get_value(obj) ? "true" : "false");
break;
case _XPC_TYPE_STRING:
sbuf_printf(sbuf, "\"%s\"\n",
xpc_string_get_string_ptr(obj));
break;
case _XPC_TYPE_INT64:
sbuf_printf(sbuf, "%lld\n",
xpc_int64_get_value(obj));
break;
case _XPC_TYPE_UINT64:
sbuf_printf(sbuf, "0x%llx\n",
xpc_uint64_get_value(obj));
break;
case _XPC_TYPE_DATE:
sbuf_printf(sbuf, "%llu\n",
xpc_date_get_value(obj));
break;
case _XPC_TYPE_UUID:
#ifdef __APPLE__
memcpy(id, xpc_uuid_get_bytes(obj), sizeof(id));
uuid_str = (char*) __builtin_alloca(40);
uuid_unparse(*id, uuid_str);
#else
id = (struct uuid *)xpc_uuid_get_bytes(obj);
uuid_to_string(id, &uuid_str, &uuid_status);
#endif
sbuf_printf(sbuf, "%s\n", uuid_str);
free(uuid_str);
break;
case _XPC_TYPE_ENDPOINT:
sbuf_printf(sbuf, "<%d>\n", xo->xo_int);
break;
case _XPC_TYPE_NULL:
sbuf_printf(sbuf, "<null>\n");
break;
}
}
struct _launch_data {
uint64_t type;
union {
struct {
union {
launch_data_t *_array;
char *string;
void *opaque;
int64_t __junk;
};
union {
uint64_t _array_cnt;
uint64_t string_len;
uint64_t opaque_size;
};
};
int64_t fd;
uint64_t mp;
uint64_t err;
int64_t number;
uint64_t boolean; /* We'd use 'bool' but this struct needs to be used under Rosetta, and sizeof(bool) is different between PowerPC and Intel */
double float_num;
};
};
static uint8_t ld_to_xpc_type[] = {
_XPC_TYPE_INVALID,
_XPC_TYPE_DICTIONARY,
_XPC_TYPE_ARRAY,
_XPC_TYPE_FD,
_XPC_TYPE_UINT64,
_XPC_TYPE_DOUBLE,
_XPC_TYPE_BOOL,
_XPC_TYPE_STRING,
_XPC_TYPE_DATA,
_XPC_TYPE_ERROR,
_XPC_TYPE_ENDPOINT
};
xpc_object_t
ld2xpc(launch_data_t ld)
{
struct xpc_object *xo;
xpc_u val;
if (ld->type > LAUNCH_DATA_MACHPORT)
return (NULL);
if (ld->type == LAUNCH_DATA_STRING || ld->type == LAUNCH_DATA_OPAQUE) {
val.str = malloc(ld->string_len);
memcpy(__DECONST(void *, val.str), ld->string, ld->string_len);
xo = _xpc_prim_create(ld_to_xpc_type[ld->type], val, ld->string_len);
} else if (ld->type == LAUNCH_DATA_BOOL) {
xo = xpc_bool_create((bool)ld->boolean);
} else if (ld->type == LAUNCH_DATA_ARRAY) {
xo = xpc_array_create(NULL, 0);
for (uint64_t i = 0; i < ld->_array_cnt; i++)
xpc_array_append_value(xo, ld2xpc(ld->_array[i]));
} else {
val.ui = ld->mp;
xo = _xpc_prim_create(ld_to_xpc_type[ld->type], val, ld->string_len);
}
return (xo);
}
xpc_object_t
xpc_copy_entitlement_for_token(const char *key __unused, audit_token_t *token __unused)
{
return xpc_bool_create(true);
}
xpc_object_t
xpc_copy_entitlements_for_pid(pid_t pid)
{
return xpc_bool_create(true);
}
#define XPC_RPORT "XPC remote port"
int
xpc_pipe_routine_reply(xpc_object_t xobj)
{
struct xpc_object *xo;
size_t size = 0, msg_size;
struct xpc_message *message;
kern_return_t kr;
int err;
xo = xobj;
assert(xo->xo_xpc_type == _XPC_TYPE_DICTIONARY);
if ((errno = xpc_serialize(xo, NULL, 0, &size)) != 0)
return errno;
msg_size = size + sizeof(struct xpc_message);
if ((message = malloc(msg_size)) == NULL)
return ENOMEM;
if ((errno = xpc_serialize(xo, message->data, size, NULL)) != 0)
return errno;
message->header.msgh_size = msg_size;
message->header.msgh_remote_port = xpc_dictionary_copy_mach_send(xobj, XPC_RPORT);
message->header.msgh_local_port = MACH_PORT_NULL;
message->size = size;
kr = mach_msg_send(&message->header);
if (kr != KERN_SUCCESS)
err = (kr == KERN_INVALID_TASK) ? EPIPE : EINVAL;
else
err = 0;
free(message);
return (err);
}
int
xpc_pipe_send(xpc_object_t xobj, mach_port_t dst, mach_port_t local,
uint64_t id)
{
struct xpc_object *xo;
size_t size = 0, msg_size;
struct xpc_message *message;
kern_return_t kr;
int err;
xo = xobj;
debugf("obj type is %d", xo->xo_xpc_type);
if (xo->xo_xpc_type != _XPC_TYPE_DICTIONARY)
debugf("obj type is %s", _xpc_get_type_name(xobj));
assert(xo->xo_xpc_type == _XPC_TYPE_DICTIONARY);
debugf("packing message");
if ((errno = xpc_serialize(xo, NULL, 0, &size)) != 0)
return -errno;
msg_size = size + sizeof(struct xpc_message);
if ((message = malloc(msg_size)) == NULL)
return -ENOMEM;
if ((errno = xpc_serialize(xo, message->data, size, NULL)) != 0)
return -errno;
debugf("sending message");
msg_size = ALIGN(size + sizeof(mach_msg_header_t) + sizeof(size_t) + sizeof(uint64_t));
message->header.msgh_size = (mach_msg_size_t)msg_size;
message->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
MACH_MSG_TYPE_MAKE_SEND);
message->header.msgh_remote_port = dst;
message->header.msgh_local_port = local;
message->id = id;
message->size = size;
kr = mach_msg_send(&message->header);
if (kr != KERN_SUCCESS)
err = (kr == KERN_INVALID_TASK || kr == MACH_SEND_INVALID_DEST) ? -EPIPE : -EINVAL;
else
err = 0;
free(message);
return (err);
}
#define LOG(msg, ...) \
do { \
debugf("%s:%u: " msg, __FILE__, __LINE__,##__VA_ARGS__); \
} while (0)
int
xpc_pipe_receive(mach_port_t local, mach_port_t *remote, xpc_object_t *result,
uint64_t *id)
{
struct xpc_recv_message message;
mach_msg_header_t *request;
kern_return_t kr;
mach_msg_trailer_t *tr;
int data_size;
struct xpc_object *xo;
audit_token_t *auditp;
request = &message.header;
/* should be size - but what about arbitrary XPC data? */
request->msgh_size = MAX_RECV;
request->msgh_local_port = local;
kr = mach_msg(request, MACH_RCV_MSG |
MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT),
0, request->msgh_size, request->msgh_local_port,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (kr != KERN_SUCCESS) {
LOG("mach_msg_receive returned %d\n", kr);
return (kr == KERN_INVALID_TASK || kr == MACH_RCV_PORT_DIED || kr == MACH_RCV_PORT_CHANGED) ? -EPIPE : -EINVAL;
}
*remote = request->msgh_remote_port;
*id = message.id;
data_size = (int)message.size;
LOG("unpacking data_size=%d", data_size);
if (xpc_deserialize(&xo, message.data, data_size, NULL) != 0)
return -1;
tr = (mach_msg_trailer_t *)(((char *)&message) + request->msgh_size);
auditp = &((mach_msg_audit_trailer_t *)tr)->msgh_audit;
xo->xo_audit_token = malloc(sizeof(*auditp));
memcpy(xo->xo_audit_token, auditp, sizeof(*auditp));
xpc_dictionary_set_mach_send(xo, XPC_RPORT, request->msgh_remote_port);
xpc_dictionary_set_uint64(xo, XPC_SEQID, message.id);
xo->xo_flags |= _XPC_FROM_WIRE;
*result = xo;
return (0);
}
int
xpc_pipe_try_receive(mach_port_t portset, xpc_object_t *requestobj, mach_port_t *rcvport,
boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *), mach_msg_size_t msgsize __unused,
int flags __unused)
{
struct xpc_recv_message message;
struct xpc_recv_message rsp_message;
mach_msg_header_t *request;
kern_return_t kr;
mach_msg_header_t *response;
mach_msg_trailer_t *tr;
int data_size;
struct xpc_object *xo;
audit_token_t *auditp;
request = &message.header;
response = &rsp_message.header;
/* should be size - but what about arbitrary XPC data? */
request->msgh_size = MAX_RECV;
request->msgh_local_port = portset;
kr = mach_msg(request, MACH_RCV_MSG |
MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT),
0, request->msgh_size, request->msgh_local_port,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (kr != 0)
LOG("mach_msg_receive returned %d\n", kr);
*rcvport = request->msgh_remote_port;
if (demux(request, response)) {
mig_reply_error_t* migError = (mig_reply_error_t*) response;
if (!(migError->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
if (migError->RetCode == MIG_NO_REPLY)
migError->Head.msgh_remote_port = MACH_PORT_NULL;
}
if (response->msgh_remote_port != MACH_PORT_NULL)
(void)mach_msg_send(response);
/* can't do anything with the return code
* just tell the caller this has been handled
*/
return (TRUE);
}
LOG("demux returned false\n");
data_size = request->msgh_size;
LOG("unpacking data_size=%d", data_size);
if (xpc_deserialize(&xo, message.data, data_size, NULL) != 0)
return -1;
/* is padding for alignment enforced in the kernel?*/
tr = (mach_msg_trailer_t *)(((char *)&message) + request->msgh_size);
auditp = &((mach_msg_audit_trailer_t *)tr)->msgh_audit;
xo->xo_audit_token = malloc(sizeof(*auditp));
memcpy(xo->xo_audit_token, auditp, sizeof(*auditp));
xpc_dictionary_set_mach_send(xo, XPC_RPORT, request->msgh_remote_port);
xpc_dictionary_set_uint64(xo, XPC_SEQID, message.id);
xo->xo_flags |= _XPC_FROM_WIRE;
*requestobj = xo;
return (0);
}
int
xpc_call_wakeup(mach_port_t rport, int retcode)
{
mig_reply_error_t msg;
int err;
kern_return_t kr;
msg.Head.msgh_remote_port = rport;
msg.RetCode = retcode;
kr = mach_msg_send(&msg.Head);
if (kr != KERN_SUCCESS)
err = (kr == KERN_INVALID_TASK) ? EPIPE : EINVAL;
else
err = 0;
return (err);
}
xpc_object_t
_od_rpc_call(const char *procname, xpc_object_t payload, xpc_pipe_t (*get_pipe)(bool))
{
printf("STUB _od_rpc_call\n");
return NULL;
}
int
xpc_pipe_routine(xpc_object_t pipe, void *payload,xpc_object_t *reply)
{
printf("STUB xpc_pipe_routine\n");
return 0;
}
void
xpc_dictionary_set_uuid(xpc_object_t xdict, const char *key, const uuid_t uuid)
{
printf("STUB xpc_dictionary_set_uuid\n");
}
int launch_activate_socket(const char* key, int** fds, size_t* count) {
// notes for someone implementing this in the future:
//
// this function is used in OpenSSH in ssh-agent.c
//
// `key` is the socket key in the current process's launchd plist
// `fds` is a pointer to an array that we allocate that is freed by the caller
// `count` is the size of that array
//
// implementing this requires looking up the current process's launchd plist
// and reading socket values from there (or talking to launchd to do that,
// if we can do that; i haven't looked into this much)
printf("STUB launch_activate_socket\n");
if (fds)
*fds = NULL;
if (count)
*count = 0;
return -1;
};
struct os_system_version {
unsigned int major;
unsigned int minor;
unsigned int patch;
};
int os_system_version_get_current_version(struct os_system_version* version_info) {
char version_string[48] = {0};
size_t version_string_length = sizeof(version_string);
char* ptr = NULL;
int status = 0;
if ((status = sysctlbyname("kern.osproductversion", version_string, &version_string_length, NULL, 0)) != 0)
goto out;
version_info->major = strtoul(version_string, &ptr, 10);
if (*ptr != '\0') {
version_info->minor = strtoul(ptr + 1, &ptr, 10);
if (*ptr != '\0') {
version_info->patch = strtoul(ptr + 1, &ptr, 10);
} else {
version_info->patch = 0;
}
} else {
version_info->minor = 0;
version_info->patch = 0;
}
out:
return status;
};

252
src/misc.m Normal file
View File

@ -0,0 +1,252 @@
#import <xpc/xpc.h>
#import <mach-o/dyld_priv.h>
#define __DISPATCH_INDIRECT__
#import <dispatch/mach_private.h>
#import <os/voucher_private.h>
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`
return false;
};
XPC_EXPORT
int _system_ios_support_version_copy_string_sysctl(char* out_string) {
return -1;
};
XPC_EXPORT
bool _system_version_copy_string_plist(char* out_string) {
return false;
};
XPC_EXPORT
bool _system_version_copy_string_sysctl(char* out_string) {
return false;
};
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)
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
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`
return -1;
};
XPC_EXPORT
xpc_object_t _xpc_payload_create_from_mach_msg(dispatch_mach_msg_t msg, int some_flag) {
return NULL;
};
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_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_EXPORT
const char* _xpc_spawnattr_unpack_string(const char* string, size_t length, uint32_t offset) {
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) {
return NULL;
};
XPC_EXPORT
xpc_object_t place_hold_on_real_loginwindow(void) {
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_copy_entitlement_for_self(const char* name) {
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_copy_entitlement_for_token(const char* name, audit_token_t* token) {
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_copy_entitlements_data_for_token(audit_token_t* token) {
// returns a data object
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_copy_entitlements_for_pid(pid_t pid) {
// returns a dictionary
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_copy_entitlements_for_self(void) {
// returns a dictionary
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_copy_bootstrap(void) {
// returns a dictionary
return NULL;
};
XPC_EXPORT
char* xpc_copy_code_signing_identity_for_token(audit_token_t* token) {
// returns a string that must be freed
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_copy_domain(void) {
// returns a dictionary with a single entry:
// "pid": <pid of current process>
return NULL;
};
XPC_EXPORT
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
return NULL;
};
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
return NULL;
};
XPC_EXPORT
void xpc_generate_audit_token(pid_t i_think_this_is_a_pid, audit_token_t* token) {
};
XPC_EXPORT
xpc_endpoint_t xpc_get_attachment_endpoint(void) {
return NULL;
};
struct some_return_remote_hooks_struct;
struct some_remote_hooks_struct;
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
return NULL;
};
XPC_EXPORT
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_EXPORT
bool xpc_test_symbols_exported() {
// not a stub
// just returns false
// probably has parameters, but there's no way to tell what they are
return false;
};
XPC_EXPORT
void xpc_track_activity(void) {
};
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
return -1;
};
XPC_EXPORT
int xpc_receive_remote_msg(void* data, size_t data_length, bool some_flag, void* something, xpc_connection_t connection, void (^oolCallback)()) {
// 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
return -1;
};
XPC_EXPORT
void* xpc_make_serialization(xpc_object_t object, size_t* out_serialization_length) {
return NULL;
};
XPC_EXPORT
void* xpc_make_serialization_with_ool(xpc_object_t object, size_t* out_serialization_length, uint64_t flags) {
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_create_from_serialization(void* data, size_t data_length) {
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`
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_create_from_plist(void* data, size_t data_length) {
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_create_from_plist_descriptor(int fd, dispatch_queue_t queue) {
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_create_reply_with_format_and_arguments(xpc_object_t original, const char* format, va_list args) {
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_create_reply_with_format(xpc_object_t original, const char* format, ...) {
va_list args;
va_start(args, format);
xpc_object_t result = xpc_create_reply_with_format_and_arguments(original, format, args);
va_end(args);
return result;
};
XPC_EXPORT
xpc_object_t xpc_create_with_format_and_arguments(const char* format, va_list args) {
return NULL;
};
XPC_EXPORT
xpc_object_t xpc_create_with_format(const char* format, ...) {
va_list args;
va_start(args, format);
xpc_object_t result = xpc_create_with_format_and_arguments(format, args);
va_end(args);
return result;
};

44
src/null.m Normal file
View File

@ -0,0 +1,44 @@
#import <xpc/objects/null.h>
#import <xpc/util.h>
#import <xpc/xpc.h>
XPC_CLASS_SYMBOL_DECL(null);
struct xpc_null_s _xpc_null = {
.base = {
XPC_GLOBAL_OBJECT_HEADER(null),
},
};
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(null)
XPC_CLASS_HEADER(null);
- (char*)xpcDescription
{
char* output = NULL;
asprintf(&output, "<%s>", xpc_class_name(self));
return output;
}
+ (instancetype)null
{
return XPC_CAST(null, &_xpc_null);
}
- (NSUInteger)hash
{
return 0x804201026298ULL;
}
@end
//
// C API
//
XPC_EXPORT
xpc_object_t xpc_null_create(void) {
return [XPC_CLASS(null) null];
};

View File

@ -1,13 +0,0 @@
#include <xpc/xpc.h>
#include <xpc/private.h>
void xpc_pipe_invalidate(xpc_pipe_t pipe)
{
}
xpc_pipe_t xpc_pipe_create(const char* name, int flags)
{
return NULL;
}

86
src/pipe.m Normal file
View File

@ -0,0 +1,86 @@
#import <xpc/objects/pipe.h>
#import <xpc/xpc.h>
#import <xpc/private.h>
#define __DISPATCH_INDIRECT__
#import <dispatch/mach_private.h>
XPC_CLASS_SYMBOL_DECL(pipe);
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(pipe)
XPC_CLASS_HEADER(pipe);
@end
//
// private C API
//
XPC_EXPORT
void xpc_pipe_invalidate(xpc_pipe_t xpipe) {
};
XPC_EXPORT
xpc_pipe_t xpc_pipe_create(const char* name, int flags) {
return NULL;
};
XPC_EXPORT
xpc_object_t _od_rpc_call(const char* procname, xpc_object_t payload, xpc_pipe_t (*get_pipe)(bool)) {
return NULL;
};
XPC_EXPORT
int xpc_pipe_routine_reply(xpc_pipe_t xpipe) {
return -1;
};
XPC_EXPORT
int xpc_pipe_routine(xpc_pipe_t xpipe, xpc_object_t payload, xpc_object_t* reply) {
return -1;
};
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;
};
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;
};
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;
};
XPC_EXPORT
int xpc_pipe_receive(mach_port_t port, xpc_object_t* out_msg, bool some_bool) {
return -1;
};
XPC_EXPORT
int xpc_pipe_routine_async(xpc_pipe_t xpipe, xpc_object_t payload, int some_flags_probably) {
return -1;
};
XPC_EXPORT
int xpc_pipe_routine_forward(xpc_pipe_t xpipe, xpc_object_t payload) {
return -1;
};
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;
};
XPC_EXPORT
int xpc_pipe_simpleroutine(xpc_pipe_t xpipe, xpc_object_t payload, int some_flags_probably) {
return -1;
};

22
src/pointer.m Normal file
View File

@ -0,0 +1,22 @@
#import <xpc/objects/pointer.h>
#import <xpc/util.h>
#import <xpc/xpc.h>
XPC_WRAPPER_CLASS_IMPL(pointer, void*, "%p");
//
// C API
//
XPC_EXPORT
xpc_object_t xpc_pointer_create(void* value) {
return [[XPC_CLASS(pointer) alloc] initWithValue: value];
};
XPC_EXPORT
void* xpc_pointer_get_value(xpc_object_t xptr) {
TO_OBJC_CHECKED(pointer, xptr, ptr) {
return ptr.value;
}
return NULL;
};

View File

@ -1,192 +0,0 @@
#include <xpc/private.h>
#include <stdio.h>
#include <stdlib.h>
int _xpc_runtime_is_app_sandboxed()
{
return 0;
}
static xpc_object_t xpc_create_with_format_impl(const char * format, va_list args) {
// simplistic implementation for now
//
// everywhere this function and `xpc_create_reply_with_format` are used in Darling,
// only simple shallow objects are created
xpc_object_t result = NULL;
// TODO: work with non-dictionaries
// like i said before, this isn't a big issue because everyone using this function
// and `xpc_create_reply_with_format` in Darling is only creating dictionaries
if (format[0] != '{')
return NULL;
++format;
result = xpc_dictionary_create(NULL, NULL, 0);
bool inKey = true;
bool ignoringWhitespace = true;
const char* key_start = NULL;
const char* key_end = NULL;
const char* str_start = NULL;
const char* str_end = NULL;
for (; *format != '\0'; ++format) {
// TODO: nested objects
if (*format == '{') {
if (result)
xpc_release(result);
result = NULL;
break;
}
if (*format == '}' || (!inKey && *format == ',')) {
inKey = true;
ignoringWhitespace = true;
if (!str_start) {
// empty dictionary
if (!key_start && *format == '}')
break;
// otherwise: bad format
if (result)
xpc_release(result);
result = NULL;
break;
}
// remove trailing whitespace
while (*(str_end - 1) == ' ' || *(str_end - 1) == '\t')
--str_end;
size_t key_len = key_end - key_start;
size_t str_len = str_end - str_start;
// needed in order to have a null terminator
char* key_buf[key_len + 1];
strncpy(key_buf, key_start, key_len);
key_buf[key_len] = '\0';
const char* key = key_buf;
// replace "%string" with a vararg
if (key_len == 7 && !strncmp(key_start, "%string", key_len))
key = va_arg(args, const char*);
xpc_object_t value = NULL;
// replace "%string" with a vararg
// replace "%value" with a vararg
if (str_len == 7 && !strncmp(str_start, "%string", str_len)) {
value = xpc_string_create(va_arg(args, const char*));
} else if (str_len == 6 && !strncmp(str_start, "%value", str_len)) {
value = va_arg(args, xpc_object_t);
xpc_retain(value); // to balance out the `xpc_release` later on
} else {
char* str_buf[str_len + 1];
strncpy(str_buf, str_start, str_len);
str_buf[str_len] = '\0';
value = xpc_string_create(str_buf);
}
xpc_dictionary_set_value(result, key, value);
xpc_release(value);
if (*format == '}')
break;
} else if (inKey) {
if (*format == ':') {
inKey = false;
ignoringWhitespace = true;
continue;
}
if (*format == ',') {
// bad format
if (result)
xpc_release(result);
result = NULL;
break;
}
if (ignoringWhitespace) {
if (*format == ' ' || *format == '\t')
continue;
ignoringWhitespace = false;
}
if (!key_start)
key_start = format;
key_end = format + 1;
} else {
if (*format == ':') {
// bad format
if (result)
xpc_release(result);
result = NULL;
break;
}
if (ignoringWhitespace) {
if (*format == ' ' || *format == '\t')
continue;
ignoringWhitespace = false;
}
if (!str_start)
str_start = format;
str_end = format + 1;
}
}
return result;
}
xpc_object_t xpc_create_with_format(const char * format, ...) {
va_list args;
va_start(args, format);
xpc_object_t result = xpc_create_with_format_impl(format, args);
va_end(args);
return result;
};
xpc_object_t xpc_create_reply_with_format(xpc_object_t original, const char * format, ...) {
xpc_object_t reply = xpc_dictionary_create_reply(original);
if (!reply)
return NULL;
va_list args;
va_start(args, format);
xpc_object_t result = xpc_create_with_format_impl(format, args);
va_end(args);
// copy reply keys into result
//
// we actually don't need to do this ATM, because `xpc_dictionary_create_reply` just
// creates an empty dictionary, but it's here so we're good if in the future its behavior
// is fixed and it does add a reference to the original xpc_object
xpc_dictionary_apply(reply, ^bool (const char* key, xpc_object_t value) {
xpc_dictionary_set_value(result, key, value);
return true;
});
xpc_release(reply);
return result;
};
xpc_object_t xpc_connection_copy_entitlement_value(xpc_connection_t connection, const char* entitlement) {
printf("%s\n", __PRETTY_FUNCTION__);
return NULL;
};
// from various notes and comments around its use in WebKit and Security,
// this function seems to tell the XPC runtime that it's ok for the process
// to exit once the XPC runtime finishes doing whatever it needs to
void xpc_transaction_exit_clean() {
// for now, we have nothing to do here except exit
exit(0);
};

View File

@ -1,8 +1,10 @@
#include <stdio.h>
#import <reboot2.h>
#import <xpc/xpc.h>
void *reboot3(int howto) {
XPC_EXPORT
void *reboot3(uint64_t flags) {
/* Let's just call reboot2 */
/* It is defined in liblaunch */
/* printf("libxpc reboot3 called with howto: %d\n", howto); */
return reboot2(howto);
return reboot2(flags);
}

34
src/runtime.m Normal file
View File

@ -0,0 +1,34 @@
#import <xpc/xpc.h>
XPC_EXPORT
xpc_object_t _xpc_runtime_get_entitlements_data(void) {
// returns a data object
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`)
return NULL;
};
XPC_EXPORT
bool _xpc_runtime_is_app_sandboxed(void) {
return false;
};
XPC_EXPORT
void xpc_main(xpc_connection_handler_t handler) {
abort();
};
XPC_EXPORT
void xpc_init_services(void) {
};
XPC_EXPORT
void xpc_impersonate_user(void) {
// not a stub
// this function just does nothing
};

10
src/serializer.m Normal file
View File

@ -0,0 +1,10 @@
#import <xpc/objects/serializer.h>
XPC_CLASS_SYMBOL_DECL(serializer);
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(serializer)
XPC_CLASS_HEADER(serializer);
@end

62
src/service.m Normal file
View File

@ -0,0 +1,62 @@
#import <xpc/objects/service.h>
#import <xpc/objects/service_instance.h>
#import <xpc/util.h>
#import <xpc/xpc.h>
XPC_CLASS_SYMBOL_DECL(service);
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(service)
XPC_CLASS_HEADER(service);
@end
//
// private C API
//
XPC_EXPORT
void xpc_service_attach(xpc_service_t xservice, bool run, bool kill) {
};
XPC_EXPORT
xpc_service_t xpc_service_create(int flags, const char* name, uint64_t handle, dispatch_queue_t queue) {
return NULL;
};
XPC_EXPORT
xpc_service_t xpc_service_create_from_specifier(const char* specifier, dispatch_queue_t queue) {
return NULL;
};
XPC_EXPORT
const char* xpc_service_get_rendezvous_token(xpc_service_t xservice) {
return NULL;
};
XPC_EXPORT
void xpc_service_kickstart(xpc_service_t xservice, bool suspended, bool kill) {
};
XPC_EXPORT
void xpc_service_set_attach_handler(xpc_service_t xservice, void (^handler)(xpc_service_instance_t xinstance)) {
};
XPC_EXPORT
void _xpc_service_last_xref_cancel(xpc_service_t xservice) {
};
XPC_EXPORT
void xpc_handle_service() {
// i have no clue what the types for the 3 arguments to this function are
};
XPC_EXPORT
void xpc_handle_subservice() {
// same as for `xpc_handle_service`
};

98
src/service_instance.m Normal file
View File

@ -0,0 +1,98 @@
#import <xpc/objects/service_instance.h>
#import <xpc/xpc.h>
#import <xpc/endpoint.h>
XPC_CLASS_SYMBOL_DECL(service_instance);
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(service_instance)
XPC_CLASS_HEADER(service_instance);
@end
//
// private C API
//
XPC_EXPORT
void xpc_service_instance_dup2(xpc_service_instance_t xinstance, int fd, int new_fd) {
};
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
return NULL;
};
XPC_EXPORT
pid_t xpc_service_instance_get_host_pid(xpc_service_instance_t xinstance) {
return -1;
};
XPC_EXPORT
pid_t xpc_service_instance_get_pid(xpc_service_instance_t xinstance) {
return -1;
};
XPC_EXPORT
int xpc_service_instance_get_type(xpc_service_instance_t xinstance) {
// unsure about the return type
return -1;
};
XPC_EXPORT
bool xpc_service_instance_is_configurable(xpc_service_instance_t xinstance) {
return false;
};
XPC_EXPORT
void xpc_service_instance_run(xpc_service_instance_t xinstance) {
};
XPC_EXPORT
void xpc_service_instance_set_binpref(xpc_service_instance_t xinstance, int binpref) {
// unsure about `binpref`'s type
};
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_EXPORT
void xpc_service_instance_set_cwd(xpc_service_instance_t xinstance, const char* cwd) {
};
XPC_EXPORT
void xpc_service_instance_set_endpoint(xpc_service_instance_t xinstance, xpc_endpoint_t endpoint) {
};
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_EXPORT
void xpc_service_instance_set_finalizer_f(xpc_service_instance_t xinstance, void (*finalizer)(void* context)) {
};
XPC_EXPORT
void xpc_service_instance_set_jetsam_properties(xpc_service_instance_t xinstance, int flags, int priority, int memlimit) {
};
XPC_EXPORT
void xpc_service_instance_set_path(xpc_service_instance_t xinstance, const char* path) {
};
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
};

45
src/shmem.m Normal file
View File

@ -0,0 +1,45 @@
#import <xpc/objects/shmem.h>
#import <xpc/util.h>
#import <xpc/xpc.h>
XPC_CLASS_SYMBOL_DECL(shmem);
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(shmem)
XPC_CLASS_HEADER(shmem);
@end
//
// C API
//
XPC_EXPORT
xpc_object_t xpc_shmem_create(void* region, size_t length) {
return NULL;
};
XPC_EXPORT
size_t xpc_shmem_map(xpc_object_t xshmem, void** region) {
return 0;
};
//
// private C API
//
XPC_EXPORT
mach_port_t _xpc_shmem_get_mach_port(xpc_object_t xshmem) {
return MACH_PORT_NULL;
};
XPC_EXPORT
xpc_object_t xpc_shmem_create_readonly(const void* region, size_t length) {
return NULL;
};
XPC_EXPORT
size_t xpc_shmem_get_length(xpc_object_t xshmem) {
return 0;
};

180
src/string.m Normal file
View File

@ -0,0 +1,180 @@
#import <xpc/objects/string.h>
#import <xpc/util.h>
#import <xpc/xpc.h>
XPC_CLASS_SYMBOL_DECL(string);
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(string)
XPC_CLASS_HEADER(string);
- (void)dealloc
{
XPC_THIS_DECL(string);
if (this->freeWhenDone && this->string) {
free((void*)this->string);
}
[super dealloc];
}
- (char*)xpcDescription
{
char* output = NULL;
if (self.UTF8String) {
asprintf(&output, "<%s: %s>", xpc_class_name(self), self.UTF8String);
} else {
asprintf(&output, "<%s: NULL>", xpc_class_name(self));
}
return output;
}
- (NSUInteger)byteLength
{
XPC_THIS_DECL(string);
if (this->byteLength == NSUIntegerMax) {
return strlen(this->string);
}
return this->byteLength;
}
- (const char*)UTF8String
{
XPC_THIS_DECL(string);
return this->string;
}
- (instancetype)initWithUTF8String: (const char*)string
{
if (self = [super init]) {
XPC_THIS_DECL(string);
this->byteLength = strlen(string);
char* buf = malloc(this->byteLength + 1);
if (!buf) {
[self release];
return nil;
}
strncpy(buf, string, this->byteLength + 1);
this->string = buf;
this->freeWhenDone = YES;
}
return self;
}
- (instancetype)initWithUTF8StringNoCopy: (const char*)string freeWhenDone: (BOOL)freeIt
{
if (self = [super init]) {
XPC_THIS_DECL(string);
this->byteLength = strlen(string);
this->string = string;
this->freeWhenDone = freeIt;
}
return self;
}
- (instancetype)initWithFormat: (const char*)format, ...
{
va_list args;
va_start(args, format);
self = [self initWithFormat: format arguments: args];
va_end(args);
return self;
}
- (instancetype)initWithFormat: (const char*)format arguments: (va_list)args
{
if (self = [super init]) {
XPC_THIS_DECL(string);
vasprintf((char**)&this->string, format, args);
if (!this->string) {
[self release];
return nil;
}
this->byteLength = strlen(this->string);
this->freeWhenDone = YES;
}
return self;
}
- (void)replaceStringWithString: (const char*)string
{
XPC_THIS_DECL(string);
size_t newByteLength = strlen(string);
char* newString = malloc(newByteLength + 1);
if (!newString) {
return;
}
if (this->freeWhenDone && this->string) {
free((void*)this->string);
}
this->byteLength = newByteLength;
strncpy(newString, string, this->byteLength + 1);
this->string = newString;
this->freeWhenDone = true;
}
- (NSUInteger)hash
{
XPC_THIS_DECL(string);
return xpc_raw_data_hash(this->string, self.byteLength + 1);
}
@end
//
// C API
//
XPC_EXPORT
xpc_object_t xpc_string_create(const char* string) {
return [[XPC_CLASS(string) alloc] initWithUTF8String: string];
};
XPC_EXPORT
xpc_object_t xpc_string_create_with_format(const char* format, ...) {
va_list args;
va_start(args, format);
xpc_object_t string = xpc_string_create_with_format_and_arguments(format, args);
va_end(args);
return string;
};
XPC_EXPORT
xpc_object_t xpc_string_create_with_format_and_arguments(const char* format, va_list args) {
return [[XPC_CLASS(string) alloc] initWithFormat: format arguments: args];
};
XPC_EXPORT
size_t xpc_string_get_length(xpc_object_t xstring) {
TO_OBJC_CHECKED(string, xstring, string) {
return string.byteLength;
}
return 0;
};
XPC_EXPORT
const char* xpc_string_get_string_ptr(xpc_object_t xstring) {
TO_OBJC_CHECKED(string, xstring, string) {
return string.UTF8String;
}
return NULL;
};
//
// private C API
//
XPC_EXPORT
void _xpc_string_set_value(xpc_object_t xstring, const char* new_string) {
TO_OBJC_CHECKED(string, xstring, string) {
[string replaceStringWithString: new_string];
}
};
XPC_EXPORT
xpc_object_t xpc_string_create_no_copy(const char* string) {
return [[XPC_CLASS(string) alloc] initWithUTF8StringNoCopy: string freeWhenDone: NO];
};

View File

@ -1,590 +0,0 @@
/*-
* Copyright (c) 2000 Poul-Henning Kamp and Dag-Erling Codan Sm¿rgrav
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#ifdef KERNEL
/* #include <ctype.h> */
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/uio_internal.h>
#include <sys/systm.h>
#include <stdarg.h>
#else /* KERNEL */
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#endif /* KERNEL */
#include <sys/sbuf.h>
#ifndef PAGE_SIZE
# define PAGE_SIZE 4096
#endif
#ifdef KERNEL
/* MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers"); */
#define SBMALLOC(size) _MALLOC(size, M_SBUF, M_WAITOK)
#define SBFREE(buf) FREE(buf, M_SBUF)
#else /* KERNEL */
#define KASSERT(e, m)
#define SBMALLOC(size) malloc(size)
#define SBFREE(buf) free(buf)
#define min(x,y) MIN(x,y)
#endif /* KERNEL */
/*
* Predicates
*/
#define SBUF_ISDYNAMIC(s) ((s)->s_flags & SBUF_DYNAMIC)
#define SBUF_ISDYNSTRUCT(s) ((s)->s_flags & SBUF_DYNSTRUCT)
#define SBUF_ISFINISHED(s) ((s)->s_flags & SBUF_FINISHED)
#define SBUF_HASOVERFLOWED(s) ((s)->s_flags & SBUF_OVERFLOWED)
#define SBUF_HASROOM(s) ((s)->s_len < (s)->s_size - 1)
#define SBUF_FREESPACE(s) ((s)->s_size - (s)->s_len - 1)
#define SBUF_CANEXTEND(s) ((s)->s_flags & SBUF_AUTOEXTEND)
/*
* Set / clear flags
*/
#define SBUF_SETFLAG(s, f) do { (s)->s_flags |= (f); } while (0)
#define SBUF_CLEARFLAG(s, f) do { (s)->s_flags &= ~(f); } while (0)
#define SBUF_MINEXTENDSIZE 16 /* Should be power of 2. */
#define SBUF_MAXEXTENDSIZE PAGE_SIZE
#define SBUF_MAXEXTENDINCR PAGE_SIZE
/*
* Debugging support
*/
#if defined(KERNEL) && defined(INVARIANTS)
static void
_assert_sbuf_integrity(const char *fun, struct sbuf *s)
{
KASSERT(s != NULL,
("%s called with a NULL sbuf pointer", fun));
KASSERT(s->s_buf != NULL,
("%s called with uninitialized or corrupt sbuf", fun));
KASSERT(s->s_len < s->s_size,
("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
}
static void
_assert_sbuf_state(const char *fun, struct sbuf *s, int state)
{
KASSERT((s->s_flags & SBUF_FINISHED) == state,
("%s called with %sfinished or corrupt sbuf", fun,
(state ? "un" : "")));
}
#define assert_sbuf_integrity(s) _assert_sbuf_integrity(__func__, (s))
#define assert_sbuf_state(s, i) _assert_sbuf_state(__func__, (s), (i))
#else /* KERNEL && INVARIANTS */
#define assert_sbuf_integrity(s) do { } while (0)
#define assert_sbuf_state(s, i) do { } while (0)
#endif /* KERNEL && INVARIANTS */
static int
sbuf_extendsize(int size)
{
int newsize;
newsize = SBUF_MINEXTENDSIZE;
while (newsize < size) {
if (newsize < (int)SBUF_MAXEXTENDSIZE)
newsize *= 2;
else
newsize += SBUF_MAXEXTENDINCR;
}
return (newsize);
}
/*
* Extend an sbuf.
*/
static int
sbuf_extend(struct sbuf *s, int addlen)
{
char *newbuf;
int newsize;
if (!SBUF_CANEXTEND(s))
return (-1);
newsize = sbuf_extendsize(s->s_size + addlen);
newbuf = (char *)SBMALLOC(newsize);
if (newbuf == NULL)
return (-1);
bcopy(s->s_buf, newbuf, s->s_size);
if (SBUF_ISDYNAMIC(s))
SBFREE(s->s_buf);
else
SBUF_SETFLAG(s, SBUF_DYNAMIC);
s->s_buf = newbuf;
s->s_size = newsize;
return (0);
}
/*
* Initialize an sbuf.
* If buf is non-NULL, it points to a static or already-allocated string
* big enough to hold at least length characters.
*/
struct sbuf *
sbuf_new(struct sbuf *s, char *buf, int length, int flags)
{
KASSERT(length >= 0,
("attempt to create an sbuf of negative length (%d)", length));
KASSERT((flags & ~SBUF_USRFLAGMSK) == 0,
("%s called with invalid flags", __func__));
flags &= SBUF_USRFLAGMSK;
if (s == NULL) {
s = (struct sbuf *)SBMALLOC(sizeof *s);
if (s == NULL)
return (NULL);
bzero(s, sizeof *s);
s->s_flags = flags;
SBUF_SETFLAG(s, SBUF_DYNSTRUCT);
} else {
bzero(s, sizeof *s);
s->s_flags = flags;
}
s->s_size = length;
if (buf) {
s->s_buf = buf;
return (s);
}
if (flags & SBUF_AUTOEXTEND)
s->s_size = sbuf_extendsize(s->s_size);
s->s_buf = (char *)SBMALLOC(s->s_size);
if (s->s_buf == NULL) {
if (SBUF_ISDYNSTRUCT(s))
SBFREE(s);
return (NULL);
}
SBUF_SETFLAG(s, SBUF_DYNAMIC);
return (s);
}
#ifdef KERNEL
/*
* Create an sbuf with uio data
*/
struct sbuf *
sbuf_uionew(struct sbuf *s, struct uio *uio, int *error)
{
KASSERT(uio != NULL,
("%s called with NULL uio pointer", __func__));
KASSERT(error != NULL,
("%s called with NULL error pointer", __func__));
s = sbuf_new(s, NULL, uio_resid(uio) + 1, 0);
if (s == NULL) {
*error = ENOMEM;
return (NULL);
}
*error = uiomove(s->s_buf, uio_resid(uio), uio);
if (*error != 0) {
sbuf_delete(s);
return (NULL);
}
s->s_len = s->s_size - 1;
*error = 0;
return (s);
}
#endif
/*
* Clear an sbuf and reset its position.
*/
void
sbuf_clear(struct sbuf *s)
{
assert_sbuf_integrity(s);
/* don't care if it's finished or not */
SBUF_CLEARFLAG(s, SBUF_FINISHED);
SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
s->s_len = 0;
}
/*
* Set the sbuf's end position to an arbitrary value.
* Effectively truncates the sbuf at the new position.
*/
int
sbuf_setpos(struct sbuf *s, int pos)
{
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
KASSERT(pos >= 0,
("attempt to seek to a negative position (%d)", pos));
KASSERT(pos < s->s_size,
("attempt to seek past end of sbuf (%d >= %d)", pos, s->s_size));
if (pos < 0 || pos > s->s_len)
return (-1);
s->s_len = pos;
return (0);
}
/*
* Append a byte string to an sbuf.
*/
int
sbuf_bcat(struct sbuf *s, const void *buf, size_t len)
{
const char *str = buf;
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
if (SBUF_HASOVERFLOWED(s))
return (-1);
for (; len; len--) {
if (!SBUF_HASROOM(s) && sbuf_extend(s, len) < 0)
break;
s->s_buf[s->s_len++] = *str++;
}
if (len) {
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
return (-1);
}
return (0);
}
#ifdef KERNEL
/*
* Copy a byte string from userland into an sbuf.
*/
int
sbuf_bcopyin(struct sbuf *s, const void *uaddr, size_t len)
{
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
if (SBUF_HASOVERFLOWED(s))
return (-1);
if (len == 0)
return (0);
if (len > (unsigned) SBUF_FREESPACE(s)) {
sbuf_extend(s, len - SBUF_FREESPACE(s));
len = min(len, SBUF_FREESPACE(s));
}
if (copyin(CAST_USER_ADDR_T(uaddr), s->s_buf + s->s_len, len) != 0)
return (-1);
s->s_len += len;
return (0);
}
#endif
/*
* Copy a byte string into an sbuf.
*/
int
sbuf_bcpy(struct sbuf *s, const void *buf, size_t len)
{
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
sbuf_clear(s);
return (sbuf_bcat(s, buf, len));
}
/*
* Append a string to an sbuf.
*/
int
sbuf_cat(struct sbuf *s, const char *str)
{
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
if (SBUF_HASOVERFLOWED(s))
return (-1);
while (*str) {
if (!SBUF_HASROOM(s) && sbuf_extend(s, strlen(str)) < 0)
break;
s->s_buf[s->s_len++] = *str++;
}
if (*str) {
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
return (-1);
}
return (0);
}
#ifdef KERNEL
/*
* Append a string from userland to an sbuf.
*/
int
sbuf_copyin(struct sbuf *s, const void *uaddr, size_t len)
{
size_t done;
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
if (SBUF_HASOVERFLOWED(s))
return (-1);
if (len == 0)
len = SBUF_FREESPACE(s); /* XXX return 0? */
if (len > (unsigned) SBUF_FREESPACE(s)) {
sbuf_extend(s, len);
len = min(len, SBUF_FREESPACE(s));
}
switch (copyinstr(CAST_USER_ADDR_T(uaddr), s->s_buf + s->s_len, len + 1, &done)) {
case ENAMETOOLONG:
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
/* fall through */
case 0:
s->s_len += done - 1;
break;
default:
return (-1); /* XXX */
}
return (done);
}
#endif
/*
* Copy a string into an sbuf.
*/
int
sbuf_cpy(struct sbuf *s, const char *str)
{
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
sbuf_clear(s);
return (sbuf_cat(s, str));
}
/*
* Format the given argument list and append the resulting string to an sbuf.
*/
int
sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
{
__builtin_va_list ap_copy; /* XXX tduffy - blame on him */
int len;
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
KASSERT(fmt != NULL,
("%s called with a NULL format string", __func__));
if (SBUF_HASOVERFLOWED(s))
return (-1);
do {
va_copy(ap_copy, ap);
len = vsnprintf(&s->s_buf[s->s_len], SBUF_FREESPACE(s) + 1,
fmt, ap_copy);
va_end(ap_copy);
} while (len > SBUF_FREESPACE(s) &&
sbuf_extend(s, len - SBUF_FREESPACE(s)) == 0);
/*
* s->s_len is the length of the string, without the terminating nul.
* When updating s->s_len, we must subtract 1 from the length that
* we passed into vsnprintf() because that length includes the
* terminating nul.
*
* vsnprintf() returns the amount that would have been copied,
* given sufficient space, hence the min() calculation below.
*/
s->s_len += min(len, SBUF_FREESPACE(s));
if (!SBUF_HASROOM(s) && !SBUF_CANEXTEND(s))
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
KASSERT(s->s_len < s->s_size,
("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
if (SBUF_HASOVERFLOWED(s))
return (-1);
return (0);
}
/*
* Format the given arguments and append the resulting string to an sbuf.
*/
int
sbuf_printf(struct sbuf *s, const char *fmt, ...)
{
va_list ap;
int result;
va_start(ap, fmt);
result = sbuf_vprintf(s, fmt, ap);
va_end(ap);
return(result);
}
/*
* Append a character to an sbuf.
*/
int
sbuf_putc(struct sbuf *s, int c)
{
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
if (SBUF_HASOVERFLOWED(s))
return (-1);
if (!SBUF_HASROOM(s) && sbuf_extend(s, 1) < 0) {
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
return (-1);
}
if (c != '\0')
s->s_buf[s->s_len++] = c;
return (0);
}
#ifdef KERNEL
static inline int
isspace(char ch)
{
return (ch == ' ' || ch == '\n' || ch == '\t');
}
#endif
/*
* Trim whitespace characters from end of an sbuf.
*/
int
sbuf_trim(struct sbuf *s)
{
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
if (SBUF_HASOVERFLOWED(s))
return (-1);
while (s->s_len && isspace(s->s_buf[s->s_len-1]))
--s->s_len;
return (0);
}
/*
* Check if an sbuf overflowed
*/
int
sbuf_overflowed(struct sbuf *s)
{
return SBUF_HASOVERFLOWED(s);
}
/*
* Finish off an sbuf.
*/
void
sbuf_finish(struct sbuf *s)
{
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
s->s_buf[s->s_len] = '\0';
SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
SBUF_SETFLAG(s, SBUF_FINISHED);
}
/*
* Return a pointer to the sbuf data.
*/
char *
sbuf_data(struct sbuf *s)
{
assert_sbuf_integrity(s);
assert_sbuf_state(s, SBUF_FINISHED);
return s->s_buf;
}
/*
* Return the length of the sbuf data.
*/
int
sbuf_len(struct sbuf *s)
{
assert_sbuf_integrity(s);
/* don't care if it's finished or not */
if (SBUF_HASOVERFLOWED(s))
return (-1);
return s->s_len;
}
/*
* Clear an sbuf, free its buffer if necessary.
*/
void
sbuf_delete(struct sbuf *s)
{
int isdyn;
assert_sbuf_integrity(s);
/* don't care if it's finished or not */
if (SBUF_ISDYNAMIC(s))
SBFREE(s->s_buf);
isdyn = SBUF_ISDYNSTRUCT(s);
bzero(s, sizeof *s);
if (isdyn)
SBFREE(s);
}
/*
* Check if an sbuf has been finished.
*/
int
sbuf_done(struct sbuf *s)
{
return(SBUF_ISFINISHED(s));
}

View File

@ -1,29 +0,0 @@
#include <xpc/private.h>
#include <stddef.h>
#include <stdio.h>
#define STUB() printf("STUB %s called\n", __func__)
xpc_object_t xpc_create_from_plist(void *data, size_t size)
{
STUB();
return NULL;
}
void xpc_connection_set_target_uid(xpc_connection_t connection, uid_t uid)
{
STUB();
}
void xpc_connection_set_instance(xpc_connection_t connection, uuid_t uid)
{
STUB();
}
/* already implemented!
void xpc_dictionary_set_mach_send(xpc_object_t object, char *type, int port)
{
STUB();
}
*/

View File

@ -1,31 +1,74 @@
#include <os/transaction_private.h>
#include <os/object_private.h>
struct os_transaction_s;
struct os_transaction_extra_vtable_s {};
struct os_transaction_vtable_s {
_OS_OBJECT_CLASS_HEADER();
struct os_transaction_extra_vtable_s _os_obj_vtable;
};
extern const struct os_transaction_vtable_s OS_OBJECT_CLASS_SYMBOL(os_transaction) __asm__(OS_OBJC_CLASS_RAW_SYMBOL_NAME(OS_OBJECT_CLASS(os_transaction)));
#define OS_TRANSACTION_CLASS (&OS_OBJECT_CLASS_SYMBOL(os_transaction))
struct os_transaction_s {
_OS_OBJECT_HEADER(
const struct os_transaction_vtable_s* os_obj_isa,
os_obj_ref_cnt,
os_obj_xref_cnt
);
};
os_transaction_t os_transaction_create(const char* transaction_name) {
return (os_transaction_t)_os_object_alloc_realized(OS_TRANSACTION_CLASS, sizeof(struct os_transaction_s));
};
#import <Foundation/NSZone.h>
#import <xpc/xpc.h>
OS_OBJECT_NONLAZY_CLASS
@implementation OS_OBJECT_CLASS(os_transaction)
OS_OBJECT_NONLAZY_CLASS_LOAD
+ (instancetype)allocWithZone: (NSZone*)zone
{
return (os_transaction_t)_os_object_alloc_realized([self class], sizeof(struct os_transaction_s));
}
- (instancetype)initWithName: (const char*)name
{
// we CANNOT call `-[super init]`.
// libdispatch makes `init` crash on `OS_object`s.
return self;
}
@end
//
// C API
//
XPC_EXPORT
os_transaction_t os_transaction_create(const char* transaction_name) {
return [[OS_OBJECT_CLASS(os_transaction) alloc] initWithName: transaction_name];
};
XPC_EXPORT
char* os_transaction_copy_description(os_transaction_t transaction) {
return NULL;
};
XPC_EXPORT
int os_transaction_needs_more_time(os_transaction_t transaction) {
return -1;
};
// these are technically not related to os_transaction,
// but the file is called `transaction.m`, so we'll dump them in here
XPC_EXPORT
void xpc_transaction_begin(void) {
};
XPC_EXPORT
void xpc_transaction_end(void) {
};
//
// private C API
//
XPC_EXPORT
void xpc_transaction_exit_clean(void) {
};
XPC_EXPORT
void xpc_transaction_interrupt_clean_exit(void) {
};
XPC_EXPORT
void xpc_transactions_enable(void) {
};

View File

@ -1,525 +0,0 @@
/*
* Copyright 2014-2015 iXsystems, Inc.
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <sys/types.h>
#include <mach/mach.h>
#include <xpc/launchd.h>
#include <sys/fileport.h>
#include <xpc/internal.h>
struct _xpc_type_s {
};
typedef const struct _xpc_type_s xt;
xt _xpc_type_activity;
xt _xpc_type_array;
xt _xpc_type_bool;
xt _xpc_type_connection;
xt _xpc_type_data;
xt _xpc_type_date;
xt _xpc_type_dictionary;
xt _xpc_type_endpoint;
xt _xpc_type_null;
xt _xpc_type_error;
xt _xpc_type_fd;
xt _xpc_type_int64;
xt _xpc_type_uint64;
xt _xpc_type_shmem;
xt _xpc_type_string;
xt _xpc_type_uuid;
xt _xpc_type_double;
xt _xpc_type_pointer;
struct _xpc_bool_s {
// we have to wrap it in `_xpc_bool_s` because the header defines the variable with that type
struct xpc_object xo;
};
const struct _xpc_bool_s _xpc_bool_true = {
.xo = _XPC_GLOBAL_OBJECT_INITIALIZER(_XPC_TYPE_BOOL, 0, .b = true),
};
const struct _xpc_bool_s _xpc_bool_false = {
.xo = _XPC_GLOBAL_OBJECT_INITIALIZER(_XPC_TYPE_BOOL, 0, .b = false),
};
const struct xpc_object _xpc_null = _XPC_GLOBAL_OBJECT_INITIALIZER(_XPC_TYPE_NULL, 0, .b = false); // requires a union value, so just set the boolean
static size_t xpc_data_hash(const uint8_t *data, size_t length);
static xpc_type_t xpc_typemap[] = {
NULL,
XPC_TYPE_DICTIONARY,
XPC_TYPE_ARRAY,
XPC_TYPE_BOOL,
XPC_TYPE_CONNECTION,
XPC_TYPE_ENDPOINT,
XPC_TYPE_NULL,
XPC_TYPE_ACTIVITY,
XPC_TYPE_INT64,
XPC_TYPE_UINT64,
XPC_TYPE_DATE,
XPC_TYPE_DATA,
XPC_TYPE_STRING,
XPC_TYPE_UUID,
XPC_TYPE_FD,
XPC_TYPE_SHMEM,
XPC_TYPE_ERROR,
XPC_TYPE_DOUBLE,
XPC_TYPE_POINTER,
};
static const char *xpc_typestr[] = {
"invalid",
"dictionary",
"array",
"bool",
"connection",
"endpoint",
"null",
"activity",
"int64",
"uint64",
"date",
"data",
"string",
"uuid",
"fd",
"shmem",
"error",
"double",
"pointer",
};
__private_extern__ struct xpc_object *
_xpc_prim_create(int type, xpc_u value, size_t size)
{
return (_xpc_prim_create_flags(type, value, size, 0));
}
__private_extern__ struct xpc_object *
_xpc_prim_create_flags(int type, xpc_u value, size_t size, uint16_t flags)
{
struct xpc_object *xo;
if ((xo = malloc(sizeof(*xo))) == NULL)
return (NULL);
xo->xo_size = size;
xo->xo_xpc_type = type;
xo->xo_flags = flags;
xo->xo_u = value;
xo->xo_refcnt = 1;
xo->xo_audit_token = NULL;
if (type == _XPC_TYPE_DICTIONARY)
TAILQ_INIT(&xo->xo_dict);
if (type == _XPC_TYPE_ARRAY)
TAILQ_INIT(&xo->xo_array);
if (type == _XPC_TYPE_ACTIVITY)
xo->xo_activity.criteria = xpc_dictionary_create(NULL, NULL, 0);
return (xo);
}
xpc_object_t
xpc_null_create(void)
{
return &_xpc_null;
}
xpc_object_t
xpc_bool_create(bool value)
{
return value ? XPC_BOOL_TRUE : XPC_BOOL_FALSE;
}
bool
xpc_bool_get_value(xpc_object_t xbool)
{
return xbool == XPC_BOOL_FALSE ? false : true;
}
xpc_object_t
xpc_int64_create(int64_t value)
{
xpc_u val;
val.i = value;
return _xpc_prim_create(_XPC_TYPE_INT64, val, 1);
}
int64_t
xpc_int64_get_value(xpc_object_t xint)
{
struct xpc_object *xo;
xo = xint;
if (xo == NULL)
return (0);
if (xo->xo_xpc_type == _XPC_TYPE_INT64)
return (xo->xo_int);
return (0);
}
xpc_object_t
xpc_uint64_create(uint64_t value)
{
xpc_u val;
val.ui = value;
return _xpc_prim_create(_XPC_TYPE_UINT64, val, 1);
}
uint64_t
xpc_uint64_get_value(xpc_object_t xuint)
{
struct xpc_object *xo;
xo = xuint;
if (xo == NULL)
return (0);
if (xo->xo_xpc_type == _XPC_TYPE_UINT64)
return (xo->xo_uint);
return (0);
}
xpc_object_t
xpc_double_create(double value)
{
xpc_u val;
val.d = value;
return _xpc_prim_create(_XPC_TYPE_DOUBLE, val, 1);
}
double
xpc_double_get_value(xpc_object_t xdouble)
{
struct xpc_object *xo = xdouble;
if (xo->xo_xpc_type == _XPC_TYPE_DOUBLE)
return (xo->xo_d);
return (0);
}
xpc_object_t
xpc_date_create(int64_t interval)
{
xpc_u val;
val.i = interval;
return _xpc_prim_create(_XPC_TYPE_DATE, val, 1);
}
xpc_object_t
xpc_date_create_from_current(void)
{
}
int64_t
xpc_date_get_value(xpc_object_t xdate)
{
struct xpc_object *xo = xdate;
if (xo == NULL)
return (0);
if (xo->xo_xpc_type == _XPC_TYPE_DATE)
return (xo->xo_int);
return (0);
}
xpc_object_t
xpc_data_create(const void *bytes, size_t length)
{
xpc_u val;
void* copy = malloc(length);
memcpy(copy, bytes, length);
val.ptr = copy;
return _xpc_prim_create(_XPC_TYPE_DATA, val, length);
}
xpc_object_t
xpc_data_create_with_dispatch_data(dispatch_data_t ddata)
{
}
size_t
xpc_data_get_length(xpc_object_t xdata)
{
struct xpc_object *xo = xdata;
if (xo == NULL)
return (0);
if (xo->xo_xpc_type == _XPC_TYPE_DATA)
return (xo->xo_size);
return (0);
}
const void *
xpc_data_get_bytes_ptr(xpc_object_t xdata)
{
struct xpc_object *xo = xdata;
if (xo == NULL)
return (NULL);
if (xo->xo_xpc_type == _XPC_TYPE_DATA)
return (void *)(xo->xo_ptr);
return (0);
}
xpc_object_t
xpc_fd_create(int fd)
{
xpc_u val;
fileport_makeport(fd, &val.port);
return _xpc_prim_create(_XPC_TYPE_FD, val, sizeof(val.port));
}
int
xpc_fd_dup(xpc_object_t xfd)
{
struct xpc_object *xo = xfd;
if (xo->xo_xpc_type != _XPC_TYPE_FD) return -1;
return fileport_makefd(xo->xo_u.port);
}
size_t
xpc_data_get_bytes(xpc_object_t xdata, void *buffer, size_t off, size_t length)
{
/* XXX */
return (0);
}
xpc_object_t
xpc_string_create(const char *string)
{
xpc_u val;
size_t len = strlen(string);
char* str = malloc(len + 1);
strncpy(str, string, len);
str[len] = '\0';
val.str = str;
return _xpc_prim_create(_XPC_TYPE_STRING, val, len);
}
xpc_object_t
xpc_string_create_with_format(const char *fmt, ...)
{
va_list ap;
xpc_u val;
va_start(ap, fmt);
vasprintf((char **)&val.str, fmt, ap);
va_end(ap);
return _xpc_prim_create(_XPC_TYPE_STRING, val, strlen(val.str));
}
xpc_object_t
xpc_string_create_with_format_and_arguments(const char *fmt, va_list ap)
{
xpc_u val;
vasprintf((char **)&val.str, fmt, ap);
return _xpc_prim_create(_XPC_TYPE_STRING, val, strlen(val.str));
}
size_t
xpc_string_get_length(xpc_object_t xstring)
{
struct xpc_object *xo = xstring;
if (xo == NULL)
return (0);
if (xo->xo_xpc_type == _XPC_TYPE_STRING)
return (xo->xo_size);
return (0);
}
const char *
xpc_string_get_string_ptr(xpc_object_t xstring)
{
struct xpc_object *xo = xstring;
if (xo == NULL)
return (NULL);
if (xo->xo_xpc_type == _XPC_TYPE_STRING)
return (xo->xo_str);
return (NULL);
}
xpc_object_t
xpc_uuid_create(const uuid_t uuid)
{
xpc_u val;
memcpy(val.uuid, uuid, sizeof(uuid_t));
return _xpc_prim_create(_XPC_TYPE_UUID, val, 1);
}
const uint8_t *
xpc_uuid_get_bytes(xpc_object_t xuuid)
{
struct xpc_object *xo;
xo = xuuid;
if (xo == NULL)
return (NULL);
if (xo->xo_xpc_type == _XPC_TYPE_UUID)
return ((uint8_t*)&xo->xo_uuid);
return (NULL);
}
xpc_type_t
xpc_get_type(xpc_object_t obj)
{
struct xpc_object* xo = obj;
// temporary hack
if (xo->xo_xpc_type == _XPC_TYPE_DICTIONARY && xpc_dictionary_get_value(obj, XPC_ERROR_KEY_DESCRIPTION) != NULL) {
return XPC_TYPE_ERROR;
}
return (xpc_typemap[xo->xo_xpc_type]);
}
bool
xpc_equal(xpc_object_t x1, xpc_object_t x2)
{
return xpc_hash(x1) == xpc_hash(x2);
}
static size_t
xpc_data_hash(const uint8_t *data, size_t length)
{
size_t hash = 5381;
while (length--)
hash = ((hash << 5) + hash) + data[length];
return (hash);
}
size_t
xpc_hash(xpc_object_t obj)
{
struct xpc_object *xo;
__block size_t hash = 0;
xo = obj;
switch (xo->xo_xpc_type) {
case _XPC_TYPE_BOOL:
case _XPC_TYPE_INT64:
case _XPC_TYPE_UINT64:
case _XPC_TYPE_DATE:
case _XPC_TYPE_ENDPOINT:
return ((size_t)xo->xo_u.ui);
case _XPC_TYPE_STRING:
return (xpc_data_hash(
(const uint8_t *)xpc_string_get_string_ptr(obj),
xpc_string_get_length(obj)));
case _XPC_TYPE_DATA:
return (xpc_data_hash(
xpc_data_get_bytes_ptr(obj),
xpc_data_get_length(obj)));
case _XPC_TYPE_DICTIONARY:
xpc_dictionary_apply(obj, ^(const char *k, xpc_object_t v) {
hash ^= xpc_data_hash((const uint8_t *)k, strlen(k));
hash ^= xpc_hash(v);
return ((bool)true);
});
return (hash);
case _XPC_TYPE_ARRAY:
xpc_array_apply(obj, ^(size_t idx, xpc_object_t v) {
hash ^= xpc_hash(v);
return ((bool)true);
});
return (hash);
case _XPC_TYPE_POINTER:
return ((size_t)xo->xo_u.ptr);
case _XPC_TYPE_CONNECTION:
// two connections can only be equal if they are the same object (i.e. with the same pointers)
return (size_t)obj;
default:
return 0;
}
}
__private_extern__ const char *
_xpc_get_type_name(xpc_object_t obj)
{
struct xpc_object *xo;
xo = obj;
return (xpc_typestr[xo->xo_xpc_type]);
}
xpc_object_t xpc_pointer_create(void* value) {
xpc_u val;
val.ptr = (uintptr_t)value;
return _xpc_prim_create(_XPC_TYPE_POINTER, val, 1);
};
void* xpc_pointer_get_value(xpc_object_t xptr) {
struct xpc_object* xo = xptr;
if (xo == NULL)
return NULL;
if (xo->xo_xpc_type == _XPC_TYPE_POINTER)
return xo->xo_ptr;
return NULL;
};

42
src/type.m Normal file
View File

@ -0,0 +1,42 @@
#import <xpc/xpc.h>
#import <xpc/util.h>
#import <objc/runtime.h>
//
// C API
//
XPC_EXPORT
xpc_type_t xpc_get_type(xpc_object_t xobject) {
TO_OBJC_CHECKED(object, xobject, object) {
return (xpc_type_t)[object class];
}
return NULL;
};
//
// private C API
//
XPC_EXPORT
const char* xpc_type_get_name(xpc_type_t xtype) {
return class_getName((Class)xtype);
};
XPC_EXPORT
Class xpc_get_class4NSXPC(xpc_type_t xtype) {
return (Class)xtype;
};
XPC_EXPORT
bool xpc_is_kind_of_xpc_object4NSXPC(xpc_object_t object) {
return [object isKindOfClass: [XPC_CLASS(object) class]];
};
struct some_extension_type_struct;
XPC_EXPORT
void xpc_extension_type_init(struct some_extension_type_struct* extension_type, void* some_user_function_probably) {
};

22
src/uint64.m Normal file
View File

@ -0,0 +1,22 @@
#import <xpc/objects/uint64.h>
#import <xpc/util.h>
#import <xpc/xpc.h>
XPC_WRAPPER_CLASS_IMPL(uint64, uint64_t, "%llu");
//
// C API
//
XPC_EXPORT
xpc_object_t xpc_uint64_create(uint64_t value) {
return [[XPC_CLASS(uint64) alloc] initWithValue: value];
};
XPC_EXPORT
uint64_t xpc_uint64_get_value(xpc_object_t xuint) {
TO_OBJC_CHECKED(uint64, xuint, uinteger) {
return uinteger.value;
}
return 0;
};

69
src/util.m Normal file
View File

@ -0,0 +1,69 @@
#import <xpc/util.h>
#import <objc/runtime.h>
XPC_CLASS(object)* xpc_retain_for_collection(XPC_CLASS(object)* object) {
// connections aren't retained by collections
if ([object isKindOfClass: [XPC_CLASS(connection) class]]) {
return object;
}
return [object retain];
};
void xpc_release_for_collection(XPC_CLASS(object)* object) {
if ([object isKindOfClass: [XPC_CLASS(connection) class]]) {
return;
}
return [object release];
};
const char* xpc_class_name(XPC_CLASS(object)* object) {
return class_getName([object class]);
};
char* xpc_description_indent(const char* description, bool indentFirstLine) {
size_t newSize = 0;
for (size_t i = 0; description[i] != '\0'; ++i) {
++newSize;
if (description[i] == '\n') {
++newSize;
}
}
if (indentFirstLine) {
++newSize;
}
char* newDescription = malloc(sizeof(char) * (newSize + 1));
if (indentFirstLine) {
newDescription[0] = '\t';
}
for (size_t i = 0, j = indentFirstLine ? 1 : 0; description[i] != '\0'; ++i, ++j) {
newDescription[j] = description[i];
if (description[i] == '\n') {
newDescription[++j] = '\t';
}
}
newDescription[newSize] = '\0';
return newDescription;
};
size_t xpc_raw_data_hash(const void* data, size_t data_length) {
if (data_length == 0) {
return 0x1505;
}
size_t result = 0;
for (size_t i = 0; i < data_length; ++i) {
uint8_t current = ((uint8_t*)data)[i];
if (current == 0) {
break;
}
result = (result * 0x21) + current;
}
return result;
};
kern_return_t xpc_mach_port_retain_right(mach_port_name_t port, mach_port_right_t right) {
return mach_port_mod_refs(mach_task_self(), port, right, 1);
};
kern_return_t xpc_mach_port_release_right(mach_port_name_t port, mach_port_right_t right) {
return mach_port_mod_refs(mach_task_self(), port, right, -1);
};

61
src/uuid.m Normal file
View File

@ -0,0 +1,61 @@
#import <xpc/objects/uuid.h>
#import <xpc/util.h>
#import <xpc/xpc.h>
#define UUID_STRING_LENGTH 36
XPC_CLASS_SYMBOL_DECL(uuid);
OS_OBJECT_NONLAZY_CLASS
@implementation XPC_CLASS(uuid)
XPC_CLASS_HEADER(uuid);
- (char*)xpcDescription
{
char* output = NULL;
char asString[UUID_STRING_LENGTH + 1];
uuid_unparse(self.bytes, asString);
asprintf(&output, "<%s: %s>", xpc_class_name(self), asString);
return output;
}
- (uint8_t*)bytes
{
XPC_THIS_DECL(uuid);
return this->value;
}
- (instancetype)initWithBytes: (const uint8_t*)bytes
{
if (self = [super init]) {
XPC_THIS_DECL(uuid);
memcpy(this->value, bytes, sizeof(this->value));
}
return self;
}
- (NSUInteger)hash
{
XPC_THIS_DECL(uuid);
return xpc_raw_data_hash(this->value, sizeof(this->value));
}
@end
//
// C API
//
XPC_EXPORT
xpc_object_t xpc_uuid_create(const uuid_t uuid) {
return [[XPC_CLASS(uuid) alloc] initWithBytes: uuid];
};
XPC_EXPORT
const uint8_t* xpc_uuid_get_bytes(xpc_object_t xuuid) {
TO_OBJC_CHECKED(uuid, xuuid, uuid) {
return uuid.bytes;
}
return NULL;
};

25
test/CMakeLists.txt Normal file
View File

@ -0,0 +1,25 @@
set(TEST_SOURCES
array.m
base.m
bool.c
data.c
dictionary.m
main.c
string.c
type.c
)
include_directories(
"${CMAKE_CURRENT_SOURCE_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}/ctest"
)
add_darling_executable(test_libxpc ${TEST_SOURCES})
target_link_libraries(test_libxpc
# link statically to ensure we always use our copy of libxpc for testing, not the system's
xpc_static
objc
)
install(TARGETS test_libxpc DESTINATION libexec/darling/usr/libexec)

129
test/array.m Normal file
View File

@ -0,0 +1,129 @@
#include "ctest-plus.h"
#include <xpc/private.h>
#include "test-util.h"
CTEST_DATA(array) {
xpc_object_t objects[OBJECT_COUNT];
xpc_object_t array;
};
CTEST_SETUP(array) {
create_objects(data->objects, OBJECT_COUNT);
data->array = xpc_array_create(data->objects, OBJECT_COUNT);
};
CTEST_TEARDOWN(array) {
xpc_release(data->array);
release_objects(data->objects, OBJECT_COUNT);
};
CTEST(array, create_empty) {
xpc_object_t array = xpc_array_create(NULL, 0);
ASSERT_NOT_NULL(array);
ASSERT_EQUAL_U(0, xpc_array_get_count(array));
xpc_release(array);
};
CTEST(array, create_nonempty) {
xpc_object_t objects[OBJECT_COUNT];
create_objects(objects, OBJECT_COUNT);
xpc_object_t array = xpc_array_create(objects, OBJECT_COUNT);
ASSERT_NOT_NULL(array);
ASSERT_EQUAL_U(OBJECT_COUNT, xpc_array_get_count(array));
for (size_t i = 0; i < OBJECT_COUNT; ++i) {
// we have a reference but the array should have one too
NSUInteger retainCount = [objects[i] retainCount];
// global objects have NSUIntegerMax as their reference count
if (retainCount != NSUIntegerMax) {
ASSERT_EQUAL_U(2, retainCount);
}
}
xpc_release(array);
for (size_t i = 0; i < OBJECT_COUNT; ++i) {
// we should have the only reference now
NSUInteger retainCount = [objects[i] retainCount];
if (retainCount != NSUIntegerMax) {
ASSERT_EQUAL_U(1, retainCount);
}
}
release_objects(objects, OBJECT_COUNT);
};
CTEST2(array, get_value) {
size_t index = rand_index(OBJECT_COUNT);
ASSERT_EQUAL_PTR(data->objects[index], xpc_array_get_value(data->array, index));
};
CTEST2(array, set_value) {
xpc_object_t newObject = xpc_int64_create(10);
NSUInteger oldRefCount = 0;
size_t index = rand_index(OBJECT_COUNT);
xpc_array_set_value(data->array, index, newObject);
// setting the new value at that index should have dropped the old value
oldRefCount = [data->objects[index] retainCount];
if (oldRefCount != NSUIntegerMax) {
ASSERT_EQUAL_U(1, [data->objects[index] retainCount]);
}
// and it should have assigned and retained the new value
ASSERT_EQUAL_PTR(newObject, xpc_array_get_value(data->array, index));
ASSERT_EQUAL_U(2, [newObject retainCount]);
xpc_release(newObject);
};
CTEST2(array, append_value) {
xpc_object_t newObject = xpc_int64_create(10);
xpc_object_t newObject2 = xpc_int64_create(15);
xpc_array_append_value(data->array, newObject);
ASSERT_EQUAL_U(OBJECT_COUNT + 1, xpc_array_get_count(data->array));
ASSERT_EQUAL_PTR(newObject, xpc_array_get_value(data->array, OBJECT_COUNT));
xpc_array_set_value(data->array, XPC_ARRAY_APPEND, newObject2);
ASSERT_EQUAL_U(OBJECT_COUNT + 2, xpc_array_get_count(data->array));
ASSERT_EQUAL_PTR(newObject2, xpc_array_get_value(data->array, OBJECT_COUNT + 1));
xpc_release(newObject2);
xpc_release(newObject);
};
static void visitor(size_t index, xpc_object_t value, void* context) {
size_t* visitedCount = context;
++*visitedCount;
};
CTEST2(array, apply) {
__block size_t visitedCount = 0;
xpc_array_apply(data->array, ^bool(size_t index, xpc_object_t value) {
++visitedCount;
return true;
});
ASSERT_EQUAL_U(OBJECT_COUNT, visitedCount);
visitedCount = 0;
xpc_array_apply(data->array, ^bool(size_t index, xpc_object_t value) {
++visitedCount;
if (index == OBJECT_COUNT / 2) {
return false;
}
return true;
});
ASSERT_EQUAL_U((OBJECT_COUNT / 2) + 1, visitedCount);
visitedCount = 0;
xpc_array_apply_f(data->array, &visitedCount, visitor);
ASSERT_EQUAL_U(OBJECT_COUNT, visitedCount);
};
// there are too many getters and setters and i don't want to write tests for all of them
// (and doing it with a macro would be tricky for some of them like fd or data)
//
// if the basic API works, everything should
// (all the getters and setters use the basic API to do their stuff)

67
test/base.m Normal file
View File

@ -0,0 +1,67 @@
#include "ctest-plus.h"
#import <xpc/xpc.h>
// this basic testing is related to the basic C API for objects and the base behavior of objects.
// for this purpose, we use int64 objects, as they're simple enough that they should never pose a problem.
// any issues with testing them should come from the xpc_object base class.
// some random non-zero value to start off the integers with
#define INT64_INITIAL_VALUE 5
#define INT64_INITIAL_VALUE_AS_STRING "5"
#define INT64_CLASS_NAME "OS_xpc_int64"
CTEST(base, create) {
xpc_object_t obj = xpc_int64_create(INT64_INITIAL_VALUE);
ASSERT_NOT_NULL(obj);
xpc_release(obj);
};
CTEST(base, refcount) {
xpc_object_t obj = xpc_int64_create(INT64_INITIAL_VALUE);
ASSERT_EQUAL_U(1, [obj retainCount]);
xpc_retain(obj);
ASSERT_EQUAL_U(2, [obj retainCount]);
xpc_release(obj);
ASSERT_EQUAL_U(1, [obj retainCount]);
xpc_release(obj);
};
// skipped because some XPC objects don't copy themselves
// (e.g. the one we're using, int64, doesn't copy itself)
CTEST_SKIP(base, copy) {
xpc_object_t obj = xpc_int64_create(INT64_INITIAL_VALUE);
xpc_object_t copy = xpc_copy(obj);
ASSERT_NOT_EQUAL_PTR(obj, copy);
xpc_release(copy);
xpc_release(obj);
};
CTEST(base, hash) {
xpc_object_t obj1 = xpc_int64_create(INT64_INITIAL_VALUE);
xpc_object_t obj2 = xpc_int64_create(INT64_INITIAL_VALUE);
xpc_object_t obj3 = xpc_int64_create(INT64_INITIAL_VALUE + 1);
ASSERT_EQUAL_U(xpc_hash(obj1), xpc_hash(obj2));
ASSERT_NOT_EQUAL_U(xpc_hash(obj1), xpc_hash(obj3));
xpc_release(obj3);
xpc_release(obj2);
xpc_release(obj1);
};
CTEST(base, equality) {
xpc_object_t obj1 = xpc_int64_create(INT64_INITIAL_VALUE);
xpc_object_t obj2 = xpc_int64_create(INT64_INITIAL_VALUE);
xpc_object_t obj3 = xpc_int64_create(INT64_INITIAL_VALUE + 1);
ASSERT_TRUE(xpc_equal(obj1, obj2));
ASSERT_FALSE(xpc_equal(obj1, obj3));
xpc_release(obj3);
xpc_release(obj2);
xpc_release(obj1);
};
CTEST(base, description) {
xpc_object_t obj = xpc_int64_create(INT64_INITIAL_VALUE);
char* description = xpc_copy_description(obj);
ASSERT_STR("<" INT64_CLASS_NAME ": " INT64_INITIAL_VALUE_AS_STRING ">", description);
free(description);
xpc_release(obj);
};

34
test/bool.c Normal file
View File

@ -0,0 +1,34 @@
#include "ctest-plus.h"
#include <xpc/private.h>
#undef bool
CTEST(bool, global) {
xpc_object_t trueObj = xpc_bool_create(true);
xpc_object_t falseObj = xpc_bool_create(false);
ASSERT_EQUAL_U(true, xpc_bool_get_value(trueObj));
ASSERT_EQUAL_U(false, xpc_bool_get_value(falseObj));
// they should be globals
ASSERT_EQUAL_PTR(XPC_BOOL_TRUE, trueObj);
ASSERT_EQUAL_PTR(XPC_BOOL_FALSE, falseObj);
xpc_release(falseObj);
xpc_release(trueObj);
};
CTEST(bool, distinct) {
xpc_object_t obj = _xpc_bool_create_distinct(true);
ASSERT_EQUAL_U(true, xpc_bool_get_value(obj));
// it should be a non-global
ASSERT_NOT_EQUAL_PTR(XPC_BOOL_TRUE, obj);
_xpc_bool_set_value(obj, false);
ASSERT_EQUAL_U(false, xpc_bool_get_value(obj));
xpc_release(obj);
};

1
test/ctest Submodule

@ -0,0 +1 @@
Subproject commit cd46041baf3f131fe74032967566c7321adcaa01

11
test/ctest-plus.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef _CTEST_PLUS_H_
#define _CTEST_PLUS_H_
#include <ctest.h>
// convenience header that adds additional macros for ctest
#define ASSERT_EQUAL_PTR(exp, real) ASSERT_EQUAL_U(((uintptr_t)(exp)), ((uintptr_t)(real)))
#define ASSERT_NOT_EQUAL_PTR(exp, real) ASSERT_NOT_EQUAL_U(((uintptr_t)(exp)), ((uintptr_t)(real)))
#endif // _CTEST_PLUS_H_

50
test/data.c Normal file
View File

@ -0,0 +1,50 @@
#include "ctest-plus.h"
#include <xpc/private.h>
#include "test-util.h"
CTEST_DATA(data) {
xpc_object_t data;
};
CTEST_SETUP(data) {
data->data = xpc_data_create(some_data, sizeof(some_data));
};
CTEST_TEARDOWN(data) {
xpc_release(data->data);
};
CTEST(data, create) {
xpc_object_t object = xpc_data_create(some_data, sizeof(some_data));
ASSERT_NOT_NULL(object);
xpc_release(object);
};
CTEST2(data, length) {
ASSERT_EQUAL_U(sizeof(some_data), xpc_data_get_length(data->data));
};
CTEST2(data, bytes_ptr) {
ASSERT_DATA(some_data, sizeof(some_data), xpc_data_get_bytes_ptr(data->data), xpc_data_get_length(data->data));
};
CTEST2(data, bytes) {
uint8_t copy[sizeof(some_data)];
size_t copyCount = xpc_data_get_bytes(data->data, copy, 0, sizeof(copy));
ASSERT_EQUAL_U(sizeof(copy), copyCount);
ASSERT_DATA(some_data, sizeof(some_data), copy, sizeof(copy));
};
CTEST(data, create_with_dispatch_data) {
dispatch_data_t ddata = dispatch_data_create(some_data, sizeof(some_data), NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
xpc_object_t object = xpc_data_create_with_dispatch_data(ddata);
ASSERT_DATA(some_data, sizeof(some_data), xpc_data_get_bytes_ptr(object), xpc_data_get_length(object));
xpc_release(object);
dispatch_release(ddata);
};
CTEST2(data, set_data) {
const uint8_t new_data[] = { 123, 124, 125, 126, 127, 128, 129, 130 };
_xpc_data_set_value(data->data, new_data, sizeof(new_data));
ASSERT_DATA(new_data, sizeof(new_data), xpc_data_get_bytes_ptr(data->data), xpc_data_get_length(data->data));
};

121
test/dictionary.m Normal file
View File

@ -0,0 +1,121 @@
#include "ctest-plus.h"
#include <xpc/private.h>
#include "test-util.h"
CTEST_DATA(dictionary) {
const char* keys[OBJECT_COUNT];
xpc_object_t objects[OBJECT_COUNT];
xpc_object_t dict;
};
CTEST_SETUP(dictionary) {
create_keys(data->keys, OBJECT_COUNT);
create_objects(data->objects, OBJECT_COUNT);
data->dict = xpc_dictionary_create(data->keys, data->objects, OBJECT_COUNT);
};
CTEST_TEARDOWN(dictionary) {
xpc_release(data->dict);
release_objects(data->objects, OBJECT_COUNT);
release_keys(data->keys, OBJECT_COUNT);
};
CTEST(dictionary, create_empty) {
xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
ASSERT_NOT_NULL(dict);
ASSERT_EQUAL_U(0, xpc_dictionary_get_count(dict));
xpc_release(dict);
};
CTEST(dictionary, create_nonempty) {
const char* keys[OBJECT_COUNT];
xpc_object_t objects[OBJECT_COUNT];
create_keys(keys, OBJECT_COUNT);
create_objects(objects, OBJECT_COUNT);
xpc_object_t dict = xpc_dictionary_create(keys, objects, OBJECT_COUNT);
ASSERT_NOT_NULL(dict);
ASSERT_EQUAL_U(OBJECT_COUNT, xpc_dictionary_get_count(dict));
for (size_t i = 0; i < OBJECT_COUNT; ++i) {
// we have a reference but the dictionary should have one too
NSUInteger retainCount = [objects[i] retainCount];
// global objects have NSUIntegerMax as their reference count
if (retainCount != NSUIntegerMax) {
ASSERT_EQUAL_U(2, retainCount);
}
}
xpc_release(dict);
for (size_t i = 0; i < OBJECT_COUNT; ++i) {
// we should have the only reference now
NSUInteger retainCount = [objects[i] retainCount];
if (retainCount != NSUIntegerMax) {
ASSERT_EQUAL_U(1, retainCount);
}
}
release_objects(objects, OBJECT_COUNT);
release_keys(keys, OBJECT_COUNT);
};
CTEST2(dictionary, get_value) {
size_t index = rand_index(OBJECT_COUNT);
ASSERT_EQUAL_PTR(data->objects[index], xpc_dictionary_get_value(data->dict, data->keys[index]));
};
CTEST2(dictionary, set_value) {
xpc_object_t newObject = xpc_int64_create(10);
NSUInteger oldRefCount = 0;
size_t index = rand_index(OBJECT_COUNT);
xpc_dictionary_set_value(data->dict, data->keys[index], newObject);
// setting the new value at that index should have dropped the old value
oldRefCount = [data->objects[index] retainCount];
if (oldRefCount != NSUIntegerMax) {
ASSERT_EQUAL_U(1, [data->objects[index] retainCount]);
}
// and it should have assigned and retained the new value
ASSERT_EQUAL_PTR(newObject, xpc_dictionary_get_value(data->dict, data->keys[index]));
ASSERT_EQUAL_U(2, [newObject retainCount]);
xpc_release(newObject);
};
CTEST2(dictionary, add_value) {
xpc_object_t newObject = xpc_int64_create(10);
xpc_object_t newObject2 = xpc_int64_create(15);
xpc_dictionary_set_value(data->dict, "some random key", newObject);
ASSERT_EQUAL_U(OBJECT_COUNT + 1, xpc_dictionary_get_count(data->dict));
ASSERT_EQUAL_PTR(newObject, xpc_dictionary_get_value(data->dict, "some random key"));
xpc_release(newObject);
};
static void visitor(const char* key, xpc_object_t value, void* context) {
size_t* visitedCount = context;
++*visitedCount;
};
CTEST2(dictionary, apply) {
__block size_t visitedCount = 0;
const char* key = rand_key(data->keys, OBJECT_COUNT);
xpc_dictionary_apply(data->dict, ^bool(const char* key, xpc_object_t value) {
++visitedCount;
return true;
});
ASSERT_EQUAL_U(OBJECT_COUNT, visitedCount);
// i'm not sure how to test the early stop with dictionaries, so for now, we won't
visitedCount = 0;
xpc_dictionary_apply_f(data->dict, &visitedCount, visitor);
ASSERT_EQUAL_U(OBJECT_COUNT, visitedCount);
};
// like arrays, dictionaries have too many getters and setters and i don't want to write tests for all of them
//
// if the basic API works, everything should work
// (all the getters and setters use the basic API to do their stuff)

12
test/main.c Normal file
View File

@ -0,0 +1,12 @@
#define CTEST_MAIN
// uncomment lines below to enable/disable features. See README.md for details
#define CTEST_SEGFAULT
//#define CTEST_NO_COLORS
#define CTEST_COLOR_OK
#include <ctest.h>
int main(int argc, char** argv) {
return ctest_main(argc, argv);
};

69
test/string.c Normal file
View File

@ -0,0 +1,69 @@
#include "ctest-plus.h"
#include <xpc/private.h>
#define INITIAL_STRING_LITERAL "foo"
#define INITIAL_STRING_LENGTH 3
CTEST(string, create) {
xpc_object_t string = xpc_string_create(INITIAL_STRING_LITERAL);
ASSERT_NOT_NULL(string);
xpc_release(string);
};
CTEST(string, get_string_ptr) {
xpc_object_t string = xpc_string_create(INITIAL_STRING_LITERAL);
ASSERT_NOT_NULL(xpc_string_get_string_ptr(string));
xpc_release(string);
};
CTEST(string, get_length) {
xpc_object_t string = xpc_string_create(INITIAL_STRING_LITERAL);
ASSERT_EQUAL_U(INITIAL_STRING_LENGTH, xpc_string_get_length(string));
xpc_release(string);
};
CTEST(string, create_with_format) {
xpc_object_t string = xpc_string_create_with_format("foo %d bar", 5);
ASSERT_NOT_NULL(string);
// make sure it formatted it correctly
ASSERT_STR("foo 5 bar", xpc_string_get_string_ptr(string));
xpc_release(string);
};
CTEST(string, create_no_copy) {
xpc_object_t string = xpc_string_create_no_copy(INITIAL_STRING_LITERAL);
ASSERT_NOT_NULL(string);
// make sure it actually didn't copy
ASSERT_EQUAL_PTR(INITIAL_STRING_LITERAL, xpc_string_get_string_ptr(string));
xpc_release(string);
};
CTEST(string, hash) {
xpc_object_t string1 = xpc_string_create(INITIAL_STRING_LITERAL);
xpc_object_t string2 = xpc_string_create(INITIAL_STRING_LITERAL);
xpc_object_t string3 = xpc_string_create(INITIAL_STRING_LITERAL " bar");
ASSERT_EQUAL_U(xpc_hash(string1), xpc_hash(string2));
ASSERT_NOT_EQUAL_U(xpc_hash(string1), xpc_hash(string3));
xpc_release(string3);
xpc_release(string2);
xpc_release(string1);
};
CTEST(string, equality) {
xpc_object_t string1 = xpc_string_create(INITIAL_STRING_LITERAL);
xpc_object_t string2 = xpc_string_create(INITIAL_STRING_LITERAL);
xpc_object_t string3 = xpc_string_create(INITIAL_STRING_LITERAL " bar");
ASSERT_TRUE(xpc_equal(string1, string2));
ASSERT_FALSE(xpc_equal(string1, string3));
xpc_release(string3);
xpc_release(string2);
xpc_release(string1);
};
CTEST(string, set_value) {
xpc_object_t string = xpc_string_create(INITIAL_STRING_LITERAL);
ASSERT_STR(INITIAL_STRING_LITERAL, xpc_string_get_string_ptr(string));
_xpc_string_set_value(string, "new string");
ASSERT_STR("new string", xpc_string_get_string_ptr(string));
xpc_release(string);
};

49
test/test-util.h Normal file
View File

@ -0,0 +1,49 @@
#ifndef _XPC_TEST_TEST_UTIL_H_
#define _XPC_TEST_TEST_UTIL_H_
#include <xpc/private.h>
#define OBJECT_COUNT 5
static size_t rand_index(size_t count) {
// not uniform but honestly who cares
return rand() % count;
};
static const char* rand_key(const char** keys, size_t count) {
return keys[rand_index(count)];
};
static const uint8_t some_data[] = {0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10};
static void create_objects(xpc_object_t* objects, size_t count) {
if (count >= OBJECT_COUNT) {
objects[0] = xpc_int64_create(5);
objects[1] = xpc_string_create("foo");
objects[2] = xpc_null_create();
objects[3] = xpc_bool_create(true);
objects[4] = xpc_data_create(some_data, sizeof(some_data));
}
};
static void create_keys(const char** keys, size_t count) {
if (count >= OBJECT_COUNT) {
keys[0] = "first";
keys[1] = "second";
keys[2] = "third";
keys[3] = "fourth";
keys[4] = "fifth";
}
};
static void release_objects(xpc_object_t* objects, size_t count) {
for (size_t i = 0; i < count; ++i) {
xpc_release(objects[i]);
}
};
static void release_keys(const char** keys, size_t count) {
// no-op
};
#endif // _XPC_TEST_TEST_UTIL_H_

14
test/type.c Normal file
View File

@ -0,0 +1,14 @@
#include "ctest-plus.h"
#include <xpc/private.h>
CTEST(type, get_type) {
xpc_object_t obj = xpc_int64_create(5);
ASSERT_EQUAL_PTR(XPC_TYPE_INT64, xpc_get_type(obj));
xpc_release(obj);
};
CTEST(type, get_type_name) {
xpc_object_t obj = xpc_int64_create(5);
ASSERT_STR("OS_xpc_int64", xpc_type_get_name(xpc_get_type(obj)));
xpc_release(obj);
};