gecko/toolkit/mozapps/update/test/TestAUSHelper.cpp
Ehsan Akhgari ab9253304f Bug 307181 - Stage Firefox updates in the background after they're downloaded, and replace the application directory on restart; r=rstrong,bbondy
When Firefox downloads an update, it previously kept the update around to apply
it on the next restart.  This patch changes this so that the updater program
is launched in the background as soon as the update has finished downloading
in order to stage the updated version of the application by copying the
existing installation directory to a temporary location and applying the update
on top of it, and replace the existing installation directory with the staged
directory on the next restart.

Because the replacing step is typically very fast, this patch eliminates the
wait for the update to be applied on restart, making it unnecessary to show a
progress dialog when restarting.

--HG--
rename : toolkit/mozapps/update/test/chrome/test_0092_finishedBackground.xul => toolkit/mozapps/update/test/chrome/test_0093_stagedBackground.xul
rename : toolkit/mozapps/update/test/unit/test_0110_general.js => toolkit/mozapps/update/test/unit/test_0113_general.js
rename : toolkit/mozapps/update/test/unit/test_0111_general.js => toolkit/mozapps/update/test/unit/test_0114_general.js
rename : toolkit/mozapps/update/test/unit/test_0112_general.js => toolkit/mozapps/update/test/unit/test_0115_general.js
rename : toolkit/mozapps/update/test/unit/test_0170_fileLocked_xp_win_complete.js => toolkit/mozapps/update/test/unit/test_0172_fileLocked_xp_win_complete.js
rename : toolkit/mozapps/update/test/unit/test_0171_fileLocked_xp_win_partial.js => toolkit/mozapps/update/test/unit/test_0173_fileLocked_xp_win_partial.js
rename : toolkit/mozapps/update/test/unit/test_0110_general.js => toolkit/mozapps/update/test_svc/unit/test_0113_general_svc.js
rename : toolkit/mozapps/update/test/unit/test_0111_general.js => toolkit/mozapps/update/test_svc/unit/test_0114_general_svc.js
rename : toolkit/mozapps/update/test/unit/test_0112_general.js => toolkit/mozapps/update/test_svc/unit/test_0115_general_svc.js
rename : toolkit/mozapps/update/test/unit/test_0170_fileLocked_xp_win_complete.js => toolkit/mozapps/update/test_svc/unit/test_0172_fileLocked_xp_win_complete_svc.js
rename : toolkit/mozapps/update/test/unit/test_0171_fileLocked_xp_win_partial.js => toolkit/mozapps/update/test_svc/unit/test_0173_fileLocked_xp_win_partial_svc.js
2012-05-22 10:50:04 -04:00

304 lines
7.8 KiB
C++

/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
#ifdef XP_WIN
#pragma comment(lib, "wintrust.lib")
#pragma comment(lib, "crypt32.lib")
# include <windows.h>
# include <wintrust.h>
# include <tlhelp32.h>
# include <softpub.h>
# include <direct.h>
# include <io.h>
typedef WCHAR NS_tchar;
# define NS_main wmain
# define F_OK 00
# define W_OK 02
# define R_OK 04
# define stat _stat
# define NS_T(str) L ## str
# define NS_tsnprintf(dest, count, fmt, ...) \
{ \
int _count = count - 1; \
_snwprintf(dest, _count, fmt, ##__VA_ARGS__); \
dest[_count] = L'\0'; \
}
# define NS_taccess _waccess
# define NS_tchdir _wchdir
# define NS_tfopen _wfopen
# define NS_tstrcmp wcscmp
# define NS_ttoi _wtoi
# define NS_tstat _wstat
# define NS_tgetcwd _wgetcwd
# define LOG_S "%S"
#include "../common/updatehelper.h"
#else
# include <unistd.h>
# define NS_main main
typedef char NS_tchar;
# define NS_T(str) str
# define NS_tsnprintf snprintf
# define NS_taccess access
# define NS_tchdir chdir
# define NS_tfopen fopen
# define NS_tstrcmp strcmp
# define NS_ttoi atoi
# define NS_tstat stat
# define NS_tgetcwd getcwd
# define LOG_S "%s"
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef MAXPATHLEN
# ifdef PATH_MAX
# define MAXPATHLEN PATH_MAX
# elif defined(MAX_PATH)
# define MAXPATHLEN MAX_PATH
# elif defined(_MAX_PATH)
# define MAXPATHLEN _MAX_PATH
# elif defined(CCHMAXPATH)
# define MAXPATHLEN CCHMAXPATH
# else
# define MAXPATHLEN 1024
# endif
#endif
static void
WriteMsg(const NS_tchar *path, const char *status)
{
FILE* outFP = NS_tfopen(path, NS_T("wb"));
if (!outFP)
return;
fprintf(outFP, "%s\n", status);
fclose(outFP);
outFP = NULL;
}
static bool
CheckMsg(const NS_tchar *path, const char *expected)
{
if (NS_taccess(path, F_OK)) {
return false;
}
FILE *inFP = NS_tfopen(path, NS_T("rb"));
if (!inFP) {
return false;
}
struct stat ms;
if (fstat(fileno(inFP), &ms)) {
return false;
}
char *mbuf = (char *) malloc(ms.st_size + 1);
if (!mbuf) {
return false;
}
size_t r = ms.st_size;
char *rb = mbuf;
size_t c = fread(rb, sizeof(char), 50, inFP);
r -= c;
rb += c;
if (c == 0 && r) {
return false;
}
mbuf[ms.st_size] = '\0';
rb = mbuf;
fclose(inFP);
inFP = NULL;
return strcmp(rb, expected) == 0;
}
#ifdef XP_WIN
/**
* Verifies the trust of the specified file path.
*
* @param filePath The file path to check.
* @return ERROR_SUCCESS if successful, or the last error code otherwise.
*/
DWORD
VerifyCertificateTrustForFile(LPCWSTR filePath)
{
// Setup the file to check.
WINTRUST_FILE_INFO fileToCheck;
ZeroMemory(&fileToCheck, sizeof(fileToCheck));
fileToCheck.cbStruct = sizeof(WINTRUST_FILE_INFO);
fileToCheck.pcwszFilePath = filePath;
// Setup what to check, we want to check it is signed and trusted.
WINTRUST_DATA trustData;
ZeroMemory(&trustData, sizeof(trustData));
trustData.cbStruct = sizeof(trustData);
trustData.pPolicyCallbackData = NULL;
trustData.pSIPClientData = NULL;
trustData.dwUIChoice = WTD_UI_NONE;
trustData.fdwRevocationChecks = WTD_REVOKE_NONE;
trustData.dwUnionChoice = WTD_CHOICE_FILE;
trustData.dwStateAction = 0;
trustData.hWVTStateData = NULL;
trustData.pwszURLReference = NULL;
// no UI
trustData.dwUIContext = 0;
trustData.pFile = &fileToCheck;
GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2;
// Check if the file is signed by something that is trusted.
return WinVerifyTrust(NULL, &policyGUID, &trustData);
}
#endif
int NS_main(int argc, NS_tchar **argv)
{
if (argc < 3) {
fprintf(stderr, \
"\n" \
"Application Update Service Test Helper\n" \
"\n" \
"Usage: WORKINGDIR INFILE OUTFILE -s SECONDS [FILETOLOCK]\n" \
" or: WORKINGDIR LOGFILE [ARG2 ARG3...]\n" \
" or: signature-check filepath\n" \
"\n" \
" WORKINGDIR \tThe relative path to the working directory to use.\n" \
" INFILE \tThe relative path from the working directory for the file to\n" \
" \tread actions to perform such as finish.\n" \
" OUTFILE \tThe relative path from the working directory for the file to\n" \
" \twrite status information.\n" \
" SECONDS \tThe number of seconds to sleep.\n" \
" FILETOLOCK \tThe relative path from the working directory to an existing\n" \
" \tfile to open exlusively.\n" \
" \tOnly available on Windows platforms and silently ignored on\n" \
" \tother platforms.\n" \
" LOGFILE \tThe relative path from the working directory to log the\n" \
" \tcommand line arguments.\n" \
" ARG2 ARG3...\tArguments to write to the LOGFILE after the preceding command\n" \
" \tline arguments.\n" \
"\n" \
"Note: All paths must be relative.\n" \
"\n");
return 1;
}
if (!NS_tstrcmp(argv[1], NS_T("check-signature"))) {
#ifdef XP_WIN
if (ERROR_SUCCESS == VerifyCertificateTrustForFile(argv[2])) {
return 0;
} else {
return 1;
}
#else
// Not implemented on non-Windows platforms
return 1;
#endif
}
if (!NS_tstrcmp(argv[1], NS_T("wait-for-service-stop"))) {
#ifdef XP_WIN
const int maxWaitSeconds = NS_ttoi(argv[3]);
LPCWSTR serviceName = argv[2];
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
return 1;
#endif
}
int i = 0;
if (NS_tchdir(argv[1]) != 0) {
return 1;
}
// File in use test helper section
if (!NS_tstrcmp(argv[4], NS_T("-s"))) {
NS_tchar *cwd = NS_tgetcwd(NULL, 0);
NS_tchar inFilePath[MAXPATHLEN];
NS_tsnprintf(inFilePath, sizeof(inFilePath)/sizeof(inFilePath[0]),
NS_T("%s/%s"), cwd, argv[2]);
NS_tchar outFilePath[MAXPATHLEN];
NS_tsnprintf(outFilePath, sizeof(outFilePath)/sizeof(outFilePath[0]),
NS_T("%s/%s"), cwd, argv[3]);
int seconds = NS_ttoi(argv[5]);
#ifdef XP_WIN
HANDLE hFile = INVALID_HANDLE_VALUE;
if (argc == 7) {
hFile = CreateFileW(argv[6],
DELETE | GENERIC_WRITE, 0,
NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
WriteMsg(outFilePath, "error_locking");
return 1;
}
}
WriteMsg(outFilePath, "sleeping");
while (!CheckMsg(inFilePath, "finish\n") && i++ <= seconds) {
Sleep(1000);
}
if (argc == 7) {
CloseHandle(hFile);
}
#else
WriteMsg(outFilePath, "sleeping");
while (!CheckMsg(inFilePath, "finish\n") && i++ <= seconds) {
sleep(1);
}
#endif
WriteMsg(outFilePath, "finished");
return 0;
}
// Command line argument test helper section
NS_tchar logFilePath[MAXPATHLEN];
NS_tsnprintf(logFilePath, sizeof(logFilePath)/sizeof(logFilePath[0]),
NS_T("%s"), argv[2]);
FILE* logFP = NS_tfopen(logFilePath, NS_T("wb"));
for (i = 1; i < argc; ++i) {
fprintf(logFP, LOG_S "\n", argv[i]);
}
fclose(logFP);
logFP = NULL;
return 0;
}