1/* 2 * Copyright (C) 2013 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 <crt_externs.h> 27#import <dlfcn.h> 28#import <mach-o/dyld.h> 29#import <spawn.h> 30#import <stdio.h> 31#import <stdlib.h> 32#import <xpc/xpc.h> 33 34namespace WebKit { 35 36struct ReexecInfo { 37 bool executableHeap; 38 char** environment; 39 cpu_type_t cpuType; 40}; 41 42static NO_RETURN void reexec(ReexecInfo *info) 43{ 44 posix_spawnattr_t attr; 45 posix_spawnattr_init(&attr); 46 47 short flags = 0; 48 49 // We just want to set the process state, not actually launch a new process, 50 // so we are going to use the darwin extension to posix_spawn POSIX_SPAWN_SETEXEC 51 // to act like a more full featured exec. 52 flags |= POSIX_SPAWN_SETEXEC; 53 54 sigset_t signalMaskSet; 55 sigemptyset(&signalMaskSet); 56 posix_spawnattr_setsigmask(&attr, &signalMaskSet); 57 flags |= POSIX_SPAWN_SETSIGMASK; 58 59 static const int allowExecutableHeapFlag = 0x2000; 60 if (info->executableHeap) 61 flags |= allowExecutableHeapFlag; 62 63 posix_spawnattr_setflags(&attr, flags); 64 65 size_t outCount = 0; 66 posix_spawnattr_setbinpref_np(&attr, 1, &info->cpuType, &outCount); 67 68 char path[4 * PATH_MAX]; 69 uint32_t pathLength = sizeof(path); 70 _NSGetExecutablePath(path, &pathLength); 71 72 char** argv = *_NSGetArgv(); 73 const char* programName = argv[0]; 74 const char* args[] = { programName, 0 }; 75 76 pid_t processIdentifier = 0; 77 posix_spawn(&processIdentifier, path, 0, &attr, const_cast<char**>(args), info->environment); 78 79 posix_spawnattr_destroy(&attr); 80 81 NSLog(@"Unable to re-exec for path: %s", path); 82 exit(EXIT_FAILURE); 83} 84 85static NO_RETURN void reexecCallBack(CFRunLoopTimerRef timer, void *info) 86{ 87 reexec(static_cast<ReexecInfo *>(info)); 88} 89 90static void XPCServiceEventHandler(xpc_connection_t peer) 91{ 92 xpc_connection_set_target_queue(peer, dispatch_get_main_queue()); 93 xpc_connection_set_event_handler(peer, ^(xpc_object_t event) { 94 xpc_type_t type = xpc_get_type(event); 95 if (type == XPC_TYPE_ERROR) { 96 if (event == XPC_ERROR_CONNECTION_INVALID || event == XPC_ERROR_TERMINATION_IMMINENT) { 97 // FIXME: Handle this case more gracefully. 98 exit(EXIT_FAILURE); 99 } 100 } else { 101 assert(type == XPC_TYPE_DICTIONARY); 102 103 if (!strcmp(xpc_dictionary_get_string(event, "message-name"), "re-exec")) { 104 ReexecInfo *info = static_cast<ReexecInfo *>(malloc(sizeof(ReexecInfo))); 105 106 info->executableHeap = xpc_dictionary_get_bool(event, "executable-heap"); 107 info->cpuType = (cpu_type_t)xpc_dictionary_get_uint64(event, "architecture"); 108 109 xpc_object_t environmentArray = xpc_dictionary_get_value(event, "environment"); 110 size_t numberOfEnvironmentVariables = xpc_array_get_count(environmentArray); 111 char** environment = static_cast<char **>(malloc(numberOfEnvironmentVariables * sizeof(char*) + 1)); 112 for (size_t i = 0; i < numberOfEnvironmentVariables; ++i) 113 environment[i] = strdup(xpc_array_get_string(environmentArray, i)); 114 environment[numberOfEnvironmentVariables] = 0; 115 info->environment = environment; 116 117 CFRunLoopTimerContext context = { 0, info, NULL, NULL, NULL }; 118 CFRunLoopTimerRef timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 0, 0, 0, reexecCallBack, &context); 119 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes); 120 } 121 122 if (!strcmp(xpc_dictionary_get_string(event, "message-name"), "bootstrap")) { 123 static void* frameworkLibrary = dlopen(xpc_dictionary_get_string(event, "framework-executable-path"), RTLD_NOW); 124 if (!frameworkLibrary) { 125 NSLog(@"Unable to load WebKit2.framework at path: %s (Error: %s)", xpc_dictionary_get_string(event, "framework-executable-path"), dlerror()); 126 exit(EXIT_FAILURE); 127 } 128 129 CFBundleRef webKit2Bundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.WebKit2")); 130 CFStringRef entryPointFunctionName = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), CFSTR("WebKitEntryPoint")); 131 132 typedef void (*InitializerFunction)(xpc_connection_t, xpc_object_t); 133 InitializerFunction initializerFunctionPtr = reinterpret_cast<InitializerFunction>(CFBundleGetFunctionPointerForName(webKit2Bundle, entryPointFunctionName)); 134 if (!initializerFunctionPtr) { 135 NSLog(@"Unable to find entry point in WebKit2.framework with name: %@", (NSString *)entryPointFunctionName); 136 exit(EXIT_FAILURE); 137 } 138 139 xpc_object_t reply = xpc_dictionary_create_reply(event); 140 xpc_dictionary_set_string(reply, "message-name", "process-finished-launching"); 141 xpc_connection_send_message(xpc_dictionary_get_remote_connection(event), reply); 142 xpc_release(reply); 143 144 dup2(xpc_dictionary_dup_fd(event, "stdout"), STDOUT_FILENO); 145 dup2(xpc_dictionary_dup_fd(event, "stderr"), STDERR_FILENO); 146 147 initializerFunctionPtr(peer, event); 148 } 149 150 if (!strcmp(xpc_dictionary_get_string(event, "message-name"), "pre-bootstrap")) { 151 // Hold on to the pre-bootstrap message. 152 xpc_retain(event); 153 } 154 } 155 }); 156 157 xpc_connection_resume(peer); 158} 159 160} // namespace WebKit; 161 162using namespace WebKit; 163 164int main(int argc, char** argv) 165{ 166 xpc_main(XPCServiceEventHandler); 167 return 0; 168} 169