Bug 1856331 [wpt PR 42276] - [fetch-later] Force sending when BackgroundSync permissi...
[gecko.git] / ipc / glue / ForkServiceChild.cpp
blobdb130343cc597aa6e5af7e415aeff1ef41091462
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "ForkServiceChild.h"
7 #include "ForkServer.h"
8 #include "mozilla/ipc/IPDLParamTraits.h"
9 #include "mozilla/Logging.h"
10 #include "mozilla/ipc/GeckoChildProcessHost.h"
11 #include "mozilla/ipc/ProtocolMessageUtils.h"
12 #include "mozilla/StaticPrefs_dom.h"
13 #include "mozilla/Services.h"
14 #include "ipc/IPCMessageUtilsSpecializations.h"
15 #include "nsIObserverService.h"
17 #include <unistd.h>
18 #include <fcntl.h>
20 namespace mozilla {
21 namespace ipc {
23 extern LazyLogModule gForkServiceLog;
25 mozilla::UniquePtr<ForkServiceChild> ForkServiceChild::sForkServiceChild;
27 static bool ConfigurePipeFd(int aFd) {
28 int flags = fcntl(aFd, F_GETFD, 0);
29 return flags != -1 && fcntl(aFd, F_SETFD, flags | FD_CLOEXEC) != -1;
32 void ForkServiceChild::StartForkServer() {
33 // Create the socket to use for communication, and mark both ends as
34 // FD_CLOEXEC.
35 int fds[2];
36 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
37 MOZ_LOG(gForkServiceLog, LogLevel::Error,
38 ("failed to create fork server socket"));
39 return;
41 UniqueFileHandle server(fds[0]);
42 UniqueFileHandle client(fds[1]);
44 if (!ConfigurePipeFd(server.get()) || !ConfigurePipeFd(client.get())) {
45 MOZ_LOG(gForkServiceLog, LogLevel::Error,
46 ("failed to configure fork server socket"));
47 return;
50 GeckoChildProcessHost* subprocess =
51 new GeckoChildProcessHost(GeckoProcessType_ForkServer, false);
52 subprocess->AddFdToRemap(client.get(), ForkServer::kClientPipeFd);
53 if (!subprocess->LaunchAndWaitForProcessHandle(std::vector<std::string>{})) {
54 MOZ_LOG(gForkServiceLog, LogLevel::Error, ("failed to launch fork server"));
55 return;
58 sForkServiceChild =
59 mozilla::MakeUnique<ForkServiceChild>(server.release(), subprocess);
62 void ForkServiceChild::StopForkServer() { sForkServiceChild = nullptr; }
64 ForkServiceChild::ForkServiceChild(int aFd, GeckoChildProcessHost* aProcess)
65 : mFailed(false), mProcess(aProcess) {
66 mTcver = MakeUnique<MiniTransceiver>(aFd);
69 ForkServiceChild::~ForkServiceChild() {
70 mProcess->Destroy();
71 close(mTcver->GetFD());
74 Result<Ok, LaunchError> ForkServiceChild::SendForkNewSubprocess(
75 const nsTArray<nsCString>& aArgv, const nsTArray<EnvVar>& aEnvMap,
76 const nsTArray<FdMapping>& aFdsRemap, pid_t* aPid) {
77 mRecvPid = -1;
78 IPC::Message msg(MSG_ROUTING_CONTROL, Msg_ForkNewSubprocess__ID);
80 IPC::MessageWriter writer(msg);
81 WriteIPDLParam(&writer, nullptr, aArgv);
82 WriteIPDLParam(&writer, nullptr, aEnvMap);
83 WriteIPDLParam(&writer, nullptr, aFdsRemap);
84 if (!mTcver->Send(msg)) {
85 MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
86 ("the pipe to the fork server is closed or having errors"));
87 OnError();
88 return Err(LaunchError("FSC::SFNS::Send"));
91 UniquePtr<IPC::Message> reply;
92 if (!mTcver->Recv(reply)) {
93 MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
94 ("the pipe to the fork server is closed or having errors"));
95 OnError();
96 return Err(LaunchError("FSC::SFNS::Recv"));
98 OnMessageReceived(std::move(reply));
100 MOZ_ASSERT(mRecvPid != -1);
101 *aPid = mRecvPid;
102 return Ok();
105 void ForkServiceChild::OnMessageReceived(UniquePtr<IPC::Message> message) {
106 if (message->type() != Reply_ForkNewSubprocess__ID) {
107 MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
108 ("unknown reply type %d", message->type()));
109 return;
111 IPC::MessageReader reader(*message);
113 if (!ReadIPDLParam(&reader, nullptr, &mRecvPid)) {
114 MOZ_CRASH("Error deserializing 'pid_t'");
116 reader.EndRead();
119 void ForkServiceChild::OnError() {
120 mFailed = true;
121 ForkServerLauncher::RestartForkServer();
124 NS_IMPL_ISUPPORTS(ForkServerLauncher, nsIObserver)
126 bool ForkServerLauncher::mHaveStartedClient = false;
127 StaticRefPtr<ForkServerLauncher> ForkServerLauncher::mSingleton;
129 ForkServerLauncher::ForkServerLauncher() {}
131 ForkServerLauncher::~ForkServerLauncher() {}
133 already_AddRefed<ForkServerLauncher> ForkServerLauncher::Create() {
134 if (mSingleton == nullptr) {
135 mSingleton = new ForkServerLauncher();
137 RefPtr<ForkServerLauncher> launcher = mSingleton;
138 return launcher.forget();
141 NS_IMETHODIMP
142 ForkServerLauncher::Observe(nsISupports* aSubject, const char* aTopic,
143 const char16_t* aData) {
144 if (strcmp(aTopic, NS_XPCOM_STARTUP_CATEGORY) == 0) {
145 nsCOMPtr<nsIObserverService> obsSvc =
146 mozilla::services::GetObserverService();
147 MOZ_ASSERT(obsSvc != nullptr);
148 // preferences are not available until final-ui-startup
149 obsSvc->AddObserver(this, "final-ui-startup", false);
150 } else if (!mHaveStartedClient && strcmp(aTopic, "final-ui-startup") == 0) {
151 if (StaticPrefs::dom_ipc_forkserver_enable_AtStartup()) {
152 mHaveStartedClient = true;
153 ForkServiceChild::StartForkServer();
155 nsCOMPtr<nsIObserverService> obsSvc =
156 mozilla::services::GetObserverService();
157 MOZ_ASSERT(obsSvc != nullptr);
158 obsSvc->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
159 } else {
160 mSingleton = nullptr;
164 if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
165 if (mHaveStartedClient) {
166 mHaveStartedClient = false;
167 ForkServiceChild::StopForkServer();
170 // To make leak checker happy!
171 mSingleton = nullptr;
173 return NS_OK;
176 void ForkServerLauncher::RestartForkServer() {
177 // Restart fork server
178 NS_SUCCEEDED(NS_DispatchToMainThreadQueue(
179 NS_NewRunnableFunction("OnForkServerError",
180 [] {
181 if (mSingleton) {
182 ForkServiceChild::StopForkServer();
183 ForkServiceChild::StartForkServer();
186 EventQueuePriority::Idle));
189 } // namespace ipc
190 } // namespace mozilla