[WTF] Add environment variable helpers
[WebKit-https.git] / Source / WebKit / UIProcess / Launcher / glib / ProcessLauncherGLib.cpp
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/Environment.h>
38 #include <wtf/FileSystem.h>
39 #include <wtf/RunLoop.h>
40 #include <wtf/UniStdExtras.h>
41 #include <wtf/glib/GLibUtilities.h>
42 #include <wtf/glib/GUniquePtr.h>
43 #include <wtf/text/CString.h>
44 #include <wtf/text/WTFString.h>
45
46 #if PLATFORM(WPE)
47 #include <wpe/wpe.h>
48 #endif
49
50 namespace WebKit {
51
52 static void childSetupFunction(gpointer userData)
53 {
54     int socket = GPOINTER_TO_INT(userData);
55     close(socket);
56 }
57
58 #if OS(LINUX)
59 static bool isInsideFlatpak()
60 {
61     static int ret = -1;
62     if (ret != -1)
63         return ret;
64
65     GUniquePtr<GKeyFile> infoFile(g_key_file_new());
66     if (!g_key_file_load_from_file(infoFile.get(), "/.flatpak-info", G_KEY_FILE_NONE, nullptr)) {
67         ret = false;
68         return ret;
69     }
70
71     // If we are in a `flatpak build` session we cannot launch ourselves since we aren't installed.
72     ret = !g_key_file_get_boolean(infoFile.get(), "Instance", "build", nullptr);
73     return ret;
74 }
75 #endif
76
77 void ProcessLauncher::launchProcess()
78 {
79     IPC::Connection::SocketPair socketPair = IPC::Connection::createPlatformConnection(IPC::Connection::ConnectionOptions::SetCloexecOnServer);
80
81     String executablePath;
82     CString realExecutablePath;
83 #if ENABLE(NETSCAPE_PLUGIN_API)
84     String pluginPath;
85     CString realPluginPath;
86 #endif
87     switch (m_launchOptions.processType) {
88     case ProcessLauncher::ProcessType::Web:
89         executablePath = executablePathOfWebProcess();
90         break;
91 #if ENABLE(NETSCAPE_PLUGIN_API)
92     case ProcessLauncher::ProcessType::Plugin64:
93     case ProcessLauncher::ProcessType::Plugin32:
94         executablePath = executablePathOfPluginProcess();
95 #if ENABLE(PLUGIN_PROCESS_GTK2)
96         if (m_launchOptions.extraInitializationData.contains("requires-gtk2"))
97             executablePath.append('2');
98 #endif
99         pluginPath = m_launchOptions.extraInitializationData.get("plugin-path");
100         realPluginPath = FileSystem::fileSystemRepresentation(pluginPath);
101         break;
102 #endif
103     case ProcessLauncher::ProcessType::Network:
104         executablePath = executablePathOfNetworkProcess();
105         break;
106     default:
107         ASSERT_NOT_REACHED();
108         return;
109     }
110
111     realExecutablePath = FileSystem::fileSystemRepresentation(executablePath);
112     GUniquePtr<gchar> processIdentifier(g_strdup_printf("%" PRIu64, m_launchOptions.processIdentifier.toUInt64()));
113     GUniquePtr<gchar> webkitSocket(g_strdup_printf("%d", socketPair.client));
114     unsigned nargs = 5; // size of the argv array for g_spawn_async()
115
116 #if PLATFORM(WPE)
117     GUniquePtr<gchar> wpeSocket;
118     CString wpeBackendLibraryParameter;
119     if (m_launchOptions.processType == ProcessLauncher::ProcessType::Web) {
120 #if defined(WPE_BACKEND_CHECK_VERSION) && WPE_BACKEND_CHECK_VERSION(0, 2, 0)
121         wpeBackendLibraryParameter = FileSystem::fileSystemRepresentation(wpe_loader_get_loaded_implementation_library_name());
122 #endif
123         nargs++;
124
125         wpeSocket = GUniquePtr<gchar>(g_strdup_printf("%d", wpe_renderer_host_create_client()));
126         nargs++;
127     }
128 #endif
129
130 #if ENABLE(DEVELOPER_MODE)
131     Vector<CString> prefixArgs;
132     if (!m_launchOptions.processCmdPrefix.isNull()) {
133         for (auto& arg : m_launchOptions.processCmdPrefix.split(' '))
134             prefixArgs.append(arg.utf8());
135         nargs += prefixArgs.size();
136     }
137 #endif
138
139     char** argv = g_newa(char*, nargs);
140     unsigned i = 0;
141 #if ENABLE(DEVELOPER_MODE)
142     // If there's a prefix command, put it before the rest of the args.
143     for (auto& arg : prefixArgs)
144         argv[i++] = const_cast<char*>(arg.data());
145 #endif
146     argv[i++] = const_cast<char*>(realExecutablePath.data());
147     argv[i++] = processIdentifier.get();
148     argv[i++] = webkitSocket.get();
149 #if PLATFORM(WPE)
150     if (m_launchOptions.processType == ProcessLauncher::ProcessType::Web) {
151         argv[i++] = const_cast<char*>(wpeBackendLibraryParameter.isNull() ? "-" : wpeBackendLibraryParameter.data());
152         argv[i++] = wpeSocket.get();
153     }
154 #endif
155 #if ENABLE(NETSCAPE_PLUGIN_API)
156     argv[i++] = const_cast<char*>(realPluginPath.data());
157 #else
158     argv[i++] = nullptr;
159 #endif
160     argv[i++] = nullptr;
161
162     GRefPtr<GSubprocessLauncher> launcher = adoptGRef(g_subprocess_launcher_new(G_SUBPROCESS_FLAGS_INHERIT_FDS));
163     g_subprocess_launcher_set_child_setup(launcher.get(), childSetupFunction, GINT_TO_POINTER(socketPair.server), nullptr);
164     g_subprocess_launcher_take_fd(launcher.get(), socketPair.client, socketPair.client);
165
166     GUniqueOutPtr<GError> error;
167     GRefPtr<GSubprocess> process;
168 #if OS(LINUX)
169     bool sandboxEnabled = m_launchOptions.extraInitializationData.get("enable-sandbox") == "true";
170
171     if (auto sandboxEnv = Environment::get("WEBKIT_FORCE_SANDBOX"))
172         sandboxEnabled = *sandboxEnv == "1";
173
174     if (sandboxEnabled && isInsideFlatpak())
175         process = flatpakSpawn(launcher.get(), m_launchOptions, argv, socketPair.client, &error.outPtr());
176 #if ENABLE(BUBBLEWRAP_SANDBOX)
177     else if (sandboxEnabled)
178         process = bubblewrapSpawn(launcher.get(), m_launchOptions, argv, &error.outPtr());
179 #endif
180     else
181 #endif
182         process = adoptGRef(g_subprocess_launcher_spawnv(launcher.get(), argv, &error.outPtr()));
183
184     if (!process.get())
185         g_error("Unable to fork a new child process: %s", error->message);
186
187     const char* processIdStr = g_subprocess_get_identifier(process.get());
188     m_processIdentifier = g_ascii_strtoll(processIdStr, nullptr, 0);
189     RELEASE_ASSERT(m_processIdentifier);
190
191     // Don't expose the parent socket to potential future children.
192     if (!setCloseOnExec(socketPair.client))
193         RELEASE_ASSERT_NOT_REACHED();
194
195     // We've finished launching the process, message back to the main run loop.
196     RunLoop::main().dispatch([protectedThis = makeRef(*this), this, serverSocket = socketPair.server] {
197         didFinishLaunchingProcess(m_processIdentifier, serverSocket);
198     });
199 }
200
201 void ProcessLauncher::terminateProcess()
202 {
203     if (m_isLaunching) {
204         invalidate();
205         return;
206     }
207
208     if (!m_processIdentifier)
209         return;
210
211     kill(m_processIdentifier, SIGKILL);
212     m_processIdentifier = 0;
213 }
214
215 void ProcessLauncher::platformInvalidate()
216 {
217 }
218
219 } // namespace WebKit