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"
16
17 #include <unistd.h>
18 #include <fcntl.h>
19
20 namespace mozilla {
21 namespace ipc {
22
23 extern LazyLogModule gForkServiceLog;
24
25 mozilla::UniquePtr<ForkServiceChild> ForkServiceChild::sForkServiceChild;
26
StartForkServer()27 void ForkServiceChild::StartForkServer() {
28 std::vector<std::string> extraArgs;
29
30 GeckoChildProcessHost* subprocess =
31 new GeckoChildProcessHost(GeckoProcessType_ForkServer, false);
32 subprocess->LaunchAndWaitForProcessHandle(std::move(extraArgs));
33
34 int fd = subprocess->GetChannel()->GetFileDescriptor();
35 fd = dup(fd); // Dup it because the channel will close it.
36 int fs_flags = fcntl(fd, F_GETFL, 0);
37 fcntl(fd, F_SETFL, fs_flags & ~O_NONBLOCK);
38 int fd_flags = fcntl(fd, F_GETFD, 0);
39 fcntl(fd, F_SETFD, fd_flags | FD_CLOEXEC);
40
41 sForkServiceChild = mozilla::MakeUnique<ForkServiceChild>(fd, subprocess);
42
43 // Without doing this, IO thread may intercept messages since the
44 // IPC::Channel created by it is still open.
45 subprocess->GetChannel()->Close();
46 }
47
StopForkServer()48 void ForkServiceChild::StopForkServer() { sForkServiceChild = nullptr; }
49
ForkServiceChild(int aFd,GeckoChildProcessHost * aProcess)50 ForkServiceChild::ForkServiceChild(int aFd, GeckoChildProcessHost* aProcess)
51 : mWaitForHello(true), mFailed(false), mProcess(aProcess) {
52 mTcver = MakeUnique<MiniTransceiver>(aFd);
53 }
54
~ForkServiceChild()55 ForkServiceChild::~ForkServiceChild() {
56 mProcess->Destroy();
57 close(mTcver->GetFD());
58 }
59
SendForkNewSubprocess(const nsTArray<nsCString> & aArgv,const nsTArray<EnvVar> & aEnvMap,const nsTArray<FdMapping> & aFdsRemap,pid_t * aPid)60 bool ForkServiceChild::SendForkNewSubprocess(
61 const nsTArray<nsCString>& aArgv, const nsTArray<EnvVar>& aEnvMap,
62 const nsTArray<FdMapping>& aFdsRemap, pid_t* aPid) {
63 if (mWaitForHello) {
64 // IPC::Channel created by the GeckoChildProcessHost has
65 // already send a HELLO. It is expected to receive a hello
66 // message from the fork server too.
67 IPC::Message hello;
68 mTcver->RecvInfallible(hello, "Fail to receive HELLO message");
69 MOZ_ASSERT(hello.type() == ForkServer::kHELLO_MESSAGE_TYPE);
70 mWaitForHello = false;
71 }
72
73 mRecvPid = -1;
74 IPC::Message msg(MSG_ROUTING_CONTROL, Msg_ForkNewSubprocess__ID);
75
76 IPC::MessageWriter writer(msg);
77 WriteIPDLParam(&writer, nullptr, aArgv);
78 WriteIPDLParam(&writer, nullptr, aEnvMap);
79 WriteIPDLParam(&writer, nullptr, aFdsRemap);
80 if (!mTcver->Send(msg)) {
81 MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
82 ("the pipe to the fork server is closed or having errors"));
83 OnError();
84 return false;
85 }
86
87 IPC::Message reply;
88 if (!mTcver->Recv(reply)) {
89 MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
90 ("the pipe to the fork server is closed or having errors"));
91 OnError();
92 return false;
93 }
94 OnMessageReceived(std::move(reply));
95
96 MOZ_ASSERT(mRecvPid != -1);
97 *aPid = mRecvPid;
98 return true;
99 }
100
OnMessageReceived(IPC::Message && message)101 void ForkServiceChild::OnMessageReceived(IPC::Message&& message) {
102 if (message.type() != Reply_ForkNewSubprocess__ID) {
103 MOZ_LOG(gForkServiceLog, LogLevel::Verbose,
104 ("unknown reply type %d", message.type()));
105 return;
106 }
107 IPC::MessageReader reader(message);
108
109 if (!ReadIPDLParam(&reader, nullptr, &mRecvPid)) {
110 MOZ_CRASH("Error deserializing 'pid_t'");
111 }
112 reader.EndRead();
113 }
114
OnError()115 void ForkServiceChild::OnError() {
116 mFailed = true;
117 ForkServerLauncher::RestartForkServer();
118 }
119
120 NS_IMPL_ISUPPORTS(ForkServerLauncher, nsIObserver)
121
122 bool ForkServerLauncher::mHaveStartedClient = false;
123 StaticRefPtr<ForkServerLauncher> ForkServerLauncher::mSingleton;
124
ForkServerLauncher()125 ForkServerLauncher::ForkServerLauncher() {}
126
~ForkServerLauncher()127 ForkServerLauncher::~ForkServerLauncher() {}
128
Create()129 already_AddRefed<ForkServerLauncher> ForkServerLauncher::Create() {
130 if (mSingleton == nullptr) {
131 mSingleton = new ForkServerLauncher();
132 }
133 RefPtr<ForkServerLauncher> launcher = mSingleton;
134 return launcher.forget();
135 }
136
137 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)138 ForkServerLauncher::Observe(nsISupports* aSubject, const char* aTopic,
139 const char16_t* aData) {
140 if (strcmp(aTopic, NS_XPCOM_STARTUP_CATEGORY) == 0) {
141 nsCOMPtr<nsIObserverService> obsSvc =
142 mozilla::services::GetObserverService();
143 MOZ_ASSERT(obsSvc != nullptr);
144 // preferences are not available until final-ui-startup
145 obsSvc->AddObserver(this, "final-ui-startup", false);
146 } else if (!mHaveStartedClient && strcmp(aTopic, "final-ui-startup") == 0) {
147 if (StaticPrefs::dom_ipc_forkserver_enable_AtStartup()) {
148 mHaveStartedClient = true;
149 ForkServiceChild::StartForkServer();
150
151 nsCOMPtr<nsIObserverService> obsSvc =
152 mozilla::services::GetObserverService();
153 MOZ_ASSERT(obsSvc != nullptr);
154 obsSvc->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
155 } else {
156 mSingleton = nullptr;
157 }
158 }
159
160 if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
161 if (mHaveStartedClient) {
162 mHaveStartedClient = false;
163 ForkServiceChild::StopForkServer();
164 }
165
166 // To make leak checker happy!
167 mSingleton = nullptr;
168 }
169 return NS_OK;
170 }
171
RestartForkServer()172 void ForkServerLauncher::RestartForkServer() {
173 // Restart fork server
174 NS_SUCCEEDED(NS_DispatchToMainThreadQueue(
175 NS_NewRunnableFunction("OnForkServerError",
176 [] {
177 if (mSingleton) {
178 ForkServiceChild::StopForkServer();
179 ForkServiceChild::StartForkServer();
180 }
181 }),
182 EventQueuePriority::Idle));
183 }
184
185 } // namespace ipc
186 } // namespace mozilla
187