1// 2// main.m 3// Used in both LauncherXPCService and LaunchRootXPCService targets 4// 5// Copyright (c) 2012 Apple Inc. All rights reserved. 6// 7#include <AvailabilityMacros.h> 8 9#if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 10#define BUILDING_ON_SNOW_LEOPARD 1 11#endif 12 13#if !BUILDING_ON_SNOW_LEOPARD 14#define __XPC_PRIVATE_H__ 15#include <xpc/xpc.h> 16#include <spawn.h> 17#include <signal.h> 18#include <assert.h> 19#include <sys/errno.h> 20#include "LauncherXPCService.h" 21 22// Declaration. Returns 0 if successful. 23int _validate_authorization(xpc_object_t message); 24 25// Returns 0 if successful. 26int _setup_posixspawn_attributes_file_actions(xpc_object_t message, posix_spawnattr_t *attr, posix_spawn_file_actions_t *file_actions) 27{ 28 *attr = 0; 29 30 int errorCode = posix_spawnattr_init(attr); 31 if (errorCode) 32 return errorCode; 33 34 cpu_type_t cpuType = (cpu_type_t)xpc_dictionary_get_int64(message, LauncherXPCServiceCPUTypeKey); 35 if (cpuType == -2) { 36 cpuType= CPU_TYPE_ANY; 37 } 38 size_t realCount; 39 errorCode = posix_spawnattr_setbinpref_np(attr, 1, &cpuType, &realCount); 40 if (errorCode) 41 return errorCode; 42 43 sigset_t no_signals; 44 sigset_t all_signals; 45 sigemptyset (&no_signals); 46 sigfillset (&all_signals); 47 posix_spawnattr_setsigmask(attr, &no_signals); 48 posix_spawnattr_setsigdefault(attr, &all_signals); 49 50 short flags = xpc_dictionary_get_int64(message, LauncherXPCServicePosixspawnFlagsKey); 51 errorCode = posix_spawnattr_setflags(attr, flags); 52 if (errorCode) 53 return errorCode; 54 55 // Setup any file actions. Here we are emulating what debugserver would do normally in Host.mm since the XPC service meant only for debugserver. 56 errorCode = posix_spawn_file_actions_init(file_actions); 57 if (errorCode) 58 return errorCode; 59 errorCode = posix_spawn_file_actions_addclose(file_actions, STDIN_FILENO); 60 if (errorCode) 61 return errorCode; 62 errorCode = posix_spawn_file_actions_addclose(file_actions, STDOUT_FILENO); 63 if (errorCode) 64 return errorCode; 65 errorCode = posix_spawn_file_actions_addclose(file_actions, STDERR_FILENO); 66 67 return errorCode; 68} 69 70bool extract_args(xpc_object_t message, const char *prefix, const char ***argsOut) 71{ 72 char buf[50]; // long enough for 'argXXX' 73 memset(buf, 0, 50); 74 sprintf(buf, "%sCount", prefix); 75 int argsCount = (int)xpc_dictionary_get_int64(message, buf); 76 if (argsCount == 0) { 77 return true; 78 } 79 80 const char **argsp = NULL; 81 argsp = (const char **)malloc((argsCount+1) * sizeof(argsp[0])); 82 if (argsp == NULL) { 83 return false; 84 } 85 86 for (int i=0; i<argsCount; i++) { 87 memset(buf, 0, 50); 88 sprintf(buf, "%s%i", prefix, i); 89 const char *arg = xpc_dictionary_get_string(message, buf); 90 argsp[i] = arg; 91 } 92 argsp[argsCount] = NULL; 93 94 *argsOut = argsp; 95 return true; 96} 97 98// Returns 0 if successful. 99int get_args(xpc_object_t message, const char **path, const char ***argsOut, const char ***envOut) 100{ 101 if (!extract_args(message, LauncherXPCServiceArgPrefxKey, argsOut)) { 102 return 1; 103 } 104 *path = (*argsOut)[0]; 105 106 if (!extract_args(message, LauncherXPCServiceEnvPrefxKey, envOut)) { 107 return 2; 108 } 109 110 return 0; 111} 112 113void _wait_for_child_exit(pid_t childPID) 114{ 115 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 116 dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, childPID, DISPATCH_PROC_EXIT, queue); 117 118 if (source) { 119 dispatch_source_set_cancel_handler(source, ^{ 120 dispatch_release(source); 121 }); 122 123 dispatch_source_set_event_handler(source, ^{ 124 125 // Either finding the process was successful, or the process disappeared before libdispatch got around to hooking up the source. 126 dispatch_source_cancel(source); 127 128 int status, ret; 129 do { 130 ret = waitpid(childPID, &status, 0); 131 } while (ret < 0 && errno == EINTR); 132 133 }); 134 dispatch_resume(source); 135 } 136} 137 138static void launcherXPC_peer_event_handler(xpc_connection_t peer, xpc_object_t event) 139{ 140 xpc_type_t type = xpc_get_type(event); 141 if (type == XPC_TYPE_ERROR) { 142 if (event == XPC_ERROR_CONNECTION_INVALID) { 143 // The client process on the other end of the connection has either 144 // crashed or cancelled the connection. After receiving this error, 145 // the connection is in an invalid state, and you do not need to 146 // call xpc_connection_cancel(). Just tear down any associated state 147 // here. 148 } else if (event == XPC_ERROR_TERMINATION_IMMINENT) { 149 // Handle per-connection termination cleanup. 150 } 151 } else { 152 assert(type == XPC_TYPE_DICTIONARY); 153 // Handle the message. 154 155 pid_t childPID = 0; 156 posix_spawn_file_actions_t file_actions; 157 posix_spawnattr_t attributes; 158 159 /* 160 Types of error. Error code will be specific to each type. 161 100 - authorization failure 162 101 - posixspawn attributes problem 163 102 - get args/env problem 164 103 - posixspawn problem 165 */ 166 int errorType = 100; 167 int errorCode = _validate_authorization(event); 168 if (!errorCode) { 169 errorType = 101; 170 errorCode = _setup_posixspawn_attributes_file_actions(event, &attributes, &file_actions); 171 if (!errorCode) { 172 const char *path = NULL; 173 const char **argvp = NULL; 174 const char **envp = NULL; 175 errorType = 102; 176 errorCode = get_args(event, &path, &argvp, &envp); 177 if (!errorCode) { 178 errorType = 103; 179 errorCode = posix_spawn(&childPID, path, &file_actions, &attributes, (char * const *)argvp, (char * const *)envp); 180 181 if (argvp) free(argvp); 182 if (envp) free(envp); 183 184 if (errorCode == 0) { 185 _wait_for_child_exit(childPID); 186 } 187 } 188 } 189 } 190 191 xpc_object_t reply = xpc_dictionary_create_reply(event); 192 193 xpc_dictionary_set_int64(reply, LauncherXPCServiceChildPIDKey, childPID); 194 if (!childPID) { 195 xpc_dictionary_set_int64(reply, LauncherXPCServiceErrorTypeKey, errorType); 196 xpc_dictionary_set_int64(reply, LauncherXPCServiceCodeTypeKey, errorCode); 197 } 198 199 xpc_connection_send_message(peer, reply); 200 xpc_release(reply); 201 } 202} 203 204static void launcherXPC_event_handler(xpc_connection_t peer) 205{ 206 // By defaults, new connections will target the default dispatch 207 // concurrent queue. 208 xpc_connection_set_event_handler(peer, ^(xpc_object_t event) { 209 launcherXPC_peer_event_handler(peer, event); 210 }); 211 212 // This will tell the connection to begin listening for events. If you 213 // have some other initialization that must be done asynchronously, then 214 // you can defer this call until after that initialization is done. 215 xpc_connection_resume(peer); 216} 217 218int main(int argc, const char *argv[]) 219{ 220 xpc_main(launcherXPC_event_handler); 221 return 0; 222} 223#endif 224