mirror of
https://github.com/darlinghq/darling-libxpc.git
synced 2024-11-23 11:49:42 +00:00
Initial rewrite commit
This commit is the first in the rewrite of libxpc. It implements the basic API (that is, the XPC object system and the most basic objects and collections). It also adds some tests for the basic API. The current test suite uses ctest as the testing framework, but for future tests (such as for connections), it may be necessary to find something a little more robust (or roll our own). Future commits will implement the rewritten connection system based on top of libdispatch's private Mach API, which is made specifically for this purpose (it even has certain methods marked as `4libxpc`).
This commit is contained in:
parent
3382d940fd
commit
675120a197
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "test/ctest"]
|
||||||
|
path = test/ctest
|
||||||
|
url = https://github.com/bvdberg/ctest.git
|
110
CMakeLists.txt
110
CMakeLists.txt
@ -1,14 +1,9 @@
|
|||||||
project(xpc)
|
project(xpc)
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 2.4.0)
|
cmake_minimum_required(VERSION 3.12)
|
||||||
enable_language(C ASM)
|
|
||||||
|
|
||||||
if(COMMAND cmake_policy)
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse -msse2 -msse3 -nostdinc -fblocks -fvisibility=hidden")
|
||||||
cmake_policy(SET CMP0003 NEW)
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__DARWIN_UNIX03 -fPIC")
|
||||||
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_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib")
|
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib")
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
@ -16,31 +11,69 @@ include_directories(
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/internal-include
|
${CMAKE_CURRENT_SOURCE_DIR}/internal-include
|
||||||
)
|
)
|
||||||
|
|
||||||
add_definitions(-DMACH)
|
add_compile_definitions(
|
||||||
|
__XPC_BUILDING_XPC__
|
||||||
set(xpc_sources
|
__XPC_PROJECT_BUILD__
|
||||||
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_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")
|
set(DYLIB_INSTALL_NAME "/usr/lib/system/libxpc.dylib")
|
||||||
add_circular(xpc FAT
|
add_circular(xpc
|
||||||
SOURCES
|
FAT
|
||||||
${xpc_sources}
|
OBJECTS
|
||||||
|
$<TARGET_OBJECTS:xpc_obj>
|
||||||
SIBLINGS
|
SIBLINGS
|
||||||
system_c
|
system_c
|
||||||
system_kernel
|
system_kernel
|
||||||
@ -51,6 +84,8 @@ add_circular(xpc FAT
|
|||||||
launch
|
launch
|
||||||
system_info
|
system_info
|
||||||
system_dyld
|
system_dyld
|
||||||
|
unwind
|
||||||
|
compiler_rt
|
||||||
UPWARD
|
UPWARD
|
||||||
# break an upward dependency on libobjc
|
# 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
|
# libSystem guarantees that we'll be initialized after libdispatch, and libdispatch already initializes libobjc
|
||||||
objc
|
objc
|
||||||
)
|
)
|
||||||
#target_link_libraries(xpc system)
|
|
||||||
install(TARGETS xpc DESTINATION libexec/darling/usr/lib/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
|
target_link_options(xpc PRIVATE
|
||||||
-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/scripts/exported-symbols.exp
|
-Wl,-reexported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/scripts/reexported-symbols.exp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (ENABLE_TESTS)
|
||||||
|
add_darling_static_library(xpc_static
|
||||||
|
FAT
|
||||||
|
SOURCES
|
||||||
|
$<TARGET_OBJECTS:xpc_obj>
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(test)
|
||||||
|
endif (ENABLE_TESTS)
|
||||||
|
@ -4,7 +4,25 @@
|
|||||||
#include <os/object_private.h>
|
#include <os/object_private.h>
|
||||||
|
|
||||||
#if OS_OBJECT_USE_OBJC
|
#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
|
#else
|
||||||
typedef struct os_transaction_s* os_transaction_t;
|
typedef struct os_transaction_s* os_transaction_t;
|
||||||
#endif
|
#endif
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#define XPC_LAUNCHD_H_
|
#define XPC_LAUNCHD_H_
|
||||||
#include <xpc/xpc.h>
|
#include <xpc/xpc.h>
|
||||||
#include <xpc/launchd_defs.h>
|
#include <xpc/launchd_defs.h>
|
||||||
|
#include <launch.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -13,9 +14,11 @@ extern "C" {
|
|||||||
#define EXSRCH 3
|
#define EXSRCH 3
|
||||||
#define EXMAX EXSRCH
|
#define EXMAX EXSRCH
|
||||||
|
|
||||||
|
XPC_DECL(xpc_pipe);
|
||||||
|
|
||||||
const char *xpc_strerror(int error);
|
const char *xpc_strerror(int error);
|
||||||
xpc_object_t xpc_copy_entitlement_for_token(const char *, audit_token_t *);
|
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 *,
|
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);
|
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);
|
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);
|
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 *);
|
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 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);
|
int launch_activate_socket(const char* key, int** fds, size_t* count);
|
||||||
|
|
||||||
|
@ -13,8 +13,6 @@ extern "C" {
|
|||||||
|
|
||||||
int _xpc_runtime_is_app_sandboxed();
|
int _xpc_runtime_is_app_sandboxed();
|
||||||
|
|
||||||
typedef struct _xpc_pipe_s* xpc_pipe_t;
|
|
||||||
|
|
||||||
void xpc_pipe_invalidate(xpc_pipe_t pipe);
|
void xpc_pipe_invalidate(xpc_pipe_t pipe);
|
||||||
|
|
||||||
xpc_pipe_t xpc_pipe_create(const char* name, int flags);
|
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);
|
xpc_object_t xpc_create_from_plist(void *data, size_t size);
|
||||||
|
|
||||||
void xpc_dictionary_get_audit_token(xpc_object_t, audit_token_t *);
|
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_pipe_t pipe, xpc_object_t payload, xpc_object_t* reply);
|
||||||
int xpc_pipe_routine(xpc_object_t pipe, void *payload,xpc_object_t *reply);
|
|
||||||
|
|
||||||
void xpc_connection_set_target_uid(xpc_connection_t connection, uid_t uid);
|
void xpc_connection_set_target_uid(xpc_connection_t connection, uid_t uid);
|
||||||
void xpc_connection_set_instance(xpc_connection_t connection, uuid_t uid);
|
void xpc_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_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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -380,15 +380,18 @@ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
|
|||||||
XPC_EXPORT XPC_NONNULL1
|
XPC_EXPORT XPC_NONNULL1
|
||||||
void
|
void
|
||||||
xpc_release(xpc_object_t object);
|
xpc_release(xpc_object_t object);
|
||||||
void
|
|
||||||
xpc_release_safe(xpc_object_t object);
|
|
||||||
#if OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
#if OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
||||||
#undef xpc_release
|
#undef xpc_release
|
||||||
#define xpc_release(object) ({ xpc_object_t _o = (object); \
|
#define xpc_release(object) ({ xpc_object_t _o = (object); \
|
||||||
_xpc_object_validate(_o); [_o release]; })
|
_xpc_object_validate(_o); [_o release]; })
|
||||||
#define xpc_release_safe(object) xpc_release(object)
|
|
||||||
|
|
||||||
#endif // OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
#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
|
* @function xpc_get_type
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
#include <sys/queue.h>
|
#include <sys/queue.h>
|
||||||
#include <xpc/xpc.h>
|
#include <xpc/xpc.h>
|
||||||
|
#include <xpc/activity.h>
|
||||||
#include <darling/emulation/simple.h>
|
#include <darling/emulation/simple.h>
|
||||||
|
|
||||||
#ifdef XPC_DEBUG
|
#ifdef XPC_DEBUG
|
||||||
|
16
internal-include/xpc/objects/activity.h
Normal file
16
internal-include/xpc/objects/activity.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_ACTIVITY_H_
|
||||||
|
#define _XPC_OBJECTS_ACTIVITY_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(activity);
|
||||||
|
|
||||||
|
struct xpc_activity_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS_INTERFACE(activity)
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_ACTIVITY_H_
|
33
internal-include/xpc/objects/array.h
Normal file
33
internal-include/xpc/objects/array.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_ARRAY_H_
|
||||||
|
#define _XPC_OBJECTS_ARRAY_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
#import <Foundation/NSEnumerator.h>
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(array);
|
||||||
|
|
||||||
|
struct xpc_array_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
NSUInteger size;
|
||||||
|
XPC_CLASS(object)** array;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS_INTERFACE(array)
|
||||||
|
|
||||||
|
// this API is modeled after NSMutableArray
|
||||||
|
|
||||||
|
@property(readonly) NSUInteger count;
|
||||||
|
|
||||||
|
- (instancetype)initWithObjects: (XPC_CLASS(object)* const*)objects count: (NSUInteger)count;
|
||||||
|
|
||||||
|
- (XPC_CLASS(object)*)objectAtIndex: (NSUInteger)index;
|
||||||
|
- (void)addObject: (XPC_CLASS(object)*)object;
|
||||||
|
- (void)replaceObjectAtIndex: (NSUInteger)index withObject: (XPC_CLASS(object)*)object;
|
||||||
|
- (void)enumerateObjectsUsingBlock: (void (^)(XPC_CLASS(object)* object, NSUInteger index, BOOL* stop))block;
|
||||||
|
- (XPC_CLASS(object)*)objectAtIndexedSubscript: (NSUInteger)index;
|
||||||
|
- (void)setObject: (XPC_CLASS(object)*)object atIndexedSubscript: (NSUInteger)index;
|
||||||
|
- (NSUInteger)countByEnumeratingWithState: (NSFastEnumerationState*)state objects: (id __unsafe_unretained [])objects count: (NSUInteger)count;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_ARRAY_H_
|
120
internal-include/xpc/objects/base.h
Normal file
120
internal-include/xpc/objects/base.h
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_BASE_H_
|
||||||
|
#define _XPC_OBJECTS_BASE_H_
|
||||||
|
|
||||||
|
#import <os/object_private.h>
|
||||||
|
#import <objc/NSObject.h>
|
||||||
|
|
||||||
|
#ifndef __XPC_INDIRECT__
|
||||||
|
OS_OBJECT_DECL(xpc_object);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __XPC_INDIRECT__
|
||||||
|
#import <xpc/base.h>
|
||||||
|
|
||||||
|
#define XPC_CLASS(name) OS_OBJECT_CLASS(xpc_ ## name)
|
||||||
|
|
||||||
|
#define XPC_CLASS_DECL(name) OS_OBJECT_DECL_SUBCLASS(xpc_ ## name, xpc_object)
|
||||||
|
|
||||||
|
#define XPC_CLASS_INTERFACE(name) XPC_CLASS(name) : XPC_CLASS(object) <XPC_CLASS(name)>
|
||||||
|
|
||||||
|
#define XPC_CAST(name, object) ((XPC_CLASS(name)*)object)
|
||||||
|
|
||||||
|
#define XPC_CLASS_HEADER(name) \
|
||||||
|
OS_OBJECT_NONLAZY_CLASS_LOAD \
|
||||||
|
+ (NSUInteger)_instanceSize \
|
||||||
|
{ \
|
||||||
|
return sizeof(struct xpc_ ## name ## _s); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define XPC_THIS(name) ((struct xpc_ ## name ## _s*)self)
|
||||||
|
#define XPC_THIS_DECL(name) struct xpc_ ## name ## _s* this = XPC_THIS(name)
|
||||||
|
|
||||||
|
#define XPC_WRAPPER_CLASS_DECL(name, type) \
|
||||||
|
XPC_CLASS_DECL(name); \
|
||||||
|
struct xpc_ ## name ## _s { \
|
||||||
|
struct xpc_object_s base; \
|
||||||
|
type value; \
|
||||||
|
}; \
|
||||||
|
@interface XPC_CLASS_INTERFACE(name) \
|
||||||
|
@property(assign) type value; \
|
||||||
|
- (instancetype)initWithValue: (type)value; \
|
||||||
|
@end
|
||||||
|
|
||||||
|
#define XPC_WRAPPER_CLASS_IMPL(name, type, format) \
|
||||||
|
XPC_CLASS_SYMBOL_DECL(name); \
|
||||||
|
OS_OBJECT_NONLAZY_CLASS \
|
||||||
|
@implementation XPC_CLASS(name) \
|
||||||
|
XPC_CLASS_HEADER(name); \
|
||||||
|
- (char*)xpcDescription \
|
||||||
|
{ \
|
||||||
|
char* output = NULL; \
|
||||||
|
asprintf(&output, "<%s: " format ">", xpc_class_name(self), self.value); \
|
||||||
|
return output; \
|
||||||
|
} \
|
||||||
|
- (type)value \
|
||||||
|
{ \
|
||||||
|
XPC_THIS_DECL(name); \
|
||||||
|
return this->value; \
|
||||||
|
} \
|
||||||
|
- (void)setValue: (type)value \
|
||||||
|
{ \
|
||||||
|
XPC_THIS_DECL(name); \
|
||||||
|
this->value = value; \
|
||||||
|
} \
|
||||||
|
- (instancetype)initWithValue: (type)value \
|
||||||
|
{ \
|
||||||
|
if (self = [super init]) { \
|
||||||
|
XPC_THIS_DECL(name); \
|
||||||
|
this->value = value; \
|
||||||
|
} \
|
||||||
|
return self; \
|
||||||
|
} \
|
||||||
|
- (NSUInteger)hash \
|
||||||
|
{ \
|
||||||
|
XPC_THIS_DECL(name); \
|
||||||
|
return xpc_raw_data_hash(&this->value, sizeof(this->value)); \
|
||||||
|
} \
|
||||||
|
@end
|
||||||
|
|
||||||
|
// hack to create symbol aliases programmatically, because the `alias` attribute isn't supported on Darwin platforms
|
||||||
|
#define _CREATE_ALIAS(original, alias) __asm__(".globl " original "; .globl " alias "; .equiv " alias ", " original)
|
||||||
|
|
||||||
|
#define XPC_CLASS_SYMBOL(name) _xpc_type_ ## name
|
||||||
|
#define XPC_CLASS_SYMBOL_DECL(name) \
|
||||||
|
XPC_EXPORT struct objc_class XPC_CLASS_SYMBOL(name); \
|
||||||
|
_CREATE_ALIAS(OS_OBJC_CLASS_RAW_SYMBOL_NAME(XPC_CLASS(name)), "_" OS_STRINGIFY(XPC_CLASS_SYMBOL(name)))
|
||||||
|
|
||||||
|
#define XPC_OBJC_CLASS(name) ((Class)&XPC_CLASS_SYMBOL(name))
|
||||||
|
|
||||||
|
#define XPC_GLOBAL_OBJECT_HEADER(className) \
|
||||||
|
.os_obj_isa = (const struct xpc_object_vtable_s*)XPC_OBJC_CLASS(className), \
|
||||||
|
.os_obj_ref_cnt = _OS_OBJECT_GLOBAL_REFCNT, \
|
||||||
|
.os_obj_xref_cnt = _OS_OBJECT_GLOBAL_REFCNT
|
||||||
|
|
||||||
|
//
|
||||||
|
// base XPC class
|
||||||
|
//
|
||||||
|
|
||||||
|
struct xpc_object_vtable_s {
|
||||||
|
_OS_OBJECT_CLASS_HEADER();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct xpc_object_s {
|
||||||
|
_OS_OBJECT_HEADER(
|
||||||
|
const struct xpc_object_vtable_s* os_obj_isa,
|
||||||
|
os_obj_ref_cnt,
|
||||||
|
os_obj_xref_cnt
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
@interface XPC_CLASS(object) : OS_OBJECT_CLASS(object) <XPC_CLASS(object)>
|
||||||
|
|
||||||
|
+ (NSUInteger)_instanceSize;
|
||||||
|
|
||||||
|
// note that this method returns a string that must be freed
|
||||||
|
- (char*)xpcDescription;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_BASE_H_
|
26
internal-include/xpc/objects/bool.h
Normal file
26
internal-include/xpc/objects/bool.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_BOOL_H_
|
||||||
|
#define _XPC_OBJECTS_BOOL_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
|
||||||
|
// bools are NOT simple wrapper objects because they are global singletons
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(bool);
|
||||||
|
|
||||||
|
struct xpc_bool_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
bool value;
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef bool
|
||||||
|
@interface XPC_CLASS_INTERFACE(bool)
|
||||||
|
|
||||||
|
@property(assign) BOOL value;
|
||||||
|
|
||||||
|
- (instancetype)initWithValue: (BOOL)value;
|
||||||
|
+ (instancetype)boolForValue: (BOOL)value;
|
||||||
|
|
||||||
|
@end
|
||||||
|
#define bool _Bool
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_BOOL_H_
|
16
internal-include/xpc/objects/bundle.h
Normal file
16
internal-include/xpc/objects/bundle.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_BUNDLE_H_
|
||||||
|
#define _XPC_OBJECTS_BUNDLE_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(bundle);
|
||||||
|
|
||||||
|
struct xpc_bundle_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS_INTERFACE(bundle)
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_BUNDLE_H_
|
16
internal-include/xpc/objects/connection.h
Normal file
16
internal-include/xpc/objects/connection.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_CONNECTION_H_
|
||||||
|
#define _XPC_OBJECTS_CONNECTION_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(connection);
|
||||||
|
|
||||||
|
struct xpc_connection_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS_INTERFACE(connection)
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_CONNECTION_H_
|
33
internal-include/xpc/objects/data.h
Normal file
33
internal-include/xpc/objects/data.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_DATA_H_
|
||||||
|
#define _XPC_OBJECTS_DATA_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
#import <dispatch/dispatch.h>
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(data);
|
||||||
|
|
||||||
|
struct xpc_data_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
dispatch_data_t data;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS_INTERFACE(data)
|
||||||
|
|
||||||
|
// this API is modeled after NSMutableData
|
||||||
|
|
||||||
|
// NOTE: deviates from NSData by always returning a pointer to the internal buffer, even if `length` is 0
|
||||||
|
@property(readonly) const void* bytes;
|
||||||
|
@property(readonly) NSUInteger length;
|
||||||
|
|
||||||
|
- (instancetype)initWithBytes: (const void*)bytes length: (NSUInteger)length;
|
||||||
|
- (instancetype)initWithDispatchData: (dispatch_data_t)data;
|
||||||
|
|
||||||
|
// NOTE: deviates from NSData by returning the number of bytes copied
|
||||||
|
- (NSUInteger)getBytes: (void*)buffer length: (NSUInteger)length;
|
||||||
|
|
||||||
|
// non-NSData method
|
||||||
|
- (void)replaceBytesWithBytes: (const void*)bytes length: (NSUInteger)length;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_DATA_H_
|
8
internal-include/xpc/objects/date.h
Normal file
8
internal-include/xpc/objects/date.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_DATE_H_
|
||||||
|
#define _XPC_OBJECTS_DATE_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
|
||||||
|
XPC_WRAPPER_CLASS_DECL(date, int64_t);
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_DATE_H_
|
54
internal-include/xpc/objects/dictionary.h
Normal file
54
internal-include/xpc/objects/dictionary.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_DICTIONARY_H_
|
||||||
|
#define _XPC_OBJECTS_DICTIONARY_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
#import <xpc/objects/connection.h>
|
||||||
|
|
||||||
|
#include <sys/queue.h>
|
||||||
|
|
||||||
|
@class XPC_CLASS(string);
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(dictionary);
|
||||||
|
|
||||||
|
typedef struct xpc_dictionary_entry_s* xpc_dictionary_entry_t;
|
||||||
|
struct xpc_dictionary_entry_s {
|
||||||
|
LIST_ENTRY(xpc_dictionary_entry_s) link;
|
||||||
|
XPC_CLASS(object)* object;
|
||||||
|
char name[];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct xpc_dictionary_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
NSUInteger size;
|
||||||
|
LIST_HEAD(, xpc_dictionary_entry_s) head;
|
||||||
|
XPC_CLASS(connection)* associatedConnection;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS_INTERFACE(dictionary)
|
||||||
|
|
||||||
|
// this API is modeled after NSMutableDictionary
|
||||||
|
|
||||||
|
@property(readonly) NSUInteger count;
|
||||||
|
// NOTE: i'm not sure if the associated connection should be strongly or weakly associated
|
||||||
|
@property(strong) XPC_CLASS(connection)* associatedConnection;
|
||||||
|
|
||||||
|
- (instancetype)initWithObjects: (XPC_CLASS(object)* const*)objects forKeys: (const char* const*)keys count: (NSUInteger)count;
|
||||||
|
|
||||||
|
- (XPC_CLASS(object)*)objectForKey: (const char*)key;
|
||||||
|
- (void)setObject: (XPC_CLASS(object)*)object forKey: (const char*)key;
|
||||||
|
- (void)removeObjectForKey: (const char*)key;
|
||||||
|
- (void)enumerateKeysAndObjectsUsingBlock: (void (^)(const char* key, XPC_CLASS(object)* obj, BOOL* stop))block;
|
||||||
|
|
||||||
|
// unfortunately, no keyed subscripts for this class because `const char*`s aren't valid subscripts
|
||||||
|
|
||||||
|
// NOTE: consider these as private methods
|
||||||
|
- (xpc_dictionary_entry_t)entryForKey: (const char*)key;
|
||||||
|
- (void)addEntry: (xpc_dictionary_entry_t)entry;
|
||||||
|
- (void)removeEntry: (xpc_dictionary_entry_t)entry;
|
||||||
|
|
||||||
|
// useful extensions:
|
||||||
|
- (XPC_CLASS(string)*)stringForKey: (const char*)key;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_DICTIONARY_H_
|
8
internal-include/xpc/objects/double.h
Normal file
8
internal-include/xpc/objects/double.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_DOUBLE_H_
|
||||||
|
#define _XPC_OBJECTS_DOUBLE_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
|
||||||
|
XPC_WRAPPER_CLASS_DECL(double, double);
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_DOUBLE_H_
|
19
internal-include/xpc/objects/endpoint.h
Normal file
19
internal-include/xpc/objects/endpoint.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_ENDPOINT_H_
|
||||||
|
#define _XPC_OBJECTS_ENDPOINT_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
#import <xpc/objects/connection.h>
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(endpoint);
|
||||||
|
|
||||||
|
struct xpc_endpoint_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS_INTERFACE(endpoint)
|
||||||
|
|
||||||
|
- (instancetype)initWithConnection: (XPC_CLASS(connection)*)connection;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_ENDPOINT_H_
|
18
internal-include/xpc/objects/error.h
Normal file
18
internal-include/xpc/objects/error.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_ERROR_H_
|
||||||
|
#define _XPC_OBJECTS_ERROR_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/dictionary.h>
|
||||||
|
|
||||||
|
// errors are unique XPC objects: they're basically dictionaries
|
||||||
|
|
||||||
|
OS_OBJECT_DECL_SUBCLASS(xpc_error, xpc_object);
|
||||||
|
|
||||||
|
struct xpc_error_s {
|
||||||
|
struct xpc_dictionary_s base;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS(error) : XPC_CLASS(dictionary) <XPC_CLASS(error)>
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_ERROR_H_
|
26
internal-include/xpc/objects/fd.h
Normal file
26
internal-include/xpc/objects/fd.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_FD_H_
|
||||||
|
#define _XPC_OBJECTS_FD_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
|
||||||
|
#include <mach/mach_port.h>
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(fd);
|
||||||
|
|
||||||
|
struct xpc_fd_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
mach_port_t port;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS_INTERFACE(fd)
|
||||||
|
|
||||||
|
@property(readonly) mach_port_t port;
|
||||||
|
|
||||||
|
- (instancetype)initWithDescriptor: (int)descriptor;
|
||||||
|
- (instancetype)initWithPort: (mach_port_t)port;
|
||||||
|
|
||||||
|
- (int)instantiateDescriptor;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_FD_H_
|
16
internal-include/xpc/objects/file_transfer.h
Normal file
16
internal-include/xpc/objects/file_transfer.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_FILE_TRANSFER_H_
|
||||||
|
#define _XPC_OBJECTS_FILE_TRANSFER_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(file_transfer);
|
||||||
|
|
||||||
|
struct xpc_file_transfer_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS_INTERFACE(file_transfer)
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_FILE_TRANSFER_H_
|
8
internal-include/xpc/objects/int64.h
Normal file
8
internal-include/xpc/objects/int64.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_INT64_H_
|
||||||
|
#define _XPC_OBJECTS_INT64_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
|
||||||
|
XPC_WRAPPER_CLASS_DECL(int64, int64_t);
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_INT64_H_
|
24
internal-include/xpc/objects/mach_recv.h
Normal file
24
internal-include/xpc/objects/mach_recv.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_MACH_RECV_H_
|
||||||
|
#define _XPC_OBJECTS_MACH_RECV_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
#import <mach/port.h>
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(mach_recv);
|
||||||
|
|
||||||
|
struct xpc_mach_recv_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS_INTERFACE(mach_recv)
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
xpc_object_t xpc_mach_recv_create(mach_port_t recv);
|
||||||
|
mach_port_t xpc_mach_recv_extract_right(xpc_object_t xrecv);
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_MACH_RECV_H_
|
27
internal-include/xpc/objects/mach_send.h
Normal file
27
internal-include/xpc/objects/mach_send.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_MACH_SEND_H_
|
||||||
|
#define _XPC_OBJECTS_MACH_SEND_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
#import <mach/mach_port.h>
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(mach_send);
|
||||||
|
|
||||||
|
struct xpc_mach_send_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS_INTERFACE(mach_send)
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
xpc_object_t xpc_mach_send_create(mach_port_t send);
|
||||||
|
mach_port_t xpc_mach_send_copy_right(xpc_object_t xsend);
|
||||||
|
xpc_object_t xpc_mach_send_create_with_disposition(mach_port_t send, unsigned int disposition);
|
||||||
|
mach_port_t xpc_mach_send_get_right(xpc_object_t xsend);
|
||||||
|
mach_port_t xpc_mach_send_extract_right(xpc_object_t xsend);
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_MACH_SEND_H_
|
20
internal-include/xpc/objects/null.h
Normal file
20
internal-include/xpc/objects/null.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_NULL_H_
|
||||||
|
#define _XPC_OBJECTS_NULL_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(null);
|
||||||
|
|
||||||
|
struct xpc_null_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS_INTERFACE(null)
|
||||||
|
|
||||||
|
// this API is modeled after NSNull
|
||||||
|
|
||||||
|
+ (instancetype)null;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_NULL_H_
|
16
internal-include/xpc/objects/pipe.h
Normal file
16
internal-include/xpc/objects/pipe.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_PIPE_H_
|
||||||
|
#define _XPC_OBJECTS_PIPE_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(pipe);
|
||||||
|
|
||||||
|
struct xpc_pipe_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS_INTERFACE(pipe)
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_PIPE_H_
|
8
internal-include/xpc/objects/pointer.h
Normal file
8
internal-include/xpc/objects/pointer.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_POINTER_H_
|
||||||
|
#define _XPC_OBJECTS_POINTER_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
|
||||||
|
XPC_WRAPPER_CLASS_DECL(pointer, void*);
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_POINTER_H_
|
16
internal-include/xpc/objects/serializer.h
Normal file
16
internal-include/xpc/objects/serializer.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_SERIALIZER_H_
|
||||||
|
#define _XPC_OBJECTS_SERIALIZER_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(serializer);
|
||||||
|
|
||||||
|
struct xpc_serializer_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS_INTERFACE(serializer)
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_SERIALIZER_H_
|
16
internal-include/xpc/objects/service.h
Normal file
16
internal-include/xpc/objects/service.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_SERVICE_H_
|
||||||
|
#define _XPC_OBJECTS_SERVICE_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(service);
|
||||||
|
|
||||||
|
struct xpc_service_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS_INTERFACE(service)
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_SERVICE_H_
|
16
internal-include/xpc/objects/service_instance.h
Normal file
16
internal-include/xpc/objects/service_instance.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_SERVICE_INSTANCE_H_
|
||||||
|
#define _XPC_OBJECTS_SERVICE_INSTANCE_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(service_instance);
|
||||||
|
|
||||||
|
struct xpc_service_instance_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS_INTERFACE(service_instance)
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_SERVICE_INSTANCE_H_
|
16
internal-include/xpc/objects/shmem.h
Normal file
16
internal-include/xpc/objects/shmem.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_SHMEM_H_
|
||||||
|
#define _XPC_OBJECTS_SHMEM_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(shmem);
|
||||||
|
|
||||||
|
struct xpc_shmem_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS_INTERFACE(shmem)
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_SHMEM_H_
|
35
internal-include/xpc/objects/string.h
Normal file
35
internal-include/xpc/objects/string.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_STRING_H_
|
||||||
|
#define _XPC_OBJECTS_STRING_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(string);
|
||||||
|
|
||||||
|
struct xpc_string_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
NSUInteger byteLength; // acts as an optional cache
|
||||||
|
BOOL freeWhenDone;
|
||||||
|
const char* string;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS_INTERFACE(string)
|
||||||
|
|
||||||
|
// this API is modeled after NSString
|
||||||
|
|
||||||
|
// non-NSString property; length of the string in bytes, not including the null terminator
|
||||||
|
@property(readonly) NSUInteger byteLength;
|
||||||
|
@property(readonly) const char* UTF8String;
|
||||||
|
|
||||||
|
- (instancetype)initWithUTF8String: (const char*)string;
|
||||||
|
// non-NSString method
|
||||||
|
- (instancetype)initWithUTF8StringNoCopy: (const char*)string freeWhenDone: (BOOL)freeIt;
|
||||||
|
- (instancetype)initWithFormat: (const char*)format, ...;
|
||||||
|
- (instancetype)initWithFormat: (const char*)format arguments: (va_list)args;
|
||||||
|
|
||||||
|
// non-NSString method
|
||||||
|
- (void)replaceStringWithString: (const char*)string;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_STRING_H_
|
8
internal-include/xpc/objects/uint64.h
Normal file
8
internal-include/xpc/objects/uint64.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_UINT64_H_
|
||||||
|
#define _XPC_OBJECTS_UINT64_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
|
||||||
|
XPC_WRAPPER_CLASS_DECL(uint64, uint64_t);
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_UINT64_H_
|
22
internal-include/xpc/objects/uuid.h
Normal file
22
internal-include/xpc/objects/uuid.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#ifndef _XPC_OBJECTS_UUID_H_
|
||||||
|
#define _XPC_OBJECTS_UUID_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
#include <uuid/uuid.h>
|
||||||
|
|
||||||
|
XPC_CLASS_DECL(uuid);
|
||||||
|
|
||||||
|
struct xpc_uuid_s {
|
||||||
|
struct xpc_object_s base;
|
||||||
|
uuid_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface XPC_CLASS_INTERFACE(uuid)
|
||||||
|
|
||||||
|
@property(readonly) uint8_t* bytes;
|
||||||
|
|
||||||
|
- (instancetype)initWithBytes: (const uint8_t*)bytes;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif // _XPC_OBJECTS_UUID_H_
|
10
internal-include/xpc/prefix.h
Normal file
10
internal-include/xpc/prefix.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#import <os/object.h>
|
||||||
|
|
||||||
|
#if __OBJC2__
|
||||||
|
// Objective-C 2.0 marks `struct objc_class` as unavailable by default.
|
||||||
|
// we know what we're doing; let's prevent the compiler from complaining.
|
||||||
|
#define OBJC2_UNAVAILABLE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define XPC_TYPE(name) struct objc_class name
|
||||||
|
#define XPC_DECL(name) OS_OBJECT_DECL_SUBCLASS(name, xpc_object)
|
90
internal-include/xpc/util.h
Normal file
90
internal-include/xpc/util.h
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#ifndef _XPC_UTIL_H_
|
||||||
|
#define _XPC_UTIL_H_
|
||||||
|
|
||||||
|
#import <xpc/objects/base.h>
|
||||||
|
#import <xpc/objects/connection.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <mach/mach.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines `objcName` by casting `origName`, but also checks to ensure it's not `nil`
|
||||||
|
* and to ensure it is an XPC object of the given `className`.
|
||||||
|
*
|
||||||
|
* You should call it with a (possibly braced) statement of what to do when it passes these checks.
|
||||||
|
* You can optionally append an `else` clause for what to do when it fails these checks.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* xpc_object_t some_input = get_some_input();
|
||||||
|
* TO_OBJC_CHECKED(string, some_input, some_checked_input) {
|
||||||
|
* printf("Yay! It's a string! Look: %s\n", some_checked_input.description.UTF8String); // note that this assumes NSString is loaded
|
||||||
|
* } else {
|
||||||
|
* printf("Oh no, you messed up and gave me an object that wasn't a string :(\n");
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
#define TO_OBJC_CHECKED(className, origName, objcName) \
|
||||||
|
XPC_CLASS(className)* objcName = XPC_CAST(className, origName); \
|
||||||
|
if (objcName && [objcName isKindOfClass: [XPC_CLASS(className) class]])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like `TO_OBJC_CHECKED`, but the condition checks for failure to pass the checks.
|
||||||
|
*
|
||||||
|
* @see `TO_OBJC_CHECKED`
|
||||||
|
*/
|
||||||
|
#define TO_OBJC_CHECKED_ON_FAIL(className, origName, objcName) \
|
||||||
|
XPC_CLASS(className)* objcName = XPC_CAST(className, origName); \
|
||||||
|
if (!objcName || ![objcName isKindOfClass: [XPC_CLASS(className) class]])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special `retain` variant for collection classes like dictionaries and arrays.
|
||||||
|
*
|
||||||
|
* This is necessary because collections do not retain certain types of objects
|
||||||
|
* and just store them weakly.
|
||||||
|
*
|
||||||
|
* @returns The object passed in, possibly with an increased reference count.
|
||||||
|
*/
|
||||||
|
XPC_CLASS(object)* xpc_retain_for_collection(XPC_CLASS(object)* object);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special `release` variant for collection classes like dictionaries and arrays.
|
||||||
|
*
|
||||||
|
* @see `xpc_retain_for_collection`
|
||||||
|
*
|
||||||
|
* @returns The object passed in, possibly with a decreased reference count.
|
||||||
|
*/
|
||||||
|
void xpc_release_for_collection(XPC_CLASS(object)* object);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the given object to a class name.
|
||||||
|
*
|
||||||
|
* @returns The mapped class name for the object.
|
||||||
|
*/
|
||||||
|
const char* xpc_class_name(XPC_CLASS(object)* object);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a string that is an indented copy of the given string.
|
||||||
|
*
|
||||||
|
* @returns A string that must be freed.
|
||||||
|
*/
|
||||||
|
char* xpc_description_indent(const char* description, bool indentFirstLine);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produces a hash from the given data.
|
||||||
|
*
|
||||||
|
* @returns A hash of the input data.
|
||||||
|
*/
|
||||||
|
size_t xpc_raw_data_hash(const void* data, size_t data_length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments the reference count on the given right for the given port.
|
||||||
|
*/
|
||||||
|
kern_return_t xpc_mach_port_retain_right(mach_port_name_t port, mach_port_right_t right);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrements the reference count on the given right for the given port.
|
||||||
|
*/
|
||||||
|
kern_return_t xpc_mach_port_release_right(mach_port_name_t port, mach_port_right_t right);
|
||||||
|
|
||||||
|
#endif // _XPC_UTIL_H_
|
@ -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
|
|
112
scripts/reexported-symbols.exp
Normal file
112
scripts/reexported-symbols.exp
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
__spawn_via_launchd
|
||||||
|
__vproc_get_last_exit_status
|
||||||
|
__vproc_grab_subset
|
||||||
|
__vproc_kickstart_by_label
|
||||||
|
__vproc_log
|
||||||
|
__vproc_log_error
|
||||||
|
__vproc_logv
|
||||||
|
__vproc_pid_is_managed
|
||||||
|
__vproc_post_fork_ping
|
||||||
|
__vproc_send_signal_by_label
|
||||||
|
__vproc_set_global_on_demand
|
||||||
|
__vproc_standby_begin
|
||||||
|
__vproc_standby_count
|
||||||
|
__vproc_standby_end
|
||||||
|
__vproc_standby_timeout
|
||||||
|
__vproc_transaction_begin
|
||||||
|
__vproc_transaction_count
|
||||||
|
__vproc_transaction_count_for_pid
|
||||||
|
__vproc_transaction_end
|
||||||
|
__vproc_transaction_set_clean_callback
|
||||||
|
__vproc_transaction_try_exit
|
||||||
|
__vproc_transactions_enable
|
||||||
|
__vprocmgr_detach_from_console
|
||||||
|
__vprocmgr_getsocket
|
||||||
|
__vprocmgr_init
|
||||||
|
__vprocmgr_log_drain
|
||||||
|
__vprocmgr_log_forward
|
||||||
|
__vprocmgr_move_subset_to_user
|
||||||
|
__vprocmgr_switch_to_session
|
||||||
|
_bootstrap_check_in
|
||||||
|
_bootstrap_check_in2
|
||||||
|
_bootstrap_check_in3
|
||||||
|
_bootstrap_create_server
|
||||||
|
_bootstrap_create_service
|
||||||
|
_bootstrap_get_root
|
||||||
|
_bootstrap_info
|
||||||
|
_bootstrap_init
|
||||||
|
_bootstrap_look_up
|
||||||
|
_bootstrap_look_up2
|
||||||
|
_bootstrap_look_up3
|
||||||
|
_bootstrap_look_up_per_user
|
||||||
|
_bootstrap_lookup_children
|
||||||
|
_bootstrap_parent
|
||||||
|
_bootstrap_register
|
||||||
|
_bootstrap_register2
|
||||||
|
_bootstrap_status
|
||||||
|
_bootstrap_strerror
|
||||||
|
_bootstrap_subset
|
||||||
|
_bootstrap_unprivileged
|
||||||
|
_create_and_switch_to_per_session_launchd
|
||||||
|
_launch_data_alloc
|
||||||
|
_launch_data_array_get_count
|
||||||
|
_launch_data_array_get_index
|
||||||
|
_launch_data_array_set_index
|
||||||
|
_launch_data_copy
|
||||||
|
_launch_data_dict_get_count
|
||||||
|
_launch_data_dict_insert
|
||||||
|
_launch_data_dict_iterate
|
||||||
|
_launch_data_dict_lookup
|
||||||
|
_launch_data_dict_remove
|
||||||
|
_launch_data_free
|
||||||
|
_launch_data_get_bool
|
||||||
|
_launch_data_get_errno
|
||||||
|
_launch_data_get_fd
|
||||||
|
_launch_data_get_integer
|
||||||
|
_launch_data_get_machport
|
||||||
|
_launch_data_get_opaque
|
||||||
|
_launch_data_get_opaque_size
|
||||||
|
_launch_data_get_real
|
||||||
|
_launch_data_get_string
|
||||||
|
_launch_data_get_type
|
||||||
|
_launch_data_new_bool
|
||||||
|
_launch_data_new_errno
|
||||||
|
_launch_data_new_fd
|
||||||
|
_launch_data_new_integer
|
||||||
|
_launch_data_new_machport
|
||||||
|
_launch_data_new_opaque
|
||||||
|
_launch_data_new_real
|
||||||
|
_launch_data_new_string
|
||||||
|
_launch_data_pack
|
||||||
|
_launch_data_set_bool
|
||||||
|
_launch_data_set_errno
|
||||||
|
_launch_data_set_fd
|
||||||
|
_launch_data_set_integer
|
||||||
|
_launch_data_set_machport
|
||||||
|
_launch_data_set_opaque
|
||||||
|
_launch_data_set_real
|
||||||
|
_launch_data_set_string
|
||||||
|
_launch_data_unpack
|
||||||
|
_launch_get_fd
|
||||||
|
_launch_msg
|
||||||
|
_launch_socket_service_check_in
|
||||||
|
_launch_wait
|
||||||
|
_launchd_close
|
||||||
|
_launchd_fdopen
|
||||||
|
_launchd_getfd
|
||||||
|
_launchd_msg_recv
|
||||||
|
_launchd_msg_send
|
||||||
|
_load_launchd_jobs_at_loginwindow_prompt
|
||||||
|
_mpm_uncork_fork
|
||||||
|
_mpm_wait
|
||||||
|
_reboot2
|
||||||
|
_vproc_release
|
||||||
|
_vproc_retain
|
||||||
|
_vproc_standby_begin
|
||||||
|
_vproc_standby_end
|
||||||
|
_vproc_swap_complex
|
||||||
|
_vproc_swap_integer
|
||||||
|
_vproc_swap_string
|
||||||
|
_vproc_transaction_begin
|
||||||
|
_vproc_transaction_end
|
||||||
|
_vprocmgr_lookup_vproc
|
144
src/activity.c
144
src/activity.c
@ -1,144 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of Darling.
|
|
||||||
|
|
||||||
Copyright (C) 2017 Darling developers
|
|
||||||
|
|
||||||
Darling is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Darling is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Block.h>
|
|
||||||
|
|
||||||
#include <xpc/xpc.h>
|
|
||||||
#include <xpc/internal.h>
|
|
||||||
|
|
||||||
|
|
||||||
const char *XPC_ACTIVITY_INTERVAL = "Interval";
|
|
||||||
const char *XPC_ACTIVITY_REPEATING = "Repeating";
|
|
||||||
const char *XPC_ACTIVITY_DELAY = "Delay";
|
|
||||||
const char *XPC_ACTIVITY_GRACE_PERIOD = "GracePeriod";
|
|
||||||
|
|
||||||
const int64_t XPC_ACTIVITY_INTERVAL_1_MIN = 60;
|
|
||||||
const int64_t XPC_ACTIVITY_INTERVAL_5_MIN = 300;
|
|
||||||
const int64_t XPC_ACTIVITY_INTERVAL_15_MIN = 900;
|
|
||||||
const int64_t XPC_ACTIVITY_INTERVAL_30_MIN = 1800;
|
|
||||||
const int64_t XPC_ACTIVITY_INTERVAL_1_HOUR = 3600;
|
|
||||||
const int64_t XPC_ACTIVITY_INTERVAL_4_HOURS = 14400;
|
|
||||||
const int64_t XPC_ACTIVITY_INTERVAL_8_HOURS = 28800;
|
|
||||||
const int64_t XPC_ACTIVITY_INTERVAL_1_DAY = 86400;
|
|
||||||
const int64_t XPC_ACTIVITY_INTERVAL_7_DAYS = 604800;
|
|
||||||
|
|
||||||
const char *XPC_ACTIVITY_PRIORITY = "Priority";
|
|
||||||
const char *XPC_ACTIVITY_PRIORITY_MAINTENANCE = "Maintenance";
|
|
||||||
const char *XPC_ACTIVITY_PRIORITY_UTILITY = "Utility";
|
|
||||||
const char *XPC_ACTIVITY_ALLOW_BATTERY = "AllowBattery";
|
|
||||||
const char *XPC_ACTIVITY_REQUIRE_SCREEN_SLEEP = "RequireScreenSleep";
|
|
||||||
const char *XPC_ACTIVITY_REQUIRE_BATTERY_LEVEL = "RequireBatteryLevel";
|
|
||||||
const char *XPC_ACTIVITY_REQUIRE_HDD_SPINNING = "RequireHDDSpinning";
|
|
||||||
|
|
||||||
const char *XPC_ACTIVITY_REQUIRE_NETWORK_CONNECTIVITY = "RequireNetworkConnectivity";
|
|
||||||
const char *XPC_ACTIVITY_REQUIRE_INEXPENSIVE_NETWORK_CONNECTIVITY = "RequireInexpensiveNetworkConnectivity";
|
|
||||||
const char *XPC_ACTIVITY_NETWORK_TRANSFER_DIRECTION = "NetworkTransferDirection";
|
|
||||||
|
|
||||||
const char *XPC_ACTIVITY_NETWORK_TRANSFER_DIRECTION_DOWNLOAD = "Download";
|
|
||||||
const char *XPC_ACTIVITY_NETWORK_TRANSFER_DIRECTION_UPLOAD = "Upload";
|
|
||||||
|
|
||||||
static const struct xpc_object _xpc_activity_check_in = {
|
|
||||||
.xo_xpc_type = _XPC_TYPE_STRING,
|
|
||||||
.xo_size = 10, /* strlen("<CHECK-IN>") */
|
|
||||||
.xo_refcnt = 1,
|
|
||||||
.xo_u = {
|
|
||||||
.str = "<CHECK-IN>"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const xpc_object_t XPC_ACTIVITY_CHECK_IN = &_xpc_activity_check_in;
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_activity_register(const char *identifier, xpc_object_t criteria,
|
|
||||||
xpc_activity_handler_t handler)
|
|
||||||
{
|
|
||||||
/* TODO: global activity registry, XPC_ACTIVITY_CHECK_IN, actual scheduling */
|
|
||||||
|
|
||||||
xpc_u value;
|
|
||||||
|
|
||||||
xpc_activity_t activity = _xpc_prim_create(_XPC_TYPE_ACTIVITY, value, 0);
|
|
||||||
xpc_activity_set_criteria(activity, criteria);
|
|
||||||
xpc_activity_set_state(activity, XPC_ACTIVITY_STATE_WAIT);
|
|
||||||
|
|
||||||
xpc_activity_handler_t handler_copy =
|
|
||||||
(xpc_activity_handler_t) Block_copy(handler);
|
|
||||||
|
|
||||||
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
|
|
||||||
dispatch_async(q, ^ {
|
|
||||||
xpc_activity_set_state(activity, XPC_ACTIVITY_STATE_RUN);
|
|
||||||
handler_copy(activity);
|
|
||||||
Block_release(handler_copy);
|
|
||||||
xpc_release(activity);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_activity_copy_criteria(xpc_activity_t activity)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
|
|
||||||
xo = activity;
|
|
||||||
return xo->xo_activity.criteria;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_activity_set_criteria(xpc_activity_t activity, xpc_object_t criteria)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
|
|
||||||
xo = activity;
|
|
||||||
xpc_release(xo->xo_activity.criteria);
|
|
||||||
xpc_retain(criteria);
|
|
||||||
xo->xo_activity.criteria = criteria;
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_activity_state_t
|
|
||||||
xpc_activity_get_state(xpc_activity_t activity)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
|
|
||||||
xo = activity;
|
|
||||||
return xo->xo_activity.state;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
xpc_activity_set_state(xpc_activity_t activity, xpc_activity_state_t state)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
|
|
||||||
xo = activity;
|
|
||||||
xo->xo_activity.state = state;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
xpc_activity_should_defer(xpc_activity_t activity)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_activity_unregister(const char *identifier)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void xpc_track_activity(void)
|
|
||||||
{
|
|
||||||
}
|
|
228
src/activity.m
Normal file
228
src/activity.m
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
#import <xpc/objects/activity.h>
|
||||||
|
#import <xpc/objects/string.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
#import <xpc/activity.h>
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(activity);
|
||||||
|
|
||||||
|
// strangely enough, the variables themselves aren't marked as const
|
||||||
|
#define KEY_DEF(name, string) XPC_EXPORT const char* XPC_ACTIVITY_ ## name = string
|
||||||
|
#define INTERVAL_DEF(name, seconds) XPC_EXPORT const int64_t XPC_ACTIVITY_INTERVAL_ ## name = seconds
|
||||||
|
|
||||||
|
KEY_DEF(ALLOW_BATTERY, "AllowBattery");
|
||||||
|
KEY_DEF(APP_REFRESH, "AppRefresh");
|
||||||
|
KEY_DEF(COMMUNICATES_WITH_PAIRED_DEVICE, "CommunicatesWithPairedDevice");
|
||||||
|
KEY_DEF(DELAY, "Delay");
|
||||||
|
KEY_DEF(DO_IT_LATER, "DoItLater");
|
||||||
|
KEY_DEF(EXCLUSIVE, "Exclusive");
|
||||||
|
KEY_DEF(EXPECTED_DURATION, "ExpectedDuration");
|
||||||
|
KEY_DEF(GRACE_PERIOD, "GracePeriod");
|
||||||
|
KEY_DEF(GROUP_CONCURRENCY_LIMIT, "ActivityGroupConcurrencyLimit");
|
||||||
|
KEY_DEF(GROUP_NAME, "ActivityGroupName");
|
||||||
|
KEY_DEF(MAY_REBOOT_DEVICE, "MayRebootDevice");
|
||||||
|
KEY_DEF(POST_INSTALL, "PostInstall");
|
||||||
|
KEY_DEF(POWER_NAP, "PowerNap");
|
||||||
|
KEY_DEF(RANDOM_INITIAL_DELAY, "RandomInitialDelay");
|
||||||
|
KEY_DEF(REPEATING, "Repeating");
|
||||||
|
KEY_DEF(REPLY_ENDPOINT, "_ReplyEndpoint");
|
||||||
|
KEY_DEF(SEQUENCE_NUMBER, "_SequenceNumber");
|
||||||
|
KEY_DEF(SHOULD_WAKE_DEVICE, "ShouldWakeDevice");
|
||||||
|
KEY_DEF(USER_REQUESTED_BACKUP_TASK, "UserRequestedBackupTask");
|
||||||
|
|
||||||
|
// interval
|
||||||
|
KEY_DEF(INTERVAL, "Interval");
|
||||||
|
INTERVAL_DEF(1_MIN, 1 * 60);
|
||||||
|
INTERVAL_DEF(5_MIN, 5 * 60);
|
||||||
|
INTERVAL_DEF(15_MIN, 15 * 60);
|
||||||
|
INTERVAL_DEF(30_MIN, 30 * 60);
|
||||||
|
INTERVAL_DEF(1_HOUR, 1 * 60 * 60);
|
||||||
|
INTERVAL_DEF(4_HOURS, 4 * 60 * 60);
|
||||||
|
INTERVAL_DEF(8_HOURS, 8 * 60 * 60);
|
||||||
|
INTERVAL_DEF(1_DAY, 1 * 24 * 60 * 60);
|
||||||
|
INTERVAL_DEF(7_DAYS, 7 * 24 * 60 * 60);
|
||||||
|
|
||||||
|
// resource intesive
|
||||||
|
KEY_DEF(CPU_INTENSIVE, "CPUIntensive");
|
||||||
|
KEY_DEF(DISK_INTENSIVE, "DiskIntensive");
|
||||||
|
KEY_DEF(MEMORY_INTENSIVE, "MemoryIntensive");
|
||||||
|
|
||||||
|
// priority
|
||||||
|
KEY_DEF(PRIORITY, "Priority");
|
||||||
|
KEY_DEF(PRIORITY_MAINTENANCE, "Maintenance");
|
||||||
|
KEY_DEF(PRIORITY_UTILITY, "Utility");
|
||||||
|
|
||||||
|
// motion state
|
||||||
|
KEY_DEF(DESIRED_MOTION_STATE, "MotionState");
|
||||||
|
KEY_DEF(MOTION_STATE_AUTOMOTIVE, "Automotive");
|
||||||
|
KEY_DEF(MOTION_STATE_AUTOMOTIVE_MOVING, "AutomotiveMoving");
|
||||||
|
KEY_DEF(MOTION_STATE_AUTOMOTIVE_STATIONARY, "AutomotiveStationary");
|
||||||
|
KEY_DEF(MOTION_STATE_CYCLING, "Cycling");
|
||||||
|
KEY_DEF(MOTION_STATE_RUNNING, "Running");
|
||||||
|
KEY_DEF(MOTION_STATE_STATIONARY, "Stationary");
|
||||||
|
KEY_DEF(MOTION_STATE_WALKING, "Walking");
|
||||||
|
|
||||||
|
// network transfer
|
||||||
|
KEY_DEF(NETWORK_TRANSFER_ENDPOINT, "NetworkEndpoint");
|
||||||
|
KEY_DEF(NETWORK_TRANSFER_PARAMETERS, "NetworkParameters");
|
||||||
|
KEY_DEF(NETWORK_TRANSFER_SIZE, "NetworkTransferSize");
|
||||||
|
KEY_DEF(NETWORK_TRANSFER_DIRECTION, "NetworkTransferDirection");
|
||||||
|
KEY_DEF(NETWORK_TRANSFER_BIDIRECTIONAL, "Bidirectional");
|
||||||
|
KEY_DEF(NETWORK_TRANSFER_DIRECTION_DOWNLOAD, "Download");
|
||||||
|
KEY_DEF(NETWORK_TRANSFER_DIRECTION_UPLOAD, "Upload");
|
||||||
|
|
||||||
|
// requirements
|
||||||
|
KEY_DEF(REQUIRES_BUDDY_COMPLETE, "RequiresBuddyComplete");
|
||||||
|
KEY_DEF(REQUIRES_CLASS_A, "RequiresClassA");
|
||||||
|
KEY_DEF(REQUIRES_CLASS_B, "RequiresClassB");
|
||||||
|
KEY_DEF(REQUIRES_CLASS_C, "RequiresClassC");
|
||||||
|
KEY_DEF(REQUIRE_BATTERY_LEVEL, "RequireBatteryLevel");
|
||||||
|
KEY_DEF(REQUIRE_HDD_SPINNING, "RequireHDDSpinning");
|
||||||
|
KEY_DEF(REQUIRE_INEXPENSIVE_NETWORK_CONNECTIVITY, "RequireInexpensiveNetworkConnectivity");
|
||||||
|
KEY_DEF(REQUIRE_NETWORK_CONNECTIVITY, "RequireNetworkConnectivity");
|
||||||
|
KEY_DEF(REQUIRE_SCREEN_SLEEP, "RequireScreenSleep");
|
||||||
|
KEY_DEF(REQUIRE_SIGNIFICANT_USER_INACTIVITY, "RequireSignificantUserInactivity");
|
||||||
|
|
||||||
|
// Duet stuff
|
||||||
|
KEY_DEF(DUET_ACTIVITY_SCHEDULER_DATA, "DASData");
|
||||||
|
KEY_DEF(DUET_ATTRIBUTE_COST, "DuetAttributeCost");
|
||||||
|
KEY_DEF(DUET_ATTRIBUTE_NAME, "DuetAttributeName");
|
||||||
|
KEY_DEF(DUET_ATTRIBUTE_VALUE, "DuetAttributeValue");
|
||||||
|
KEY_DEF(DUET_RELATED_APPLICATIONS, "DuetRelatedApplications");
|
||||||
|
KEY_DEF(USES_DUET_POWER_BUDGETING, "DuetPowerBudgeting");
|
||||||
|
|
||||||
|
static const struct xpc_string_s _xpc_activity_check_in = {
|
||||||
|
.base = {
|
||||||
|
XPC_GLOBAL_OBJECT_HEADER(string),
|
||||||
|
},
|
||||||
|
.byteLength = NSUIntegerMax,
|
||||||
|
.string = "<CHECK-IN>",
|
||||||
|
.freeWhenDone = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const xpc_object_t XPC_ACTIVITY_CHECK_IN = XPC_GLOBAL_OBJECT(_xpc_activity_check_in);
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(activity)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(activity);
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_activity_register(const char *identifier, xpc_object_t criteria, xpc_activity_handler_t handler) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_activity_copy_criteria(xpc_activity_t activity) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_activity_set_criteria(xpc_activity_t activity, xpc_object_t criteria) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_activity_state_t xpc_activity_get_state(xpc_activity_t activity) {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
bool xpc_activity_set_state(xpc_activity_t activity, xpc_activity_state_t state) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
bool xpc_activity_should_defer(xpc_activity_t activity) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_activity_unregister(const char* identifier) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
struct xpc_activity_eligibility_changed_handler_s;
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
struct xpc_activity_eligibility_changed_handler_s* xpc_activity_add_eligibility_changed_handler(xpc_activity_t xactivity, void (^handler)()) {
|
||||||
|
// the return type is some kind of array or structure that must be freed
|
||||||
|
// no clue what the handler parameters are
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
dispatch_queue_t xpc_activity_copy_dispatch_queue(xpc_activity_t xactivity) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
char* xpc_activity_copy_identifier(xpc_activity_t xactivity) {
|
||||||
|
// returns a string that must be freed
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_activity_debug(const char* identifier, uint64_t flags) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_activity_defer_until_network_change() {
|
||||||
|
// not a stub
|
||||||
|
// just returns 0
|
||||||
|
// no indiciation of parameters, but probably takes an activity object as an argument
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_activity_defer_until_percentage() {
|
||||||
|
// not a stub
|
||||||
|
// just returns 0
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_activity_get_percentage() {
|
||||||
|
// not a stub
|
||||||
|
// just returns 0
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_activity_list(const char* identifier) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_activity_remove_eligibility_changed_handler(xpc_activity_t xactivity, struct xpc_activity_eligibility_changed_handler_s* handler_context) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_activity_run(const char* identifier) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_activity_set_completion_status(xpc_activity_t activity, uint64_t status) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_activity_set_network_threshold() {
|
||||||
|
// not a stub
|
||||||
|
// just does nothing
|
||||||
|
// probably has parameters, but no way to tell
|
||||||
|
};
|
339
src/array.c
339
src/array.c
@ -1,339 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2014-2015 iXsystems, Inc.
|
|
||||||
* All rights reserved
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted providing that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
||||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
||||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
||||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <mach/mach.h>
|
|
||||||
#include <xpc/launchd.h>
|
|
||||||
#include <xpc/internal.h>
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_array_create(const xpc_object_t *objects, size_t count)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
size_t i;
|
|
||||||
xpc_u val; bzero(&val, sizeof(val));
|
|
||||||
|
|
||||||
xo = _xpc_prim_create(_XPC_TYPE_ARRAY, val, 0);
|
|
||||||
|
|
||||||
for (i = 0; i < count; i++)
|
|
||||||
xpc_array_append_value(xo, objects[i]);
|
|
||||||
|
|
||||||
return (xo);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_array_set_value(xpc_object_t xarray, size_t index, xpc_object_t value)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo, *xotmp, *xotmp2;
|
|
||||||
struct xpc_array_head *arr;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
xo = xarray;
|
|
||||||
arr = &xo->xo_array;
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
if (index == XPC_ARRAY_APPEND)
|
|
||||||
return xpc_array_append_value(xarray, value);
|
|
||||||
|
|
||||||
if (index >= (size_t)xo->xo_size)
|
|
||||||
return;
|
|
||||||
|
|
||||||
TAILQ_FOREACH_SAFE(xotmp, arr, xo_link, xotmp2) {
|
|
||||||
if (i++ == index) {
|
|
||||||
TAILQ_INSERT_AFTER(arr, (struct xpc_object *)value,
|
|
||||||
xotmp, xo_link);
|
|
||||||
TAILQ_REMOVE(arr, xotmp, xo_link);
|
|
||||||
xpc_retain(value);
|
|
||||||
xpc_release(xotmp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_array_append_value(xpc_object_t xarray, xpc_object_t value)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
struct xpc_array_head *arr;
|
|
||||||
|
|
||||||
xo = xarray;
|
|
||||||
arr = &xo->xo_array;
|
|
||||||
|
|
||||||
TAILQ_INSERT_TAIL(arr, (struct xpc_object *)value, xo_link);
|
|
||||||
xpc_retain(value);
|
|
||||||
++xo->xo_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_array_get_value(xpc_object_t xarray, size_t index)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo, *xotmp;
|
|
||||||
struct xpc_array_head *arr;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
xo = xarray;
|
|
||||||
arr = &xo->xo_array;
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
if (index > xo->xo_size)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
TAILQ_FOREACH(xotmp, arr, xo_link) {
|
|
||||||
if (i++ == index)
|
|
||||||
return (xotmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
|
||||||
xpc_array_get_count(xpc_object_t xarray)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
|
|
||||||
xo = xarray;
|
|
||||||
return (xo->xo_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_array_set_bool(xpc_object_t xarray, size_t index, bool value)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo, *xotmp;
|
|
||||||
|
|
||||||
xo = xarray;
|
|
||||||
xotmp = xpc_bool_create(value);
|
|
||||||
return (xpc_array_set_value(xarray, index, xotmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_array_set_int64(xpc_object_t xarray, size_t index, int64_t value)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo, *xotmp;
|
|
||||||
|
|
||||||
xo = xarray;
|
|
||||||
xotmp = xpc_int64_create(value);
|
|
||||||
return (xpc_array_set_value(xarray, index, xotmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_array_set_uint64(xpc_object_t xarray, size_t index, uint64_t value)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo, *xotmp;
|
|
||||||
|
|
||||||
xo = xarray;
|
|
||||||
xotmp = xpc_uint64_create(value);
|
|
||||||
return (xpc_array_set_value(xarray, index, xotmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_array_set_double(xpc_object_t xarray, size_t index, double value)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo, *xotmp;
|
|
||||||
|
|
||||||
xo = xarray;
|
|
||||||
xotmp = xpc_double_create(value);
|
|
||||||
return (xpc_array_set_value(xarray, index, xotmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_array_set_date(xpc_object_t xarray, size_t index, int64_t value)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo, *xotmp;
|
|
||||||
|
|
||||||
xo = xarray;
|
|
||||||
xotmp = xpc_date_create(value);
|
|
||||||
return (xpc_array_set_value(xarray, index, xotmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_array_set_data(xpc_object_t xarray, size_t index, const void *data,
|
|
||||||
size_t length)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo, *xotmp;
|
|
||||||
|
|
||||||
xo = xarray;
|
|
||||||
xotmp = xpc_data_create(data, length);
|
|
||||||
return (xpc_array_set_value(xarray, index, xotmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_array_set_string(xpc_object_t xarray, size_t index, const char *string)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo, *xotmp;
|
|
||||||
|
|
||||||
xo = xarray;
|
|
||||||
xotmp = xpc_string_create(string);
|
|
||||||
return (xpc_array_set_value(xarray, index, xotmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_array_set_uuid(xpc_object_t xarray, size_t index, const uuid_t value)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo, *xotmp;
|
|
||||||
|
|
||||||
xo = xarray;
|
|
||||||
xotmp = xpc_uuid_create(value);
|
|
||||||
return (xpc_array_set_value(xarray, index, xotmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_array_set_fd(xpc_object_t xarray, size_t index, int value)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo, *xotmp;
|
|
||||||
|
|
||||||
xo = xarray;
|
|
||||||
xotmp = xpc_fd_create(value);
|
|
||||||
return (xpc_array_set_value(xarray, index, xotmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_array_set_connection(xpc_object_t xarray, size_t index,
|
|
||||||
xpc_connection_t value)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
xpc_array_get_bool(xpc_object_t xarray, size_t index)
|
|
||||||
{
|
|
||||||
struct xpc_object *xotmp;
|
|
||||||
|
|
||||||
xotmp = xpc_array_get_value(xarray, index);
|
|
||||||
return (xpc_bool_get_value(xotmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t
|
|
||||||
xpc_array_get_int64(xpc_object_t xarray, size_t index)
|
|
||||||
{
|
|
||||||
struct xpc_object *xotmp;
|
|
||||||
|
|
||||||
xotmp = xpc_array_get_value(xarray, index);
|
|
||||||
return (xpc_int64_get_value(xotmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t
|
|
||||||
xpc_array_get_uint64(xpc_object_t xarray, size_t index)
|
|
||||||
{
|
|
||||||
struct xpc_object *xotmp;
|
|
||||||
|
|
||||||
xotmp = xpc_array_get_value(xarray, index);
|
|
||||||
return (xpc_uint64_get_value(xotmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
double
|
|
||||||
xpc_array_get_double(xpc_object_t xarray, size_t index)
|
|
||||||
{
|
|
||||||
struct xpc_object *xotmp;
|
|
||||||
|
|
||||||
xotmp = xpc_array_get_value(xarray, index);
|
|
||||||
return (xpc_double_get_value(xotmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t
|
|
||||||
xpc_array_get_date(xpc_object_t xarray, size_t index)
|
|
||||||
{
|
|
||||||
struct xpc_object *xotmp;
|
|
||||||
|
|
||||||
xotmp = xpc_array_get_value(xarray, index);
|
|
||||||
return (xpc_date_get_value(xotmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
const void *
|
|
||||||
xpc_array_get_data(xpc_object_t xarray, size_t index, size_t *length)
|
|
||||||
{
|
|
||||||
struct xpc_object *xotmp;
|
|
||||||
|
|
||||||
xotmp = xpc_array_get_value(xarray, index);
|
|
||||||
*length = xpc_data_get_length(xotmp);
|
|
||||||
return (xpc_data_get_bytes_ptr(xotmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t *
|
|
||||||
xpc_array_get_uuid(xpc_object_t xarray, size_t index)
|
|
||||||
{
|
|
||||||
struct xpc_object *xotmp;
|
|
||||||
|
|
||||||
xotmp = xpc_array_get_value(xarray, index);
|
|
||||||
return (xpc_uuid_get_bytes(xotmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *
|
|
||||||
xpc_array_get_string(xpc_object_t xarray, size_t index)
|
|
||||||
{
|
|
||||||
struct xpc_object *xotmp;
|
|
||||||
|
|
||||||
xotmp = xpc_array_get_value(xarray, index);
|
|
||||||
return (xpc_string_get_string_ptr(xotmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
xpc_array_dup_fd(xpc_object_t array, size_t index)
|
|
||||||
{
|
|
||||||
/* XXX */
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_connection_t
|
|
||||||
xpc_array_get_connection(xpc_object_t array, size_t index)
|
|
||||||
{
|
|
||||||
/* XXX */
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
xpc_array_apply(xpc_object_t xarray, xpc_array_applier_t applier)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo, *xotmp;
|
|
||||||
struct xpc_array_head *arr;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
xo = xarray;
|
|
||||||
arr = &xo->xo_array;
|
|
||||||
|
|
||||||
TAILQ_FOREACH(xotmp, arr, xo_link) {
|
|
||||||
if (!applier(i++, xotmp))
|
|
||||||
return (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* xpc_array_get_pointer(xpc_object_t xarray, size_t index) {
|
|
||||||
struct xpc_object* xo = xpc_array_get_value(xarray, index);
|
|
||||||
if (xo)
|
|
||||||
return xpc_pointer_get_value(xo);
|
|
||||||
return NULL;
|
|
||||||
};
|
|
||||||
|
|
||||||
void xpc_array_set_pointer(xpc_object_t xarray, size_t index, void* value) {
|
|
||||||
struct xpc_object* xo = xpc_pointer_create(value);
|
|
||||||
xpc_array_set_value(xarray, index, xo);
|
|
||||||
xpc_release(xo);
|
|
||||||
};
|
|
390
src/array.m
Normal file
390
src/array.m
Normal file
@ -0,0 +1,390 @@
|
|||||||
|
#import <xpc/objects/array.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#import <xpc/private.h>
|
||||||
|
#import <xpc/connection.h>
|
||||||
|
#import <xpc/objects/mach_send.h>
|
||||||
|
#import <xpc/objects/dictionary.h>
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(array);
|
||||||
|
|
||||||
|
// the old code for xpc_array used each xpc_object as a linked list node
|
||||||
|
// and stored a head pointer, but this code uses an actual array because:
|
||||||
|
// 1. random access is constant time with arrays
|
||||||
|
// 2. xpc_arrays can only grow by appending values
|
||||||
|
// 3. xpc_arrays can never shrink or have items deleted
|
||||||
|
// 4. slightly less memory per object
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(array)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(array);
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(array);
|
||||||
|
for (NSUInteger i = 0; i < this->size; ++i) {
|
||||||
|
xpc_release_for_collection(this->array[i]);
|
||||||
|
}
|
||||||
|
free(this->array);
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (char*)xpcDescription
|
||||||
|
{
|
||||||
|
char* output = NULL;
|
||||||
|
size_t count = self.count;
|
||||||
|
|
||||||
|
if (count == 0) {
|
||||||
|
asprintf(&output, "<%s: %p> []", xpc_class_name(self), self);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
char** descriptions = malloc(sizeof(char*) * count);
|
||||||
|
|
||||||
|
size_t outputLength = 0;
|
||||||
|
outputLength += snprintf(NULL, 0, "<%s: %p> [\n", xpc_class_name(self), self);
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
char* description = self[i].xpcDescription;
|
||||||
|
descriptions[i] = xpc_description_indent(description, true);
|
||||||
|
free(description);
|
||||||
|
outputLength += snprintf(NULL, 0, "%s\n", descriptions[i]);
|
||||||
|
}
|
||||||
|
outputLength += snprintf(NULL, 0, "]");
|
||||||
|
|
||||||
|
output = malloc(outputLength + 1);
|
||||||
|
|
||||||
|
size_t offset = 0;
|
||||||
|
offset += snprintf(output + offset, outputLength + 1 - offset, "<%s: %p> [\n", xpc_class_name(self), self);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
offset += snprintf(output + offset, outputLength + 1 - offset, "%s\n", descriptions[i]);
|
||||||
|
free(descriptions[i]);
|
||||||
|
}
|
||||||
|
free(descriptions);
|
||||||
|
|
||||||
|
offset += snprintf(output + offset, outputLength + 1 - offset, "]");
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)count
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(array);
|
||||||
|
return this->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithObjects: (XPC_CLASS(object)* const*)objects count: (NSUInteger)count
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
XPC_THIS_DECL(array);
|
||||||
|
|
||||||
|
this->array = malloc(count * sizeof(XPC_CLASS(object)*));
|
||||||
|
|
||||||
|
if (!this->array) {
|
||||||
|
[self release];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->size = count;
|
||||||
|
for (NSUInteger i = 0; i < count; ++i) {
|
||||||
|
this->array[i] = xpc_retain_for_collection(XPC_CAST(object, objects[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (XPC_CLASS(object)*)objectAtIndex: (NSUInteger)index
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(array);
|
||||||
|
|
||||||
|
if (index >= this->size) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->array[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)addObject: (XPC_CLASS(object)*)object
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(array);
|
||||||
|
|
||||||
|
// TODO: check what we should actually do when someone tries to append `nil`
|
||||||
|
if (object == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
XPC_CLASS(object)** expandedArray = realloc(this->array, (this->size + 1) * sizeof(XPC_CLASS(object)*));
|
||||||
|
if (expandedArray == NULL) {
|
||||||
|
// we have no way to report errors, so just silently leave everything in its previous state
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->array = expandedArray;
|
||||||
|
|
||||||
|
this->array[this->size++] = xpc_retain_for_collection(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)replaceObjectAtIndex: (NSUInteger)index withObject: (XPC_CLASS(object)*)object
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(array);
|
||||||
|
|
||||||
|
if (index >= this->size) {
|
||||||
|
// again, no way to report errors
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
XPC_CLASS(object)* old = this->array[index];
|
||||||
|
this->array[index] = xpc_retain_for_collection(object);
|
||||||
|
[old release];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)enumerateObjectsUsingBlock: (void (^)(XPC_CLASS(object)* object, NSUInteger index, BOOL* stop))block
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(array);
|
||||||
|
|
||||||
|
for (NSUInteger i = 0; i < this->size; ++i) {
|
||||||
|
BOOL stop = NO;
|
||||||
|
block(this->array[i], i, &stop);
|
||||||
|
if (stop) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (XPC_CLASS(object)*)objectAtIndexedSubscript: (NSUInteger)index
|
||||||
|
{
|
||||||
|
return [self objectAtIndex: index];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setObject: (XPC_CLASS(object)*)object atIndexedSubscript: (NSUInteger)index
|
||||||
|
{
|
||||||
|
return [self replaceObjectAtIndex: index withObject: object];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)countByEnumeratingWithState: (NSFastEnumerationState*)state objects: (id __unsafe_unretained [])objects count: (NSUInteger)count
|
||||||
|
{
|
||||||
|
if (state->state == 0) {
|
||||||
|
XPC_THIS_DECL(array);
|
||||||
|
|
||||||
|
// note that this will only detect mutations of adding new objects, not reassigning existing ones
|
||||||
|
state->mutationsPtr = &this->size;
|
||||||
|
state->itemsPtr = this->array;
|
||||||
|
state->state = 1;
|
||||||
|
return this->size;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)hash
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(array);
|
||||||
|
NSUInteger result = 0;
|
||||||
|
|
||||||
|
for (NSUInteger i = 0; i < this->size; ++i) {
|
||||||
|
result += [this->array[i] hash];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)copy
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(array);
|
||||||
|
|
||||||
|
XPC_CLASS(array)* result = [XPC_CLASS(array) new];
|
||||||
|
|
||||||
|
for (NSUInteger i = 0; i < this->size; ++i) {
|
||||||
|
XPC_CLASS(object)* copied = [this->array[i] copy];
|
||||||
|
[result addObject: copied];
|
||||||
|
[copied release];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_array_create(const xpc_object_t* objects, size_t count) {
|
||||||
|
return [[XPC_CLASS(array) alloc] initWithObjects: (XPC_CLASS(object)* const*)objects count: count];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_array_set_value(xpc_object_t xarray, size_t index, xpc_object_t value) {
|
||||||
|
if (index == XPC_ARRAY_APPEND) {
|
||||||
|
return xpc_array_append_value(xarray, value);
|
||||||
|
}
|
||||||
|
TO_OBJC_CHECKED(array, xarray, array) {
|
||||||
|
array[index] = XPC_CAST(object, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_array_append_value(xpc_object_t xarray, xpc_object_t value) {
|
||||||
|
TO_OBJC_CHECKED(array, xarray, array) {
|
||||||
|
[array addObject: XPC_CAST(object, value)];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
size_t xpc_array_get_count(xpc_object_t xarray) {
|
||||||
|
TO_OBJC_CHECKED(array, xarray, array) {
|
||||||
|
return array.count;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_array_get_value(xpc_object_t xarray, size_t index) {
|
||||||
|
TO_OBJC_CHECKED(array, xarray, array) {
|
||||||
|
return array[index];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
bool xpc_array_apply(xpc_object_t xarray, xpc_array_applier_t applier) {
|
||||||
|
TO_OBJC_CHECKED(array, xarray, array) {
|
||||||
|
for (NSUInteger i = 0; i < array.count; ++i) {
|
||||||
|
if (!applier(i, array[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_array_apply_f(xpc_object_t xarray, void* context, xpc_array_applier_f applier) {
|
||||||
|
xpc_array_apply(xarray, ^bool (size_t index, xpc_object_t value) {
|
||||||
|
applier(index, value, context);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// setters
|
||||||
|
//
|
||||||
|
|
||||||
|
#define SIMPLE_SETTER(name, type) \
|
||||||
|
XPC_EXPORT \
|
||||||
|
void xpc_array_set_ ## name(xpc_object_t xarray, size_t index, type value) { \
|
||||||
|
TO_OBJC_CHECKED(array, xarray, array) { \
|
||||||
|
xpc_object_t object = xpc_ ## name ## _create(value); \
|
||||||
|
xpc_array_set_value(xarray, index, object); \
|
||||||
|
xpc_release(object); \
|
||||||
|
} \
|
||||||
|
};
|
||||||
|
|
||||||
|
SIMPLE_SETTER(bool, bool);
|
||||||
|
SIMPLE_SETTER(int64, int64_t);
|
||||||
|
SIMPLE_SETTER(uint64, uint64_t);
|
||||||
|
SIMPLE_SETTER(double, double);
|
||||||
|
SIMPLE_SETTER(date, int64_t);
|
||||||
|
SIMPLE_SETTER(string, const char*);
|
||||||
|
SIMPLE_SETTER(uuid, const uuid_t);
|
||||||
|
SIMPLE_SETTER(fd, int);
|
||||||
|
SIMPLE_SETTER(pointer, void*);
|
||||||
|
SIMPLE_SETTER(mach_send, mach_port_t); // private setter
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_array_set_data(xpc_object_t xarray, size_t index, const void* bytes, size_t length) {
|
||||||
|
TO_OBJC_CHECKED(array, xarray, array) {
|
||||||
|
xpc_object_t object = xpc_data_create(bytes, length);
|
||||||
|
xpc_array_set_value(xarray, index, object);
|
||||||
|
xpc_release(object);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_array_set_connection(xpc_object_t xarray, size_t index, xpc_connection_t connection) {
|
||||||
|
return xpc_array_set_value(xarray, index, connection);
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// getters
|
||||||
|
//
|
||||||
|
|
||||||
|
#define SIMPLE_GETTER(name, type) \
|
||||||
|
XPC_EXPORT \
|
||||||
|
type xpc_array_get_ ## name(xpc_object_t xarray, size_t index) { \
|
||||||
|
xpc_object_t object = xpc_array_get_value(xarray, index); \
|
||||||
|
return xpc_ ## name ## _get_value(object); \
|
||||||
|
};
|
||||||
|
|
||||||
|
SIMPLE_GETTER(bool, bool);
|
||||||
|
SIMPLE_GETTER(int64, int64_t);
|
||||||
|
SIMPLE_GETTER(uint64, uint64_t);
|
||||||
|
SIMPLE_GETTER(double, double);
|
||||||
|
SIMPLE_GETTER(date, int64_t);
|
||||||
|
SIMPLE_GETTER(pointer, void*);
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const void* xpc_array_get_data(xpc_object_t xarray, size_t index, size_t* length) {
|
||||||
|
xpc_object_t object = xpc_array_get_value(xarray, index);
|
||||||
|
if (length != NULL) {
|
||||||
|
*length = xpc_data_get_length(object);
|
||||||
|
}
|
||||||
|
return xpc_data_get_bytes_ptr(object);
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const char* xpc_array_get_string(xpc_object_t xarray, size_t index) {
|
||||||
|
xpc_object_t object = xpc_array_get_value(xarray, index);
|
||||||
|
return xpc_string_get_string_ptr(object);
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const uint8_t* xpc_array_get_uuid(xpc_object_t xarray, size_t index) {
|
||||||
|
xpc_object_t object = xpc_array_get_value(xarray, index);
|
||||||
|
return xpc_uuid_get_bytes(object);
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_array_dup_fd(xpc_object_t xarray, size_t index) {
|
||||||
|
xpc_object_t object = xpc_array_get_value(xarray, index);
|
||||||
|
return xpc_fd_dup(object);
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_connection_t xpc_array_create_connection(xpc_object_t xarray, size_t index) {
|
||||||
|
xpc_object_t object = xpc_array_get_value(xarray, index);
|
||||||
|
return xpc_connection_create_from_endpoint((xpc_endpoint_t)object);
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// private getters
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
mach_port_t xpc_array_copy_mach_send(xpc_object_t xarray, size_t index) {
|
||||||
|
xpc_object_t object = xpc_array_get_value(xarray, index);
|
||||||
|
return xpc_mach_send_copy_right(object);
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_array_get_array(xpc_object_t xarray, size_t index) {
|
||||||
|
xpc_object_t object = xpc_array_get_value(xarray, index);
|
||||||
|
TO_OBJC_CHECKED(array, object, other_array) {
|
||||||
|
return other_array;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_array_get_dictionary(xpc_object_t xarray, size_t index) {
|
||||||
|
xpc_object_t object = xpc_array_get_value(xarray, index);
|
||||||
|
TO_OBJC_CHECKED(dictionary, object, dict) {
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
};
|
138
src/base.m
Normal file
138
src/base.m
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
#import <xpc/objects/base.h>
|
||||||
|
#import <Foundation/NSZone.h>
|
||||||
|
#import <Foundation/NSString.h>
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
// the symbol alias for the base xpc_object class is named differently
|
||||||
|
XPC_EXPORT struct objc_class _xpc_type_base;
|
||||||
|
_CREATE_ALIAS(OS_OBJC_CLASS_RAW_SYMBOL_NAME(XPC_CLASS(object)), "__xpc_type_base");
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(object)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(object);
|
||||||
|
|
||||||
|
+ (instancetype)allocWithZone: (NSZone*)zone
|
||||||
|
{
|
||||||
|
return (id)_os_object_alloc_realized([self class], [self _instanceSize]);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)init
|
||||||
|
{
|
||||||
|
// we CANNOT call `-[super init]`.
|
||||||
|
// libdispatch makes `init` crash on `OS_object`s.
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (char*)xpcDescription
|
||||||
|
{
|
||||||
|
char* string = NULL;
|
||||||
|
asprintf(&string, "<%s: %p>", class_getName([self class]), self);
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString*)description
|
||||||
|
{
|
||||||
|
Class nsstring = objc_lookUpClass("NSString");
|
||||||
|
if (!nsstring) return nil;
|
||||||
|
char* desc = self.xpcDescription;
|
||||||
|
NSString* string = [nsstring stringWithUTF8String: desc];
|
||||||
|
free(desc);
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isEqual: (id)object
|
||||||
|
{
|
||||||
|
if (![object isKindOfClass: [self class]]) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
return [self hash] == [object hash];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)copy
|
||||||
|
{
|
||||||
|
return [self retain];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t (xpc_retain)(xpc_object_t object) {
|
||||||
|
return [object retain];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void (xpc_release)(xpc_object_t object) {
|
||||||
|
return [object release];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_copy(xpc_object_t object) {
|
||||||
|
return [object copy];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
bool xpc_equal(xpc_object_t object1, xpc_object_t object2) {
|
||||||
|
return [object1 isEqual: object2];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
size_t xpc_hash(xpc_object_t object) {
|
||||||
|
return [object hash];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
char* xpc_copy_description(xpc_object_t object) {
|
||||||
|
return [XPC_CAST(object, object) xpcDescription];
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
char* xpc_copy_debug_description(xpc_object_t object) {
|
||||||
|
// for now
|
||||||
|
return xpc_copy_description(object);
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
char* xpc_copy_short_description(xpc_object_t object) {
|
||||||
|
// for now
|
||||||
|
return xpc_copy_description(object);
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
char* xpc_copy_clean_description(xpc_object_t object) {
|
||||||
|
// for now
|
||||||
|
return xpc_copy_description(object);
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
char* xpc_inspect_copy_description(xpc_object_t object, xpc_object_t another_object) {
|
||||||
|
// returns a string that must be freed
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
char* xpc_inspect_copy_description_local(xpc_object_t object, xpc_object_t another_object) {
|
||||||
|
// returns a string that must be freed
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
char* xpc_inspect_copy_short_description(xpc_object_t object, xpc_object_t another_object) {
|
||||||
|
// returns a string that must be freed
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
char* xpc_inspect_copy_short_description_local(xpc_object_t object, xpc_object_t another_object) {
|
||||||
|
// returns a string that must be freed
|
||||||
|
return NULL;
|
||||||
|
};
|
127
src/bool.m
Normal file
127
src/bool.m
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
#import <xpc/objects/bool.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
#undef bool
|
||||||
|
|
||||||
|
// workaround because `xpc/xpc.h` uses `_xpc_bool_s` instead of `xpc_bool_s`
|
||||||
|
struct _xpc_bool_s {
|
||||||
|
struct xpc_bool_s the_real_thing;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(bool);
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const struct _xpc_bool_s _xpc_bool_true = {
|
||||||
|
.the_real_thing = {
|
||||||
|
.base = {
|
||||||
|
XPC_GLOBAL_OBJECT_HEADER(bool),
|
||||||
|
},
|
||||||
|
.value = true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const struct _xpc_bool_s _xpc_bool_false = {
|
||||||
|
.the_real_thing = {
|
||||||
|
.base = {
|
||||||
|
XPC_GLOBAL_OBJECT_HEADER(bool),
|
||||||
|
},
|
||||||
|
.value = false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(bool)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(bool);
|
||||||
|
|
||||||
|
- (char*)xpcDescription
|
||||||
|
{
|
||||||
|
char* output = NULL;
|
||||||
|
asprintf(&output, "<%s: %s>", xpc_class_name(self), self.value ? "TRUE" : "FALSE");
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)value
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(bool);
|
||||||
|
return this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setValue: (BOOL)value
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(bool);
|
||||||
|
this->value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithValue: (BOOL)value
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
XPC_THIS_DECL(bool);
|
||||||
|
this->value = value;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
+ (instancetype)boolForValue: (BOOL)value
|
||||||
|
{
|
||||||
|
if (value) {
|
||||||
|
return XPC_CAST(bool, &_xpc_bool_true);
|
||||||
|
} else {
|
||||||
|
return XPC_CAST(bool, &_xpc_bool_false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)hash
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(bool);
|
||||||
|
|
||||||
|
if (this->value) {
|
||||||
|
return 0xb001;
|
||||||
|
} else {
|
||||||
|
return 0x100b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#define bool _Bool
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_bool_create(bool value) {
|
||||||
|
return [XPC_CLASS(bool) boolForValue: value];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
bool xpc_bool_get_value(xpc_object_t xbool) {
|
||||||
|
#undef bool
|
||||||
|
TO_OBJC_CHECKED(bool, xbool, boolObj) {
|
||||||
|
#define bool _Bool
|
||||||
|
return boolObj.value;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t _xpc_bool_create_distinct(bool value) {
|
||||||
|
return [[XPC_CLASS(bool) alloc] initWithValue: value];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void _xpc_bool_set_value(xpc_object_t xbool, bool value) {
|
||||||
|
#undef bool
|
||||||
|
TO_OBJC_CHECKED(bool, xbool, boolObj) {
|
||||||
|
#define bool _Bool
|
||||||
|
boolObj.value = value;
|
||||||
|
}
|
||||||
|
};
|
113
src/bundle.m
Normal file
113
src/bundle.m
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#import <xpc/objects/bundle.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(bundle);
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(bundle)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(bundle);
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_bundle_copy_info_dictionary(xpc_bundle_t xbundle) {
|
||||||
|
// doesn't actually copy the dictionary; only retains it
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
char* xpc_bundle_copy_resource_path(xpc_bundle_t xbundle) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_bundle_copy_services(xpc_bundle_t xbundle) {
|
||||||
|
// doesn't actually copy the array; only retains it
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_bundle_t xpc_bundle_create(const char* path, unsigned int flags) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_bundle_t xpc_bundle_create_from_origin(unsigned int origin, const char* path) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_bundle_t xpc_bundle_create_main(void) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_bundle_get_error(xpc_bundle_t xbundle) {
|
||||||
|
// unsure about the return type
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const char* xpc_bundle_get_executable_path(xpc_bundle_t xbundle) {
|
||||||
|
// does NOT copy the path it returns
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_bundle_get_info_dictionary(xpc_bundle_t xbundle) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const char* xpc_bundle_get_path(xpc_bundle_t xbundle) {
|
||||||
|
// does NOT copy the path it returns
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
uint64_t xpc_bundle_get_property(xpc_bundle_t xbundle, unsigned int property) {
|
||||||
|
// unsure about the return type; it can actually return both integers and pointers
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_bundle_get_xpcservice_dictionary(xpc_bundle_t xbundle) {
|
||||||
|
// the dictionary is contained within the info dictionary
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_bundle_populate(xpc_bundle_t xbundle, xpc_object_t info_dictionary, xpc_object_t services_array) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_bundle_resolve(xpc_bundle_t xbundle, dispatch_queue_t queue_for_later, void* something, void* something_else) {
|
||||||
|
// no clue what `something` and `something_else` are
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_bundle_resolve_on_queue(xpc_bundle_t xbundle, dispatch_queue_t queue_for_later, dispatch_queue_t queue_for_now, void* something, void* something_else) {
|
||||||
|
// no clue what `something` and `something_else` are
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_bundle_resolve_sync(xpc_bundle_t xbundle) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_add_bundle(const char* path, unsigned int flags) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_add_bundles_for_domain(xpc_object_t domain, xpc_object_t bundles) {
|
||||||
|
|
||||||
|
};
|
20
src/coalition.m
Normal file
20
src/coalition.m
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
XPC_EXPORT const char* XPC_COALITION_INFO_KEY_BUNDLE_IDENTIFIER = "bundle_identifier";
|
||||||
|
XPC_EXPORT const char* XPC_COALITION_INFO_KEY_CID = "cid";
|
||||||
|
XPC_EXPORT const char* XPC_COALITION_INFO_KEY_NAME = "name";
|
||||||
|
XPC_EXPORT const char* XPC_COALITION_INFO_KEY_RESOURCE_USAGE_BLOB = "resource-usage-blob";
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_coalition_copy_info(uint64_t cid) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_coalition_history_pipe_async(int flags) {
|
||||||
|
return -1;
|
||||||
|
};
|
617
src/connection.c
617
src/connection.c
@ -1,617 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2014-2015 iXsystems, Inc.
|
|
||||||
* All rights reserved
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted providing that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
||||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
||||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
||||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <mach/mach.h>
|
|
||||||
#include <servers/bootstrap.h>
|
|
||||||
#include <xpc/xpc.h>
|
|
||||||
#include <machine/atomic.h>
|
|
||||||
#include <Block.h>
|
|
||||||
#include <libkern/OSAtomic.h>
|
|
||||||
#include <xpc/internal.h>
|
|
||||||
|
|
||||||
static uint64_t generate_message_id(xpc_connection_t conn) {
|
|
||||||
uint64_t num = 0;
|
|
||||||
arc4random_buf(&num, sizeof(num));
|
|
||||||
return num;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void xpc_connection_recv_message();
|
|
||||||
static void xpc_send(xpc_connection_t xconn, xpc_object_t message, uint64_t id);
|
|
||||||
|
|
||||||
static inline struct xpc_connection* conn_extract(xpc_connection_t obj)
|
|
||||||
{
|
|
||||||
struct xpc_object* o = (struct xpc_object*) obj;
|
|
||||||
return &o->xo_connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_connection_t
|
|
||||||
xpc_connection_create(const char *name, dispatch_queue_t targetq)
|
|
||||||
{
|
|
||||||
kern_return_t kr;
|
|
||||||
char *qname;
|
|
||||||
struct xpc_connection *conn;
|
|
||||||
xpc_connection_t rv;
|
|
||||||
|
|
||||||
xpc_u val = { 0 };
|
|
||||||
rv = _xpc_prim_create(_XPC_TYPE_CONNECTION, val, 0);
|
|
||||||
if (rv == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
debugf("xpc_connection_create(%s)\n", name);
|
|
||||||
|
|
||||||
conn = conn_extract(rv);
|
|
||||||
|
|
||||||
memset(conn, 0, sizeof(struct xpc_connection));
|
|
||||||
TAILQ_INIT(&conn->xc_peers);
|
|
||||||
TAILQ_INIT(&conn->xc_pending);
|
|
||||||
|
|
||||||
/* Create send queue */
|
|
||||||
asprintf(&qname, "com.ixsystems.xpc.connection.sendq.%p", conn);
|
|
||||||
conn->xc_send_queue = dispatch_queue_create(qname, NULL);
|
|
||||||
free(qname);
|
|
||||||
|
|
||||||
/* Create recv queue */
|
|
||||||
asprintf(&qname, "com.ixsystems.xpc.connection.recvq.%p", conn);
|
|
||||||
conn->xc_recv_queue = dispatch_queue_create(qname, NULL);
|
|
||||||
free(qname);
|
|
||||||
|
|
||||||
/* Create target queue */
|
|
||||||
conn->xc_target_queue = targetq ? targetq : dispatch_get_global_queue(DISPATCH_TARGET_QUEUE_DEFAULT, 0);
|
|
||||||
|
|
||||||
/* Receive queue is initially suspended */
|
|
||||||
dispatch_suspend(conn->xc_recv_queue);
|
|
||||||
|
|
||||||
/* Create local port */
|
|
||||||
kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
|
|
||||||
&conn->xc_local_port);
|
|
||||||
if (kr != KERN_SUCCESS) {
|
|
||||||
debugf("Failed to allocate port");
|
|
||||||
errno = EPERM;
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
kr = mach_port_insert_right(mach_task_self(), conn->xc_local_port,
|
|
||||||
conn->xc_local_port, MACH_MSG_TYPE_MAKE_SEND);
|
|
||||||
if (kr != KERN_SUCCESS) {
|
|
||||||
debugf("Failed to insert right");
|
|
||||||
errno = EPERM;
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
debugf("Connection obj created");
|
|
||||||
return (rv);
|
|
||||||
}
|
|
||||||
|
|
||||||
void xpc_connection_destroy(xpc_connection_t obj)
|
|
||||||
{
|
|
||||||
debugf("xpc_connection_destroy: %p", obj);
|
|
||||||
|
|
||||||
struct xpc_connection *conn = conn_extract(obj);
|
|
||||||
dispatch_release(conn->xc_send_queue);
|
|
||||||
dispatch_release(conn->xc_recv_queue);
|
|
||||||
mach_port_deallocate(mach_task_self(), conn->xc_local_port);
|
|
||||||
|
|
||||||
if (conn->xc_handler)
|
|
||||||
Block_release(conn->xc_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_connection_t
|
|
||||||
xpc_connection_create_mach_service(const char *name, dispatch_queue_t targetq,
|
|
||||||
uint64_t flags)
|
|
||||||
{
|
|
||||||
kern_return_t kr;
|
|
||||||
struct xpc_connection *conn;
|
|
||||||
xpc_connection_t rv;
|
|
||||||
debugf("Create mach service %s\n", name);
|
|
||||||
|
|
||||||
rv = xpc_connection_create(name, targetq);
|
|
||||||
if (rv == NULL)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
conn = conn_extract(rv);
|
|
||||||
conn->xc_flags = flags;
|
|
||||||
|
|
||||||
if (flags & XPC_CONNECTION_MACH_SERVICE_LISTENER) {
|
|
||||||
kr = bootstrap_check_in(bootstrap_port, name,
|
|
||||||
&conn->xc_local_port);
|
|
||||||
if (kr != KERN_SUCCESS) {
|
|
||||||
errno = EBUSY;
|
|
||||||
xpc_connection_resume(rv);
|
|
||||||
xpc_release(rv);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (rv);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp(name, "bootstrap")) {
|
|
||||||
conn->xc_remote_port = bootstrap_port;
|
|
||||||
return (rv);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Look up named mach service */
|
|
||||||
kr = bootstrap_look_up(bootstrap_port, name, &conn->xc_remote_port);
|
|
||||||
if (kr != KERN_SUCCESS) {
|
|
||||||
errno = ENOENT;
|
|
||||||
xpc_connection_resume(rv);
|
|
||||||
xpc_release(rv);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
debugf("success");
|
|
||||||
return (rv);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_connection_t
|
|
||||||
xpc_connection_create_from_endpoint(xpc_endpoint_t endpoint)
|
|
||||||
{
|
|
||||||
kern_return_t kr;
|
|
||||||
struct xpc_connection *conn;
|
|
||||||
xpc_connection_t rv;
|
|
||||||
|
|
||||||
rv = xpc_connection_create("anonymous", NULL);
|
|
||||||
if (rv == NULL)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
conn = conn_extract(rv);
|
|
||||||
conn->xc_remote_port = (mach_port_t)endpoint;
|
|
||||||
return (rv);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_connection_set_target_queue(xpc_connection_t xconn,
|
|
||||||
dispatch_queue_t targetq)
|
|
||||||
{
|
|
||||||
struct xpc_connection *conn;
|
|
||||||
|
|
||||||
debugf("xpc_connection_set_target_queue(): connection=%p", xconn);
|
|
||||||
conn = conn_extract(xconn);
|
|
||||||
conn->xc_target_queue = targetq;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_connection_set_event_handler(xpc_connection_t xconn,
|
|
||||||
xpc_handler_t handler)
|
|
||||||
{
|
|
||||||
struct xpc_connection *conn;
|
|
||||||
|
|
||||||
debugf("xpc_connection_set_event_handler(): connection=%p", xconn);
|
|
||||||
conn = conn_extract(xconn);
|
|
||||||
conn->xc_handler = (xpc_handler_t)Block_copy(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_connection_suspend(xpc_connection_t xconn)
|
|
||||||
{
|
|
||||||
struct xpc_connection *conn;
|
|
||||||
debugf("xpc_connection_suspend(): connection=%p", xconn);
|
|
||||||
|
|
||||||
conn = conn_extract(xconn);
|
|
||||||
dispatch_suspend(conn->xc_recv_source);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_connection_resume(xpc_connection_t xconn)
|
|
||||||
{
|
|
||||||
struct xpc_connection *conn;
|
|
||||||
|
|
||||||
conn = conn_extract(xconn);
|
|
||||||
debugf("xpc_connection_resume(): connection=%p, internally %p", xconn, conn);
|
|
||||||
|
|
||||||
/* Create dispatch source for top-level connection */
|
|
||||||
if (conn->xc_parent == NULL) {
|
|
||||||
conn->xc_recv_source = dispatch_source_create(
|
|
||||||
DISPATCH_SOURCE_TYPE_MACH_RECV, conn->xc_local_port, 0,
|
|
||||||
conn->xc_recv_queue);
|
|
||||||
dispatch_set_context(conn->xc_recv_source, conn);
|
|
||||||
dispatch_source_set_event_handler_f(conn->xc_recv_source,
|
|
||||||
xpc_connection_recv_message);
|
|
||||||
dispatch_resume(conn->xc_recv_source);
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch_resume(conn->xc_recv_queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_connection_send_message(xpc_connection_t xconn,
|
|
||||||
xpc_object_t message)
|
|
||||||
{
|
|
||||||
struct xpc_connection *conn;
|
|
||||||
uint64_t id;
|
|
||||||
|
|
||||||
conn = conn_extract(xconn);
|
|
||||||
|
|
||||||
xpc_object_t seq_id = xpc_dictionary_get_value(message, XPC_SEQID);
|
|
||||||
if (seq_id) {
|
|
||||||
id = xpc_uint64_get_value(seq_id);
|
|
||||||
} else {
|
|
||||||
id = generate_message_id(xconn);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_retain(message);
|
|
||||||
dispatch_async(conn->xc_send_queue, ^{
|
|
||||||
xpc_send(xconn, message, id);
|
|
||||||
xpc_release(message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_connection_send_message_with_reply(xpc_connection_t xconn,
|
|
||||||
xpc_object_t message, dispatch_queue_t targetq, xpc_handler_t handler)
|
|
||||||
{
|
|
||||||
struct xpc_connection *conn;
|
|
||||||
struct xpc_pending_call *call;
|
|
||||||
uint64_t id;
|
|
||||||
|
|
||||||
xpc_object_t seq_id = xpc_dictionary_get_value(message, XPC_SEQID);
|
|
||||||
if (seq_id) {
|
|
||||||
id = xpc_uint64_get_value(seq_id);
|
|
||||||
} else {
|
|
||||||
id = generate_message_id(xconn);
|
|
||||||
}
|
|
||||||
|
|
||||||
conn = conn_extract(xconn);
|
|
||||||
call = malloc(sizeof(struct xpc_pending_call));
|
|
||||||
call->xp_id = id;
|
|
||||||
call->xp_handler = Block_copy(handler);
|
|
||||||
call->xp_queue = targetq;
|
|
||||||
TAILQ_INSERT_TAIL(&conn->xc_pending, call, xp_link);
|
|
||||||
|
|
||||||
xpc_retain(message);
|
|
||||||
dispatch_async(conn->xc_send_queue, ^{
|
|
||||||
xpc_send(xconn, message, call->xp_id);
|
|
||||||
xpc_release(message);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_connection_send_message_with_reply_sync(xpc_connection_t conn,
|
|
||||||
xpc_object_t message)
|
|
||||||
{
|
|
||||||
__block xpc_object_t result;
|
|
||||||
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
|
|
||||||
|
|
||||||
xpc_connection_send_message_with_reply(conn, message, NULL,
|
|
||||||
^(xpc_object_t o) {
|
|
||||||
debugf("received reply");
|
|
||||||
result = o;
|
|
||||||
dispatch_semaphore_signal(sem);
|
|
||||||
});
|
|
||||||
|
|
||||||
debugf("I'm waiting (blocking) for reply");
|
|
||||||
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
|
|
||||||
return (result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_connection_send_barrier(xpc_connection_t xconn, dispatch_block_t barrier)
|
|
||||||
{
|
|
||||||
struct xpc_connection *conn;
|
|
||||||
|
|
||||||
conn = conn_extract(xconn);
|
|
||||||
dispatch_sync(conn->xc_send_queue, barrier);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_connection_cancel(xpc_connection_t connection)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *
|
|
||||||
xpc_connection_get_name(xpc_connection_t connection)
|
|
||||||
{
|
|
||||||
|
|
||||||
return ("unknown"); /* ??? */
|
|
||||||
}
|
|
||||||
|
|
||||||
uid_t
|
|
||||||
xpc_connection_get_euid(xpc_connection_t xconn)
|
|
||||||
{
|
|
||||||
struct xpc_connection *conn;
|
|
||||||
|
|
||||||
conn = conn_extract(xconn);
|
|
||||||
return (conn->xc_remote_euid);
|
|
||||||
}
|
|
||||||
|
|
||||||
gid_t
|
|
||||||
xpc_connection_get_guid(xpc_connection_t xconn)
|
|
||||||
{
|
|
||||||
struct xpc_connection *conn;
|
|
||||||
|
|
||||||
conn = conn_extract(xconn);
|
|
||||||
return (conn->xc_remote_guid);
|
|
||||||
}
|
|
||||||
|
|
||||||
pid_t
|
|
||||||
xpc_connection_get_pid(xpc_connection_t xconn)
|
|
||||||
{
|
|
||||||
struct xpc_connection *conn;
|
|
||||||
|
|
||||||
conn = conn_extract(xconn);
|
|
||||||
return (conn->xc_remote_pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
au_asid_t
|
|
||||||
xpc_connection_get_asid(xpc_connection_t xconn)
|
|
||||||
{
|
|
||||||
struct xpc_connection *conn;
|
|
||||||
|
|
||||||
conn = conn_extract(xconn);
|
|
||||||
return (conn->xc_remote_asid);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_connection_set_context(xpc_connection_t xconn, void *ctx)
|
|
||||||
{
|
|
||||||
struct xpc_connection *conn;
|
|
||||||
|
|
||||||
conn = conn_extract(xconn);
|
|
||||||
conn->xc_context = ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *
|
|
||||||
xpc_connection_get_context(xpc_connection_t xconn)
|
|
||||||
{
|
|
||||||
struct xpc_connection *conn;
|
|
||||||
|
|
||||||
conn = conn_extract(xconn);
|
|
||||||
return (conn->xc_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_connection_set_finalizer_f(xpc_connection_t connection,
|
|
||||||
xpc_finalizer_t finalizer)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_endpoint_t
|
|
||||||
xpc_endpoint_create(xpc_connection_t connection)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_main(xpc_connection_handler_t handler)
|
|
||||||
{
|
|
||||||
|
|
||||||
dispatch_main();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_transaction_begin(void)
|
|
||||||
{
|
|
||||||
vproc_transaction_begin(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_transaction_end(void)
|
|
||||||
{
|
|
||||||
vproc_transaction_end(NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xpc_connection_invalidate(xpc_connection_t xconn) {
|
|
||||||
struct xpc_pending_call* call = NULL;
|
|
||||||
struct xpc_connection* conn = conn_extract(xconn);
|
|
||||||
struct xpc_connection* peer = NULL;
|
|
||||||
|
|
||||||
// tell all outstanding waiters that the connection is now invalid
|
|
||||||
while ((call = TAILQ_FIRST(&conn->xc_pending))) {
|
|
||||||
TAILQ_REMOVE(&conn->xc_pending, call, xp_link);
|
|
||||||
if (call->xp_handler) {
|
|
||||||
xpc_handler_t handler = call->xp_handler;
|
|
||||||
dispatch_async(conn->xc_target_queue, ^{
|
|
||||||
handler((xpc_object_t)XPC_ERROR_CONNECTION_INVALID);
|
|
||||||
Block_release(handler);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
free(call);
|
|
||||||
}
|
|
||||||
|
|
||||||
// notify the main event handler
|
|
||||||
if (conn->xc_handler) {
|
|
||||||
dispatch_async(conn->xc_target_queue, ^{
|
|
||||||
conn->xc_handler((xpc_object_t)XPC_ERROR_CONNECTION_INVALID);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
xpc_send(xpc_connection_t xconn, xpc_object_t message, uint64_t id)
|
|
||||||
{
|
|
||||||
struct xpc_connection *conn;
|
|
||||||
int status;
|
|
||||||
|
|
||||||
debugf("xpc_send(): connection=%p, message=%p (type=%d), id=%d", xconn, message, ((struct xpc_object*) message)->xo_xpc_type, id);
|
|
||||||
|
|
||||||
conn = conn_extract(xconn);
|
|
||||||
status = xpc_pipe_send(message, conn->xc_remote_port,
|
|
||||||
conn->xc_local_port, id);
|
|
||||||
|
|
||||||
if (status != 0) {
|
|
||||||
debugf("send failed, kr=%d", status);
|
|
||||||
|
|
||||||
if (status == -EPIPE) {
|
|
||||||
xpc_connection_invalidate(xconn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
xpc_connection_set_credentials(struct xpc_connection *conn, audit_token_t *tok)
|
|
||||||
{
|
|
||||||
uid_t uid;
|
|
||||||
gid_t gid;
|
|
||||||
pid_t pid;
|
|
||||||
au_asid_t asid;
|
|
||||||
|
|
||||||
if (tok == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
audit_token_to_au32(*tok, NULL, &uid, &gid, NULL, NULL, &pid, &asid,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
conn->xc_remote_euid = uid;
|
|
||||||
conn->xc_remote_guid = gid;
|
|
||||||
conn->xc_remote_pid = pid;
|
|
||||||
conn->xc_remote_asid = asid;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
xpc_connection_recv_message(void *context)
|
|
||||||
{
|
|
||||||
struct xpc_pending_call *call;
|
|
||||||
struct xpc_connection *conn, *peer;
|
|
||||||
xpc_object_t result;
|
|
||||||
mach_port_t remote;
|
|
||||||
int status;
|
|
||||||
uint64_t id;
|
|
||||||
|
|
||||||
debugf("xpc_recv: connection=%p", context);
|
|
||||||
|
|
||||||
conn = context;
|
|
||||||
status = xpc_pipe_receive(conn->xc_local_port, &remote, &result, &id);
|
|
||||||
if (status != 0) {
|
|
||||||
if (status == -EPIPE) {
|
|
||||||
xpc_connection_invalidate((xpc_connection_t)conn);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
debugf("xpc_recv: message=%p, id=%d, remote=<%d>", result, id, remote);
|
|
||||||
|
|
||||||
if (conn->xc_flags & XPC_CONNECTION_MACH_SERVICE_LISTENER) {
|
|
||||||
TAILQ_FOREACH(peer, &conn->xc_peers, xc_link) {
|
|
||||||
if (remote == peer->xc_remote_port) {
|
|
||||||
// first, check if it's a reply to any message with pending replies
|
|
||||||
TAILQ_FOREACH(call, &peer->xc_pending, xp_link) {
|
|
||||||
if (call->xp_id == id) {
|
|
||||||
debugf("Found matching ID, async run handler on queue %p", peer->xc_target_queue);
|
|
||||||
if (call->xp_handler) {
|
|
||||||
dispatch_async(peer->xc_target_queue, ^{
|
|
||||||
debugf("pass to handler");
|
|
||||||
call->xp_handler(result);
|
|
||||||
Block_release(call->xp_handler);
|
|
||||||
TAILQ_REMOVE(&peer->xc_pending, call, xp_link);
|
|
||||||
free(call);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, pass it to the main handler
|
|
||||||
if (peer->xc_handler) {
|
|
||||||
dispatch_async(peer->xc_target_queue, ^{
|
|
||||||
peer->xc_handler(result);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debugf("new peer on port <%u>", remote);
|
|
||||||
|
|
||||||
/* New peer */
|
|
||||||
xpc_object_t peerx = xpc_connection_create(NULL, NULL);
|
|
||||||
peer = conn_extract(peerx);
|
|
||||||
peer->xc_parent = conn;
|
|
||||||
peer->xc_remote_port = remote;
|
|
||||||
xpc_connection_set_credentials(peer,
|
|
||||||
((struct xpc_object *)result)->xo_audit_token);
|
|
||||||
|
|
||||||
TAILQ_INSERT_TAIL(&conn->xc_peers, peer, xc_link);
|
|
||||||
|
|
||||||
if (conn->xc_handler) {
|
|
||||||
debugf("Async run conn handler");
|
|
||||||
dispatch_async(conn->xc_target_queue, ^{
|
|
||||||
debugf("Sync run conn handler");
|
|
||||||
conn->xc_handler(peerx);
|
|
||||||
debugf("Sync conn handler done");
|
|
||||||
|
|
||||||
if (peer->xc_handler) {
|
|
||||||
dispatch_async(peer->xc_target_queue, ^{
|
|
||||||
debugf("Sync result handler");
|
|
||||||
peer->xc_handler(result);
|
|
||||||
debugf("Sync result handler done");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
xpc_connection_set_credentials(conn,
|
|
||||||
((struct xpc_object *)result)->xo_audit_token);
|
|
||||||
|
|
||||||
debugf("To run client handler");
|
|
||||||
TAILQ_FOREACH(call, &conn->xc_pending, xp_link) {
|
|
||||||
if (call->xp_id == id) {
|
|
||||||
debugf("Found matching ID, async run handler on queue %p", conn->xc_target_queue);
|
|
||||||
if (call->xp_handler) {
|
|
||||||
dispatch_async(conn->xc_target_queue, ^{
|
|
||||||
debugf("pass to handler");
|
|
||||||
call->xp_handler(result);
|
|
||||||
Block_release(call->xp_handler);
|
|
||||||
TAILQ_REMOVE(&conn->xc_pending, call,
|
|
||||||
xp_link);
|
|
||||||
free(call);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conn->xc_handler) {
|
|
||||||
debugf("Using xc_handler");
|
|
||||||
dispatch_async(conn->xc_target_queue, ^{
|
|
||||||
conn->xc_handler(result);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void xpc_connection_get_audit_token(xpc_connection_t connection, audit_token_t* auditToken) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void xpc_connection_set_legacy(xpc_connection_t connection) {
|
|
||||||
puts("STUB: xpc_connection_set_legacy called");
|
|
||||||
}
|
|
||||||
|
|
||||||
void xpc_connection_set_privileged(xpc_connection_t connection) {
|
|
||||||
puts("STUB: xpc_connection_set_privileged");
|
|
||||||
}
|
|
||||||
|
|
||||||
void xpc_connection_activate(xpc_connection_t connection) {
|
|
||||||
debugf("xpc_connection_activate: %p\n", connection);
|
|
||||||
|
|
||||||
// according to the man page for `xpc_connection_create_mach_service(3)`, a call to `xpc_connection_resume` for a never-before-activated connection
|
|
||||||
// is identical to calling `xpc_connection_activate`, so logically, since we've just been using `xpc_connection_resume` in the past, we can just defer to it here
|
|
||||||
// there's not much documentation for `xpc_connection_activate`, though, but i don't think there's much else to it
|
|
||||||
xpc_connection_resume(connection);
|
|
||||||
};
|
|
244
src/connection.m
Normal file
244
src/connection.m
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
#import <xpc/objects/connection.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
#import <xpc/connection.h>
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(connection);
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(connection)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(connection);
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_connection_t xpc_connection_create(const char* name, dispatch_queue_t targetq) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_connection_t xpc_connection_create_mach_service(const char* name, dispatch_queue_t targetq, uint64_t flags) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_connection_t xpc_connection_create_from_endpoint(xpc_endpoint_t endpoint) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_set_target_queue(xpc_connection_t xconn, dispatch_queue_t targetq) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_set_event_handler(xpc_connection_t xconn, xpc_handler_t handler) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_suspend(xpc_connection_t xconn) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_resume(xpc_connection_t xconn) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_send_message(xpc_connection_t xconn, xpc_object_t message) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_send_barrier(xpc_connection_t xconn, dispatch_block_t barrier) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_send_message_with_reply(xpc_connection_t xconn, xpc_object_t message, dispatch_queue_t replyq, xpc_handler_t handler) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_connection_send_message_with_reply_sync(xpc_connection_t xconn, xpc_object_t message) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_cancel(xpc_connection_t xconn) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const char* xpc_connection_get_name(xpc_connection_t xconn) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
uid_t xpc_connection_get_euid(xpc_connection_t xconn) {
|
||||||
|
return UID_MAX;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
gid_t xpc_connection_get_egid(xpc_connection_t xconn) {
|
||||||
|
return GID_MAX;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
pid_t xpc_connection_get_pid(xpc_connection_t xconn) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
au_asid_t xpc_connection_get_asid(xpc_connection_t xconn) {
|
||||||
|
return AU_ASSIGN_ASID;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_set_context(xpc_connection_t xconn, void* context) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void* xpc_connection_get_context(xpc_connection_t xconn) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_set_finalizer_f(xpc_connection_t xconn, xpc_finalizer_t finalizer) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_set_legacy(xpc_connection_t xconn) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_set_privileged(xpc_connection_t xconn) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_activate(xpc_connection_t xconn) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_set_target_uid(xpc_connection_t xconn, uid_t uid) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_set_instance(xpc_connection_t xconn, uuid_t uuid) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_connection_copy_entitlement_value(xpc_connection_t xconn, const char* entitlement) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void _xpc_connection_set_event_handler_f(xpc_connection_t xconn, void (*handler)(xpc_object_t event, void* context)) {
|
||||||
|
// unsure about the parameters to the handler
|
||||||
|
// maybe the second parameter to the handler is actually the connection object?
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
char* xpc_connection_copy_bundle_id(xpc_connection_t xconn) {
|
||||||
|
// returns a string that must be freed
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_connection_t xpc_connection_create_listener(const char* name, dispatch_queue_t queue) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_enable_sim2host_4sim(xpc_connection_t xconn) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_enable_termination_imminent_event(xpc_connection_t xconn) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_get_audit_token(xpc_connection_t xconn, audit_token_t* out_token) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
uint8_t xpc_connection_get_bs_type(xpc_connection_t xconn) {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_get_instance(xpc_connection_t xconn, uint8_t* out_uuid) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
bool xpc_connection_is_extension(xpc_connection_t xconn) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_kill(xpc_connection_t xconn, int signal) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
|
||||||
|
void xpc_connection_send_notification(xpc_connection_t xconn, xpc_object_t details) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_set_bootstrap(xpc_connection_t xconn, xpc_object_t bootstrap) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_set_bs_type(xpc_connection_t xconn, uint8_t type) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_set_event_channel(xpc_connection_t xconn, const char* channel_name) {
|
||||||
|
// parameter 2 is a guess
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_set_non_launching(xpc_connection_t xconn, bool non_launching) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_set_oneshot_instance(xpc_connection_t xconn, const uint8_t* uuid) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_set_qos_class_fallback(xpc_connection_t xconn, dispatch_qos_class_t qos_class) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_connection_set_qos_class_floor(xpc_connection_t xconn, dispatch_qos_class_t qos_class, int relative_priority) {
|
||||||
|
|
||||||
|
};
|
141
src/data.m
Normal file
141
src/data.m
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
#import <xpc/objects/data.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#define __DISPATCH_INDIRECT__
|
||||||
|
#import <dispatch/data_private.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(data);
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(data)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(data);
|
||||||
|
|
||||||
|
- (char*)xpcDescription
|
||||||
|
{
|
||||||
|
char* output = NULL;
|
||||||
|
asprintf(&output, "<%s: %p, %zu byte%s>", xpc_class_name(self), self, self.length, self.length == 1 ? "" : "s");
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(data);
|
||||||
|
dispatch_release(this->data);
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (const void*)bytes
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(data);
|
||||||
|
return dispatch_data_get_flattened_bytes_4libxpc(this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)length
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(data);
|
||||||
|
return dispatch_data_get_size(this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithBytes: (const void*)bytes length: (NSUInteger)length
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
XPC_THIS_DECL(data);
|
||||||
|
this->data = dispatch_data_create(bytes, length, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithDispatchData: (dispatch_data_t)data
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
XPC_THIS_DECL(data);
|
||||||
|
this->data = data;
|
||||||
|
dispatch_retain(this->data);
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)getBytes: (void*)buffer length: (NSUInteger)length
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(data);
|
||||||
|
length = MIN(length, self.length);
|
||||||
|
memcpy(buffer, self.bytes, length);
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)replaceBytesWithBytes: (const void*)bytes length: (NSUInteger)length
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(data);
|
||||||
|
dispatch_release(this->data);
|
||||||
|
this->data = dispatch_data_create(bytes, length, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)hash
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(data);
|
||||||
|
__block NSUInteger result = 0;
|
||||||
|
dispatch_data_apply(this->data, ^bool (dispatch_data_t region, size_t offset, const void* buffer, size_t size) {
|
||||||
|
result += xpc_raw_data_hash(buffer, size);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_data_create(const void* bytes, size_t length) {
|
||||||
|
return [[XPC_CLASS(data) alloc] initWithBytes: bytes length: length];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_data_create_with_dispatch_data(dispatch_data_t ddata) {
|
||||||
|
return [[XPC_CLASS(data) alloc] initWithDispatchData: ddata];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
size_t xpc_data_get_length(xpc_object_t xdata) {
|
||||||
|
TO_OBJC_CHECKED(data, xdata, data) {
|
||||||
|
return data.length;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const void* xpc_data_get_bytes_ptr(xpc_object_t xdata) {
|
||||||
|
TO_OBJC_CHECKED(data, xdata, data) {
|
||||||
|
return data.bytes;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
size_t xpc_data_get_bytes(xpc_object_t xdata, void* buffer, size_t offset, size_t length) {
|
||||||
|
TO_OBJC_CHECKED(data, xdata, data) {
|
||||||
|
return [data getBytes: ((char*)buffer + offset) length: length];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void _xpc_data_set_value(xpc_object_t xdata, const void* bytes, size_t length) {
|
||||||
|
TO_OBJC_CHECKED(data, xdata, data) {
|
||||||
|
[data replaceBytesWithBytes: bytes length: length];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
size_t xpc_data_get_inline_max(xpc_object_t xdata) {
|
||||||
|
// not a stub
|
||||||
|
// just returns 0
|
||||||
|
return 0;
|
||||||
|
};
|
55
src/date.m
Normal file
55
src/date.m
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#import <xpc/objects/date.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
#import <Foundation/NSDate.h>
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
XPC_WRAPPER_CLASS_IMPL(date, int64_t, "%lld");
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_date_create(int64_t value) {
|
||||||
|
return [[XPC_CLASS(date) alloc] initWithValue: value];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_date_create_from_current(void) {
|
||||||
|
return xpc_date_create((int64_t)clock_gettime_nsec_np(CLOCK_REALTIME));
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int64_t xpc_date_get_value(xpc_object_t xdate) {
|
||||||
|
TO_OBJC_CHECKED(date, xdate, date) {
|
||||||
|
return date.value;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_date_create_absolute(int64_t value) {
|
||||||
|
return [[XPC_CLASS(date) alloc] initWithValue: (value + NSTimeIntervalSince1970) * NSEC_PER_SEC];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int64_t xpc_date_get_value_absolute(xpc_object_t xdate) {
|
||||||
|
TO_OBJC_CHECKED(date, xdate, date) {
|
||||||
|
return (date.value / NSEC_PER_SEC) - NSTimeIntervalSince1970;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
bool xpc_date_is_int64_range(xpc_object_t xdate) {
|
||||||
|
TO_OBJC_CHECKED(date, xdate, date) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
344
src/dictionary.c
344
src/dictionary.c
@ -1,344 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2014-2015 iXsystems, Inc.
|
|
||||||
* All rights reserved
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted providing that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
||||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
||||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
||||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <mach/mach.h>
|
|
||||||
#include <xpc/launchd.h>
|
|
||||||
#include <xpc/internal.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_dictionary_create(const char * const *keys, const xpc_object_t *values,
|
|
||||||
size_t count)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
size_t i;
|
|
||||||
xpc_u val = {0};
|
|
||||||
|
|
||||||
xo = _xpc_prim_create(_XPC_TYPE_DICTIONARY, val, 0);
|
|
||||||
|
|
||||||
for (i = 0; i < count; i++)
|
|
||||||
xpc_dictionary_set_value(xo, keys[i], values[i]);
|
|
||||||
|
|
||||||
return (xo);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_dictionary_create_reply(xpc_object_t original)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo, *xo_orig;
|
|
||||||
xpc_u val;
|
|
||||||
|
|
||||||
if (xpc_get_type(original) != XPC_TYPE_DICTIONARY)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
xo_orig = original;
|
|
||||||
if ((xo_orig->xo_flags & _XPC_FROM_WIRE) == 0)
|
|
||||||
{
|
|
||||||
debugf("xpc_dictionary_create_reply: not from wire!");
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
xo_orig->xo_flags &= ~_XPC_FROM_WIRE;
|
|
||||||
|
|
||||||
// possible BUG: i'm not sure if this is how Apple associates the 2 dictionaries or not
|
|
||||||
// if we want to replicate their format exactly, we need to double-check this
|
|
||||||
xpc_object_t new_dict = xpc_dictionary_create(NULL, NULL, 0);
|
|
||||||
xpc_object_t seq_id = xpc_dictionary_get_value(original, XPC_SEQID);
|
|
||||||
if (seq_id != NULL) {
|
|
||||||
xpc_dictionary_set_uint64(new_dict, XPC_SEQID, xpc_uint64_get_value(seq_id));
|
|
||||||
}
|
|
||||||
return new_dict;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_dictionary_get_audit_token(xpc_object_t xdict, audit_token_t *token)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
|
|
||||||
xo = xdict;
|
|
||||||
if (xo->xo_audit_token != NULL)
|
|
||||||
memcpy(token, xo->xo_audit_token, sizeof(*token));
|
|
||||||
}
|
|
||||||
void
|
|
||||||
xpc_dictionary_set_mach_recv(xpc_object_t xdict, const char *key, mach_port_t port)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo = xdict;
|
|
||||||
struct xpc_object *xotmp;
|
|
||||||
xpc_u val;
|
|
||||||
|
|
||||||
val.port = port;
|
|
||||||
xotmp = _xpc_prim_create(_XPC_TYPE_ENDPOINT, val, 0);
|
|
||||||
|
|
||||||
xpc_dictionary_set_value(xdict, key, xotmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_dictionary_set_mach_send(xpc_object_t xdict, const char *key, mach_port_t port)
|
|
||||||
{
|
|
||||||
struct xpc_object *xotmp;
|
|
||||||
xpc_u val;
|
|
||||||
|
|
||||||
val.port = port;
|
|
||||||
xotmp = _xpc_prim_create(_XPC_TYPE_ENDPOINT, val, 0);
|
|
||||||
|
|
||||||
xpc_dictionary_set_value(xdict, key, xotmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
mach_port_t
|
|
||||||
xpc_dictionary_copy_mach_send(xpc_object_t xdict, const char *key)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
const struct xpc_object *xotmp;
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
xo = xdict;
|
|
||||||
if (nvlist_exists_type(xo->xo_nv, key, NV_TYPE_ENDPOINT))
|
|
||||||
return (nvlist_get_number(xo->xo_nv, key));
|
|
||||||
else if (nvlist_exists_binary(xo->xo_nv, key)) {
|
|
||||||
xotmp = (void *)nvlist_get_number(xo->xo_nv, key);
|
|
||||||
return (xotmp->xo_uint);
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_dictionary_set_value(xpc_object_t xdict, const char *key, xpc_object_t value)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo, *xotmp;
|
|
||||||
struct xpc_dict_head *head;
|
|
||||||
struct xpc_dict_pair *pair;
|
|
||||||
|
|
||||||
xo = xdict;
|
|
||||||
head = &xo->xo_dict;
|
|
||||||
|
|
||||||
TAILQ_FOREACH(pair, head, xo_link) {
|
|
||||||
if (!strcmp(pair->key, key)) {
|
|
||||||
xpc_retain(value);
|
|
||||||
xpc_release(pair->value);
|
|
||||||
pair->value = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xo->xo_size++;
|
|
||||||
pair = malloc(sizeof(struct xpc_dict_pair));
|
|
||||||
size_t len = strlen(key);
|
|
||||||
char* str = malloc(len + 1);
|
|
||||||
strncpy(str, key, len);
|
|
||||||
str[len] = '\0';
|
|
||||||
pair->key = str;
|
|
||||||
pair->value = value;
|
|
||||||
TAILQ_INSERT_TAIL(&xo->xo_dict, pair, xo_link);
|
|
||||||
xpc_retain(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_dictionary_get_value(xpc_object_t xdict, const char *key)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
struct xpc_dict_head *head;
|
|
||||||
struct xpc_dict_pair *pair;
|
|
||||||
|
|
||||||
xo = xdict;
|
|
||||||
head = &xo->xo_dict;
|
|
||||||
|
|
||||||
TAILQ_FOREACH(pair, head, xo_link) {
|
|
||||||
if (!strcmp(pair->key, key))
|
|
||||||
return (pair->value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
|
||||||
xpc_dictionary_get_count(xpc_object_t xdict)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
|
|
||||||
xo = xdict;
|
|
||||||
return (xo->xo_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_dictionary_set_bool(xpc_object_t xdict, const char *key, bool value)
|
|
||||||
{;
|
|
||||||
struct xpc_object *xo, *xotmp;
|
|
||||||
|
|
||||||
xo = xdict;
|
|
||||||
xotmp = xpc_bool_create(value);
|
|
||||||
xpc_dictionary_set_value(xdict, key, xotmp);
|
|
||||||
xpc_release(xotmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_dictionary_set_int64(xpc_object_t xdict, const char *key, int64_t value)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo, *xotmp;
|
|
||||||
|
|
||||||
xo = xdict;
|
|
||||||
xotmp = xpc_int64_create(value);
|
|
||||||
xpc_dictionary_set_value(xdict, key, xotmp);
|
|
||||||
xpc_release(xotmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_dictionary_set_uint64(xpc_object_t xdict, const char *key, uint64_t value)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo, *xotmp;
|
|
||||||
|
|
||||||
xo = xdict;
|
|
||||||
xotmp = xpc_uint64_create(value);
|
|
||||||
xpc_dictionary_set_value(xdict, key, xotmp);
|
|
||||||
xpc_release(xotmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_dictionary_set_string(xpc_object_t xdict, const char *key, const char *value)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo, *xotmp;
|
|
||||||
|
|
||||||
xo = xdict;
|
|
||||||
xotmp = xpc_string_create(value);
|
|
||||||
xpc_dictionary_set_value(xdict, key, xotmp);
|
|
||||||
xpc_release(xotmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool xpc_dictionary_get_bool(xpc_object_t xdict, const char* key) {
|
|
||||||
xpc_object_t xo = xpc_dictionary_get_value(xdict, key);
|
|
||||||
if (xo)
|
|
||||||
return xpc_bool_get_value(xo);
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
int64_t xpc_dictionary_get_int64(xpc_object_t xdict, const char* key) {
|
|
||||||
xpc_object_t xo = xpc_dictionary_get_value(xdict, key);
|
|
||||||
if (xo)
|
|
||||||
return xpc_int64_get_value(xo);
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
uint64_t xpc_dictionary_get_uint64(xpc_object_t xdict, const char* key) {
|
|
||||||
xpc_object_t xo = xpc_dictionary_get_value(xdict, key);
|
|
||||||
if (xo)
|
|
||||||
return xpc_uint64_get_value(xo);
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
const char* xpc_dictionary_get_string(xpc_object_t xdict, const char* key) {
|
|
||||||
xpc_object_t xo = xpc_dictionary_get_value(xdict, key);
|
|
||||||
if (xo)
|
|
||||||
return xpc_string_get_string_ptr(xo);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
double xpc_dictionary_get_double(xpc_object_t xdict, const char* key) {
|
|
||||||
xpc_object_t xo = xpc_dictionary_get_value(xdict, key);
|
|
||||||
if (xo)
|
|
||||||
return xpc_double_get_value(xo);
|
|
||||||
return NAN;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool
|
|
||||||
xpc_dictionary_apply(xpc_object_t xdict, xpc_dictionary_applier_t applier)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo, *xotmp;
|
|
||||||
struct xpc_dict_head *head;
|
|
||||||
struct xpc_dict_pair *pair;
|
|
||||||
|
|
||||||
xo = xdict;
|
|
||||||
head = &xo->xo_dict;
|
|
||||||
|
|
||||||
TAILQ_FOREACH(pair, head, xo_link) {
|
|
||||||
if (!applier(pair->key, pair->value))
|
|
||||||
return (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const void* xpc_dictionary_get_data(xpc_object_t xdict, const char* key, size_t* length) {
|
|
||||||
xpc_object_t xo = xpc_dictionary_get_value(xdict, key);
|
|
||||||
*length = xpc_data_get_length(xo);
|
|
||||||
return xpc_data_get_bytes_ptr(xo);
|
|
||||||
};
|
|
||||||
|
|
||||||
void xpc_dictionary_set_data(xpc_object_t xdict, const char* key, const void* bytes, size_t length) {
|
|
||||||
xpc_object_t xo = xpc_data_create(bytes, length);
|
|
||||||
xpc_dictionary_set_value(xdict, key, xo);
|
|
||||||
xpc_release(xo);
|
|
||||||
};
|
|
||||||
|
|
||||||
xpc_connection_t
|
|
||||||
xpc_dictionary_get_remote_connection(xpc_object_t xdict)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
xpc_dictionary_dup_fd(xpc_object_t xdict, const char *key)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_dictionary_set_fd(xpc_object_t xdict, const char *key, int fd)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void xpc_dictionary_set_double(xpc_object_t xdict, const char* key, double value) {
|
|
||||||
xpc_object_t xo = xpc_double_create(value);
|
|
||||||
xpc_dictionary_set_value(xdict, key, xo);
|
|
||||||
xpc_release(xo);
|
|
||||||
};
|
|
||||||
|
|
||||||
const uint8_t *
|
|
||||||
xpc_dictionary_get_uuid(xpc_object_t xdict, const char *key)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t xpc_dictionary_get_dictionary(xpc_object_t xdict, const char* key) {
|
|
||||||
xpc_object_t xo = xpc_dictionary_get_value(xdict, key);
|
|
||||||
if (xo && xpc_get_type(xo) == XPC_TYPE_DICTIONARY)
|
|
||||||
return xo;
|
|
||||||
return NULL;
|
|
||||||
};
|
|
||||||
|
|
||||||
void* xpc_dictionary_get_pointer(xpc_object_t xdict, const char* key) {
|
|
||||||
xpc_object_t xo = xpc_dictionary_get_value(xdict, key);
|
|
||||||
if (xo)
|
|
||||||
return xpc_pointer_get_value(xo);
|
|
||||||
return NULL;
|
|
||||||
};
|
|
||||||
|
|
||||||
void xpc_dictionary_set_pointer(xpc_object_t xdict, const char* key, void* value) {
|
|
||||||
xpc_object_t xo = xpc_pointer_create(value);
|
|
||||||
xpc_dictionary_set_value(xdict, key, xo);
|
|
||||||
xpc_release(xo);
|
|
||||||
};
|
|
533
src/dictionary.m
Normal file
533
src/dictionary.m
Normal file
@ -0,0 +1,533 @@
|
|||||||
|
#import <xpc/objects/dictionary.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#import <xpc/private.h>
|
||||||
|
#import <xpc/connection.h>
|
||||||
|
#import <xpc/objects/string.h>
|
||||||
|
#import <xpc/objects/mach_send.h>
|
||||||
|
#import <xpc/objects/mach_recv.h>
|
||||||
|
#import <xpc/objects/array.h>
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(dictionary);
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(dictionary)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(dictionary);
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(dictionary);
|
||||||
|
while (!LIST_EMPTY(&this->head)) {
|
||||||
|
xpc_dictionary_entry_t entry = LIST_FIRST(&this->head);
|
||||||
|
[self removeEntry: entry];
|
||||||
|
xpc_release_for_collection(entry->object);
|
||||||
|
free(entry);
|
||||||
|
}
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (char*)xpcDescription
|
||||||
|
{
|
||||||
|
char* output = NULL;
|
||||||
|
size_t count = self.count;
|
||||||
|
|
||||||
|
if (count == 0) {
|
||||||
|
asprintf(&output, "<%s: %p> {}", xpc_class_name(self), self);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
char** descriptions = malloc(sizeof(char*) * count);
|
||||||
|
|
||||||
|
size_t outputLength = 0;
|
||||||
|
outputLength += snprintf(NULL, 0, "<%s: %p> {\n", xpc_class_name(self), self);
|
||||||
|
|
||||||
|
XPC_THIS_DECL(dictionary);
|
||||||
|
xpc_dictionary_entry_t current = LIST_FIRST(&this->head);
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
char* description = current->object.xpcDescription;
|
||||||
|
descriptions[i] = xpc_description_indent(description, false);
|
||||||
|
free(description);
|
||||||
|
outputLength += snprintf(NULL, 0, "\t%s: %s\n", current->name, descriptions[i]);
|
||||||
|
current = LIST_NEXT(current, link);
|
||||||
|
}
|
||||||
|
|
||||||
|
outputLength += snprintf(NULL, 0, "}");
|
||||||
|
|
||||||
|
output = malloc(outputLength + 1);
|
||||||
|
|
||||||
|
size_t offset = 0;
|
||||||
|
offset += snprintf(output + offset, outputLength + 1 - offset, "<%s: %p> {\n", xpc_class_name(self), self);
|
||||||
|
|
||||||
|
current = LIST_FIRST(&this->head);
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
offset += snprintf(output + offset, outputLength + 1 - offset, "\t%s: %s\n", current->name, descriptions[i]);
|
||||||
|
free(descriptions[i]);
|
||||||
|
current = LIST_NEXT(current, link);
|
||||||
|
}
|
||||||
|
free(descriptions);
|
||||||
|
|
||||||
|
offset += snprintf(output + offset, outputLength + 1 - offset, "}");
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)init
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
XPC_THIS_DECL(dictionary);
|
||||||
|
LIST_INIT(&this->head);
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithObjects: (XPC_CLASS(object)* const*)objects forKeys: (const char* const*)keys count: (NSUInteger)count
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
XPC_THIS_DECL(dictionary);
|
||||||
|
LIST_INIT(&this->head);
|
||||||
|
|
||||||
|
for (NSUInteger i = 0; i < count; ++i) {
|
||||||
|
[self setObject: objects[i] forKey: keys[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (xpc_dictionary_entry_t)entryForKey: (const char*)key
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(dictionary);
|
||||||
|
xpc_dictionary_entry_t entry = NULL;
|
||||||
|
|
||||||
|
LIST_FOREACH(entry, &this->head, link) {
|
||||||
|
if (strcmp(entry->name, key) == 0) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)addEntry: (xpc_dictionary_entry_t)entry
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(dictionary);
|
||||||
|
LIST_INSERT_HEAD(&this->head, entry, link);
|
||||||
|
++this->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)removeEntry: (xpc_dictionary_entry_t)entry
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(dictionary);
|
||||||
|
LIST_REMOVE(entry, link);
|
||||||
|
--this->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)count
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(dictionary);
|
||||||
|
return this->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (XPC_CLASS(connection)*)associatedConnection
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(dictionary);
|
||||||
|
return this->associatedConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setAssociatedConnection: (XPC_CLASS(connection)*)associatedConnection
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(dictionary);
|
||||||
|
XPC_CLASS(connection)* old = this->associatedConnection;
|
||||||
|
this->associatedConnection = [associatedConnection retain];
|
||||||
|
[old release];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (XPC_CLASS(object)*)objectForKey: (const char*)key
|
||||||
|
{
|
||||||
|
xpc_dictionary_entry_t entry = [self entryForKey: key];
|
||||||
|
if (entry == NULL) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
return entry->object;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setObject: (XPC_CLASS(object)*)object forKey: (const char*)key
|
||||||
|
{
|
||||||
|
if (object == nil) {
|
||||||
|
return [self removeObjectForKey: key];
|
||||||
|
}
|
||||||
|
|
||||||
|
xpc_dictionary_entry_t entry = [self entryForKey: key];
|
||||||
|
|
||||||
|
if (entry != NULL) {
|
||||||
|
XPC_CLASS(object)* old = entry->object;
|
||||||
|
entry->object = xpc_retain_for_collection(object);
|
||||||
|
[old release];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t keyLength = strlen(key);
|
||||||
|
entry = malloc(sizeof(struct xpc_dictionary_entry_s) + keyLength + 1);
|
||||||
|
|
||||||
|
if (entry == NULL) {
|
||||||
|
// no way to report errors
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->object = xpc_retain_for_collection(object);
|
||||||
|
strlcpy(entry->name, key, keyLength + 1);
|
||||||
|
[self addEntry: entry];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)removeObjectForKey: (const char*)key
|
||||||
|
{
|
||||||
|
xpc_dictionary_entry_t entry = [self entryForKey: key];
|
||||||
|
|
||||||
|
if (entry == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[self removeEntry: entry];
|
||||||
|
|
||||||
|
free(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)enumerateKeysAndObjectsUsingBlock: (void (^)(const char* key, XPC_CLASS(object)* obj, BOOL* stop))block
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(dictionary);
|
||||||
|
xpc_dictionary_entry_t entry = NULL;
|
||||||
|
|
||||||
|
LIST_FOREACH(entry, &this->head, link) {
|
||||||
|
BOOL stop = NO;
|
||||||
|
block(entry->name, entry->object, &stop);
|
||||||
|
if (stop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (XPC_CLASS(string)*)stringForKey: (const char*)key
|
||||||
|
{
|
||||||
|
XPC_CLASS(object)* object = [self objectForKey: key];
|
||||||
|
TO_OBJC_CHECKED(string, object, string) {
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)hash
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(dictionary);
|
||||||
|
NSUInteger result = 0;
|
||||||
|
xpc_dictionary_entry_t entry = NULL;
|
||||||
|
|
||||||
|
LIST_FOREACH(entry, &this->head, link) {
|
||||||
|
result += [entry->object hash];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)copy
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(dictionary);
|
||||||
|
|
||||||
|
XPC_CLASS(dictionary)* result = [XPC_CLASS(dictionary) new];
|
||||||
|
xpc_dictionary_entry_t entry = NULL;
|
||||||
|
|
||||||
|
LIST_FOREACH(entry, &this->head, link) {
|
||||||
|
XPC_CLASS(object)* copied = [entry->object copy];
|
||||||
|
[result setObject: copied forKey: entry->name];
|
||||||
|
[copied release];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_dictionary_create(const char* const* keys, const xpc_object_t* values, size_t count) {
|
||||||
|
return [[XPC_CLASS(dictionary) alloc] initWithObjects: (XPC_CLASS(object)* const*)values forKeys: keys count: count];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_dictionary_create_reply(xpc_object_t original) {
|
||||||
|
// TODO
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_dictionary_set_value(xpc_object_t xdict, const char* key, xpc_object_t value) {
|
||||||
|
TO_OBJC_CHECKED(dictionary, xdict, dict) {
|
||||||
|
[dict setObject: XPC_CAST(object, value) forKey: key];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_dictionary_get_value(xpc_object_t xdict, const char* key) {
|
||||||
|
TO_OBJC_CHECKED(dictionary, xdict, dict) {
|
||||||
|
return [dict objectForKey: key];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
size_t xpc_dictionary_get_count(xpc_object_t xdict) {
|
||||||
|
TO_OBJC_CHECKED(dictionary, xdict, dict) {
|
||||||
|
return dict.count;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
bool xpc_dictionary_apply(xpc_object_t xdict, xpc_dictionary_applier_t applier) {
|
||||||
|
TO_OBJC_CHECKED(dictionary, xdict, dict) {
|
||||||
|
__block BOOL stoppedEarly = NO;
|
||||||
|
[dict enumerateKeysAndObjectsUsingBlock: ^(const char* key, XPC_CLASS(object)* value, BOOL* stop) {
|
||||||
|
if (!applier(key, value)) {
|
||||||
|
stoppedEarly = *stop = YES;
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
return !stoppedEarly;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_connection_t xpc_dictionary_get_remote_connection(xpc_object_t xdict) {
|
||||||
|
TO_OBJC_CHECKED(dictionary, xdict, dict) {
|
||||||
|
return dict.associatedConnection;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_dictionary_get_audit_token(xpc_object_t xdict, audit_token_t* token) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t _xpc_dictionary_create_reply_with_port(mach_port_t port) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
mach_msg_id_t _xpc_dictionary_extract_reply_msg_id(xpc_object_t xdict) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
mach_port_t _xpc_dictionary_extract_reply_port(xpc_object_t xdict) {
|
||||||
|
return MACH_PORT_NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
mach_msg_id_t _xpc_dictionary_get_reply_msg_id(xpc_object_t xdict) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void _xpc_dictionary_set_remote_connection(xpc_object_t xdict, xpc_connection_t xconn) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void _xpc_dictionary_set_reply_msg_id(xpc_object_t xdict, mach_msg_id_t msg_id) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_dictionary_apply_f(xpc_object_t xdict, void* context, xpc_dictionary_applier_f applier) {
|
||||||
|
xpc_dictionary_apply(xdict, ^bool (const char* key, xpc_object_t value) {
|
||||||
|
// can't stop it early
|
||||||
|
applier(key, value, context);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
char* xpc_dictionary_copy_basic_description(xpc_object_t xdict) {
|
||||||
|
// returns a string that must freed
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
bool xpc_dictionary_expects_reply(xpc_object_t xdict) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_dictionary_handoff_reply(xpc_object_t xdict, dispatch_queue_t queue, dispatch_block_t block) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_dictionary_handoff_reply_f(xpc_object_t xdict, dispatch_queue_t queue, void* context, dispatch_function_t function) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_dictionary_send_reply(xpc_object_t xdict) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// setters
|
||||||
|
//
|
||||||
|
|
||||||
|
#define SIMPLE_SETTER(name, type) \
|
||||||
|
XPC_EXPORT \
|
||||||
|
void xpc_dictionary_set_ ## name(xpc_object_t xdict, const char* key, type value) { \
|
||||||
|
TO_OBJC_CHECKED(dictionary, xdict, dict) { \
|
||||||
|
xpc_object_t object = xpc_ ## name ## _create(value); \
|
||||||
|
xpc_dictionary_set_value(xdict, key, object); \
|
||||||
|
xpc_release(object); \
|
||||||
|
} \
|
||||||
|
};
|
||||||
|
|
||||||
|
SIMPLE_SETTER(bool, bool);
|
||||||
|
SIMPLE_SETTER(int64, int64_t);
|
||||||
|
SIMPLE_SETTER(uint64, uint64_t);
|
||||||
|
SIMPLE_SETTER(double, double);
|
||||||
|
SIMPLE_SETTER(date, int64_t);
|
||||||
|
SIMPLE_SETTER(string, const char*);
|
||||||
|
SIMPLE_SETTER(uuid, const uuid_t);
|
||||||
|
SIMPLE_SETTER(fd, int);
|
||||||
|
SIMPLE_SETTER(pointer, void*);
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_dictionary_set_data(xpc_object_t xdict, const char* key, const void* bytes, size_t length) {
|
||||||
|
TO_OBJC_CHECKED(dictionary, xdict, dict) {
|
||||||
|
xpc_object_t object = xpc_data_create(bytes, length);
|
||||||
|
xpc_dictionary_set_value(xdict, key, object);
|
||||||
|
xpc_release(object);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_dictionary_set_connection(xpc_object_t xdict, const char* key, xpc_connection_t connection) {
|
||||||
|
return xpc_dictionary_set_value(xdict, key, connection);
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_dictionary_set_mach_send(xpc_object_t xdict, const char* key, mach_port_t port) {
|
||||||
|
xpc_object_t object = xpc_mach_send_create(port);
|
||||||
|
xpc_dictionary_set_value(xdict, key, object);
|
||||||
|
xpc_release(object);
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_dictionary_set_mach_recv(xpc_object_t xdict, const char* key, mach_port_t recv) {
|
||||||
|
xpc_object_t object = xpc_mach_recv_create(recv);
|
||||||
|
xpc_dictionary_set_value(xdict, key, object);
|
||||||
|
xpc_release(object);
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// getters
|
||||||
|
//
|
||||||
|
|
||||||
|
#define SIMPLE_GETTER(name, type) \
|
||||||
|
XPC_EXPORT \
|
||||||
|
type xpc_dictionary_get_ ## name(xpc_object_t xdict, const char* key) { \
|
||||||
|
xpc_object_t object = xpc_dictionary_get_value(xdict, key); \
|
||||||
|
return xpc_ ## name ## _get_value(object); \
|
||||||
|
};
|
||||||
|
|
||||||
|
SIMPLE_GETTER(bool, bool);
|
||||||
|
SIMPLE_GETTER(int64, int64_t);
|
||||||
|
SIMPLE_GETTER(uint64, uint64_t);
|
||||||
|
SIMPLE_GETTER(double, double);
|
||||||
|
SIMPLE_GETTER(date, int64_t);
|
||||||
|
SIMPLE_GETTER(pointer, void*);
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const void* xpc_dictionary_get_data(xpc_object_t xdict, const char* key, size_t* length) {
|
||||||
|
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
|
||||||
|
if (length != NULL) {
|
||||||
|
*length = xpc_data_get_length(object);
|
||||||
|
}
|
||||||
|
return xpc_data_get_bytes_ptr(object);
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const char* xpc_dictionary_get_string(xpc_object_t xdict, const char* key) {
|
||||||
|
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
|
||||||
|
return xpc_string_get_string_ptr(object);
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const uint8_t* xpc_dictionary_get_uuid(xpc_object_t xdict, const char* key) {
|
||||||
|
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
|
||||||
|
return xpc_uuid_get_bytes(object);
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_dictionary_dup_fd(xpc_object_t xdict, const char* key) {
|
||||||
|
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
|
||||||
|
return xpc_fd_dup(object);
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_connection_t xpc_dictionary_create_connection(xpc_object_t xdict, const char* key) {
|
||||||
|
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
|
||||||
|
return xpc_connection_create_from_endpoint((xpc_endpoint_t)object);
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_dictionary_get_dictionary(xpc_object_t xdict, const char* key) {
|
||||||
|
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
|
||||||
|
TO_OBJC_CHECKED(dictionary, object, dict) {
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
mach_port_t _xpc_dictionary_extract_mach_send(xpc_object_t xdict, const char* key) {
|
||||||
|
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
|
||||||
|
TO_OBJC_CHECKED(mach_send, object, send) {
|
||||||
|
return xpc_mach_send_extract_right(send);
|
||||||
|
}
|
||||||
|
return MACH_PORT_NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
mach_port_t xpc_dictionary_copy_mach_send(xpc_object_t xdict, const char* key) {
|
||||||
|
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
|
||||||
|
TO_OBJC_CHECKED(mach_send, object, send) {
|
||||||
|
return xpc_mach_send_copy_right(send);
|
||||||
|
}
|
||||||
|
return MACH_PORT_NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
mach_port_t xpc_dictionary_extract_mach_recv(xpc_object_t xdict, const char* key) {
|
||||||
|
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
|
||||||
|
TO_OBJC_CHECKED(mach_recv, object, recv) {
|
||||||
|
return xpc_mach_recv_extract_right(recv);
|
||||||
|
}
|
||||||
|
return MACH_PORT_NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_dictionary_get_array(xpc_object_t xdict, const char* key) {
|
||||||
|
xpc_object_t object = xpc_dictionary_get_value(xdict, key);
|
||||||
|
TO_OBJC_CHECKED(array, object, array) {
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_connection_t xpc_dictionary_get_connection(xpc_object_t xdict) {
|
||||||
|
return xpc_dictionary_get_remote_connection(xdict);
|
||||||
|
};
|
35
src/double.m
Normal file
35
src/double.m
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#import <xpc/objects/double.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
XPC_WRAPPER_CLASS_IMPL(double, double, "%f");
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_double_create(double value) {
|
||||||
|
return [[XPC_CLASS(double) alloc] initWithValue: value];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
double xpc_double_get_value(xpc_object_t xdouble) {
|
||||||
|
TO_OBJC_CHECKED(double, xdouble, doubleObj) {
|
||||||
|
return doubleObj.value;
|
||||||
|
}
|
||||||
|
return NAN;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void _xpc_double_set_value(xpc_object_t xdouble, double value) {
|
||||||
|
TO_OBJC_CHECKED(double, xdouble, doubleObj) {
|
||||||
|
doubleObj.value = value;
|
||||||
|
}
|
||||||
|
};
|
54
src/endpoint.m
Normal file
54
src/endpoint.m
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#import <xpc/objects/endpoint.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(endpoint);
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(endpoint)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(endpoint);
|
||||||
|
|
||||||
|
- (instancetype)initWithConnection: (XPC_CLASS(connection)*)connection
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
#warning TODO: -[XPC_CLASS(endpoint) initWithConnection:]
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_endpoint_t xpc_endpoint_create(xpc_connection_t xconn) {
|
||||||
|
return [[XPC_CLASS(endpoint) alloc] initWithConnection: XPC_CAST(connection, xconn)];
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_endpoint_compare(xpc_endpoint_t lhs, xpc_endpoint_t rhs) {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
mach_port_t xpc_endpoint_copy_listener_port_4sim(xpc_endpoint_t xendpoint) {
|
||||||
|
return MACH_PORT_NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_endpoint_t xpc_endpoint_create_bs_named(const char* name, uint64_t flags, uint8_t* out_type) {
|
||||||
|
// parameter 3's purpose is a guess
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_endpoint_t xpc_endpoint_create_mach_port_4sim(mach_port_t port) {
|
||||||
|
return NULL;
|
||||||
|
};
|
151
src/error.c
151
src/error.c
@ -1,151 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of Darling.
|
|
||||||
|
|
||||||
Copyright (C) 2017 Darling developers
|
|
||||||
|
|
||||||
Darling is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Darling is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <xpc/xpc.h>
|
|
||||||
#include <xpc/internal.h>
|
|
||||||
|
|
||||||
/* The only key for XPC_ERROR_* dictionaries */
|
|
||||||
#define _XPC_ERROR_KEY_DESCRIPTION_STR "XPCErrorDescription"
|
|
||||||
const char *const _xpc_error_key_description = _XPC_ERROR_KEY_DESCRIPTION_STR;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* XPC_ERROR_* constants are declared to be of type `struct _xpc_dictionary_s`
|
|
||||||
* in Apple's <xpc/connection.h>, yet we want `struct _xpc_dictionary_s *` to
|
|
||||||
* be interchangable with `struct xpc_object *`, because both types get passed
|
|
||||||
* as opaque pointers of type `xpc_object_t` to various XPC functions.
|
|
||||||
* Thankfully, `struct _xpc_dictionary_s` isn't used anywhere else, so we can
|
|
||||||
* make it a simple wrapper.
|
|
||||||
*/
|
|
||||||
struct _xpc_dictionary_s {
|
|
||||||
struct xpc_object inner;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We cannot initialize these structures using any function because they are
|
|
||||||
* declared as `const` in Apple's <xpc/connection.h>
|
|
||||||
* See <sys/queue.h> for TAILQ_ENTRY & TAILQ_HEAD structure details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* XPC_ERROR_CONNECTION_INTERRUPTED */
|
|
||||||
|
|
||||||
const struct _xpc_dictionary_s _xpc_error_connection_interrupted;
|
|
||||||
|
|
||||||
static const struct xpc_object _xpc_error_connection_interrupted_val = {
|
|
||||||
.xo_xpc_type = _XPC_TYPE_STRING,
|
|
||||||
.xo_size = 22, /* strlen("Connection interrupted") */
|
|
||||||
.xo_refcnt = 1,
|
|
||||||
.xo_u = {
|
|
||||||
.str = "Connection interrupted"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct xpc_dict_pair _xpc_error_connection_interrupted_pair = {
|
|
||||||
.key = _XPC_ERROR_KEY_DESCRIPTION_STR,
|
|
||||||
.value = &_xpc_error_connection_interrupted_val,
|
|
||||||
.xo_link = {
|
|
||||||
.tqe_next = NULL,
|
|
||||||
.tqe_prev = &_xpc_error_connection_interrupted.inner.xo_u.dict.tqh_first
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct _xpc_dictionary_s _xpc_error_connection_interrupted = {
|
|
||||||
.inner = {
|
|
||||||
.xo_xpc_type = _XPC_TYPE_DICTIONARY,
|
|
||||||
.xo_size = 1,
|
|
||||||
.xo_refcnt = 1,
|
|
||||||
.xo_u = {
|
|
||||||
.dict = {
|
|
||||||
.tqh_first = &_xpc_error_connection_interrupted_pair,
|
|
||||||
.tqh_last = &_xpc_error_connection_interrupted_pair.xo_link.tqe_next
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* XPC_ERROR_CONNECTION_INVALID */
|
|
||||||
|
|
||||||
const struct _xpc_dictionary_s _xpc_error_connection_invalid;
|
|
||||||
|
|
||||||
static const struct xpc_object _xpc_error_connection_invalid_val = {
|
|
||||||
.xo_xpc_type = _XPC_TYPE_STRING,
|
|
||||||
.xo_size = 18, /* strlen("Connection invalid") */
|
|
||||||
.xo_refcnt = 1,
|
|
||||||
.xo_u = {
|
|
||||||
.str = "Connection invalid"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct xpc_dict_pair _xpc_error_connection_invalid_pair = {
|
|
||||||
.key = _XPC_ERROR_KEY_DESCRIPTION_STR,
|
|
||||||
.value = &_xpc_error_connection_invalid_val,
|
|
||||||
.xo_link = {
|
|
||||||
.tqe_next = NULL,
|
|
||||||
.tqe_prev = &_xpc_error_connection_invalid.inner.xo_u.dict.tqh_first
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct _xpc_dictionary_s _xpc_error_connection_invalid = {
|
|
||||||
.inner = {
|
|
||||||
.xo_xpc_type = _XPC_TYPE_DICTIONARY,
|
|
||||||
.xo_size = 1,
|
|
||||||
.xo_refcnt = 1,
|
|
||||||
.xo_u = {
|
|
||||||
.dict = {
|
|
||||||
.tqh_first = &_xpc_error_connection_invalid_pair,
|
|
||||||
.tqh_last = &_xpc_error_connection_invalid_pair.xo_link.tqe_next
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* XPC_ERROR_TERMINATION_IMMINENT */
|
|
||||||
|
|
||||||
const struct _xpc_dictionary_s _xpc_error_termination_imminent;
|
|
||||||
|
|
||||||
static const struct xpc_object _xpc_error_termination_imminent_val = {
|
|
||||||
.xo_xpc_type = _XPC_TYPE_STRING,
|
|
||||||
.xo_size = 20, /* strlen("Termination imminent") */
|
|
||||||
.xo_refcnt = 1,
|
|
||||||
.xo_u = {
|
|
||||||
.str = "Termination imminent"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct xpc_dict_pair _xpc_error_termination_imminent_pair = {
|
|
||||||
.key = _XPC_ERROR_KEY_DESCRIPTION_STR,
|
|
||||||
.value = &_xpc_error_termination_imminent_val,
|
|
||||||
.xo_link = {
|
|
||||||
.tqe_next = NULL,
|
|
||||||
.tqe_prev = &_xpc_error_termination_imminent.inner.xo_u.dict.tqh_first
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct _xpc_dictionary_s _xpc_error_termination_imminent = {
|
|
||||||
.inner = {
|
|
||||||
.xo_xpc_type = _XPC_TYPE_DICTIONARY,
|
|
||||||
.xo_size = 1,
|
|
||||||
.xo_refcnt = 1,
|
|
||||||
.xo_u = {
|
|
||||||
.dict = {
|
|
||||||
.tqh_first = &_xpc_error_termination_imminent_pair,
|
|
||||||
.tqh_last = &_xpc_error_termination_imminent_pair.xo_link.tqe_next
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
73
src/error.m
Normal file
73
src/error.m
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#import <xpc/objects/error.h>
|
||||||
|
#import <xpc/objects/string.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(error);
|
||||||
|
|
||||||
|
#define XPCErrorDescriptionKey "XPCErrorDescription"
|
||||||
|
const char* const _xpc_error_key_description = XPCErrorDescriptionKey;
|
||||||
|
|
||||||
|
// workaround like in `bool.m`
|
||||||
|
struct _xpc_dictionary_s {
|
||||||
|
struct xpc_error_s the_real_thing;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define XPC_ERROR_DEFINITION(_name, _description) \
|
||||||
|
struct xpc_string_s _xpc_error_ ## _name ## _entry_string = { \
|
||||||
|
.base = { \
|
||||||
|
XPC_GLOBAL_OBJECT_HEADER(string), \
|
||||||
|
}, \
|
||||||
|
.byteLength = NSUIntegerMax, \
|
||||||
|
.string = _description, \
|
||||||
|
.freeWhenDone = false \
|
||||||
|
}; \
|
||||||
|
extern struct _xpc_dictionary_s _xpc_error_ ## _name; \
|
||||||
|
struct xpc_dictionary_entry_s _xpc_error_ ## _name ## _entry = { \
|
||||||
|
.link = { \
|
||||||
|
.le_next = NULL, \
|
||||||
|
.le_prev = &LIST_FIRST(&_xpc_error_ ## _name.the_real_thing.base.head), \
|
||||||
|
}, \
|
||||||
|
.object = XPC_CAST(string, &_xpc_error_ ## _name ## _entry_string), \
|
||||||
|
.name = XPCErrorDescriptionKey, \
|
||||||
|
}; \
|
||||||
|
XPC_EXPORT struct _xpc_dictionary_s _xpc_error_ ## _name = { \
|
||||||
|
.the_real_thing = { \
|
||||||
|
.base = { \
|
||||||
|
.base = { \
|
||||||
|
XPC_GLOBAL_OBJECT_HEADER(error), \
|
||||||
|
}, \
|
||||||
|
.size = 1, \
|
||||||
|
.head = { \
|
||||||
|
.lh_first = &_xpc_error_ ## _name ## _entry, \
|
||||||
|
}, \
|
||||||
|
}, \
|
||||||
|
}, \
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_ERROR_DEFINITION(connection_interrupted, "Connection interrupted");
|
||||||
|
XPC_ERROR_DEFINITION(connection_invalid, "Connection invalid");
|
||||||
|
XPC_ERROR_DEFINITION(termination_imminent, "Termination imminent");
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(error)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(error);
|
||||||
|
|
||||||
|
- (char*)xpcDescription
|
||||||
|
{
|
||||||
|
char* output = NULL;
|
||||||
|
asprintf(&output, "<%s: %s>", xpc_class_name(self), [self stringForKey: XPCErrorDescriptionKey].UTF8String);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const char* xpc_strerror(int error) {
|
||||||
|
return NULL;
|
||||||
|
};
|
121
src/event.m
Normal file
121
src/event.m
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
// actually, the event publisher is it's own class
|
||||||
|
typedef xpc_object_t xpc_event_publisher_t;
|
||||||
|
|
||||||
|
XPC_EXPORT const char* const _xpc_event_key_name = "XPCEventName";
|
||||||
|
XPC_EXPORT const char* const _xpc_event_key_stream_name = "XPCEventStreamName";
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_event_publisher_activate(xpc_event_publisher_t xpub) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_event_publisher_create(const char* name, void* some_parameter) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_event_publisher_fire(xpc_event_publisher_t xpub, uint64_t token, xpc_object_t details) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_event_publisher_fire_noboost(xpc_event_publisher_t xpub, uint64_t token, xpc_object_t details) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_event_publisher_fire_with_reply() {
|
||||||
|
// i didn't feel like determining the parameters for this one
|
||||||
|
// should be similar to `xpc_event_publisher_fire`
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_event_publisher_fire_with_reply_sync() {
|
||||||
|
// should be similar to `xpc_event_publisher_fire_with_reply`
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
au_asid_t xpc_event_publisher_get_subscriber_asid(xpc_event_publisher_t xpub, uint64_t token) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_event_publisher_set_error_handler(xpc_event_publisher_t xpub, void (^handler)()) {
|
||||||
|
// no clue what the parameters for the handler are
|
||||||
|
// (probably includes some sort of error parameter, but i have no clue what that is)
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_event_publisher_set_handler(xpc_event_publisher_t xpub, void (^handler)()) {
|
||||||
|
// likewise, no clue what the parameters to the handler are
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_event_publisher_set_initial_load_completed_handler_4remoted(xpc_event_publisher_t xpub, void (^handler)()) {
|
||||||
|
// once again, no clue about the handler parameters
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_event_publisher_set_subscriber_keepalive(xpc_event_publisher_t xpub, uint64_t token, bool state) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_event_stream_check_in() {
|
||||||
|
// not a stub
|
||||||
|
// just returns 0
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_event_stream_check_in2() {
|
||||||
|
// not a stub
|
||||||
|
// just returns 0
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_get_event_name(const char* stream, uint64_t token, char* out_name) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_copy_event(const char* stream, const char* name) {
|
||||||
|
// returns a dictionary
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_copy_event_entitlements(const char* stream, uint64_t token) {
|
||||||
|
// returns a dictionary
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_set_event(const char* stream, const char* name, xpc_object_t descriptor) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_set_event_state(const char* stream, uint64_t token, bool state) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_set_event_stream_handler(const char* connection_name, dispatch_queue_t queue, void (^handler)(xpc_object_t event)) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_set_event_with_flags(const char* stream, const char* name, xpc_object_t descriptor, uint64_t flags) {
|
||||||
|
|
||||||
|
};
|
109
src/fd.m
Normal file
109
src/fd.m
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
#import <xpc/objects/fd.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
#include <sys/fileport.h>
|
||||||
|
#include <mach/mach.h>
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(fd);
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(fd)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(fd);
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(fd);
|
||||||
|
if (this->port != MACH_PORT_NULL) {
|
||||||
|
xpc_mach_port_release_right(this->port, MACH_PORT_RIGHT_SEND);
|
||||||
|
}
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (char*)xpcDescription
|
||||||
|
{
|
||||||
|
char* output = NULL;
|
||||||
|
asprintf(&output, "<%s: port = %d>", xpc_class_name(self), self.port);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (mach_port_t)port
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(fd);
|
||||||
|
return this->port;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithDescriptor: (int)descriptor
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
XPC_THIS_DECL(fd);
|
||||||
|
if (fileport_makeport(descriptor, &this->port) != KERN_SUCCESS) {
|
||||||
|
[self release];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithPort: (mach_port_t)port
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
XPC_THIS_DECL(fd);
|
||||||
|
if (xpc_mach_port_retain_right(port, MACH_PORT_RIGHT_SEND) != KERN_SUCCESS) {
|
||||||
|
[self release];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
this->port = port;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (int)instantiateDescriptor
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(fd);
|
||||||
|
return fileport_makefd(this->port);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)hash
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(fd);
|
||||||
|
return (NSUInteger)this->port;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)copy
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(fd);
|
||||||
|
return [[XPC_CLASS(fd) alloc] initWithPort: this->port];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_fd_create(int fd) {
|
||||||
|
return [[XPC_CLASS(fd) alloc] initWithDescriptor: fd];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_fd_dup(xpc_object_t xfd) {
|
||||||
|
TO_OBJC_CHECKED(fd, xfd, fd) {
|
||||||
|
return [fd instantiateDescriptor];
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
mach_port_t _xpc_fd_get_port(xpc_object_t xfd) {
|
||||||
|
TO_OBJC_CHECKED(fd, xfd, fd) {
|
||||||
|
return fd.port;
|
||||||
|
}
|
||||||
|
return MACH_PORT_NULL;
|
||||||
|
};
|
75
src/file_transfer.m
Normal file
75
src/file_transfer.m
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#import <xpc/objects/file_transfer.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(file_transfer);
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(file_transfer)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(file_transfer);
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_file_transfer_cancel(xpc_file_transfer_t xtransfer) {
|
||||||
|
// actually just crashes
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
dispatch_io_t xpc_file_transfer_copy_io(xpc_file_transfer_t xtransfer) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_file_transfer_t xpc_file_transfer_create_with_fd(int fd, void (^completionCallback)(void)) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_file_transfer_t xpc_file_transfer_create_with_path(const char* path, void (^completionCallback)(void)) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
uint64_t xpc_file_transfer_get_size(xpc_file_transfer_t xtransfer) {
|
||||||
|
// return type is either `uint64_t` or `size_t`
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
uint64_t xpc_file_transfer_get_transfer_id(xpc_file_transfer_t xtransfer) {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_file_transfer_send_finished(xpc_file_transfer_t xtransfer, bool succeeded) {
|
||||||
|
// unsure about the second parameter (both the return type and the purpose)
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_file_transfer_set_transport_writing_callbacks(xpc_file_transfer_t xtransfer, void (^writeCallback)(void), void (^sendCallback)(void)) {
|
||||||
|
// parameters 2 & 3 are complete guesses;
|
||||||
|
// the only thing i know about them is that they're blocks
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_file_transfer_write_finished(xpc_file_transfer_t xtransfer, bool succeeded) {
|
||||||
|
// i'm guessing this function is like `xpc_file_transfer_send_finished`
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void* xpc_file_transfer_write_to_fd(xpc_file_transfer_t xtransfer, int fd, void (^someCallback)(void)) {
|
||||||
|
// no clue what the return type is
|
||||||
|
// also unsure about parameter 3's type
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void* xpc_file_transfer_write_to_path(xpc_file_transfer_t xtransfer, const char* path, void (^someCallback)(void)) {
|
||||||
|
// same issues as `xpc_file_transfer_write_to_fd`
|
||||||
|
return NULL;
|
||||||
|
};
|
@ -1,20 +1,25 @@
|
|||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
extern void bootstrap_init(void); // in liblaunch
|
extern void bootstrap_init(void); // in liblaunch
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
void _libxpc_initializer(void)
|
void _libxpc_initializer(void)
|
||||||
{
|
{
|
||||||
bootstrap_init();
|
bootstrap_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
void xpc_atfork_child(void)
|
void xpc_atfork_child(void)
|
||||||
{
|
{
|
||||||
bootstrap_init();
|
bootstrap_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
void xpc_atfork_parent(void)
|
void xpc_atfork_parent(void)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
void xpc_atfork_prepare(void)
|
void xpc_atfork_prepare(void)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
33
src/int64.m
Normal file
33
src/int64.m
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#import <xpc/objects/int64.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
XPC_WRAPPER_CLASS_IMPL(int64, int64_t, "%lld");
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_int64_create(int64_t value) {
|
||||||
|
return [[XPC_CLASS(int64) alloc] initWithValue: value];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int64_t xpc_int64_get_value(xpc_object_t xint) {
|
||||||
|
TO_OBJC_CHECKED(int64, xint, integer) {
|
||||||
|
return integer.value;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void _xpc_int64_set_value(xpc_object_t xint, int64_t value) {
|
||||||
|
TO_OBJC_CHECKED(int64, xint, integer) {
|
||||||
|
integer.value = value;
|
||||||
|
}
|
||||||
|
};
|
173
src/launchd.m
Normal file
173
src/launchd.m
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
#import <xpc/xpc.h>
|
||||||
|
#import <launch.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
struct some_launch_service_stats_struct;
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t _launch_msg2(xpc_object_t request, int type, uint64_t handle) {
|
||||||
|
// `request` is probably a dictionary
|
||||||
|
// valid values for `type`: 0, 1, 2, 3
|
||||||
|
// `handle` can be a normal value or it can be UINT64_MAX (only matters when `type` == )
|
||||||
|
// returns a uint64 object
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int _launch_service_stats_copy_4ppse_impl(struct some_launch_service_stats_struct* launch_service_stats, int type) {
|
||||||
|
// no clue what the structure layout is
|
||||||
|
// `type` MUST be 2 or else the function crashes
|
||||||
|
// the return type seems to be a status code
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int launch_activate_socket(const char* key, int** fds, size_t* count) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int launch_add_external_service(int handle, const char* path, xpc_object_t overlay) {
|
||||||
|
// `overlay` is probably a dictionary
|
||||||
|
// no clue what type of value `handle` is other than some integer
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int launch_bootout_user_service_4coresim(const char* name) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t launch_copy_busy_extension_instances(const char** names, size_t name_count) {
|
||||||
|
// returns an array
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t launch_copy_endpoints_properties_for_pid(pid_t pid) {
|
||||||
|
// returns a dictionary
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t launch_copy_extension_properties(xpc_connection_t xconn) {
|
||||||
|
// returns a dictionary
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t launch_copy_extension_properties_for_pid(pid_t pid) {
|
||||||
|
// returns a dictionary
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t launch_copy_properties_for_pid_4assertiond(pid_t pid) {
|
||||||
|
// returns a dictionary
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int launch_create_persona(uid_t uid, uint64_t flags) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int launch_destroy_persona(int handle) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int launch_disable_directory(const char* path) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int launch_enable_directory(const char* path) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void launch_extension_check_in_live_4UIKit(void) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT const char* launch_extension_property_bundle_id = "XPCExtensionBundleIdentifier";
|
||||||
|
XPC_EXPORT const char* launch_extension_property_host_bundle_id = "XPCExtensionHostBundleIdentifier";
|
||||||
|
XPC_EXPORT const char* launch_extension_property_host_pid = "XPCExtensionHostPID";
|
||||||
|
XPC_EXPORT const char* launch_extension_property_path = "XPCExtensionPath";
|
||||||
|
XPC_EXPORT const char* launch_extension_property_pid = "XPCExtensionPID";
|
||||||
|
XPC_EXPORT const char* launch_extension_property_version = "XPCExtensionBundleVersion";
|
||||||
|
XPC_EXPORT const char* launch_extension_property_xpc_bundle = "XPCExtensionXPCBundle";
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int launch_get_service_enabled(const char* name, bool* out_loaded, bool* out_enabled) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int launch_get_system_service_enabled(const char* name, bool* out_loaded, bool* out_enabled) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
char* launch_path_for_user_service_4coresim(const char* name) {
|
||||||
|
// returns a string that must be freed
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT const char* launch_perfcheck_property_endpoint_active = "XPCServiceEndpointActive";
|
||||||
|
XPC_EXPORT const char* launch_perfcheck_property_endpoint_event = "XPCServiceEndpointEvent";
|
||||||
|
XPC_EXPORT const char* launch_perfcheck_property_endpoint_name = "XPCServiceEndpointName";
|
||||||
|
XPC_EXPORT const char* launch_perfcheck_property_endpoint_needs_activation = "XPCServiceEndpointNeedsActivation";
|
||||||
|
XPC_EXPORT const char* launch_perfcheck_property_endpoints = "XPCServiceEndpoints";
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void launch_remove_external_service(const char* name, const char* version, dispatch_queue_t queue, void (^callback)(int error)) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int launch_service_stats_disable_4ppse(void) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int launch_service_stats_enable_4ppse(void) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
bool launch_service_stats_is_enabled_4ppse() {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int launch_set_service_enabled(const char* name, bool enabled) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int launch_set_system_service_enabled(const char* name, bool enabled) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
char* launch_version_for_user_service_4coresim(const char* name) {
|
||||||
|
// returns a string that must be freed
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t ld2xpc(launch_data_t data) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
kern_return_t xpc_call_wakeup(mach_port_t port, int status) {
|
||||||
|
return -1;
|
||||||
|
};
|
25
src/mach_recv.m
Normal file
25
src/mach_recv.m
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#import <xpc/objects/mach_recv.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(mach_recv);
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(mach_recv)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(mach_recv);
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_mach_recv_create(mach_port_t recv) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
mach_port_t xpc_mach_recv_extract_right(xpc_object_t xrecv) {
|
||||||
|
return MACH_PORT_NULL;
|
||||||
|
};
|
45
src/mach_send.m
Normal file
45
src/mach_send.m
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#import <xpc/objects/mach_send.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(mach_send);
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(mach_send)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(mach_send);
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
mach_port_t xpc_mach_send_copy_right(xpc_object_t xsend) {
|
||||||
|
// retains the send right
|
||||||
|
return MACH_PORT_NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_mach_send_create(mach_port_t send) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_mach_send_create_with_disposition(mach_port_t send, unsigned int disposition) {
|
||||||
|
// values for `disposition` are either:
|
||||||
|
// - 0x11 - no action taken with send
|
||||||
|
// - 0x13 - send is retained
|
||||||
|
// - 0x14 - send is `make_send`ed
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
mach_port_t xpc_mach_send_get_right(xpc_object_t xsend) {
|
||||||
|
// only returns the send right; doesn't retain it
|
||||||
|
return MACH_PORT_NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
mach_port_t xpc_mach_send_extract_right(xpc_object_t xsend) {
|
||||||
|
return MACH_PORT_NULL;
|
||||||
|
};
|
656
src/misc.c
656
src/misc.c
@ -1,656 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2014-2015 iXsystems, Inc.
|
|
||||||
* All rights reserved
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted providing that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
||||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
||||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
||||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/errno.h>
|
|
||||||
#include <sys/sbuf.h>
|
|
||||||
#include <mach/mach.h>
|
|
||||||
#include <xpc/launchd.h>
|
|
||||||
#include <libkern/OSAtomic.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <syslog.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <uuid/uuid.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <xpc/internal.h>
|
|
||||||
#include <libkern/OSByteOrder.h>
|
|
||||||
#include <sys/sysctl.h>
|
|
||||||
|
|
||||||
#define RECV_BUFFER_SIZE 65536
|
|
||||||
|
|
||||||
#include <xpc/private.h>
|
|
||||||
#include <xpc/serialization.h>
|
|
||||||
|
|
||||||
#define sbuf_new_auto() sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND)
|
|
||||||
|
|
||||||
#define MAX_RECV 8192
|
|
||||||
#define XPC_RECV_SIZE \
|
|
||||||
MAX_RECV - \
|
|
||||||
sizeof(mach_msg_header_t) - \
|
|
||||||
sizeof(mach_msg_trailer_t) - \
|
|
||||||
sizeof(uint64_t) - \
|
|
||||||
sizeof(size_t)
|
|
||||||
|
|
||||||
const char *const _xpc_event_key_name = "XPCEventName";
|
|
||||||
|
|
||||||
struct xpc_message {
|
|
||||||
mach_msg_header_t header;
|
|
||||||
size_t size;
|
|
||||||
uint64_t id;
|
|
||||||
char data[0];
|
|
||||||
mach_msg_trailer_t trailer;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct xpc_recv_message {
|
|
||||||
mach_msg_header_t header;
|
|
||||||
size_t size;
|
|
||||||
uint64_t id;
|
|
||||||
char data[XPC_RECV_SIZE];
|
|
||||||
mach_msg_trailer_t trailer;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void xpc_copy_description_level(xpc_object_t obj, struct sbuf *sbuf,
|
|
||||||
int level);
|
|
||||||
|
|
||||||
void
|
|
||||||
fail_log(const char *exp)
|
|
||||||
{
|
|
||||||
debugf("%s", exp);
|
|
||||||
//sleep(1);
|
|
||||||
printf("%s", exp);
|
|
||||||
//abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
xpc_dictionary_destroy(struct xpc_object *dict)
|
|
||||||
{
|
|
||||||
struct xpc_dict_head *head;
|
|
||||||
struct xpc_dict_pair *p, *ptmp;
|
|
||||||
|
|
||||||
head = &dict->xo_dict;
|
|
||||||
|
|
||||||
TAILQ_FOREACH_SAFE(p, head, xo_link, ptmp) {
|
|
||||||
TAILQ_REMOVE(head, p, xo_link);
|
|
||||||
free(p->key);
|
|
||||||
xpc_release(p->value);
|
|
||||||
free(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
xpc_array_destroy(struct xpc_object *dict)
|
|
||||||
{
|
|
||||||
struct xpc_object *p, *ptmp;
|
|
||||||
struct xpc_array_head *head;
|
|
||||||
|
|
||||||
head = &dict->xo_array;
|
|
||||||
|
|
||||||
TAILQ_FOREACH_SAFE(p, head, xo_link, ptmp) {
|
|
||||||
TAILQ_REMOVE(head, p, xo_link);
|
|
||||||
xpc_release(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_object_destroy(struct xpc_object *xo)
|
|
||||||
{
|
|
||||||
if (xo->xo_refcnt == _XPC_KEEP_ALIVE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (xo->xo_xpc_type == _XPC_TYPE_DICTIONARY)
|
|
||||||
xpc_dictionary_destroy(xo);
|
|
||||||
|
|
||||||
if (xo->xo_xpc_type == _XPC_TYPE_ARRAY)
|
|
||||||
xpc_array_destroy(xo);
|
|
||||||
|
|
||||||
if (xo->xo_xpc_type == _XPC_TYPE_CONNECTION)
|
|
||||||
xpc_connection_destroy(xo);
|
|
||||||
|
|
||||||
if (xo->xo_xpc_type == _XPC_TYPE_STRING)
|
|
||||||
free(xo->xo_u.str);
|
|
||||||
|
|
||||||
if (xo->xo_xpc_type == _XPC_TYPE_DATA)
|
|
||||||
free(xo->xo_u.ptr);
|
|
||||||
|
|
||||||
free(xo);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_retain(xpc_object_t obj)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo = obj;
|
|
||||||
if (xo->xo_refcnt == _XPC_KEEP_ALIVE)
|
|
||||||
return obj;
|
|
||||||
|
|
||||||
OSAtomicIncrement32(&xo->xo_refcnt);
|
|
||||||
return (obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_release(xpc_object_t obj)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo = obj;
|
|
||||||
if (xo->xo_refcnt == _XPC_KEEP_ALIVE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (OSAtomicDecrement32(&xo->xo_refcnt) > 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
xpc_object_destroy(xo);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The other one is unsafe?
|
|
||||||
// This is called by Security and is private
|
|
||||||
void
|
|
||||||
xpc_release_safe(xpc_object_t obj)
|
|
||||||
{
|
|
||||||
xpc_release(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *xpc_errors[] = {
|
|
||||||
"No Error Found",
|
|
||||||
"No Memory",
|
|
||||||
"Invalid Argument",
|
|
||||||
"No Such Process"
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const char *
|
|
||||||
xpc_strerror(int error)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (error > EXMAX || error < 0)
|
|
||||||
return "BAD ERROR";
|
|
||||||
return (xpc_errors[error]);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
xpc_copy_description(xpc_object_t obj)
|
|
||||||
{
|
|
||||||
char *result;
|
|
||||||
struct sbuf *sbuf;
|
|
||||||
|
|
||||||
sbuf = sbuf_new_auto();
|
|
||||||
xpc_copy_description_level(obj, sbuf, 0);
|
|
||||||
sbuf_finish(sbuf);
|
|
||||||
result = strdup(sbuf_data(sbuf));
|
|
||||||
sbuf_delete(sbuf);
|
|
||||||
|
|
||||||
return (result);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
xpc_copy_description_level(xpc_object_t obj, struct sbuf *sbuf, int level)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo = obj;
|
|
||||||
#ifndef __APPLE__
|
|
||||||
struct uuid *id;
|
|
||||||
#else
|
|
||||||
uuid_t id;
|
|
||||||
#endif
|
|
||||||
char *uuid_str;
|
|
||||||
uint32_t uuid_status;
|
|
||||||
|
|
||||||
if (obj == NULL) {
|
|
||||||
sbuf_printf(sbuf, "<null value>\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sbuf_printf(sbuf, "(%s) ", _xpc_get_type_name(obj));
|
|
||||||
|
|
||||||
switch (xo->xo_xpc_type) {
|
|
||||||
case _XPC_TYPE_DICTIONARY:
|
|
||||||
sbuf_printf(sbuf, "\n");
|
|
||||||
xpc_dictionary_apply(xo, ^(const char *k, xpc_object_t v) {
|
|
||||||
sbuf_printf(sbuf, "%*s\"%s\": ", level * 4, " ", k);
|
|
||||||
xpc_copy_description_level(v, sbuf, level + 1);
|
|
||||||
return ((bool)true);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _XPC_TYPE_ARRAY:
|
|
||||||
sbuf_printf(sbuf, "\n");
|
|
||||||
xpc_array_apply(xo, ^(size_t idx, xpc_object_t v) {
|
|
||||||
sbuf_printf(sbuf, "%*s%ld: ", level * 4, " ", idx);
|
|
||||||
xpc_copy_description_level(v, sbuf, level + 1);
|
|
||||||
return ((bool)true);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _XPC_TYPE_BOOL:
|
|
||||||
sbuf_printf(sbuf, "%s\n",
|
|
||||||
xpc_bool_get_value(obj) ? "true" : "false");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _XPC_TYPE_STRING:
|
|
||||||
sbuf_printf(sbuf, "\"%s\"\n",
|
|
||||||
xpc_string_get_string_ptr(obj));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _XPC_TYPE_INT64:
|
|
||||||
sbuf_printf(sbuf, "%lld\n",
|
|
||||||
xpc_int64_get_value(obj));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _XPC_TYPE_UINT64:
|
|
||||||
sbuf_printf(sbuf, "0x%llx\n",
|
|
||||||
xpc_uint64_get_value(obj));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _XPC_TYPE_DATE:
|
|
||||||
sbuf_printf(sbuf, "%llu\n",
|
|
||||||
xpc_date_get_value(obj));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _XPC_TYPE_UUID:
|
|
||||||
#ifdef __APPLE__
|
|
||||||
memcpy(id, xpc_uuid_get_bytes(obj), sizeof(id));
|
|
||||||
uuid_str = (char*) __builtin_alloca(40);
|
|
||||||
uuid_unparse(*id, uuid_str);
|
|
||||||
#else
|
|
||||||
id = (struct uuid *)xpc_uuid_get_bytes(obj);
|
|
||||||
uuid_to_string(id, &uuid_str, &uuid_status);
|
|
||||||
#endif
|
|
||||||
sbuf_printf(sbuf, "%s\n", uuid_str);
|
|
||||||
free(uuid_str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _XPC_TYPE_ENDPOINT:
|
|
||||||
sbuf_printf(sbuf, "<%d>\n", xo->xo_int);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case _XPC_TYPE_NULL:
|
|
||||||
sbuf_printf(sbuf, "<null>\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct _launch_data {
|
|
||||||
uint64_t type;
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
union {
|
|
||||||
launch_data_t *_array;
|
|
||||||
char *string;
|
|
||||||
void *opaque;
|
|
||||||
int64_t __junk;
|
|
||||||
};
|
|
||||||
union {
|
|
||||||
uint64_t _array_cnt;
|
|
||||||
uint64_t string_len;
|
|
||||||
uint64_t opaque_size;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
int64_t fd;
|
|
||||||
uint64_t mp;
|
|
||||||
uint64_t err;
|
|
||||||
int64_t number;
|
|
||||||
uint64_t boolean; /* We'd use 'bool' but this struct needs to be used under Rosetta, and sizeof(bool) is different between PowerPC and Intel */
|
|
||||||
double float_num;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
static uint8_t ld_to_xpc_type[] = {
|
|
||||||
_XPC_TYPE_INVALID,
|
|
||||||
_XPC_TYPE_DICTIONARY,
|
|
||||||
_XPC_TYPE_ARRAY,
|
|
||||||
_XPC_TYPE_FD,
|
|
||||||
_XPC_TYPE_UINT64,
|
|
||||||
_XPC_TYPE_DOUBLE,
|
|
||||||
_XPC_TYPE_BOOL,
|
|
||||||
_XPC_TYPE_STRING,
|
|
||||||
_XPC_TYPE_DATA,
|
|
||||||
_XPC_TYPE_ERROR,
|
|
||||||
_XPC_TYPE_ENDPOINT
|
|
||||||
};
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
ld2xpc(launch_data_t ld)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
xpc_u val;
|
|
||||||
|
|
||||||
|
|
||||||
if (ld->type > LAUNCH_DATA_MACHPORT)
|
|
||||||
return (NULL);
|
|
||||||
if (ld->type == LAUNCH_DATA_STRING || ld->type == LAUNCH_DATA_OPAQUE) {
|
|
||||||
val.str = malloc(ld->string_len);
|
|
||||||
memcpy(__DECONST(void *, val.str), ld->string, ld->string_len);
|
|
||||||
xo = _xpc_prim_create(ld_to_xpc_type[ld->type], val, ld->string_len);
|
|
||||||
} else if (ld->type == LAUNCH_DATA_BOOL) {
|
|
||||||
xo = xpc_bool_create((bool)ld->boolean);
|
|
||||||
} else if (ld->type == LAUNCH_DATA_ARRAY) {
|
|
||||||
xo = xpc_array_create(NULL, 0);
|
|
||||||
for (uint64_t i = 0; i < ld->_array_cnt; i++)
|
|
||||||
xpc_array_append_value(xo, ld2xpc(ld->_array[i]));
|
|
||||||
} else {
|
|
||||||
val.ui = ld->mp;
|
|
||||||
xo = _xpc_prim_create(ld_to_xpc_type[ld->type], val, ld->string_len);
|
|
||||||
}
|
|
||||||
return (xo);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_copy_entitlement_for_token(const char *key __unused, audit_token_t *token __unused)
|
|
||||||
{
|
|
||||||
return xpc_bool_create(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_copy_entitlements_for_pid(pid_t pid)
|
|
||||||
{
|
|
||||||
return xpc_bool_create(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define XPC_RPORT "XPC remote port"
|
|
||||||
int
|
|
||||||
xpc_pipe_routine_reply(xpc_object_t xobj)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
size_t size = 0, msg_size;
|
|
||||||
struct xpc_message *message;
|
|
||||||
kern_return_t kr;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
xo = xobj;
|
|
||||||
assert(xo->xo_xpc_type == _XPC_TYPE_DICTIONARY);
|
|
||||||
|
|
||||||
if ((errno = xpc_serialize(xo, NULL, 0, &size)) != 0)
|
|
||||||
return errno;
|
|
||||||
|
|
||||||
msg_size = size + sizeof(struct xpc_message);
|
|
||||||
|
|
||||||
if ((message = malloc(msg_size)) == NULL)
|
|
||||||
return ENOMEM;
|
|
||||||
|
|
||||||
if ((errno = xpc_serialize(xo, message->data, size, NULL)) != 0)
|
|
||||||
return errno;
|
|
||||||
|
|
||||||
message->header.msgh_size = msg_size;
|
|
||||||
message->header.msgh_remote_port = xpc_dictionary_copy_mach_send(xobj, XPC_RPORT);
|
|
||||||
message->header.msgh_local_port = MACH_PORT_NULL;
|
|
||||||
message->size = size;
|
|
||||||
kr = mach_msg_send(&message->header);
|
|
||||||
if (kr != KERN_SUCCESS)
|
|
||||||
err = (kr == KERN_INVALID_TASK) ? EPIPE : EINVAL;
|
|
||||||
else
|
|
||||||
err = 0;
|
|
||||||
free(message);
|
|
||||||
return (err);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
xpc_pipe_send(xpc_object_t xobj, mach_port_t dst, mach_port_t local,
|
|
||||||
uint64_t id)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
size_t size = 0, msg_size;
|
|
||||||
struct xpc_message *message;
|
|
||||||
kern_return_t kr;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
xo = xobj;
|
|
||||||
debugf("obj type is %d", xo->xo_xpc_type);
|
|
||||||
if (xo->xo_xpc_type != _XPC_TYPE_DICTIONARY)
|
|
||||||
debugf("obj type is %s", _xpc_get_type_name(xobj));
|
|
||||||
assert(xo->xo_xpc_type == _XPC_TYPE_DICTIONARY);
|
|
||||||
|
|
||||||
debugf("packing message");
|
|
||||||
if ((errno = xpc_serialize(xo, NULL, 0, &size)) != 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
msg_size = size + sizeof(struct xpc_message);
|
|
||||||
if ((message = malloc(msg_size)) == NULL)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
if ((errno = xpc_serialize(xo, message->data, size, NULL)) != 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
debugf("sending message");
|
|
||||||
msg_size = ALIGN(size + sizeof(mach_msg_header_t) + sizeof(size_t) + sizeof(uint64_t));
|
|
||||||
message->header.msgh_size = (mach_msg_size_t)msg_size;
|
|
||||||
message->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
|
|
||||||
MACH_MSG_TYPE_MAKE_SEND);
|
|
||||||
message->header.msgh_remote_port = dst;
|
|
||||||
message->header.msgh_local_port = local;
|
|
||||||
message->id = id;
|
|
||||||
message->size = size;
|
|
||||||
kr = mach_msg_send(&message->header);
|
|
||||||
if (kr != KERN_SUCCESS)
|
|
||||||
err = (kr == KERN_INVALID_TASK || kr == MACH_SEND_INVALID_DEST) ? -EPIPE : -EINVAL;
|
|
||||||
else
|
|
||||||
err = 0;
|
|
||||||
free(message);
|
|
||||||
return (err);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LOG(msg, ...) \
|
|
||||||
do { \
|
|
||||||
debugf("%s:%u: " msg, __FILE__, __LINE__,##__VA_ARGS__); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
int
|
|
||||||
xpc_pipe_receive(mach_port_t local, mach_port_t *remote, xpc_object_t *result,
|
|
||||||
uint64_t *id)
|
|
||||||
{
|
|
||||||
struct xpc_recv_message message;
|
|
||||||
mach_msg_header_t *request;
|
|
||||||
kern_return_t kr;
|
|
||||||
mach_msg_trailer_t *tr;
|
|
||||||
int data_size;
|
|
||||||
struct xpc_object *xo;
|
|
||||||
audit_token_t *auditp;
|
|
||||||
|
|
||||||
request = &message.header;
|
|
||||||
/* should be size - but what about arbitrary XPC data? */
|
|
||||||
request->msgh_size = MAX_RECV;
|
|
||||||
request->msgh_local_port = local;
|
|
||||||
kr = mach_msg(request, MACH_RCV_MSG |
|
|
||||||
MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
|
|
||||||
MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT),
|
|
||||||
0, request->msgh_size, request->msgh_local_port,
|
|
||||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
|
||||||
|
|
||||||
if (kr != KERN_SUCCESS) {
|
|
||||||
LOG("mach_msg_receive returned %d\n", kr);
|
|
||||||
return (kr == KERN_INVALID_TASK || kr == MACH_RCV_PORT_DIED || kr == MACH_RCV_PORT_CHANGED) ? -EPIPE : -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
*remote = request->msgh_remote_port;
|
|
||||||
*id = message.id;
|
|
||||||
data_size = (int)message.size;
|
|
||||||
LOG("unpacking data_size=%d", data_size);
|
|
||||||
if (xpc_deserialize(&xo, message.data, data_size, NULL) != 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
tr = (mach_msg_trailer_t *)(((char *)&message) + request->msgh_size);
|
|
||||||
auditp = &((mach_msg_audit_trailer_t *)tr)->msgh_audit;
|
|
||||||
|
|
||||||
xo->xo_audit_token = malloc(sizeof(*auditp));
|
|
||||||
memcpy(xo->xo_audit_token, auditp, sizeof(*auditp));
|
|
||||||
|
|
||||||
xpc_dictionary_set_mach_send(xo, XPC_RPORT, request->msgh_remote_port);
|
|
||||||
xpc_dictionary_set_uint64(xo, XPC_SEQID, message.id);
|
|
||||||
xo->xo_flags |= _XPC_FROM_WIRE;
|
|
||||||
|
|
||||||
*result = xo;
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
xpc_pipe_try_receive(mach_port_t portset, xpc_object_t *requestobj, mach_port_t *rcvport,
|
|
||||||
boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *), mach_msg_size_t msgsize __unused,
|
|
||||||
int flags __unused)
|
|
||||||
{
|
|
||||||
struct xpc_recv_message message;
|
|
||||||
struct xpc_recv_message rsp_message;
|
|
||||||
mach_msg_header_t *request;
|
|
||||||
kern_return_t kr;
|
|
||||||
mach_msg_header_t *response;
|
|
||||||
mach_msg_trailer_t *tr;
|
|
||||||
int data_size;
|
|
||||||
struct xpc_object *xo;
|
|
||||||
audit_token_t *auditp;
|
|
||||||
|
|
||||||
request = &message.header;
|
|
||||||
response = &rsp_message.header;
|
|
||||||
/* should be size - but what about arbitrary XPC data? */
|
|
||||||
request->msgh_size = MAX_RECV;
|
|
||||||
request->msgh_local_port = portset;
|
|
||||||
kr = mach_msg(request, MACH_RCV_MSG |
|
|
||||||
MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
|
|
||||||
MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT),
|
|
||||||
0, request->msgh_size, request->msgh_local_port,
|
|
||||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
|
||||||
|
|
||||||
if (kr != 0)
|
|
||||||
LOG("mach_msg_receive returned %d\n", kr);
|
|
||||||
*rcvport = request->msgh_remote_port;
|
|
||||||
if (demux(request, response)) {
|
|
||||||
mig_reply_error_t* migError = (mig_reply_error_t*) response;
|
|
||||||
|
|
||||||
if (!(migError->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
|
|
||||||
if (migError->RetCode == MIG_NO_REPLY)
|
|
||||||
migError->Head.msgh_remote_port = MACH_PORT_NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response->msgh_remote_port != MACH_PORT_NULL)
|
|
||||||
(void)mach_msg_send(response);
|
|
||||||
/* can't do anything with the return code
|
|
||||||
* just tell the caller this has been handled
|
|
||||||
*/
|
|
||||||
return (TRUE);
|
|
||||||
}
|
|
||||||
LOG("demux returned false\n");
|
|
||||||
data_size = request->msgh_size;
|
|
||||||
LOG("unpacking data_size=%d", data_size);
|
|
||||||
if (xpc_deserialize(&xo, message.data, data_size, NULL) != 0)
|
|
||||||
return -1;
|
|
||||||
/* is padding for alignment enforced in the kernel?*/
|
|
||||||
tr = (mach_msg_trailer_t *)(((char *)&message) + request->msgh_size);
|
|
||||||
auditp = &((mach_msg_audit_trailer_t *)tr)->msgh_audit;
|
|
||||||
|
|
||||||
xo->xo_audit_token = malloc(sizeof(*auditp));
|
|
||||||
memcpy(xo->xo_audit_token, auditp, sizeof(*auditp));
|
|
||||||
|
|
||||||
xpc_dictionary_set_mach_send(xo, XPC_RPORT, request->msgh_remote_port);
|
|
||||||
xpc_dictionary_set_uint64(xo, XPC_SEQID, message.id);
|
|
||||||
xo->xo_flags |= _XPC_FROM_WIRE;
|
|
||||||
*requestobj = xo;
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
xpc_call_wakeup(mach_port_t rport, int retcode)
|
|
||||||
{
|
|
||||||
mig_reply_error_t msg;
|
|
||||||
int err;
|
|
||||||
kern_return_t kr;
|
|
||||||
|
|
||||||
msg.Head.msgh_remote_port = rport;
|
|
||||||
msg.RetCode = retcode;
|
|
||||||
kr = mach_msg_send(&msg.Head);
|
|
||||||
if (kr != KERN_SUCCESS)
|
|
||||||
err = (kr == KERN_INVALID_TASK) ? EPIPE : EINVAL;
|
|
||||||
else
|
|
||||||
err = 0;
|
|
||||||
|
|
||||||
return (err);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
_od_rpc_call(const char *procname, xpc_object_t payload, xpc_pipe_t (*get_pipe)(bool))
|
|
||||||
{
|
|
||||||
printf("STUB _od_rpc_call\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
xpc_pipe_routine(xpc_object_t pipe, void *payload,xpc_object_t *reply)
|
|
||||||
{
|
|
||||||
printf("STUB xpc_pipe_routine\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
xpc_dictionary_set_uuid(xpc_object_t xdict, const char *key, const uuid_t uuid)
|
|
||||||
{
|
|
||||||
printf("STUB xpc_dictionary_set_uuid\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int launch_activate_socket(const char* key, int** fds, size_t* count) {
|
|
||||||
// notes for someone implementing this in the future:
|
|
||||||
//
|
|
||||||
// this function is used in OpenSSH in ssh-agent.c
|
|
||||||
//
|
|
||||||
// `key` is the socket key in the current process's launchd plist
|
|
||||||
// `fds` is a pointer to an array that we allocate that is freed by the caller
|
|
||||||
// `count` is the size of that array
|
|
||||||
//
|
|
||||||
// implementing this requires looking up the current process's launchd plist
|
|
||||||
// and reading socket values from there (or talking to launchd to do that,
|
|
||||||
// if we can do that; i haven't looked into this much)
|
|
||||||
printf("STUB launch_activate_socket\n");
|
|
||||||
if (fds)
|
|
||||||
*fds = NULL;
|
|
||||||
if (count)
|
|
||||||
*count = 0;
|
|
||||||
return -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct os_system_version {
|
|
||||||
unsigned int major;
|
|
||||||
unsigned int minor;
|
|
||||||
unsigned int patch;
|
|
||||||
};
|
|
||||||
|
|
||||||
int os_system_version_get_current_version(struct os_system_version* version_info) {
|
|
||||||
char version_string[48] = {0};
|
|
||||||
size_t version_string_length = sizeof(version_string);
|
|
||||||
char* ptr = NULL;
|
|
||||||
int status = 0;
|
|
||||||
|
|
||||||
if ((status = sysctlbyname("kern.osproductversion", version_string, &version_string_length, NULL, 0)) != 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
version_info->major = strtoul(version_string, &ptr, 10);
|
|
||||||
if (*ptr != '\0') {
|
|
||||||
version_info->minor = strtoul(ptr + 1, &ptr, 10);
|
|
||||||
if (*ptr != '\0') {
|
|
||||||
version_info->patch = strtoul(ptr + 1, &ptr, 10);
|
|
||||||
} else {
|
|
||||||
version_info->patch = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
version_info->minor = 0;
|
|
||||||
version_info->patch = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
return status;
|
|
||||||
};
|
|
252
src/misc.m
Normal file
252
src/misc.m
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
#import <xpc/xpc.h>
|
||||||
|
#import <mach-o/dyld_priv.h>
|
||||||
|
#define __DISPATCH_INDIRECT__
|
||||||
|
#import <dispatch/mach_private.h>
|
||||||
|
#import <os/voucher_private.h>
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
bool _availability_version_check(size_t version_count, dyld_build_version_t* versions) {
|
||||||
|
// i'm *pretty* sure the second argument is an array of `dyld_build_version_t`
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int _system_ios_support_version_copy_string_sysctl(char* out_string) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
bool _system_version_copy_string_plist(char* out_string) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
bool _system_version_copy_string_sysctl(char* out_string) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT uint32_t _system_version_fallback[] = { 10, 15, 0 };
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
bool _system_version_parse_string(const char* string, uint32_t* out_version) {
|
||||||
|
// `version` is an array of 3 `uint32_t`s (or `int`s)
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int os_system_version_get_current_version(uint32_t* out_version) {
|
||||||
|
// parameter 1's type is a good guess, but i'm unsure
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int os_system_version_sim_get_current_host_version(uint32_t* out_version) {
|
||||||
|
// same as with `os_system_version_get_current_version`
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t _xpc_payload_create_from_mach_msg(dispatch_mach_msg_t msg, int some_flag) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void _xpc_spawnattr_pack_string(char* string_but_with_an_offset_of_AEh, uint32_t* offset, size_t* length, const char* string_to_pack) {
|
||||||
|
// `string_to_pack` is copied into `string_but_with_an_offset_of_AEh` after offsetting it by 0xae + `*offset`
|
||||||
|
// the length of `string_to_pack` (including the null terminator) is added to `*offset` and subtracted from `*length`
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void _xpc_spawnattr_pack_string_fragment(char* string_but_with_an_offset_of_AEh, uint32_t* offset, size_t* length, const char* string_to_pack) {
|
||||||
|
// same as `_xpc_spawnattr_pack_string`, but doesn't include the null terminator in the math
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const char* _xpc_spawnattr_unpack_string(const char* string, size_t length, uint32_t offset) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
char* _xpc_spawnattr_unpack_strings(char* string_but_with_an_offset_of_AEh, size_t length, uint32_t offset, const char** out_strings, size_t strings_array_size) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t place_hold_on_real_loginwindow(void) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_copy_entitlement_for_self(const char* name) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_copy_entitlement_for_token(const char* name, audit_token_t* token) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_copy_entitlements_data_for_token(audit_token_t* token) {
|
||||||
|
// returns a data object
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_copy_entitlements_for_pid(pid_t pid) {
|
||||||
|
// returns a dictionary
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_copy_entitlements_for_self(void) {
|
||||||
|
// returns a dictionary
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_copy_bootstrap(void) {
|
||||||
|
// returns a dictionary
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
char* xpc_copy_code_signing_identity_for_token(audit_token_t* token) {
|
||||||
|
// returns a string that must be freed
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_copy_domain(void) {
|
||||||
|
// returns a dictionary with a single entry:
|
||||||
|
// "pid": <pid of current process>
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_copy_extension_sdk_entry() {
|
||||||
|
// not a stub
|
||||||
|
// just returns NULL
|
||||||
|
// probably has parameters, but there's no way to tell what they are
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const char* xpc_exit_reason_get_label(int reason) {
|
||||||
|
// `reason` is actually an enum value
|
||||||
|
// this function just maps the values with their names
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_generate_audit_token(pid_t i_think_this_is_a_pid, audit_token_t* token) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_endpoint_t xpc_get_attachment_endpoint(void) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct some_return_remote_hooks_struct;
|
||||||
|
struct some_remote_hooks_struct;
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
struct some_return_remote_hooks_struct* xpc_install_remote_hooks(struct some_remote_hooks_struct* hooks) {
|
||||||
|
// the return struct is different from the input struct
|
||||||
|
// it's probably some hooks for the caller to call back into libxpc
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_set_idle_handler() {
|
||||||
|
// not a stub
|
||||||
|
// just does nothing
|
||||||
|
// probably has parameters, but there's no way to tell what they are
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
bool xpc_test_symbols_exported() {
|
||||||
|
// not a stub
|
||||||
|
// just returns false
|
||||||
|
// probably has parameters, but there's no way to tell what they are
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_track_activity(void) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_receive_mach_msg(dispatch_mach_msg_t msg, bool end_transaction, voucher_t voucher, xpc_connection_t connection, xpc_object_t* out_object) {
|
||||||
|
// parameter 2's purpose is a guess
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_receive_remote_msg(void* data, size_t data_length, bool some_flag, void* something, xpc_connection_t connection, void (^oolCallback)()) {
|
||||||
|
// parameter 4 is unknown
|
||||||
|
// parameter 6 seems to be a callback, but i'm not sure if it's a raw function or a block (probably a block)
|
||||||
|
// i'm also unsure what the parameters for the callback are
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void* xpc_make_serialization(xpc_object_t object, size_t* out_serialization_length) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void* xpc_make_serialization_with_ool(xpc_object_t object, size_t* out_serialization_length, uint64_t flags) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_create_from_serialization(void* data, size_t data_length) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_create_from_serialization_with_ool(void* data, size_t data_length, void (^oolCallback)()) {
|
||||||
|
// same issue with `oolCallback` as in `xpc_receive_remote_msg`
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_create_from_plist(void* data, size_t data_length) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_create_from_plist_descriptor(int fd, dispatch_queue_t queue) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_create_reply_with_format_and_arguments(xpc_object_t original, const char* format, va_list args) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_create_reply_with_format(xpc_object_t original, const char* format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
xpc_object_t result = xpc_create_reply_with_format_and_arguments(original, format, args);
|
||||||
|
va_end(args);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_create_with_format_and_arguments(const char* format, va_list args) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_create_with_format(const char* format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
xpc_object_t result = xpc_create_with_format_and_arguments(format, args);
|
||||||
|
va_end(args);
|
||||||
|
return result;
|
||||||
|
};
|
44
src/null.m
Normal file
44
src/null.m
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#import <xpc/objects/null.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(null);
|
||||||
|
|
||||||
|
struct xpc_null_s _xpc_null = {
|
||||||
|
.base = {
|
||||||
|
XPC_GLOBAL_OBJECT_HEADER(null),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(null)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(null);
|
||||||
|
|
||||||
|
- (char*)xpcDescription
|
||||||
|
{
|
||||||
|
char* output = NULL;
|
||||||
|
asprintf(&output, "<%s>", xpc_class_name(self));
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (instancetype)null
|
||||||
|
{
|
||||||
|
return XPC_CAST(null, &_xpc_null);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)hash
|
||||||
|
{
|
||||||
|
return 0x804201026298ULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_null_create(void) {
|
||||||
|
return [XPC_CLASS(null) null];
|
||||||
|
};
|
13
src/pipe.c
13
src/pipe.c
@ -1,13 +0,0 @@
|
|||||||
#include <xpc/xpc.h>
|
|
||||||
#include <xpc/private.h>
|
|
||||||
|
|
||||||
|
|
||||||
void xpc_pipe_invalidate(xpc_pipe_t pipe)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_pipe_t xpc_pipe_create(const char* name, int flags)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
86
src/pipe.m
Normal file
86
src/pipe.m
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#import <xpc/objects/pipe.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
#import <xpc/private.h>
|
||||||
|
#define __DISPATCH_INDIRECT__
|
||||||
|
#import <dispatch/mach_private.h>
|
||||||
|
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(pipe);
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(pipe)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(pipe);
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_pipe_invalidate(xpc_pipe_t xpipe) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_pipe_t xpc_pipe_create(const char* name, int flags) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t _od_rpc_call(const char* procname, xpc_object_t payload, xpc_pipe_t (*get_pipe)(bool)) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_pipe_routine_reply(xpc_pipe_t xpipe) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_pipe_routine(xpc_pipe_t xpipe, xpc_object_t payload, xpc_object_t* reply) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_pipe_try_receive(mach_port_t port, xpc_object_t* out_object, mach_port_t* out_remote_port, boolean_t (*demux)(mach_msg_header_t* request, mach_msg_header_t* reply), mach_msg_size_t max_message_size, int flags) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int _xpc_pipe_handle_mig(mach_msg_header_t* request, mach_msg_header_t* reply, bool (*demux)(mach_msg_header_t* request, mach_msg_header_t* reply)) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_pipe_t xpc_pipe_create_from_port(mach_port_t port, mach_port_type_t port_type) {
|
||||||
|
// that second parameter is just a guess
|
||||||
|
// a good guess, but a guess nonetheless
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_pipe_receive(mach_port_t port, xpc_object_t* out_msg, bool some_bool) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_pipe_routine_async(xpc_pipe_t xpipe, xpc_object_t payload, int some_flags_probably) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_pipe_routine_forward(xpc_pipe_t xpipe, xpc_object_t payload) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_pipe_routine_with_flags(xpc_pipe_t xpipe, xpc_object_t payload, xpc_object_t* reply, uint64_t flags) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_pipe_simpleroutine(xpc_pipe_t xpipe, xpc_object_t payload, int some_flags_probably) {
|
||||||
|
return -1;
|
||||||
|
};
|
22
src/pointer.m
Normal file
22
src/pointer.m
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#import <xpc/objects/pointer.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
XPC_WRAPPER_CLASS_IMPL(pointer, void*, "%p");
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_pointer_create(void* value) {
|
||||||
|
return [[XPC_CLASS(pointer) alloc] initWithValue: value];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void* xpc_pointer_get_value(xpc_object_t xptr) {
|
||||||
|
TO_OBJC_CHECKED(pointer, xptr, ptr) {
|
||||||
|
return ptr.value;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
};
|
192
src/private.c
192
src/private.c
@ -1,192 +0,0 @@
|
|||||||
#include <xpc/private.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
int _xpc_runtime_is_app_sandboxed()
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static xpc_object_t xpc_create_with_format_impl(const char * format, va_list args) {
|
|
||||||
// simplistic implementation for now
|
|
||||||
//
|
|
||||||
// everywhere this function and `xpc_create_reply_with_format` are used in Darling,
|
|
||||||
// only simple shallow objects are created
|
|
||||||
|
|
||||||
xpc_object_t result = NULL;
|
|
||||||
|
|
||||||
// TODO: work with non-dictionaries
|
|
||||||
// like i said before, this isn't a big issue because everyone using this function
|
|
||||||
// and `xpc_create_reply_with_format` in Darling is only creating dictionaries
|
|
||||||
if (format[0] != '{')
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
++format;
|
|
||||||
|
|
||||||
result = xpc_dictionary_create(NULL, NULL, 0);
|
|
||||||
|
|
||||||
bool inKey = true;
|
|
||||||
bool ignoringWhitespace = true;
|
|
||||||
const char* key_start = NULL;
|
|
||||||
const char* key_end = NULL;
|
|
||||||
const char* str_start = NULL;
|
|
||||||
const char* str_end = NULL;
|
|
||||||
|
|
||||||
for (; *format != '\0'; ++format) {
|
|
||||||
// TODO: nested objects
|
|
||||||
if (*format == '{') {
|
|
||||||
if (result)
|
|
||||||
xpc_release(result);
|
|
||||||
result = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*format == '}' || (!inKey && *format == ',')) {
|
|
||||||
inKey = true;
|
|
||||||
ignoringWhitespace = true;
|
|
||||||
|
|
||||||
if (!str_start) {
|
|
||||||
// empty dictionary
|
|
||||||
if (!key_start && *format == '}')
|
|
||||||
break;
|
|
||||||
|
|
||||||
// otherwise: bad format
|
|
||||||
if (result)
|
|
||||||
xpc_release(result);
|
|
||||||
result = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove trailing whitespace
|
|
||||||
while (*(str_end - 1) == ' ' || *(str_end - 1) == '\t')
|
|
||||||
--str_end;
|
|
||||||
|
|
||||||
size_t key_len = key_end - key_start;
|
|
||||||
size_t str_len = str_end - str_start;
|
|
||||||
|
|
||||||
// needed in order to have a null terminator
|
|
||||||
char* key_buf[key_len + 1];
|
|
||||||
strncpy(key_buf, key_start, key_len);
|
|
||||||
key_buf[key_len] = '\0';
|
|
||||||
|
|
||||||
const char* key = key_buf;
|
|
||||||
|
|
||||||
// replace "%string" with a vararg
|
|
||||||
if (key_len == 7 && !strncmp(key_start, "%string", key_len))
|
|
||||||
key = va_arg(args, const char*);
|
|
||||||
|
|
||||||
xpc_object_t value = NULL;
|
|
||||||
|
|
||||||
// replace "%string" with a vararg
|
|
||||||
// replace "%value" with a vararg
|
|
||||||
if (str_len == 7 && !strncmp(str_start, "%string", str_len)) {
|
|
||||||
value = xpc_string_create(va_arg(args, const char*));
|
|
||||||
} else if (str_len == 6 && !strncmp(str_start, "%value", str_len)) {
|
|
||||||
value = va_arg(args, xpc_object_t);
|
|
||||||
xpc_retain(value); // to balance out the `xpc_release` later on
|
|
||||||
} else {
|
|
||||||
char* str_buf[str_len + 1];
|
|
||||||
strncpy(str_buf, str_start, str_len);
|
|
||||||
str_buf[str_len] = '\0';
|
|
||||||
value = xpc_string_create(str_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_dictionary_set_value(result, key, value);
|
|
||||||
xpc_release(value);
|
|
||||||
|
|
||||||
if (*format == '}')
|
|
||||||
break;
|
|
||||||
} else if (inKey) {
|
|
||||||
if (*format == ':') {
|
|
||||||
inKey = false;
|
|
||||||
ignoringWhitespace = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*format == ',') {
|
|
||||||
// bad format
|
|
||||||
if (result)
|
|
||||||
xpc_release(result);
|
|
||||||
result = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ignoringWhitespace) {
|
|
||||||
if (*format == ' ' || *format == '\t')
|
|
||||||
continue;
|
|
||||||
ignoringWhitespace = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!key_start)
|
|
||||||
key_start = format;
|
|
||||||
key_end = format + 1;
|
|
||||||
} else {
|
|
||||||
if (*format == ':') {
|
|
||||||
// bad format
|
|
||||||
if (result)
|
|
||||||
xpc_release(result);
|
|
||||||
result = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ignoringWhitespace) {
|
|
||||||
if (*format == ' ' || *format == '\t')
|
|
||||||
continue;
|
|
||||||
ignoringWhitespace = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!str_start)
|
|
||||||
str_start = format;
|
|
||||||
str_end = format + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t xpc_create_with_format(const char * format, ...) {
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
xpc_object_t result = xpc_create_with_format_impl(format, args);
|
|
||||||
va_end(args);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
xpc_object_t xpc_create_reply_with_format(xpc_object_t original, const char * format, ...) {
|
|
||||||
xpc_object_t reply = xpc_dictionary_create_reply(original);
|
|
||||||
|
|
||||||
if (!reply)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
xpc_object_t result = xpc_create_with_format_impl(format, args);
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
// copy reply keys into result
|
|
||||||
//
|
|
||||||
// we actually don't need to do this ATM, because `xpc_dictionary_create_reply` just
|
|
||||||
// creates an empty dictionary, but it's here so we're good if in the future its behavior
|
|
||||||
// is fixed and it does add a reference to the original xpc_object
|
|
||||||
xpc_dictionary_apply(reply, ^bool (const char* key, xpc_object_t value) {
|
|
||||||
xpc_dictionary_set_value(result, key, value);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
xpc_release(reply);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
xpc_object_t xpc_connection_copy_entitlement_value(xpc_connection_t connection, const char* entitlement) {
|
|
||||||
printf("%s\n", __PRETTY_FUNCTION__);
|
|
||||||
return NULL;
|
|
||||||
};
|
|
||||||
|
|
||||||
// from various notes and comments around its use in WebKit and Security,
|
|
||||||
// this function seems to tell the XPC runtime that it's ok for the process
|
|
||||||
// to exit once the XPC runtime finishes doing whatever it needs to
|
|
||||||
void xpc_transaction_exit_clean() {
|
|
||||||
// for now, we have nothing to do here except exit
|
|
||||||
exit(0);
|
|
||||||
};
|
|
@ -1,8 +1,10 @@
|
|||||||
#include <stdio.h>
|
#import <reboot2.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
void *reboot3(int howto) {
|
XPC_EXPORT
|
||||||
|
void *reboot3(uint64_t flags) {
|
||||||
/* Let's just call reboot2 */
|
/* Let's just call reboot2 */
|
||||||
/* It is defined in liblaunch */
|
/* It is defined in liblaunch */
|
||||||
/* printf("libxpc reboot3 called with howto: %d\n", howto); */
|
/* printf("libxpc reboot3 called with howto: %d\n", howto); */
|
||||||
return reboot2(howto);
|
return reboot2(flags);
|
||||||
}
|
}
|
||||||
|
34
src/runtime.m
Normal file
34
src/runtime.m
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t _xpc_runtime_get_entitlements_data(void) {
|
||||||
|
// returns a data object
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t _xpc_runtime_get_self_entitlements(void) {
|
||||||
|
// returns a dictionary (parsed from the plist data from `_xpc_runtime_get_entitlements_data`)
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
bool _xpc_runtime_is_app_sandboxed(void) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_main(xpc_connection_handler_t handler) {
|
||||||
|
abort();
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_init_services(void) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_impersonate_user(void) {
|
||||||
|
// not a stub
|
||||||
|
// this function just does nothing
|
||||||
|
};
|
10
src/serializer.m
Normal file
10
src/serializer.m
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#import <xpc/objects/serializer.h>
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(serializer);
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(serializer)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(serializer);
|
||||||
|
|
||||||
|
@end
|
62
src/service.m
Normal file
62
src/service.m
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#import <xpc/objects/service.h>
|
||||||
|
#import <xpc/objects/service_instance.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(service);
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(service)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(service);
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_service_attach(xpc_service_t xservice, bool run, bool kill) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_service_t xpc_service_create(int flags, const char* name, uint64_t handle, dispatch_queue_t queue) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_service_t xpc_service_create_from_specifier(const char* specifier, dispatch_queue_t queue) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const char* xpc_service_get_rendezvous_token(xpc_service_t xservice) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_service_kickstart(xpc_service_t xservice, bool suspended, bool kill) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_service_set_attach_handler(xpc_service_t xservice, void (^handler)(xpc_service_instance_t xinstance)) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void _xpc_service_last_xref_cancel(xpc_service_t xservice) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_handle_service() {
|
||||||
|
// i have no clue what the types for the 3 arguments to this function are
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_handle_subservice() {
|
||||||
|
// same as for `xpc_handle_service`
|
||||||
|
};
|
98
src/service_instance.m
Normal file
98
src/service_instance.m
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#import <xpc/objects/service_instance.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
#import <xpc/endpoint.h>
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(service_instance);
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(service_instance)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(service_instance);
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_service_instance_dup2(xpc_service_instance_t xinstance, int fd, int new_fd) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void* xpc_service_instance_get_context(xpc_service_instance_t xinstance) {
|
||||||
|
// unsure about the return type, but given the name, it's the most likely type
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
pid_t xpc_service_instance_get_host_pid(xpc_service_instance_t xinstance) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
pid_t xpc_service_instance_get_pid(xpc_service_instance_t xinstance) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
int xpc_service_instance_get_type(xpc_service_instance_t xinstance) {
|
||||||
|
// unsure about the return type
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
bool xpc_service_instance_is_configurable(xpc_service_instance_t xinstance) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_service_instance_run(xpc_service_instance_t xinstance) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_service_instance_set_binpref(xpc_service_instance_t xinstance, int binpref) {
|
||||||
|
// unsure about `binpref`'s type
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_service_instance_set_context(xpc_service_instance_t xinstance, void* context) {
|
||||||
|
// unsure about `context`'s type, but given the name, it's the most likely type
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_service_instance_set_cwd(xpc_service_instance_t xinstance, const char* cwd) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_service_instance_set_endpoint(xpc_service_instance_t xinstance, xpc_endpoint_t endpoint) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_service_instance_set_environment(xpc_service_instance_t xinstance, xpc_object_t environment) {
|
||||||
|
// i'm pretty sure `environment` is a dictionary
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_service_instance_set_finalizer_f(xpc_service_instance_t xinstance, void (*finalizer)(void* context)) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_service_instance_set_jetsam_properties(xpc_service_instance_t xinstance, int flags, int priority, int memlimit) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_service_instance_set_path(xpc_service_instance_t xinstance, const char* path) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_service_instance_set_start_suspended(xpc_service_instance_t xinstance) {
|
||||||
|
// takes no other arguments; supposed to set the "start suspended" internal flag
|
||||||
|
};
|
45
src/shmem.m
Normal file
45
src/shmem.m
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#import <xpc/objects/shmem.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(shmem);
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(shmem)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(shmem);
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_shmem_create(void* region, size_t length) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
size_t xpc_shmem_map(xpc_object_t xshmem, void** region) {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
mach_port_t _xpc_shmem_get_mach_port(xpc_object_t xshmem) {
|
||||||
|
return MACH_PORT_NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_shmem_create_readonly(const void* region, size_t length) {
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
size_t xpc_shmem_get_length(xpc_object_t xshmem) {
|
||||||
|
return 0;
|
||||||
|
};
|
180
src/string.m
Normal file
180
src/string.m
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
#import <xpc/objects/string.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(string);
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(string)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(string);
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(string);
|
||||||
|
if (this->freeWhenDone && this->string) {
|
||||||
|
free((void*)this->string);
|
||||||
|
}
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (char*)xpcDescription
|
||||||
|
{
|
||||||
|
char* output = NULL;
|
||||||
|
if (self.UTF8String) {
|
||||||
|
asprintf(&output, "<%s: %s>", xpc_class_name(self), self.UTF8String);
|
||||||
|
} else {
|
||||||
|
asprintf(&output, "<%s: NULL>", xpc_class_name(self));
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)byteLength
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(string);
|
||||||
|
if (this->byteLength == NSUIntegerMax) {
|
||||||
|
return strlen(this->string);
|
||||||
|
}
|
||||||
|
return this->byteLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (const char*)UTF8String
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(string);
|
||||||
|
return this->string;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithUTF8String: (const char*)string
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
XPC_THIS_DECL(string);
|
||||||
|
|
||||||
|
this->byteLength = strlen(string);
|
||||||
|
char* buf = malloc(this->byteLength + 1);
|
||||||
|
if (!buf) {
|
||||||
|
[self release];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
strncpy(buf, string, this->byteLength + 1);
|
||||||
|
this->string = buf;
|
||||||
|
this->freeWhenDone = YES;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithUTF8StringNoCopy: (const char*)string freeWhenDone: (BOOL)freeIt
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
XPC_THIS_DECL(string);
|
||||||
|
|
||||||
|
this->byteLength = strlen(string);
|
||||||
|
this->string = string;
|
||||||
|
this->freeWhenDone = freeIt;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithFormat: (const char*)format, ...
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
self = [self initWithFormat: format arguments: args];
|
||||||
|
va_end(args);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithFormat: (const char*)format arguments: (va_list)args
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
XPC_THIS_DECL(string);
|
||||||
|
|
||||||
|
vasprintf((char**)&this->string, format, args);
|
||||||
|
if (!this->string) {
|
||||||
|
[self release];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
this->byteLength = strlen(this->string);
|
||||||
|
this->freeWhenDone = YES;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)replaceStringWithString: (const char*)string
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(string);
|
||||||
|
size_t newByteLength = strlen(string);
|
||||||
|
char* newString = malloc(newByteLength + 1);
|
||||||
|
if (!newString) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this->freeWhenDone && this->string) {
|
||||||
|
free((void*)this->string);
|
||||||
|
}
|
||||||
|
this->byteLength = newByteLength;
|
||||||
|
strncpy(newString, string, this->byteLength + 1);
|
||||||
|
this->string = newString;
|
||||||
|
this->freeWhenDone = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)hash
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(string);
|
||||||
|
return xpc_raw_data_hash(this->string, self.byteLength + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_string_create(const char* string) {
|
||||||
|
return [[XPC_CLASS(string) alloc] initWithUTF8String: string];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_string_create_with_format(const char* format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
xpc_object_t string = xpc_string_create_with_format_and_arguments(format, args);
|
||||||
|
va_end(args);
|
||||||
|
return string;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_string_create_with_format_and_arguments(const char* format, va_list args) {
|
||||||
|
return [[XPC_CLASS(string) alloc] initWithFormat: format arguments: args];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
size_t xpc_string_get_length(xpc_object_t xstring) {
|
||||||
|
TO_OBJC_CHECKED(string, xstring, string) {
|
||||||
|
return string.byteLength;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const char* xpc_string_get_string_ptr(xpc_object_t xstring) {
|
||||||
|
TO_OBJC_CHECKED(string, xstring, string) {
|
||||||
|
return string.UTF8String;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void _xpc_string_set_value(xpc_object_t xstring, const char* new_string) {
|
||||||
|
TO_OBJC_CHECKED(string, xstring, string) {
|
||||||
|
[string replaceStringWithString: new_string];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_string_create_no_copy(const char* string) {
|
||||||
|
return [[XPC_CLASS(string) alloc] initWithUTF8StringNoCopy: string freeWhenDone: NO];
|
||||||
|
};
|
590
src/subr_sbuf.c
590
src/subr_sbuf.c
@ -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 <sys/cdefs.h>
|
|
||||||
|
|
||||||
#include <sys/param.h>
|
|
||||||
|
|
||||||
#ifdef KERNEL
|
|
||||||
/* #include <ctype.h> */
|
|
||||||
#include <sys/kernel.h>
|
|
||||||
#include <sys/malloc.h>
|
|
||||||
#include <sys/systm.h>
|
|
||||||
#include <sys/uio.h>
|
|
||||||
#include <sys/uio_internal.h>
|
|
||||||
#include <sys/systm.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#else /* KERNEL */
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#endif /* KERNEL */
|
|
||||||
|
|
||||||
#include <sys/sbuf.h>
|
|
||||||
|
|
||||||
#ifndef PAGE_SIZE
|
|
||||||
# define PAGE_SIZE 4096
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef KERNEL
|
|
||||||
/* MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers"); */
|
|
||||||
#define SBMALLOC(size) _MALLOC(size, M_SBUF, M_WAITOK)
|
|
||||||
#define SBFREE(buf) FREE(buf, M_SBUF)
|
|
||||||
#else /* KERNEL */
|
|
||||||
#define KASSERT(e, m)
|
|
||||||
#define SBMALLOC(size) malloc(size)
|
|
||||||
#define SBFREE(buf) free(buf)
|
|
||||||
#define min(x,y) MIN(x,y)
|
|
||||||
|
|
||||||
#endif /* KERNEL */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Predicates
|
|
||||||
*/
|
|
||||||
#define SBUF_ISDYNAMIC(s) ((s)->s_flags & SBUF_DYNAMIC)
|
|
||||||
#define SBUF_ISDYNSTRUCT(s) ((s)->s_flags & SBUF_DYNSTRUCT)
|
|
||||||
#define SBUF_ISFINISHED(s) ((s)->s_flags & SBUF_FINISHED)
|
|
||||||
#define SBUF_HASOVERFLOWED(s) ((s)->s_flags & SBUF_OVERFLOWED)
|
|
||||||
#define SBUF_HASROOM(s) ((s)->s_len < (s)->s_size - 1)
|
|
||||||
#define SBUF_FREESPACE(s) ((s)->s_size - (s)->s_len - 1)
|
|
||||||
#define SBUF_CANEXTEND(s) ((s)->s_flags & SBUF_AUTOEXTEND)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set / clear flags
|
|
||||||
*/
|
|
||||||
#define SBUF_SETFLAG(s, f) do { (s)->s_flags |= (f); } while (0)
|
|
||||||
#define SBUF_CLEARFLAG(s, f) do { (s)->s_flags &= ~(f); } while (0)
|
|
||||||
|
|
||||||
#define SBUF_MINEXTENDSIZE 16 /* Should be power of 2. */
|
|
||||||
#define SBUF_MAXEXTENDSIZE PAGE_SIZE
|
|
||||||
#define SBUF_MAXEXTENDINCR PAGE_SIZE
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Debugging support
|
|
||||||
*/
|
|
||||||
#if defined(KERNEL) && defined(INVARIANTS)
|
|
||||||
static void
|
|
||||||
_assert_sbuf_integrity(const char *fun, struct sbuf *s)
|
|
||||||
{
|
|
||||||
KASSERT(s != NULL,
|
|
||||||
("%s called with a NULL sbuf pointer", fun));
|
|
||||||
KASSERT(s->s_buf != NULL,
|
|
||||||
("%s called with uninitialized or corrupt sbuf", fun));
|
|
||||||
KASSERT(s->s_len < s->s_size,
|
|
||||||
("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
_assert_sbuf_state(const char *fun, struct sbuf *s, int state)
|
|
||||||
{
|
|
||||||
KASSERT((s->s_flags & SBUF_FINISHED) == state,
|
|
||||||
("%s called with %sfinished or corrupt sbuf", fun,
|
|
||||||
(state ? "un" : "")));
|
|
||||||
}
|
|
||||||
#define assert_sbuf_integrity(s) _assert_sbuf_integrity(__func__, (s))
|
|
||||||
#define assert_sbuf_state(s, i) _assert_sbuf_state(__func__, (s), (i))
|
|
||||||
#else /* KERNEL && INVARIANTS */
|
|
||||||
#define assert_sbuf_integrity(s) do { } while (0)
|
|
||||||
#define assert_sbuf_state(s, i) do { } while (0)
|
|
||||||
#endif /* KERNEL && INVARIANTS */
|
|
||||||
|
|
||||||
static int
|
|
||||||
sbuf_extendsize(int size)
|
|
||||||
{
|
|
||||||
int newsize;
|
|
||||||
|
|
||||||
newsize = SBUF_MINEXTENDSIZE;
|
|
||||||
while (newsize < size) {
|
|
||||||
if (newsize < (int)SBUF_MAXEXTENDSIZE)
|
|
||||||
newsize *= 2;
|
|
||||||
else
|
|
||||||
newsize += SBUF_MAXEXTENDINCR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (newsize);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Extend an sbuf.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
sbuf_extend(struct sbuf *s, int addlen)
|
|
||||||
{
|
|
||||||
char *newbuf;
|
|
||||||
int newsize;
|
|
||||||
|
|
||||||
if (!SBUF_CANEXTEND(s))
|
|
||||||
return (-1);
|
|
||||||
|
|
||||||
newsize = sbuf_extendsize(s->s_size + addlen);
|
|
||||||
newbuf = (char *)SBMALLOC(newsize);
|
|
||||||
if (newbuf == NULL)
|
|
||||||
return (-1);
|
|
||||||
bcopy(s->s_buf, newbuf, s->s_size);
|
|
||||||
if (SBUF_ISDYNAMIC(s))
|
|
||||||
SBFREE(s->s_buf);
|
|
||||||
else
|
|
||||||
SBUF_SETFLAG(s, SBUF_DYNAMIC);
|
|
||||||
s->s_buf = newbuf;
|
|
||||||
s->s_size = newsize;
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize an sbuf.
|
|
||||||
* If buf is non-NULL, it points to a static or already-allocated string
|
|
||||||
* big enough to hold at least length characters.
|
|
||||||
*/
|
|
||||||
struct sbuf *
|
|
||||||
sbuf_new(struct sbuf *s, char *buf, int length, int flags)
|
|
||||||
{
|
|
||||||
KASSERT(length >= 0,
|
|
||||||
("attempt to create an sbuf of negative length (%d)", length));
|
|
||||||
KASSERT((flags & ~SBUF_USRFLAGMSK) == 0,
|
|
||||||
("%s called with invalid flags", __func__));
|
|
||||||
|
|
||||||
flags &= SBUF_USRFLAGMSK;
|
|
||||||
if (s == NULL) {
|
|
||||||
s = (struct sbuf *)SBMALLOC(sizeof *s);
|
|
||||||
if (s == NULL)
|
|
||||||
return (NULL);
|
|
||||||
bzero(s, sizeof *s);
|
|
||||||
s->s_flags = flags;
|
|
||||||
SBUF_SETFLAG(s, SBUF_DYNSTRUCT);
|
|
||||||
} else {
|
|
||||||
bzero(s, sizeof *s);
|
|
||||||
s->s_flags = flags;
|
|
||||||
}
|
|
||||||
s->s_size = length;
|
|
||||||
if (buf) {
|
|
||||||
s->s_buf = buf;
|
|
||||||
return (s);
|
|
||||||
}
|
|
||||||
if (flags & SBUF_AUTOEXTEND)
|
|
||||||
s->s_size = sbuf_extendsize(s->s_size);
|
|
||||||
s->s_buf = (char *)SBMALLOC(s->s_size);
|
|
||||||
if (s->s_buf == NULL) {
|
|
||||||
if (SBUF_ISDYNSTRUCT(s))
|
|
||||||
SBFREE(s);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
SBUF_SETFLAG(s, SBUF_DYNAMIC);
|
|
||||||
return (s);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef KERNEL
|
|
||||||
/*
|
|
||||||
* Create an sbuf with uio data
|
|
||||||
*/
|
|
||||||
struct sbuf *
|
|
||||||
sbuf_uionew(struct sbuf *s, struct uio *uio, int *error)
|
|
||||||
{
|
|
||||||
KASSERT(uio != NULL,
|
|
||||||
("%s called with NULL uio pointer", __func__));
|
|
||||||
KASSERT(error != NULL,
|
|
||||||
("%s called with NULL error pointer", __func__));
|
|
||||||
|
|
||||||
s = sbuf_new(s, NULL, uio_resid(uio) + 1, 0);
|
|
||||||
if (s == NULL) {
|
|
||||||
*error = ENOMEM;
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
*error = uiomove(s->s_buf, uio_resid(uio), uio);
|
|
||||||
if (*error != 0) {
|
|
||||||
sbuf_delete(s);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
s->s_len = s->s_size - 1;
|
|
||||||
*error = 0;
|
|
||||||
return (s);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Clear an sbuf and reset its position.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
sbuf_clear(struct sbuf *s)
|
|
||||||
{
|
|
||||||
assert_sbuf_integrity(s);
|
|
||||||
/* don't care if it's finished or not */
|
|
||||||
|
|
||||||
SBUF_CLEARFLAG(s, SBUF_FINISHED);
|
|
||||||
SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
|
|
||||||
s->s_len = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set the sbuf's end position to an arbitrary value.
|
|
||||||
* Effectively truncates the sbuf at the new position.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
sbuf_setpos(struct sbuf *s, int pos)
|
|
||||||
{
|
|
||||||
assert_sbuf_integrity(s);
|
|
||||||
assert_sbuf_state(s, 0);
|
|
||||||
|
|
||||||
KASSERT(pos >= 0,
|
|
||||||
("attempt to seek to a negative position (%d)", pos));
|
|
||||||
KASSERT(pos < s->s_size,
|
|
||||||
("attempt to seek past end of sbuf (%d >= %d)", pos, s->s_size));
|
|
||||||
|
|
||||||
if (pos < 0 || pos > s->s_len)
|
|
||||||
return (-1);
|
|
||||||
s->s_len = pos;
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Append a byte string to an sbuf.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
sbuf_bcat(struct sbuf *s, const void *buf, size_t len)
|
|
||||||
{
|
|
||||||
const char *str = buf;
|
|
||||||
|
|
||||||
assert_sbuf_integrity(s);
|
|
||||||
assert_sbuf_state(s, 0);
|
|
||||||
|
|
||||||
if (SBUF_HASOVERFLOWED(s))
|
|
||||||
return (-1);
|
|
||||||
|
|
||||||
for (; len; len--) {
|
|
||||||
if (!SBUF_HASROOM(s) && sbuf_extend(s, len) < 0)
|
|
||||||
break;
|
|
||||||
s->s_buf[s->s_len++] = *str++;
|
|
||||||
}
|
|
||||||
if (len) {
|
|
||||||
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef KERNEL
|
|
||||||
/*
|
|
||||||
* Copy a byte string from userland into an sbuf.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
sbuf_bcopyin(struct sbuf *s, const void *uaddr, size_t len)
|
|
||||||
{
|
|
||||||
assert_sbuf_integrity(s);
|
|
||||||
assert_sbuf_state(s, 0);
|
|
||||||
|
|
||||||
if (SBUF_HASOVERFLOWED(s))
|
|
||||||
return (-1);
|
|
||||||
|
|
||||||
if (len == 0)
|
|
||||||
return (0);
|
|
||||||
if (len > (unsigned) SBUF_FREESPACE(s)) {
|
|
||||||
sbuf_extend(s, len - SBUF_FREESPACE(s));
|
|
||||||
len = min(len, SBUF_FREESPACE(s));
|
|
||||||
}
|
|
||||||
if (copyin(CAST_USER_ADDR_T(uaddr), s->s_buf + s->s_len, len) != 0)
|
|
||||||
return (-1);
|
|
||||||
s->s_len += len;
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy a byte string into an sbuf.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
sbuf_bcpy(struct sbuf *s, const void *buf, size_t len)
|
|
||||||
{
|
|
||||||
assert_sbuf_integrity(s);
|
|
||||||
assert_sbuf_state(s, 0);
|
|
||||||
|
|
||||||
sbuf_clear(s);
|
|
||||||
return (sbuf_bcat(s, buf, len));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Append a string to an sbuf.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
sbuf_cat(struct sbuf *s, const char *str)
|
|
||||||
{
|
|
||||||
assert_sbuf_integrity(s);
|
|
||||||
assert_sbuf_state(s, 0);
|
|
||||||
|
|
||||||
if (SBUF_HASOVERFLOWED(s))
|
|
||||||
return (-1);
|
|
||||||
|
|
||||||
while (*str) {
|
|
||||||
if (!SBUF_HASROOM(s) && sbuf_extend(s, strlen(str)) < 0)
|
|
||||||
break;
|
|
||||||
s->s_buf[s->s_len++] = *str++;
|
|
||||||
}
|
|
||||||
if (*str) {
|
|
||||||
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef KERNEL
|
|
||||||
/*
|
|
||||||
* Append a string from userland to an sbuf.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
sbuf_copyin(struct sbuf *s, const void *uaddr, size_t len)
|
|
||||||
{
|
|
||||||
size_t done;
|
|
||||||
|
|
||||||
assert_sbuf_integrity(s);
|
|
||||||
assert_sbuf_state(s, 0);
|
|
||||||
|
|
||||||
if (SBUF_HASOVERFLOWED(s))
|
|
||||||
return (-1);
|
|
||||||
|
|
||||||
if (len == 0)
|
|
||||||
len = SBUF_FREESPACE(s); /* XXX return 0? */
|
|
||||||
if (len > (unsigned) SBUF_FREESPACE(s)) {
|
|
||||||
sbuf_extend(s, len);
|
|
||||||
len = min(len, SBUF_FREESPACE(s));
|
|
||||||
}
|
|
||||||
switch (copyinstr(CAST_USER_ADDR_T(uaddr), s->s_buf + s->s_len, len + 1, &done)) {
|
|
||||||
case ENAMETOOLONG:
|
|
||||||
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
|
|
||||||
/* fall through */
|
|
||||||
case 0:
|
|
||||||
s->s_len += done - 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return (-1); /* XXX */
|
|
||||||
}
|
|
||||||
|
|
||||||
return (done);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy a string into an sbuf.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
sbuf_cpy(struct sbuf *s, const char *str)
|
|
||||||
{
|
|
||||||
assert_sbuf_integrity(s);
|
|
||||||
assert_sbuf_state(s, 0);
|
|
||||||
|
|
||||||
sbuf_clear(s);
|
|
||||||
return (sbuf_cat(s, str));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Format the given argument list and append the resulting string to an sbuf.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
|
|
||||||
{
|
|
||||||
__builtin_va_list ap_copy; /* XXX tduffy - blame on him */
|
|
||||||
int len;
|
|
||||||
|
|
||||||
assert_sbuf_integrity(s);
|
|
||||||
assert_sbuf_state(s, 0);
|
|
||||||
|
|
||||||
KASSERT(fmt != NULL,
|
|
||||||
("%s called with a NULL format string", __func__));
|
|
||||||
|
|
||||||
if (SBUF_HASOVERFLOWED(s))
|
|
||||||
return (-1);
|
|
||||||
|
|
||||||
do {
|
|
||||||
va_copy(ap_copy, ap);
|
|
||||||
len = vsnprintf(&s->s_buf[s->s_len], SBUF_FREESPACE(s) + 1,
|
|
||||||
fmt, ap_copy);
|
|
||||||
va_end(ap_copy);
|
|
||||||
} while (len > SBUF_FREESPACE(s) &&
|
|
||||||
sbuf_extend(s, len - SBUF_FREESPACE(s)) == 0);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* s->s_len is the length of the string, without the terminating nul.
|
|
||||||
* When updating s->s_len, we must subtract 1 from the length that
|
|
||||||
* we passed into vsnprintf() because that length includes the
|
|
||||||
* terminating nul.
|
|
||||||
*
|
|
||||||
* vsnprintf() returns the amount that would have been copied,
|
|
||||||
* given sufficient space, hence the min() calculation below.
|
|
||||||
*/
|
|
||||||
s->s_len += min(len, SBUF_FREESPACE(s));
|
|
||||||
if (!SBUF_HASROOM(s) && !SBUF_CANEXTEND(s))
|
|
||||||
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
|
|
||||||
|
|
||||||
KASSERT(s->s_len < s->s_size,
|
|
||||||
("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
|
|
||||||
|
|
||||||
if (SBUF_HASOVERFLOWED(s))
|
|
||||||
return (-1);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Format the given arguments and append the resulting string to an sbuf.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
sbuf_printf(struct sbuf *s, const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
result = sbuf_vprintf(s, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
return(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Append a character to an sbuf.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
sbuf_putc(struct sbuf *s, int c)
|
|
||||||
{
|
|
||||||
assert_sbuf_integrity(s);
|
|
||||||
assert_sbuf_state(s, 0);
|
|
||||||
|
|
||||||
if (SBUF_HASOVERFLOWED(s))
|
|
||||||
return (-1);
|
|
||||||
|
|
||||||
if (!SBUF_HASROOM(s) && sbuf_extend(s, 1) < 0) {
|
|
||||||
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
if (c != '\0')
|
|
||||||
s->s_buf[s->s_len++] = c;
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef KERNEL
|
|
||||||
static inline int
|
|
||||||
isspace(char ch)
|
|
||||||
{
|
|
||||||
return (ch == ' ' || ch == '\n' || ch == '\t');
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Trim whitespace characters from end of an sbuf.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
sbuf_trim(struct sbuf *s)
|
|
||||||
{
|
|
||||||
assert_sbuf_integrity(s);
|
|
||||||
assert_sbuf_state(s, 0);
|
|
||||||
|
|
||||||
if (SBUF_HASOVERFLOWED(s))
|
|
||||||
return (-1);
|
|
||||||
|
|
||||||
while (s->s_len && isspace(s->s_buf[s->s_len-1]))
|
|
||||||
--s->s_len;
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check if an sbuf overflowed
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
sbuf_overflowed(struct sbuf *s)
|
|
||||||
{
|
|
||||||
return SBUF_HASOVERFLOWED(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Finish off an sbuf.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
sbuf_finish(struct sbuf *s)
|
|
||||||
{
|
|
||||||
assert_sbuf_integrity(s);
|
|
||||||
assert_sbuf_state(s, 0);
|
|
||||||
|
|
||||||
s->s_buf[s->s_len] = '\0';
|
|
||||||
SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
|
|
||||||
SBUF_SETFLAG(s, SBUF_FINISHED);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return a pointer to the sbuf data.
|
|
||||||
*/
|
|
||||||
char *
|
|
||||||
sbuf_data(struct sbuf *s)
|
|
||||||
{
|
|
||||||
assert_sbuf_integrity(s);
|
|
||||||
assert_sbuf_state(s, SBUF_FINISHED);
|
|
||||||
|
|
||||||
return s->s_buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return the length of the sbuf data.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
sbuf_len(struct sbuf *s)
|
|
||||||
{
|
|
||||||
assert_sbuf_integrity(s);
|
|
||||||
/* don't care if it's finished or not */
|
|
||||||
|
|
||||||
if (SBUF_HASOVERFLOWED(s))
|
|
||||||
return (-1);
|
|
||||||
return s->s_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Clear an sbuf, free its buffer if necessary.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
sbuf_delete(struct sbuf *s)
|
|
||||||
{
|
|
||||||
int isdyn;
|
|
||||||
|
|
||||||
assert_sbuf_integrity(s);
|
|
||||||
/* don't care if it's finished or not */
|
|
||||||
|
|
||||||
if (SBUF_ISDYNAMIC(s))
|
|
||||||
SBFREE(s->s_buf);
|
|
||||||
isdyn = SBUF_ISDYNSTRUCT(s);
|
|
||||||
bzero(s, sizeof *s);
|
|
||||||
if (isdyn)
|
|
||||||
SBFREE(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check if an sbuf has been finished.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
sbuf_done(struct sbuf *s)
|
|
||||||
{
|
|
||||||
|
|
||||||
return(SBUF_ISFINISHED(s));
|
|
||||||
}
|
|
29
src/todo.c
29
src/todo.c
@ -1,29 +0,0 @@
|
|||||||
#include <xpc/private.h>
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#define STUB() printf("STUB %s called\n", __func__)
|
|
||||||
|
|
||||||
xpc_object_t xpc_create_from_plist(void *data, size_t size)
|
|
||||||
{
|
|
||||||
STUB();
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void xpc_connection_set_target_uid(xpc_connection_t connection, uid_t uid)
|
|
||||||
{
|
|
||||||
STUB();
|
|
||||||
}
|
|
||||||
|
|
||||||
void xpc_connection_set_instance(xpc_connection_t connection, uuid_t uid)
|
|
||||||
{
|
|
||||||
STUB();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* already implemented!
|
|
||||||
void xpc_dictionary_set_mach_send(xpc_object_t object, char *type, int port)
|
|
||||||
{
|
|
||||||
STUB();
|
|
||||||
}
|
|
||||||
*/
|
|
@ -1,31 +1,74 @@
|
|||||||
#include <os/transaction_private.h>
|
#include <os/transaction_private.h>
|
||||||
#include <os/object_private.h>
|
#include <os/object_private.h>
|
||||||
|
#import <Foundation/NSZone.h>
|
||||||
struct os_transaction_s;
|
#import <xpc/xpc.h>
|
||||||
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));
|
|
||||||
};
|
|
||||||
|
|
||||||
OS_OBJECT_NONLAZY_CLASS
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
@implementation OS_OBJECT_CLASS(os_transaction)
|
@implementation OS_OBJECT_CLASS(os_transaction)
|
||||||
|
|
||||||
OS_OBJECT_NONLAZY_CLASS_LOAD
|
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
|
@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) {
|
||||||
|
|
||||||
|
};
|
||||||
|
525
src/type.c
525
src/type.c
@ -1,525 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2014-2015 iXsystems, Inc.
|
|
||||||
* All rights reserved
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted providing that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
||||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
||||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
||||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <mach/mach.h>
|
|
||||||
#include <xpc/launchd.h>
|
|
||||||
#include <sys/fileport.h>
|
|
||||||
#include <xpc/internal.h>
|
|
||||||
|
|
||||||
struct _xpc_type_s {
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef const struct _xpc_type_s xt;
|
|
||||||
xt _xpc_type_activity;
|
|
||||||
xt _xpc_type_array;
|
|
||||||
xt _xpc_type_bool;
|
|
||||||
xt _xpc_type_connection;
|
|
||||||
xt _xpc_type_data;
|
|
||||||
xt _xpc_type_date;
|
|
||||||
xt _xpc_type_dictionary;
|
|
||||||
xt _xpc_type_endpoint;
|
|
||||||
xt _xpc_type_null;
|
|
||||||
xt _xpc_type_error;
|
|
||||||
xt _xpc_type_fd;
|
|
||||||
xt _xpc_type_int64;
|
|
||||||
xt _xpc_type_uint64;
|
|
||||||
xt _xpc_type_shmem;
|
|
||||||
xt _xpc_type_string;
|
|
||||||
xt _xpc_type_uuid;
|
|
||||||
xt _xpc_type_double;
|
|
||||||
xt _xpc_type_pointer;
|
|
||||||
|
|
||||||
struct _xpc_bool_s {
|
|
||||||
// we have to wrap it in `_xpc_bool_s` because the header defines the variable with that type
|
|
||||||
struct xpc_object xo;
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct _xpc_bool_s _xpc_bool_true = {
|
|
||||||
.xo = _XPC_GLOBAL_OBJECT_INITIALIZER(_XPC_TYPE_BOOL, 0, .b = true),
|
|
||||||
};
|
|
||||||
const struct _xpc_bool_s _xpc_bool_false = {
|
|
||||||
.xo = _XPC_GLOBAL_OBJECT_INITIALIZER(_XPC_TYPE_BOOL, 0, .b = false),
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct xpc_object _xpc_null = _XPC_GLOBAL_OBJECT_INITIALIZER(_XPC_TYPE_NULL, 0, .b = false); // requires a union value, so just set the boolean
|
|
||||||
|
|
||||||
static size_t xpc_data_hash(const uint8_t *data, size_t length);
|
|
||||||
|
|
||||||
static xpc_type_t xpc_typemap[] = {
|
|
||||||
NULL,
|
|
||||||
XPC_TYPE_DICTIONARY,
|
|
||||||
XPC_TYPE_ARRAY,
|
|
||||||
XPC_TYPE_BOOL,
|
|
||||||
XPC_TYPE_CONNECTION,
|
|
||||||
XPC_TYPE_ENDPOINT,
|
|
||||||
XPC_TYPE_NULL,
|
|
||||||
XPC_TYPE_ACTIVITY,
|
|
||||||
XPC_TYPE_INT64,
|
|
||||||
XPC_TYPE_UINT64,
|
|
||||||
XPC_TYPE_DATE,
|
|
||||||
XPC_TYPE_DATA,
|
|
||||||
XPC_TYPE_STRING,
|
|
||||||
XPC_TYPE_UUID,
|
|
||||||
XPC_TYPE_FD,
|
|
||||||
XPC_TYPE_SHMEM,
|
|
||||||
XPC_TYPE_ERROR,
|
|
||||||
XPC_TYPE_DOUBLE,
|
|
||||||
XPC_TYPE_POINTER,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char *xpc_typestr[] = {
|
|
||||||
"invalid",
|
|
||||||
"dictionary",
|
|
||||||
"array",
|
|
||||||
"bool",
|
|
||||||
"connection",
|
|
||||||
"endpoint",
|
|
||||||
"null",
|
|
||||||
"activity",
|
|
||||||
"int64",
|
|
||||||
"uint64",
|
|
||||||
"date",
|
|
||||||
"data",
|
|
||||||
"string",
|
|
||||||
"uuid",
|
|
||||||
"fd",
|
|
||||||
"shmem",
|
|
||||||
"error",
|
|
||||||
"double",
|
|
||||||
"pointer",
|
|
||||||
};
|
|
||||||
|
|
||||||
__private_extern__ struct xpc_object *
|
|
||||||
_xpc_prim_create(int type, xpc_u value, size_t size)
|
|
||||||
{
|
|
||||||
|
|
||||||
return (_xpc_prim_create_flags(type, value, size, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
__private_extern__ struct xpc_object *
|
|
||||||
_xpc_prim_create_flags(int type, xpc_u value, size_t size, uint16_t flags)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
|
|
||||||
if ((xo = malloc(sizeof(*xo))) == NULL)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
xo->xo_size = size;
|
|
||||||
xo->xo_xpc_type = type;
|
|
||||||
xo->xo_flags = flags;
|
|
||||||
xo->xo_u = value;
|
|
||||||
xo->xo_refcnt = 1;
|
|
||||||
xo->xo_audit_token = NULL;
|
|
||||||
|
|
||||||
if (type == _XPC_TYPE_DICTIONARY)
|
|
||||||
TAILQ_INIT(&xo->xo_dict);
|
|
||||||
|
|
||||||
if (type == _XPC_TYPE_ARRAY)
|
|
||||||
TAILQ_INIT(&xo->xo_array);
|
|
||||||
|
|
||||||
if (type == _XPC_TYPE_ACTIVITY)
|
|
||||||
xo->xo_activity.criteria = xpc_dictionary_create(NULL, NULL, 0);
|
|
||||||
|
|
||||||
return (xo);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_null_create(void)
|
|
||||||
{
|
|
||||||
return &_xpc_null;
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_bool_create(bool value)
|
|
||||||
{
|
|
||||||
return value ? XPC_BOOL_TRUE : XPC_BOOL_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
xpc_bool_get_value(xpc_object_t xbool)
|
|
||||||
{
|
|
||||||
return xbool == XPC_BOOL_FALSE ? false : true;
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_int64_create(int64_t value)
|
|
||||||
{
|
|
||||||
xpc_u val;
|
|
||||||
|
|
||||||
val.i = value;
|
|
||||||
return _xpc_prim_create(_XPC_TYPE_INT64, val, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t
|
|
||||||
xpc_int64_get_value(xpc_object_t xint)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
|
|
||||||
xo = xint;
|
|
||||||
if (xo == NULL)
|
|
||||||
return (0);
|
|
||||||
|
|
||||||
if (xo->xo_xpc_type == _XPC_TYPE_INT64)
|
|
||||||
return (xo->xo_int);
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_uint64_create(uint64_t value)
|
|
||||||
{
|
|
||||||
xpc_u val;
|
|
||||||
|
|
||||||
val.ui = value;
|
|
||||||
return _xpc_prim_create(_XPC_TYPE_UINT64, val, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t
|
|
||||||
xpc_uint64_get_value(xpc_object_t xuint)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
|
|
||||||
xo = xuint;
|
|
||||||
if (xo == NULL)
|
|
||||||
return (0);
|
|
||||||
|
|
||||||
if (xo->xo_xpc_type == _XPC_TYPE_UINT64)
|
|
||||||
return (xo->xo_uint);
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_double_create(double value)
|
|
||||||
{
|
|
||||||
xpc_u val;
|
|
||||||
|
|
||||||
val.d = value;
|
|
||||||
return _xpc_prim_create(_XPC_TYPE_DOUBLE, val, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
double
|
|
||||||
xpc_double_get_value(xpc_object_t xdouble)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo = xdouble;
|
|
||||||
|
|
||||||
if (xo->xo_xpc_type == _XPC_TYPE_DOUBLE)
|
|
||||||
return (xo->xo_d);
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_date_create(int64_t interval)
|
|
||||||
{
|
|
||||||
xpc_u val;
|
|
||||||
|
|
||||||
val.i = interval;
|
|
||||||
return _xpc_prim_create(_XPC_TYPE_DATE, val, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_date_create_from_current(void)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t
|
|
||||||
xpc_date_get_value(xpc_object_t xdate)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo = xdate;
|
|
||||||
|
|
||||||
if (xo == NULL)
|
|
||||||
return (0);
|
|
||||||
|
|
||||||
if (xo->xo_xpc_type == _XPC_TYPE_DATE)
|
|
||||||
return (xo->xo_int);
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_data_create(const void *bytes, size_t length)
|
|
||||||
{
|
|
||||||
xpc_u val;
|
|
||||||
|
|
||||||
void* copy = malloc(length);
|
|
||||||
memcpy(copy, bytes, length);
|
|
||||||
val.ptr = copy;
|
|
||||||
return _xpc_prim_create(_XPC_TYPE_DATA, val, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_data_create_with_dispatch_data(dispatch_data_t ddata)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
|
||||||
xpc_data_get_length(xpc_object_t xdata)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo = xdata;
|
|
||||||
|
|
||||||
if (xo == NULL)
|
|
||||||
return (0);
|
|
||||||
|
|
||||||
if (xo->xo_xpc_type == _XPC_TYPE_DATA)
|
|
||||||
return (xo->xo_size);
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const void *
|
|
||||||
xpc_data_get_bytes_ptr(xpc_object_t xdata)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo = xdata;
|
|
||||||
|
|
||||||
if (xo == NULL)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
if (xo->xo_xpc_type == _XPC_TYPE_DATA)
|
|
||||||
return (void *)(xo->xo_ptr);
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_fd_create(int fd)
|
|
||||||
{
|
|
||||||
xpc_u val;
|
|
||||||
fileport_makeport(fd, &val.port);
|
|
||||||
return _xpc_prim_create(_XPC_TYPE_FD, val, sizeof(val.port));
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
xpc_fd_dup(xpc_object_t xfd)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo = xfd;
|
|
||||||
if (xo->xo_xpc_type != _XPC_TYPE_FD) return -1;
|
|
||||||
return fileport_makefd(xo->xo_u.port);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
|
||||||
xpc_data_get_bytes(xpc_object_t xdata, void *buffer, size_t off, size_t length)
|
|
||||||
{
|
|
||||||
|
|
||||||
/* XXX */
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_string_create(const char *string)
|
|
||||||
{
|
|
||||||
xpc_u val;
|
|
||||||
|
|
||||||
size_t len = strlen(string);
|
|
||||||
char* str = malloc(len + 1);
|
|
||||||
strncpy(str, string, len);
|
|
||||||
str[len] = '\0';
|
|
||||||
val.str = str;
|
|
||||||
return _xpc_prim_create(_XPC_TYPE_STRING, val, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_string_create_with_format(const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
xpc_u val;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vasprintf((char **)&val.str, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
return _xpc_prim_create(_XPC_TYPE_STRING, val, strlen(val.str));
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_string_create_with_format_and_arguments(const char *fmt, va_list ap)
|
|
||||||
{
|
|
||||||
xpc_u val;
|
|
||||||
|
|
||||||
vasprintf((char **)&val.str, fmt, ap);
|
|
||||||
return _xpc_prim_create(_XPC_TYPE_STRING, val, strlen(val.str));
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
|
||||||
xpc_string_get_length(xpc_object_t xstring)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo = xstring;
|
|
||||||
|
|
||||||
if (xo == NULL)
|
|
||||||
return (0);
|
|
||||||
|
|
||||||
if (xo->xo_xpc_type == _XPC_TYPE_STRING)
|
|
||||||
return (xo->xo_size);
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *
|
|
||||||
xpc_string_get_string_ptr(xpc_object_t xstring)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo = xstring;
|
|
||||||
|
|
||||||
if (xo == NULL)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
if (xo->xo_xpc_type == _XPC_TYPE_STRING)
|
|
||||||
return (xo->xo_str);
|
|
||||||
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t
|
|
||||||
xpc_uuid_create(const uuid_t uuid)
|
|
||||||
{
|
|
||||||
xpc_u val;
|
|
||||||
|
|
||||||
memcpy(val.uuid, uuid, sizeof(uuid_t));
|
|
||||||
return _xpc_prim_create(_XPC_TYPE_UUID, val, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t *
|
|
||||||
xpc_uuid_get_bytes(xpc_object_t xuuid)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
|
|
||||||
xo = xuuid;
|
|
||||||
if (xo == NULL)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
if (xo->xo_xpc_type == _XPC_TYPE_UUID)
|
|
||||||
return ((uint8_t*)&xo->xo_uuid);
|
|
||||||
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_type_t
|
|
||||||
xpc_get_type(xpc_object_t obj)
|
|
||||||
{
|
|
||||||
struct xpc_object* xo = obj;
|
|
||||||
|
|
||||||
// temporary hack
|
|
||||||
if (xo->xo_xpc_type == _XPC_TYPE_DICTIONARY && xpc_dictionary_get_value(obj, XPC_ERROR_KEY_DESCRIPTION) != NULL) {
|
|
||||||
return XPC_TYPE_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (xpc_typemap[xo->xo_xpc_type]);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
xpc_equal(xpc_object_t x1, xpc_object_t x2)
|
|
||||||
{
|
|
||||||
return xpc_hash(x1) == xpc_hash(x2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t
|
|
||||||
xpc_data_hash(const uint8_t *data, size_t length)
|
|
||||||
{
|
|
||||||
size_t hash = 5381;
|
|
||||||
|
|
||||||
while (length--)
|
|
||||||
hash = ((hash << 5) + hash) + data[length];
|
|
||||||
|
|
||||||
return (hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
|
||||||
xpc_hash(xpc_object_t obj)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
__block size_t hash = 0;
|
|
||||||
|
|
||||||
xo = obj;
|
|
||||||
switch (xo->xo_xpc_type) {
|
|
||||||
case _XPC_TYPE_BOOL:
|
|
||||||
case _XPC_TYPE_INT64:
|
|
||||||
case _XPC_TYPE_UINT64:
|
|
||||||
case _XPC_TYPE_DATE:
|
|
||||||
case _XPC_TYPE_ENDPOINT:
|
|
||||||
return ((size_t)xo->xo_u.ui);
|
|
||||||
|
|
||||||
case _XPC_TYPE_STRING:
|
|
||||||
return (xpc_data_hash(
|
|
||||||
(const uint8_t *)xpc_string_get_string_ptr(obj),
|
|
||||||
xpc_string_get_length(obj)));
|
|
||||||
|
|
||||||
case _XPC_TYPE_DATA:
|
|
||||||
return (xpc_data_hash(
|
|
||||||
xpc_data_get_bytes_ptr(obj),
|
|
||||||
xpc_data_get_length(obj)));
|
|
||||||
|
|
||||||
case _XPC_TYPE_DICTIONARY:
|
|
||||||
xpc_dictionary_apply(obj, ^(const char *k, xpc_object_t v) {
|
|
||||||
hash ^= xpc_data_hash((const uint8_t *)k, strlen(k));
|
|
||||||
hash ^= xpc_hash(v);
|
|
||||||
return ((bool)true);
|
|
||||||
});
|
|
||||||
return (hash);
|
|
||||||
|
|
||||||
case _XPC_TYPE_ARRAY:
|
|
||||||
xpc_array_apply(obj, ^(size_t idx, xpc_object_t v) {
|
|
||||||
hash ^= xpc_hash(v);
|
|
||||||
return ((bool)true);
|
|
||||||
});
|
|
||||||
return (hash);
|
|
||||||
case _XPC_TYPE_POINTER:
|
|
||||||
return ((size_t)xo->xo_u.ptr);
|
|
||||||
case _XPC_TYPE_CONNECTION:
|
|
||||||
// two connections can only be equal if they are the same object (i.e. with the same pointers)
|
|
||||||
return (size_t)obj;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__private_extern__ const char *
|
|
||||||
_xpc_get_type_name(xpc_object_t obj)
|
|
||||||
{
|
|
||||||
struct xpc_object *xo;
|
|
||||||
|
|
||||||
xo = obj;
|
|
||||||
return (xpc_typestr[xo->xo_xpc_type]);
|
|
||||||
}
|
|
||||||
|
|
||||||
xpc_object_t xpc_pointer_create(void* value) {
|
|
||||||
xpc_u val;
|
|
||||||
val.ptr = (uintptr_t)value;
|
|
||||||
return _xpc_prim_create(_XPC_TYPE_POINTER, val, 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
void* xpc_pointer_get_value(xpc_object_t xptr) {
|
|
||||||
struct xpc_object* xo = xptr;
|
|
||||||
|
|
||||||
if (xo == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (xo->xo_xpc_type == _XPC_TYPE_POINTER)
|
|
||||||
return xo->xo_ptr;
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
};
|
|
42
src/type.m
Normal file
42
src/type.m
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#import <xpc/xpc.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_type_t xpc_get_type(xpc_object_t xobject) {
|
||||||
|
TO_OBJC_CHECKED(object, xobject, object) {
|
||||||
|
return (xpc_type_t)[object class];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// private C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const char* xpc_type_get_name(xpc_type_t xtype) {
|
||||||
|
return class_getName((Class)xtype);
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
Class xpc_get_class4NSXPC(xpc_type_t xtype) {
|
||||||
|
return (Class)xtype;
|
||||||
|
};
|
||||||
|
XPC_EXPORT
|
||||||
|
bool xpc_is_kind_of_xpc_object4NSXPC(xpc_object_t object) {
|
||||||
|
return [object isKindOfClass: [XPC_CLASS(object) class]];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct some_extension_type_struct;
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
void xpc_extension_type_init(struct some_extension_type_struct* extension_type, void* some_user_function_probably) {
|
||||||
|
|
||||||
|
};
|
22
src/uint64.m
Normal file
22
src/uint64.m
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#import <xpc/objects/uint64.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
XPC_WRAPPER_CLASS_IMPL(uint64, uint64_t, "%llu");
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_uint64_create(uint64_t value) {
|
||||||
|
return [[XPC_CLASS(uint64) alloc] initWithValue: value];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
uint64_t xpc_uint64_get_value(xpc_object_t xuint) {
|
||||||
|
TO_OBJC_CHECKED(uint64, xuint, uinteger) {
|
||||||
|
return uinteger.value;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
69
src/util.m
Normal file
69
src/util.m
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#import <xpc/util.h>
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
|
||||||
|
XPC_CLASS(object)* xpc_retain_for_collection(XPC_CLASS(object)* object) {
|
||||||
|
// connections aren't retained by collections
|
||||||
|
if ([object isKindOfClass: [XPC_CLASS(connection) class]]) {
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
return [object retain];
|
||||||
|
};
|
||||||
|
|
||||||
|
void xpc_release_for_collection(XPC_CLASS(object)* object) {
|
||||||
|
if ([object isKindOfClass: [XPC_CLASS(connection) class]]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return [object release];
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* xpc_class_name(XPC_CLASS(object)* object) {
|
||||||
|
return class_getName([object class]);
|
||||||
|
};
|
||||||
|
|
||||||
|
char* xpc_description_indent(const char* description, bool indentFirstLine) {
|
||||||
|
size_t newSize = 0;
|
||||||
|
for (size_t i = 0; description[i] != '\0'; ++i) {
|
||||||
|
++newSize;
|
||||||
|
if (description[i] == '\n') {
|
||||||
|
++newSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (indentFirstLine) {
|
||||||
|
++newSize;
|
||||||
|
}
|
||||||
|
char* newDescription = malloc(sizeof(char) * (newSize + 1));
|
||||||
|
if (indentFirstLine) {
|
||||||
|
newDescription[0] = '\t';
|
||||||
|
}
|
||||||
|
for (size_t i = 0, j = indentFirstLine ? 1 : 0; description[i] != '\0'; ++i, ++j) {
|
||||||
|
newDescription[j] = description[i];
|
||||||
|
if (description[i] == '\n') {
|
||||||
|
newDescription[++j] = '\t';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newDescription[newSize] = '\0';
|
||||||
|
return newDescription;
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t xpc_raw_data_hash(const void* data, size_t data_length) {
|
||||||
|
if (data_length == 0) {
|
||||||
|
return 0x1505;
|
||||||
|
}
|
||||||
|
size_t result = 0;
|
||||||
|
for (size_t i = 0; i < data_length; ++i) {
|
||||||
|
uint8_t current = ((uint8_t*)data)[i];
|
||||||
|
if (current == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result = (result * 0x21) + current;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
kern_return_t xpc_mach_port_retain_right(mach_port_name_t port, mach_port_right_t right) {
|
||||||
|
return mach_port_mod_refs(mach_task_self(), port, right, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
kern_return_t xpc_mach_port_release_right(mach_port_name_t port, mach_port_right_t right) {
|
||||||
|
return mach_port_mod_refs(mach_task_self(), port, right, -1);
|
||||||
|
};
|
61
src/uuid.m
Normal file
61
src/uuid.m
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#import <xpc/objects/uuid.h>
|
||||||
|
#import <xpc/util.h>
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
#define UUID_STRING_LENGTH 36
|
||||||
|
|
||||||
|
XPC_CLASS_SYMBOL_DECL(uuid);
|
||||||
|
|
||||||
|
OS_OBJECT_NONLAZY_CLASS
|
||||||
|
@implementation XPC_CLASS(uuid)
|
||||||
|
|
||||||
|
XPC_CLASS_HEADER(uuid);
|
||||||
|
|
||||||
|
- (char*)xpcDescription
|
||||||
|
{
|
||||||
|
char* output = NULL;
|
||||||
|
char asString[UUID_STRING_LENGTH + 1];
|
||||||
|
uuid_unparse(self.bytes, asString);
|
||||||
|
asprintf(&output, "<%s: %s>", xpc_class_name(self), asString);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (uint8_t*)bytes
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(uuid);
|
||||||
|
return this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithBytes: (const uint8_t*)bytes
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
XPC_THIS_DECL(uuid);
|
||||||
|
memcpy(this->value, bytes, sizeof(this->value));
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)hash
|
||||||
|
{
|
||||||
|
XPC_THIS_DECL(uuid);
|
||||||
|
return xpc_raw_data_hash(this->value, sizeof(this->value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// C API
|
||||||
|
//
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
xpc_object_t xpc_uuid_create(const uuid_t uuid) {
|
||||||
|
return [[XPC_CLASS(uuid) alloc] initWithBytes: uuid];
|
||||||
|
};
|
||||||
|
|
||||||
|
XPC_EXPORT
|
||||||
|
const uint8_t* xpc_uuid_get_bytes(xpc_object_t xuuid) {
|
||||||
|
TO_OBJC_CHECKED(uuid, xuuid, uuid) {
|
||||||
|
return uuid.bytes;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
};
|
25
test/CMakeLists.txt
Normal file
25
test/CMakeLists.txt
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
set(TEST_SOURCES
|
||||||
|
array.m
|
||||||
|
base.m
|
||||||
|
bool.c
|
||||||
|
data.c
|
||||||
|
dictionary.m
|
||||||
|
main.c
|
||||||
|
string.c
|
||||||
|
type.c
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/ctest"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_darling_executable(test_libxpc ${TEST_SOURCES})
|
||||||
|
|
||||||
|
target_link_libraries(test_libxpc
|
||||||
|
# link statically to ensure we always use our copy of libxpc for testing, not the system's
|
||||||
|
xpc_static
|
||||||
|
objc
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS test_libxpc DESTINATION libexec/darling/usr/libexec)
|
129
test/array.m
Normal file
129
test/array.m
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
#include "ctest-plus.h"
|
||||||
|
#include <xpc/private.h>
|
||||||
|
#include "test-util.h"
|
||||||
|
|
||||||
|
CTEST_DATA(array) {
|
||||||
|
xpc_object_t objects[OBJECT_COUNT];
|
||||||
|
xpc_object_t array;
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST_SETUP(array) {
|
||||||
|
create_objects(data->objects, OBJECT_COUNT);
|
||||||
|
data->array = xpc_array_create(data->objects, OBJECT_COUNT);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST_TEARDOWN(array) {
|
||||||
|
xpc_release(data->array);
|
||||||
|
release_objects(data->objects, OBJECT_COUNT);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST(array, create_empty) {
|
||||||
|
xpc_object_t array = xpc_array_create(NULL, 0);
|
||||||
|
ASSERT_NOT_NULL(array);
|
||||||
|
ASSERT_EQUAL_U(0, xpc_array_get_count(array));
|
||||||
|
xpc_release(array);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST(array, create_nonempty) {
|
||||||
|
xpc_object_t objects[OBJECT_COUNT];
|
||||||
|
create_objects(objects, OBJECT_COUNT);
|
||||||
|
xpc_object_t array = xpc_array_create(objects, OBJECT_COUNT);
|
||||||
|
|
||||||
|
ASSERT_NOT_NULL(array);
|
||||||
|
ASSERT_EQUAL_U(OBJECT_COUNT, xpc_array_get_count(array));
|
||||||
|
for (size_t i = 0; i < OBJECT_COUNT; ++i) {
|
||||||
|
// we have a reference but the array should have one too
|
||||||
|
NSUInteger retainCount = [objects[i] retainCount];
|
||||||
|
// global objects have NSUIntegerMax as their reference count
|
||||||
|
if (retainCount != NSUIntegerMax) {
|
||||||
|
ASSERT_EQUAL_U(2, retainCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xpc_release(array);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < OBJECT_COUNT; ++i) {
|
||||||
|
// we should have the only reference now
|
||||||
|
NSUInteger retainCount = [objects[i] retainCount];
|
||||||
|
if (retainCount != NSUIntegerMax) {
|
||||||
|
ASSERT_EQUAL_U(1, retainCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
release_objects(objects, OBJECT_COUNT);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST2(array, get_value) {
|
||||||
|
size_t index = rand_index(OBJECT_COUNT);
|
||||||
|
|
||||||
|
ASSERT_EQUAL_PTR(data->objects[index], xpc_array_get_value(data->array, index));
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST2(array, set_value) {
|
||||||
|
xpc_object_t newObject = xpc_int64_create(10);
|
||||||
|
NSUInteger oldRefCount = 0;
|
||||||
|
size_t index = rand_index(OBJECT_COUNT);
|
||||||
|
|
||||||
|
xpc_array_set_value(data->array, index, newObject);
|
||||||
|
// setting the new value at that index should have dropped the old value
|
||||||
|
oldRefCount = [data->objects[index] retainCount];
|
||||||
|
if (oldRefCount != NSUIntegerMax) {
|
||||||
|
ASSERT_EQUAL_U(1, [data->objects[index] retainCount]);
|
||||||
|
}
|
||||||
|
// and it should have assigned and retained the new value
|
||||||
|
ASSERT_EQUAL_PTR(newObject, xpc_array_get_value(data->array, index));
|
||||||
|
ASSERT_EQUAL_U(2, [newObject retainCount]);
|
||||||
|
|
||||||
|
xpc_release(newObject);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST2(array, append_value) {
|
||||||
|
xpc_object_t newObject = xpc_int64_create(10);
|
||||||
|
xpc_object_t newObject2 = xpc_int64_create(15);
|
||||||
|
|
||||||
|
xpc_array_append_value(data->array, newObject);
|
||||||
|
ASSERT_EQUAL_U(OBJECT_COUNT + 1, xpc_array_get_count(data->array));
|
||||||
|
ASSERT_EQUAL_PTR(newObject, xpc_array_get_value(data->array, OBJECT_COUNT));
|
||||||
|
|
||||||
|
xpc_array_set_value(data->array, XPC_ARRAY_APPEND, newObject2);
|
||||||
|
ASSERT_EQUAL_U(OBJECT_COUNT + 2, xpc_array_get_count(data->array));
|
||||||
|
ASSERT_EQUAL_PTR(newObject2, xpc_array_get_value(data->array, OBJECT_COUNT + 1));
|
||||||
|
|
||||||
|
xpc_release(newObject2);
|
||||||
|
xpc_release(newObject);
|
||||||
|
};
|
||||||
|
|
||||||
|
static void visitor(size_t index, xpc_object_t value, void* context) {
|
||||||
|
size_t* visitedCount = context;
|
||||||
|
++*visitedCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST2(array, apply) {
|
||||||
|
__block size_t visitedCount = 0;
|
||||||
|
|
||||||
|
xpc_array_apply(data->array, ^bool(size_t index, xpc_object_t value) {
|
||||||
|
++visitedCount;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
ASSERT_EQUAL_U(OBJECT_COUNT, visitedCount);
|
||||||
|
|
||||||
|
visitedCount = 0;
|
||||||
|
xpc_array_apply(data->array, ^bool(size_t index, xpc_object_t value) {
|
||||||
|
++visitedCount;
|
||||||
|
if (index == OBJECT_COUNT / 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
ASSERT_EQUAL_U((OBJECT_COUNT / 2) + 1, visitedCount);
|
||||||
|
|
||||||
|
visitedCount = 0;
|
||||||
|
xpc_array_apply_f(data->array, &visitedCount, visitor);
|
||||||
|
ASSERT_EQUAL_U(OBJECT_COUNT, visitedCount);
|
||||||
|
};
|
||||||
|
|
||||||
|
// there are too many getters and setters and i don't want to write tests for all of them
|
||||||
|
// (and doing it with a macro would be tricky for some of them like fd or data)
|
||||||
|
//
|
||||||
|
// if the basic API works, everything should
|
||||||
|
// (all the getters and setters use the basic API to do their stuff)
|
67
test/base.m
Normal file
67
test/base.m
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#include "ctest-plus.h"
|
||||||
|
#import <xpc/xpc.h>
|
||||||
|
|
||||||
|
// this basic testing is related to the basic C API for objects and the base behavior of objects.
|
||||||
|
// for this purpose, we use int64 objects, as they're simple enough that they should never pose a problem.
|
||||||
|
// any issues with testing them should come from the xpc_object base class.
|
||||||
|
|
||||||
|
// some random non-zero value to start off the integers with
|
||||||
|
#define INT64_INITIAL_VALUE 5
|
||||||
|
#define INT64_INITIAL_VALUE_AS_STRING "5"
|
||||||
|
#define INT64_CLASS_NAME "OS_xpc_int64"
|
||||||
|
|
||||||
|
CTEST(base, create) {
|
||||||
|
xpc_object_t obj = xpc_int64_create(INT64_INITIAL_VALUE);
|
||||||
|
ASSERT_NOT_NULL(obj);
|
||||||
|
xpc_release(obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST(base, refcount) {
|
||||||
|
xpc_object_t obj = xpc_int64_create(INT64_INITIAL_VALUE);
|
||||||
|
ASSERT_EQUAL_U(1, [obj retainCount]);
|
||||||
|
xpc_retain(obj);
|
||||||
|
ASSERT_EQUAL_U(2, [obj retainCount]);
|
||||||
|
xpc_release(obj);
|
||||||
|
ASSERT_EQUAL_U(1, [obj retainCount]);
|
||||||
|
xpc_release(obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
// skipped because some XPC objects don't copy themselves
|
||||||
|
// (e.g. the one we're using, int64, doesn't copy itself)
|
||||||
|
CTEST_SKIP(base, copy) {
|
||||||
|
xpc_object_t obj = xpc_int64_create(INT64_INITIAL_VALUE);
|
||||||
|
xpc_object_t copy = xpc_copy(obj);
|
||||||
|
ASSERT_NOT_EQUAL_PTR(obj, copy);
|
||||||
|
xpc_release(copy);
|
||||||
|
xpc_release(obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST(base, hash) {
|
||||||
|
xpc_object_t obj1 = xpc_int64_create(INT64_INITIAL_VALUE);
|
||||||
|
xpc_object_t obj2 = xpc_int64_create(INT64_INITIAL_VALUE);
|
||||||
|
xpc_object_t obj3 = xpc_int64_create(INT64_INITIAL_VALUE + 1);
|
||||||
|
ASSERT_EQUAL_U(xpc_hash(obj1), xpc_hash(obj2));
|
||||||
|
ASSERT_NOT_EQUAL_U(xpc_hash(obj1), xpc_hash(obj3));
|
||||||
|
xpc_release(obj3);
|
||||||
|
xpc_release(obj2);
|
||||||
|
xpc_release(obj1);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST(base, equality) {
|
||||||
|
xpc_object_t obj1 = xpc_int64_create(INT64_INITIAL_VALUE);
|
||||||
|
xpc_object_t obj2 = xpc_int64_create(INT64_INITIAL_VALUE);
|
||||||
|
xpc_object_t obj3 = xpc_int64_create(INT64_INITIAL_VALUE + 1);
|
||||||
|
ASSERT_TRUE(xpc_equal(obj1, obj2));
|
||||||
|
ASSERT_FALSE(xpc_equal(obj1, obj3));
|
||||||
|
xpc_release(obj3);
|
||||||
|
xpc_release(obj2);
|
||||||
|
xpc_release(obj1);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST(base, description) {
|
||||||
|
xpc_object_t obj = xpc_int64_create(INT64_INITIAL_VALUE);
|
||||||
|
char* description = xpc_copy_description(obj);
|
||||||
|
ASSERT_STR("<" INT64_CLASS_NAME ": " INT64_INITIAL_VALUE_AS_STRING ">", description);
|
||||||
|
free(description);
|
||||||
|
xpc_release(obj);
|
||||||
|
};
|
34
test/bool.c
Normal file
34
test/bool.c
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#include "ctest-plus.h"
|
||||||
|
#include <xpc/private.h>
|
||||||
|
|
||||||
|
#undef bool
|
||||||
|
|
||||||
|
CTEST(bool, global) {
|
||||||
|
xpc_object_t trueObj = xpc_bool_create(true);
|
||||||
|
xpc_object_t falseObj = xpc_bool_create(false);
|
||||||
|
|
||||||
|
ASSERT_EQUAL_U(true, xpc_bool_get_value(trueObj));
|
||||||
|
ASSERT_EQUAL_U(false, xpc_bool_get_value(falseObj));
|
||||||
|
|
||||||
|
// they should be globals
|
||||||
|
ASSERT_EQUAL_PTR(XPC_BOOL_TRUE, trueObj);
|
||||||
|
ASSERT_EQUAL_PTR(XPC_BOOL_FALSE, falseObj);
|
||||||
|
|
||||||
|
xpc_release(falseObj);
|
||||||
|
xpc_release(trueObj);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST(bool, distinct) {
|
||||||
|
xpc_object_t obj = _xpc_bool_create_distinct(true);
|
||||||
|
|
||||||
|
ASSERT_EQUAL_U(true, xpc_bool_get_value(obj));
|
||||||
|
|
||||||
|
// it should be a non-global
|
||||||
|
ASSERT_NOT_EQUAL_PTR(XPC_BOOL_TRUE, obj);
|
||||||
|
|
||||||
|
_xpc_bool_set_value(obj, false);
|
||||||
|
|
||||||
|
ASSERT_EQUAL_U(false, xpc_bool_get_value(obj));
|
||||||
|
|
||||||
|
xpc_release(obj);
|
||||||
|
};
|
1
test/ctest
Submodule
1
test/ctest
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit cd46041baf3f131fe74032967566c7321adcaa01
|
11
test/ctest-plus.h
Normal file
11
test/ctest-plus.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#ifndef _CTEST_PLUS_H_
|
||||||
|
#define _CTEST_PLUS_H_
|
||||||
|
|
||||||
|
#include <ctest.h>
|
||||||
|
|
||||||
|
// convenience header that adds additional macros for ctest
|
||||||
|
|
||||||
|
#define ASSERT_EQUAL_PTR(exp, real) ASSERT_EQUAL_U(((uintptr_t)(exp)), ((uintptr_t)(real)))
|
||||||
|
#define ASSERT_NOT_EQUAL_PTR(exp, real) ASSERT_NOT_EQUAL_U(((uintptr_t)(exp)), ((uintptr_t)(real)))
|
||||||
|
|
||||||
|
#endif // _CTEST_PLUS_H_
|
50
test/data.c
Normal file
50
test/data.c
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#include "ctest-plus.h"
|
||||||
|
#include <xpc/private.h>
|
||||||
|
#include "test-util.h"
|
||||||
|
|
||||||
|
CTEST_DATA(data) {
|
||||||
|
xpc_object_t data;
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST_SETUP(data) {
|
||||||
|
data->data = xpc_data_create(some_data, sizeof(some_data));
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST_TEARDOWN(data) {
|
||||||
|
xpc_release(data->data);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST(data, create) {
|
||||||
|
xpc_object_t object = xpc_data_create(some_data, sizeof(some_data));
|
||||||
|
ASSERT_NOT_NULL(object);
|
||||||
|
xpc_release(object);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST2(data, length) {
|
||||||
|
ASSERT_EQUAL_U(sizeof(some_data), xpc_data_get_length(data->data));
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST2(data, bytes_ptr) {
|
||||||
|
ASSERT_DATA(some_data, sizeof(some_data), xpc_data_get_bytes_ptr(data->data), xpc_data_get_length(data->data));
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST2(data, bytes) {
|
||||||
|
uint8_t copy[sizeof(some_data)];
|
||||||
|
size_t copyCount = xpc_data_get_bytes(data->data, copy, 0, sizeof(copy));
|
||||||
|
ASSERT_EQUAL_U(sizeof(copy), copyCount);
|
||||||
|
ASSERT_DATA(some_data, sizeof(some_data), copy, sizeof(copy));
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST(data, create_with_dispatch_data) {
|
||||||
|
dispatch_data_t ddata = dispatch_data_create(some_data, sizeof(some_data), NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
|
||||||
|
xpc_object_t object = xpc_data_create_with_dispatch_data(ddata);
|
||||||
|
ASSERT_DATA(some_data, sizeof(some_data), xpc_data_get_bytes_ptr(object), xpc_data_get_length(object));
|
||||||
|
xpc_release(object);
|
||||||
|
dispatch_release(ddata);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST2(data, set_data) {
|
||||||
|
const uint8_t new_data[] = { 123, 124, 125, 126, 127, 128, 129, 130 };
|
||||||
|
_xpc_data_set_value(data->data, new_data, sizeof(new_data));
|
||||||
|
ASSERT_DATA(new_data, sizeof(new_data), xpc_data_get_bytes_ptr(data->data), xpc_data_get_length(data->data));
|
||||||
|
};
|
121
test/dictionary.m
Normal file
121
test/dictionary.m
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
#include "ctest-plus.h"
|
||||||
|
#include <xpc/private.h>
|
||||||
|
#include "test-util.h"
|
||||||
|
|
||||||
|
CTEST_DATA(dictionary) {
|
||||||
|
const char* keys[OBJECT_COUNT];
|
||||||
|
xpc_object_t objects[OBJECT_COUNT];
|
||||||
|
xpc_object_t dict;
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST_SETUP(dictionary) {
|
||||||
|
create_keys(data->keys, OBJECT_COUNT);
|
||||||
|
create_objects(data->objects, OBJECT_COUNT);
|
||||||
|
data->dict = xpc_dictionary_create(data->keys, data->objects, OBJECT_COUNT);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST_TEARDOWN(dictionary) {
|
||||||
|
xpc_release(data->dict);
|
||||||
|
release_objects(data->objects, OBJECT_COUNT);
|
||||||
|
release_keys(data->keys, OBJECT_COUNT);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST(dictionary, create_empty) {
|
||||||
|
xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
|
||||||
|
ASSERT_NOT_NULL(dict);
|
||||||
|
ASSERT_EQUAL_U(0, xpc_dictionary_get_count(dict));
|
||||||
|
xpc_release(dict);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST(dictionary, create_nonempty) {
|
||||||
|
const char* keys[OBJECT_COUNT];
|
||||||
|
xpc_object_t objects[OBJECT_COUNT];
|
||||||
|
create_keys(keys, OBJECT_COUNT);
|
||||||
|
create_objects(objects, OBJECT_COUNT);
|
||||||
|
xpc_object_t dict = xpc_dictionary_create(keys, objects, OBJECT_COUNT);
|
||||||
|
|
||||||
|
ASSERT_NOT_NULL(dict);
|
||||||
|
ASSERT_EQUAL_U(OBJECT_COUNT, xpc_dictionary_get_count(dict));
|
||||||
|
for (size_t i = 0; i < OBJECT_COUNT; ++i) {
|
||||||
|
// we have a reference but the dictionary should have one too
|
||||||
|
NSUInteger retainCount = [objects[i] retainCount];
|
||||||
|
// global objects have NSUIntegerMax as their reference count
|
||||||
|
if (retainCount != NSUIntegerMax) {
|
||||||
|
ASSERT_EQUAL_U(2, retainCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xpc_release(dict);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < OBJECT_COUNT; ++i) {
|
||||||
|
// we should have the only reference now
|
||||||
|
NSUInteger retainCount = [objects[i] retainCount];
|
||||||
|
if (retainCount != NSUIntegerMax) {
|
||||||
|
ASSERT_EQUAL_U(1, retainCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
release_objects(objects, OBJECT_COUNT);
|
||||||
|
release_keys(keys, OBJECT_COUNT);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST2(dictionary, get_value) {
|
||||||
|
size_t index = rand_index(OBJECT_COUNT);
|
||||||
|
ASSERT_EQUAL_PTR(data->objects[index], xpc_dictionary_get_value(data->dict, data->keys[index]));
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST2(dictionary, set_value) {
|
||||||
|
xpc_object_t newObject = xpc_int64_create(10);
|
||||||
|
NSUInteger oldRefCount = 0;
|
||||||
|
size_t index = rand_index(OBJECT_COUNT);
|
||||||
|
|
||||||
|
xpc_dictionary_set_value(data->dict, data->keys[index], newObject);
|
||||||
|
// setting the new value at that index should have dropped the old value
|
||||||
|
oldRefCount = [data->objects[index] retainCount];
|
||||||
|
if (oldRefCount != NSUIntegerMax) {
|
||||||
|
ASSERT_EQUAL_U(1, [data->objects[index] retainCount]);
|
||||||
|
}
|
||||||
|
// and it should have assigned and retained the new value
|
||||||
|
ASSERT_EQUAL_PTR(newObject, xpc_dictionary_get_value(data->dict, data->keys[index]));
|
||||||
|
ASSERT_EQUAL_U(2, [newObject retainCount]);
|
||||||
|
|
||||||
|
xpc_release(newObject);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST2(dictionary, add_value) {
|
||||||
|
xpc_object_t newObject = xpc_int64_create(10);
|
||||||
|
xpc_object_t newObject2 = xpc_int64_create(15);
|
||||||
|
|
||||||
|
xpc_dictionary_set_value(data->dict, "some random key", newObject);
|
||||||
|
ASSERT_EQUAL_U(OBJECT_COUNT + 1, xpc_dictionary_get_count(data->dict));
|
||||||
|
ASSERT_EQUAL_PTR(newObject, xpc_dictionary_get_value(data->dict, "some random key"));
|
||||||
|
|
||||||
|
xpc_release(newObject);
|
||||||
|
};
|
||||||
|
|
||||||
|
static void visitor(const char* key, xpc_object_t value, void* context) {
|
||||||
|
size_t* visitedCount = context;
|
||||||
|
++*visitedCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST2(dictionary, apply) {
|
||||||
|
__block size_t visitedCount = 0;
|
||||||
|
const char* key = rand_key(data->keys, OBJECT_COUNT);
|
||||||
|
|
||||||
|
xpc_dictionary_apply(data->dict, ^bool(const char* key, xpc_object_t value) {
|
||||||
|
++visitedCount;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
ASSERT_EQUAL_U(OBJECT_COUNT, visitedCount);
|
||||||
|
|
||||||
|
// i'm not sure how to test the early stop with dictionaries, so for now, we won't
|
||||||
|
|
||||||
|
visitedCount = 0;
|
||||||
|
xpc_dictionary_apply_f(data->dict, &visitedCount, visitor);
|
||||||
|
ASSERT_EQUAL_U(OBJECT_COUNT, visitedCount);
|
||||||
|
};
|
||||||
|
|
||||||
|
// like arrays, dictionaries have too many getters and setters and i don't want to write tests for all of them
|
||||||
|
//
|
||||||
|
// if the basic API works, everything should work
|
||||||
|
// (all the getters and setters use the basic API to do their stuff)
|
12
test/main.c
Normal file
12
test/main.c
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#define CTEST_MAIN
|
||||||
|
|
||||||
|
// uncomment lines below to enable/disable features. See README.md for details
|
||||||
|
#define CTEST_SEGFAULT
|
||||||
|
//#define CTEST_NO_COLORS
|
||||||
|
#define CTEST_COLOR_OK
|
||||||
|
|
||||||
|
#include <ctest.h>
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
return ctest_main(argc, argv);
|
||||||
|
};
|
69
test/string.c
Normal file
69
test/string.c
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#include "ctest-plus.h"
|
||||||
|
#include <xpc/private.h>
|
||||||
|
|
||||||
|
#define INITIAL_STRING_LITERAL "foo"
|
||||||
|
#define INITIAL_STRING_LENGTH 3
|
||||||
|
|
||||||
|
CTEST(string, create) {
|
||||||
|
xpc_object_t string = xpc_string_create(INITIAL_STRING_LITERAL);
|
||||||
|
ASSERT_NOT_NULL(string);
|
||||||
|
xpc_release(string);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST(string, get_string_ptr) {
|
||||||
|
xpc_object_t string = xpc_string_create(INITIAL_STRING_LITERAL);
|
||||||
|
ASSERT_NOT_NULL(xpc_string_get_string_ptr(string));
|
||||||
|
xpc_release(string);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST(string, get_length) {
|
||||||
|
xpc_object_t string = xpc_string_create(INITIAL_STRING_LITERAL);
|
||||||
|
ASSERT_EQUAL_U(INITIAL_STRING_LENGTH, xpc_string_get_length(string));
|
||||||
|
xpc_release(string);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST(string, create_with_format) {
|
||||||
|
xpc_object_t string = xpc_string_create_with_format("foo %d bar", 5);
|
||||||
|
ASSERT_NOT_NULL(string);
|
||||||
|
// make sure it formatted it correctly
|
||||||
|
ASSERT_STR("foo 5 bar", xpc_string_get_string_ptr(string));
|
||||||
|
xpc_release(string);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST(string, create_no_copy) {
|
||||||
|
xpc_object_t string = xpc_string_create_no_copy(INITIAL_STRING_LITERAL);
|
||||||
|
ASSERT_NOT_NULL(string);
|
||||||
|
// make sure it actually didn't copy
|
||||||
|
ASSERT_EQUAL_PTR(INITIAL_STRING_LITERAL, xpc_string_get_string_ptr(string));
|
||||||
|
xpc_release(string);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST(string, hash) {
|
||||||
|
xpc_object_t string1 = xpc_string_create(INITIAL_STRING_LITERAL);
|
||||||
|
xpc_object_t string2 = xpc_string_create(INITIAL_STRING_LITERAL);
|
||||||
|
xpc_object_t string3 = xpc_string_create(INITIAL_STRING_LITERAL " bar");
|
||||||
|
ASSERT_EQUAL_U(xpc_hash(string1), xpc_hash(string2));
|
||||||
|
ASSERT_NOT_EQUAL_U(xpc_hash(string1), xpc_hash(string3));
|
||||||
|
xpc_release(string3);
|
||||||
|
xpc_release(string2);
|
||||||
|
xpc_release(string1);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST(string, equality) {
|
||||||
|
xpc_object_t string1 = xpc_string_create(INITIAL_STRING_LITERAL);
|
||||||
|
xpc_object_t string2 = xpc_string_create(INITIAL_STRING_LITERAL);
|
||||||
|
xpc_object_t string3 = xpc_string_create(INITIAL_STRING_LITERAL " bar");
|
||||||
|
ASSERT_TRUE(xpc_equal(string1, string2));
|
||||||
|
ASSERT_FALSE(xpc_equal(string1, string3));
|
||||||
|
xpc_release(string3);
|
||||||
|
xpc_release(string2);
|
||||||
|
xpc_release(string1);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST(string, set_value) {
|
||||||
|
xpc_object_t string = xpc_string_create(INITIAL_STRING_LITERAL);
|
||||||
|
ASSERT_STR(INITIAL_STRING_LITERAL, xpc_string_get_string_ptr(string));
|
||||||
|
_xpc_string_set_value(string, "new string");
|
||||||
|
ASSERT_STR("new string", xpc_string_get_string_ptr(string));
|
||||||
|
xpc_release(string);
|
||||||
|
};
|
49
test/test-util.h
Normal file
49
test/test-util.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#ifndef _XPC_TEST_TEST_UTIL_H_
|
||||||
|
#define _XPC_TEST_TEST_UTIL_H_
|
||||||
|
|
||||||
|
#include <xpc/private.h>
|
||||||
|
|
||||||
|
#define OBJECT_COUNT 5
|
||||||
|
|
||||||
|
static size_t rand_index(size_t count) {
|
||||||
|
// not uniform but honestly who cares
|
||||||
|
return rand() % count;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* rand_key(const char** keys, size_t count) {
|
||||||
|
return keys[rand_index(count)];
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t some_data[] = {0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10};
|
||||||
|
|
||||||
|
static void create_objects(xpc_object_t* objects, size_t count) {
|
||||||
|
if (count >= OBJECT_COUNT) {
|
||||||
|
objects[0] = xpc_int64_create(5);
|
||||||
|
objects[1] = xpc_string_create("foo");
|
||||||
|
objects[2] = xpc_null_create();
|
||||||
|
objects[3] = xpc_bool_create(true);
|
||||||
|
objects[4] = xpc_data_create(some_data, sizeof(some_data));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void create_keys(const char** keys, size_t count) {
|
||||||
|
if (count >= OBJECT_COUNT) {
|
||||||
|
keys[0] = "first";
|
||||||
|
keys[1] = "second";
|
||||||
|
keys[2] = "third";
|
||||||
|
keys[3] = "fourth";
|
||||||
|
keys[4] = "fifth";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void release_objects(xpc_object_t* objects, size_t count) {
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
xpc_release(objects[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void release_keys(const char** keys, size_t count) {
|
||||||
|
// no-op
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _XPC_TEST_TEST_UTIL_H_
|
14
test/type.c
Normal file
14
test/type.c
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#include "ctest-plus.h"
|
||||||
|
#include <xpc/private.h>
|
||||||
|
|
||||||
|
CTEST(type, get_type) {
|
||||||
|
xpc_object_t obj = xpc_int64_create(5);
|
||||||
|
ASSERT_EQUAL_PTR(XPC_TYPE_INT64, xpc_get_type(obj));
|
||||||
|
xpc_release(obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
CTEST(type, get_type_name) {
|
||||||
|
xpc_object_t obj = xpc_int64_create(5);
|
||||||
|
ASSERT_STR("OS_xpc_int64", xpc_type_get_name(xpc_get_type(obj)));
|
||||||
|
xpc_release(obj);
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user