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/. */
7 #include "ForkServiceChild.h"
8 #include "ForkServer.h"
9 #include "mozilla/Atomics.h"
10 #include "mozilla/Logging.h"
11 #include "mozilla/ipc/GeckoChildProcessHost.h"
12 #include "mozilla/ipc/IPDLParamTraits.h"
13 #include "mozilla/ipc/ProtocolMessageUtils.h"
14 #include "mozilla/StaticPrefs_dom.h"
15 #include "mozilla/Services.h"
16 #include "ipc/IPCMessageUtilsSpecializations.h"
17 #include "nsIObserverService.h"
25 extern LazyLogModule gForkServiceLog
;
27 mozilla::UniquePtr
<ForkServiceChild
> ForkServiceChild::sForkServiceChild
;
28 Atomic
<bool> ForkServiceChild::sForkServiceUsed
;
30 static bool ConfigurePipeFd(int aFd
) {
31 int flags
= fcntl(aFd
, F_GETFD
, 0);
32 return flags
!= -1 && fcntl(aFd
, F_SETFD
, flags
| FD_CLOEXEC
) != -1;
35 void ForkServiceChild::StartForkServer() {
36 // Create the socket to use for communication, and mark both ends as
39 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, fds
) < 0) {
40 MOZ_LOG(gForkServiceLog
, LogLevel::Error
,
41 ("failed to create fork server socket"));
44 UniqueFileHandle
server(fds
[0]);
45 UniqueFileHandle
client(fds
[1]);
47 if (!ConfigurePipeFd(server
.get()) || !ConfigurePipeFd(client
.get())) {
48 MOZ_LOG(gForkServiceLog
, LogLevel::Error
,
49 ("failed to configure fork server socket"));
53 GeckoChildProcessHost
* subprocess
=
54 new GeckoChildProcessHost(GeckoProcessType_ForkServer
, false);
55 subprocess
->AddFdToRemap(client
.get(), ForkServer::kClientPipeFd
);
56 if (!subprocess
->LaunchAndWaitForProcessHandle(std::vector
<std::string
>{})) {
57 MOZ_LOG(gForkServiceLog
, LogLevel::Error
, ("failed to launch fork server"));
61 sForkServiceUsed
= true;
63 mozilla::MakeUnique
<ForkServiceChild
>(server
.release(), subprocess
);
66 void ForkServiceChild::StopForkServer() { sForkServiceChild
= nullptr; }
68 ForkServiceChild::ForkServiceChild(int aFd
, GeckoChildProcessHost
* aProcess
)
69 : mFailed(false), mProcess(aProcess
) {
70 mTcver
= MakeUnique
<MiniTransceiver
>(aFd
);
73 ForkServiceChild::~ForkServiceChild() {
75 close(mTcver
->GetFD());
78 Result
<Ok
, LaunchError
> ForkServiceChild::SendForkNewSubprocess(
79 const Args
& aArgs
, pid_t
* aPid
) {
81 IPC::Message
msg(MSG_ROUTING_CONTROL
, Msg_ForkNewSubprocess__ID
);
83 IPC::MessageWriter
writer(msg
);
84 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
85 WriteIPDLParam(&writer
, nullptr, aArgs
.mForkFlags
);
86 WriteIPDLParam(&writer
, nullptr, aArgs
.mChroot
);
88 WriteIPDLParam(&writer
, nullptr, aArgs
.mArgv
);
89 WriteIPDLParam(&writer
, nullptr, aArgs
.mEnv
);
90 WriteIPDLParam(&writer
, nullptr, aArgs
.mFdsRemap
);
91 if (!mTcver
->Send(msg
)) {
92 MOZ_LOG(gForkServiceLog
, LogLevel::Verbose
,
93 ("the pipe to the fork server is closed or having errors"));
95 return Err(LaunchError("FSC::SFNS::Send"));
98 UniquePtr
<IPC::Message
> reply
;
99 if (!mTcver
->Recv(reply
)) {
100 MOZ_LOG(gForkServiceLog
, LogLevel::Verbose
,
101 ("the pipe to the fork server is closed or having errors"));
103 return Err(LaunchError("FSC::SFNS::Recv"));
105 OnMessageReceived(std::move(reply
));
107 MOZ_ASSERT(mRecvPid
!= -1);
112 void ForkServiceChild::OnMessageReceived(UniquePtr
<IPC::Message
> message
) {
113 if (message
->type() != Reply_ForkNewSubprocess__ID
) {
114 MOZ_LOG(gForkServiceLog
, LogLevel::Verbose
,
115 ("unknown reply type %d", message
->type()));
118 IPC::MessageReader
reader(*message
);
120 if (!ReadIPDLParam(&reader
, nullptr, &mRecvPid
)) {
121 MOZ_CRASH("Error deserializing 'pid_t'");
126 void ForkServiceChild::OnError() {
128 ForkServerLauncher::RestartForkServer();
131 NS_IMPL_ISUPPORTS(ForkServerLauncher
, nsIObserver
)
133 bool ForkServerLauncher::mHaveStartedClient
= false;
134 StaticRefPtr
<ForkServerLauncher
> ForkServerLauncher::mSingleton
;
136 ForkServerLauncher::ForkServerLauncher() {}
138 ForkServerLauncher::~ForkServerLauncher() {}
140 already_AddRefed
<ForkServerLauncher
> ForkServerLauncher::Create() {
141 if (mSingleton
== nullptr) {
142 mSingleton
= new ForkServerLauncher();
144 RefPtr
<ForkServerLauncher
> launcher
= mSingleton
;
145 return launcher
.forget();
149 ForkServerLauncher::Observe(nsISupports
* aSubject
, const char* aTopic
,
150 const char16_t
* aData
) {
151 if (strcmp(aTopic
, NS_XPCOM_STARTUP_CATEGORY
) == 0) {
152 nsCOMPtr
<nsIObserverService
> obsSvc
=
153 mozilla::services::GetObserverService();
154 MOZ_ASSERT(obsSvc
!= nullptr);
155 // preferences are not available until final-ui-startup
156 obsSvc
->AddObserver(this, "final-ui-startup", false);
157 } else if (!mHaveStartedClient
&& strcmp(aTopic
, "final-ui-startup") == 0) {
158 if (StaticPrefs::dom_ipc_forkserver_enable_AtStartup()) {
159 mHaveStartedClient
= true;
160 ForkServiceChild::StartForkServer();
162 nsCOMPtr
<nsIObserverService
> obsSvc
=
163 mozilla::services::GetObserverService();
164 MOZ_ASSERT(obsSvc
!= nullptr);
165 obsSvc
->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, false);
167 mSingleton
= nullptr;
171 if (strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
) == 0) {
172 if (mHaveStartedClient
) {
173 mHaveStartedClient
= false;
174 ForkServiceChild::StopForkServer();
177 // To make leak checker happy!
178 mSingleton
= nullptr;
183 void ForkServerLauncher::RestartForkServer() {
184 // Restart fork server
185 NS_SUCCEEDED(NS_DispatchToMainThreadQueue(
186 NS_NewRunnableFunction("OnForkServerError",
189 ForkServiceChild::StopForkServer();
190 ForkServiceChild::StartForkServer();
193 EventQueuePriority::Idle
));
197 } // namespace mozilla