1/*
2 * Copyright (C) 2010-2014 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#import "config.h"
27#import "ProcessLauncher.h"
28
29#import "DynamicLinkerEnvironmentExtractor.h"
30#import "EnvironmentVariables.h"
31#import "WebKitSystemInterface.h"
32#import <WebCore/SoftLinking.h>
33#import <crt_externs.h>
34#import <mach-o/dyld.h>
35#import <mach/machine.h>
36#import <servers/bootstrap.h>
37#import <spawn.h>
38#import <sys/param.h>
39#import <sys/stat.h>
40#import <wtf/PassRefPtr.h>
41#import <wtf/RetainPtr.h>
42#import <wtf/RunLoop.h>
43#import <wtf/Threading.h>
44#import <wtf/text/CString.h>
45#import <wtf/text/WTFString.h>
46#import <xpc/xpc.h>
47
48#if __has_include(<xpc/private.h>)
49#import <xpc/private.h>
50#endif
51
52// FIXME: We should be doing this another way.
53extern "C" kern_return_t bootstrap_register2(mach_port_t, name_t, mach_port_t, uint64_t);
54
55extern "C" void xpc_connection_set_instance(xpc_connection_t, uuid_t);
56extern "C" void xpc_dictionary_set_mach_send(xpc_object_t, const char*, mach_port_t);
57
58#if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 10100
59extern "C" void xpc_connection_set_bootstrap(xpc_connection_t connection, xpc_object_t bootstrap);
60
61// FIXME: Soft linking is temporary, make this into a regular function call once this function is available everywhere we need.
62SOFT_LINK_FRAMEWORK(CoreFoundation)
63SOFT_LINK_OPTIONAL(CoreFoundation, _CFBundleSetupXPCBootstrap, void, unused, (xpc_object_t))
64
65#endif
66
67namespace WebKit {
68
69namespace {
70
71struct UUIDHolder : public RefCounted<UUIDHolder> {
72    static PassRefPtr<UUIDHolder> create()
73    {
74        return adoptRef(new UUIDHolder);
75    }
76
77    UUIDHolder()
78    {
79        uuid_generate(uuid);
80    }
81
82    uuid_t uuid;
83};
84
85}
86
87static void setUpTerminationNotificationHandler(pid_t pid)
88{
89    dispatch_source_t processDiedSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, DISPATCH_PROC_EXIT, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
90    dispatch_source_set_event_handler(processDiedSource, ^{
91        int status;
92        waitpid(dispatch_source_get_handle(processDiedSource), &status, 0);
93        dispatch_source_cancel(processDiedSource);
94    });
95    dispatch_source_set_cancel_handler(processDiedSource, ^{
96        dispatch_release(processDiedSource);
97    });
98    dispatch_resume(processDiedSource);
99}
100
101static void addDYLDEnvironmentAdditions(const ProcessLauncher::LaunchOptions& launchOptions, bool isWebKitDevelopmentBuild, EnvironmentVariables& environmentVariables)
102{
103    DynamicLinkerEnvironmentExtractor environmentExtractor([[NSBundle mainBundle] executablePath], _NSGetMachExecuteHeader()->cputype);
104    environmentExtractor.getExtractedEnvironmentVariables(environmentVariables);
105
106    NSBundle *webKitBundle = [NSBundle bundleWithIdentifier:@"com.apple.WebKit"];
107    NSString *frameworksPath = [[webKitBundle bundlePath] stringByDeletingLastPathComponent];
108
109    // To make engineering builds work, if the path is outside of /System set up
110    // DYLD_FRAMEWORK_PATH to pick up other frameworks, but don't do it for the
111    // production configuration because it involves extra file system access.
112    if (isWebKitDevelopmentBuild)
113        environmentVariables.appendValue("DYLD_FRAMEWORK_PATH", [frameworksPath fileSystemRepresentation], ':');
114
115    NSString *processShimPathNSString = nil;
116#if ENABLE(NETSCAPE_PLUGIN_API)
117    if (launchOptions.processType == ProcessLauncher::PluginProcess) {
118        NSString *processPath = [webKitBundle pathForAuxiliaryExecutable:@"PluginProcess.app"];
119        NSString *processAppExecutablePath = [[NSBundle bundleWithPath:processPath] executablePath];
120
121        processShimPathNSString = [[processAppExecutablePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"PluginProcessShim.dylib"];
122    } else
123#endif // ENABLE(NETSCAPE_PLUGIN_API)
124#if ENABLE(NETWORK_PROCESS)
125    if (launchOptions.processType == ProcessLauncher::NetworkProcess) {
126        NSString *processPath = [webKitBundle pathForAuxiliaryExecutable:@"NetworkProcess.app"];
127        NSString *processAppExecutablePath = [[NSBundle bundleWithPath:processPath] executablePath];
128
129        processShimPathNSString = [[processAppExecutablePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"SecItemShim.dylib"];
130    } else
131#endif // ENABLE(NETWORK_PROCESS)
132    if (launchOptions.processType == ProcessLauncher::WebProcess) {
133        NSString *processPath = [webKitBundle pathForAuxiliaryExecutable:@"WebProcess.app"];
134        NSString *processAppExecutablePath = [[NSBundle bundleWithPath:processPath] executablePath];
135
136        processShimPathNSString = [[processAppExecutablePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"WebProcessShim.dylib"];
137    }
138
139    // Make sure that the shim library file exists and insert it.
140    if (processShimPathNSString) {
141        const char* processShimPath = [processShimPathNSString fileSystemRepresentation];
142        struct stat statBuf;
143        if (stat(processShimPath, &statBuf) == 0 && (statBuf.st_mode & S_IFMT) == S_IFREG)
144            environmentVariables.appendValue("DYLD_INSERT_LIBRARIES", processShimPath, ':');
145    }
146
147}
148
149typedef void (ProcessLauncher::*DidFinishLaunchingProcessFunction)(PlatformProcessIdentifier, IPC::Connection::Identifier);
150
151static const char* serviceName(const ProcessLauncher::LaunchOptions& launchOptions, bool forDevelopment)
152{
153    switch (launchOptions.processType) {
154    case ProcessLauncher::WebProcess:
155        if (forDevelopment)
156            return "com.apple.WebKit.WebContent.Development";
157        return "com.apple.WebKit.WebContent";
158#if ENABLE(NETWORK_PROCESS)
159    case ProcessLauncher::NetworkProcess:
160        if (forDevelopment)
161            return "com.apple.WebKit.Networking.Development";
162        return "com.apple.WebKit.Networking";
163#endif
164#if ENABLE(DATABASE_PROCESS)
165    case ProcessLauncher::DatabaseProcess:
166        if (forDevelopment)
167            return "com.apple.WebKit.Databases.Development";
168        return "com.apple.WebKit.Databases";
169#endif
170#if ENABLE(NETSCAPE_PLUGIN_API)
171    case ProcessLauncher::PluginProcess:
172        if (forDevelopment)
173            return "com.apple.WebKit.Plugin.Development";
174
175        // FIXME: Support plugins that require an executable heap.
176        if (launchOptions.architecture == CPU_TYPE_X86)
177            return "com.apple.WebKit.Plugin.32";
178        if (launchOptions.architecture == CPU_TYPE_X86_64)
179            return "com.apple.WebKit.Plugin.64";
180
181        ASSERT_NOT_REACHED();
182        return 0;
183#endif
184    }
185}
186
187static bool shouldLeakBoost(const ProcessLauncher::LaunchOptions& launchOptions)
188{
189#if PLATFORM(IOS)
190    // On iOS, leak a boost onto all child processes
191    UNUSED_PARAM(launchOptions);
192    return true;
193#elif ENABLE(NETWORK_PROCESS)
194    // On Mac, leak a boost onto the NetworkProcess.
195    return launchOptions.processType == ProcessLauncher::NetworkProcess;
196#else
197    UNUSED_PARAM(launchOptions);
198    return false;
199#endif
200}
201
202static void connectToService(const ProcessLauncher::LaunchOptions& launchOptions, bool forDevelopment, ProcessLauncher* that, DidFinishLaunchingProcessFunction didFinishLaunchingProcessFunction, UUIDHolder* instanceUUID)
203{
204    // Create a connection to the WebKit XPC service.
205    auto connection = IPC::adoptXPC(xpc_connection_create(serviceName(launchOptions, forDevelopment), 0));
206    xpc_connection_set_instance(connection.get(), instanceUUID->uuid);
207
208#if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 10100
209    // Inherit UI process localization. It can be different from child process default localization:
210    // 1. When the application and system frameworks simply have different localized resources available, we should match the application.
211    // 1.1. An important case is WebKitTestRunner, where we should use English localizations for all system frameworks.
212    // 2. When AppleLanguages is passed as command line argument for UI process, or set in its preferences, we should respect it in child processes.
213    RetainPtr<CFStringRef> localization = adoptCF(WKCopyCFLocalizationPreferredName(0));
214    if (localization && _CFBundleSetupXPCBootstrapPtr()) {
215        auto initializationMessage = IPC::adoptXPC(xpc_dictionary_create(nullptr, nullptr, 0));
216        _CFBundleSetupXPCBootstrapPtr()(initializationMessage.get());
217        xpc_connection_set_bootstrap(connection.get(), initializationMessage.get());
218    }
219#endif
220
221    // XPC requires having an event handler, even if it is not used.
222    xpc_connection_set_event_handler(connection.get(), ^(xpc_object_t event) { });
223    xpc_connection_resume(connection.get());
224
225    if (shouldLeakBoost(launchOptions)) {
226        auto preBootstrapMessage = IPC::adoptXPC(xpc_dictionary_create(nullptr, nullptr, 0));
227        xpc_dictionary_set_string(preBootstrapMessage.get(), "message-name", "pre-bootstrap");
228        xpc_connection_send_message(connection.get(), preBootstrapMessage.get());
229    }
230
231    // Create the listening port.
232    mach_port_t listeningPort;
233    mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &listeningPort);
234
235    // Insert a send right so we can send to it.
236    mach_port_insert_right(mach_task_self(), listeningPort, listeningPort, MACH_MSG_TYPE_MAKE_SEND);
237
238    NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
239    CString clientIdentifier = bundleIdentifier ? String([[NSBundle mainBundle] bundleIdentifier]).utf8() : *_NSGetProgname();
240
241    // FIXME: Switch to xpc_connection_set_bootstrap once it's available everywhere we need.
242    auto bootstrapMessage = IPC::adoptXPC(xpc_dictionary_create(nullptr, nullptr, 0));
243    xpc_dictionary_set_string(bootstrapMessage.get(), "message-name", "bootstrap");
244    xpc_dictionary_set_string(bootstrapMessage.get(), "framework-executable-path", [[[NSBundle bundleWithIdentifier:@"com.apple.WebKit"] executablePath] fileSystemRepresentation]);
245    xpc_dictionary_set_mach_send(bootstrapMessage.get(), "server-port", listeningPort);
246    xpc_dictionary_set_string(bootstrapMessage.get(), "client-identifier", clientIdentifier.data());
247    xpc_dictionary_set_string(bootstrapMessage.get(), "ui-process-name", [[[NSProcessInfo processInfo] processName] UTF8String]);
248
249    if (forDevelopment) {
250        xpc_dictionary_set_fd(bootstrapMessage.get(), "stdout", STDOUT_FILENO);
251        xpc_dictionary_set_fd(bootstrapMessage.get(), "stderr", STDERR_FILENO);
252    }
253
254    auto extraInitializationData = IPC::adoptXPC(xpc_dictionary_create(nullptr, nullptr, 0));
255
256    for (const auto& keyValuePair : launchOptions.extraInitializationData)
257        xpc_dictionary_set_string(extraInitializationData.get(), keyValuePair.key.utf8().data(), keyValuePair.value.utf8().data());
258
259    xpc_dictionary_set_value(bootstrapMessage.get(), "extra-initialization-data", extraInitializationData.get());
260
261    that->ref();
262
263    xpc_connection_send_message_with_reply(connection.get(), bootstrapMessage.get(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(xpc_object_t reply) {
264        xpc_type_t type = xpc_get_type(reply);
265        if (type == XPC_TYPE_ERROR) {
266            // We failed to launch. Release the send right.
267            mach_port_deallocate(mach_task_self(), listeningPort);
268
269            // And the receive right.
270            mach_port_mod_refs(mach_task_self(), listeningPort, MACH_PORT_RIGHT_RECEIVE, -1);
271
272            RunLoop::main().dispatch(bind(didFinishLaunchingProcessFunction, that, 0, IPC::Connection::Identifier()));
273        } else {
274            ASSERT(type == XPC_TYPE_DICTIONARY);
275            ASSERT(!strcmp(xpc_dictionary_get_string(reply, "message-name"), "process-finished-launching"));
276
277            // The process has finished launching, grab the pid from the connection.
278            pid_t processIdentifier = xpc_connection_get_pid(connection.get());
279
280            // We've finished launching the process, message back to the main run loop. This takes ownership of the connection.
281            RunLoop::main().dispatch(bind(didFinishLaunchingProcessFunction, that, processIdentifier, IPC::Connection::Identifier(listeningPort, connection)));
282        }
283
284        that->deref();
285    });
286}
287
288static void connectToReExecService(const ProcessLauncher::LaunchOptions& launchOptions, ProcessLauncher* that, DidFinishLaunchingProcessFunction didFinishLaunchingProcessFunction)
289{
290    EnvironmentVariables environmentVariables;
291    addDYLDEnvironmentAdditions(launchOptions, true, environmentVariables);
292
293#if ENABLE(NETSCAPE_PLUGIN_API)
294    if (launchOptions.processType == ProcessLauncher::PluginProcess) {
295        // Tagged pointers break video in Flash, see bug 135354.
296        environmentVariables.set("NSStringDisableTagged", "YES");
297    }
298#endif
299
300    // Generate the uuid for the service instance we are about to create.
301    // FIXME: This UUID should be stored on the ChildProcessProxy.
302    RefPtr<UUIDHolder> instanceUUID = UUIDHolder::create();
303
304    // FIXME: It would be nice if we could use XPCPtr for this connection as well, but we'd have to be careful
305    // not to introduce any retain cycles in the call to xpc_connection_set_event_handler below.
306    xpc_connection_t reExecConnection = xpc_connection_create(serviceName(launchOptions, true), 0);
307    xpc_connection_set_instance(reExecConnection, instanceUUID->uuid);
308
309    // Keep the ProcessLauncher alive while we do the re-execing (balanced in event handler).
310    that->ref();
311
312    // We wait for the connection to tear itself down (indicated via an error event)
313    // to indicate that the service instance re-execed itself, and is now ready to be
314    // connected to.
315    xpc_connection_set_event_handler(reExecConnection, ^(xpc_object_t event) {
316        ASSERT(xpc_get_type(event) == XPC_TYPE_ERROR);
317
318        connectToService(launchOptions, true, that, didFinishLaunchingProcessFunction, instanceUUID.get());
319
320        // Release the connection.
321        xpc_release(reExecConnection);
322
323        // Other end of ref called before we setup the event handler.
324        that->deref();
325    });
326    xpc_connection_resume(reExecConnection);
327
328    xpc_object_t reExecMessage = xpc_dictionary_create(0, 0, 0);
329    xpc_dictionary_set_string(reExecMessage, "message-name", "re-exec");
330
331    cpu_type_t architecture = launchOptions.architecture == ProcessLauncher::LaunchOptions::MatchCurrentArchitecture ? _NSGetMachExecuteHeader()->cputype : launchOptions.architecture;
332    xpc_dictionary_set_uint64(reExecMessage, "architecture", (uint64_t)architecture);
333
334    xpc_object_t environment = xpc_array_create(0, 0);
335    char** environmentPointer = environmentVariables.environmentPointer();
336    Vector<CString> temps;
337    for (size_t i = 0; environmentPointer[i]; ++i) {
338        CString temp(environmentPointer[i], strlen(environmentPointer[i]));
339        temps.append(temp);
340
341        xpc_array_set_string(environment, XPC_ARRAY_APPEND, temp.data());
342    }
343    xpc_dictionary_set_value(reExecMessage, "environment", environment);
344    xpc_release(environment);
345
346    xpc_dictionary_set_bool(reExecMessage, "executable-heap", launchOptions.executableHeap);
347
348    xpc_connection_send_message(reExecConnection, reExecMessage);
349    xpc_release(reExecMessage);
350}
351
352static void createService(const ProcessLauncher::LaunchOptions& launchOptions, bool forDevelopment, ProcessLauncher* that, DidFinishLaunchingProcessFunction didFinishLaunchingProcessFunction)
353{
354    if (forDevelopment) {
355        connectToReExecService(launchOptions, that, didFinishLaunchingProcessFunction);
356        return;
357    }
358
359    // Generate the uuid for the service instance we are about to create.
360    // FIXME: This UUID should be stored on the ChildProcessProxy.
361    RefPtr<UUIDHolder> instanceUUID = UUIDHolder::create();
362    connectToService(launchOptions, false, that, didFinishLaunchingProcessFunction, instanceUUID.get());
363}
364
365static bool tryPreexistingProcess(const ProcessLauncher::LaunchOptions& launchOptions, ProcessLauncher* that, DidFinishLaunchingProcessFunction didFinishLaunchingProcessFunction)
366{
367    EnvironmentVariables environmentVariables;
368    static const char* preexistingProcessServiceName = environmentVariables.get(EnvironmentVariables::preexistingProcessServiceNameKey());
369
370    ProcessLauncher::ProcessType preexistingProcessType;
371    if (preexistingProcessServiceName)
372        ProcessLauncher::getProcessTypeFromString(environmentVariables.get(EnvironmentVariables::preexistingProcessTypeKey()), preexistingProcessType);
373
374    bool usePreexistingProcess = preexistingProcessServiceName && preexistingProcessType == launchOptions.processType;
375    if (!usePreexistingProcess)
376        return false;
377
378    // Create the listening port.
379    mach_port_t listeningPort;
380    mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &listeningPort);
381
382    // Insert a send right so we can send to it.
383    mach_port_insert_right(mach_task_self(), listeningPort, listeningPort, MACH_MSG_TYPE_MAKE_SEND);
384
385    pid_t processIdentifier = 0;
386
387    mach_port_t lookupPort;
388    bootstrap_look_up(bootstrap_port, preexistingProcessServiceName, &lookupPort);
389
390    mach_msg_header_t header;
391    header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND);
392    header.msgh_id = 0;
393    header.msgh_local_port = listeningPort;
394    header.msgh_remote_port = lookupPort;
395    header.msgh_size = sizeof(header);
396    kern_return_t kr = mach_msg(&header, MACH_SEND_MSG, sizeof(header), 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
397
398    mach_port_deallocate(mach_task_self(), lookupPort);
399    preexistingProcessServiceName = 0;
400
401    if (kr) {
402        LOG_ERROR("Failed to pick up preexisting process at %s (%x). Launching a new process of type %s instead.", preexistingProcessServiceName, kr, ProcessLauncher::processTypeAsString(launchOptions.processType));
403        return false;
404    }
405
406    // We've finished launching the process, message back to the main run loop.
407    RunLoop::main().dispatch(bind(didFinishLaunchingProcessFunction, that, processIdentifier, IPC::Connection::Identifier(listeningPort)));
408    return true;
409}
410
411static void createProcess(const ProcessLauncher::LaunchOptions& launchOptions, bool isWebKitDevelopmentBuild, ProcessLauncher* that, DidFinishLaunchingProcessFunction didFinishLaunchingProcessFunction)
412{
413    EnvironmentVariables environmentVariables;
414    addDYLDEnvironmentAdditions(launchOptions, isWebKitDevelopmentBuild, environmentVariables);
415
416    // Create the listening port.
417    mach_port_t listeningPort;
418    mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &listeningPort);
419
420    // Insert a send right so we can send to it.
421    mach_port_insert_right(mach_task_self(), listeningPort, listeningPort, MACH_MSG_TYPE_MAKE_SEND);
422
423    NSBundle *webKitBundle = [NSBundle bundleWithIdentifier:@"com.apple.WebKit"];
424
425    NSString *processPath = nil;
426    switch (launchOptions.processType) {
427    case ProcessLauncher::WebProcess:
428        processPath = [webKitBundle pathForAuxiliaryExecutable:@"WebProcess.app"];
429        break;
430#if ENABLE(NETSCAPE_PLUGIN_API)
431    case ProcessLauncher::PluginProcess:
432        processPath = [webKitBundle pathForAuxiliaryExecutable:@"PluginProcess.app"];
433        break;
434#endif
435#if ENABLE(NETWORK_PROCESS)
436    case ProcessLauncher::NetworkProcess:
437        processPath = [webKitBundle pathForAuxiliaryExecutable:@"NetworkProcess.app"];
438        break;
439#endif
440#if ENABLE(DATABASE_PROCESS)
441    case ProcessLauncher::DatabaseProcess:
442        processPath = [webKitBundle pathForAuxiliaryExecutable:@"DatabaseProcess.app"];
443        break;
444#endif
445    }
446
447    NSString *frameworkExecutablePath = [webKitBundle executablePath];
448    NSString *processAppExecutablePath = [[NSBundle bundleWithPath:processPath] executablePath];
449
450    NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
451    CString clientIdentifier = bundleIdentifier ? String([[NSBundle mainBundle] bundleIdentifier]).utf8() : *_NSGetProgname();
452
453    // Make a unique, per pid, per process launcher web process service name.
454    CString serviceName = String::format("com.apple.WebKit.WebProcess-%d-%p", getpid(), that).utf8();
455
456    // Inherit UI process localization. It can be different from child process default localization:
457    // 1. When the application and system frameworks simply have different localized resources available, we should match the application.
458    // 1.1. An important case is WebKitTestRunner, where we should use English localizations for all system frameworks.
459    // 2. When AppleLanguages is passed as command line argument for UI process, or set in its preferences, we should respect it in child processes.
460    CString appleLanguagesArgument = String("('" + String(adoptCF(WKCopyCFLocalizationPreferredName(0)).get()) + "')").utf8();
461
462    Vector<const char*> args;
463    args.append([processAppExecutablePath fileSystemRepresentation]);
464    args.append([frameworkExecutablePath fileSystemRepresentation]);
465    args.append("-type");
466    args.append(ProcessLauncher::processTypeAsString(launchOptions.processType));
467    args.append("-servicename");
468    args.append(serviceName.data());
469    args.append("-client-identifier");
470    args.append(clientIdentifier.data());
471    args.append("-ui-process-name");
472    args.append([[[NSProcessInfo processInfo] processName] UTF8String]);
473    args.append("-AppleLanguages"); // This argument will be handled by Core Foundation.
474    args.append(appleLanguagesArgument.data());
475
476    HashMap<String, String>::const_iterator it = launchOptions.extraInitializationData.begin();
477    HashMap<String, String>::const_iterator end = launchOptions.extraInitializationData.end();
478    Vector<CString> temps;
479    for (; it != end; ++it) {
480        String keyPlusDash = "-" + it->key;
481        CString key(keyPlusDash.utf8().data());
482        temps.append(key);
483        args.append(key.data());
484
485        CString value(it->value.utf8().data());
486        temps.append(value);
487        args.append(value.data());
488    }
489
490    args.append(nullptr);
491
492    // Register ourselves.
493    kern_return_t kr = bootstrap_register2(bootstrap_port, const_cast<char*>(serviceName.data()), listeningPort, 0);
494    ASSERT_UNUSED(kr, kr == KERN_SUCCESS);
495
496    posix_spawnattr_t attr;
497    posix_spawnattr_init(&attr);
498
499    short flags = 0;
500
501    // We want our process to receive all signals.
502    sigset_t signalMaskSet;
503    sigemptyset(&signalMaskSet);
504
505    posix_spawnattr_setsigmask(&attr, &signalMaskSet);
506    flags |= POSIX_SPAWN_SETSIGMASK;
507
508    // Determine the architecture to use.
509    cpu_type_t architecture = launchOptions.architecture;
510    if (architecture == ProcessLauncher::LaunchOptions::MatchCurrentArchitecture)
511        architecture = _NSGetMachExecuteHeader()->cputype;
512
513    cpu_type_t cpuTypes[] = { architecture };
514    size_t outCount = 0;
515    posix_spawnattr_setbinpref_np(&attr, 1, cpuTypes, &outCount);
516
517    // Start suspended so we can set up the termination notification handler.
518    flags |= POSIX_SPAWN_START_SUSPENDED;
519
520    static const int allowExecutableHeapFlag = 0x2000;
521    if (launchOptions.executableHeap)
522        flags |= allowExecutableHeapFlag;
523
524    posix_spawnattr_setflags(&attr, flags);
525
526    pid_t processIdentifier = 0;
527    int result = posix_spawn(&processIdentifier, args[0], 0, &attr, const_cast<char**>(args.data()), environmentVariables.environmentPointer());
528
529    posix_spawnattr_destroy(&attr);
530
531    if (!result) {
532        // Set up the termination notification handler and then ask the child process to continue.
533        setUpTerminationNotificationHandler(processIdentifier);
534        kill(processIdentifier, SIGCONT);
535    } else {
536        // We failed to launch. Release the send right.
537        mach_port_deallocate(mach_task_self(), listeningPort);
538
539        // And the receive right.
540        mach_port_mod_refs(mach_task_self(), listeningPort, MACH_PORT_RIGHT_RECEIVE, -1);
541
542        listeningPort = MACH_PORT_NULL;
543        processIdentifier = 0;
544    }
545
546    // We've finished launching the process, message back to the main run loop.
547    RunLoop::main().dispatch(bind(didFinishLaunchingProcessFunction, that, processIdentifier, IPC::Connection::Identifier(listeningPort)));
548}
549
550static NSString *systemDirectoryPath()
551{
552    static NSString *path = [^{
553#if PLATFORM(IOS_SIMULATOR)
554        char *simulatorRoot = getenv("SIMULATOR_ROOT");
555        return simulatorRoot ? [NSString stringWithFormat:@"%s/System/", simulatorRoot] : @"/System/";
556#else
557        return @"/System/";
558#endif
559    }() copy];
560
561    return path;
562}
563
564void ProcessLauncher::launchProcess()
565{
566    if (tryPreexistingProcess(m_launchOptions, this, &ProcessLauncher::didFinishLaunchingProcess))
567        return;
568
569    bool isWebKitDevelopmentBuild = ![[[[NSBundle bundleWithIdentifier:@"com.apple.WebKit"] bundlePath] stringByDeletingLastPathComponent] hasPrefix:systemDirectoryPath()];
570
571    if (m_launchOptions.useXPC) {
572        createService(m_launchOptions, isWebKitDevelopmentBuild, this, &ProcessLauncher::didFinishLaunchingProcess);
573        return;
574    }
575
576    createProcess(m_launchOptions, isWebKitDevelopmentBuild, this, &ProcessLauncher::didFinishLaunchingProcess);
577}
578
579void ProcessLauncher::terminateProcess()
580{
581    if (m_isLaunching) {
582        invalidate();
583        return;
584    }
585
586    if (!m_processIdentifier)
587        return;
588
589    kill(m_processIdentifier, SIGKILL);
590    m_processIdentifier = 0;
591}
592
593void ProcessLauncher::platformInvalidate()
594{
595}
596
597} // namespace WebKit
598