This is a follow up to the thread, Troubleshooting the launch of local user XPC Launch Agent, in the original (and now archived) Dev Forums.
Based on further tests, I assume there must be some configuration that I'm not setting up correctly, as opposed to the code.
I started from scratch again with a fresh Xcode Project. This time, I based the code mostly on the example code posted by Apple Engineer eskimo1 in this thread (which uses an privileged Launch Daemon) with a modification or two based on the code posted by Apple Engineer dsorresso in this thread. As I assume that their code can be expected to work (i.e.- to help rule out a coding error on my part). Except that I modified the code so that it works with/as an XPC Service Launch Agent without the privileged aspect and which resides inside of the user's home directory.
The following was all done on OS X 10.10.3 (14D136) Yosemite and built with Xcode 6.3.2 (6D2105).
* I first started off both the command line interface program and the XPC Service Launch Agent without Code Signing or Sandboxing. When the command line interface program was run at the command line, I got output such as the following. Which more or less resembles the behaviour I was seeing with my own test code.
connection event
error Connection invalid
* Then I code signed both items. No change in the output.
* Next, I Sandboxed both. While there was no change in the output at the command line, some entries containing 'deny mach-lookup' began to appear in the System Console logs -> in asl -> with the AUX prefix. e.g.
CLIHelloToLaunch(6103) deny mach-lookup com.test.XPCLA
* Later, I added a com.apple.security.temporary-exception.mach-lookup.global-name key of Type Array containing the Bundle Identifier of the XPC Service Launch Agent in the Entitlements files for both Targets. No change in the command line output and 'deny mach-lookup' entries still appeared in System Console logs -> asl -> AUX.
* As well, I also added an App Group to the Entitlements files for both Targets. No change in the command line output and 'deny mach-lookup' entries still appeared in System Console logs -> asl -> AUX.
What could be the cause of the XPC connection errors? I presume that the 'deny mach-lookup' entries are related. How can I fix this?
This is the contents of the launchd plist file for the XPC Service Launch Agent. It resides in ~/Library/LaunchAgents:
<?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>Label</key>
<string>com.test.XPCLA</string>
<key>MachService</key>
<dict>
<key>com.test.XPCLA</key>
<true/>
</dict>
<key>ProgramArguments</key>
<array>
<string>~/Library/Application Support/com.test.XPCLA.xpc</string>
</array>
</dict>
</plist>
This is the code being used in the XPC Service Launch Agent. Specifically, in it's main.m file. After building, I put the .xpc Launch Agent in ~/Library/Application Support for testing.
#import <Foundation/Foundation.h>
#include <xpc/xpc.h>
static void SetupConnection(xpc_connection_t connection)
{
xpc_connection_set_event_handler(connection, ^(xpc_object_t object) {
if ( xpc_get_type(object) == XPC_TYPE_ERROR ) {
fprintf(stderr, "connection error %s\n", xpc_dictionary_get_string(object, XPC_ERROR_KEY_DESCRIPTION));
xpc_connection_cancel(connection);
} else if ( xpc_get_type(object) == XPC_TYPE_DICTIONARY ) {
const char * name;
fprintf(stderr, "connection message\n");
name = xpc_dictionary_get_string(object, "name");
if (name == NULL) {
fprintf(stderr, "no name\n");
xpc_connection_cancel(connection);
} else {
xpc_object_t response;
char * responseStr;
fprintf(stderr, "name is '%s'\n", name);
(void) asprintf(&responseStr, "hello %s", name);
response = xpc_dictionary_create(NULL, NULL, 0);
assert(response != NULL);
xpc_dictionary_set_string(response, "greeting", responseStr);
xpc_connection_send_message(connection, response);
// xpc_release(response);
}
} else {
assert(false);
}
});
xpc_connection_resume(connection);
}
int main(int argc, char** argv)
{
xpc_connection_t listener;
fprintf(stderr, "XPC Helper start\n");
listener = xpc_connection_create_mach_service("com.test.XPCLA", NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);
assert(listener != NULL);
xpc_connection_set_event_handler(listener, ^(xpc_object_t object) {
if ( xpc_get_type(object) == XPC_TYPE_ERROR ) {
fprintf(stderr, "listener error\n"); // XPC_ERROR_KEY_DESCRIPTION
} else if ( xpc_get_type(object) == XPC_TYPE_CONNECTION ) {
fprintf(stderr, "listener connection\n");
SetupConnection(object);
} else {
assert(false);
}
});
xpc_connection_resume(listener);
dispatch_main();
return EXIT_SUCCESS;
}
This is the code in the command line interface program's main.m file. After building, I put the program into ~/Applications.
#import <Foundation/Foundation.h>
#include <xpc/xpc.h>
static BOOL Test(void)
{
BOOL success;
xpc_connection_t connection;
xpc_object_t request;
xpc_object_t response;
response = NULL;
connection = xpc_connection_create_mach_service("com.test.XPCLA", NULL, 0);
assert(connection != NULL);
request = xpc_dictionary_create(NULL, NULL, 0);
assert(request != NULL);
xpc_dictionary_set_string(request, "name", "Hello service, this is the program speaking, are you out there?");
xpc_connection_set_event_handler(connection, ^(xpc_object_t object) {
fprintf(stderr, "connection event\n");
if ( xpc_get_type(object) == XPC_TYPE_ERROR ) {
fprintf(stderr, "error %s\n", xpc_dictionary_get_string(object, XPC_ERROR_KEY_DESCRIPTION));
} else if ( xpc_get_type(object) == XPC_TYPE_DICTIONARY ) {
const char * greeting;
fprintf(stderr, "response\n");
greeting = xpc_dictionary_get_string(object, "greeting");
fprintf(stderr, "greeting is '%s'\n", greeting);
} else {
fprintf(stderr, "something else\n");
}
});
xpc_connection_resume(connection);
xpc_connection_send_message(connection, request);
dispatch_main();
if (request != NULL) {
xpc_release(request);
}
if (response != NULL) {
xpc_release(response);
}
return success;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"%@", @"About to run test of XPC Service.");
Test();
}
return 0;
}
Here are the command line program's Entitlements.
<?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>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.temporary-exception.mach-lookup.global-name </key>
<array>
<string>com.test.XPCLA</string>
</array>
<key>com.apple.security.application-groups</key>
<array>
<string>$(TeamIdentifierPrefix)IPC</string>
</array>
</dict>
</plist>
The XPC Service Launch Agent's Entitlements.
<?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>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>$(TeamIdentifierPrefix)IPC</string>
</array>
<key>com.apple.security.temporary-exception.mach-lookup.global-name </key>
<array>
<string>com.test.XPCLA</string>
</array>
</dict>
</plist>
Additional notes, if it's relevant:
* The command line interface program has an embedded info.plist since it's a single file executable.
* Command line interface program was created via Xcode 6.3.2's New Target -> Command Line Tool template
* XPC Service was created via Xcode 6.3.2's New Target -> XPC Service template.