Home | History | Annotate | Download | only in mac
      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         size_t size = m_allocatedStrings.size();
     81         for (size_t i = 0; i < size; ++i)
     82             fastFree(m_allocatedStrings[i]);
     83     }
     84 
     85     void set(const char* name, const char* value)
     86     {
     87         // Check if we need to copy the environment.
     88         if (m_environmentPointer == *_NSGetEnviron())
     89             copyEnvironmentVariables();
     90 
     91         // Allocate a string for the name and value.
     92         const char* nameAndValue = createStringForVariable(name, value);
     93 
     94         for (size_t i = 0; i < m_environmentVariables.size() - 1; ++i) {
     95             if (valueIfVariableHasName(m_environmentVariables[i], name)) {
     96                 // Just replace the environment variable.
     97                 m_environmentVariables[i] = const_cast<char*>(nameAndValue);
     98                 return;
     99             }
    100         }
    101 
    102         // Append the new string.
    103         ASSERT(!m_environmentVariables.last());
    104         m_environmentVariables.last() = const_cast<char*>(nameAndValue);
    105         m_environmentVariables.append(static_cast<char*>(0));
    106 
    107         m_environmentPointer = m_environmentVariables.data();
    108     }
    109 
    110     const char* get(const char* name) const
    111     {
    112         for (size_t i = 0; m_environmentPointer[i]; ++i) {
    113             if (const 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         const 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     const char* valueIfVariableHasName(const char* environmentVariable, const char* name) const
    140     {
    141         // Find the environment variable name.
    142         const char* equalsLocation = strchr(environmentVariable, '=');
    143         ASSERT(equalsLocation);
    144 
    145         size_t nameLength = equalsLocation - environmentVariable;
    146         if (strlen(name) != nameLength)
    147             return 0;
    148         if (memcmp(environmentVariable, name, nameLength))
    149             return 0;
    150 
    151         return equalsLocation + 1;
    152     }
    153 
    154     const char* createStringForVariable(const char* name, const char* value)
    155     {
    156         int nameLength = strlen(name);
    157         int valueLength = strlen(value);
    158 
    159         // Allocate enough room to hold 'name=value' and the null character.
    160         char* string = static_cast<char*>(fastMalloc(nameLength + 1 + valueLength + 1));
    161         memcpy(string, name, nameLength);
    162         string[nameLength] = '=';
    163         memcpy(string + nameLength + 1, value, valueLength);
    164         string[nameLength + 1 + valueLength] = '\0';
    165 
    166         m_allocatedStrings.append(string);
    167 
    168         return string;
    169     }
    170 
    171     void copyEnvironmentVariables()
    172     {
    173         for (size_t i = 0; (*_NSGetEnviron())[i]; i++)
    174             m_environmentVariables.append((*_NSGetEnviron())[i]);
    175 
    176         // Null-terminate the array.
    177         m_environmentVariables.append(static_cast<char*>(0));
    178 
    179         // Update the environment pointer.
    180         m_environmentPointer = m_environmentVariables.data();
    181     }
    182 
    183     char** m_environmentPointer;
    184     Vector<char*> m_environmentVariables;
    185 
    186     // These allocated strings will be freed in the destructor.
    187     Vector<char*> m_allocatedStrings;
    188 };
    189 
    190 void ProcessLauncher::launchProcess()
    191 {
    192     // Create the listening port.
    193     mach_port_t listeningPort;
    194     mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &listeningPort);
    195 
    196     // Insert a send right so we can send to it.
    197     mach_port_insert_right(mach_task_self(), listeningPort, listeningPort, MACH_MSG_TYPE_MAKE_SEND);
    198 
    199     NSBundle *webKit2Bundle = [NSBundle bundleWithIdentifier:@"com.apple.WebKit2"];
    200     NSString *frameworksPath = [[webKit2Bundle bundlePath] stringByDeletingLastPathComponent];
    201     const char* frameworkExecutablePath = [[webKit2Bundle executablePath] fileSystemRepresentation];
    202 
    203     NSString *processPath;
    204     if (m_launchOptions.processType == ProcessLauncher::PluginProcess)
    205         processPath = [webKit2Bundle pathForAuxiliaryExecutable:@"PluginProcess.app"];
    206     else
    207         processPath = [webKit2Bundle pathForAuxiliaryExecutable:@"WebProcess.app"];
    208 
    209     NSString *processAppExecutablePath = [[NSBundle bundleWithPath:processPath] executablePath];
    210 
    211     RetainPtr<CFStringRef> cfLocalization(AdoptCF, WKCopyCFLocalizationPreferredName(NULL));
    212     CString localization = String(cfLocalization.get()).utf8();
    213 
    214     // Make a unique, per pid, per process launcher web process service name.
    215     CString serviceName = String::format("com.apple.WebKit.WebProcess-%d-%p", getpid(), this).utf8();
    216 
    217     const char* args[] = { [processAppExecutablePath fileSystemRepresentation], frameworkExecutablePath, "-type", processTypeAsString(m_launchOptions.processType), "-servicename", serviceName.data(), "-localization", localization.data(), 0 };
    218 
    219     // Register ourselves.
    220     kern_return_t kr = bootstrap_register2(bootstrap_port, const_cast<char*>(serviceName.data()), listeningPort, 0);
    221     ASSERT_UNUSED(kr, kr == KERN_SUCCESS);
    222 
    223     posix_spawnattr_t attr;
    224     posix_spawnattr_init(&attr);
    225 
    226     short flags = 0;
    227 
    228     // We want our process to receive all signals.
    229     sigset_t signalMaskSet;
    230     sigemptyset(&signalMaskSet);
    231 
    232     posix_spawnattr_setsigmask(&attr, &signalMaskSet);
    233     flags |= POSIX_SPAWN_SETSIGMASK;
    234 
    235     // Determine the architecture to use.
    236     cpu_type_t architecture = m_launchOptions.architecture;
    237     if (architecture == LaunchOptions::MatchCurrentArchitecture)
    238         architecture = _NSGetMachExecuteHeader()->cputype;
    239 
    240     cpu_type_t cpuTypes[] = { architecture };
    241     size_t outCount = 0;
    242     posix_spawnattr_setbinpref_np(&attr, 1, cpuTypes, &outCount);
    243 
    244     // Start suspended so we can set up the termination notification handler.
    245     flags |= POSIX_SPAWN_START_SUSPENDED;
    246 
    247 #ifndef BUILDING_ON_SNOW_LEOPARD
    248     static const int allowExecutableHeapFlag = 0x2000;
    249     if (m_launchOptions.executableHeap)
    250         flags |= allowExecutableHeapFlag;
    251 #endif
    252 
    253     posix_spawnattr_setflags(&attr, flags);
    254 
    255     pid_t processIdentifier;
    256 
    257     EnvironmentVariables environmentVariables;
    258 
    259     // To make engineering builds work, if the path is outside of /System set up
    260     // DYLD_FRAMEWORK_PATH to pick up other frameworks, but don't do it for the
    261     // production configuration because it involves extra file system access.
    262     if (![frameworksPath hasPrefix:@"/System/"])
    263         environmentVariables.appendValue("DYLD_FRAMEWORK_PATH", [frameworksPath fileSystemRepresentation], ':');
    264 
    265     if (m_launchOptions.processType == ProcessLauncher::PluginProcess) {
    266         // We need to insert the plug-in process shim.
    267         NSString *pluginProcessShimPathNSString = [[processAppExecutablePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"PluginProcessShim.dylib"];
    268         const char* pluginProcessShimPath = [pluginProcessShimPathNSString fileSystemRepresentation];
    269 
    270         // Make sure that the file exists.
    271         struct stat statBuf;
    272         if (stat(pluginProcessShimPath, &statBuf) == 0 && (statBuf.st_mode & S_IFMT) == S_IFREG)
    273             environmentVariables.appendValue("DYLD_INSERT_LIBRARIES", pluginProcessShimPath, ':');
    274     }
    275 
    276     int result = posix_spawn(&processIdentifier, args[0], 0, &attr, const_cast<char**>(args), environmentVariables.environmentPointer());
    277 
    278     posix_spawnattr_destroy(&attr);
    279 
    280     if (!result) {
    281         // Set up the termination notification handler and then ask the child process to continue.
    282         setUpTerminationNotificationHandler(processIdentifier);
    283         kill(processIdentifier, SIGCONT);
    284     } else {
    285         // We failed to launch. Release the send right.
    286         mach_port_deallocate(mach_task_self(), listeningPort);
    287 
    288         // And the receive right.
    289         mach_port_mod_refs(mach_task_self(), listeningPort, MACH_PORT_RIGHT_RECEIVE, -1);
    290 
    291         listeningPort = MACH_PORT_NULL;
    292         processIdentifier = 0;
    293     }
    294 
    295     // We've finished launching the process, message back to the main run loop.
    296     RunLoop::main()->scheduleWork(WorkItem::create(this, &ProcessLauncher::didFinishLaunchingProcess, processIdentifier, listeningPort));
    297 }
    298 
    299 void ProcessLauncher::terminateProcess()
    300 {
    301     if (!m_processIdentifier)
    302         return;
    303 
    304     kill(m_processIdentifier, SIGKILL);
    305 }
    306 
    307 void ProcessLauncher::platformInvalidate()
    308 {
    309 }
    310 
    311 } // namespace WebKit
    312