зеркало из https://github.com/mozilla/gecko-dev.git
Bug 711692 - Various fixes for intermittent failures. r=rstrong.
This commit is contained in:
Родитель
e152dc12ed
Коммит
6829b31d89
|
@ -35,9 +35,6 @@
|
|||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
# Required Plugins:
|
||||
# ServicesHelper Mozilla specific plugin that is located in /other-licenses/nsis
|
||||
|
||||
; Set verbosity to 3 (e.g. no script) to lessen the noise in the build logs
|
||||
!verbose 3
|
||||
|
||||
|
@ -170,18 +167,12 @@ Section "MaintenanceService"
|
|||
CreateDirectory $INSTDIR
|
||||
SetOutPath $INSTDIR
|
||||
|
||||
; If the service already exists, then stop it if it is running.
|
||||
ServicesHelper::IsInstalled "MozillaMaintenance"
|
||||
Pop $R9
|
||||
${If} $R9 == 1
|
||||
; Stop the maintenance service so we can overwrite any
|
||||
; binaries that it uses.
|
||||
ServicesHelper::Stop "MozillaMaintenance"
|
||||
${EndIf}
|
||||
|
||||
; If we don't have maintenanceservice.exe already installed
|
||||
; then keep that name. If we do use maintenanceservice_tmp.exe
|
||||
; which will auto install itself when you call it with the install parameter.
|
||||
; If the service already exists, then it will be stopped when upgrading it
|
||||
; via the maintenanceservice_tmp.exe command executed below.
|
||||
; The maintenanceservice_tmp.exe command will rename the file to
|
||||
; maintenanceservice.exe if maintenanceservice_tmp.exe is newer.
|
||||
; If the service does not exist yet, we install it and drop the file on
|
||||
; disk as maintenanceservice.exe directly.
|
||||
StrCpy $TempMaintServiceName "maintenanceservice.exe"
|
||||
IfFileExists "$INSTDIR\maintenanceservice.exe" 0 skipAlreadyExists
|
||||
StrCpy $TempMaintServiceName "maintenanceservice_tmp.exe"
|
||||
|
|
|
@ -50,8 +50,9 @@
|
|||
|
||||
SERVICE_STATUS gSvcStatus = { 0 };
|
||||
SERVICE_STATUS_HANDLE gSvcStatusHandle = NULL;
|
||||
HANDLE ghSvcStopEvent = NULL;
|
||||
BOOL gServiceStopping = FALSE;
|
||||
HANDLE gWorkDoneEvent = NULL;
|
||||
HANDLE gThread = NULL;
|
||||
bool gServiceControlStopping = false;
|
||||
|
||||
// logs are pretty small ~10 lines, so 5 seems reasonable.
|
||||
#define LOGS_TO_KEEP 5
|
||||
|
@ -129,33 +130,19 @@ wmain(int argc, WCHAR **argv)
|
|||
}
|
||||
|
||||
SERVICE_TABLE_ENTRYW DispatchTable[] = {
|
||||
{ SVC_NAME, (LPSERVICE_MAIN_FUNCTION) SvcMain },
|
||||
{ SVC_NAME, (LPSERVICE_MAIN_FUNCTIONW) SvcMain },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
// This call returns when the service has stopped.
|
||||
// The process should simply terminate when the call returns.
|
||||
if (!StartServiceCtrlDispatcher(DispatchTable)) {
|
||||
if (!StartServiceCtrlDispatcherW(DispatchTable)) {
|
||||
LOG(("StartServiceCtrlDispatcher failed (%d)\n", GetLastError()));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper callback for the monitoring thread.
|
||||
*
|
||||
* @param param Unused thread callback parameter
|
||||
*/
|
||||
DWORD
|
||||
WINAPI StartMaintenanceServiceThread(LPVOID param)
|
||||
{
|
||||
ThreadData *threadData = reinterpret_cast<ThreadData*>(param);
|
||||
ExecuteServiceCommand(threadData->argc, threadData->argv);
|
||||
delete threadData;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the base path where logs should be stored
|
||||
*
|
||||
|
@ -163,7 +150,7 @@ WINAPI StartMaintenanceServiceThread(LPVOID param)
|
|||
* @return TRUE if successful.
|
||||
*/
|
||||
BOOL
|
||||
GetLogDirectoryPath(WCHAR *path)
|
||||
GetLogDirectoryPath(WCHAR *path)
|
||||
{
|
||||
HRESULT hr = SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL,
|
||||
SHGFP_TYPE_CURRENT, path);
|
||||
|
@ -218,7 +205,7 @@ GetBackupLogPath(LPWSTR path, LPCWSTR basePath, int logNumber)
|
|||
* @param numLogsToKeep The number of logs to keep
|
||||
*/
|
||||
void
|
||||
BackupOldLogs(LPCWSTR basePath, int numLogsToKeep)
|
||||
BackupOldLogs(LPCWSTR basePath, int numLogsToKeep)
|
||||
{
|
||||
WCHAR oldPath[MAX_PATH + 1];
|
||||
WCHAR newPath[MAX_PATH + 1];
|
||||
|
@ -231,17 +218,51 @@ BackupOldLogs(LPCWSTR basePath, int numLogsToKeep)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!MoveFileEx(oldPath, newPath, MOVEFILE_REPLACE_EXISTING)) {
|
||||
if (!MoveFileExW(oldPath, newPath, MOVEFILE_REPLACE_EXISTING)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the service is shutdown once all work is complete.
|
||||
* There is an issue on XP SP2 and below where the service can hang
|
||||
* in a stop pending state even though the SCM is notified of a stopped
|
||||
* state. Control *should* be returned to StartServiceCtrlDispatcher from the
|
||||
* call to SetServiceStatus on a stopped state in the wmain thread.
|
||||
* Sometimes this is not the case though. This thread will terminate the process
|
||||
* if it has been 5 seconds after all work is done and the process is still not
|
||||
* terminated. This thread is only started once a stopped state was sent to the
|
||||
* SCM. The stop pending hang can be reproduced intermittently even if you set
|
||||
* a stopped state dirctly and never set a stop pending state. It is safe to
|
||||
* forcefully terminate the process ourselves since all work is done once we
|
||||
* start this thread.
|
||||
*/
|
||||
DWORD WINAPI
|
||||
EnsureProcessTerminatedThread(LPVOID)
|
||||
{
|
||||
Sleep(5000);
|
||||
exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
StartTerminationThread()
|
||||
{
|
||||
// If the process does not self terminate like it should, this thread
|
||||
// will terminate the process after 5 seconds.
|
||||
HANDLE thread = CreateThread(NULL, 0, EnsureProcessTerminatedThread,
|
||||
NULL, 0, NULL);
|
||||
if (thread) {
|
||||
CloseHandle(thread);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point when running as a service.
|
||||
*/
|
||||
void WINAPI
|
||||
SvcMain(DWORD dwArgc, LPWSTR *lpszArgv)
|
||||
void WINAPI
|
||||
SvcMain(DWORD argc, LPWSTR *argv)
|
||||
{
|
||||
// Setup logging, and backup the old logs
|
||||
WCHAR updatePath[MAX_PATH + 1];
|
||||
|
@ -258,51 +279,45 @@ SvcMain(DWORD dwArgc, LPWSTR *lpszArgv)
|
|||
gSvcStatusHandle = RegisterServiceCtrlHandlerW(SVC_NAME, SvcCtrlHandler);
|
||||
if (!gSvcStatusHandle) {
|
||||
LOG(("RegisterServiceCtrlHandler failed (%d)\n", GetLastError()));
|
||||
return;
|
||||
ExecuteServiceCommand(argc, argv);
|
||||
LogFinish();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// These SERVICE_STATUS members remain as set here
|
||||
// These values will be re-used later in calls involving gSvcStatus
|
||||
gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||
gSvcStatus.dwServiceSpecificExitCode = 0;
|
||||
|
||||
// Report initial status to the SCM
|
||||
ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
|
||||
|
||||
// Perform service-specific initialization and work.
|
||||
SvcInit(dwArgc, lpszArgv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Service initialization.
|
||||
*/
|
||||
void
|
||||
SvcInit(DWORD argc, LPWSTR *argv)
|
||||
{
|
||||
// Create an event. The control handler function, SvcCtrlHandler,
|
||||
// signals this event when it receives the stop control code.
|
||||
ghSvcStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (NULL == ghSvcStopEvent) {
|
||||
// This event will be used to tell the SvcCtrlHandler when the work is
|
||||
// done for when a stop comamnd is manually issued.
|
||||
gWorkDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!gWorkDoneEvent) {
|
||||
ReportSvcStatus(SERVICE_STOPPED, 1, 0);
|
||||
StartTerminationThread();
|
||||
return;
|
||||
}
|
||||
|
||||
ThreadData *threadData = new ThreadData();
|
||||
threadData->argc = argc;
|
||||
threadData->argv = argv;
|
||||
|
||||
DWORD threadID;
|
||||
HANDLE thread = CreateThread(NULL, 0, StartMaintenanceServiceThread,
|
||||
threadData, 0, &threadID);
|
||||
|
||||
// Report running status when initialization is complete.
|
||||
// Initialization complete and we're about to start working on
|
||||
// the actual command. Report the service state as running to the SCM.
|
||||
ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
|
||||
|
||||
// Perform work until service stops.
|
||||
for(;;) {
|
||||
// Check whether to stop the service.
|
||||
WaitForSingleObject(ghSvcStopEvent, INFINITE);
|
||||
// The service command was executed, stop logging and set an event
|
||||
// to indicate the work is done in case someone is waiting on a
|
||||
// service stop operation.
|
||||
ExecuteServiceCommand(argc, argv);
|
||||
LogFinish();
|
||||
|
||||
SetEvent(gWorkDoneEvent);
|
||||
|
||||
// If we aren't already in a stopping state then tell the SCM we're stopped
|
||||
// now. If we are already in a stopping state then the SERVICE_STOPPED state
|
||||
// will be set by the SvcCtrlHandler.
|
||||
if (!gServiceControlStopping) {
|
||||
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
|
||||
return;
|
||||
StartTerminationThread();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -320,15 +335,16 @@ ReportSvcStatus(DWORD currentState,
|
|||
{
|
||||
static DWORD dwCheckPoint = 1;
|
||||
|
||||
// Fill in the SERVICE_STATUS structure.
|
||||
gSvcStatus.dwCurrentState = currentState;
|
||||
gSvcStatus.dwWin32ExitCode = exitCode;
|
||||
gSvcStatus.dwWaitHint = waitHint;
|
||||
|
||||
if (SERVICE_START_PENDING == currentState) {
|
||||
if (SERVICE_START_PENDING == currentState ||
|
||||
SERVICE_STOP_PENDING == currentState) {
|
||||
gSvcStatus.dwControlsAccepted = 0;
|
||||
} else {
|
||||
gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
|
||||
gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
|
||||
SERVICE_ACCEPT_SHUTDOWN;
|
||||
}
|
||||
|
||||
if ((SERVICE_RUNNING == currentState) ||
|
||||
|
@ -342,6 +358,23 @@ ReportSvcStatus(DWORD currentState,
|
|||
SetServiceStatus(gSvcStatusHandle, &gSvcStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Since the SvcCtrlHandler should only spend at most 30 seconds before
|
||||
* returning, this function does the service stop work for the SvcCtrlHandler.
|
||||
*/
|
||||
DWORD WINAPI
|
||||
StopServiceAndWaitForCommandThread(LPVOID)
|
||||
{
|
||||
do {
|
||||
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 1000);
|
||||
} while(WaitForSingleObject(gWorkDoneEvent, 100) == WAIT_TIMEOUT);
|
||||
CloseHandle(gWorkDoneEvent);
|
||||
gWorkDoneEvent = NULL;
|
||||
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
|
||||
StartTerminationThread();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by SCM whenever a control code is sent to the service
|
||||
* using the ControlService function.
|
||||
|
@ -349,18 +382,34 @@ ReportSvcStatus(DWORD currentState,
|
|||
void WINAPI
|
||||
SvcCtrlHandler(DWORD dwCtrl)
|
||||
{
|
||||
// After a SERVICE_CONTROL_STOP there should be no more commands sent to
|
||||
// the SvcCtrlHandler.
|
||||
if (gServiceControlStopping) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle the requested control code.
|
||||
switch(dwCtrl) {
|
||||
case SERVICE_CONTROL_STOP:
|
||||
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
|
||||
// Signal the service to stop.
|
||||
SetEvent(ghSvcStopEvent);
|
||||
ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
|
||||
LogFinish();
|
||||
case SERVICE_CONTROL_SHUTDOWN:
|
||||
case SERVICE_CONTROL_STOP: {
|
||||
gServiceControlStopping = true;
|
||||
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 1000);
|
||||
|
||||
// The SvcCtrlHandler thread should not spend more than 30 seconds in
|
||||
// shutdown so we spawn a new thread for stopping the service
|
||||
HANDLE thread = CreateThread(NULL, 0, StopServiceAndWaitForCommandThread,
|
||||
NULL, 0, NULL);
|
||||
if (thread) {
|
||||
CloseHandle(thread);
|
||||
} else {
|
||||
// Couldn't start the thread so just call the stop ourselves.
|
||||
// If it happens to take longer than 30 seconds the caller will
|
||||
// get an error.
|
||||
StopServiceAndWaitForCommandThread(NULL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SERVICE_CONTROL_INTERROGATE:
|
||||
break;
|
||||
default:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,3 @@ void WINAPI SvcCtrlHandler(DWORD dwCtrl);
|
|||
void ReportSvcStatus(DWORD dwCurrentState,
|
||||
DWORD dwWin32ExitCode,
|
||||
DWORD dwWaitHint);
|
||||
struct ThreadData {
|
||||
LPWSTR *argv;
|
||||
DWORD argc;
|
||||
};
|
||||
|
|
|
@ -189,12 +189,13 @@ SvcInstall(SvcInstallAction action)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
if (!DeleteFile(serviceConfig.lpBinaryPathName)) {
|
||||
LOG(("Could not delete old service binary file. (%d)\n", GetLastError()));
|
||||
if (!DeleteFileW(serviceConfig.lpBinaryPathName)) {
|
||||
LOG(("Could not delete old service binary file %ls. (%d)\n",
|
||||
serviceConfig.lpBinaryPathName, GetLastError()));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!CopyFile(newServiceBinaryPath,
|
||||
if (!CopyFileW(newServiceBinaryPath,
|
||||
serviceConfig.lpBinaryPathName, FALSE)) {
|
||||
LOG(("Could not overwrite old service binary file. "
|
||||
"This should never happen, but if it does the next upgrade will fix"
|
||||
|
@ -207,7 +208,7 @@ SvcInstall(SvcInstallAction action)
|
|||
// We made a copy of ourselves to the existing location.
|
||||
// The tmp file (the process of which we are executing right now) will be
|
||||
// left over. Attempt to delete the file on the next reboot.
|
||||
MoveFileEx(newServiceBinaryPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
|
||||
MoveFileExW(newServiceBinaryPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
|
||||
|
||||
// Setup the new module path
|
||||
wcsncpy(newServiceBinaryPath, serviceConfig.lpBinaryPathName, MAX_PATH);
|
||||
|
@ -216,7 +217,7 @@ SvcInstall(SvcInstallAction action)
|
|||
// We don't need to copy ourselves to the existing location.
|
||||
// The tmp file (the process of which we are executing right now) will be
|
||||
// left over. Attempt to delete the file on the next reboot.
|
||||
MoveFileEx(newServiceBinaryPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
|
||||
MoveFileExW(newServiceBinaryPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
|
||||
|
||||
return TRUE; // nothing to do, we already have a newer service installed
|
||||
}
|
||||
|
@ -274,10 +275,24 @@ StopService()
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
// Stop logging before stopping the service.
|
||||
LogFinish();
|
||||
LOG(("Sending stop request...\n"));
|
||||
SERVICE_STATUS status;
|
||||
if (!ControlService(schService, SERVICE_CONTROL_STOP, &status)) {
|
||||
LOG(("Error sending stop request: %d\n", GetLastError()));
|
||||
}
|
||||
|
||||
return WaitForServiceStop(SVC_NAME, 60);
|
||||
schSCManager.reset();
|
||||
schService.reset();
|
||||
|
||||
LOG(("Waiting for service stop...\n"));
|
||||
DWORD lastState = WaitForServiceStop(SVC_NAME, 30);
|
||||
|
||||
// The service can be in a stopped state but the exe still in use
|
||||
// so make sure the process is really gone before proceeding
|
||||
WaitForProcessExit(L"maintenanceservice.exe", 30);
|
||||
LOG(("Done waiting for service stop, last service state: %d\n", lastState));
|
||||
|
||||
return lastState == SERVICE_STOPPED;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -57,8 +57,6 @@
|
|||
#include "uachelper.h"
|
||||
#include "updatehelper.h"
|
||||
|
||||
extern HANDLE ghSvcStopEvent;
|
||||
|
||||
// Wait 15 minutes for an update operation to run at most.
|
||||
// Updates usually take less than a minute so this seems like a
|
||||
// significantly large and safe amount of time to wait.
|
||||
|
@ -75,6 +73,45 @@ const int SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS = 16001;
|
|||
const int SERVICE_UPDATER_SIGN_ERROR = 16002;
|
||||
const int SERVICE_UPDATER_COMPARE_ERROR = 16003;
|
||||
const int SERVICE_UPDATER_IDENTITY_ERROR = 16004;
|
||||
const int SERVICE_STILL_APPLYING_ON_SUCCESS = 16005;
|
||||
const int SERVICE_STILL_APPLYING_ON_FAILURE = 16006;
|
||||
|
||||
/*
|
||||
* Read the update.status file and sets isApplying to true if
|
||||
* the status is set to applying
|
||||
*
|
||||
* @param updateDirPath The directory where update.status is stored
|
||||
* @param isApplying Out parameter for specifying if the status
|
||||
* is set to applying or not.
|
||||
* @return TRUE if the information was filled.
|
||||
*/
|
||||
static BOOL
|
||||
IsStatusApplying(LPCWSTR updateDirPath, BOOL &isApplying)
|
||||
{
|
||||
isApplying = FALSE;
|
||||
WCHAR updateStatusFilePath[MAX_PATH + 1];
|
||||
wcscpy(updateStatusFilePath, updateDirPath);
|
||||
if (!PathAppendSafe(updateStatusFilePath, L"update.status")) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
nsAutoHandle statusFile(CreateFileW(updateStatusFilePath, GENERIC_READ,
|
||||
FILE_SHARE_READ |
|
||||
FILE_SHARE_WRITE |
|
||||
FILE_SHARE_DELETE,
|
||||
NULL, OPEN_EXISTING, 0, NULL));
|
||||
char buf[32] = { 0 };
|
||||
DWORD read;
|
||||
if (!ReadFile(statusFile, buf, sizeof(buf), &read, NULL)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
const char kApplying[] = "applying";
|
||||
isApplying = strncmp(buf, kApplying,
|
||||
sizeof(kApplying) - 1) == 0;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Runs an update process as the service using the SYSTEM account.
|
||||
|
@ -124,25 +161,22 @@ StartUpdateProcess(int argc,
|
|||
// because of background updates.
|
||||
if (PathGetSiblingFilePath(updaterINI, argv[0], L"updater.ini") &&
|
||||
PathGetSiblingFilePath(updaterINITemp, argv[0], L"updater.tmp")) {
|
||||
selfHandlePostUpdate = MoveFileEx(updaterINI, updaterINITemp,
|
||||
MOVEFILE_REPLACE_EXISTING);
|
||||
selfHandlePostUpdate = MoveFileExW(updaterINI, updaterINITemp,
|
||||
MOVEFILE_REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
// Add an env var for MOZ_USING_SERVICE so the updater.exe can
|
||||
// do anything special that it needs to do for service updates.
|
||||
// Search in updater.cpp for more info on MOZ_USING_SERVICE
|
||||
// for more info.
|
||||
WCHAR envVarString[32];
|
||||
wsprintf(envVarString, L"MOZ_USING_SERVICE=1");
|
||||
_wputenv(envVarString);
|
||||
|
||||
// Empty value on _wputenv is how you remove an env variable in Windows
|
||||
// Search in updater.cpp for more info on MOZ_USING_SERVICE.
|
||||
putenv(const_cast<char*>("MOZ_USING_SERVICE=1"));
|
||||
LOG(("Starting service with cmdline: %ls\n", cmdLine));
|
||||
processStarted = CreateProcessW(argv[0], cmdLine,
|
||||
NULL, NULL, FALSE,
|
||||
CREATE_DEFAULT_ERROR_MODE,
|
||||
NULL,
|
||||
NULL, &si, &pi);
|
||||
_wputenv(L"MOZ_USING_SERVICE=");
|
||||
// Empty value on putenv is how you remove an env variable in Windows
|
||||
putenv(const_cast<char*>("MOZ_USING_SERVICE="));
|
||||
|
||||
BOOL updateWasSuccessful = FALSE;
|
||||
if (processStarted) {
|
||||
|
@ -166,6 +200,31 @@ StartUpdateProcess(int argc,
|
|||
}
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
// Check just in case updater.exe didn't change the status from
|
||||
// applying. If this is the case we report an error.
|
||||
BOOL isApplying = FALSE;
|
||||
if (IsStatusApplying(argv[1], isApplying) && isApplying) {
|
||||
if (updateWasSuccessful) {
|
||||
LOG(("update.status is still applying even know update "
|
||||
" was successful.\n"));
|
||||
if (!WriteStatusFailure(argv[1],
|
||||
SERVICE_STILL_APPLYING_ON_SUCCESS)) {
|
||||
LOG(("Could not write update.status still applying on"
|
||||
" success error.\n"));
|
||||
}
|
||||
// Since we still had applying we know updater.exe didn't do its
|
||||
// job correctly.
|
||||
updateWasSuccessful = FALSE;
|
||||
} else {
|
||||
LOG(("update.status is still applying and update was not successful.\n"));
|
||||
if (!WriteStatusFailure(argv[1],
|
||||
SERVICE_STILL_APPLYING_ON_FAILURE)) {
|
||||
LOG(("Could not write update.status still applying on"
|
||||
" success error.\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DWORD lastError = GetLastError();
|
||||
LOG(("Could not create process as current user, "
|
||||
|
@ -177,7 +236,7 @@ StartUpdateProcess(int argc,
|
|||
// We use it ourselves, and also we want it back in case we had any type
|
||||
// of error so that the normal update process can use it.
|
||||
if (selfHandlePostUpdate) {
|
||||
MoveFileEx(updaterINITemp, updaterINI, MOVEFILE_REPLACE_EXISTING);
|
||||
MoveFileExW(updaterINITemp, updaterINI, MOVEFILE_REPLACE_EXISTING);
|
||||
|
||||
// Only run the PostUpdate if the update was successful
|
||||
if (updateWasSuccessful && argc > 2) {
|
||||
|
@ -211,7 +270,7 @@ StartUpdateProcess(int argc,
|
|||
* @return TRUE if the update was successful.
|
||||
*/
|
||||
BOOL
|
||||
ProessSoftwareUpdateCommand(DWORD argc, LPWSTR *argv)
|
||||
ProcessSoftwareUpdateCommand(DWORD argc, LPWSTR *argv)
|
||||
{
|
||||
BOOL result = TRUE;
|
||||
if (argc < 3) {
|
||||
|
@ -362,11 +421,10 @@ ProessSoftwareUpdateCommand(DWORD argc, LPWSTR *argv)
|
|||
* @return FALSE if there was an error executing the service command.
|
||||
*/
|
||||
BOOL
|
||||
ExecuteServiceCommand(int argc, LPWSTR *argv)
|
||||
ExecuteServiceCommand(int argc, LPWSTR *argv)
|
||||
{
|
||||
if (argc < 3) {
|
||||
LOG(("Not enough command line arguments to execute a service command\n"));
|
||||
SetEvent(ghSvcStopEvent);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -384,7 +442,7 @@ ExecuteServiceCommand(int argc, LPWSTR *argv)
|
|||
|
||||
BOOL result = FALSE;
|
||||
if (!lstrcmpi(argv[2], L"software-update")) {
|
||||
result = ProessSoftwareUpdateCommand(argc - 3, argv + 3);
|
||||
result = ProcessSoftwareUpdateCommand(argc - 3, argv + 3);
|
||||
// We might not reach here if the service install succeeded
|
||||
// because the service self updates itself and the service
|
||||
// installer will stop the service.
|
||||
|
@ -396,6 +454,5 @@ ExecuteServiceCommand(int argc, LPWSTR *argv)
|
|||
|
||||
LOG(("service command %ls complete with result: %ls.\n",
|
||||
argv[1], (result ? L"Success" : L"Failure")));
|
||||
SetEvent(ghSvcStopEvent);
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ LPCTSTR UACHelper::PrivsToDisable[] = {
|
|||
* @return TRUE if the OS is vista or later.
|
||||
*/
|
||||
BOOL
|
||||
UACHelper::IsVistaOrLater()
|
||||
UACHelper::IsVistaOrLater()
|
||||
{
|
||||
// Check if we are running Vista or later.
|
||||
OSVERSIONINFO osInfo;
|
||||
|
@ -127,7 +127,7 @@ UACHelper::OpenUserToken(DWORD sessionID)
|
|||
* Caller should close the handle.
|
||||
*/
|
||||
HANDLE
|
||||
UACHelper::OpenLinkedToken(HANDLE token)
|
||||
UACHelper::OpenLinkedToken(HANDLE token)
|
||||
{
|
||||
// Magic below...
|
||||
// UAC creates 2 tokens. One is the restricted token which we have.
|
||||
|
|
|
@ -42,10 +42,11 @@
|
|||
|
||||
// Needed for PathAppendW
|
||||
#include <shlwapi.h>
|
||||
// Needed for CreateToolhelp32Snapshot
|
||||
#include <tlhelp32.h>
|
||||
#pragma comment(lib, "shlwapi.lib")
|
||||
|
||||
WCHAR*
|
||||
MakeCommandLine(int argc, WCHAR **argv);
|
||||
WCHAR* MakeCommandLine(int argc, WCHAR **argv);
|
||||
BOOL PathAppendSafe(LPWSTR base, LPCWSTR extra);
|
||||
|
||||
/**
|
||||
|
@ -258,66 +259,71 @@ StartServiceUpdate(int argc, LPWSTR *argv)
|
|||
BOOL svcUpdateProcessStarted = CreateProcessW(maintserviceInstallerPath,
|
||||
cmdLine,
|
||||
NULL, NULL, FALSE,
|
||||
CREATE_DEFAULT_ERROR_MODE |
|
||||
CREATE_UNICODE_ENVIRONMENT,
|
||||
0,
|
||||
NULL, argv[2], &si, &pi);
|
||||
if (svcUpdateProcessStarted) {
|
||||
// Wait on the process to finish updating to avoid problems with
|
||||
// tests that are running. maintenanceservice_installer.exe
|
||||
// will execute very fast anyway.
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
||||
return svcUpdateProcessStarted;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes a maintenance service command
|
||||
*
|
||||
* @param argc The total number of arguments in argv
|
||||
* @param argv An array of null terminated strings to pass to the service,
|
||||
* @return TRUE if the service command was started.
|
||||
* @return ERROR_SUCCESS if the service command was started.
|
||||
* Less than 16000, a windows system error code from StartServiceW
|
||||
* More than 20000, 20000 + the last state of the service constant if
|
||||
* the last state is something other than stopped.
|
||||
* 17001 if the SCM could not be opened
|
||||
* 17002 if the service could not be opened
|
||||
*/
|
||||
BOOL
|
||||
StartServiceCommand(int argc, LPCWSTR* argv)
|
||||
DWORD
|
||||
StartServiceCommand(int argc, LPCWSTR* argv)
|
||||
{
|
||||
DWORD lastState = WaitForServiceStop(SVC_NAME, 5);
|
||||
if (lastState != SERVICE_STOPPED) {
|
||||
return 20000 + lastState;
|
||||
}
|
||||
|
||||
// Get a handle to the SCM database.
|
||||
SC_HANDLE serviceManager = OpenSCManager(NULL, NULL,
|
||||
SC_MANAGER_CONNECT |
|
||||
SC_MANAGER_ENUMERATE_SERVICE);
|
||||
if (!serviceManager) {
|
||||
return FALSE;
|
||||
return 17001;
|
||||
}
|
||||
|
||||
// Get a handle to the service.
|
||||
SC_HANDLE service = OpenServiceW(serviceManager,
|
||||
SVC_NAME,
|
||||
SERVICE_QUERY_STATUS | SERVICE_START);
|
||||
SERVICE_START);
|
||||
if (!service) {
|
||||
CloseServiceHandle(serviceManager);
|
||||
return FALSE;
|
||||
return 17002;
|
||||
}
|
||||
|
||||
// Make sure the service is not stopped.
|
||||
SERVICE_STATUS_PROCESS ssp;
|
||||
DWORD bytesNeeded;
|
||||
if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
|
||||
sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded)) {
|
||||
CloseServiceHandle(service);
|
||||
CloseServiceHandle(serviceManager);
|
||||
return FALSE;
|
||||
// Wait at most 5 seconds trying to start the service in case of errors
|
||||
// like ERROR_SERVICE_DATABASE_LOCKED or ERROR_SERVICE_REQUEST_TIMEOUT.
|
||||
const DWORD maxWaitMS = 5000;
|
||||
DWORD currentWaitMS = 0;
|
||||
DWORD lastError = ERROR_SUCCESS;
|
||||
while (currentWaitMS < maxWaitMS) {
|
||||
BOOL result = StartServiceW(service, argc, argv);
|
||||
if (result) {
|
||||
lastError = ERROR_SUCCESS;
|
||||
break;
|
||||
} else {
|
||||
lastError = GetLastError();
|
||||
}
|
||||
Sleep(100);
|
||||
currentWaitMS += 100;
|
||||
}
|
||||
|
||||
// The service is already in use.
|
||||
if (ssp.dwCurrentState != SERVICE_STOPPED) {
|
||||
CloseServiceHandle(service);
|
||||
CloseServiceHandle(serviceManager);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return StartServiceW(service, argc, argv);
|
||||
CloseServiceHandle(service);
|
||||
CloseServiceHandle(serviceManager);
|
||||
return lastError;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -328,16 +334,16 @@ StartServiceCommand(int argc, LPCWSTR* argv)
|
|||
* @param argc The total number of arguments in argv
|
||||
* @param argv An array of null terminated strings to pass to the exePath,
|
||||
* argv[0] must be the path to the updater.exe
|
||||
* @return TRUE if successful
|
||||
* @return ERROR_SUCCESS if successful
|
||||
*/
|
||||
BOOL
|
||||
DWORD
|
||||
LaunchServiceSoftwareUpdateCommand(DWORD argc, LPCWSTR* argv)
|
||||
{
|
||||
// The service command is the same as the updater.exe command line except
|
||||
// it has 2 extra args: 1) The Path to udpater.exe, and 2) the command
|
||||
// being executed which is "software-update"
|
||||
LPCWSTR *updaterServiceArgv = new LPCWSTR[argc + 2];
|
||||
updaterServiceArgv[0] = L"maintenanceservice.exe";
|
||||
updaterServiceArgv[0] = L"MozillaMaintenance";
|
||||
updaterServiceArgv[1] = L"software-update";
|
||||
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
|
@ -346,9 +352,9 @@ LaunchServiceSoftwareUpdateCommand(DWORD argc, LPCWSTR* argv)
|
|||
|
||||
// Execute the service command by starting the service with
|
||||
// the passed in arguments.
|
||||
BOOL result = StartServiceCommand(argc + 2, updaterServiceArgv);
|
||||
DWORD ret = StartServiceCommand(argc + 2, updaterServiceArgv);
|
||||
delete[] updaterServiceArgv;
|
||||
return result;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -405,7 +411,7 @@ WriteStatusPending(LPCWSTR updateDirPath)
|
|||
* @return TRUE if successful
|
||||
*/
|
||||
BOOL
|
||||
WriteStatusFailure(LPCWSTR updateDirPath, int errorCode)
|
||||
WriteStatusFailure(LPCWSTR updateDirPath, int errorCode)
|
||||
{
|
||||
WCHAR updateStatusFilePath[MAX_PATH + 1];
|
||||
wcscpy(updateStatusFilePath, updateDirPath);
|
||||
|
@ -434,20 +440,53 @@ WriteStatusFailure(LPCWSTR updateDirPath, int errorCode)
|
|||
* This function does not stop the service, it just blocks until the service
|
||||
* is stopped.
|
||||
*
|
||||
* @param serviceName The service to wait for.
|
||||
* @param maxWaitSeconds The maximum number of seconds to wait
|
||||
* @return TRUE if the service was stopped after waiting at most maxWaitSeconds
|
||||
* FALSE on an error or when the service was not stopped
|
||||
* @param serviceName The service to wait for.
|
||||
* @param maxWaitSeconds The maximum number of seconds to wait
|
||||
* @return state of the service after a timeout or when stopped.
|
||||
* A value of 255 is returned for an error. Typical values are:
|
||||
* SERVICE_STOPPED 0x00000001
|
||||
* SERVICE_START_PENDING 0x00000002
|
||||
* SERVICE_STOP_PENDING 0x00000003
|
||||
* SERVICE_RUNNING 0x00000004
|
||||
* SERVICE_CONTINUE_PENDING 0x00000005
|
||||
* SERVICE_PAUSE_PENDING 0x00000006
|
||||
* SERVICE_PAUSED 0x00000007
|
||||
* last status not set 0x000000CF
|
||||
* Could no query status 0x000000DF
|
||||
* Could not open service, access denied 0x000000EB
|
||||
* Could not open service, invalid handle 0x000000EC
|
||||
* Could not open service, invalid name 0x000000ED
|
||||
* Could not open service, does not exist 0x000000EE
|
||||
* Could not open service, other error 0x000000EF
|
||||
* Could not open SCM, access denied 0x000000FD
|
||||
* Could not open SCM, database does not exist 0x000000FE;
|
||||
* Could not open SCM, other error 0x000000FF;
|
||||
* Note: The strange choice of error codes above SERVICE_PAUSED are chosen
|
||||
* in case Windows comes out with other service stats higher than 7, they
|
||||
* would likely call it 8 and above. JS code that uses this in TestAUSHelper
|
||||
* only handles values up to 255 so that's why we don't use GetLastError
|
||||
* directly.
|
||||
*/
|
||||
BOOL
|
||||
WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds)
|
||||
DWORD
|
||||
WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds)
|
||||
{
|
||||
// 0x000000CF is defined above to be not set
|
||||
DWORD lastServiceState = 0x000000CF;
|
||||
|
||||
// Get a handle to the SCM database.
|
||||
SC_HANDLE serviceManager = OpenSCManager(NULL, NULL,
|
||||
SC_MANAGER_CONNECT |
|
||||
SC_MANAGER_ENUMERATE_SERVICE);
|
||||
if (!serviceManager) {
|
||||
return FALSE;
|
||||
DWORD lastError = GetLastError();
|
||||
switch(lastError) {
|
||||
case ERROR_ACCESS_DENIED:
|
||||
return 0x000000FD;
|
||||
case ERROR_DATABASE_DOES_NOT_EXIST:
|
||||
return 0x000000FE;
|
||||
default:
|
||||
return 0x000000FF;
|
||||
}
|
||||
}
|
||||
|
||||
// Get a handle to the service.
|
||||
|
@ -455,31 +494,140 @@ WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds)
|
|||
serviceName,
|
||||
SERVICE_QUERY_STATUS);
|
||||
if (!service) {
|
||||
DWORD lastError = GetLastError();
|
||||
CloseServiceHandle(serviceManager);
|
||||
return FALSE;
|
||||
switch(lastError) {
|
||||
case ERROR_ACCESS_DENIED:
|
||||
return 0x000000EB;
|
||||
case ERROR_INVALID_HANDLE:
|
||||
return 0x000000EC;
|
||||
case ERROR_INVALID_NAME:
|
||||
return 0x000000ED;
|
||||
case ERROR_SERVICE_DOES_NOT_EXIST:
|
||||
return 0x000000EE;
|
||||
default:
|
||||
return 0x000000EF;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL gotStop = FALSE;
|
||||
DWORD currentWaitMS = 0;
|
||||
SERVICE_STATUS_PROCESS ssp;
|
||||
ssp.dwCurrentState = lastServiceState;
|
||||
while (currentWaitMS < maxWaitSeconds * 1000) {
|
||||
// Make sure the service is not stopped.
|
||||
SERVICE_STATUS_PROCESS ssp;
|
||||
DWORD bytesNeeded;
|
||||
if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
|
||||
sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded)) {
|
||||
DWORD lastError = GetLastError();
|
||||
switch (lastError) {
|
||||
case ERROR_INVALID_HANDLE:
|
||||
ssp.dwCurrentState = 0x000000D9;
|
||||
break;
|
||||
case ERROR_ACCESS_DENIED:
|
||||
ssp.dwCurrentState = 0x000000DA;
|
||||
break;
|
||||
case ERROR_INSUFFICIENT_BUFFER:
|
||||
ssp.dwCurrentState = 0x000000DB;
|
||||
break;
|
||||
case ERROR_INVALID_PARAMETER:
|
||||
ssp.dwCurrentState = 0x000000DC;
|
||||
break;
|
||||
case ERROR_INVALID_LEVEL:
|
||||
ssp.dwCurrentState = 0x000000DD;
|
||||
break;
|
||||
case ERROR_SHUTDOWN_IN_PROGRESS:
|
||||
ssp.dwCurrentState = 0x000000DE;
|
||||
break;
|
||||
// These 3 errors can occur when the service is not yet stopped but
|
||||
// it is stopping.
|
||||
case ERROR_INVALID_SERVICE_CONTROL:
|
||||
case ERROR_SERVICE_CANNOT_ACCEPT_CTRL:
|
||||
case ERROR_SERVICE_NOT_ACTIVE:
|
||||
currentWaitMS += 50;
|
||||
Sleep(50);
|
||||
continue;
|
||||
default:
|
||||
ssp.dwCurrentState = 0x000000DF;
|
||||
}
|
||||
|
||||
// We couldn't query the status so just break out
|
||||
break;
|
||||
}
|
||||
|
||||
// The service is already in use.
|
||||
if (ssp.dwCurrentState == SERVICE_STOPPED) {
|
||||
gotStop = TRUE;
|
||||
break;
|
||||
}
|
||||
currentWaitMS += 50;
|
||||
Sleep(50);
|
||||
}
|
||||
|
||||
lastServiceState = ssp.dwCurrentState;
|
||||
CloseServiceHandle(service);
|
||||
CloseServiceHandle(serviceManager);
|
||||
return gotStop;
|
||||
return lastServiceState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if there is at least one process running for the specified
|
||||
* application. A match will be found across any session for any user.
|
||||
*
|
||||
* @param process The process to check for existance
|
||||
* @return ERROR_NOT_FOUND if the process was not found
|
||||
* @ ERROR_SUCCESS if the process was found and there were no errors
|
||||
* @ Other Win32 system error code for other errors
|
||||
**/
|
||||
DWORD
|
||||
IsProcessRunning(LPCWSTR filename)
|
||||
{
|
||||
// Take a snapshot of all processes in the system.
|
||||
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (INVALID_HANDLE_VALUE == snapshot) {
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
PROCESSENTRY32W processEntry;
|
||||
processEntry.dwSize = sizeof(PROCESSENTRY32W);
|
||||
if (!Process32FirstW(snapshot, &processEntry)) {
|
||||
DWORD lastError = GetLastError();
|
||||
CloseHandle(snapshot);
|
||||
return lastError;
|
||||
}
|
||||
|
||||
do {
|
||||
if (wcsicmp(filename, processEntry.szExeFile) == 0) {
|
||||
CloseHandle(snapshot);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
} while (Process32NextW(snapshot, &processEntry));
|
||||
CloseHandle(snapshot);
|
||||
return ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the specified applicaiton to exit.
|
||||
*
|
||||
* @param filename The application to wait for.
|
||||
* @param maxSeconds The maximum amount of seconds to wait for all
|
||||
* instances of the application to exit.
|
||||
* @return ERROR_SUCCESS if no instances of the application exist
|
||||
* WAIT_TIMEOUT if the process is still running after maxSeconds.
|
||||
* Any other Win32 system error code.
|
||||
*/
|
||||
DWORD
|
||||
WaitForProcessExit(LPCWSTR filename, DWORD maxSeconds)
|
||||
{
|
||||
DWORD applicationRunningError = WAIT_TIMEOUT;
|
||||
for(DWORD i = 0; i < maxSeconds; i++) {
|
||||
DWORD applicationRunningError = IsProcessRunning(filename);
|
||||
if (ERROR_NOT_FOUND == applicationRunningError) {
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
Sleep(1000);
|
||||
}
|
||||
|
||||
if (ERROR_SUCCESS == applicationRunningError) {
|
||||
return WAIT_TIMEOUT;
|
||||
}
|
||||
|
||||
return applicationRunningError;
|
||||
}
|
||||
|
|
|
@ -41,8 +41,10 @@ BOOL LaunchWinPostProcess(const WCHAR *installationDir,
|
|||
HANDLE userToken);
|
||||
BOOL StartServiceUpdate(int argc, LPWSTR *argv);
|
||||
BOOL GetUpdateDirectoryPath(LPWSTR path);
|
||||
BOOL LaunchServiceSoftwareUpdateCommand(DWORD argc, LPCWSTR *argv);
|
||||
DWORD LaunchServiceSoftwareUpdateCommand(DWORD argc, LPCWSTR *argv);
|
||||
BOOL WriteStatusFailure(LPCWSTR updateDirPath, int errorCode);
|
||||
BOOL WriteStatusPending(LPCWSTR updateDirPath);
|
||||
BOOL WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds);
|
||||
DWORD WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds);
|
||||
DWORD WaitForProcessExit(LPCWSTR filename, DWORD maxSeconds);
|
||||
|
||||
#define SVC_NAME L"MozillaMaintenance"
|
||||
|
|
|
@ -148,6 +148,8 @@ const SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS = 16001;
|
|||
const SERVICE_UPDATER_SIGN_ERROR = 16002;
|
||||
const SERVICE_UPDATER_COMPARE_ERROR = 16003;
|
||||
const SERVICE_UPDATER_IDENTITY_ERROR = 16004;
|
||||
const SERVICE_STILL_APPLYING_ON_SUCCESS = 16005;
|
||||
const SERVICE_STILL_APPLYING_ON_FAILURE = 16006;
|
||||
|
||||
const CERT_ATTR_CHECK_FAILED_NO_UPDATE = 100;
|
||||
const CERT_ATTR_CHECK_FAILED_HAS_UPDATE = 101;
|
||||
|
@ -1439,7 +1441,9 @@ UpdateService.prototype = {
|
|||
update.errorCode == SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS ||
|
||||
update.errorCode == SERVICE_UPDATER_SIGN_ERROR ||
|
||||
update.errorCode == SERVICE_UPDATER_COMPARE_ERROR ||
|
||||
update.errorCode == SERVICE_UPDATER_IDENTITY_ERROR) {
|
||||
update.errorCode == SERVICE_UPDATER_IDENTITY_ERROR ||
|
||||
update.errorCode == SERVICE_STILL_APPLYING_ON_SUCCESS ||
|
||||
update.errorCode == SERVICE_STILL_APPLYING_ON_FAILURE) {
|
||||
var failCount = getPref("getIntPref",
|
||||
PREF_APP_UPDATE_SERVICE_ERRORS, 0);
|
||||
var maxFail = getPref("getIntPref",
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#pragma comment(lib, "crypt32.lib")
|
||||
# include <windows.h>
|
||||
# include <wintrust.h>
|
||||
# include <tlhelp32.h>
|
||||
# include <softpub.h>
|
||||
# include <direct.h>
|
||||
# include <io.h>
|
||||
|
@ -157,19 +158,53 @@ VerifyCertificateTrustForFile(LPCWSTR filePath)
|
|||
* This function does not stop the service, it just blocks until the service
|
||||
* is stopped.
|
||||
*
|
||||
* @param serviceName The service to wait for.
|
||||
* @param maxWaitSeconds The maximum number of seconds to wait
|
||||
* @return true if the service was stopped after waiting at most maxWaitSeconds
|
||||
* false on an error or when the service was not stopped
|
||||
* @param serviceName The service to wait for.
|
||||
* @param maxWaitSeconds The maximum number of seconds to wait
|
||||
* @return state of the service after a timeout or when stopped.
|
||||
* A value of 255 is returned for an error. Typical values are:
|
||||
* SERVICE_STOPPED 0x00000001
|
||||
* SERVICE_START_PENDING 0x00000002
|
||||
* SERVICE_STOP_PENDING 0x00000003
|
||||
* SERVICE_RUNNING 0x00000004
|
||||
* SERVICE_CONTINUE_PENDING 0x00000005
|
||||
* SERVICE_PAUSE_PENDING 0x00000006
|
||||
* SERVICE_PAUSED 0x00000007
|
||||
* last status not set 0x000000CF
|
||||
* Could no query status 0x000000DF
|
||||
* Could not open service, access denied 0x000000EB
|
||||
* Could not open service, invalid handle 0x000000EC
|
||||
* Could not open service, invalid name 0x000000ED
|
||||
* Could not open service, does not exist 0x000000EE
|
||||
* Could not open service, other error 0x000000EF
|
||||
* Could not open SCM, access denied 0x000000FD
|
||||
* Could not open SCM, database does not exist 0x000000FE;
|
||||
* Could not open SCM, other error 0x000000FF;
|
||||
* Note: The strange choice of error codes above SERVICE_PAUSED are chosen
|
||||
* in case Windows comes out with other service stats higher than 7, they
|
||||
* would likely call it 8 and above. JS code that uses this in TestAUSHelper
|
||||
* only handles values up to 255 so that's why we don't use GetLastError
|
||||
* directly.
|
||||
*/
|
||||
bool WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds)
|
||||
DWORD
|
||||
WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds)
|
||||
{
|
||||
// 0x000000CF is defined above to be not set
|
||||
DWORD lastServiceState = 0x000000CF;
|
||||
|
||||
// Get a handle to the SCM database.
|
||||
SC_HANDLE serviceManager = OpenSCManager(NULL, NULL,
|
||||
SC_MANAGER_CONNECT |
|
||||
SC_MANAGER_ENUMERATE_SERVICE);
|
||||
if (!serviceManager) {
|
||||
return false;
|
||||
DWORD lastError = GetLastError();
|
||||
switch(lastError) {
|
||||
case ERROR_ACCESS_DENIED:
|
||||
return 0x000000FD;
|
||||
case ERROR_DATABASE_DOES_NOT_EXIST:
|
||||
return 0x000000FE;
|
||||
default:
|
||||
return 0x000000FF;
|
||||
}
|
||||
}
|
||||
|
||||
// Get a handle to the service.
|
||||
|
@ -177,33 +212,152 @@ bool WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds)
|
|||
serviceName,
|
||||
SERVICE_QUERY_STATUS);
|
||||
if (!service) {
|
||||
DWORD lastError = GetLastError();
|
||||
CloseServiceHandle(serviceManager);
|
||||
return false;
|
||||
switch(lastError) {
|
||||
case ERROR_ACCESS_DENIED:
|
||||
return 0x000000EB;
|
||||
case ERROR_INVALID_HANDLE:
|
||||
return 0x000000EC;
|
||||
case ERROR_INVALID_NAME:
|
||||
return 0x000000ED;
|
||||
// If the service does not exist, keep trying in case it does exist soon.
|
||||
// I think there might be an issue with the TALOS machines and some of
|
||||
// the machines having an old maintenanceservice.exe that used to
|
||||
// uninstall when upgrading. Those should already be upgraded but this
|
||||
// is safer.
|
||||
case ERROR_SERVICE_DOES_NOT_EXIST:
|
||||
if (maxWaitSeconds == 0) {
|
||||
return 0x000000EE;
|
||||
} else {
|
||||
Sleep(1000);
|
||||
return WaitForServiceStop(serviceName, maxWaitSeconds - 1);
|
||||
}
|
||||
default:
|
||||
return 0x000000EF;
|
||||
}
|
||||
}
|
||||
|
||||
bool gotStop = false;
|
||||
DWORD currentWaitMS = 0;
|
||||
SERVICE_STATUS_PROCESS ssp;
|
||||
ssp.dwCurrentState = lastServiceState;
|
||||
while (currentWaitMS < maxWaitSeconds * 1000) {
|
||||
// Make sure the service is not stopped.
|
||||
SERVICE_STATUS_PROCESS ssp;
|
||||
DWORD bytesNeeded;
|
||||
if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
|
||||
sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded)) {
|
||||
DWORD lastError = GetLastError();
|
||||
switch (lastError) {
|
||||
case ERROR_INVALID_HANDLE:
|
||||
ssp.dwCurrentState = 0x000000D9;
|
||||
break;
|
||||
case ERROR_ACCESS_DENIED:
|
||||
ssp.dwCurrentState = 0x000000DA;
|
||||
break;
|
||||
case ERROR_INSUFFICIENT_BUFFER:
|
||||
ssp.dwCurrentState = 0x000000DB;
|
||||
break;
|
||||
case ERROR_INVALID_PARAMETER:
|
||||
ssp.dwCurrentState = 0x000000DC;
|
||||
break;
|
||||
case ERROR_INVALID_LEVEL:
|
||||
ssp.dwCurrentState = 0x000000DD;
|
||||
break;
|
||||
case ERROR_SHUTDOWN_IN_PROGRESS:
|
||||
ssp.dwCurrentState = 0x000000DE;
|
||||
break;
|
||||
// These 3 errors can occur when the service is not yet stopped but
|
||||
// it is stopping.
|
||||
case ERROR_INVALID_SERVICE_CONTROL:
|
||||
case ERROR_SERVICE_CANNOT_ACCEPT_CTRL:
|
||||
case ERROR_SERVICE_NOT_ACTIVE:
|
||||
currentWaitMS += 50;
|
||||
Sleep(50);
|
||||
continue;
|
||||
default:
|
||||
ssp.dwCurrentState = 0x000000DF;
|
||||
}
|
||||
|
||||
// We couldn't query the status so just break out
|
||||
break;
|
||||
}
|
||||
|
||||
// The service is already in use.
|
||||
if (ssp.dwCurrentState == SERVICE_STOPPED) {
|
||||
gotStop = true;
|
||||
break;
|
||||
}
|
||||
currentWaitMS += 50;
|
||||
Sleep(50);
|
||||
}
|
||||
|
||||
lastServiceState = ssp.dwCurrentState;
|
||||
CloseServiceHandle(service);
|
||||
CloseServiceHandle(serviceManager);
|
||||
return gotStop;
|
||||
return lastServiceState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if there is at least one process running for the specified
|
||||
* application. A match will be found across any session for any user.
|
||||
*
|
||||
* @param process The process to check for existance
|
||||
* @return ERROR_NOT_FOUND if the process was not found
|
||||
* @ ERROR_SUCCESS if the process was found and there were no errors
|
||||
* @ Other Win32 system error code for other errors
|
||||
**/
|
||||
DWORD
|
||||
IsProcessRunning(LPCWSTR filename)
|
||||
{
|
||||
// Take a snapshot of all processes in the system.
|
||||
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (INVALID_HANDLE_VALUE == snapshot) {
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
PROCESSENTRY32W processEntry;
|
||||
processEntry.dwSize = sizeof(PROCESSENTRY32W);
|
||||
if (!Process32FirstW(snapshot, &processEntry)) {
|
||||
DWORD lastError = GetLastError();
|
||||
CloseHandle(snapshot);
|
||||
return lastError;
|
||||
}
|
||||
|
||||
do {
|
||||
if (wcsicmp(filename, processEntry.szExeFile) == 0) {
|
||||
CloseHandle(snapshot);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
} while (Process32NextW(snapshot, &processEntry));
|
||||
CloseHandle(snapshot);
|
||||
return ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the specified applicaiton to exit.
|
||||
*
|
||||
* @param filename The application to wait for.
|
||||
* @param maxSeconds The maximum amount of seconds to wait for all
|
||||
* instances of the application to exit.
|
||||
* @return ERROR_SUCCESS if no instances of the application exist
|
||||
* WAIT_TIMEOUT if the process is still running after maxSeconds.
|
||||
* Any other Win32 system error code.
|
||||
*/
|
||||
DWORD
|
||||
WaitForProcessExit(LPCWSTR filename, DWORD maxSeconds)
|
||||
{
|
||||
DWORD applicationRunningError = WAIT_TIMEOUT;
|
||||
for(DWORD i = 0; i < maxSeconds; i++) {
|
||||
DWORD applicationRunningError = IsProcessRunning(filename);
|
||||
if (ERROR_NOT_FOUND == applicationRunningError) {
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
Sleep(1000);
|
||||
}
|
||||
|
||||
if (ERROR_SUCCESS == applicationRunningError) {
|
||||
return WAIT_TIMEOUT;
|
||||
}
|
||||
|
||||
return applicationRunningError;
|
||||
}
|
||||
|
||||
|
||||
|
@ -258,10 +412,29 @@ int NS_main(int argc, NS_tchar **argv)
|
|||
#ifdef XP_WIN
|
||||
const int maxWaitSeconds = NS_ttoi(argv[3]);
|
||||
LPCWSTR serviceName = argv[2];
|
||||
if (WaitForServiceStop(serviceName, maxWaitSeconds)) {
|
||||
DWORD serviceState = WaitForServiceStop(serviceName, maxWaitSeconds);
|
||||
if (SERVICE_STOPPED == serviceState) {
|
||||
return 0;
|
||||
} else {
|
||||
return serviceState;
|
||||
}
|
||||
#else
|
||||
// Not implemented on non-Windows platforms
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!NS_tstrcmp(argv[1], NS_T("wait-for-application-exit"))) {
|
||||
#ifdef XP_WIN
|
||||
const int maxWaitSeconds = NS_ttoi(argv[3]);
|
||||
LPCWSTR application = argv[2];
|
||||
DWORD ret = WaitForProcessExit(application, maxWaitSeconds);
|
||||
if (ERROR_SUCCESS == ret) {
|
||||
return 0;
|
||||
} else if (WAIT_TIMEOUT == ret) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
#else
|
||||
// Not implemented on non-Windows platforms
|
||||
|
|
|
@ -462,7 +462,12 @@ let gServiceLaunchedCallbackArgs = null;
|
|||
*
|
||||
* @return true if the test should run and false if it shouldn't.
|
||||
*/
|
||||
function shouldRunServiceTest() {
|
||||
function shouldRunServiceTest(aFirstTest) {
|
||||
// In case the machine is running an old maintenance service or if it
|
||||
// is not installed, and permissions exist to install it. Then install
|
||||
// the newer bin that we have.
|
||||
attemptServiceInstall();
|
||||
|
||||
const REG_PATH = "SOFTWARE\\Mozilla\\MaintenanceService\\" +
|
||||
"3932ecacee736d366d6436db0f55bce4";
|
||||
|
||||
|
@ -490,17 +495,38 @@ function shouldRunServiceTest() {
|
|||
updaterBinPath = '"' + updaterBinPath + '"';
|
||||
}
|
||||
|
||||
// Check to make sure the service is installed
|
||||
let helperBin = do_get_file(HELPER_BIN_FILE);
|
||||
let args = ["check-signature", updaterBinPath];
|
||||
let args = ["wait-for-service-stop", "MozillaMaintenance", "10"];
|
||||
let process = AUS_Cc["@mozilla.org/process/util;1"].
|
||||
createInstance(AUS_Ci.nsIProcess);
|
||||
process.init(helperBin);
|
||||
logTestInfo("Checking if the service exists on this machine.");
|
||||
process.run(true, args, args.length);
|
||||
if (process.exitValue == 0xEE) {
|
||||
logTestInfo("this test can only run when the service is installed.");
|
||||
return false;
|
||||
} else {
|
||||
logTestInfo("Service exists, return value: " + process.exitValue);
|
||||
}
|
||||
|
||||
// If this is the first test in the series, then there is no reason the
|
||||
// service should be anything but stopped, so be strict here and throw
|
||||
// an error.
|
||||
if (aFirstTest && process.exitValue != 0) {
|
||||
do_throw("First test, check for service stopped state returned error " +
|
||||
process.exitValue);
|
||||
}
|
||||
|
||||
// Make sure the binaries are signed
|
||||
args = ["check-signature", updaterBinPath];
|
||||
process = AUS_Cc["@mozilla.org/process/util;1"].
|
||||
createInstance(AUS_Ci.nsIProcess);
|
||||
process.init(helperBin);
|
||||
process.run(true, args, args.length);
|
||||
if (process.exitValue == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
logTestInfo("this test can only run on builds with signed binaries. " +
|
||||
HELPER_BIN_FILE + " returned " + process.exitValue)
|
||||
return false;
|
||||
|
@ -526,6 +552,34 @@ function copyBinToApplyToDir(filename) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to upgrade the maintenance service if permissions are allowed.
|
||||
* This is useful for XP where we have permission to upgrade in case an
|
||||
* older service installer exists. Also if the user manually installed into
|
||||
* a unprivileged location.
|
||||
*/
|
||||
function attemptServiceInstall() {
|
||||
var version = AUS_Cc["@mozilla.org/system-info;1"]
|
||||
.getService(AUS_Ci.nsIPropertyBag2)
|
||||
.getProperty("version");
|
||||
var isVistaOrHigher = (parseFloat(version) >= 6.0);
|
||||
if (isVistaOrHigher) {
|
||||
return;
|
||||
}
|
||||
|
||||
let binDir = getGREDir();
|
||||
let installerFile = binDir.clone();
|
||||
installerFile.append(MAINTENANCE_SERVICE_INSTALLER_BIN_FILE);
|
||||
if (!installerFile.exists()) {
|
||||
do_throw(MAINTENANCE_SERVICE_INSTALLER_BIN_FILE + " not found.");
|
||||
}
|
||||
let installerProcess = AUS_Cc["@mozilla.org/process/util;1"].
|
||||
createInstance(AUS_Ci.nsIProcess);
|
||||
installerProcess.init(installerFile);
|
||||
logTestInfo("Starting installer process...");
|
||||
installerProcess.run(true, [], 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for updater tests for launching the updater using the
|
||||
* maintenance service to apply a mar file.
|
||||
|
@ -554,26 +608,60 @@ function runUpdateUsingService(aInitialStatus, aExpectedStatus,
|
|||
file.append("maintenanceservice.log");
|
||||
return readFile(file);
|
||||
}
|
||||
function waitServiceApps() {
|
||||
// maintenanceservice_installer.exe is started async during updates.
|
||||
waitForApplicationStop("maintenanceservice_installer.exe");
|
||||
// maintenanceservice_tmp.exe is started async from the service installer.
|
||||
waitForApplicationStop("maintenanceservice_tmp.exe");
|
||||
// In case the SCM thinks the service is stopped, but process still exists.
|
||||
waitForApplicationStop("maintenanceservice.exe");
|
||||
}
|
||||
function waitForServiceStop(aFailTest) {
|
||||
waitServiceApps();
|
||||
logTestInfo("Waiting for service to stop if necessary...");
|
||||
// Use the helper bin to ensure the service is stopped. If not
|
||||
// stopped then wait for the service to be stopped (at most 20 seconds)
|
||||
// stopped then wait for the service to be stopped (at most 120 seconds)
|
||||
let helperBin = do_get_file(HELPER_BIN_FILE);
|
||||
let helperBinArgs = ["wait-for-service-stop",
|
||||
"MozillaMaintenance",
|
||||
"20"];
|
||||
"120"];
|
||||
let helperBinProcess = AUS_Cc["@mozilla.org/process/util;1"].
|
||||
createInstance(AUS_Ci.nsIProcess);
|
||||
helperBinProcess.init(helperBin);
|
||||
logTestInfo("Stopping service...");
|
||||
helperBinProcess.run(true, helperBinArgs, helperBinArgs.length);
|
||||
if (helperBinProcess.exitValue == 0xEE) {
|
||||
do_throw("The service does not exist on this machine. Return value: " +
|
||||
helperBinProcess.exitValue);
|
||||
} else if (helperBinProcess.exitValue != 0) {
|
||||
if (aFailTest) {
|
||||
do_throw("maintenance service did not stop, last state: " +
|
||||
helperBinProcess.exitValue + ". Forcing test failure.");
|
||||
} else {
|
||||
logTestInfo("maintenance service did not stop, last state: " +
|
||||
helperBinProcess.exitValue + ". May cause failures.");
|
||||
}
|
||||
} else {
|
||||
logTestInfo("Service stopped.");
|
||||
}
|
||||
waitServiceApps();
|
||||
}
|
||||
function waitForApplicationStop(application) {
|
||||
logTestInfo("Waiting for " + application + " to stop if " +
|
||||
"necessary...");
|
||||
// Use the helper bin to ensure the application is stopped.
|
||||
// If not, then wait for it to be stopped (at most 120 seconds)
|
||||
let helperBin = do_get_file(HELPER_BIN_FILE);
|
||||
let helperBinArgs = ["wait-for-application-exit",
|
||||
application,
|
||||
"120"];
|
||||
let helperBinProcess = AUS_Cc["@mozilla.org/process/util;1"].
|
||||
createInstance(AUS_Ci.nsIProcess);
|
||||
helperBinProcess.init(helperBin);
|
||||
helperBinProcess.run(true, helperBinArgs, helperBinArgs.length);
|
||||
if (helperBinProcess.exitValue != 0) {
|
||||
if (aFailTest) {
|
||||
do_throw("maintenance service did not stop, forcing test failure.");
|
||||
} else {
|
||||
logTestInfo("maintenance service did not stop, may cause failures.");
|
||||
}
|
||||
} else {
|
||||
logTestInfo("Service stopped.");
|
||||
do_throw(application + " did not stop, last state: " +
|
||||
helperBinProcess.exitValue + ". Forcing test failure.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -587,19 +675,6 @@ function runUpdateUsingService(aInitialStatus, aExpectedStatus,
|
|||
do_register_cleanup(function serviceCleanup() {
|
||||
resetEnvironment();
|
||||
|
||||
// Remove the copy of the application executable used for the test on
|
||||
// Windows if it exists.
|
||||
let appBinCopy = getCurrentProcessDir();
|
||||
appBinCopy.append(TEST_ID + FILE_WIN_TEST_EXE);
|
||||
if (appBinCopy.exists()) {
|
||||
try {
|
||||
appBinCopy.remove(false);
|
||||
}
|
||||
catch (e) {
|
||||
logTestInfo("unable to remove file during cleanup. Exception: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
// This will delete the app console log file if it exists.
|
||||
try {
|
||||
getAppConsoleLogPath();
|
||||
|
@ -672,7 +747,7 @@ function runUpdateUsingService(aInitialStatus, aExpectedStatus,
|
|||
copyBinToApplyToDir(MAINTENANCE_SERVICE_INSTALLER_BIN_FILE);
|
||||
|
||||
// Firefox does not wait for the service command to finish, but
|
||||
// we still launch the process sync to avoid intemittent failures with
|
||||
// we still launch the process sync to avoid intermittent failures with
|
||||
// the log file not being written out yet.
|
||||
// We will rely on watching the update.status file and waiting for the service
|
||||
// to stop to know the service command is done.
|
||||
|
@ -696,14 +771,15 @@ function runUpdateUsingService(aInitialStatus, aExpectedStatus,
|
|||
" status, got " + status + " for now...");
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure all of the logs are written out.
|
||||
waitForServiceStop(false);
|
||||
|
||||
do_check_eq(status, aExpectedStatus);
|
||||
|
||||
timer.cancel();
|
||||
timer = null;
|
||||
|
||||
// Make sure all of the logs are written out.
|
||||
waitForServiceStop(false);
|
||||
|
||||
if (aCheckSvcLog) {
|
||||
checkServiceLogs(svcOriginalLog);
|
||||
}
|
||||
|
@ -1206,8 +1282,30 @@ function checkCallbackServiceLog() {
|
|||
"and the expected command line arguments passed to it");
|
||||
do_check_eq(logContents, expectedLogContents);
|
||||
|
||||
// Use a timeout to give any files that were in use additional time to close.
|
||||
do_timeout(TEST_HELPER_TIMEOUT, do_test_finished);
|
||||
|
||||
(function removeCallbackCopy() {
|
||||
// Remove the copy of the application executable used for the test on
|
||||
// Windows if it exists.
|
||||
let appBinCopy = getCurrentProcessDir();
|
||||
appBinCopy.append(TEST_ID + FILE_WIN_TEST_EXE);
|
||||
if (appBinCopy.exists()) {
|
||||
try {
|
||||
appBinCopy.remove(false);
|
||||
// Use a timeout to give any files that were in use additional
|
||||
// time to close. Same as updater.exe without service tests.
|
||||
do_timeout(TEST_HELPER_TIMEOUT, do_test_finished);
|
||||
}
|
||||
catch (e) {
|
||||
logTestInfo("unable to remove file during cleanup. Exception: " + e);
|
||||
do_timeout(TEST_HELPER_TIMEOUT, removeCallbackCopy);
|
||||
}
|
||||
} else {
|
||||
do_timeout(TEST_HELPER_TIMEOUT, do_test_finished);
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,7 +21,7 @@ const TEST_FILES = [
|
|||
];
|
||||
|
||||
function run_test() {
|
||||
if (!shouldRunServiceTest()) {
|
||||
if (!shouldRunServiceTest(true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1605,8 +1605,8 @@ int NS_main(int argc, NS_tchar **argv)
|
|||
|
||||
bool useService = false;
|
||||
bool testOnlyFallbackKeyExists = false;
|
||||
bool noServiceFallback = _wgetenv(L"MOZ_NO_SERVICE_FALLBACK") != NULL;
|
||||
_wputenv(L"MOZ_NO_SERVICE_FALLBACK=");
|
||||
bool noServiceFallback = getenv("MOZ_NO_SERVICE_FALLBACK") != NULL;
|
||||
putenv(const_cast<char*>("MOZ_NO_SERVICE_FALLBACK="));
|
||||
|
||||
// We never want the service to be used unless we build with
|
||||
// the maintenance service.
|
||||
|
@ -1680,9 +1680,10 @@ int NS_main(int argc, NS_tchar **argv)
|
|||
// argument prior to callbackIndex is the working directory.
|
||||
const int callbackIndex = 5;
|
||||
|
||||
bool usingService = false;
|
||||
#if defined(XP_WIN)
|
||||
bool usingService = _wgetenv(L"MOZ_USING_SERVICE") != NULL;
|
||||
_wputenv(L"MOZ_USING_SERVICE=");
|
||||
usingService = getenv("MOZ_USING_SERVICE") != NULL;
|
||||
putenv(const_cast<char*>("MOZ_USING_SERVICE="));
|
||||
// lastFallbackError keeps track of the last error for the service not being
|
||||
// used, in case of an error when fallback is not enabled we write the
|
||||
// error to the update.status file.
|
||||
|
@ -1783,16 +1784,19 @@ int NS_main(int argc, NS_tchar **argv)
|
|||
if (useService) {
|
||||
// If the update couldn't be started, then set useService to false so
|
||||
// we do the update the old way.
|
||||
useService = LaunchServiceSoftwareUpdateCommand(argc, (LPCWSTR *)argv);
|
||||
|
||||
DWORD ret = LaunchServiceSoftwareUpdateCommand(argc, (LPCWSTR *)argv);
|
||||
useService = (ret == ERROR_SUCCESS);
|
||||
// If the command was launched then wait for the service to be done.
|
||||
if (useService) {
|
||||
if (!WaitForServiceStop(SVC_NAME, 600)) {
|
||||
DWORD lastState = WaitForServiceStop(SVC_NAME, 600);
|
||||
if (lastState != SERVICE_STOPPED) {
|
||||
// If the service doesn't stop after 10 minutes there is
|
||||
// something seriously wrong.
|
||||
lastFallbackError = FALLBACKKEY_SERVICE_NO_STOP_ERROR;
|
||||
useService = false;
|
||||
}
|
||||
} else {
|
||||
lastFallbackError = FALLBACKKEY_LAUNCH_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1903,8 +1907,12 @@ int NS_main(int argc, NS_tchar **argv)
|
|||
HANDLE callbackFile = INVALID_HANDLE_VALUE;
|
||||
if (argc > callbackIndex) {
|
||||
// If the callback executable is specified it must exist for a successful
|
||||
// update.
|
||||
// update. It is important we null out the whole buffer here because later
|
||||
// we make the assumption that the callback application is inside the
|
||||
// apply-to dir. If we don't have a fully null'ed out buffer it can lead
|
||||
// to stack corruption which causes crashes and other problems.
|
||||
NS_tchar callbackLongPath[MAXPATHLEN];
|
||||
ZeroMemory(callbackLongPath, sizeof(callbackLongPath));
|
||||
if (!GetLongPathNameW(argv[callbackIndex], callbackLongPath,
|
||||
sizeof(callbackLongPath)/sizeof(callbackLongPath[0]))) {
|
||||
LOG(("NS_main: unable to find callback file: " LOG_S "\n", argv[callbackIndex]));
|
||||
|
|
|
@ -3136,6 +3136,25 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
|
|||
if (NS_FAILED(rv))
|
||||
updRoot = dirProvider.GetAppDir();
|
||||
|
||||
// If the MOZ_PROCESS_UPDATES environment variable already exists, then
|
||||
// we are being called from the callback application.
|
||||
if (EnvHasValue("MOZ_PROCESS_UPDATES")) {
|
||||
// If the caller has asked us to log our arguments, do so. This is used
|
||||
// to make sure that the maintenance service successfully launches the
|
||||
// callback application.
|
||||
const char *logFile = nsnull;
|
||||
if (ARG_FOUND == CheckArg("dump-args", false, &logFile)) {
|
||||
FILE* logFP = fopen(logFile, "wb");
|
||||
if (logFP) {
|
||||
for (i = 1; i < gRestartArgc; ++i) {
|
||||
fprintf(logFP, "%s\n", gRestartArgv[i]);
|
||||
}
|
||||
fclose(logFP);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Support for processing an update and exiting. The MOZ_PROCESS_UPDATES
|
||||
// environment variable will be part of the updater's environment and the
|
||||
// application that is relaunched by the updater. When the application is
|
||||
|
@ -3152,20 +3171,6 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
|
|||
appData.version);
|
||||
if (EnvHasValue("MOZ_PROCESS_UPDATES")) {
|
||||
SaveToEnv("MOZ_PROCESS_UPDATES=");
|
||||
|
||||
// If the caller has asked us to log our arguments, do so. This is used
|
||||
// to make sure that the maintenance service successfully launches the
|
||||
// callback application.
|
||||
const char *logFile = nsnull;
|
||||
if (ARG_FOUND == CheckArg("dump-args", false, &logFile)) {
|
||||
FILE* logFP = fopen(logFile, "wb");
|
||||
if (logFP) {
|
||||
for (i = 1; i < gRestartArgc; ++i) {
|
||||
fprintf(logFP, "%s\n", gRestartArgv[i]);
|
||||
}
|
||||
fclose(logFP);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -265,13 +265,17 @@ WinLaunchChild(const PRUnichar *exePath,
|
|||
si.lpDesktop = L"winsta0\\Default";
|
||||
PROCESS_INFORMATION pi = {0};
|
||||
|
||||
DWORD creationFlags = 0;
|
||||
#ifdef DEBUG
|
||||
creationFlags |= CREATE_NEW_CONSOLE;
|
||||
#endif
|
||||
if (userToken == NULL) {
|
||||
ok = CreateProcessW(exePath,
|
||||
cl,
|
||||
NULL, // no special security attributes
|
||||
NULL, // no special thread attributes
|
||||
FALSE, // don't inherit filehandles
|
||||
0, // No special process creation flags
|
||||
creationFlags,
|
||||
NULL, // inherit my environment
|
||||
NULL, // use my current directory
|
||||
&si,
|
||||
|
|
Загрузка…
Ссылка в новой задаче