1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
|
/* -*- 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 "AppShutdown.h"
#ifdef XP_WIN
# include <windows.h>
#else
# include <unistd.h>
#endif
#include "GeckoProfiler.h"
#include "mozilla/CmdLineAndEnvUtils.h"
#include "mozilla/PoisonIOInterposer.h"
#include "mozilla/Printf.h"
#include "mozilla/scache/StartupCache.h"
#include "mozilla/SpinEventLoopUntil.h"
#include "mozilla/StartupTimeline.h"
#include "mozilla/StaticPrefs_toolkit.h"
#include "mozilla/LateWriteChecks.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsAppRunner.h"
#include "nsDirectoryServiceUtils.h"
#include "nsICertStorage.h"
#include "nsThreadUtils.h"
#include "prenv.h"
#ifdef MOZ_NEW_XULSTORE
# include "mozilla/XULStore.h"
#endif
namespace mozilla {
static ShutdownPhase sFastShutdownPhase = ShutdownPhase::NotInShutdown;
static ShutdownPhase sLateWriteChecksPhase = ShutdownPhase::NotInShutdown;
static AppShutdownMode sShutdownMode = AppShutdownMode::Normal;
static Atomic<bool, MemoryOrdering::Relaxed> sIsShuttingDown;
static int sExitCode = 0;
// These environment variable strings are all deliberately copied and leaked
// due to requirements of PR_SetEnv and similar.
static char* sSavedXulAppFile = nullptr;
#ifdef XP_WIN
static wchar_t* sSavedProfDEnvVar = nullptr;
static wchar_t* sSavedProfLDEnvVar = nullptr;
#else
static char* sSavedProfDEnvVar = nullptr;
static char* sSavedProfLDEnvVar = nullptr;
#endif
ShutdownPhase GetShutdownPhaseFromPrefValue(int32_t aPrefValue) {
switch (aPrefValue) {
case 1:
return ShutdownPhase::ShutdownPostLastCycleCollection;
case 2:
return ShutdownPhase::ShutdownThreads;
case 3:
return ShutdownPhase::Shutdown;
// NOTE: the remaining values from the ShutdownPhase enum will be added
// when we're at least reasonably confident that the world won't come
// crashing down if we do a fast shutdown at that point.
}
return ShutdownPhase::NotInShutdown;
}
bool AppShutdown::IsShuttingDown() { return sIsShuttingDown; }
int AppShutdown::GetExitCode() { return sExitCode; }
void AppShutdown::SaveEnvVarsForPotentialRestart() {
const char* s = PR_GetEnv("XUL_APP_FILE");
if (s) {
sSavedXulAppFile = Smprintf("%s=%s", "XUL_APP_FILE", s).release();
MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(sSavedXulAppFile);
}
}
void AppShutdown::MaybeDoRestart() {
if (sShutdownMode == AppShutdownMode::Restart) {
StopLateWriteChecks();
// Since we'll be launching our child while we're still alive, make sure
// we've unlocked the profile first, otherwise the child could hit its
// profile lock check before we've exited and thus released our lock.
UnlockProfile();
if (sSavedXulAppFile) {
PR_SetEnv(sSavedXulAppFile);
}
#ifdef XP_WIN
if (sSavedProfDEnvVar && !EnvHasValue("XRE_PROFILE_PATH")) {
SetEnvironmentVariableW(L"XRE_PROFILE_PATH", sSavedProfDEnvVar);
}
if (sSavedProfLDEnvVar && !EnvHasValue("XRE_PROFILE_LOCAL_PATH")) {
SetEnvironmentVariableW(L"XRE_PROFILE_LOCAL_PATH", sSavedProfLDEnvVar);
}
#else
if (sSavedProfDEnvVar && !EnvHasValue("XRE_PROFILE_PATH")) {
PR_SetEnv(sSavedProfDEnvVar);
}
if (sSavedProfLDEnvVar && !EnvHasValue("XRE_PROFILE_LOCAL_PATH")) {
PR_SetEnv(sSavedProfLDEnvVar);
}
#endif
LaunchChild(true);
}
}
#ifdef XP_WIN
wchar_t* CopyPathIntoNewWCString(nsIFile* aFile) {
wchar_t* result = nullptr;
nsAutoString resStr;
aFile->GetPath(resStr);
if (resStr.Length() > 0) {
result = (wchar_t*)malloc((resStr.Length() + 1) * sizeof(wchar_t));
if (result) {
wcscpy(result, resStr.get());
result[resStr.Length()] = 0;
}
}
return result;
}
#endif
void AppShutdown::Init(AppShutdownMode aMode, int aExitCode) {
if (sShutdownMode == AppShutdownMode::Normal) {
sShutdownMode = aMode;
}
sExitCode = aExitCode;
// Late-write checks needs to find the profile directory, so it has to
// be initialized before services::Shutdown or (because of
// xpcshell tests replacing the service) modules being unloaded.
InitLateWriteChecks();
int32_t fastShutdownPref = StaticPrefs::toolkit_shutdown_fastShutdownStage();
sFastShutdownPhase = GetShutdownPhaseFromPrefValue(fastShutdownPref);
int32_t lateWriteChecksPref =
StaticPrefs::toolkit_shutdown_lateWriteChecksStage();
sLateWriteChecksPhase = GetShutdownPhaseFromPrefValue(lateWriteChecksPref);
// Very early shutdowns can happen before the startup cache is even
// initialized; don't bother initializing it during shutdown.
if (auto* cache = scache::StartupCache::GetSingletonNoInit()) {
cache->MaybeInitShutdownWrite();
}
}
void AppShutdown::MaybeFastShutdown(ShutdownPhase aPhase) {
// For writes which we want to ensure are recorded, we don't want to trip
// the late write checking code. Anything that writes to disk and which
// we don't want to skip should be listed out explicitly in this section.
if (aPhase == sFastShutdownPhase || aPhase == sLateWriteChecksPhase) {
if (auto* cache = scache::StartupCache::GetSingletonNoInit()) {
cache->EnsureShutdownWriteComplete();
}
nsresult rv;
#ifdef MOZ_NEW_XULSTORE
rv = XULStore::Shutdown();
NS_ASSERTION(NS_SUCCEEDED(rv), "XULStore::Shutdown() failed.");
#endif
nsCOMPtr<nsICertStorage> certStorage =
do_GetService("@mozilla.org/security/certstorage;1", &rv);
if (NS_SUCCEEDED(rv)) {
SpinEventLoopUntil([&]() {
int32_t remainingOps;
nsresult rv = certStorage->GetRemainingOperationCount(&remainingOps);
NS_ASSERTION(NS_SUCCEEDED(rv),
"nsICertStorage::getRemainingOperationCount failed during "
"shutdown");
return NS_FAILED(rv) || remainingOps <= 0;
});
}
}
if (aPhase == sFastShutdownPhase) {
StopLateWriteChecks();
RecordShutdownEndTimeStamp();
MaybeDoRestart();
#ifdef MOZ_GECKO_PROFILER
profiler_shutdown(IsFastShutdown::Yes);
#endif
DoImmediateExit(sExitCode);
} else if (aPhase == sLateWriteChecksPhase) {
#ifdef XP_MACOSX
OnlyReportDirtyWrites();
#endif /* XP_MACOSX */
BeginLateWriteChecks();
}
}
void AppShutdown::OnShutdownConfirmed() {
sIsShuttingDown = true;
// If we're restarting, we need to save environment variables correctly
// while everything is still alive to do so.
if (sShutdownMode == AppShutdownMode::Restart) {
nsCOMPtr<nsIFile> profD;
nsCOMPtr<nsIFile> profLD;
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(profD));
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
getter_AddRefs(profLD));
#ifdef XP_WIN
sSavedProfDEnvVar = CopyPathIntoNewWCString(profD);
sSavedProfLDEnvVar = CopyPathIntoNewWCString(profLD);
#else
nsAutoCString profDStr;
profD->GetNativePath(profDStr);
sSavedProfDEnvVar =
Smprintf("XRE_PROFILE_PATH=%s", profDStr.get()).release();
nsAutoCString profLDStr;
profLD->GetNativePath(profLDStr);
sSavedProfLDEnvVar =
Smprintf("XRE_PROFILE_LOCAL_PATH=%s", profLDStr.get()).release();
#endif
MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(sSavedProfDEnvVar);
MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(sSavedProfLDEnvVar);
}
}
void AppShutdown::DoImmediateExit(int aExitCode) {
#ifdef XP_WIN
HANDLE process = ::GetCurrentProcess();
if (::TerminateProcess(process, aExitCode)) {
::WaitForSingleObject(process, INFINITE);
}
MOZ_CRASH("TerminateProcess failed.");
#else
_exit(aExitCode);
#endif
}
bool AppShutdown::IsRestarting() {
return sShutdownMode == AppShutdownMode::Restart;
}
} // namespace mozilla
|