1 | /* |
2 | * Copyright (C) 2010 Apple Inc. All rights reserved. |
3 | * Portions Copyright (c) 2010 Motorola Mobility, Inc. All rights reserved. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions |
7 | * are met: |
8 | * 1. Redistributions of source code must retain the above copyright |
9 | * notice, this list of conditions and the following disclaimer. |
10 | * 2. Redistributions in binary form must reproduce the above copyright |
11 | * notice, this list of conditions and the following disclaimer in the |
12 | * documentation and/or other materials provided with the distribution. |
13 | * |
14 | * THIS SOFTWARE IS PROVIDED BY MOTOROLA INC. AND ITS CONTRIBUTORS ``AS IS'' |
15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
16 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
17 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA INC. OR ITS CONTRIBUTORS |
18 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
24 | * THE POSSIBILITY OF SUCH DAMAGE. |
25 | */ |
26 | |
27 | #include "config.h" |
28 | #include "ProcessLauncher.h" |
29 | |
30 | #include "BubblewrapLauncher.h" |
31 | #include "Connection.h" |
32 | #include "FlatpakLauncher.h" |
33 | #include "ProcessExecutablePath.h" |
34 | #include <errno.h> |
35 | #include <fcntl.h> |
36 | #include <glib.h> |
37 | #include <wtf/FileSystem.h> |
38 | #include <wtf/RunLoop.h> |
39 | #include <wtf/UniStdExtras.h> |
40 | #include <wtf/glib/GLibUtilities.h> |
41 | #include <wtf/glib/GUniquePtr.h> |
42 | #include <wtf/text/CString.h> |
43 | #include <wtf/text/WTFString.h> |
44 | |
45 | namespace WebKit { |
46 | |
47 | static void childSetupFunction(gpointer userData) |
48 | { |
49 | int socket = GPOINTER_TO_INT(userData); |
50 | close(socket); |
51 | } |
52 | |
53 | #if OS(LINUX) |
54 | static bool isInsideFlatpak() |
55 | { |
56 | static int ret = -1; |
57 | if (ret != -1) |
58 | return ret; |
59 | |
60 | GUniquePtr<GKeyFile> infoFile(g_key_file_new()); |
61 | if (!g_key_file_load_from_file(infoFile.get(), "/.flatpak-info" , G_KEY_FILE_NONE, nullptr)) { |
62 | ret = false; |
63 | return ret; |
64 | } |
65 | |
66 | // If we are in a `flatpak build` session we cannot launch ourselves since we aren't installed. |
67 | ret = !g_key_file_get_boolean(infoFile.get(), "Instance" , "build" , nullptr); |
68 | return ret; |
69 | } |
70 | #endif |
71 | |
72 | void ProcessLauncher::launchProcess() |
73 | { |
74 | IPC::Connection::SocketPair socketPair = IPC::Connection::createPlatformConnection(IPC::Connection::ConnectionOptions::SetCloexecOnServer); |
75 | |
76 | String executablePath; |
77 | CString realExecutablePath; |
78 | #if ENABLE(NETSCAPE_PLUGIN_API) |
79 | String pluginPath; |
80 | CString realPluginPath; |
81 | #endif |
82 | switch (m_launchOptions.processType) { |
83 | case ProcessLauncher::ProcessType::Web: |
84 | executablePath = executablePathOfWebProcess(); |
85 | break; |
86 | #if ENABLE(NETSCAPE_PLUGIN_API) |
87 | case ProcessLauncher::ProcessType::Plugin64: |
88 | case ProcessLauncher::ProcessType::Plugin32: |
89 | executablePath = executablePathOfPluginProcess(); |
90 | #if ENABLE(PLUGIN_PROCESS_GTK2) |
91 | if (m_launchOptions.extraInitializationData.contains("requires-gtk2" )) |
92 | executablePath.append('2'); |
93 | #endif |
94 | pluginPath = m_launchOptions.extraInitializationData.get("plugin-path" ); |
95 | realPluginPath = FileSystem::fileSystemRepresentation(pluginPath); |
96 | break; |
97 | #endif |
98 | case ProcessLauncher::ProcessType::Network: |
99 | executablePath = executablePathOfNetworkProcess(); |
100 | break; |
101 | default: |
102 | ASSERT_NOT_REACHED(); |
103 | return; |
104 | } |
105 | |
106 | realExecutablePath = FileSystem::fileSystemRepresentation(executablePath); |
107 | GUniquePtr<gchar> processIdentifier(g_strdup_printf("%" PRIu64, m_launchOptions.processIdentifier.toUInt64())); |
108 | GUniquePtr<gchar> webkitSocket(g_strdup_printf("%d" , socketPair.client)); |
109 | unsigned nargs = 5; // size of the argv array for g_spawn_async() |
110 | |
111 | #if ENABLE(DEVELOPER_MODE) |
112 | Vector<CString> prefixArgs; |
113 | if (!m_launchOptions.processCmdPrefix.isNull()) { |
114 | for (auto& arg : m_launchOptions.processCmdPrefix.split(' ')) |
115 | prefixArgs.append(arg.utf8()); |
116 | nargs += prefixArgs.size(); |
117 | } |
118 | #endif |
119 | |
120 | char** argv = g_newa(char*, nargs); |
121 | unsigned i = 0; |
122 | #if ENABLE(DEVELOPER_MODE) |
123 | // If there's a prefix command, put it before the rest of the args. |
124 | for (auto& arg : prefixArgs) |
125 | argv[i++] = const_cast<char*>(arg.data()); |
126 | #endif |
127 | argv[i++] = const_cast<char*>(realExecutablePath.data()); |
128 | argv[i++] = processIdentifier.get(); |
129 | argv[i++] = webkitSocket.get(); |
130 | #if ENABLE(NETSCAPE_PLUGIN_API) |
131 | argv[i++] = const_cast<char*>(realPluginPath.data()); |
132 | #else |
133 | argv[i++] = nullptr; |
134 | #endif |
135 | argv[i++] = nullptr; |
136 | |
137 | GRefPtr<GSubprocessLauncher> launcher = adoptGRef(g_subprocess_launcher_new(G_SUBPROCESS_FLAGS_INHERIT_FDS)); |
138 | g_subprocess_launcher_set_child_setup(launcher.get(), childSetupFunction, GINT_TO_POINTER(socketPair.server), nullptr); |
139 | g_subprocess_launcher_take_fd(launcher.get(), socketPair.client, socketPair.client); |
140 | |
141 | GUniqueOutPtr<GError> error; |
142 | GRefPtr<GSubprocess> process; |
143 | #if OS(LINUX) |
144 | const char* sandboxEnv = g_getenv("WEBKIT_FORCE_SANDBOX" ); |
145 | bool sandboxEnabled = m_launchOptions.extraInitializationData.get("enable-sandbox" ) == "true" ; |
146 | |
147 | if (sandboxEnv) |
148 | sandboxEnabled = !strcmp(sandboxEnv, "1" ); |
149 | |
150 | if (sandboxEnabled && isInsideFlatpak()) |
151 | process = flatpakSpawn(launcher.get(), m_launchOptions, argv, socketPair.client, &error.outPtr()); |
152 | #if ENABLE(BUBBLEWRAP_SANDBOX) |
153 | else if (sandboxEnabled) |
154 | process = bubblewrapSpawn(launcher.get(), m_launchOptions, argv, &error.outPtr()); |
155 | #endif |
156 | else |
157 | #endif |
158 | process = adoptGRef(g_subprocess_launcher_spawnv(launcher.get(), argv, &error.outPtr())); |
159 | |
160 | if (!process.get()) |
161 | g_error("Unable to fork a new child process: %s" , error->message); |
162 | |
163 | const char* processIdStr = g_subprocess_get_identifier(process.get()); |
164 | m_processIdentifier = g_ascii_strtoll(processIdStr, nullptr, 0); |
165 | RELEASE_ASSERT(m_processIdentifier); |
166 | |
167 | // Don't expose the parent socket to potential future children. |
168 | if (!setCloseOnExec(socketPair.client)) |
169 | RELEASE_ASSERT_NOT_REACHED(); |
170 | |
171 | // We've finished launching the process, message back to the main run loop. |
172 | RunLoop::main().dispatch([protectedThis = makeRef(*this), this, serverSocket = socketPair.server] { |
173 | didFinishLaunchingProcess(m_processIdentifier, serverSocket); |
174 | }); |
175 | } |
176 | |
177 | void ProcessLauncher::terminateProcess() |
178 | { |
179 | if (m_isLaunching) { |
180 | invalidate(); |
181 | return; |
182 | } |
183 | |
184 | if (!m_processIdentifier) |
185 | return; |
186 | |
187 | kill(m_processIdentifier, SIGKILL); |
188 | m_processIdentifier = 0; |
189 | } |
190 | |
191 | void ProcessLauncher::platformInvalidate() |
192 | { |
193 | } |
194 | |
195 | } // namespace WebKit |
196 | |