2011-02-13 Anders Carlsson <andersca@apple.com>
[WebKit-https.git] / Source / WebKit2 / UIProcess / Launcher / mac / ProcessLauncherMac.mm
1 /*
2  * Copyright (C) 2010 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 "RunLoop.h"
30 #import "WebProcess.h"
31 #import "WebKitSystemInterface.h"
32 #import <crt_externs.h>
33 #import <mach-o/dyld.h>
34 #import <mach/machine.h>
35 #import <runtime/InitializeThreading.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/Threading.h>
43 #import <wtf/text/CString.h>
44 #import <wtf/text/WTFString.h>
45
46 using namespace WebCore;
47
48 // FIXME: We should be doing this another way.
49 extern "C" kern_return_t bootstrap_register2(mach_port_t, name_t, mach_port_t, uint64_t);
50
51 namespace WebKit {
52
53 static void setUpTerminationNotificationHandler(pid_t pid)
54 {
55 #if HAVE(DISPATCH_H)
56     dispatch_source_t processDiedSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, DISPATCH_PROC_EXIT, dispatch_get_current_queue());
57     dispatch_source_set_event_handler(processDiedSource, ^{
58         int status;
59         waitpid(dispatch_source_get_handle(processDiedSource), &status, 0);
60         dispatch_source_cancel(processDiedSource);
61     });
62     dispatch_source_set_cancel_handler(processDiedSource, ^{
63         dispatch_release(processDiedSource);
64     });
65     dispatch_resume(processDiedSource);
66 #endif
67 }
68
69 class EnvironmentVariables {
70     WTF_MAKE_NONCOPYABLE(EnvironmentVariables);
71
72 public:
73     EnvironmentVariables()
74         : m_environmentPointer(*_NSGetEnviron())
75     {
76     }
77
78     ~EnvironmentVariables()
79     {
80         deleteAllValues(m_allocatedStrings);
81     }
82
83     void set(const char* name, const char* value)
84     {
85         // Check if we need to copy the environment.
86         if (m_environmentPointer == *_NSGetEnviron())
87             copyEnvironmentVariables();
88
89         // Allocate a string for the name and value.
90         char* nameAndValue = createStringForVariable(name, value);
91
92         for (size_t i = 0; i < m_environmentVariables.size() - 1; ++i) {
93             char* environmentVariable = m_environmentVariables[i];
94
95             if (valueIfVariableHasName(environmentVariable, name)) {
96                 // Just replace the environment variable.
97                 m_environmentVariables[i] = nameAndValue;
98                 return;
99             }
100         }
101
102         // Append the new string.
103         ASSERT(!m_environmentVariables.last());
104         m_environmentVariables.last() = nameAndValue;
105         m_environmentVariables.append(static_cast<char*>(0));
106
107         m_environmentPointer = m_environmentVariables.data();
108     }
109
110     char* get(const char* name) const
111     {
112         for (size_t i = 0; m_environmentPointer[i]; ++i) {
113             if (char* value = valueIfVariableHasName(m_environmentPointer[i], name))
114                 return value;
115         }
116         return 0;
117     }
118
119     // Will append the value with the given separator if the environment variable already exists.
120     void appendValue(const char* name, const char* value, char separator)
121     {
122         char* existingValue = get(name);
123         if (!existingValue) {
124             set(name, value);
125             return;
126         }
127
128         Vector<char, 128> newValue;
129         newValue.append(existingValue, strlen(existingValue));
130         newValue.append(separator);
131         newValue.append(value, strlen(value) + 1);
132
133         set(name, newValue.data());
134     }
135
136     char** environmentPointer() const { return m_environmentPointer; }
137
138 private:
139     char *valueIfVariableHasName(const char* environmentVariable, const char* name) const
140     {
141         // Find the environment variable name.
142         char* equalsLocation = strchr(environmentVariable, '=');
143         ASSERT(equalsLocation);
144
145         size_t nameLength = equalsLocation - environmentVariable;
146         if (strncmp(environmentVariable, name, nameLength))
147             return 0;
148
149         return equalsLocation + 1;
150     }
151
152     char* createStringForVariable(const char* name, const char* value)
153     {
154         int nameLength = strlen(name);
155         int valueLength = strlen(value);
156
157         // Allocate enough room to hold 'name=value' and the null character.
158         char* string = static_cast<char*>(fastMalloc(nameLength + 1 + valueLength + 1));
159         memcpy(string, name, nameLength);
160         string[nameLength] = '=';
161         memcpy(string + nameLength + 1, value, valueLength);
162         string[nameLength + 1 + valueLength] = '\0';
163
164         m_allocatedStrings.append(string);
165
166         return string;
167     }
168
169     void copyEnvironmentVariables()
170     {
171         for (size_t i = 0; (*_NSGetEnviron())[i]; i++)
172             m_environmentVariables.append((*_NSGetEnviron())[i]);
173
174         // Null-terminate the array.
175         m_environmentVariables.append(static_cast<char*>(0));
176     }
177
178     char** m_environmentPointer;
179     Vector<char*> m_environmentVariables;
180
181     // These allocated strings will be freed in the destructor.
182     Vector<char*> m_allocatedStrings;
183 };
184
185 void ProcessLauncher::launchProcess()
186 {
187     // Create the listening port.
188     mach_port_t listeningPort;
189     mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &listeningPort);
190     
191     // Insert a send right so we can send to it.
192     mach_port_insert_right(mach_task_self(), listeningPort, listeningPort, MACH_MSG_TYPE_MAKE_SEND);
193
194     NSBundle *webKit2Bundle = [NSBundle bundleWithIdentifier:@"com.apple.WebKit2"];
195     const char* bundlePath = [[webKit2Bundle executablePath] fileSystemRepresentation];
196
197     NSString *webProcessAppPath = [webKit2Bundle pathForAuxiliaryExecutable:@"WebProcess.app"];
198     NSString *webProcessAppExecutablePath = [[NSBundle bundleWithPath:webProcessAppPath] executablePath];
199
200     // Make a unique, per pid, per process launcher web process service name.
201     CString serviceName = String::format("com.apple.WebKit.WebProcess-%d-%p", getpid(), this).utf8();
202
203     const char* path = [webProcessAppExecutablePath fileSystemRepresentation];
204     const char* args[] = { path, bundlePath, "-type", processTypeAsString(m_launchOptions.processType), "-servicename", serviceName.data(), 0 };
205
206     // Register ourselves.
207     kern_return_t kr = bootstrap_register2(bootstrap_port, const_cast<char*>(serviceName.data()), listeningPort, 0);
208     ASSERT_UNUSED(kr, kr == KERN_SUCCESS);
209
210     posix_spawnattr_t attr;
211     posix_spawnattr_init(&attr);
212
213     short flags = 0;
214
215     // We want our process to receive all signals.
216     sigset_t signalMaskSet;
217     sigemptyset(&signalMaskSet);
218
219     posix_spawnattr_setsigmask(&attr, &signalMaskSet);
220     flags |= POSIX_SPAWN_SETSIGMASK;
221
222     // Determine the architecture to use.
223     cpu_type_t architecture = m_launchOptions.architecture;
224     if (architecture == LaunchOptions::MatchCurrentArchitecture)
225         architecture = _NSGetMachExecuteHeader()->cputype;
226
227     cpu_type_t cpuTypes[] = { architecture };    
228     size_t outCount = 0;
229     posix_spawnattr_setbinpref_np(&attr, 1, cpuTypes, &outCount);
230
231     // Start suspended so we can set up the termination notification handler.
232     flags |= POSIX_SPAWN_START_SUSPENDED;
233
234 #ifndef BUILDING_ON_SNOW_LEOPARD
235     static const int allowExecutableHeapFlag = 0x2000;
236     if (m_launchOptions.executableHeap)
237         flags |= allowExecutableHeapFlag;
238 #endif
239
240     posix_spawnattr_setflags(&attr, flags);
241
242     pid_t processIdentifier;
243
244     EnvironmentVariables environmentVariables;
245
246     if (m_launchOptions.processType == ProcessLauncher::PluginProcess) {
247         // We need to insert the plug-in process shim.
248         NSString *pluginProcessShimPathNSString = [[webProcessAppExecutablePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"PluginProcessShim.dylib"];
249         const char *pluginProcessShimPath = [pluginProcessShimPathNSString fileSystemRepresentation];
250
251         // Make sure that the file exists.
252         struct stat statBuf;
253         if (stat(pluginProcessShimPath, &statBuf) == 0 && (statBuf.st_mode & S_IFMT) == S_IFREG)
254             environmentVariables.appendValue("DYLD_INSERT_LIBRARIES", pluginProcessShimPath, ':');
255     }
256     
257     int result = posix_spawn(&processIdentifier, path, 0, &attr, (char *const*)args, environmentVariables.environmentPointer());
258
259     posix_spawnattr_destroy(&attr);
260
261     if (!result) {
262         // Set up the termination notification handler and then ask the child process to continue.
263         setUpTerminationNotificationHandler(processIdentifier);
264         kill(processIdentifier, SIGCONT);
265     } else {
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         listeningPort = MACH_PORT_NULL;
273         processIdentifier = 0;
274     }
275     
276     // We've finished launching the process, message back to the main run loop.
277     RunLoop::main()->scheduleWork(WorkItem::create(this, &ProcessLauncher::didFinishLaunchingProcess, processIdentifier, listeningPort));
278 }
279
280 void ProcessLauncher::terminateProcess()
281 {    
282     if (!m_processIdentifier)
283         return;
284     
285     kill(m_processIdentifier, SIGKILL);
286 }
287     
288 void ProcessLauncher::platformInvalidate()
289 {
290 }
291
292 } // namespace WebKit