| // |
| // main.m |
| // Used in both LauncherXPCService and LaunchRootXPCService targets |
| // |
| // Copyright (c) 2012 Apple Inc. All rights reserved. |
| // |
| #include <AvailabilityMacros.h> |
| |
| #if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 |
| #define BUILDING_ON_SNOW_LEOPARD 1 |
| #endif |
| |
| #if !BUILDING_ON_SNOW_LEOPARD |
| #define __XPC_PRIVATE_H__ |
| #include <xpc/xpc.h> |
| #include <spawn.h> |
| #include <signal.h> |
| #include <assert.h> |
| #include <sys/errno.h> |
| #include "LauncherXPCService.h" |
| |
| // Declaration. Returns 0 if successful. |
| int _validate_authorization(xpc_object_t message); |
| |
| // Returns 0 if successful. |
| int _setup_posixspawn_attributes_file_actions(xpc_object_t message, posix_spawnattr_t *attr, posix_spawn_file_actions_t *file_actions) |
| { |
| *attr = 0; |
| |
| int errorCode = posix_spawnattr_init(attr); |
| if (errorCode) |
| return errorCode; |
| |
| cpu_type_t cpuType = (cpu_type_t)xpc_dictionary_get_int64(message, LauncherXPCServiceCPUTypeKey); |
| if (cpuType == -2) { |
| cpuType= CPU_TYPE_ANY; |
| } |
| size_t realCount; |
| errorCode = posix_spawnattr_setbinpref_np(attr, 1, &cpuType, &realCount); |
| if (errorCode) |
| return errorCode; |
| |
| sigset_t no_signals; |
| sigset_t all_signals; |
| sigemptyset (&no_signals); |
| sigfillset (&all_signals); |
| posix_spawnattr_setsigmask(attr, &no_signals); |
| posix_spawnattr_setsigdefault(attr, &all_signals); |
| |
| short flags = xpc_dictionary_get_int64(message, LauncherXPCServicePosixspawnFlagsKey); |
| errorCode = posix_spawnattr_setflags(attr, flags); |
| if (errorCode) |
| return errorCode; |
| |
| // Setup any file actions. |
| errorCode = posix_spawn_file_actions_init(file_actions); |
| if (errorCode) |
| return errorCode; |
| |
| const char *path = xpc_dictionary_get_string(message, LauncherXPCServiceStdInPathKeyKey); |
| if (path) |
| { |
| errorCode = posix_spawn_file_actions_addopen(file_actions, STDIN_FILENO, path, O_NOCTTY | O_RDONLY, 0); |
| } |
| else |
| { |
| errorCode = posix_spawn_file_actions_addclose(file_actions, STDIN_FILENO); |
| } |
| if (errorCode) |
| return errorCode; |
| |
| path = xpc_dictionary_get_string(message, LauncherXPCServiceStdOutPathKeyKey); |
| if (path) |
| { |
| errorCode = posix_spawn_file_actions_addopen(file_actions, STDOUT_FILENO, path, O_NOCTTY | O_CREAT | O_WRONLY, 0640); |
| } |
| else |
| { |
| errorCode = posix_spawn_file_actions_addclose(file_actions, STDOUT_FILENO); |
| } |
| if (errorCode) |
| return errorCode; |
| |
| path = xpc_dictionary_get_string(message, LauncherXPCServiceStdErrPathKeyKey); |
| if (path) |
| { |
| errorCode = posix_spawn_file_actions_addopen(file_actions, STDERR_FILENO, path, O_NOCTTY | O_CREAT | O_RDWR, 0640); |
| } |
| else |
| { |
| errorCode = posix_spawn_file_actions_addclose(file_actions, STDERR_FILENO); |
| } |
| |
| return errorCode; |
| } |
| |
| bool extract_args(xpc_object_t message, const char *prefix, const char ***argsOut) |
| { |
| char buf[50]; // long enough for 'argXXX' |
| memset(buf, 0, 50); |
| sprintf(buf, "%sCount", prefix); |
| int argsCount = (int)xpc_dictionary_get_int64(message, buf); |
| if (argsCount == 0) { |
| return true; |
| } |
| |
| const char **argsp = NULL; |
| argsp = (const char **)malloc((argsCount+1) * sizeof(argsp[0])); |
| if (argsp == NULL) { |
| return false; |
| } |
| |
| for (int i=0; i<argsCount; i++) { |
| memset(buf, 0, 50); |
| sprintf(buf, "%s%i", prefix, i); |
| const char *arg = xpc_dictionary_get_string(message, buf); |
| argsp[i] = arg; |
| } |
| argsp[argsCount] = NULL; |
| |
| *argsOut = argsp; |
| return true; |
| } |
| |
| // Returns 0 if successful. |
| int get_args(xpc_object_t message, const char **path, const char ***argsOut, const char ***envOut) |
| { |
| if (!extract_args(message, LauncherXPCServiceArgPrefxKey, argsOut)) { |
| return 1; |
| } |
| *path = (*argsOut)[0]; |
| |
| if (!extract_args(message, LauncherXPCServiceEnvPrefxKey, envOut)) { |
| return 2; |
| } |
| |
| return 0; |
| } |
| |
| void _wait_for_child_exit(pid_t childPID) |
| { |
| dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); |
| dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, childPID, DISPATCH_PROC_EXIT, queue); |
| |
| if (source) { |
| dispatch_source_set_cancel_handler(source, ^{ |
| dispatch_release(source); |
| }); |
| |
| dispatch_source_set_event_handler(source, ^{ |
| |
| // Either finding the process was successful, or the process disappeared before libdispatch got around to hooking up the source. |
| dispatch_source_cancel(source); |
| |
| int status, ret; |
| do { |
| ret = waitpid(childPID, &status, 0); |
| } while (ret < 0 && errno == EINTR); |
| |
| }); |
| dispatch_resume(source); |
| } |
| } |
| |
| static void launcherXPC_peer_event_handler(xpc_connection_t peer, xpc_object_t event) |
| { |
| xpc_type_t type = xpc_get_type(event); |
| if (type == XPC_TYPE_ERROR) { |
| if (event == XPC_ERROR_CONNECTION_INVALID) { |
| // The client process on the other end of the connection has either |
| // crashed or cancelled the connection. After receiving this error, |
| // the connection is in an invalid state, and you do not need to |
| // call xpc_connection_cancel(). Just tear down any associated state |
| // here. |
| } else if (event == XPC_ERROR_TERMINATION_IMMINENT) { |
| // Handle per-connection termination cleanup. |
| } |
| } else { |
| assert(type == XPC_TYPE_DICTIONARY); |
| // Handle the message. |
| |
| pid_t childPID = 0; |
| posix_spawn_file_actions_t file_actions; |
| posix_spawnattr_t attributes; |
| |
| /* |
| Types of error. Error code will be specific to each type. |
| 100 - authorization failure |
| 101 - posixspawn attributes problem |
| 102 - get args/env problem |
| 103 - posixspawn problem |
| */ |
| int errorType = 100; |
| int errorCode = _validate_authorization(event); |
| if (!errorCode) { |
| errorType = 101; |
| errorCode = _setup_posixspawn_attributes_file_actions(event, &attributes, &file_actions); |
| if (!errorCode) { |
| const char *path = NULL; |
| const char **argvp = NULL; |
| const char **envp = NULL; |
| errorType = 102; |
| errorCode = get_args(event, &path, &argvp, &envp); |
| if (!errorCode) { |
| errorType = 103; |
| errorCode = posix_spawn(&childPID, path, &file_actions, &attributes, (char * const *)argvp, (char * const *)envp); |
| |
| if (argvp) free(argvp); |
| if (envp) free(envp); |
| |
| if (errorCode == 0) { |
| _wait_for_child_exit(childPID); |
| } |
| } |
| } |
| } |
| |
| xpc_object_t reply = xpc_dictionary_create_reply(event); |
| |
| xpc_dictionary_set_int64(reply, LauncherXPCServiceChildPIDKey, childPID); |
| if (!childPID) { |
| xpc_dictionary_set_int64(reply, LauncherXPCServiceErrorTypeKey, errorType); |
| xpc_dictionary_set_int64(reply, LauncherXPCServiceCodeTypeKey, errorCode); |
| } |
| |
| xpc_connection_send_message(peer, reply); |
| xpc_release(reply); |
| } |
| } |
| |
| static void launcherXPC_event_handler(xpc_connection_t peer) |
| { |
| // By defaults, new connections will target the default dispatch |
| // concurrent queue. |
| xpc_connection_set_event_handler(peer, ^(xpc_object_t event) { |
| launcherXPC_peer_event_handler(peer, event); |
| }); |
| |
| // This will tell the connection to begin listening for events. If you |
| // have some other initialization that must be done asynchronously, then |
| // you can defer this call until after that initialization is done. |
| xpc_connection_resume(peer); |
| } |
| |
| int main(int argc, const char *argv[]) |
| { |
| xpc_main(launcherXPC_event_handler); |
| return 0; |
| } |
| #endif |