Remove more unused process launching code
[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 #if ASAN_ENABLED
70 static const char* copyASanDynamicLibraryPath()
71 {
72     uint32_t imageCount = _dyld_image_count();
73     for (uint32_t i = 0; i < imageCount; ++i) {
74         if (strstr(_dyld_get_image_name(i), "/libclang_rt.asan_"))
75             return fastStrDup(_dyld_get_image_name(i));
76     }
77
78     return 0;
79 }
80 #endif
81
82 typedef void (ProcessLauncher::*DidFinishLaunchingProcessFunction)(PlatformProcessIdentifier, IPC::Connection::Identifier);
83
84 static const char* serviceName(const ProcessLauncher::LaunchOptions& launchOptions)
85 {
86     switch (launchOptions.processType) {
87     case ProcessLauncher::WebProcess:
88         return "com.apple.WebKit.WebContent" WK_XPC_SERVICE_SUFFIX;
89     case ProcessLauncher::NetworkProcess:
90         return "com.apple.WebKit.Networking" WK_XPC_SERVICE_SUFFIX;
91 #if ENABLE(DATABASE_PROCESS)
92     case ProcessLauncher::DatabaseProcess:
93         return "com.apple.WebKit.Databases" WK_XPC_SERVICE_SUFFIX;
94 #endif
95 #if ENABLE(NETSCAPE_PLUGIN_API)
96     case ProcessLauncher::PluginProcess:
97         // FIXME: Support plugins that require an executable heap.
98         if (launchOptions.architecture == CPU_TYPE_X86)
99             return "com.apple.WebKit.Plugin.32" WK_XPC_SERVICE_SUFFIX;
100         if (launchOptions.architecture == CPU_TYPE_X86_64)
101             return "com.apple.WebKit.Plugin.64" WK_XPC_SERVICE_SUFFIX;
102
103         ASSERT_NOT_REACHED();
104         return 0;
105 #endif
106     }
107 }
108     
109 static bool shouldLeakBoost(const ProcessLauncher::LaunchOptions& launchOptions)
110 {
111 #if PLATFORM(IOS)
112     // On iOS, leak a boost onto all child processes
113     UNUSED_PARAM(launchOptions);
114     return true;
115 #else
116     // On Mac, leak a boost onto the NetworkProcess.
117     return launchOptions.processType == ProcessLauncher::NetworkProcess;
118 #endif
119 }
120     
121 static void connectToService(const ProcessLauncher::LaunchOptions& launchOptions, bool forDevelopment, ProcessLauncher* that, DidFinishLaunchingProcessFunction didFinishLaunchingProcessFunction, UUIDHolder* instanceUUID)
122 {
123     // Create a connection to the WebKit XPC service.
124     auto connection = adoptOSObject(xpc_connection_create(serviceName(launchOptions), 0));
125     xpc_connection_set_oneshot_instance(connection.get(), instanceUUID->uuid);
126
127     // Inherit UI process localization. It can be different from child process default localization:
128     // 1. When the application and system frameworks simply have different localized resources available, we should match the application.
129     // 1.1. An important case is WebKitTestRunner, where we should use English localizations for all system frameworks.
130     // 2. When AppleLanguages is passed as command line argument for UI process, or set in its preferences, we should respect it in child processes.
131     auto initializationMessage = adoptOSObject(xpc_dictionary_create(nullptr, nullptr, 0));
132     _CFBundleSetupXPCBootstrap(initializationMessage.get());
133 #if PLATFORM(IOS)
134     // Clients that set these environment variables explicitly do not have the values automatically forwarded by libxpc.
135     auto containerEnvironmentVariables = adoptOSObject(xpc_dictionary_create(nullptr, nullptr, 0));
136     if (const char* environmentHOME = getenv("HOME"))
137         xpc_dictionary_set_string(containerEnvironmentVariables.get(), "HOME", environmentHOME);
138     if (const char* environmentCFFIXED_USER_HOME = getenv("CFFIXED_USER_HOME"))
139         xpc_dictionary_set_string(containerEnvironmentVariables.get(), "CFFIXED_USER_HOME", environmentCFFIXED_USER_HOME);
140     if (const char* environmentTMPDIR = getenv("TMPDIR"))
141         xpc_dictionary_set_string(containerEnvironmentVariables.get(), "TMPDIR", environmentTMPDIR);
142     xpc_dictionary_set_value(initializationMessage.get(), "ContainerEnvironmentVariables", containerEnvironmentVariables.get());
143 #endif
144
145     auto languagesIterator = launchOptions.extraInitializationData.find("OverrideLanguages");
146     if (languagesIterator != launchOptions.extraInitializationData.end()) {
147         auto languages = adoptOSObject(xpc_array_create(nullptr, 0));
148         Vector<String> languageVector;
149         languagesIterator->value.split(",", false, languageVector);
150         for (auto& language : languageVector)
151             xpc_array_set_string(languages.get(), XPC_ARRAY_APPEND, language.utf8().data());
152         xpc_dictionary_set_value(initializationMessage.get(), "OverrideLanguages", languages.get());
153     }
154
155     xpc_connection_set_bootstrap(connection.get(), initializationMessage.get());
156
157     // XPC requires having an event handler, even if it is not used.
158     xpc_connection_set_event_handler(connection.get(), ^(xpc_object_t event) { });
159     xpc_connection_resume(connection.get());
160     
161     if (shouldLeakBoost(launchOptions)) {
162         auto preBootstrapMessage = adoptOSObject(xpc_dictionary_create(nullptr, nullptr, 0));
163         xpc_dictionary_set_string(preBootstrapMessage.get(), "message-name", "pre-bootstrap");
164         xpc_connection_send_message(connection.get(), preBootstrapMessage.get());
165     }
166     
167     // Create the listening port.
168     mach_port_t listeningPort;
169     mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &listeningPort);
170     
171     // Insert a send right so we can send to it.
172     mach_port_insert_right(mach_task_self(), listeningPort, listeningPort, MACH_MSG_TYPE_MAKE_SEND);
173
174     NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
175     CString clientIdentifier = bundleIdentifier ? String([[NSBundle mainBundle] bundleIdentifier]).utf8() : *_NSGetProgname();
176
177     // FIXME: Switch to xpc_connection_set_bootstrap once it's available everywhere we need.
178     auto bootstrapMessage = adoptOSObject(xpc_dictionary_create(nullptr, nullptr, 0));
179     xpc_dictionary_set_string(bootstrapMessage.get(), "message-name", "bootstrap");
180
181     xpc_dictionary_set_mach_send(bootstrapMessage.get(), "server-port", listeningPort);
182     mach_port_deallocate(mach_task_self(), listeningPort);
183
184     xpc_dictionary_set_string(bootstrapMessage.get(), "client-identifier", clientIdentifier.data());
185     xpc_dictionary_set_string(bootstrapMessage.get(), "ui-process-name", [[[NSProcessInfo processInfo] processName] UTF8String]);
186
187     if (forDevelopment) {
188         xpc_dictionary_set_fd(bootstrapMessage.get(), "stdout", STDOUT_FILENO);
189         xpc_dictionary_set_fd(bootstrapMessage.get(), "stderr", STDERR_FILENO);
190     }
191
192     auto extraInitializationData = adoptOSObject(xpc_dictionary_create(nullptr, nullptr, 0));
193
194     for (const auto& keyValuePair : launchOptions.extraInitializationData)
195         xpc_dictionary_set_string(extraInitializationData.get(), keyValuePair.key.utf8().data(), keyValuePair.value.utf8().data());
196
197     xpc_dictionary_set_value(bootstrapMessage.get(), "extra-initialization-data", extraInitializationData.get());
198
199     that->ref();
200
201     xpc_connection_send_message_with_reply(connection.get(), bootstrapMessage.get(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(xpc_object_t reply) {
202         xpc_type_t type = xpc_get_type(reply);
203         if (type == XPC_TYPE_ERROR) {
204             // We failed to launch. Release the send right.
205             mach_port_deallocate(mach_task_self(), listeningPort);
206
207             // And the receive right.
208             mach_port_mod_refs(mach_task_self(), listeningPort, MACH_PORT_RIGHT_RECEIVE, -1);
209
210             RefPtr<ProcessLauncher> protector(that);
211             RunLoop::main().dispatch([protector, didFinishLaunchingProcessFunction] {
212                 (*protector.*didFinishLaunchingProcessFunction)(0, IPC::Connection::Identifier());
213             });
214         } else {
215             ASSERT(type == XPC_TYPE_DICTIONARY);
216             ASSERT(!strcmp(xpc_dictionary_get_string(reply, "message-name"), "process-finished-launching"));
217
218             // The process has finished launching, grab the pid from the connection.
219             pid_t processIdentifier = xpc_connection_get_pid(connection.get());
220
221             // We've finished launching the process, message back to the main run loop. This takes ownership of the connection.
222             RefPtr<ProcessLauncher> protector(that);
223             RunLoop::main().dispatch([protector, didFinishLaunchingProcessFunction, processIdentifier, listeningPort, connection] {
224                 (*protector.*didFinishLaunchingProcessFunction)(processIdentifier, IPC::Connection::Identifier(listeningPort, connection));
225             });
226         }
227
228         that->deref();
229     });
230 }
231
232 static void createService(const ProcessLauncher::LaunchOptions& launchOptions, bool forDevelopment, ProcessLauncher* that, DidFinishLaunchingProcessFunction didFinishLaunchingProcessFunction)
233 {
234     // Generate the uuid for the service instance we are about to create.
235     // FIXME: This UUID should be stored on the ChildProcessProxy.
236     RefPtr<UUIDHolder> instanceUUID = UUIDHolder::create();
237     connectToService(launchOptions, forDevelopment, that, didFinishLaunchingProcessFunction, instanceUUID.get());
238 }
239
240 static NSString *systemDirectoryPath()
241 {
242     static NSString *path = [^{
243 #if PLATFORM(IOS_SIMULATOR)
244         char *simulatorRoot = getenv("SIMULATOR_ROOT");
245         return simulatorRoot ? [NSString stringWithFormat:@"%s/System/", simulatorRoot] : @"/System/";
246 #else
247         return @"/System/";
248 #endif
249     }() copy];
250
251     return path;
252 }
253
254 void ProcessLauncher::launchProcess()
255 {
256     bool isWebKitDevelopmentBuild = ![[[[NSBundle bundleWithIdentifier:@"com.apple.WebKit"] bundlePath] stringByDeletingLastPathComponent] hasPrefix:systemDirectoryPath()];
257
258     createService(m_launchOptions, isWebKitDevelopmentBuild, this, &ProcessLauncher::didFinishLaunchingProcess);
259 }
260
261 void ProcessLauncher::terminateProcess()
262 {
263     if (m_isLaunching) {
264         invalidate();
265         return;
266     }
267
268     if (!m_processIdentifier)
269         return;
270     
271     kill(m_processIdentifier, SIGKILL);
272     m_processIdentifier = 0;
273 }
274     
275 void ProcessLauncher::platformInvalidate()
276 {
277 }
278
279 } // namespace WebKit