mirror of
https://github.com/darlinghq/darling-libxpc.git
synced 2024-11-23 11:49:42 +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(
|
||||
-Wno-extern-initializer
|
||||
-Wno-gnu-folding-constant
|
||||
)
|
||||
|
||||
add_compile_options(
|
||||
|
@ -32,6 +32,9 @@ struct xpc_bundle_s {
|
||||
// this API is modeled after NSBundle
|
||||
// (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)* executablePath;
|
||||
@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(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
|
||||
{
|
||||
// frameworks are special
|
||||
@ -361,11 +370,7 @@ xpc_object_t xpc_bundle_create_from_origin(unsigned int origin, const char* path
|
||||
XPC_EXPORT
|
||||
xpc_object_t xpc_bundle_create_main(void) {
|
||||
@autoreleasepool {
|
||||
char* exec_path = xpc_copy_main_executable_path();
|
||||
if (!exec_path) {
|
||||
return NULL;
|
||||
}
|
||||
return [[XPC_CLASS(bundle) alloc] initWithPath: [XPC_CLASS(string) stringWithUTF8StringNoCopy: exec_path freeWhenDone: YES]];
|
||||
return [[XPC_CLASS(bundle) mainBundle] retain];
|
||||
}
|
||||
};
|
||||
|
||||
|
134
src/runtime.m
134
src/runtime.m
@ -1,6 +1,35 @@
|
||||
#import <xpc/xpc.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_object_t _xpc_runtime_get_entitlements_data(void) {
|
||||
// returns a data object
|
||||
@ -21,10 +50,111 @@ bool _xpc_runtime_is_app_sandboxed(void) {
|
||||
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
|
||||
void xpc_main(xpc_connection_handler_t handler) {
|
||||
xpc_stub();
|
||||
abort();
|
||||
@autoreleasepool {
|
||||
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
|
||||
|
@ -1,4 +1,5 @@
|
||||
add_subdirectory(launchd-service)
|
||||
add_subdirectory(bundled-service)
|
||||
|
||||
set(TEST_SOURCES
|
||||
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