diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f6eb1a3 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "test/ctest"] + path = test/ctest + url = https://github.com/bvdberg/ctest.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 9eb221a..ab6d61a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 + $ 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 + $ + ) + + add_subdirectory(test) +endif (ENABLE_TESTS) diff --git a/include/os/transaction_private.h b/include/os/transaction_private.h index a084852..a5de6d0 100644 --- a/include/os/transaction_private.h +++ b/include/os/transaction_private.h @@ -4,7 +4,25 @@ #include #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 diff --git a/include/xpc/launchd.h b/include/xpc/launchd.h index 67cb198..cb800e4 100644 --- a/include/xpc/launchd.h +++ b/include/xpc/launchd.h @@ -2,6 +2,7 @@ #define XPC_LAUNCHD_H_ #include #include +#include #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); diff --git a/include/xpc/private.h b/include/xpc/private.h index dda1a40..8ba1df3 100644 --- a/include/xpc/private.h +++ b/include/xpc/private.h @@ -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 diff --git a/include/xpc/xpc.h b/include/xpc/xpc.h index 12e184b..ebd7186 100644 --- a/include/xpc/xpc.h +++ b/include/xpc/xpc.h @@ -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 diff --git a/internal-include/xpc/internal.h b/internal-include/xpc/internal.h index 5a47935..1d59863 100644 --- a/internal-include/xpc/internal.h +++ b/internal-include/xpc/internal.h @@ -30,6 +30,7 @@ #include #include +#include #include #ifdef XPC_DEBUG diff --git a/internal-include/xpc/objects/activity.h b/internal-include/xpc/objects/activity.h new file mode 100644 index 0000000..6b10823 --- /dev/null +++ b/internal-include/xpc/objects/activity.h @@ -0,0 +1,16 @@ +#ifndef _XPC_OBJECTS_ACTIVITY_H_ +#define _XPC_OBJECTS_ACTIVITY_H_ + +#import + +XPC_CLASS_DECL(activity); + +struct xpc_activity_s { + struct xpc_object_s base; +}; + +@interface XPC_CLASS_INTERFACE(activity) + +@end + +#endif // _XPC_OBJECTS_ACTIVITY_H_ diff --git a/internal-include/xpc/objects/array.h b/internal-include/xpc/objects/array.h new file mode 100644 index 0000000..14d86bf --- /dev/null +++ b/internal-include/xpc/objects/array.h @@ -0,0 +1,33 @@ +#ifndef _XPC_OBJECTS_ARRAY_H_ +#define _XPC_OBJECTS_ARRAY_H_ + +#import +#import + +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_ diff --git a/internal-include/xpc/objects/base.h b/internal-include/xpc/objects/base.h new file mode 100644 index 0000000..0303c88 --- /dev/null +++ b/internal-include/xpc/objects/base.h @@ -0,0 +1,120 @@ +#ifndef _XPC_OBJECTS_BASE_H_ +#define _XPC_OBJECTS_BASE_H_ + +#import +#import + +#ifndef __XPC_INDIRECT__ +OS_OBJECT_DECL(xpc_object); +#endif + +#define __XPC_INDIRECT__ +#import + +#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) + +#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) + ++ (NSUInteger)_instanceSize; + +// note that this method returns a string that must be freed +- (char*)xpcDescription; + +@end + +#endif // _XPC_OBJECTS_BASE_H_ diff --git a/internal-include/xpc/objects/bool.h b/internal-include/xpc/objects/bool.h new file mode 100644 index 0000000..58c58ad --- /dev/null +++ b/internal-include/xpc/objects/bool.h @@ -0,0 +1,26 @@ +#ifndef _XPC_OBJECTS_BOOL_H_ +#define _XPC_OBJECTS_BOOL_H_ + +#import + +// 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_ diff --git a/internal-include/xpc/objects/bundle.h b/internal-include/xpc/objects/bundle.h new file mode 100644 index 0000000..4e4e0b3 --- /dev/null +++ b/internal-include/xpc/objects/bundle.h @@ -0,0 +1,16 @@ +#ifndef _XPC_OBJECTS_BUNDLE_H_ +#define _XPC_OBJECTS_BUNDLE_H_ + +#import + +XPC_CLASS_DECL(bundle); + +struct xpc_bundle_s { + struct xpc_object_s base; +}; + +@interface XPC_CLASS_INTERFACE(bundle) + +@end + +#endif // _XPC_OBJECTS_BUNDLE_H_ diff --git a/internal-include/xpc/objects/connection.h b/internal-include/xpc/objects/connection.h new file mode 100644 index 0000000..96ed5d6 --- /dev/null +++ b/internal-include/xpc/objects/connection.h @@ -0,0 +1,16 @@ +#ifndef _XPC_OBJECTS_CONNECTION_H_ +#define _XPC_OBJECTS_CONNECTION_H_ + +#import + +XPC_CLASS_DECL(connection); + +struct xpc_connection_s { + struct xpc_object_s base; +}; + +@interface XPC_CLASS_INTERFACE(connection) + +@end + +#endif // _XPC_OBJECTS_CONNECTION_H_ diff --git a/internal-include/xpc/objects/data.h b/internal-include/xpc/objects/data.h new file mode 100644 index 0000000..aa0bd95 --- /dev/null +++ b/internal-include/xpc/objects/data.h @@ -0,0 +1,33 @@ +#ifndef _XPC_OBJECTS_DATA_H_ +#define _XPC_OBJECTS_DATA_H_ + +#import +#import + +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_ diff --git a/internal-include/xpc/objects/date.h b/internal-include/xpc/objects/date.h new file mode 100644 index 0000000..f5c865b --- /dev/null +++ b/internal-include/xpc/objects/date.h @@ -0,0 +1,8 @@ +#ifndef _XPC_OBJECTS_DATE_H_ +#define _XPC_OBJECTS_DATE_H_ + +#import + +XPC_WRAPPER_CLASS_DECL(date, int64_t); + +#endif // _XPC_OBJECTS_DATE_H_ diff --git a/internal-include/xpc/objects/dictionary.h b/internal-include/xpc/objects/dictionary.h new file mode 100644 index 0000000..d15d940 --- /dev/null +++ b/internal-include/xpc/objects/dictionary.h @@ -0,0 +1,54 @@ +#ifndef _XPC_OBJECTS_DICTIONARY_H_ +#define _XPC_OBJECTS_DICTIONARY_H_ + +#import +#import + +#include + +@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_ diff --git a/internal-include/xpc/objects/double.h b/internal-include/xpc/objects/double.h new file mode 100644 index 0000000..229c3fe --- /dev/null +++ b/internal-include/xpc/objects/double.h @@ -0,0 +1,8 @@ +#ifndef _XPC_OBJECTS_DOUBLE_H_ +#define _XPC_OBJECTS_DOUBLE_H_ + +#import + +XPC_WRAPPER_CLASS_DECL(double, double); + +#endif // _XPC_OBJECTS_DOUBLE_H_ diff --git a/internal-include/xpc/objects/endpoint.h b/internal-include/xpc/objects/endpoint.h new file mode 100644 index 0000000..a7b3714 --- /dev/null +++ b/internal-include/xpc/objects/endpoint.h @@ -0,0 +1,19 @@ +#ifndef _XPC_OBJECTS_ENDPOINT_H_ +#define _XPC_OBJECTS_ENDPOINT_H_ + +#import +#import + +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_ diff --git a/internal-include/xpc/objects/error.h b/internal-include/xpc/objects/error.h new file mode 100644 index 0000000..f7df719 --- /dev/null +++ b/internal-include/xpc/objects/error.h @@ -0,0 +1,18 @@ +#ifndef _XPC_OBJECTS_ERROR_H_ +#define _XPC_OBJECTS_ERROR_H_ + +#import + +// 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) + +@end + +#endif // _XPC_OBJECTS_ERROR_H_ diff --git a/internal-include/xpc/objects/fd.h b/internal-include/xpc/objects/fd.h new file mode 100644 index 0000000..7649dd2 --- /dev/null +++ b/internal-include/xpc/objects/fd.h @@ -0,0 +1,26 @@ +#ifndef _XPC_OBJECTS_FD_H_ +#define _XPC_OBJECTS_FD_H_ + +#import + +#include + +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_ diff --git a/internal-include/xpc/objects/file_transfer.h b/internal-include/xpc/objects/file_transfer.h new file mode 100644 index 0000000..8e09542 --- /dev/null +++ b/internal-include/xpc/objects/file_transfer.h @@ -0,0 +1,16 @@ +#ifndef _XPC_OBJECTS_FILE_TRANSFER_H_ +#define _XPC_OBJECTS_FILE_TRANSFER_H_ + +#import + +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_ diff --git a/internal-include/xpc/objects/int64.h b/internal-include/xpc/objects/int64.h new file mode 100644 index 0000000..46e5c0f --- /dev/null +++ b/internal-include/xpc/objects/int64.h @@ -0,0 +1,8 @@ +#ifndef _XPC_OBJECTS_INT64_H_ +#define _XPC_OBJECTS_INT64_H_ + +#import + +XPC_WRAPPER_CLASS_DECL(int64, int64_t); + +#endif // _XPC_OBJECTS_INT64_H_ diff --git a/internal-include/xpc/objects/mach_recv.h b/internal-include/xpc/objects/mach_recv.h new file mode 100644 index 0000000..dfdba01 --- /dev/null +++ b/internal-include/xpc/objects/mach_recv.h @@ -0,0 +1,24 @@ +#ifndef _XPC_OBJECTS_MACH_RECV_H_ +#define _XPC_OBJECTS_MACH_RECV_H_ + +#import +#import + +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_ diff --git a/internal-include/xpc/objects/mach_send.h b/internal-include/xpc/objects/mach_send.h new file mode 100644 index 0000000..99d4130 --- /dev/null +++ b/internal-include/xpc/objects/mach_send.h @@ -0,0 +1,27 @@ +#ifndef _XPC_OBJECTS_MACH_SEND_H_ +#define _XPC_OBJECTS_MACH_SEND_H_ + +#import +#import + +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_ diff --git a/internal-include/xpc/objects/null.h b/internal-include/xpc/objects/null.h new file mode 100644 index 0000000..3874a34 --- /dev/null +++ b/internal-include/xpc/objects/null.h @@ -0,0 +1,20 @@ +#ifndef _XPC_OBJECTS_NULL_H_ +#define _XPC_OBJECTS_NULL_H_ + +#import + +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_ diff --git a/internal-include/xpc/objects/pipe.h b/internal-include/xpc/objects/pipe.h new file mode 100644 index 0000000..1593f57 --- /dev/null +++ b/internal-include/xpc/objects/pipe.h @@ -0,0 +1,16 @@ +#ifndef _XPC_OBJECTS_PIPE_H_ +#define _XPC_OBJECTS_PIPE_H_ + +#import + +XPC_CLASS_DECL(pipe); + +struct xpc_pipe_s { + struct xpc_object_s base; +}; + +@interface XPC_CLASS_INTERFACE(pipe) + +@end + +#endif // _XPC_OBJECTS_PIPE_H_ diff --git a/internal-include/xpc/objects/pointer.h b/internal-include/xpc/objects/pointer.h new file mode 100644 index 0000000..3d5117a --- /dev/null +++ b/internal-include/xpc/objects/pointer.h @@ -0,0 +1,8 @@ +#ifndef _XPC_OBJECTS_POINTER_H_ +#define _XPC_OBJECTS_POINTER_H_ + +#import + +XPC_WRAPPER_CLASS_DECL(pointer, void*); + +#endif // _XPC_OBJECTS_POINTER_H_ diff --git a/internal-include/xpc/objects/serializer.h b/internal-include/xpc/objects/serializer.h new file mode 100644 index 0000000..8b58658 --- /dev/null +++ b/internal-include/xpc/objects/serializer.h @@ -0,0 +1,16 @@ +#ifndef _XPC_OBJECTS_SERIALIZER_H_ +#define _XPC_OBJECTS_SERIALIZER_H_ + +#import + +XPC_CLASS_DECL(serializer); + +struct xpc_serializer_s { + struct xpc_object_s base; +}; + +@interface XPC_CLASS_INTERFACE(serializer) + +@end + +#endif // _XPC_OBJECTS_SERIALIZER_H_ diff --git a/internal-include/xpc/objects/service.h b/internal-include/xpc/objects/service.h new file mode 100644 index 0000000..4569207 --- /dev/null +++ b/internal-include/xpc/objects/service.h @@ -0,0 +1,16 @@ +#ifndef _XPC_OBJECTS_SERVICE_H_ +#define _XPC_OBJECTS_SERVICE_H_ + +#import + +XPC_CLASS_DECL(service); + +struct xpc_service_s { + struct xpc_object_s base; +}; + +@interface XPC_CLASS_INTERFACE(service) + +@end + +#endif // _XPC_OBJECTS_SERVICE_H_ diff --git a/internal-include/xpc/objects/service_instance.h b/internal-include/xpc/objects/service_instance.h new file mode 100644 index 0000000..4f7e5f0 --- /dev/null +++ b/internal-include/xpc/objects/service_instance.h @@ -0,0 +1,16 @@ +#ifndef _XPC_OBJECTS_SERVICE_INSTANCE_H_ +#define _XPC_OBJECTS_SERVICE_INSTANCE_H_ + +#import + +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_ diff --git a/internal-include/xpc/objects/shmem.h b/internal-include/xpc/objects/shmem.h new file mode 100644 index 0000000..55f7cdb --- /dev/null +++ b/internal-include/xpc/objects/shmem.h @@ -0,0 +1,16 @@ +#ifndef _XPC_OBJECTS_SHMEM_H_ +#define _XPC_OBJECTS_SHMEM_H_ + +#import + +XPC_CLASS_DECL(shmem); + +struct xpc_shmem_s { + struct xpc_object_s base; +}; + +@interface XPC_CLASS_INTERFACE(shmem) + +@end + +#endif // _XPC_OBJECTS_SHMEM_H_ diff --git a/internal-include/xpc/objects/string.h b/internal-include/xpc/objects/string.h new file mode 100644 index 0000000..23038fd --- /dev/null +++ b/internal-include/xpc/objects/string.h @@ -0,0 +1,35 @@ +#ifndef _XPC_OBJECTS_STRING_H_ +#define _XPC_OBJECTS_STRING_H_ + +#import +#include + +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_ diff --git a/internal-include/xpc/objects/uint64.h b/internal-include/xpc/objects/uint64.h new file mode 100644 index 0000000..7dc0d6f --- /dev/null +++ b/internal-include/xpc/objects/uint64.h @@ -0,0 +1,8 @@ +#ifndef _XPC_OBJECTS_UINT64_H_ +#define _XPC_OBJECTS_UINT64_H_ + +#import + +XPC_WRAPPER_CLASS_DECL(uint64, uint64_t); + +#endif // _XPC_OBJECTS_UINT64_H_ diff --git a/internal-include/xpc/objects/uuid.h b/internal-include/xpc/objects/uuid.h new file mode 100644 index 0000000..23c2c0e --- /dev/null +++ b/internal-include/xpc/objects/uuid.h @@ -0,0 +1,22 @@ +#ifndef _XPC_OBJECTS_UUID_H_ +#define _XPC_OBJECTS_UUID_H_ + +#import +#include + +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_ diff --git a/internal-include/xpc/prefix.h b/internal-include/xpc/prefix.h new file mode 100644 index 0000000..82dd3cb --- /dev/null +++ b/internal-include/xpc/prefix.h @@ -0,0 +1,10 @@ +#import + +#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) diff --git a/internal-include/xpc/util.h b/internal-include/xpc/util.h new file mode 100644 index 0000000..1207c8c --- /dev/null +++ b/internal-include/xpc/util.h @@ -0,0 +1,90 @@ +#ifndef _XPC_UTIL_H_ +#define _XPC_UTIL_H_ + +#import +#import + +#include +#include +#include + +/** + * 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_ diff --git a/scripts/exported-symbols.exp b/scripts/exported-symbols.exp deleted file mode 100644 index 23a308f..0000000 --- a/scripts/exported-symbols.exp +++ /dev/null @@ -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 diff --git a/scripts/reexported-symbols.exp b/scripts/reexported-symbols.exp new file mode 100644 index 0000000..153eba6 --- /dev/null +++ b/scripts/reexported-symbols.exp @@ -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 diff --git a/src/activity.c b/src/activity.c deleted file mode 100644 index e891622..0000000 --- a/src/activity.c +++ /dev/null @@ -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 . -*/ - -#include - -#include -#include - - -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("") */ - .xo_refcnt = 1, - .xo_u = { - .str = "" - } -}; - -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) -{ -} diff --git a/src/activity.m b/src/activity.m new file mode 100644 index 0000000..b875097 --- /dev/null +++ b/src/activity.m @@ -0,0 +1,228 @@ +#import +#import +#import +#import +#import + +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 = "", + .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 +}; diff --git a/src/array.c b/src/array.c deleted file mode 100644 index 6955d49..0000000 --- a/src/array.c +++ /dev/null @@ -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 -#include -#include -#include - -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); -}; diff --git a/src/array.m b/src/array.m new file mode 100644 index 0000000..a0aad47 --- /dev/null +++ b/src/array.m @@ -0,0 +1,390 @@ +#import +#import +#import +#import +#import +#import + +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; +}; diff --git a/src/base.m b/src/base.m new file mode 100644 index 0000000..a0383b4 --- /dev/null +++ b/src/base.m @@ -0,0 +1,138 @@ +#import +#import +#import +#import +#import + +// 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; +}; diff --git a/src/bool.m b/src/bool.m new file mode 100644 index 0000000..f1b68c7 --- /dev/null +++ b/src/bool.m @@ -0,0 +1,127 @@ +#import +#import +#import + +#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; + } +}; diff --git a/src/bundle.m b/src/bundle.m new file mode 100644 index 0000000..d7b75e2 --- /dev/null +++ b/src/bundle.m @@ -0,0 +1,113 @@ +#import +#import +#import + +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) { + +}; diff --git a/src/coalition.m b/src/coalition.m new file mode 100644 index 0000000..f9e3e37 --- /dev/null +++ b/src/coalition.m @@ -0,0 +1,20 @@ +#import + +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; +}; diff --git a/src/connection.c b/src/connection.c deleted file mode 100644 index fec87a2..0000000 --- a/src/connection.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include - -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); -}; diff --git a/src/connection.m b/src/connection.m new file mode 100644 index 0000000..f963d18 --- /dev/null +++ b/src/connection.m @@ -0,0 +1,244 @@ +#import +#import +#import +#import + +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) { + +}; diff --git a/src/data.m b/src/data.m new file mode 100644 index 0000000..9351066 --- /dev/null +++ b/src/data.m @@ -0,0 +1,141 @@ +#import +#import +#define __DISPATCH_INDIRECT__ +#import +#import + +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; +}; diff --git a/src/date.m b/src/date.m new file mode 100644 index 0000000..1c2ce50 --- /dev/null +++ b/src/date.m @@ -0,0 +1,55 @@ +#import +#import +#import +#import + +#include + +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; +}; diff --git a/src/dictionary.c b/src/dictionary.c deleted file mode 100644 index b20dc94..0000000 --- a/src/dictionary.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#include - -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); -}; diff --git a/src/dictionary.m b/src/dictionary.m new file mode 100644 index 0000000..45aa7ae --- /dev/null +++ b/src/dictionary.m @@ -0,0 +1,533 @@ +#import +#import +#import +#import +#import +#import +#import +#import + +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); +}; diff --git a/src/double.m b/src/double.m new file mode 100644 index 0000000..aa9d3b3 --- /dev/null +++ b/src/double.m @@ -0,0 +1,35 @@ +#import +#import +#import + +#include + +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; + } +}; diff --git a/src/endpoint.m b/src/endpoint.m new file mode 100644 index 0000000..9e919d4 --- /dev/null +++ b/src/endpoint.m @@ -0,0 +1,54 @@ +#import +#import +#import + +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; +}; diff --git a/src/error.c b/src/error.c deleted file mode 100644 index 8cd9720..0000000 --- a/src/error.c +++ /dev/null @@ -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 . -*/ - -#include -#include - -/* 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 , 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 -* See 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 - } - } - } -}; diff --git a/src/error.m b/src/error.m new file mode 100644 index 0000000..3dd67d0 --- /dev/null +++ b/src/error.m @@ -0,0 +1,73 @@ +#import +#import +#import +#import + +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; +}; diff --git a/src/event.m b/src/event.m new file mode 100644 index 0000000..b40a5ac --- /dev/null +++ b/src/event.m @@ -0,0 +1,121 @@ +#import + +// 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) { + +}; diff --git a/src/fd.m b/src/fd.m new file mode 100644 index 0000000..44ff747 --- /dev/null +++ b/src/fd.m @@ -0,0 +1,109 @@ +#import +#import +#import + +#include +#include + +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; +}; diff --git a/src/file_transfer.m b/src/file_transfer.m new file mode 100644 index 0000000..55ec4e6 --- /dev/null +++ b/src/file_transfer.m @@ -0,0 +1,75 @@ +#import +#import + +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; +}; diff --git a/src/init.c b/src/init.c index 252cde1..b8a7f1b 100644 --- a/src/init.c +++ b/src/init.c @@ -1,20 +1,25 @@ +#import 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) { } diff --git a/src/int64.m b/src/int64.m new file mode 100644 index 0000000..2892bac --- /dev/null +++ b/src/int64.m @@ -0,0 +1,33 @@ +#import +#import +#import + +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; + } +}; diff --git a/src/launchd.m b/src/launchd.m new file mode 100644 index 0000000..af42940 --- /dev/null +++ b/src/launchd.m @@ -0,0 +1,173 @@ +#import +#import + +// +// 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; +}; diff --git a/src/mach_recv.m b/src/mach_recv.m new file mode 100644 index 0000000..c66a96f --- /dev/null +++ b/src/mach_recv.m @@ -0,0 +1,25 @@ +#import +#import + +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; +}; diff --git a/src/mach_send.m b/src/mach_send.m new file mode 100644 index 0000000..3658c6c --- /dev/null +++ b/src/mach_send.m @@ -0,0 +1,45 @@ +#import +#import + +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; +}; diff --git a/src/misc.c b/src/misc.c deleted file mode 100644 index fd9d027..0000000 --- a/src/misc.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define RECV_BUFFER_SIZE 65536 - -#include -#include - -#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, "\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, "\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; -}; diff --git a/src/misc.m b/src/misc.m new file mode 100644 index 0000000..5b7b932 --- /dev/null +++ b/src/misc.m @@ -0,0 +1,252 @@ +#import +#import +#define __DISPATCH_INDIRECT__ +#import +#import + +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": + 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; +}; diff --git a/src/null.m b/src/null.m new file mode 100644 index 0000000..2cc8cf1 --- /dev/null +++ b/src/null.m @@ -0,0 +1,44 @@ +#import +#import +#import + +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]; +}; diff --git a/src/pipe.c b/src/pipe.c deleted file mode 100644 index 420f431..0000000 --- a/src/pipe.c +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include - - -void xpc_pipe_invalidate(xpc_pipe_t pipe) -{ - -} - -xpc_pipe_t xpc_pipe_create(const char* name, int flags) -{ - return NULL; -} diff --git a/src/pipe.m b/src/pipe.m new file mode 100644 index 0000000..00f9408 --- /dev/null +++ b/src/pipe.m @@ -0,0 +1,86 @@ +#import +#import +#import +#define __DISPATCH_INDIRECT__ +#import + + +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; +}; diff --git a/src/pointer.m b/src/pointer.m new file mode 100644 index 0000000..ca4740b --- /dev/null +++ b/src/pointer.m @@ -0,0 +1,22 @@ +#import +#import +#import + +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; +}; diff --git a/src/private.c b/src/private.c deleted file mode 100644 index 4e7e498..0000000 --- a/src/private.c +++ /dev/null @@ -1,192 +0,0 @@ -#include -#include -#include - -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); -}; diff --git a/src/reboot3.c b/src/reboot3.c index 42ea089..d7763e8 100644 --- a/src/reboot3.c +++ b/src/reboot3.c @@ -1,8 +1,10 @@ -#include +#import +#import -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); } diff --git a/src/runtime.m b/src/runtime.m new file mode 100644 index 0000000..4a3cf15 --- /dev/null +++ b/src/runtime.m @@ -0,0 +1,34 @@ +#import + +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 +}; diff --git a/src/serializer.m b/src/serializer.m new file mode 100644 index 0000000..2875f75 --- /dev/null +++ b/src/serializer.m @@ -0,0 +1,10 @@ +#import + +XPC_CLASS_SYMBOL_DECL(serializer); + +OS_OBJECT_NONLAZY_CLASS +@implementation XPC_CLASS(serializer) + +XPC_CLASS_HEADER(serializer); + +@end diff --git a/src/service.m b/src/service.m new file mode 100644 index 0000000..a31e741 --- /dev/null +++ b/src/service.m @@ -0,0 +1,62 @@ +#import +#import +#import +#import + +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` +}; diff --git a/src/service_instance.m b/src/service_instance.m new file mode 100644 index 0000000..0f29c86 --- /dev/null +++ b/src/service_instance.m @@ -0,0 +1,98 @@ +#import +#import +#import + +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 +}; diff --git a/src/shmem.m b/src/shmem.m new file mode 100644 index 0000000..095a46f --- /dev/null +++ b/src/shmem.m @@ -0,0 +1,45 @@ +#import +#import +#import + +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; +}; diff --git a/src/string.m b/src/string.m new file mode 100644 index 0000000..c99e07a --- /dev/null +++ b/src/string.m @@ -0,0 +1,180 @@ +#import +#import +#import + +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]; +}; diff --git a/src/subr_sbuf.c b/src/subr_sbuf.c deleted file mode 100644 index 1a3ab5f..0000000 --- a/src/subr_sbuf.c +++ /dev/null @@ -1,590 +0,0 @@ -/*- - * Copyright (c) 2000 Poul-Henning Kamp and Dag-Erling Co•dan 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 - -#include - -#ifdef KERNEL -/* #include */ -#include -#include -#include -#include -#include -#include -#include -#else /* KERNEL */ -#include -#include -#include -#include -#include -#endif /* KERNEL */ - -#include - -#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)); -} diff --git a/src/todo.c b/src/todo.c deleted file mode 100644 index da9c4c8..0000000 --- a/src/todo.c +++ /dev/null @@ -1,29 +0,0 @@ -#include - -#include -#include - -#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(); -} -*/ diff --git a/src/transaction.m b/src/transaction.m index 2e26923..f18b8f6 100644 --- a/src/transaction.m +++ b/src/transaction.m @@ -1,31 +1,74 @@ #include #include - -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 +#import 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) { + +}; diff --git a/src/type.c b/src/type.c deleted file mode 100644 index b89ce08..0000000 --- a/src/type.c +++ /dev/null @@ -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 -#include -#include -#include -#include - -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; -}; diff --git a/src/type.m b/src/type.m new file mode 100644 index 0000000..d5a5b22 --- /dev/null +++ b/src/type.m @@ -0,0 +1,42 @@ +#import +#import +#import + +// +// 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) { + +}; diff --git a/src/uint64.m b/src/uint64.m new file mode 100644 index 0000000..32c9b81 --- /dev/null +++ b/src/uint64.m @@ -0,0 +1,22 @@ +#import +#import +#import + +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; +}; diff --git a/src/util.m b/src/util.m new file mode 100644 index 0000000..e31879d --- /dev/null +++ b/src/util.m @@ -0,0 +1,69 @@ +#import +#import + +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); +}; diff --git a/src/uuid.m b/src/uuid.m new file mode 100644 index 0000000..d69b428 --- /dev/null +++ b/src/uuid.m @@ -0,0 +1,61 @@ +#import +#import +#import + +#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; +}; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..fd80a5c --- /dev/null +++ b/test/CMakeLists.txt @@ -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) diff --git a/test/array.m b/test/array.m new file mode 100644 index 0000000..80ebb4c --- /dev/null +++ b/test/array.m @@ -0,0 +1,129 @@ +#include "ctest-plus.h" +#include +#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) diff --git a/test/base.m b/test/base.m new file mode 100644 index 0000000..bdf8ff6 --- /dev/null +++ b/test/base.m @@ -0,0 +1,67 @@ +#include "ctest-plus.h" +#import + +// 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); +}; diff --git a/test/bool.c b/test/bool.c new file mode 100644 index 0000000..c3fa33f --- /dev/null +++ b/test/bool.c @@ -0,0 +1,34 @@ +#include "ctest-plus.h" +#include + +#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); +}; diff --git a/test/ctest b/test/ctest new file mode 160000 index 0000000..cd46041 --- /dev/null +++ b/test/ctest @@ -0,0 +1 @@ +Subproject commit cd46041baf3f131fe74032967566c7321adcaa01 diff --git a/test/ctest-plus.h b/test/ctest-plus.h new file mode 100644 index 0000000..e1900b5 --- /dev/null +++ b/test/ctest-plus.h @@ -0,0 +1,11 @@ +#ifndef _CTEST_PLUS_H_ +#define _CTEST_PLUS_H_ + +#include + +// 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_ diff --git a/test/data.c b/test/data.c new file mode 100644 index 0000000..abde5d9 --- /dev/null +++ b/test/data.c @@ -0,0 +1,50 @@ +#include "ctest-plus.h" +#include +#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)); +}; diff --git a/test/dictionary.m b/test/dictionary.m new file mode 100644 index 0000000..5d1db48 --- /dev/null +++ b/test/dictionary.m @@ -0,0 +1,121 @@ +#include "ctest-plus.h" +#include +#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) diff --git a/test/main.c b/test/main.c new file mode 100644 index 0000000..a591a23 --- /dev/null +++ b/test/main.c @@ -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 + +int main(int argc, char** argv) { + return ctest_main(argc, argv); +}; diff --git a/test/string.c b/test/string.c new file mode 100644 index 0000000..f67d29f --- /dev/null +++ b/test/string.c @@ -0,0 +1,69 @@ +#include "ctest-plus.h" +#include + +#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); +}; diff --git a/test/test-util.h b/test/test-util.h new file mode 100644 index 0000000..9028507 --- /dev/null +++ b/test/test-util.h @@ -0,0 +1,49 @@ +#ifndef _XPC_TEST_TEST_UTIL_H_ +#define _XPC_TEST_TEST_UTIL_H_ + +#include + +#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_ diff --git a/test/type.c b/test/type.c new file mode 100644 index 0000000..e924fd5 --- /dev/null +++ b/test/type.c @@ -0,0 +1,14 @@ +#include "ctest-plus.h" +#include + +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); +};