Fixed an off-by-(sizeof(char*) - 1) bug.
[WebKit-https.git] / Source / WebKit2 / Shared / EntryPointUtilities / mac / XPCService / XPCServiceMain.Development.mm
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
34 namespace WebKit {
35
36 struct ReexecInfo {
37     bool executableHeap;
38     char** environment;
39     cpu_type_t cpuType;
40 };
41
42 static 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
85 static NO_RETURN void reexecCallBack(CFRunLoopTimerRef timer, void *info)
86 {
87     reexec(static_cast<ReexecInfo *>(info));
88 }
89
90 static 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 + 1) * sizeof(char*)));
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
162 using namespace WebKit;
163
164 int main(int argc, char** argv)
165 {
166     xpc_main(XPCServiceEventHandler);
167     return 0;
168 }