mirror of
https://github.com/darlinghq/darling-libxpc.git
synced 2024-11-23 03:39:40 +00:00
Implement xpc_main
However, bundled XPC services still don't work properly. The problem is that bundled XPC service support needs to be implemented in launchd. The open-source launchd sources are severely outdated, much like CoreFoundation. The new launchd is based on libxpc and is closed-source. We probably don't want to completely rewrite launchd, but at the very least, we do need to add support for new XPC-based features.
This commit is contained in:
parent
96434fd2f1
commit
0ec7a91082
@ -18,6 +18,7 @@ add_compile_definitions(
|
|||||||
|
|
||||||
add_compile_options(
|
add_compile_options(
|
||||||
-Wno-extern-initializer
|
-Wno-extern-initializer
|
||||||
|
-Wno-gnu-folding-constant
|
||||||
)
|
)
|
||||||
|
|
||||||
add_compile_options(
|
add_compile_options(
|
||||||
|
@ -32,6 +32,9 @@ struct xpc_bundle_s {
|
|||||||
// this API is modeled after NSBundle
|
// this API is modeled after NSBundle
|
||||||
// (but we've added lots of non-NSBundle extensions)
|
// (but we've added lots of non-NSBundle extensions)
|
||||||
|
|
||||||
|
// NOTE: differs from NSBundle by returning a new bundle every time it's called
|
||||||
|
@property(class, readonly, copy) XPC_CLASS(bundle)* mainBundle;
|
||||||
|
|
||||||
@property(strong) XPC_CLASS(string)* bundlePath;
|
@property(strong) XPC_CLASS(string)* bundlePath;
|
||||||
@property(strong) XPC_CLASS(string)* executablePath;
|
@property(strong) XPC_CLASS(string)* executablePath;
|
||||||
@property(strong) XPC_CLASS(dictionary)* infoDictionary;
|
@property(strong) XPC_CLASS(dictionary)* infoDictionary;
|
||||||
|
15
src/bundle.m
15
src/bundle.m
@ -20,6 +20,15 @@ OS_OBJECT_NONLAZY_CLASS
|
|||||||
|
|
||||||
XPC_CLASS_HEADER(bundle);
|
XPC_CLASS_HEADER(bundle);
|
||||||
|
|
||||||
|
+ (XPC_CLASS(bundle)*)mainBundle
|
||||||
|
{
|
||||||
|
char* exec_path = xpc_copy_main_executable_path();
|
||||||
|
if (!exec_path) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return [[[[self class] alloc] initWithPath: [XPC_CLASS(string) stringWithUTF8StringNoCopy: exec_path freeWhenDone: YES]] autorelease];
|
||||||
|
}
|
||||||
|
|
||||||
+ (BOOL)pathIsBundleRoot: (XPC_CLASS(string)*)path
|
+ (BOOL)pathIsBundleRoot: (XPC_CLASS(string)*)path
|
||||||
{
|
{
|
||||||
// frameworks are special
|
// frameworks are special
|
||||||
@ -361,11 +370,7 @@ xpc_object_t xpc_bundle_create_from_origin(unsigned int origin, const char* path
|
|||||||
XPC_EXPORT
|
XPC_EXPORT
|
||||||
xpc_object_t xpc_bundle_create_main(void) {
|
xpc_object_t xpc_bundle_create_main(void) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
char* exec_path = xpc_copy_main_executable_path();
|
return [[XPC_CLASS(bundle) mainBundle] retain];
|
||||||
if (!exec_path) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return [[XPC_CLASS(bundle) alloc] initWithPath: [XPC_CLASS(string) stringWithUTF8StringNoCopy: exec_path freeWhenDone: YES]];
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
134
src/runtime.m
134
src/runtime.m
@ -1,6 +1,35 @@
|
|||||||
#import <xpc/xpc.h>
|
#import <xpc/xpc.h>
|
||||||
#import <xpc/util.h>
|
#import <xpc/util.h>
|
||||||
|
|
||||||
|
#import <xpc/private/bundle.h>
|
||||||
|
|
||||||
|
#import <xpc/objects/bundle.h>
|
||||||
|
#import <xpc/objects/dictionary.h>
|
||||||
|
#import <xpc/objects/string.h>
|
||||||
|
#import <xpc/objects/connection.h>
|
||||||
|
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
|
||||||
|
#import <Foundation/NSRunLoop.h>
|
||||||
|
#import <dlfcn.h>
|
||||||
|
#import <AppKit/NSApplication.h>
|
||||||
|
#import <crt_externs.h>
|
||||||
|
|
||||||
|
#define INFO_DICT_IDENTIFIER_KEY "CFBundleIdentifier"
|
||||||
|
#define INFO_DICT_PACKAGE_TYPE_KEY "CFBundlePackageType"
|
||||||
|
|
||||||
|
#define XPC_SERVICE_DICT_RUNLOOP_TYPE_KEY "RunLoopType"
|
||||||
|
|
||||||
|
#define XPC_PACKAGE_TYPE "XPC!"
|
||||||
|
|
||||||
|
OS_ENUM(xpc_service_runloop_type, uint8_t,
|
||||||
|
xpc_service_runloop_type_invalid,
|
||||||
|
xpc_service_runloop_type_dispatch,
|
||||||
|
xpc_service_runloop_type_nsrunloop,
|
||||||
|
xpc_service_runloop_type_nsapplicationmain,
|
||||||
|
xpc_service_runloop_type_uiapplicationmain,
|
||||||
|
);
|
||||||
|
|
||||||
XPC_EXPORT
|
XPC_EXPORT
|
||||||
xpc_object_t _xpc_runtime_get_entitlements_data(void) {
|
xpc_object_t _xpc_runtime_get_entitlements_data(void) {
|
||||||
// returns a data object
|
// returns a data object
|
||||||
@ -21,10 +50,111 @@ bool _xpc_runtime_is_app_sandboxed(void) {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static xpc_service_runloop_type_t runloop_name_to_type(const char* name) {
|
||||||
|
if (!name) {
|
||||||
|
return xpc_service_runloop_type_dispatch;
|
||||||
|
} else if (strcmp(name, "_UIApplicationMain") == 0) {
|
||||||
|
return xpc_service_runloop_type_uiapplicationmain;
|
||||||
|
} else if (strcmp(name, "_NSApplicationMain") == 0) {
|
||||||
|
return xpc_service_runloop_type_nsapplicationmain;
|
||||||
|
} else if (strcmp(name, "NSRunLoop") == 0 || strcmp(name, "_WebKit") == 0) {
|
||||||
|
return xpc_service_runloop_type_nsrunloop;
|
||||||
|
} else {
|
||||||
|
return xpc_service_runloop_type_dispatch;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
XPC_EXPORT
|
XPC_EXPORT
|
||||||
void xpc_main(xpc_connection_handler_t handler) {
|
void xpc_main(xpc_connection_handler_t handler) {
|
||||||
xpc_stub();
|
@autoreleasepool {
|
||||||
abort();
|
XPC_CLASS(bundle)* mainBundle = [XPC_CLASS(bundle) mainBundle];
|
||||||
|
XPC_CLASS(string)* identifier = nil;
|
||||||
|
xpc_service_runloop_type_t runloopType = xpc_service_runloop_type_invalid;
|
||||||
|
XPC_CLASS(connection)* server = nil;
|
||||||
|
|
||||||
|
if (!mainBundle) {
|
||||||
|
xpc_abort("failed to retrieve main bundle information");
|
||||||
|
}
|
||||||
|
|
||||||
|
[mainBundle resolve];
|
||||||
|
if (mainBundle.error != 0) {
|
||||||
|
xpc_abort("failed to resolve main bundle information");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (![[mainBundle.infoDictionary stringForKey: INFO_DICT_PACKAGE_TYPE_KEY] isEqualToString: XPC_PACKAGE_TYPE]) {
|
||||||
|
xpc_abort("main bundle was not an XPC service bundle");
|
||||||
|
}
|
||||||
|
|
||||||
|
identifier = [mainBundle.infoDictionary stringForKey: INFO_DICT_IDENTIFIER_KEY];
|
||||||
|
if (!identifier) {
|
||||||
|
xpc_abort("failed to determine main bundle identifier");
|
||||||
|
}
|
||||||
|
|
||||||
|
server = [[XPC_CLASS(connection) alloc] initAsServerForService: identifier.UTF8String queue: NULL];
|
||||||
|
if (!server) {
|
||||||
|
xpc_abort("failed to create server connection");
|
||||||
|
}
|
||||||
|
|
||||||
|
server.eventHandler = ^(xpc_object_t object) {
|
||||||
|
xpc_type_t type = xpc_get_type(object);
|
||||||
|
if (type == (xpc_type_t)XPC_TYPE_CONNECTION) {
|
||||||
|
handler(XPC_CAST(connection, object));
|
||||||
|
} else if (type == (xpc_type_t)XPC_TYPE_ERROR) {
|
||||||
|
if (object == XPC_ERROR_TERMINATION_IMMINENT) {
|
||||||
|
xpc_log(XPC_LOG_WARNING, "someone wants us to terminate");
|
||||||
|
} else {
|
||||||
|
xpc_abort("unexpected error receive in managed server event handler: %s", xpc_copy_description(object));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
xpc_abort("invalid object received in managed server event handler: %s", xpc_copy_description(object));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// schedule the connection to be activated once the runloop is kicked off
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[server activate];
|
||||||
|
});
|
||||||
|
|
||||||
|
runloopType = runloop_name_to_type([XPC_CAST(dictionary, xpc_bundle_get_xpcservice_dictionary(mainBundle)) stringForKey: XPC_SERVICE_DICT_RUNLOOP_TYPE_KEY].UTF8String);
|
||||||
|
|
||||||
|
switch (runloopType) {
|
||||||
|
case xpc_service_runloop_type_dispatch: {
|
||||||
|
dispatch_main();
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case xpc_service_runloop_type_nsrunloop: {
|
||||||
|
Class NSRunLoopClass = objc_getClass("NSRunLoop");
|
||||||
|
if (!NSRunLoopClass) {
|
||||||
|
xpc_abort("failed to load NSRunLoop class");
|
||||||
|
}
|
||||||
|
[[NSRunLoopClass currentRunLoop] run];
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case xpc_service_runloop_type_nsapplicationmain: {
|
||||||
|
void* appkit = dlopen("/System/Library/Frameworks/AppKit.framework/AppKit", RTLD_LAZY);
|
||||||
|
if (!appkit) {
|
||||||
|
xpc_abort("failed to load AppKit");
|
||||||
|
}
|
||||||
|
|
||||||
|
__typeof__(NSApplicationMain)* NSApplicationMain_ptr = dlsym(appkit, "NSApplicationMain");
|
||||||
|
if (!NSApplicationMain_ptr) {
|
||||||
|
xpc_abort("failed to load NSApplicationMain from AppKit");
|
||||||
|
}
|
||||||
|
|
||||||
|
NSApplicationMain_ptr(*_NSGetArgc(), (const char**)*_NSGetArgv());
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case xpc_service_runloop_type_uiapplicationmain: {
|
||||||
|
xpc_abort("UIApplicationMain runloop not implemented");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
xpc_abort("failed to determine runloop type");
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xpc_abort("runloop returned");
|
||||||
};
|
};
|
||||||
|
|
||||||
XPC_EXPORT
|
XPC_EXPORT
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
add_subdirectory(launchd-service)
|
add_subdirectory(launchd-service)
|
||||||
|
add_subdirectory(bundled-service)
|
||||||
|
|
||||||
set(TEST_SOURCES
|
set(TEST_SOURCES
|
||||||
array.m
|
array.m
|
||||||
|
24
test/bundled-service/CMakeLists.txt
Normal file
24
test/bundled-service/CMakeLists.txt
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
add_darling_executable(libxpc_test_bundled_service service.c)
|
||||||
|
|
||||||
|
set_target_properties(libxpc_test_bundled_service PROPERTIES
|
||||||
|
OUTPUT_NAME "XPCBundledService"
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(libxpc_test_bundled_service
|
||||||
|
xpc_static
|
||||||
|
objc
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
TARGETS
|
||||||
|
libxpc_test_bundled_service
|
||||||
|
DESTINATION
|
||||||
|
libexec/darling/usr/libexec/test/xpc/XPCBundledService.xpc/Contents/MacOS
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
FILES
|
||||||
|
Info.plist
|
||||||
|
DESTINATION
|
||||||
|
libexec/darling/usr/libexec/test/xpc/XPCBundledService.xpc/Contents
|
||||||
|
)
|
27
test/bundled-service/Info.plist
Normal file
27
test/bundled-service/Info.plist
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>XPC Bundled Service</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>XPCBundledService</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>org.darlinghq.libxpc.test.bundled-service</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>XPCBundledService</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>XPC!</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>XPCService</key>
|
||||||
|
<dict>
|
||||||
|
<key>ServiceType</key>
|
||||||
|
<string>Application</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
19
test/bundled-service/service.c
Normal file
19
test/bundled-service/service.c
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include <xpc/xpc.h>
|
||||||
|
#include <xpc/connection.h>
|
||||||
|
|
||||||
|
static void connection_handler(xpc_connection_t connection) {
|
||||||
|
printf("Got a new connection: %p\n", connection);
|
||||||
|
|
||||||
|
xpc_connection_set_event_handler(connection, ^(xpc_object_t object) {
|
||||||
|
char* desc = xpc_copy_description(object);
|
||||||
|
xpc_type_t type = xpc_get_type(object);
|
||||||
|
printf("Recevied %s from connection %p: %s\n", (type == (xpc_type_t)XPC_TYPE_DICTIONARY) ? "message" : ((type == (xpc_type_t)XPC_TYPE_ERROR) ? "error" : "unexpected object"), connection, desc);
|
||||||
|
free(desc);
|
||||||
|
});
|
||||||
|
xpc_connection_resume(connection);
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
xpc_main(connection_handler);
|
||||||
|
return 0;
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user