Home | History | Annotate | Download | only in launcherXPCService
      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.
     23 int _validate_authorization(xpc_object_t message);
     24 
     25 // Returns 0 if successful.
     26 int _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 
     70 bool 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.
     99 int 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 
    113 void _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 
    138 static 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 
    204 static 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 
    218 int main(int argc, const char *argv[])
    219 {
    220 	xpc_main(launcherXPC_event_handler);
    221 	return 0;
    222 }
    223 #endif
    224