From f9a82146b7a3101c2efc42a716804b27f5720b50 Mon Sep 17 00:00:00 2001 From: Robert Strong Date: Wed, 12 Apr 2017 18:07:07 -0700 Subject: [PATCH] Bug 1355361 - nsUpdateProcessor::ProcessUpdate should use the directory service instead of nsXREDirProvider (also removes app update gonk support). r=mhowell Since the ApplyUpdate and SwitchToUpdatedApp functions are almost entirely the same this moves everything into ApplyUpdate. Removes most of the gonk code from application update. Makes client code and xpcshell tests use the same code for directory providers in nsUpdateDriver.cpp. --- .../update/tests/data/xpcshellUtilsAUS.js | 16 +- .../update/updater/automounter_gonk.cpp | 251 ------ .../mozapps/update/updater/automounter_gonk.h | 48 - .../update/updater/progressui_gonk.cpp | 53 -- .../update/updater/updater-common.build | 11 - toolkit/mozapps/update/updater/updater.cpp | 188 +--- toolkit/xre/nsAppRunner.cpp | 2 +- toolkit/xre/nsUpdateDriver.cpp | 828 +++++------------- toolkit/xre/nsUpdateDriver.h | 11 +- 9 files changed, 255 insertions(+), 1153 deletions(-) delete mode 100644 toolkit/mozapps/update/updater/automounter_gonk.cpp delete mode 100644 toolkit/mozapps/update/updater/automounter_gonk.h delete mode 100644 toolkit/mozapps/update/updater/progressui_gonk.cpp diff --git a/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js b/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js index 8c1000da1ace..86cfb5e44249 100644 --- a/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js +++ b/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js @@ -772,6 +772,8 @@ function setupTestCommon() { let caller = Components.stack.caller; gTestID = caller.filename.toString().split("/").pop().split(".")[0]; + createAppInfo("xpcshell@tests.mozilla.org", APP_INFO_NAME, "1.0", "2.0"); + // Tests that don't work with XULRunner. const XUL_RUNNER_INCOMPATIBLE = ["marAppApplyUpdateAppBinInUseStageSuccess_win", "marAppApplyUpdateStageSuccess", @@ -1105,7 +1107,6 @@ function checkPostUpdateRunningFile(aShouldExist) { * update service stub. */ function standardInit() { - createAppInfo("xpcshell@tests.mozilla.org", APP_INFO_NAME, "1.0", "2.0"); // Initialize the update service stub component initUpdateServiceStub(); } @@ -1892,10 +1893,15 @@ function stageUpdate(aCheckSvcLog) { setAppBundleModTime(); setEnvironment(); - // Stage the update. - Cc["@mozilla.org/updates/update-processor;1"]. - createInstance(Ci.nsIUpdateProcessor). - processUpdate(gUpdateManager.activeUpdate); + try { + // Stage the update. + Cc["@mozilla.org/updates/update-processor;1"]. + createInstance(Ci.nsIUpdateProcessor). + processUpdate(gUpdateManager.activeUpdate); + } catch (e) { + Assert.ok(false, + "error thrown while calling processUpdate, exception: " + e); + } // The environment is not reset here because processUpdate in // nsIUpdateProcessor uses a new thread and clearing the environment diff --git a/toolkit/mozapps/update/updater/automounter_gonk.cpp b/toolkit/mozapps/update/updater/automounter_gonk.cpp deleted file mode 100644 index 3dff2a1337a1..000000000000 --- a/toolkit/mozapps/update/updater/automounter_gonk.cpp +++ /dev/null @@ -1,251 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "automounter_gonk.h" -#include "updatedefines.h" -#include "updatelogging.h" - -#define LOG_TAG "GonkAutoMounter" - -#define GONK_LOG(level, format, ...) \ - LOG((LOG_TAG ": " format "\n", ##__VA_ARGS__)); \ - __android_log_print(level, LOG_TAG, format, ##__VA_ARGS__) - -#define LOGI(format, ...) GONK_LOG(ANDROID_LOG_INFO, format, ##__VA_ARGS__) -#define LOGE(format, ...) GONK_LOG(ANDROID_LOG_ERROR, format, ##__VA_ARGS__) - -const char *kGonkMountsPath = "/proc/mounts"; -const char *kGonkSystemPath = "/system"; - -GonkAutoMounter::GonkAutoMounter() : mDevice(nullptr), mAccess(Unknown) -{ - if (!RemountSystem(ReadWrite)) { - LOGE("Could not remount %s as read-write.", kGonkSystemPath); - } -} - -GonkAutoMounter::~GonkAutoMounter() -{ - bool result = RemountSystem(ReadOnly); - free(mDevice); - - if (!result) { - // Don't take any chances when remounting as read-only fails, just reboot. - Reboot(); - } -} - -void -GonkAutoMounter::Reboot() -{ - // The android_reboot wrapper provides more safety, doing fancier read-only - // remounting and attempting to sync() the filesystem first. If this fails - // our only hope is to force a reboot directly without these protections. - // For more, see system/core/libcutils/android_reboot.c - LOGE("Could not remount %s as read-only, forcing a system reboot.", - kGonkSystemPath); - LogFlush(); - - if (android_reboot(ANDROID_RB_RESTART, 0, nullptr) != 0) { - LOGE("Safe system reboot failed, attempting to force"); - LogFlush(); - - if (reboot(RB_AUTOBOOT) != 0) { - LOGE("CRITICAL: Failed to force restart"); - } - } -} - -static const char * -MountAccessToString(MountAccess access) -{ - switch (access) { - case ReadOnly: return "read-only"; - case ReadWrite: return "read-write"; - default: return "unknown"; - } -} - -bool -GonkAutoMounter::RemountSystem(MountAccess access) -{ - if (!UpdateMountStatus()) { - return false; - } - - if (mAccess == access) { - return true; - } - - unsigned long flags = MS_REMOUNT; - if (access == ReadOnly) { - flags |= MS_RDONLY; - // Give the system a chance to flush file buffers - sync(); - } - - if (!MountSystem(flags)) { - return false; - } - - // Check status again to verify /system has been properly remounted - if (!UpdateMountStatus()) { - return false; - } - - if (mAccess != access) { - LOGE("Updated mount status %s should be %s", - MountAccessToString(mAccess), - MountAccessToString(access)); - return false; - } - - return true; -} - -bool -GonkAutoMounter::UpdateMountStatus() -{ - FILE *mountsFile = NS_tfopen(kGonkMountsPath, "r"); - - if (mountsFile == nullptr) { - LOGE("Error opening %s: %s", kGonkMountsPath, strerror(errno)); - return false; - } - - // /proc/mounts returns a 0 size from fstat, so we use the same - // pre-allocated buffer size that ADB does here - const int mountsMaxSize = 4096; - char mountData[mountsMaxSize]; - size_t read = fread(mountData, 1, mountsMaxSize - 1, mountsFile); - mountData[read + 1] = '\0'; - - if (ferror(mountsFile)) { - LOGE("Error reading %s, %s", kGonkMountsPath, strerror(errno)); - fclose(mountsFile); - return false; - } - - char *token, *tokenContext; - bool foundSystem = false; - - for (token = strtok_r(mountData, "\n", &tokenContext); - token; - token = strtok_r(nullptr, "\n", &tokenContext)) - { - if (ProcessMount(token)) { - foundSystem = true; - break; - } - } - - fclose(mountsFile); - - if (!foundSystem) { - LOGE("Couldn't find %s mount in %s", kGonkSystemPath, kGonkMountsPath); - } - return foundSystem; -} - -bool -GonkAutoMounter::ProcessMount(const char *mount) -{ - const int strSize = 256; - char mountDev[strSize]; - char mountDir[strSize]; - char mountAccess[strSize]; - - int rv = sscanf(mount, "%255s %255s %*s %255s %*d %*d\n", - mountDev, mountDir, mountAccess); - mountDev[strSize - 1] = '\0'; - mountDir[strSize - 1] = '\0'; - mountAccess[strSize - 1] = '\0'; - - if (rv != 3) { - return false; - } - - if (strcmp(kGonkSystemPath, mountDir) != 0) { - return false; - } - - free(mDevice); - mDevice = strdup(mountDev); - mAccess = Unknown; - - char *option, *optionContext; - for (option = strtok_r(mountAccess, ",", &optionContext); - option; - option = strtok_r(nullptr, ",", &optionContext)) - { - if (strcmp("ro", option) == 0) { - mAccess = ReadOnly; - break; - } else if (strcmp("rw", option) == 0) { - mAccess = ReadWrite; - break; - } - } - - return true; -} - -/* - * Mark the given block device as read-write or read-only, using the BLKROSET - * ioctl. - */ -static void SetBlockReadWriteStatus(const char *blockdev, bool setReadOnly) { - int fd; - int roMode = setReadOnly ? 1 : 0; - - fd = open(blockdev, O_RDONLY); - if (fd < 0) { - return; - } - - if (ioctl(fd, BLKROSET, &roMode) == -1) { - LOGE("Error setting read-only mode on %s to %s: %s", blockdev, - setReadOnly ? "true": "false", strerror(errno)); - } - close(fd); -} - - -bool -GonkAutoMounter::MountSystem(unsigned long flags) -{ - if (!mDevice) { - LOGE("No device was found for %s", kGonkSystemPath); - return false; - } - - // Without setting the block device ro mode to false, we get a permission - // denied error while trying to remount it in read-write. - SetBlockReadWriteStatus(mDevice, (flags & MS_RDONLY)); - - const char *readOnly = flags & MS_RDONLY ? "read-only" : "read-write"; - int result = mount(mDevice, kGonkSystemPath, "none", flags, nullptr); - - if (result != 0) { - LOGE("Error mounting %s as %s: %s", kGonkSystemPath, readOnly, - strerror(errno)); - return false; - } - - LOGI("Mounted %s partition as %s", kGonkSystemPath, readOnly); - return true; -} diff --git a/toolkit/mozapps/update/updater/automounter_gonk.h b/toolkit/mozapps/update/updater/automounter_gonk.h deleted file mode 100644 index e40cacbc2e15..000000000000 --- a/toolkit/mozapps/update/updater/automounter_gonk.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef AUTOMOUNTER_GONK_H__ -#define AUTOMOUNTER_GONK_H__ - -typedef enum { - ReadOnly, - ReadWrite, - Unknown -} MountAccess; - -/** - * This class will remount the /system partition as read-write in Gonk to allow - * the updater write access. Upon destruction, /system will be remounted back to - * read-only. If something causes /system to remain read-write, this class will - * reboot the device and allow the system to mount as read-only. - * - * Code inspired from AOSP system/core/adb/remount_service.c - */ -class GonkAutoMounter -{ -public: - GonkAutoMounter(); - ~GonkAutoMounter(); - - MountAccess GetAccess() const - { - return mAccess; - } - -private: - bool RemountSystem(MountAccess access); - bool ForceRemountReadOnly(); - bool UpdateMountStatus(); - bool ProcessMount(const char *mount); - bool MountSystem(unsigned long flags); - void Reboot(); - -private: - char *mDevice; - MountAccess mAccess; -}; - -#endif // AUTOMOUNTER_GONK_H__ diff --git a/toolkit/mozapps/update/updater/progressui_gonk.cpp b/toolkit/mozapps/update/updater/progressui_gonk.cpp deleted file mode 100644 index f77d0af6338a..000000000000 --- a/toolkit/mozapps/update/updater/progressui_gonk.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: sw=2 ts=8 et : - */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include -#include - -#include - -#include "android/log.h" - -#include "progressui.h" - -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoUpdater" , ## args) - -using namespace std; - -int InitProgressUI(int *argc, char ***argv) -{ - return 0; -} - -int ShowProgressUI() -{ - LOG("Starting to apply update ...\n"); - return 0; -} - -void QuitProgressUI() -{ - LOG("Finished applying update\n"); -} - -void UpdateProgressUI(float progress) -{ - assert(0.0f <= progress && progress <= 100.0f); - - static const size_t kProgressBarLength = 50; - static size_t sLastNumBars; - size_t numBars = size_t(float(kProgressBarLength) * progress / 100.0f); - if (numBars == sLastNumBars) { - return; - } - sLastNumBars = numBars; - - size_t numSpaces = kProgressBarLength - numBars; - string bars(numBars, '='); - string spaces(numSpaces, ' '); - LOG("Progress [ %s%s ]\n", bars.c_str(), spaces.c_str()); -} diff --git a/toolkit/mozapps/update/updater/updater-common.build b/toolkit/mozapps/update/updater/updater-common.build index 2d0a9c8ee70a..228d87011799 100644 --- a/toolkit/mozapps/update/updater/updater-common.build +++ b/toolkit/mozapps/update/updater/updater-common.build @@ -90,17 +90,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': LOCAL_INCLUDES += [ '/toolkit/xre', ] -elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': - have_progressui = 1 - srcs += [ - 'automounter_gonk.cpp', - 'progressui_gonk.cpp', - ] - DISABLE_STL_WRAPPING = True - OS_LIBS += [ - 'cutils', - 'sysutils', - ] if have_progressui == 0: srcs += [ diff --git a/toolkit/mozapps/update/updater/updater.cpp b/toolkit/mozapps/update/updater/updater.cpp index 521d759bd53d..f7ed1da653f8 100644 --- a/toolkit/mozapps/update/updater/updater.cpp +++ b/toolkit/mozapps/update/updater/updater.cpp @@ -110,33 +110,8 @@ struct UpdateServerThreadArgs #define USE_EXECV #endif -#if defined(MOZ_WIDGET_GONK) -# include "automounter_gonk.h" -# include -# include -# include -# include - -#if ANDROID_VERSION < 21 -// The only header file in bionic which has a function prototype for ioprio_set -// is libc/include/sys/linux-unistd.h. However, linux-unistd.h conflicts -// badly with unistd.h, so we declare the prototype for ioprio_set directly. -extern "C" MOZ_EXPORT int ioprio_set(int which, int who, int ioprio); -#else -# include -static int ioprio_set(int which, int who, int ioprio) { - return syscall(__NR_ioprio_set, which, who, ioprio); -} -#endif - -# define MAYBE_USE_HARD_LINKS 1 -static bool sUseHardLinks = true; -#else -# define MAYBE_USE_HARD_LINKS 0 -#endif - #if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && \ - !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK) + !defined(XP_MACOSX) #include "nss.h" #include "prerror.h" #endif @@ -314,7 +289,6 @@ static bool gSucceeded = false; static bool sStagedUpdate = false; static bool sReplaceRequest = false; static bool sUsingService = false; -static bool sIsOSUpdate = false; #ifdef XP_WIN // The current working directory specified in the command line. @@ -375,12 +349,20 @@ mstrtok(const NS_tchar *delims, NS_tchar **str) return ret; } +#if defined(TEST_UPDATER) +#define HAS_ENV_CHECK 1 +#elif defined(MOZ_MAINTENANCE_SERVICE) +#define HAS_ENV_CHECK 1 +#endif + +#if defined(HAS_ENV_CHECK) static bool EnvHasValue(const char *name) { const char *val = getenv(name); return (val && *val); } +#endif /** * Coverts a relative update path to a full path. @@ -688,25 +670,6 @@ static int ensure_copy_symlink(const NS_tchar *path, const NS_tchar *dest) } #endif -#if MAYBE_USE_HARD_LINKS -/* - * Creates a hardlink (destFilename) which points to the existing file - * (srcFilename). - * - * @return 0 if successful, an error otherwise - */ - -static int -create_hard_link(const NS_tchar *srcFilename, const NS_tchar *destFilename) -{ - if (link(srcFilename, destFilename) < 0) { - LOG(("link(%s, %s) failed errno = %d", srcFilename, destFilename, errno)); - return WRITE_ERROR; - } - return OK; -} -#endif - // Copy the file named path onto a new file named dest. static int ensure_copy(const NS_tchar *path, const NS_tchar *dest) { @@ -734,16 +697,6 @@ static int ensure_copy(const NS_tchar *path, const NS_tchar *dest) } #endif -#if MAYBE_USE_HARD_LINKS - if (sUseHardLinks) { - if (!create_hard_link(path, dest)) { - return OK; - } - // Since we failed to create the hard link, fall through and copy the file. - sUseHardLinks = false; - } -#endif - AutoFile infile(ensure_open(path, NS_T("rb"), ss.st_mode)); if (!infile) { LOG(("ensure_copy: failed to open the file for reading: " LOG_S ", err: %d", @@ -2476,44 +2429,8 @@ ReadMARChannelIDs(const NS_tchar *path, MARChannelStringTable *results) static int GetUpdateFileName(NS_tchar *fileName, int maxChars) { -#if defined(MOZ_WIDGET_GONK) - // If an update.link file exists, then it will contain the name - // of the update file (terminated by a newline). - - NS_tchar linkFileName[MAXPATHLEN]; - NS_tsnprintf(linkFileName, sizeof(linkFileName)/sizeof(linkFileName[0]), - NS_T("%s/update.link"), gPatchDirPath); - AutoFile linkFile(NS_tfopen(linkFileName, NS_T("rb"))); - if (linkFile == nullptr) { - NS_tsnprintf(fileName, maxChars, - NS_T("%s/update.mar"), gPatchDirPath); - return OK; - } - - char dataFileName[MAXPATHLEN]; - size_t bytesRead; - - if ((bytesRead = fread(dataFileName, 1, sizeof(dataFileName)-1, linkFile)) <= 0) { - *fileName = NS_T('\0'); - return READ_ERROR; - } - if (dataFileName[bytesRead-1] == '\n') { - // Strip trailing newline (for \n and \r\n) - bytesRead--; - } - if (dataFileName[bytesRead-1] == '\r') { - // Strip trailing CR (for \r, \r\n) - bytesRead--; - } - dataFileName[bytesRead] = '\0'; - - strncpy(fileName, dataFileName, maxChars-1); - fileName[maxChars-1] = '\0'; -#else - // We currently only support update.link files under GONK NS_tsnprintf(fileName, maxChars, NS_T("%s/update.mar"), gPatchDirPath); -#endif return OK; } @@ -2595,7 +2512,7 @@ UpdateThreadFunc(void *param) } #endif - if (rv == OK && sStagedUpdate && !sIsOSUpdate) { + if (rv == OK && sStagedUpdate) { #ifdef TEST_UPDATER // The MOZ_TEST_SKIP_UPDATE_STAGE environment variable prevents copying // the files in dist/bin in the test updater when staging an update since @@ -2757,26 +2674,7 @@ int NS_main(int argc, NS_tchar **argv) } #endif -#if defined(MOZ_WIDGET_GONK) - if (EnvHasValue("LD_PRELOAD")) { - // If the updater is launched with LD_PRELOAD set, then we wind up - // preloading libmozglue.so. Under some circumstances, this can cause - // the remount of /system to fail when going from rw to ro, so if we - // detect LD_PRELOAD we unsetenv it and relaunch ourselves without it. - // This will cause the offending preloaded library to be closed. - // - // For a variety of reasons, this is really hard to do in a safe manner - // in the parent process, so we do it here. - unsetenv("LD_PRELOAD"); - execv(argv[0], argv); - __android_log_print(ANDROID_LOG_INFO, "updater", - "execve failed: errno: %d. Exiting...", errno); - _exit(1); - } -#endif - -#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && \ - !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK) +#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && !defined(XP_MACOSX) // On Windows and Mac we rely on native APIs to do verifications so we don't // need to initialize NSS at all there. // Otherwise, minimize the amount of NSS we depend on by avoiding all the NSS @@ -2929,11 +2827,6 @@ int NS_main(int argc, NS_tchar **argv) } #endif - if (EnvHasValue("MOZ_OS_UPDATE")) { - sIsOSUpdate = true; - putenv(const_cast("MOZ_OS_UPDATE=")); - } - LogInit(gPatchDirPath, NS_T("update.log")); if (!WriteStatusFile("applying")) { @@ -2988,36 +2881,6 @@ int NS_main(int argc, NS_tchar **argv) } #endif -#ifdef MOZ_WIDGET_GONK - const char *prioEnv = getenv("MOZ_UPDATER_PRIO"); - if (prioEnv) { - int32_t prioVal; - int32_t oomScoreAdj; - int32_t ioprioClass; - int32_t ioprioLevel; - if (sscanf(prioEnv, "%d/%d/%d/%d", - &prioVal, &oomScoreAdj, &ioprioClass, &ioprioLevel) == 4) { - LOG(("MOZ_UPDATER_PRIO=%s", prioEnv)); - if (setpriority(PRIO_PROCESS, 0, prioVal)) { - LOG(("setpriority(%d) failed, errno = %d", prioVal, errno)); - } - if (ioprio_set(IOPRIO_WHO_PROCESS, 0, - IOPRIO_PRIO_VALUE(ioprioClass, ioprioLevel))) { - LOG(("ioprio_set(%d,%d) failed: errno = %d", - ioprioClass, ioprioLevel, errno)); - } - FILE *fs = fopen("/proc/self/oom_score_adj", "w"); - if (fs) { - fprintf(fs, "%d", oomScoreAdj); - fclose(fs); - } else { - LOG(("Unable to open /proc/self/oom_score_adj for writing, errno = %d", - errno)); - } - } - } -#endif - #ifdef XP_WIN if (pid > 0) { HANDLE parent = OpenProcess(SYNCHRONIZE, false, (DWORD) pid); @@ -3342,27 +3205,6 @@ int NS_main(int argc, NS_tchar **argv) } #endif -#if defined(MOZ_WIDGET_GONK) - // In gonk, the master b2g process sets its umask to 0027 because - // there's no reason for it to ever create world-readable files. - // The updater binary, however, needs to do this, and it inherits - // the master process's cautious umask. So we drop down a bit here. - umask(0022); - - // Remount the /system partition as read-write for gonk. The destructor will - // remount /system as read-only. We add an extra level of scope here to avoid - // calling LogFinish() before the GonkAutoMounter destructor has a chance - // to be called - { -#if !defined(TEST_UPDATER) - GonkAutoMounter mounter; - if (mounter.GetAccess() != MountAccess::ReadWrite) { - WriteStatusFile(FILESYSTEM_MOUNT_READWRITE_ERROR); - return 1; - } -#endif -#endif - if (sStagedUpdate) { // When staging updates, blow away the old installation directory and create // it from scratch. @@ -3662,10 +3504,6 @@ int NS_main(int argc, NS_tchar **argv) } #endif /* XP_WIN */ -#if defined(MOZ_WIDGET_GONK) - } // end the extra level of scope for the GonkAutoMounter -#endif - #ifdef XP_MACOSX // When the update is successful remove the precomplete file in the root of // the application bundle and move the distribution directory from @@ -4177,10 +4015,6 @@ GetManifestContents(const NS_tchar *manifest) int AddPreCompleteActions(ActionList *list) { - if (sIsOSUpdate) { - return OK; - } - #ifdef XP_MACOSX mozilla::UniquePtr manifestPath(get_full_path( NS_T("Contents/Resources/precomplete"))); diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index 1423170a5ab5..e4adbde5924a 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -3965,7 +3965,7 @@ XREMain::XRE_mainStartup(bool* aExitFlag) } #endif -#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID) +#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_WIDGET_GONK) // Check for and process any available updates nsCOMPtr updRoot; bool persistent; diff --git a/toolkit/xre/nsUpdateDriver.cpp b/toolkit/xre/nsUpdateDriver.cpp index fcbdcb9e08ad..7551f9aee751 100644 --- a/toolkit/xre/nsUpdateDriver.cpp +++ b/toolkit/xre/nsUpdateDriver.cpp @@ -18,8 +18,6 @@ #include "mozilla/Logging.h" #include "prenv.h" #include "nsVersionComparator.h" -#include "nsXREDirProvider.h" -#include "SpecialSystemDirectory.h" #include "nsDirectoryServiceDefs.h" #include "nsThreadUtils.h" #include "nsIXULAppInfo.h" @@ -40,7 +38,6 @@ # include # include # include -# include "nsWindowsHelpers.h" # define getcwd(path, size) _getcwd(path, size) # define getpid() GetCurrentProcessId() #elif defined(XP_UNIX) @@ -68,23 +65,6 @@ static LazyLogModule sUpdateLog("updatedriver"); #define UPDATER_PNG "updater.png" #endif -#if defined(MOZ_WIDGET_GONK) -#include - -static const int kB2GServiceArgc = 2; -static const char *kB2GServiceArgv[] = { "/system/bin/start", "b2g" }; - -static const char kAppUpdaterPrio[] = "app.update.updater.prio"; -static const char kAppUpdaterOomScoreAdj[] = "app.update.updater.oom_score_adj"; -static const char kAppUpdaterIOPrioClass[] = "app.update.updater.ioprio.class"; -static const char kAppUpdaterIOPrioLevel[] = "app.update.updater.ioprio.level"; - -static const int kAppUpdaterPrioDefault = 19; // -20..19 where 19 = lowest priority -static const int kAppUpdaterOomScoreAdjDefault = -1000; // -1000 = Never kill -static const int kAppUpdaterIOPrioClassDefault = IOPRIO_CLASS_IDLE; -static const int kAppUpdaterIOPrioLevelDefault = 0; // Doesn't matter for CLASS IDLE -#endif - #ifdef XP_MACOSX static void UpdateDriverSetupMacCommandLine(int& argc, char**& argv, bool restart) @@ -376,7 +356,7 @@ static bool CopyUpdaterIntoUpdateDir(nsIFile *greDir, nsIFile *appDir, nsIFile *updateDir, nsCOMPtr &updater) { - // Copy the updater application from the GRE and the updater ini from the app + // Copy the updater application from the GRE and the updater ini from the app. #if defined(XP_MACOSX) if (!CopyFileIntoUpdateDir(appDir, NS_LITERAL_CSTRING(UPDATER_APP), updateDir)) return false; @@ -417,8 +397,7 @@ CopyUpdaterIntoUpdateDir(nsIFile *greDir, nsIFile *appDir, nsIFile *updateDir, * * @param pathToAppend A new library path to prepend to LD_LIBRARY_PATH */ -#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && \ - !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK) +#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && !defined(XP_MACOSX) #include "prprf.h" #define PATH_SEPARATOR ":" #define LD_LIBRARY_PATH_ENVVAR_NAME "LD_LIBRARY_PATH" @@ -443,439 +422,193 @@ AppendToLibPath(const char *pathToAppend) #endif /** - * Switch an existing application directory to an updated version that has been - * staged. + * Applies, switches, or stages an update. * - * @param greDir the GRE dir - * @param updateDir the update dir where the mar file is located - * @param appDir the app dir - * @param appArgc the number of args to the application - * @param appArgv the args to the application, used for restarting if needed + * @param greDir the GRE directory + * @param updateDir the update root directory + * @param appDir the application directory + * @param appArgc the number of args passed to the application + * @param appArgv the args passed to the application + * (used for restarting the application when necessary) + * @param restart true when a restart is necessary. + * @param isStaged true when the update has already been staged + * @param outpid (out) parameter holding the handle to the updater application + * when staging updates */ static void -SwitchToUpdatedApp(nsIFile *greDir, nsIFile *updateDir, - nsIFile *appDir, int appArgc, char **appArgv) +ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *appDir, int appArgc, + char **appArgv, bool restart, bool isStaged, ProcessType *outpid) { + // The following determines the update operation to perform. + // 1. When restart is false the update will be staged. + // 2. When restart is true and isStaged is false the update will apply the mar + // file to the installation directory. + // 3. When restart is true and isStaged is true the update will switch the + // staged update with the installation directory. + nsresult rv; - // Steps: - // - copy updater into updates/0/MozUpdater/bgupdate/ dir on all platforms - // except Windows - // - run updater with the correct arguments -#ifndef XP_WIN - nsCOMPtr mozUpdaterDir; - rv = updateDir->Clone(getter_AddRefs(mozUpdaterDir)); - if (NS_FAILED(rv)) { - LOG(("failed cloning update dir\n")); - return; - } - - // Create a new directory named MozUpdater in the updates/0 directory to copy - // the updater files to that will be used to replace the installation with the - // staged application that has been updated. Note that we don't check for - // directory creation errors since the call to CopyUpdaterIntoUpdateDir will - // fail if the creation of the directory fails. A unique directory is created - // in MozUpdater in case a previous attempt locked the directory or files. - mozUpdaterDir->Append(NS_LITERAL_STRING("MozUpdater")); - mozUpdaterDir->Append(NS_LITERAL_STRING("bgupdate")); - rv = mozUpdaterDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0755); - if (NS_FAILED(rv)) { - LOG(("failed creating unique dir\n")); - return; - } - nsCOMPtr updater; - if (!CopyUpdaterIntoUpdateDir(greDir, appDir, mozUpdaterDir, updater)) { - LOG(("failed copying updater\n")); - return; - } -#endif - - // We need to use the value returned from XRE_GetBinaryPath when attempting - // to restart the running application. - nsCOMPtr appFile; - -#if defined(XP_MACOSX) - // On OS X we need to pass the location of the xulrunner-stub executable - // rather than xulrunner-bin. See bug 349737. - GetXULRunnerStubPath(appArgv[0], getter_AddRefs(appFile)); -#else - XRE_GetBinaryPath(appArgv[0], getter_AddRefs(appFile)); -#endif - - if (!appFile) - return; - -#ifdef XP_WIN - nsAutoString appFilePathW; - rv = appFile->GetPath(appFilePathW); - if (NS_FAILED(rv)) { - return; - } - NS_ConvertUTF16toUTF8 appFilePath(appFilePathW); - - nsCOMPtr updater; - rv = greDir->Clone(getter_AddRefs(updater)); - if (NS_FAILED(rv)) { - return; - } - - rv = updater->AppendNative(NS_LITERAL_CSTRING(UPDATER_BIN)); - if (NS_FAILED(rv)) { + nsAutoCString updaterPath; + nsAutoCString updateDirPath; +#if defined(XP_WIN) + // Get an nsIFile reference for the updater in the installation dir. + if (!GetFile(greDir, NS_LITERAL_CSTRING(UPDATER_BIN), updater)) { return; } + // Get the path to the updater. nsAutoString updaterPathW; rv = updater->GetPath(updaterPathW); if (NS_FAILED(rv)) { return; } - NS_ConvertUTF16toUTF8 updaterPath(updaterPathW); -#else + updaterPath = NS_ConvertUTF16toUTF8(updaterPathW); - nsAutoCString appFilePath; -#if defined(MOZ_WIDGET_GONK) - appFilePath.Assign(kB2GServiceArgv[0]); - appArgc = kB2GServiceArgc; - appArgv = const_cast(kB2GServiceArgv); -#else - rv = appFile->GetNativePath(appFilePath); - if (NS_FAILED(rv)) - return; -#endif - - nsAutoCString updaterPath; - rv = updater->GetNativePath(updaterPath); - if (NS_FAILED(rv)) - return; -#endif - - nsAutoCString installDirPath; - rv = GetInstallDirPath(appDir, installDirPath); - if (NS_FAILED(rv)) { - return; - } - - // Get the directory where the update will be staged. - nsAutoCString applyToDir; - nsCOMPtr updatedDir; -#ifdef XP_MACOSX - if (!GetFile(updateDir, NS_LITERAL_CSTRING("Updated.app"), updatedDir)) { -#else - if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) { -#endif - return; - } -#ifdef XP_WIN - nsAutoString applyToDirW; - rv = updatedDir->GetPath(applyToDirW); - if (NS_FAILED(rv)) { - return; - } - applyToDir = NS_ConvertUTF16toUTF8(applyToDirW); -#else - rv = updatedDir->GetNativePath(applyToDir); -#endif - if (NS_FAILED(rv)) { - return; - } - - // Make sure that the updated directory exists - bool updatedDirExists = false; - updatedDir->Exists(&updatedDirExists); - if (!updatedDirExists) { - return; - } - -#if defined(XP_WIN) + // Get the path to the update dir. nsAutoString updateDirPathW; rv = updateDir->GetPath(updateDirPathW); - NS_ConvertUTF16toUTF8 updateDirPath(updateDirPathW); + if (NS_FAILED(rv)) { + return; + } + updateDirPath = NS_ConvertUTF16toUTF8(updateDirPathW); #else - nsAutoCString updateDirPath; + if (isStaged) { + nsCOMPtr mozUpdaterDir; + rv = updateDir->Clone(getter_AddRefs(mozUpdaterDir)); + if (NS_FAILED(rv)) { + LOG(("failed cloning update dir\n")); + return; + } + + // Create a new directory named MozUpdater in the updates/0 directory to copy + // the updater files to that will be used to replace the installation with the + // staged application that has been updated. Note that we don't check for + // directory creation errors since the call to CopyUpdaterIntoUpdateDir will + // fail if the creation of the directory fails. A unique directory is created + // in MozUpdater in case a previous attempt locked the directory or files. + mozUpdaterDir->Append(NS_LITERAL_STRING("MozUpdater")); + mozUpdaterDir->Append(NS_LITERAL_STRING("bgupdate")); + rv = mozUpdaterDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0755); + if (NS_FAILED(rv)) { + LOG(("failed creating unique dir\n")); + return; + } + + // Copy the updater and files needed to update into the MozUpdater/bgupdate + // directory in the update dir and get an nsIFile reference to the copied + // updater. + if (!CopyUpdaterIntoUpdateDir(greDir, appDir, mozUpdaterDir, updater)) { + LOG(("failed copying updater\n")); + return; + } + } else { + // Copy the updater and files needed to update into the update directory and + // get an nsIFile reference to the copied updater. + if (!CopyUpdaterIntoUpdateDir(greDir, appDir, updateDir, updater)) { + LOG(("failed copying updater\n")); + return; + } + } + + // Get the path to the updater that will be used. + rv = updater->GetNativePath(updaterPath); + if (NS_FAILED(rv)) { + return; + } + + // Get the path to the update dir. rv = updateDir->GetNativePath(updateDirPath); + if (NS_FAILED(rv)) { + return; + } #endif - if (NS_FAILED(rv)) - return; - // Get the current working directory. + // appFilePath and workingDirPath are only used when the application will be + // restarted. + nsAutoCString appFilePath; char workingDirPath[MAXPATHLEN]; - rv = GetCurrentWorkingDir(workingDirPath, sizeof(workingDirPath)); - if (NS_FAILED(rv)) - return; + if (restart) { + // Get the path to the current working directory. + rv = GetCurrentWorkingDir(workingDirPath, sizeof(workingDirPath)); + if (NS_FAILED(rv)) { + return; + } - // Construct the PID argument for this process. We start the updater using - // execv on all Unix platforms except Mac, so on those platforms we pass 0 - // instead of a good PID to signal the updater not to try and wait for us. -#if defined(XP_UNIX) & !defined(XP_MACOSX) - nsAutoCString pid("0"); + // Get the application file path used by the updater to restart the + // application after the update has finished. + nsCOMPtr appFile; +#if defined(XP_MACOSX) + // On OS X we need to pass the location of the xulrunner-stub executable + // rather than xulrunner-bin. See bug 349737. + GetXULRunnerStubPath(appArgv[0], getter_AddRefs(appFile)); #else - nsAutoCString pid; - pid.AppendInt((int32_t) getpid()); + XRE_GetBinaryPath(appArgv[0], getter_AddRefs(appFile)); #endif + if (!appFile) { + return; + } - // Append a special token to the PID in order to let the updater know that it - // just needs to replace the update directory. - pid.AppendLiteral("/replace"); - - int immersiveArgc = 0; - int argc = appArgc + 6 + immersiveArgc; - char **argv = new char*[argc + 1]; - if (!argv) - return; - argv[0] = (char*) updaterPath.get(); - argv[1] = (char*) updateDirPath.get(); - argv[2] = (char*) installDirPath.get(); - argv[3] = (char*) applyToDir.get(); - argv[4] = (char*) pid.get(); - if (appArgc) { - argv[5] = workingDirPath; - argv[6] = (char*) appFilePath.get(); - for (int i = 1; i < appArgc; ++i) - argv[6 + i] = appArgv[i]; -#ifdef XP_WIN - if (immersiveArgc) { - argv[argc - 1] = "-ServerName:DefaultBrowserServer"; +#if defined(XP_WIN) + nsAutoString appFilePathW; + rv = appFile->GetPath(appFilePathW); + if (NS_FAILED(rv)) { + return; + } + appFilePath = NS_ConvertUTF16toUTF8(appFilePathW); +#else + rv = appFile->GetNativePath(appFilePath); + if (NS_FAILED(rv)) { + return; } #endif - argv[argc] = nullptr; - } else { - argc = 5; - argv[5] = nullptr; } - if (gSafeMode) { - PR_SetEnv("MOZ_SAFE_MODE_RESTART=1"); - } -#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && \ - !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK) - AppendToLibPath(installDirPath.get()); -#endif - - LOG(("spawning updater process for replacing [%s]\n", updaterPath.get())); - -#if defined(XP_UNIX) & !defined(XP_MACOSX) -# if defined(MOZ_WIDGET_GONK) - // In Gonk, we preload libmozglue, which the updater process doesn't need. - // Since the updater will move and delete libmozglue.so, this can actually - // stop the /system mount from correctly being remounted as read-only. - unsetenv("LD_PRELOAD"); -# endif - exit(execv(updaterPath.get(), argv)); -#elif defined(XP_WIN) - // Switch the application using updater.exe - if (!WinLaunchChild(updaterPathW.get(), argc, argv)) { - return; - } - _exit(0); -#elif defined(XP_MACOSX) - UpdateDriverSetupMacCommandLine(argc, argv, true); - LaunchChildMac(argc, argv); - exit(0); -#else - PR_CreateProcessDetached(updaterPath.get(), argv, nullptr, nullptr); - exit(0); -#endif -} - -#if defined(MOZ_WIDGET_GONK) -static nsresult -GetOSApplyToDir(nsACString& applyToDir) -{ - nsCOMPtr ds = - do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); - NS_ASSERTION(ds, "Can't get directory service"); - - nsCOMPtr osApplyToDir; - nsresult rv = ds->Get(XRE_OS_UPDATE_APPLY_TO_DIR, NS_GET_IID(nsIFile), - getter_AddRefs(osApplyToDir)); - if (NS_FAILED(rv)) { - LOG(("Can't get the OS applyTo dir")); - return rv; - } - - return osApplyToDir->GetNativePath(applyToDir); -} - -static void -SetOSApplyToDir(nsIUpdate* update, const nsACString& osApplyToDir) -{ - nsresult rv; - nsCOMPtr updateProperties = - do_QueryInterface(update, &rv); - - if (NS_FAILED(rv)) { - return; - } - - RefPtr variant = new nsVariant(); - rv = variant->SetAsACString(osApplyToDir); - if (NS_FAILED(rv)) { - return; - } - - updateProperties->SetProperty(NS_LITERAL_STRING("osApplyToDir"), variant); -} -#endif - -/** - * Apply an update. This applies to both normal and staged updates. - * - * @param greDir the GRE dir - * @param updateDir the update root dir - * @param statusFile the update.status file - * @param appDir the app dir - * @param appArgc the number of args to the application - * @param appArgv the args to the application, used for restarting if needed - * @param restart if true, apply the update in the foreground and restart the - * application when done. otherwise, stage the update and don't - * restart the application. - * @param outpid out parameter holding the handle to the updater application for - * staging updates. - */ -static void -ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, - nsIFile *appDir, int appArgc, char **appArgv, - bool restart, bool isOSUpdate, nsIFile *osApplyToDir, - ProcessType *outpid) -{ - nsresult rv; - - // Steps: - // - mark update as 'applying' - // - copy updater into update dir on all platforms except Windows - // - run updater w/ appDir as the current working dir -#ifndef XP_WIN - nsCOMPtr updater; - if (!CopyUpdaterIntoUpdateDir(greDir, appDir, updateDir, updater)) { - LOG(("failed copying updater\n")); - return; - } -#endif - - // We need to use the value returned from XRE_GetBinaryPath when attempting - // to restart the running application. - nsCOMPtr appFile; - -#if defined(XP_MACOSX) - // On OS X we need to pass the location of the xulrunner-stub executable - // rather than xulrunner-bin. See bug 349737. - GetXULRunnerStubPath(appArgv[0], getter_AddRefs(appFile)); -#else - XRE_GetBinaryPath(appArgv[0], getter_AddRefs(appFile)); -#endif - - if (!appFile) - return; - -#ifdef XP_WIN - nsAutoString appFilePathW; - rv = appFile->GetPath(appFilePathW); - if (NS_FAILED(rv)) { - return; - } - NS_ConvertUTF16toUTF8 appFilePath(appFilePathW); - - nsCOMPtr updater; - rv = greDir->Clone(getter_AddRefs(updater)); - if (NS_FAILED(rv)) { - return; - } - - rv = updater->AppendNative(NS_LITERAL_CSTRING(UPDATER_BIN)); - if (NS_FAILED(rv)) { - return; - } - - nsAutoString updaterPathW; - rv = updater->GetPath(updaterPathW); - if (NS_FAILED(rv)) { - return; - } - NS_ConvertUTF16toUTF8 updaterPath(updaterPathW); -#else - nsAutoCString appFilePath; - rv = appFile->GetNativePath(appFilePath); - if (NS_FAILED(rv)) - return; - - nsAutoCString updaterPath; - rv = updater->GetNativePath(updaterPath); - if (NS_FAILED(rv)) - return; - -#endif - + // Get the installation directory path. nsAutoCString installDirPath; rv = GetInstallDirPath(appDir, installDirPath); - if (NS_FAILED(rv)) + if (NS_FAILED(rv)) { return; + } - // Get the directory where the update was staged for replace and GONK OS - // Updates or where it will be applied. -#ifndef MOZ_WIDGET_GONK - // OS Updates are only supported on GONK so force it to false on everything - // but GONK to simplify the following logic. - isOSUpdate = false; -#endif - nsAutoCString applyToDir; + nsAutoCString applyToDirPath; nsCOMPtr updatedDir; - if (restart && !isOSUpdate) { - applyToDir.Assign(installDirPath); + if (restart && !isStaged) { + // The install directory is the same as the apply to directory. + applyToDirPath.Assign(installDirPath); } else { -#ifdef XP_MACOSX + // Get the directory where the update is staged or will be staged. +#if defined(XP_MACOSX) if (!GetFile(updateDir, NS_LITERAL_CSTRING("Updated.app"), updatedDir)) { #else if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) { #endif return; } -#ifdef XP_WIN - nsAutoString applyToDirW; - rv = updatedDir->GetPath(applyToDirW); +#if defined(XP_WIN) + nsAutoString applyToDirPathW; + rv = updatedDir->GetPath(applyToDirPathW); if (NS_FAILED(rv)) { return; } - applyToDir = NS_ConvertUTF16toUTF8(applyToDirW); -#elif MOZ_WIDGET_GONK - if (isOSUpdate) { - if (!osApplyToDir) { - return; - } - rv = osApplyToDir->GetNativePath(applyToDir); - } else { - rv = updatedDir->GetNativePath(applyToDir); - } + applyToDirPath = NS_ConvertUTF16toUTF8(applyToDirPathW); + rv = updatedDir->GetNativePath(applyToDirPath); #else - rv = updatedDir->GetNativePath(applyToDir); + rv = updatedDir->GetNativePath(applyToDirPath); #endif } - if (NS_FAILED(rv)) - return; - -#if defined(XP_WIN) - nsAutoString updateDirPathW; - rv = updateDir->GetPath(updateDirPathW); - NS_ConvertUTF16toUTF8 updateDirPath(updateDirPathW); -#else - nsAutoCString updateDirPath; - rv = updateDir->GetNativePath(updateDirPath); -#endif if (NS_FAILED(rv)) { - return; + return; } - // Get the current working directory. - char workingDirPath[MAXPATHLEN]; - rv = GetCurrentWorkingDir(workingDirPath, sizeof(workingDirPath)); - if (NS_FAILED(rv)) - return; - - // We used to write out "Applying" to the update.status file here. - // Instead we do this from within the updater application now. - // This is so that we don't overwrite the status of pending-service - // in the Windows case. This change was made for all platforms so - // that it stays consistent across all OS. + if (restart && isStaged) { + // When the update should already be staged make sure that the updated + // directory exists. + bool updatedDirExists = false; + if (NS_FAILED(updatedDir->Exists(&updatedDirExists)) || !updatedDirExists) { + return; + } + } // On platforms where we are not calling execv, we may need to make the // updater executable wait for the calling process to exit. Otherwise, the @@ -887,78 +620,58 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, // new process with the same PID could be created. This situation is unlikely, // however, given the way most operating systems recycle PIDs. We'll take our // chances ;-) - // Construct the PID argument for this process. If we are using execv, then - // we pass "0" which is then ignored by the updater. + // Construct the PID argument for this process to pass to the updater. nsAutoCString pid; - if (!restart) { - // Signal the updater application that it should stage the update. - pid.AssignASCII("-1"); - } else { + if (restart) { #if defined(XP_UNIX) & !defined(XP_MACOSX) + // When execv is used for an update that requires a restart 0 is passed + // which is ignored by the updater. pid.AssignASCII("0"); #else pid.AppendInt((int32_t) getpid()); #endif + if (isStaged) { + // Append a special token to the PID in order to inform the updater that + // it should replace install with the updated directory. + pid.AppendLiteral("/replace"); + } + } else { + // Signal the updater application that it should stage the update. + pid.AssignASCII("-1"); } - int immersiveArgc = 0; - int argc = appArgc + 6 + immersiveArgc; - char **argv = new char*[argc + 1 ]; - if (!argv) + int argc = 5; + if (restart) { + argc = appArgc + 6; + } + char **argv = new char*[argc + 1]; + if (!argv) { return; + } argv[0] = (char*) updaterPath.get(); argv[1] = (char*) updateDirPath.get(); argv[2] = (char*) installDirPath.get(); - argv[3] = (char*) applyToDir.get(); + argv[3] = (char*) applyToDirPath.get(); argv[4] = (char*) pid.get(); if (restart && appArgc) { argv[5] = workingDirPath; argv[6] = (char*) appFilePath.get(); - for (int i = 1; i < appArgc; ++i) + for (int i = 1; i < appArgc; ++i) { argv[6 + i] = appArgv[i]; -#ifdef XP_WIN - if (immersiveArgc) { - argv[argc - 1] = "-ServerName:DefaultBrowserServer"; } -#endif argv[argc] = nullptr; } else { - argc = 5; argv[5] = nullptr; } - if (gSafeMode) { + if (restart && gSafeMode) { PR_SetEnv("MOZ_SAFE_MODE_RESTART=1"); } -#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && \ - !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK) + +#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && !defined(XP_MACOSX) AppendToLibPath(installDirPath.get()); #endif - if (isOSUpdate) { - PR_SetEnv("MOZ_OS_UPDATE=1"); - } -#if defined(MOZ_WIDGET_GONK) - // We want the updater to be CPU friendly and not subject to being killed by - // the low memory killer, so we pass in some preferences to allow it to - // adjust its priority. - - int32_t prioVal = Preferences::GetInt(kAppUpdaterPrio, - kAppUpdaterPrioDefault); - int32_t oomScoreAdj = Preferences::GetInt(kAppUpdaterOomScoreAdj, - kAppUpdaterOomScoreAdjDefault); - int32_t ioprioClass = Preferences::GetInt(kAppUpdaterIOPrioClass, - kAppUpdaterIOPrioClassDefault); - int32_t ioprioLevel = Preferences::GetInt(kAppUpdaterIOPrioLevel, - kAppUpdaterIOPrioLevelDefault); - nsPrintfCString prioEnv("MOZ_UPDATER_PRIO=%d/%d/%d/%d", - prioVal, oomScoreAdj, ioprioClass, ioprioLevel); - // Note: we allocate a new string on heap and pass that to PR_SetEnv, since - // the string can be used after this function returns. This means that we - // will intentionally leak this buffer. - PR_SetEnv(ToNewCString(prioEnv)); -#endif - LOG(("spawning updater process [%s]\n", updaterPath.get())); #if defined(XP_UNIX) && !defined(XP_MACOSX) @@ -977,35 +690,39 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, exit(execv(updaterPath.get(), argv)); } #elif defined(XP_WIN) - // Launch the update using updater.exe - if (!WinLaunchChild(updaterPathW.get(), argc, argv, nullptr, outpid)) { - return; - } - - if (restart) { - // We are going to process an update so we should exit now - _exit(0); + if (isStaged) { + // Launch the updater to replace the installation with the staged updated. + if (!WinLaunchChild(updaterPathW.get(), argc, argv)) { + return; + } + } else { + // Launch the updater to either stage or apply an update. + if (!WinLaunchChild(updaterPathW.get(), argc, argv, nullptr, outpid)) { + return; + } } #elif defined(XP_MACOSX) UpdateDriverSetupMacCommandLine(argc, argv, restart); - // We need to detect whether elevation is required for this update. This can - // occur when an admin user installs the application, but another admin - // user attempts to update (see bug 394984). - if (restart && !IsRecursivelyWritable(installDirPath.get())) { - if (!LaunchElevatedUpdate(argc, argv, outpid)) { - LOG(("Failed to launch elevated update!")); - exit(1); - } - exit(0); + // 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. + if (isStaged) { + // Launch the updater to replace the installation with the staged updated. + LaunchChildMac(argc, argv); } else { - if (restart) { - LaunchChildMac(argc, argv); - exit(0); - } + // Launch the updater to either stage or apply an update. LaunchChildMac(argc, argv, outpid); } #else - *outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr); + if (isStaged) { + // Launch the updater to replace the installation with the staged updated. + PR_CreateProcessDetached(updaterPath.get(), argv, nullptr, nullptr); + } else { + // Launch the updater to either stage or apply an update. + *outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr); + } +#endif +#if !defined(USE_EXECV) if (restart) { exit(0); } @@ -1061,23 +778,25 @@ ProcessHasTerminated(ProcessType pt) nsresult ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir, int argc, char **argv, const char *appVersion, - bool restart, bool isOSUpdate, nsIFile *osApplyToDir, - ProcessType *pid) + bool restart, ProcessType *pid) { nsresult rv; nsCOMPtr updatesDir; rv = updRootDir->Clone(getter_AddRefs(updatesDir)); - if (NS_FAILED(rv)) + if (NS_FAILED(rv)) { return rv; + } rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("updates")); - if (NS_FAILED(rv)) + if (NS_FAILED(rv)) { return rv; + } rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("0")); - if (NS_FAILED(rv)) + if (NS_FAILED(rv)) { return rv; + } nsCOMPtr statusFile; UpdateStatus status = GetUpdateStatus(updatesDir, statusFile); @@ -1105,8 +824,7 @@ ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir, IsOlderVersion(versionFile, appVersion)) { updatesDir->Remove(true); } else { - ApplyUpdate(greDir, updatesDir, statusFile, - appDir, argc, argv, restart, isOSUpdate, osApplyToDir, pid); + ApplyUpdate(greDir, updatesDir, appDir, argc, argv, restart, false, pid); } break; } @@ -1114,7 +832,7 @@ ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir, case eAppliedService: // An update was staged and needs to be switched so the updated application // is used. - SwitchToUpdatedApp(greDir, updatesDir, appDir, argc, argv); + ApplyUpdate(greDir, updatesDir, appDir, argc, argv, restart, true, pid); break; case eNoUpdateAction: // We don't need to do any special processing here, we'll just continue to @@ -1141,140 +859,54 @@ nsUpdateProcessor::~nsUpdateProcessor() NS_IMETHODIMP nsUpdateProcessor::ProcessUpdate(nsIUpdate* aUpdate) { - nsCOMPtr greDir, appDir, updRoot; - nsAutoCString appVersion; - int argc; - char **argv; + nsresult rv; - nsAutoCString binPath; - nsXREDirProvider* dirProvider = nsXREDirProvider::GetSingleton(); - if (dirProvider) { // Normal code path - // Check for and process any available updates - bool persistent; - nsresult rv = NS_ERROR_FAILURE; // Take the NS_FAILED path when non-GONK -#ifdef MOZ_WIDGET_GONK - // Check in the sdcard for updates first, since that's our preferred - // download location. - rv = dirProvider->GetFile(XRE_UPDATE_ARCHIVE_DIR, &persistent, - getter_AddRefs(updRoot)); -#endif - if (NS_FAILED(rv)) { - rv = dirProvider->GetFile(XRE_UPDATE_ROOT_DIR, &persistent, - getter_AddRefs(updRoot)); - } - // XRE_UPDATE_ROOT_DIR may fail. Fallback to appDir if failed - if (NS_FAILED(rv)) - updRoot = dirProvider->GetAppDir(); + nsCOMPtr ds = + do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); - greDir = dirProvider->GetGREDir(); - nsCOMPtr exeFile; - rv = dirProvider->GetFile(XRE_EXECUTABLE_FILE, &persistent, - getter_AddRefs(exeFile)); - if (NS_SUCCEEDED(rv)) - rv = exeFile->GetParent(getter_AddRefs(appDir)); + nsCOMPtr exeFile; + rv = ds->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile), + getter_AddRefs(exeFile)); + NS_ENSURE_SUCCESS(rv, rv); - if (NS_FAILED(rv)) - appDir = dirProvider->GetAppDir(); + nsCOMPtr appDir; + rv = exeFile->GetParent(getter_AddRefs(appDir)); + NS_ENSURE_SUCCESS(rv, rv); - appVersion = gAppData->version; - argc = gRestartArgc; - argv = gRestartArgv; - } else { - // In the xpcshell environment, the usual XRE_main is not run, so things - // like dirProvider and gAppData do not exist. This code path accesses - // XPCOM (which is not available in the previous code path) in order to get - // the same information. - nsCOMPtr ds = - do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); - if (!ds) { - NS_ABORT(); // There's nothing which we can do if this fails! - } + nsCOMPtr greDir; + rv = ds->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir)); + NS_ENSURE_SUCCESS(rv, rv); - nsresult rv = ds->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), - getter_AddRefs(greDir)); - NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the GRE dir"); + nsCOMPtr updRoot; + rv = ds->Get(XRE_UPDATE_ROOT_DIR, NS_GET_IID(nsIFile), + getter_AddRefs(updRoot)); + NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the UpdRootD dir"); - nsCOMPtr exeFile; - rv = ds->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile), - getter_AddRefs(exeFile)); - if (NS_SUCCEEDED(rv)) - rv = exeFile->GetParent(getter_AddRefs(appDir)); - - NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the XREExeF parent dir"); - - rv = ds->Get(XRE_UPDATE_ROOT_DIR, NS_GET_IID(nsIFile), - getter_AddRefs(updRoot)); - NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the UpdRootD dir"); - - nsCOMPtr appInfo = - do_GetService("@mozilla.org/xre/app-info;1"); - if (appInfo) { - rv = appInfo->GetVersion(appVersion); - NS_ENSURE_SUCCESS(rv, rv); - } else { - appVersion = MOZ_APP_VERSION; - } - - // We need argv[0] to point to the current executable's name. The rest of - // the entries in this array will be ignored if argc<2. Therefore, for - // xpcshell, we only fill out that item, and leave the rest empty. - argc = 1; - nsCOMPtr binary; - rv = ds->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile), - getter_AddRefs(binary)); - NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the binary path"); - binary->GetNativePath(binPath); + // XRE_UPDATE_ROOT_DIR should not fail but if it does fallback to the + // application directory just to be safe. + if (NS_FAILED(rv)) { + rv = appDir->Clone(getter_AddRefs(updRoot)); + NS_ENSURE_SUCCESS(rv, rv); } + nsCOMPtr appInfo = + do_GetService("@mozilla.org/xre/app-info;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString appVersion; + rv = appInfo->GetVersion(appVersion); + NS_ENSURE_SUCCESS(rv, rv); + // Copy the parameters to the StagedUpdateInfo structure shared with the // watcher thread. mInfo.mGREDir = greDir; mInfo.mAppDir = appDir; mInfo.mUpdateRoot = updRoot; - mInfo.mArgc = argc; - mInfo.mArgv = new char*[argc]; - if (dirProvider) { - for (int i = 0; i < argc; ++i) { - const size_t length = strlen(argv[i]); - mInfo.mArgv[i] = new char[length + 1]; - strcpy(mInfo.mArgv[i], argv[i]); - } - } else { - MOZ_ASSERT(argc == 1); // see above - const size_t length = binPath.Length(); - mInfo.mArgv[0] = new char[length + 1]; - strcpy(mInfo.mArgv[0], binPath.get()); - } + mInfo.mArgc = 0; + mInfo.mArgv = nullptr; mInfo.mAppVersion = appVersion; -#if defined(MOZ_WIDGET_GONK) - NS_ENSURE_ARG_POINTER(aUpdate); - - bool isOSUpdate; - if (NS_SUCCEEDED(aUpdate->GetIsOSUpdate(&isOSUpdate)) && - isOSUpdate) { - nsAutoCString osApplyToDir; - - // This needs to be done on the main thread, so we pass it along in - // BackgroundThreadInfo - nsresult rv = GetOSApplyToDir(osApplyToDir); - if (NS_FAILED(rv)) { - LOG(("Can't get the OS apply to dir")); - return rv; - } - - SetOSApplyToDir(aUpdate, osApplyToDir); - - mInfo.mIsOSUpdate = true; - rv = NS_NewNativeLocalFile(osApplyToDir, false, - getter_AddRefs(mInfo.mOSApplyToDir)); - if (NS_FAILED(rv)) { - LOG(("Can't create nsIFile for OS apply to dir")); - return rv; - } - } -#endif - MOZ_ASSERT(NS_IsMainThread(), "not main thread"); nsCOMPtr r = NewRunnableMethod(this, &nsUpdateProcessor::StartStagedUpdate); return NS_NewNamedThread("Update Watcher", getter_AddRefs(mProcessWatcher), @@ -1295,8 +927,6 @@ nsUpdateProcessor::StartStagedUpdate() mInfo.mArgv, mInfo.mAppVersion.get(), false, - mInfo.mIsOSUpdate, - mInfo.mOSApplyToDir, &mUpdaterPID); NS_ENSURE_SUCCESS_VOID(rv); diff --git a/toolkit/xre/nsUpdateDriver.h b/toolkit/xre/nsUpdateDriver.h index ed877519804b..56036c9460e2 100644 --- a/toolkit/xre/nsUpdateDriver.h +++ b/toolkit/xre/nsUpdateDriver.h @@ -46,14 +46,9 @@ class nsIFile; * * This function does not modify appDir. */ -nsresult ProcessUpdates(nsIFile *greDir, nsIFile *appDir, - nsIFile *updRootDir, - int argc, char **argv, - const char *appVersion, - bool restart = true, - bool isOSUpdate = false, - nsIFile *osApplyToDir = nullptr, - ProcessType *pid = nullptr); +nsresult ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir, + int argc, char **argv, const char *appVersion, + bool restart = true, ProcessType *pid = nullptr); // The implementation of the update processor handles the task of loading the // updater application for staging an update.