summaryrefslogtreecommitdiffstats
path: root/ipc/glue/ForkServiceChild.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /ipc/glue/ForkServiceChild.cpp
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ipc/glue/ForkServiceChild.cpp')
-rw-r--r--ipc/glue/ForkServiceChild.cpp182
1 files changed, 182 insertions, 0 deletions
diff --git a/ipc/glue/ForkServiceChild.cpp b/ipc/glue/ForkServiceChild.cpp
new file mode 100644
index 0000000000..a808e7389f
--- /dev/null
+++ b/ipc/glue/ForkServiceChild.cpp
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "ForkServiceChild.h"
+#include "ForkServer.h"
+#include "mozilla/ipc/IPDLParamTraits.h"
+#include "mozilla/Logging.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/ipc/ProtocolMessageUtils.h"
+#include "mozilla/StaticPrefs_dom.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+
+namespace mozilla {
+namespace ipc {
+
+extern LazyLogModule gForkServiceLog;
+
+mozilla::UniquePtr<ForkServiceChild> ForkServiceChild::sForkServiceChild;
+
+void ForkServiceChild::StartForkServer() {
+ std::vector<std::string> extraArgs;
+
+ GeckoChildProcessHost* subprocess =
+ new GeckoChildProcessHost(GeckoProcessType_ForkServer, false);
+ subprocess->LaunchAndWaitForProcessHandle(std::move(extraArgs));
+
+ int fd = subprocess->GetChannel()->GetFileDescriptor();
+ fd = dup(fd); // Dup it because the channel will close it.
+ int fs_flags = fcntl(fd, F_GETFL, 0);
+ fcntl(fd, F_SETFL, fs_flags & ~O_NONBLOCK);
+ int fd_flags = fcntl(fd, F_GETFD, 0);
+ fcntl(fd, F_SETFD, fd_flags | FD_CLOEXEC);
+
+ sForkServiceChild = mozilla::MakeUnique<ForkServiceChild>(fd, subprocess);
+
+ // Without doing this, IO thread may intercept messages since the
+ // IPC::Channel created by it is still open.
+ subprocess->GetChannel()->Close();
+}
+
+void ForkServiceChild::StopForkServer() { sForkServiceChild = nullptr; }
+
+ForkServiceChild::ForkServiceChild(int aFd, GeckoChildProcessHost* aProcess)
+ : mWaitForHello(true), mFailed(false), mProcess(aProcess) {
+ mTcver = MakeUnique<MiniTransceiver>(aFd);
+}
+
+ForkServiceChild::~ForkServiceChild() {
+ mProcess->Destroy();
+ close(mTcver->GetFD());
+}
+
+bool ForkServiceChild::SendForkNewSubprocess(
+ const nsTArray<nsCString>& aArgv, const nsTArray<EnvVar>& aEnvMap,
+ const nsTArray<FdMapping>& aFdsRemap, pid_t* aPid) {
+ if (mWaitForHello) {
+ // IPC::Channel created by the GeckoChildProcessHost has
+ // already send a HELLO. It is expected to receive a hello
+ // message from the fork server too.
+ IPC::Message hello;
+ mTcver->RecvInfallible(hello, "Fail to receive HELLO message");
+ MOZ_ASSERT(hello.type() == ForkServer::kHELLO_MESSAGE_TYPE);
+ mWaitForHello = false;
+ }
+
+ mRecvPid = -1;
+ IPC::Message msg(MSG_ROUTING_CONTROL, Msg_ForkNewSubprocess__ID);
+
+ WriteIPDLParam(&msg, nullptr, aArgv);
+ WriteIPDLParam(&msg, nullptr, aEnvMap);
+ WriteIPDLParam(&msg, nullptr, aFdsRemap);
+ if (!mTcver->Send(msg)) {
+ MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
+ ("the pipe to the fork server is closed or having errors"));
+ OnError();
+ return false;
+ }
+
+ IPC::Message reply;
+ if (!mTcver->Recv(reply)) {
+ MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
+ ("the pipe to the fork server is closed or having errors"));
+ OnError();
+ return false;
+ }
+ OnMessageReceived(std::move(reply));
+
+ MOZ_ASSERT(mRecvPid != -1);
+ *aPid = mRecvPid;
+ return true;
+}
+
+void ForkServiceChild::OnMessageReceived(IPC::Message&& message) {
+ if (message.type() != Reply_ForkNewSubprocess__ID) {
+ MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
+ ("unknown reply type %d", message.type()));
+ return;
+ }
+ PickleIterator iter__(message);
+
+ if (!ReadIPDLParam(&message, &iter__, nullptr, &mRecvPid)) {
+ MOZ_CRASH("Error deserializing 'pid_t'");
+ }
+ message.EndRead(iter__, message.type());
+}
+
+void ForkServiceChild::OnError() {
+ mFailed = true;
+ ForkServerLauncher::RestartForkServer();
+}
+
+NS_IMPL_ISUPPORTS(ForkServerLauncher, nsIObserver)
+
+bool ForkServerLauncher::mHaveStartedClient = false;
+StaticRefPtr<ForkServerLauncher> ForkServerLauncher::mSingleton;
+
+ForkServerLauncher::ForkServerLauncher() {}
+
+ForkServerLauncher::~ForkServerLauncher() {}
+
+already_AddRefed<ForkServerLauncher> ForkServerLauncher::Create() {
+ if (mSingleton == nullptr) {
+ mSingleton = new ForkServerLauncher();
+ }
+ RefPtr<ForkServerLauncher> launcher = mSingleton;
+ return launcher.forget();
+}
+
+NS_IMETHODIMP
+ForkServerLauncher::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ if (strcmp(aTopic, NS_XPCOM_STARTUP_CATEGORY) == 0) {
+ nsCOMPtr<nsIObserverService> obsSvc =
+ mozilla::services::GetObserverService();
+ MOZ_ASSERT(obsSvc != nullptr);
+ // preferences are not available until final-ui-startup
+ obsSvc->AddObserver(this, "final-ui-startup", false);
+ } else if (!mHaveStartedClient && strcmp(aTopic, "final-ui-startup") == 0) {
+ if (StaticPrefs::dom_ipc_forkserver_enable_AtStartup()) {
+ mHaveStartedClient = true;
+ ForkServiceChild::StartForkServer();
+
+ nsCOMPtr<nsIObserverService> obsSvc =
+ mozilla::services::GetObserverService();
+ MOZ_ASSERT(obsSvc != nullptr);
+ obsSvc->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+ } else {
+ mSingleton = nullptr;
+ }
+ }
+
+ if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
+ if (mHaveStartedClient) {
+ mHaveStartedClient = false;
+ ForkServiceChild::StopForkServer();
+ }
+
+ // To make leak checker happy!
+ mSingleton = nullptr;
+ }
+ return NS_OK;
+}
+
+void ForkServerLauncher::RestartForkServer() {
+ // Restart fork server
+ NS_SUCCEEDED(NS_DispatchToMainThreadQueue(
+ NS_NewRunnableFunction("OnForkServerError",
+ [] {
+ if (mSingleton) {
+ ForkServiceChild::StopForkServer();
+ ForkServiceChild::StartForkServer();
+ }
+ }),
+ EventQueuePriority::Idle));
+}
+
+} // namespace ipc
+} // namespace mozilla