2b4b8085a134b6deed87d88a1096a9bbba727390
[WebKit-https.git] / Source / WebKit2 / UIProcess / Launcher / mac / ProcessLauncherMac.mm
1 /*
2  * Copyright (C) 2010-2016 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 <WebCore/CFBundleSPI.h>
32 #import <WebCore/ServersSPI.h>
33 #import <WebCore/SoftLinking.h>
34 #import <WebCore/WebCoreNSStringExtras.h>
35 #import <crt_externs.h>
36 #import <mach-o/dyld.h>
37 #import <mach/machine.h>
38 #import <spawn.h>
39 #import <sys/param.h>
40 #import <sys/stat.h>
41 #import <wtf/PassRefPtr.h>
42 #import <wtf/RetainPtr.h>
43 #import <wtf/RunLoop.h>
44 #import <wtf/Threading.h>
45 #import <wtf/spi/darwin/XPCSPI.h>
46 #import <wtf/text/CString.h>
47 #import <wtf/text/WTFString.h>
48
49 namespace WebKit {
50
51 namespace {
52
53 struct UUIDHolder : public RefCounted<UUIDHolder> {
54     static Ref<UUIDHolder> create()
55     {
56         return adoptRef(*new UUIDHolder);
57     }
58
59     UUIDHolder()
60     {
61         uuid_generate(uuid);
62     }
63
64     uuid_t uuid;
65 };
66
67 }
68
69 static void setUpTerminationNotificationHandler(pid_t pid)
70 {
71     dispatch_source_t processDiedSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, DISPATCH_PROC_EXIT, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
72     dispatch_source_set_event_handler(processDiedSource, ^{
73         int status;
74         waitpid(dispatch_source_get_handle(processDiedSource), &status, 0);
75         dispatch_source_cancel(processDiedSource);
76     });
77     dispatch_source_set_cancel_handler(processDiedSource, ^{
78         dispatch_release(processDiedSource);
79     });
80     dispatch_resume(processDiedSource);
81 }
82
83 #if ASAN_ENABLED
84 static const char* copyASanDynamicLibraryPath()
85 {
86     uint32_t imageCount = _dyld_image_count();
87     for (uint32_t i = 0; i < imageCount; ++i) {
88         if (strstr(_dyld_get_image_name(i), "/libclang_rt.asan_"))
89             return fastStrDup(_dyld_get_image_name(i));
90     }
91
92     return 0;
93 }
94 #endif
95
96 #if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101100
97 static RetainPtr<NSString> computeProcessShimPath(const ProcessLauncher::LaunchOptions& launchOptions, NSBundle *webKitBundle)
98 {
99 #if ENABLE(NETSCAPE_PLUGIN_API)
100     if (launchOptions.processType == ProcessLauncher::PluginProcess)
101         return [[webKitBundle privateFrameworksPath] stringByAppendingPathComponent:@"PluginProcessShim.dylib"];
102 #endif
103
104     if (launchOptions.processType == ProcessLauncher::NetworkProcess)
105         return [[webKitBundle privateFrameworksPath] stringByAppendingPathComponent:@"SecItemShim.dylib"];
106
107     if (launchOptions.processType == ProcessLauncher::WebProcess)
108         return [[webKitBundle privateFrameworksPath] stringByAppendingPathComponent:@"WebProcessShim.dylib"];
109
110     return nil;
111 }
112 #endif
113
114 static void addDYLDEnvironmentAdditions(const ProcessLauncher::LaunchOptions& launchOptions, bool isWebKitDevelopmentBuild, EnvironmentVariables& environmentVariables)
115 {
116     DynamicLinkerEnvironmentExtractor environmentExtractor([[NSBundle mainBundle] executablePath], _NSGetMachExecuteHeader()->cputype);
117     environmentExtractor.getExtractedEnvironmentVariables(environmentVariables);
118
119     NSBundle *webKitBundle = [NSBundle bundleWithIdentifier:@"com.apple.WebKit"];
120     NSString *frameworksPath = [[webKitBundle bundlePath] stringByDeletingLastPathComponent];
121
122     // To make engineering builds work, if the path is outside of /System set up
123     // DYLD_FRAMEWORK_PATH to pick up other frameworks, but don't do it for the
124     // production configuration because it involves extra file system access.
125     if (isWebKitDevelopmentBuild) {
126         environmentVariables.appendValue("DYLD_FRAMEWORK_PATH", [frameworksPath fileSystemRepresentation], ':');
127         environmentVariables.appendValue("DYLD_LIBRARY_PATH", webKitBundle.privateFrameworksPath.fileSystemRepresentation, ':');
128     }
129
130 #if ASAN_ENABLED
131     static const char* asanLibraryPath = copyASanDynamicLibraryPath();
132     ASSERT(asanLibraryPath); // ASan runtime library was not found in the current process. This code may need to be updated if the library name has changed.
133     // ASan doesn't require this library to be inserted, but it otherwise automatically performs a re-exec, making the child process stop in a debugger on launch one extra time.
134     if (asanLibraryPath)
135         environmentVariables.appendValue("DYLD_INSERT_LIBRARIES", asanLibraryPath, ':');
136 #endif
137
138 #if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101100
139     if (auto shimPath = computeProcessShimPath(launchOptions, webKitBundle)) {
140         // Make sure that the shim library file exists and insert it.
141         const char* processShimPath = [shimPath 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 #endif
147 }
148
149 typedef void (ProcessLauncher::*DidFinishLaunchingProcessFunction)(PlatformProcessIdentifier, IPC::Connection::Identifier);
150
151 static const char* serviceName(const ProcessLauncher::LaunchOptions& launchOptions)
152 {
153     switch (launchOptions.processType) {
154     case ProcessLauncher::WebProcess:
155         return "com.apple.WebKit.WebContent" WK_XPC_SERVICE_SUFFIX;
156     case ProcessLauncher::NetworkProcess:
157         return "com.apple.WebKit.Networking" WK_XPC_SERVICE_SUFFIX;
158 #if ENABLE(DATABASE_PROCESS)
159     case ProcessLauncher::DatabaseProcess:
160         return "com.apple.WebKit.Databases" WK_XPC_SERVICE_SUFFIX;
161 #endif
162 #if ENABLE(NETSCAPE_PLUGIN_API)
163     case ProcessLauncher::PluginProcess:
164         // FIXME: Support plugins that require an executable heap.
165         if (launchOptions.architecture == CPU_TYPE_X86)
166             return "com.apple.WebKit.Plugin.32" WK_XPC_SERVICE_SUFFIX;
167         if (launchOptions.architecture == CPU_TYPE_X86_64)
168             return "com.apple.WebKit.Plugin.64" WK_XPC_SERVICE_SUFFIX;
169
170         ASSERT_NOT_REACHED();
171         return 0;
172 #endif
173     }
174 }
175     
176 static bool shouldLeakBoost(const ProcessLauncher::LaunchOptions& launchOptions)
177 {
178 #if PLATFORM(IOS)
179     // On iOS, leak a boost onto all child processes
180     UNUSED_PARAM(launchOptions);
181     return true;
182 #else
183     // On Mac, leak a boost onto the NetworkProcess.
184     return launchOptions.processType == ProcessLauncher::NetworkProcess;
185 #endif
186 }
187     
188 static void connectToService(const ProcessLauncher::LaunchOptions& launchOptions, bool forDevelopment, ProcessLauncher* that, DidFinishLaunchingProcessFunction didFinishLaunchingProcessFunction, UUIDHolder* instanceUUID)
189 {
190     // Create a connection to the WebKit XPC service.
191     auto connection = adoptOSObject(xpc_connection_create(serviceName(launchOptions), 0));
192     xpc_connection_set_oneshot_instance(connection.get(), instanceUUID->uuid);
193
194     // Inherit UI process localization. It can be different from child process default localization:
195     // 1. When the application and system frameworks simply have different localized resources available, we should match the application.
196     // 1.1. An important case is WebKitTestRunner, where we should use English localizations for all system frameworks.
197     // 2. When AppleLanguages is passed as command line argument for UI process, or set in its preferences, we should respect it in child processes.
198     auto initializationMessage = adoptOSObject(xpc_dictionary_create(nullptr, nullptr, 0));
199     _CFBundleSetupXPCBootstrap(initializationMessage.get());
200 #if PLATFORM(IOS)
201     // Clients that set these environment variables explicitly do not have the values automatically forwarded by libxpc.
202     auto containerEnvironmentVariables = adoptOSObject(xpc_dictionary_create(nullptr, nullptr, 0));
203     if (const char* environmentHOME = getenv("HOME"))
204         xpc_dictionary_set_string(containerEnvironmentVariables.get(), "HOME", environmentHOME);
205     if (const char* environmentCFFIXED_USER_HOME = getenv("CFFIXED_USER_HOME"))
206         xpc_dictionary_set_string(containerEnvironmentVariables.get(), "CFFIXED_USER_HOME", environmentCFFIXED_USER_HOME);
207     if (const char* environmentTMPDIR = getenv("TMPDIR"))
208         xpc_dictionary_set_string(containerEnvironmentVariables.get(), "TMPDIR", environmentTMPDIR);
209     xpc_dictionary_set_value(initializationMessage.get(), "ContainerEnvironmentVariables", containerEnvironmentVariables.get());
210 #endif
211
212     auto languagesIterator = launchOptions.extraInitializationData.find("OverrideLanguages");
213     if (languagesIterator != launchOptions.extraInitializationData.end()) {
214         auto languages = adoptOSObject(xpc_array_create(nullptr, 0));
215         Vector<String> languageVector;
216         languagesIterator->value.split(",", false, languageVector);
217         for (auto& language : languageVector)
218             xpc_array_set_string(languages.get(), XPC_ARRAY_APPEND, language.utf8().data());
219         xpc_dictionary_set_value(initializationMessage.get(), "OverrideLanguages", languages.get());
220     }
221
222     xpc_connection_set_bootstrap(connection.get(), initializationMessage.get());
223
224     // XPC requires having an event handler, even if it is not used.
225     xpc_connection_set_event_handler(connection.get(), ^(xpc_object_t event) { });
226     xpc_connection_resume(connection.get());
227     
228     if (shouldLeakBoost(launchOptions)) {
229         auto preBootstrapMessage = adoptOSObject(xpc_dictionary_create(nullptr, nullptr, 0));
230         xpc_dictionary_set_string(preBootstrapMessage.get(), "message-name", "pre-bootstrap");
231         xpc_connection_send_message(connection.get(), preBootstrapMessage.get());
232     }
233     
234     // Create the listening port.
235     mach_port_t listeningPort;
236     mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &listeningPort);
237     
238     // Insert a send right so we can send to it.
239     mach_port_insert_right(mach_task_self(), listeningPort, listeningPort, MACH_MSG_TYPE_MAKE_SEND);
240
241     NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
242     CString clientIdentifier = bundleIdentifier ? String([[NSBundle mainBundle] bundleIdentifier]).utf8() : *_NSGetProgname();
243
244     // FIXME: Switch to xpc_connection_set_bootstrap once it's available everywhere we need.
245     auto bootstrapMessage = adoptOSObject(xpc_dictionary_create(nullptr, nullptr, 0));
246     xpc_dictionary_set_string(bootstrapMessage.get(), "message-name", "bootstrap");
247
248     xpc_dictionary_set_mach_send(bootstrapMessage.get(), "server-port", listeningPort);
249     mach_port_deallocate(mach_task_self(), listeningPort);
250
251     xpc_dictionary_set_string(bootstrapMessage.get(), "client-identifier", clientIdentifier.data());
252     xpc_dictionary_set_string(bootstrapMessage.get(), "ui-process-name", [[[NSProcessInfo processInfo] processName] UTF8String]);
253
254     if (forDevelopment) {
255         xpc_dictionary_set_fd(bootstrapMessage.get(), "stdout", STDOUT_FILENO);
256         xpc_dictionary_set_fd(bootstrapMessage.get(), "stderr", STDERR_FILENO);
257     }
258
259     auto extraInitializationData = adoptOSObject(xpc_dictionary_create(nullptr, nullptr, 0));
260
261     for (const auto& keyValuePair : launchOptions.extraInitializationData)
262         xpc_dictionary_set_string(extraInitializationData.get(), keyValuePair.key.utf8().data(), keyValuePair.value.utf8().data());
263
264     xpc_dictionary_set_value(bootstrapMessage.get(), "extra-initialization-data", extraInitializationData.get());
265
266     that->ref();
267
268     xpc_connection_send_message_with_reply(connection.get(), bootstrapMessage.get(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(xpc_object_t reply) {
269         xpc_type_t type = xpc_get_type(reply);
270         if (type == XPC_TYPE_ERROR) {
271             // We failed to launch. Release the send right.
272             mach_port_deallocate(mach_task_self(), listeningPort);
273
274             // And the receive right.
275             mach_port_mod_refs(mach_task_self(), listeningPort, MACH_PORT_RIGHT_RECEIVE, -1);
276
277             RefPtr<ProcessLauncher> protector(that);
278             RunLoop::main().dispatch([protector, didFinishLaunchingProcessFunction] {
279                 (*protector.*didFinishLaunchingProcessFunction)(0, IPC::Connection::Identifier());
280             });
281         } else {
282             ASSERT(type == XPC_TYPE_DICTIONARY);
283             ASSERT(!strcmp(xpc_dictionary_get_string(reply, "message-name"), "process-finished-launching"));
284
285             // The process has finished launching, grab the pid from the connection.
286             pid_t processIdentifier = xpc_connection_get_pid(connection.get());
287
288             // We've finished launching the process, message back to the main run loop. This takes ownership of the connection.
289             RefPtr<ProcessLauncher> protector(that);
290             RunLoop::main().dispatch([protector, didFinishLaunchingProcessFunction, processIdentifier, listeningPort, connection] {
291                 (*protector.*didFinishLaunchingProcessFunction)(processIdentifier, IPC::Connection::Identifier(listeningPort, connection));
292             });
293         }
294
295         that->deref();
296     });
297 }
298
299 static void createService(const ProcessLauncher::LaunchOptions& launchOptions, bool forDevelopment, ProcessLauncher* that, DidFinishLaunchingProcessFunction didFinishLaunchingProcessFunction)
300 {
301     // Generate the uuid for the service instance we are about to create.
302     // FIXME: This UUID should be stored on the ChildProcessProxy.
303     RefPtr<UUIDHolder> instanceUUID = UUIDHolder::create();
304     connectToService(launchOptions, forDevelopment, that, didFinishLaunchingProcessFunction, instanceUUID.get());
305 }
306
307 static bool tryPreexistingProcess(const ProcessLauncher::LaunchOptions& launchOptions, ProcessLauncher* that, DidFinishLaunchingProcessFunction didFinishLaunchingProcessFunction)
308 {
309     EnvironmentVariables environmentVariables;
310     static const char* preexistingProcessServiceName = environmentVariables.get(EnvironmentVariables::preexistingProcessServiceNameKey());
311
312     ProcessLauncher::ProcessType preexistingProcessType;
313     if (preexistingProcessServiceName)
314         ProcessLauncher::getProcessTypeFromString(environmentVariables.get(EnvironmentVariables::preexistingProcessTypeKey()), preexistingProcessType);
315
316     bool usePreexistingProcess = preexistingProcessServiceName && preexistingProcessType == launchOptions.processType;
317     if (!usePreexistingProcess)
318         return false;
319
320     // Create the listening port.
321     mach_port_t listeningPort;
322     mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &listeningPort);
323     
324     // Insert a send right so we can send to it.
325     mach_port_insert_right(mach_task_self(), listeningPort, listeningPort, MACH_MSG_TYPE_MAKE_SEND);
326
327     pid_t processIdentifier = 0;
328
329     mach_port_t lookupPort;
330     bootstrap_look_up(bootstrap_port, preexistingProcessServiceName, &lookupPort);
331
332     mach_msg_header_t header;
333     header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND);
334     header.msgh_id = 0;
335     header.msgh_local_port = listeningPort;
336     header.msgh_remote_port = lookupPort;
337     header.msgh_size = sizeof(header);
338     kern_return_t kr = mach_msg(&header, MACH_SEND_MSG, sizeof(header), 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
339
340     mach_port_deallocate(mach_task_self(), lookupPort);
341     preexistingProcessServiceName = 0;
342
343     if (kr) {
344         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));
345         return false;
346     }
347     
348     // We've finished launching the process, message back to the main run loop.
349     RefPtr<ProcessLauncher> protector(that);
350     RunLoop::main().dispatch([protector, didFinishLaunchingProcessFunction, processIdentifier, listeningPort] {
351         (*protector.*didFinishLaunchingProcessFunction)(processIdentifier, IPC::Connection::Identifier(listeningPort));
352     });
353     return true;
354 }
355
356 static void createProcess(const ProcessLauncher::LaunchOptions& launchOptions, bool isWebKitDevelopmentBuild, ProcessLauncher* that, DidFinishLaunchingProcessFunction didFinishLaunchingProcessFunction)
357 {
358     EnvironmentVariables environmentVariables;
359     addDYLDEnvironmentAdditions(launchOptions, isWebKitDevelopmentBuild, environmentVariables);
360
361     // Create the listening port.
362     mach_port_t listeningPort;
363     mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &listeningPort);
364
365     // Insert a send right so we can send to it.
366     mach_port_insert_right(mach_task_self(), listeningPort, listeningPort, MACH_MSG_TYPE_MAKE_SEND);
367
368     NSBundle *webKitBundle = [NSBundle bundleWithIdentifier:@"com.apple.WebKit"];
369
370     NSString *processPath = nil;
371     switch (launchOptions.processType) {
372     case ProcessLauncher::WebProcess:
373         processPath = [webKitBundle pathForAuxiliaryExecutable:@"WebProcess.app"];
374         break;
375 #if ENABLE(NETSCAPE_PLUGIN_API)
376     case ProcessLauncher::PluginProcess:
377         processPath = [webKitBundle pathForAuxiliaryExecutable:@"PluginProcess.app"];
378         break;
379 #endif
380     case ProcessLauncher::NetworkProcess:
381         processPath = [webKitBundle pathForAuxiliaryExecutable:@"NetworkProcess.app"];
382         break;
383 #if ENABLE(DATABASE_PROCESS)
384     case ProcessLauncher::DatabaseProcess:
385         processPath = [webKitBundle pathForAuxiliaryExecutable:@"DatabaseProcess.app"];
386         break;
387 #endif
388     }
389
390     NSString *frameworkExecutablePath = [webKitBundle executablePath];
391     NSString *processAppExecutablePath = [[NSBundle bundleWithPath:processPath] executablePath];
392
393     NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
394     CString clientIdentifier = bundleIdentifier ? String([[NSBundle mainBundle] bundleIdentifier]).utf8() : *_NSGetProgname();
395
396     // Make a unique, per pid, per process launcher web process service name.
397     CString serviceName = String::format("com.apple.WebKit.WebProcess-%d-%p", getpid(), that).utf8();
398
399     // Inherit UI process localization. It can be different from child process default localization:
400     // 1. When the application and system frameworks simply have different localized resources available, we should match the application.
401     // 1.1. An important case is WebKitTestRunner, where we should use English localizations for all system frameworks.
402     // 2. When AppleLanguages is passed as command line argument for UI process, or set in its preferences, we should respect it in child processes.
403     CString appleLanguagesArgument = String("('" + String(preferredBundleLocalizationName()) + "')").utf8();
404
405     Vector<const char*> args;
406     args.append([processAppExecutablePath fileSystemRepresentation]);
407     args.append([frameworkExecutablePath fileSystemRepresentation]);
408     args.append("-type");
409     args.append(ProcessLauncher::processTypeAsString(launchOptions.processType));
410     args.append("-servicename");
411     args.append(serviceName.data());
412     args.append("-client-identifier");
413     args.append(clientIdentifier.data());
414     args.append("-ui-process-name");
415     args.append([[[NSProcessInfo processInfo] processName] UTF8String]);
416     args.append("-AppleLanguages"); // This argument will be handled by Core Foundation.
417     args.append(appleLanguagesArgument.data());
418
419     HashMap<String, String>::const_iterator it = launchOptions.extraInitializationData.begin();
420     HashMap<String, String>::const_iterator end = launchOptions.extraInitializationData.end();
421     Vector<CString> temps;
422     for (; it != end; ++it) {
423         String keyPlusDash = "-" + it->key;
424         CString key(keyPlusDash.utf8().data());
425         temps.append(key);
426         args.append(key.data());
427
428         CString value(it->value.utf8().data());
429         temps.append(value);
430         args.append(value.data());
431     }
432
433     args.append(nullptr);
434
435 #pragma clang diagnostic push
436 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
437     // Register ourselves.
438     kern_return_t kr = bootstrap_register2(bootstrap_port, const_cast<char*>(serviceName.data()), listeningPort, 0);
439     ASSERT_UNUSED(kr, kr == KERN_SUCCESS);
440 #pragma clang diagnostic pop
441
442     posix_spawnattr_t attr;
443     posix_spawnattr_init(&attr);
444
445     short flags = 0;
446
447     // We want our process to receive all signals.
448     sigset_t signalMaskSet;
449     sigemptyset(&signalMaskSet);
450
451     posix_spawnattr_setsigmask(&attr, &signalMaskSet);
452     flags |= POSIX_SPAWN_SETSIGMASK;
453
454     // Determine the architecture to use.
455     cpu_type_t architecture = launchOptions.architecture;
456     if (architecture == ProcessLauncher::LaunchOptions::MatchCurrentArchitecture)
457         architecture = _NSGetMachExecuteHeader()->cputype;
458
459     cpu_type_t cpuTypes[] = { architecture };
460     size_t outCount = 0;
461     posix_spawnattr_setbinpref_np(&attr, 1, cpuTypes, &outCount);
462
463     // Start suspended so we can set up the termination notification handler.
464     flags |= POSIX_SPAWN_START_SUSPENDED;
465
466     static const int allowExecutableHeapFlag = 0x2000;
467     if (launchOptions.executableHeap)
468         flags |= allowExecutableHeapFlag;
469
470     posix_spawnattr_setflags(&attr, flags);
471
472     pid_t processIdentifier = 0;
473     int result = posix_spawn(&processIdentifier, args[0], 0, &attr, const_cast<char**>(args.data()), environmentVariables.environmentPointer());
474
475     posix_spawnattr_destroy(&attr);
476
477     if (!result) {
478         // Set up the termination notification handler and then ask the child process to continue.
479         setUpTerminationNotificationHandler(processIdentifier);
480         kill(processIdentifier, SIGCONT);
481     } else {
482         // We failed to launch. Release the send right.
483         mach_port_deallocate(mach_task_self(), listeningPort);
484
485         // And the receive right.
486         mach_port_mod_refs(mach_task_self(), listeningPort, MACH_PORT_RIGHT_RECEIVE, -1);
487     
488         listeningPort = MACH_PORT_NULL;
489         processIdentifier = 0;
490     }
491
492     // We've finished launching the process, message back to the main run loop.
493     RefPtr<ProcessLauncher> protector(that);
494     RunLoop::main().dispatch([protector, didFinishLaunchingProcessFunction, processIdentifier, listeningPort] {
495         (*protector.*didFinishLaunchingProcessFunction)(processIdentifier, IPC::Connection::Identifier(listeningPort));
496     });
497 }
498
499 static NSString *systemDirectoryPath()
500 {
501     static NSString *path = [^{
502 #if PLATFORM(IOS_SIMULATOR)
503         char *simulatorRoot = getenv("SIMULATOR_ROOT");
504         return simulatorRoot ? [NSString stringWithFormat:@"%s/System/", simulatorRoot] : @"/System/";
505 #else
506         return @"/System/";
507 #endif
508     }() copy];
509
510     return path;
511 }
512
513 void ProcessLauncher::launchProcess()
514 {
515     if (tryPreexistingProcess(m_launchOptions, this, &ProcessLauncher::didFinishLaunchingProcess))
516         return;
517
518     bool isWebKitDevelopmentBuild = ![[[[NSBundle bundleWithIdentifier:@"com.apple.WebKit"] bundlePath] stringByDeletingLastPathComponent] hasPrefix:systemDirectoryPath()];
519
520     if (m_launchOptions.useXPC) {
521         createService(m_launchOptions, isWebKitDevelopmentBuild, this, &ProcessLauncher::didFinishLaunchingProcess);
522         return;
523     }
524
525     createProcess(m_launchOptions, isWebKitDevelopmentBuild, this, &ProcessLauncher::didFinishLaunchingProcess);
526 }
527
528 void ProcessLauncher::terminateProcess()
529 {
530     if (m_isLaunching) {
531         invalidate();
532         return;
533     }
534
535     if (!m_processIdentifier)
536         return;
537     
538     kill(m_processIdentifier, SIGKILL);
539     m_processIdentifier = 0;
540 }
541     
542 void ProcessLauncher::platformInvalidate()
543 {
544 }
545
546 } // namespace WebKit