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