Bug 692255 - Get rid of prefetch files on Windows for faster startup. r=rstrong

This commit is contained in:
Brian R. Bondy 2012-05-27 22:40:48 -04:00
parent 2d2e1e01d3
commit 4ae600a9d9
13 changed files with 488 additions and 15 deletions

View File

@ -26,6 +26,7 @@
#include "nsUnicharUtils.h"
#include "nsIWinTaskbar.h"
#include "nsISupportsPrimitives.h"
#include "nsThreadUtils.h"
#include "windows.h"
#include "shellapi.h"
@ -52,6 +53,11 @@
#define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
// We clear the prefetch files one time after the browser is started after
// 60 seconds. After this is done once we set a pref so this will never happen
// again except in updater code.
#define CLEAR_PREFETCH_TIMEOUT_MS 60000
NS_IMPL_ISUPPORTS2(nsWindowsShellService, nsIWindowsShellService, nsIShellService)
static nsresult
@ -189,6 +195,16 @@ static SETTING gDDESettings[] = {
{ MAKE_KEY_NAME1("Software\\Classes\\HTTPS", SOD) }
};
#if defined(MOZ_MAINTENANCE_SERVICE)
#define ONLY_SERVICE_LAUNCHING
#include "updatehelper.h"
#include "updatehelper.cpp"
static const char kPrefetchClearedPref[] = "app.update.service.prefetchCleared";
static nsCOMPtr<nsIThread> sThread;
#endif
nsresult
GetHelperPath(nsAutoString& aPath)
{
@ -908,6 +924,123 @@ nsWindowsShellService::SetDesktopBackgroundColor(PRUint32 aColor)
return regKey->Close();
}
nsWindowsShellService::nsWindowsShellService() :
mCheckedThisSession(false)
{
#if defined(MOZ_MAINTENANCE_SERVICE)
// Check to make sure the service is installed
PRUint32 installed = 0;
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1");
if (!regKey ||
NS_FAILED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
NS_LITERAL_STRING(
"SOFTWARE\\Mozilla\\MaintenanceService"),
nsIWindowsRegKey::ACCESS_READ |
nsIWindowsRegKey::WOW64_64)) ||
NS_FAILED(regKey->ReadIntValue(NS_LITERAL_STRING("Installed"),
&installed)) ||
!installed) {
return;
}
// check to see if we have attempted to do the one time operation of clearing
// the prefetch.
bool prefetchCleared;
nsCOMPtr<nsIPrefBranch> prefBranch;
nsCOMPtr<nsIPrefService> prefs =
do_GetService(NS_PREFSERVICE_CONTRACTID);
if (!prefs ||
NS_FAILED(prefs->GetBranch(nsnull, getter_AddRefs(prefBranch))) ||
(NS_SUCCEEDED(prefBranch->GetBoolPref(kPrefetchClearedPref,
&prefetchCleared)) &&
prefetchCleared)) {
return;
}
// In a minute after startup is definitely complete, launch the
// service command.
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
if (mTimer) {
mTimer->InitWithFuncCallback(
nsWindowsShellService::LaunchPrefetchClearCommand,
nsnull, CLEAR_PREFETCH_TIMEOUT_MS, nsITimer::TYPE_ONE_SHOT);
}
#endif
}
nsWindowsShellService::~nsWindowsShellService()
{
#if defined(MOZ_MAINTENANCE_SERVICE)
if (mTimer) {
mTimer->Cancel();
mTimer = nsnull;
}
if (sThread) {
sThread->Shutdown();
sThread = nsnull;
}
#endif
}
#if defined(MOZ_MAINTENANCE_SERVICE)
class ClearPrefetchEvent : public nsRunnable {
public:
ClearPrefetchEvent()
{
}
NS_IMETHOD Run()
{
// Start the service command
LPCWSTR updaterServiceArgv[2];
updaterServiceArgv[0] = L"MozillaMaintenance";
updaterServiceArgv[1] = L"clear-prefetch";
// If this command fails, it is not critical as prefetch will be cleared
// on the next software update.
StartServiceCommand(NS_ARRAY_LENGTH(updaterServiceArgv),
updaterServiceArgv);
return NS_OK;
}
};
#endif
/**
* For faster startup we attempt to clear the prefetch if the maintenance
* service is installed. Please see the definition of ClearPrefetch()
* in toolkit/components/maintenanceservice/prefetch.cpp for more info.
* For now the only application that gets prefetch cleaned is Firefox
* since we have not done performance checking for other applications.
* This is done on every update but also there is a one time operation done
* from within the program for first time installs.
*/
#if defined(MOZ_MAINTENANCE_SERVICE)
void
nsWindowsShellService::LaunchPrefetchClearCommand(nsITimer *aTimer, void*)
{
// Make sure we don't call this again from the application, it will be
// called on each application update instead.
nsCOMPtr<nsIPrefBranch> prefBranch;
nsCOMPtr<nsIPrefService> prefs =
do_GetService(NS_PREFSERVICE_CONTRACTID);
if (prefs) {
if (NS_SUCCEEDED(prefs->GetBranch(nsnull, getter_AddRefs(prefBranch)))) {
prefBranch->SetBoolPref(kPrefetchClearedPref, true);
}
}
// Starting the sevice can take a bit of time and we don't want to block the
// main thread, so start an event on another thread to handle the operation
NS_NewThread(getter_AddRefs(sThread));
if (sThread) {
nsCOMPtr<nsIRunnable> prefetchEvent = new ClearPrefetchEvent();
sThread->Dispatch(prefetchEvent, NS_DISPATCH_NORMAL);
}
}
#endif
NS_IMETHODIMP
nsWindowsShellService::OpenApplicationWithURI(nsILocalFile* aApplication,
const nsACString& aURI)

View File

@ -9,6 +9,7 @@
#include "nscore.h"
#include "nsStringAPI.h"
#include "nsIWindowsShellService.h"
#include "nsITimer.h"
#include <windows.h>
#include <ole2.h>
@ -16,8 +17,8 @@
class nsWindowsShellService : public nsIWindowsShellService
{
public:
nsWindowsShellService() : mCheckedThisSession(false) {};
virtual ~nsWindowsShellService() {};
nsWindowsShellService();
virtual ~nsWindowsShellService();
NS_DECL_ISUPPORTS
NS_DECL_NSISHELLSERVICE
@ -27,6 +28,10 @@ protected:
bool IsDefaultBrowserVista(bool* aIsDefaultBrowser);
private:
#if defined(MOZ_MAINTENANCE_SERVICE)
static void LaunchPrefetchClearCommand(nsITimer *aTimer, void*);
nsCOMPtr<nsITimer> mTimer;
#endif
bool mCheckedThisSession;
};

View File

@ -587,6 +587,7 @@
SetRegView 64
${EndIf}
DeleteRegKey HKLM "$R0"
WriteRegStr HKLM "$R0" "prefetchProcessName" "FIREFOX"
WriteRegStr HKLM "$R0\0" "name" "${CERTIFICATE_NAME}"
WriteRegStr HKLM "$R0\0" "issuer" "${CERTIFICATE_ISSUER}"
${If} ${RunningX64}

View File

@ -16,6 +16,7 @@ CPPSRCS = \
certificatecheck.cpp \
servicebase.cpp \
registrycertificates.cpp \
prefetch.cpp \
$(NULL)
# For debugging purposes only

View File

@ -14,6 +14,7 @@
#include "workmonitor.h"
#include "uachelper.h"
#include "updatehelper.h"
#include "prefetch.h"
SERVICE_STATUS gSvcStatus = { 0 };
SERVICE_STATUS_HANDLE gSvcStatusHandle = NULL;
@ -26,6 +27,18 @@ bool gServiceControlStopping = false;
BOOL GetLogDirectoryPath(WCHAR *path);
/**
* Wraps all commands that should be executed by the service on each install
* and upgrade.
*/
void
RunCommandsForEachInstallAndUpgrade()
{
LOG(("Running install/upgrade commands...\n"));
ClearKnownPrefetch();
LOG(("Finished install/upgrade commands\n"));
}
int
wmain(int argc, WCHAR **argv)
{
@ -52,7 +65,9 @@ wmain(int argc, WCHAR **argv)
}
LOG(("...\n"));
if (!SvcInstall(action)) {
bool ret = SvcInstall(action);
RunCommandsForEachInstallAndUpgrade();
if (!ret) {
LOG(("Could not install service (%d)\n", GetLastError()));
LogFinish();
return 1;
@ -69,7 +84,10 @@ wmain(int argc, WCHAR **argv)
LogInit(updatePath, L"maintenanceservice-install.log");
}
LOG(("Upgrading service if installed...\n"));
if (!SvcInstall(UpgradeSvc)) {
bool ret = SvcInstall(UpgradeSvc);
RunCommandsForEachInstallAndUpgrade();
if (!ret) {
LOG(("Could not upgrade service (%d)\n", GetLastError()));
LogFinish();
return 1;

View File

@ -0,0 +1,255 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Maintenance service prefetch cleaning.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Brian R. Bondy <netzen@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <shlwapi.h>
#include "servicebase.h"
#include "updatehelper.h"
#include "nsWindowsHelpers.h"
#define MAX_KEY_LENGTH 255
/**
* We found that prefetch actually causes large applications like Firefox
* to startup slower. This will get rid of the Windows prefetch files for
* applications like firefox (FIREFOX-*.pf files) and instead replace them
* with 0 byte files which are read only. Windows will not use prefetch
* if the associated prefetch file is a 0 byte read only file.
* Some of the problems with prefetch relating to Windows and Firefox:
* - Prefetch reads in a lot before we even run (fonts, plugins, etc...)
* - Prefetch happens before our code runs, so it delays UI showing.
* - Prefetch does not use windows readahead.
* Previous test data on an acer i7 laptop with a 5400rpm HD showed startup
* time difference was 1.6s (without prefetch) vs 2.6s+ with prefetch.
* The "Windows Internals" book mentions that prefetch can be disabled for
* the whole system only, so there is no other application specific way to
* disable it.
*
* @param prefetchProcessName The name of the process who's prefetch files
* should be cleared.
* @return TRUE if no errors occurred during the clear operation.
*/
BOOL
ClearPrefetch(LPCWSTR prefetchProcessName)
{
LOG(("Clearing prefetch files...\n"));
size_t prefetchProcessNameLength = wcslen(prefetchProcessName);
// Make sure we were passed in a safe value for the prefetch process name
// because it will be appended to a filepath and deleted.
// We check for < 2 to avoid things like "." as the path
// We check for a max path of MAX_PATH - 10 because we add \\ and '.EXE-*.pf'
if (wcsstr(prefetchProcessName, L"..") ||
wcsstr(prefetchProcessName, L"\\") ||
wcsstr(prefetchProcessName, L"*") ||
wcsstr(prefetchProcessName, L"/") ||
prefetchProcessNameLength < 2 ||
prefetchProcessNameLength >= (MAX_PATH - 10)) {
LOG(("Prefetch path to clear is not safe\n"));
return FALSE;
}
// Quick shortcut to make sure we don't try to clear multiple times for
// different FIREFOX installs.
static WCHAR lastPrefetchProcessName[MAX_PATH - 10] = { '\0' };
if (!wcscmp(lastPrefetchProcessName, prefetchProcessName)) {
LOG(("Already processed process name\n"));
return FALSE;
}
wcscpy(lastPrefetchProcessName, prefetchProcessName);
// Obtain the windows prefetch directory path.
WCHAR prefetchPath[MAX_PATH + 1];
if (!GetWindowsDirectoryW(prefetchPath,
sizeof(prefetchPath) /
sizeof(prefetchPath[0]))) {
LOG(("Could not obtain windows directory\n"));
return FALSE;
}
if (!PathAppendSafe(prefetchPath, L"prefetch")) {
LOG(("Could not obtain prefetch directory\n"));
return FALSE;
}
size_t prefetchDirLen = wcslen(prefetchPath);
WCHAR prefetchSearchFile[MAX_PATH + 1];
// We know this is safe based on the check at the start of this function
wsprintf(prefetchSearchFile, L"\\%s.EXE-*.pf", prefetchProcessName);
// Append the search file to the full path
wcscpy(prefetchPath + prefetchDirLen, prefetchSearchFile);
// Find the first path matching and get a find handle for future calls.
WIN32_FIND_DATAW findFileData;
HANDLE findHandle = FindFirstFileW(prefetchPath, &findFileData);
if (INVALID_HANDLE_VALUE == findHandle) {
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
LOG(("No files matching firefox.exe prefetch path.\n"));
return TRUE;
} else {
LOG(("Error finding firefox.exe prefetch files. (%d)\n",
GetLastError()));
return FALSE;
}
}
BOOL deletedAllFFPrefetch = TRUE;
do {
// Reset back to the prefetch directory, we know from an above check that
// we aren't exceeding MAX_PATH + 1 characters with prefetchDirLen + 1.
// From above we know: prefetchPath[prefetchDirLen] == L'\\';
prefetchPath[prefetchDirLen + 1] = L'\0';
// Get the new file's prefetch path
LPWSTR filenameOffsetInBuffer = prefetchPath + prefetchDirLen + 1;
if (wcslen(findFileData.cFileName) + prefetchDirLen + 1 > MAX_PATH) {
LOG(("Error appending prefetch path %ls, path is too long. (%d)\n",
findFileData.cFileName, GetLastError()));
deletedAllFFPrefetch = FALSE;
continue;
}
if (!PathAppendSafe(filenameOffsetInBuffer, findFileData.cFileName)) {
LOG(("Error appending prefetch path %ls. (%d)\n", findFileData.cFileName,
GetLastError()));
deletedAllFFPrefetch = FALSE;
continue;
}
// Delete the prefetch file and replace it with a blank read only file
HANDLE prefetchFile =
CreateFile(prefetchPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
if (INVALID_HANDLE_VALUE == prefetchFile) {
LOG(("Error replacing prefetch path %ls. (%d)\n", findFileData.cFileName,
GetLastError()));
deletedAllFFPrefetch = FALSE;
continue;
}
CloseHandle(prefetchFile);
DWORD attributes = GetFileAttributes(prefetchPath);
if (INVALID_FILE_ATTRIBUTES == attributes) {
LOG(("Could not get/set attributes on prefetch file: %ls. (%d)\n",
findFileData.cFileName, GetLastError()));
continue;
}
if (!SetFileAttributes(prefetchPath,
attributes | FILE_ATTRIBUTE_READONLY)) {
LOG(("Could not set read only on prefetch file: %ls. (%d)\n",
findFileData.cFileName, GetLastError()));
continue;
}
LOG(("Prefetch file cleared and set to read-only successfully: %ls\n",
prefetchPath));
} while (FindNextFileW(findHandle, &findFileData));
LOG(("Done searching prefetch paths. (%d)\n", GetLastError()));
// Cleanup after ourselves.
FindClose(findHandle);
return deletedAllFFPrefetch;
}
/**
* Clears all prefetch files specified in the installation dirctories
* @return FALSE if there was an error clearing the known prefetch directories
*/
BOOL
ClearKnownPrefetch()
{
// The service always uses the 64-bit registry key
HKEY baseKeyRaw;
LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
BASE_SERVICE_REG_KEY, 0,
KEY_READ | KEY_WOW64_64KEY, &baseKeyRaw);
if (retCode != ERROR_SUCCESS) {
LOG(("Could not open maintenance service base key. (%d)\n", retCode));
return FALSE;
}
nsAutoRegKey baseKey(baseKeyRaw);
// Get the number of subkeys.
DWORD subkeyCount = 0;
retCode = RegQueryInfoKeyW(baseKey, NULL, NULL, NULL, &subkeyCount, NULL,
NULL, NULL, NULL, NULL, NULL, NULL);
if (retCode != ERROR_SUCCESS) {
LOG(("Could not query info key: %d\n", retCode));
return FALSE;
}
// Enumerate the subkeys, each subkey represents an install
for (DWORD i = 0; i < subkeyCount; i++) {
WCHAR subkeyBuffer[MAX_KEY_LENGTH];
DWORD subkeyBufferCount = MAX_KEY_LENGTH;
retCode = RegEnumKeyExW(baseKey, i, subkeyBuffer,
&subkeyBufferCount, NULL,
NULL, NULL, NULL);
if (retCode != ERROR_SUCCESS) {
LOG(("Could not enum installations: %d\n", retCode));
return FALSE;
}
// Open the subkey
HKEY subKeyRaw;
retCode = RegOpenKeyExW(baseKey,
subkeyBuffer,
0,
KEY_READ | KEY_WOW64_64KEY,
&subKeyRaw);
nsAutoRegKey subKey(subKeyRaw);
if (retCode != ERROR_SUCCESS) {
LOG(("Could not open subkey: %d\n", retCode));
continue; // Try the next subkey
}
const int MAX_CHAR_COUNT = 256;
DWORD valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR);
WCHAR prefetchProcessName[MAX_CHAR_COUNT] = { L'\0' };
// Get the prefetch process name from the registry
retCode = RegQueryValueExW(subKey, L"prefetchProcessName", 0, NULL,
(LPBYTE)prefetchProcessName, &valueBufSize);
if (retCode != ERROR_SUCCESS) {
LOG(("Could not obtain process name from registry: %d\n", retCode));
continue; // Try the next subkey
}
// The value for prefetch process name comes from HKLM so is trusted.
// We will do some sanity checks on it though inside ClearPrefetch.
ClearPrefetch(prefetchProcessName);
}
return TRUE;
}

View File

@ -0,0 +1,38 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Maintenance service prefetch cleaning.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2012
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Brian R. Bondy <netzen@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
BOOL ClearKnownPrefetch();

View File

@ -10,6 +10,7 @@
#include "pathhash.h"
#include "nsWindowsHelpers.h"
#include "servicebase.h"
#include "updatehelper.h"
#define MAX_KEY_LENGTH 255
/**

View File

@ -24,6 +24,7 @@
#include "uachelper.h"
#include "updatehelper.h"
#include "errors.h"
#include "prefetch.h"
// Wait 15 minutes for an update operation to run at most.
// Updates usually take less than a minute so this seems like a
@ -482,6 +483,8 @@ ExecuteServiceCommand(int argc, LPWSTR *argv)
// because the service self updates itself and the service
// installer will stop the service.
LOG(("Service command %ls complete.\n", argv[2]));
} else if (!lstrcmpi(argv[2], L"clear-prefetch")) {
result = ClearKnownPrefetch();
} else {
LOG(("Service command not recognized: %ls.\n", argv[2]));
// result is already set to FALSE

View File

@ -34,6 +34,7 @@ CPPSRCS += updatehelper.cpp \
$(NULL)
EXPORTS = updatehelper.h \
updatehelper.cpp \
uachelper.h \
pathhash.h \
$(NULL)

View File

@ -16,13 +16,4 @@
BOOL CalculateRegistryPathFromFilePath(const LPCWSTR filePath,
LPWSTR registryPath);
// The test only fallback key, as its name implies, is only present on machines
// that will use automated tests. Since automated tests always run from a
// different directory for each test, the presence of this key bypasses the
// "This is a valid installation directory" check. This key also stores
// the allowed name and issuer for cert checks so that the cert check
// code can still be run unchanged.
#define TEST_ONLY_FALLBACK_KEY_PATH \
L"SOFTWARE\\Mozilla\\MaintenanceService\\3932ecacee736d366d6436db0f55bce4"
#endif

View File

@ -3,6 +3,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <windows.h>
// Needed for CreateToolhelp32Snapshot
#include <tlhelp32.h>
#ifndef ONLY_SERVICE_LAUNCHING
#include <stdio.h>
#include "shlobj.h"
#include "updatehelper.h"
@ -10,8 +15,6 @@
// Needed for PathAppendW
#include <shlwapi.h>
// Needed for CreateToolhelp32Snapshot
#include <tlhelp32.h>
#pragma comment(lib, "shlwapi.lib")
WCHAR* MakeCommandLine(int argc, WCHAR **argv);
@ -236,6 +239,8 @@ StartServiceUpdate(int argc, LPWSTR *argv)
return svcUpdateProcessStarted;
}
#endif
/**
* Executes a maintenance service command
*
@ -294,6 +299,8 @@ StartServiceCommand(int argc, LPCWSTR* argv)
return lastError;
}
#ifndef ONLY_SERVICE_LAUNCHING
/**
* Launch a service initiated action for a software update with the
* specified arguments.
@ -403,6 +410,8 @@ WriteStatusFailure(LPCWSTR updateDirPath, int errorCode)
return ok && wrote == toWrite;
}
#endif
/**
* Waits for a service to enter a stopped state.
* This function does not stop the service, it just blocks until the service
@ -535,6 +544,8 @@ WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds)
return lastServiceState;
}
#ifndef ONLY_SERVICE_LAUNCHING
/**
* Determines if there is at least one process running for the specified
* application. A match will be found across any session for any user.
@ -620,6 +631,8 @@ DoesFallbackKeyExist()
return TRUE;
}
#endif
/**
* Determines if the file system for the specified file handle is local
* @param file path to check the filesystem type for, must be at most MAX_PATH

View File

@ -15,5 +15,18 @@ DWORD WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds);
DWORD WaitForProcessExit(LPCWSTR filename, DWORD maxSeconds);
BOOL DoesFallbackKeyExist();
BOOL IsLocalFile(LPCWSTR file, BOOL &isLocal);
DWORD StartServiceCommand(int argc, LPCWSTR* argv);
#define SVC_NAME L"MozillaMaintenance"
#define BASE_SERVICE_REG_KEY \
L"SOFTWARE\\Mozilla\\MaintenanceService"
// The test only fallback key, as its name implies, is only present on machines
// that will use automated tests. Since automated tests always run from a
// different directory for each test, the presence of this key bypasses the
// "This is a valid installation directory" check. This key also stores
// the allowed name and issuer for cert checks so that the cert check
// code can still be run unchanged.
#define TEST_ONLY_FALLBACK_KEY_PATH \
BASE_SERVICE_REG_KEY L"\\3932ecacee736d366d6436db0f55bce4"