Bug 600777: Update driver code should launch updater application with a preference for the parent architecture. r=rstrong a=blocking-beta7

This commit is contained in:
Josh Aas 2010-10-02 00:11:24 -04:00
parent 9819aaabcf
commit a4e524e09b
6 changed files with 84 additions and 123 deletions

View File

@ -154,7 +154,7 @@ function runUpdate(aUpdatesDir, aUpdater) {
var process = AUS_Cc["@mozilla.org/process/util;1"].
createInstance(AUS_Ci.nsIProcess);
process.init(updateBin);
var args = [updatesDirPath, 0, cwdPath];
var args = [updatesDirPath, cwdPath];
process.run(true, args, args.length);
return process.exitValue;
}

View File

@ -87,12 +87,6 @@ var gTestFiles = [
}];
function run_test() {
var isOSX = ("nsILocalFileMac" in Components.interfaces);
if (isOSX) {
dump("INFO | test_0110_general.js | Skipping test on mac, bug 599477")
return;
}
var testFile;
// The directory the updates will be applied to is the current working
// directory and not dist/bin.

View File

@ -105,12 +105,6 @@ var gTestFiles = [
}];
function run_test() {
var isOSX = ("nsILocalFileMac" in Components.interfaces);
if (isOSX) {
dump("INFO | test_0111_general.js | Skipping test on mac, bug 599477")
return;
}
var testFile;
// The directory the updates will be applied to is the current working
// directory and not dist/bin.

View File

@ -87,12 +87,6 @@ var gTestFiles = [
}];
function run_test() {
var isOSX = ("nsILocalFileMac" in Components.interfaces);
if (isOSX) {
dump("INFO | test_0112_general.js | Skipping test on mac, bug 599477")
return;
}
var testFile;
// The directory the updates will be applied to is the current working
// directory and not dist/bin.

View File

@ -1466,32 +1466,39 @@ UpdateThreadFunc(void *param)
int NS_main(int argc, NS_tchar **argv)
{
InitProgressUI(&argc, &argv);
// The updater command line consists of the directory path containing the
// updater.mar file to process followed by the PID of the calling process.
// The updater will wait on the parent process to exit if the PID is non-
// zero. This is leveraged on platforms such as Windows where it is
// necessary for the parent process to exit before its executable image may
// be altered.
// To process an update the updater command line must at a minimum have the
// directory path containing the updater.mar file to process as the first argument
// and the directory to apply the update to as the second argument. When the
// updater is launched by another process the PID of the parent process should be
// provided in the optional third argument and the updater will wait on the parent
// process to exit if the value is non-zero and the process is present. This is
// necessary due to not being able to update files that are in use on Windows. The
// optional fourth argument is the callback's working directory and the optional
// fifth argument is the callback path. The callback is the application to launch
// after updating and it will be launched when these arguments are provided
// whether the update was successful or not. All remaining arguments are optional
// and are passed to the callback when it is launched.
if (argc < 3) {
fprintf(stderr, "Usage: updater update-dir apply-to-dir [wait-pid [callback-working-dir callback-path args...]]\n");
return 1;
}
// Change current directory to the directory where we need to apply the update.
#ifndef WINCE
if (argc < 2) {
fprintf(stderr, "Usage: updater <dir-path> [parent-pid [working-dir callback args...]]\n");
return 1;
}
#else
if (argc < 4) {
fprintf(stderr, "Usage: updater <dir-path> parent-pid <working-dir> [callback args...]]\n");
if (NS_tchdir(argv[2]) != 0) {
return 1;
}
#endif
if (argc > 2 ) {
// If there is a PID specified and it is not '0' then wait for the process to exit.
if (argc > 3) {
#ifdef XP_WIN
__int64 pid = _wtoi64(argv[2]);
__int64 pid = _wtoi64(argv[3]);
#else
int pid = atoi(argv[2]);
int pid = atoi(argv[3]);
#endif
if (pid) {
if (pid != 0) {
#ifdef XP_WIN
HANDLE parent = OpenProcess(SYNCHRONIZE, FALSE, (DWORD) pid);
// May return NULL if the parent process has already gone away.
@ -1508,29 +1515,29 @@ int NS_main(int argc, NS_tchar **argv)
// This is a terrible hack, but it'll have to do for now :-(
Sleep(50);
#else
int status;
waitpid(pid, &status, 0);
waitpid(pid, NULL, 0);
#endif
}
}
// The callback is the last N command line arguments starting from argOffset.
// The argument specified by argOffset is the callback executable and the
// argument prior to argOffset is the working directory.
const int argOffset = 4;
// The directory containing the update information.
gSourcePath = argv[1];
// The callback is the remaining arguments starting at callbackIndex.
// The argument specified by callbackIndex is the callback executable and the
// argument prior to callbackIndex is the working directory.
const int callbackIndex = 5;
#if defined(XP_WIN) && !defined(WINCE)
// Launch a second instance of the updater with the runas verb on Windows
// when write access is denied to the installation directory.
HANDLE updateLockFileHandle;
NS_tchar elevatedLockFilePath[MAXPATHLEN];
if (argc > argOffset) {
if (argc > callbackIndex) {
NS_tchar updateLockFilePath[MAXPATHLEN];
NS_tsnprintf(updateLockFilePath,
sizeof(updateLockFilePath)/sizeof(updateLockFilePath[0]),
NS_T("%s.update_in_progress.lock"), argv[argOffset]);
NS_T("%s.update_in_progress.lock"), argv[callbackIndex]);
// The update_in_progress.lock file should only exist during an update. In
// case it exists attempt to remove it and exit if that fails to prevent
@ -1602,8 +1609,8 @@ int NS_main(int argc, NS_tchar **argv)
WriteStatusFile(ELEVATION_CANCELED);
}
if (argc > argOffset) {
LaunchCallbackApp(argv[3], argc - argOffset, argv + argOffset);
if (argc > callbackIndex) {
LaunchCallbackApp(argv[4], argc - callbackIndex, argv + callbackIndex);
}
CloseHandle(elevatedFileHandle);
@ -1613,35 +1620,30 @@ int NS_main(int argc, NS_tchar **argv)
#endif
LogInit();
LOG(("SOURCE DIRECTORY " LOG_S "\n", gSourcePath));
// The destination directory (the same as the working-dir argument) does not
// have to be specified when updating manually.
if (argc > argOffset - 1) {
LOG(("DESTINATION DIRECTORY " LOG_S "\n", argv[3]));
}
LOG(("SOURCE DIRECTORY " LOG_S "\n", argv[1]));
LOG(("DESTINATION DIRECTORY " LOG_S "\n", argv[2]));
#ifdef WINCE
// This is the working directory to apply the update and is required on WinCE
// since it doesn't have the concept of a working directory.
gDestPath = argv[3];
gDestPath = argv[2];
#endif
#ifdef XP_WIN
HANDLE callbackFile = INVALID_HANDLE_VALUE;
NS_tchar callbackBackupPath[MAXPATHLEN];
if (argc > argOffset) {
if (argc > callbackIndex) {
// FindFirstFileW is used to get the callback's filename for comparison
// with the callback's patch since it will return the correct case and the
// long name instead of the 8.3 format name.
HANDLE hFindFile;
hFindFile = FindFirstFileW(argv[argOffset], &gFFData);
hFindFile = FindFirstFileW(argv[callbackIndex], &gFFData);
if (hFindFile == INVALID_HANDLE_VALUE) {
LOG(("NS_main: unable to find callback file: " LOG_S "\n", argv[argOffset]));
LOG(("NS_main: unable to find callback file: " LOG_S "\n", argv[callbackIndex]));
LogFinish();
WriteStatusFile(WRITE_ERROR);
EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 1);
LaunchCallbackApp(argv[3], argc - argOffset, argv + argOffset);
LaunchCallbackApp(argv[4], argc - callbackIndex, argv + callbackIndex);
return 1;
}
FindClose(hFindFile);
@ -1649,13 +1651,13 @@ int NS_main(int argc, NS_tchar **argv)
// Make a copy of the callback executable.
NS_tsnprintf(callbackBackupPath,
sizeof(callbackBackupPath)/sizeof(callbackBackupPath[0]),
NS_T("%s" CALLBACK_BACKUP_EXT), argv[argOffset]);
NS_T("%s" CALLBACK_BACKUP_EXT), argv[callbackIndex]);
NS_tremove(callbackBackupPath);
CopyFileW(argv[argOffset], callbackBackupPath, FALSE);
CopyFileW(argv[callbackIndex], callbackBackupPath, FALSE);
// By opening a file handle to the callback executable, the OS will prevent
// launching the process while it is being updated.
callbackFile = CreateFileW(argv[argOffset],
callbackFile = CreateFileW(argv[callbackIndex],
#ifdef WINCE
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
@ -1668,12 +1670,12 @@ int NS_main(int argc, NS_tchar **argv)
// it isn't possible to update write the status file and return.
if (callbackFile == INVALID_HANDLE_VALUE) {
LOG(("NS_main: file in use - failed to exclusively open executable " \
"file: " LOG_S "\n", argv[argOffset]));
"file: " LOG_S "\n", argv[callbackIndex]));
LogFinish();
WriteStatusFile(WRITE_ERROR);
NS_tremove(callbackBackupPath);
EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 1);
LaunchCallbackApp(argv[3], argc - argOffset, argv + argOffset);
LaunchCallbackApp(argv[4], argc - callbackIndex, argv + callbackIndex);
return 1;
}
}
@ -1696,7 +1698,8 @@ int NS_main(int argc, NS_tchar **argv)
NS_tremove(callbackBackupPath);
EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 1);
#endif
LaunchCallbackApp(argv[3], argc - argOffset, argv + argOffset);
if (argc > callbackIndex)
LaunchCallbackApp(argv[4], argc - callbackIndex, argv + callbackIndex);
return 1;
}
}
@ -1711,11 +1714,11 @@ int NS_main(int argc, NS_tchar **argv)
t.Join();
#ifdef XP_WIN
if (argc > argOffset) {
if (argc > callbackIndex) {
CloseHandle(callbackFile);
// CopyFile will preserve the case of the destination file if it already
// exists.
if (CopyFileW(callbackBackupPath, argv[argOffset], FALSE) != 0) {
if (CopyFileW(callbackBackupPath, argv[callbackIndex], FALSE) != 0) {
NS_tremove(callbackBackupPath);
}
}
@ -1725,19 +1728,19 @@ int NS_main(int argc, NS_tchar **argv)
free(BigBuffer);
BigBuffer = NULL;
if (argc > argOffset) {
if (argc > callbackIndex) {
#if defined(XP_WIN) && !defined(WINCE)
if (gSucceeded) {
LaunchWinPostProcess(argv[argOffset]);
LaunchWinPostProcess(argv[callbackIndex]);
}
EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 0);
#endif
#ifdef XP_MACOSX
if (gSucceeded) {
LaunchMacPostProcess(argv[argOffset]);
LaunchMacPostProcess(argv[callbackIndex]);
}
#endif /* XP_MACOSX */
LaunchCallbackApp(argv[3], argc - argOffset, argv + argOffset);
LaunchCallbackApp(argv[3], argc - callbackIndex, argv + callbackIndex);
}
return 0;

View File

@ -23,6 +23,7 @@
* Darin Fisher <darin@meer.net>
* Ben Turner <mozilla@songbirdnest.com>
* Robert Strong <robert.bugzilla@gmail.com>
* Josh Aas <josh@mozilla.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
@ -55,6 +56,7 @@
#ifdef XP_MACOSX
#include "nsILocalFileMac.h"
#include "nsCommandLineServiceMac.h"
#include "MacLaunchHelper.h"
#endif
#if defined(XP_WIN)
@ -79,7 +81,7 @@
// Windows.
//
// On platforms where we are not calling execv, we may need to make the
// udpaterfail executable wait for the calling process to exit. Otherwise, the
// updater executable wait for the calling process to exit. Otherwise, the
// updater may have trouble modifying our executable image (because it might
// still be in use). This is accomplished by passing our PID to the updater so
// that it can wait for us to exit. This is not perfect as there is a race
@ -136,43 +138,30 @@ GetCurrentWorkingDir(char *buf, size_t size)
}
#if defined(XP_MACOSX)
// This is a copy of OS X's XRE_GetBinaryPath from nsAppRunner.cpp with the
// gBinaryPath check removed so that the updater can reload the stub executable
// instead of xulrunner-bin. See bug 349737.
static nsresult
GetXULRunnerStubPath(const char* argv0, nsILocalFile* *aResult)
{
nsresult rv;
nsCOMPtr<nsILocalFile> lf;
NS_NewNativeLocalFile(EmptyCString(), PR_TRUE, getter_AddRefs(lf));
nsCOMPtr<nsILocalFileMac> lfm (do_QueryInterface(lf));
if (!lfm)
return NS_ERROR_FAILURE;
// Works even if we're not bundled.
CFBundleRef appBundle = CFBundleGetMainBundle();
CFBundleRef appBundle = ::CFBundleGetMainBundle();
if (!appBundle)
return NS_ERROR_FAILURE;
CFURLRef bundleURL = CFBundleCopyExecutableURL(appBundle);
CFURLRef bundleURL = ::CFBundleCopyExecutableURL(appBundle);
if (!bundleURL)
return NS_ERROR_FAILURE;
FSRef fileRef;
if (!CFURLGetFSRef(bundleURL, &fileRef)) {
CFRelease(bundleURL);
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsILocalFileMac> lfm;
nsresult rv = NS_NewLocalFileWithCFURL(bundleURL, PR_TRUE, getter_AddRefs(lfm));
rv = lfm->InitWithFSRef(&fileRef);
CFRelease(bundleURL);
::CFRelease(bundleURL);
if (NS_FAILED(rv))
return rv;
NS_ADDREF(*aResult = lf);
NS_ADDREF(*aResult = static_cast<nsILocalFile*>(lfm.get()));
return NS_OK;
}
#endif /* XP_MACOSX */
@ -456,22 +445,24 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsILocalFile *statusFile,
pid.AppendInt((PRInt32) getpid());
#endif
int argc = appArgc + 4;
int argc = appArgc + 5;
char **argv = new char*[argc + 1];
if (!argv)
return;
argv[0] = (char*) updaterPath.get();
argv[1] = (char*) updateDirPath.get();
argv[2] = (char*) pid.get();
argv[2] = (char*) applyToDir.get();
argv[3] = (char*) pid.get();
if (appArgc) {
argv[3] = workingDirPath;
argv[4] = (char*) appFilePath.get();
argv[4] = workingDirPath;
argv[5] = (char*) appFilePath.get();
for (int i = 1; i < appArgc; ++i)
argv[4 + i] = appArgv[i];
argv[4 + appArgc] = nsnull;
argv[5 + i] = appArgv[i];
argc = 5 + appArgc;
argv[argc] = NULL;
} else {
argv[3] = nsnull;
argc = 3;
argc = 4;
argv[4] = NULL;
}
if (gSafeMode) {
@ -481,36 +472,21 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsILocalFile *statusFile,
LOG(("spawning updater process [%s]\n", updaterPath.get()));
#if defined(USE_EXECV)
chdir(applyToDir.get());
execv(updaterPath.get(), argv);
#elif defined(XP_WIN)
_wchdir(applyToDir.get());
if (!WinLaunchChild(updaterPathW.get(), appArgc + 4, argv))
if (!WinLaunchChild(updaterPathW.get(), argc, argv))
return;
_exit(0);
#else
PRStatus status;
PRProcessAttr *attr;
attr = PR_NewProcessAttr();
if (!attr)
goto end;
status = PR_ProcessAttrSetCurrentDirectory(attr, applyToDir.get());
if (status != PR_SUCCESS)
goto end;
#ifdef XP_MACOSX
#elif defined(XP_MACOSX)
CommandLineServiceMac::SetupMacCommandLine(argc, argv, PR_TRUE);
#endif
PR_CreateProcessDetached(updaterPath.get(), argv, nsnull, attr);
// LaunchChildMac uses posix_spawnp and prefers the current
// architecture when launching. It doesn't require a
// null-terminated string but it doesn't matter if we pass one.
LaunchChildMac(argc, argv);
exit(0);
#else
PR_CreateProcessDetached(updaterPath.get(), argv, NULL, NULL);
exit(0);
end:
PR_DestroyProcessAttr(attr);
delete[] argv;
#endif
}