michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include "nsUpdateDriver.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include "nsAppRunner.h" michael@0: #include "nsIWritablePropertyBag.h" michael@0: #include "nsIFile.h" michael@0: #include "nsIVariant.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsString.h" michael@0: #include "prproces.h" michael@0: #include "prlog.h" michael@0: #include "prenv.h" michael@0: #include "nsVersionComparator.h" michael@0: #include "nsXREDirProvider.h" michael@0: #include "SpecialSystemDirectory.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsIXULAppInfo.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "mozilla/DebugOnly.h" michael@0: michael@0: #ifdef XP_MACOSX michael@0: #include "nsILocalFileMac.h" michael@0: #include "nsCommandLineServiceMac.h" michael@0: #include "MacLaunchHelper.h" michael@0: #endif michael@0: michael@0: #if defined(XP_WIN) michael@0: # include michael@0: # include michael@0: # include michael@0: # include michael@0: # include "nsWindowsHelpers.h" michael@0: # include "prprf.h" michael@0: # define getcwd(path, size) _getcwd(path, size) michael@0: # define getpid() GetCurrentProcessId() michael@0: #elif defined(XP_UNIX) michael@0: # include michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: michael@0: // michael@0: // We use execv to spawn the updater process on all UNIX systems except Mac OSX michael@0: // since it is known to cause problems on the Mac. Windows has execv, but it michael@0: // is a faked implementation that doesn't really replace the current process. michael@0: // Instead it spawns a new process, so we gain nothing from using execv on michael@0: // Windows. michael@0: // michael@0: // On platforms where we are not calling execv, we may need to make the michael@0: // updater executable wait for the calling process to exit. Otherwise, the michael@0: // updater may have trouble modifying our executable image (because it might michael@0: // still be in use). This is accomplished by passing our PID to the updater so michael@0: // that it can wait for us to exit. This is not perfect as there is a race michael@0: // condition that could bite us. It's possible that the calling process could michael@0: // exit before the updater waits on the specified PID, and in the meantime a michael@0: // new process with the same PID could be created. This situation is unlikely, michael@0: // however, given the way most operating systems recycle PIDs. We'll take our michael@0: // chances ;-) michael@0: // michael@0: // A similar #define lives in updater.cpp and should be kept in sync with this. michael@0: // michael@0: #if defined(XP_UNIX) && !defined(XP_MACOSX) michael@0: #define USE_EXECV michael@0: #endif michael@0: michael@0: #ifdef PR_LOGGING michael@0: static PRLogModuleInfo * michael@0: GetUpdateLog() michael@0: { michael@0: static PRLogModuleInfo *sUpdateLog; michael@0: if (!sUpdateLog) michael@0: sUpdateLog = PR_NewLogModule("updatedriver"); michael@0: return sUpdateLog; michael@0: } michael@0: #endif michael@0: #define LOG(args) PR_LOG(GetUpdateLog(), PR_LOG_DEBUG, args) michael@0: michael@0: #ifdef XP_WIN michael@0: static const char kUpdaterBin[] = "updater.exe"; michael@0: #else michael@0: static const char kUpdaterBin[] = "updater"; michael@0: #endif michael@0: static const char kUpdaterINI[] = "updater.ini"; michael@0: #ifdef XP_MACOSX michael@0: static const char kUpdaterApp[] = "updater.app"; michael@0: #endif michael@0: #if defined(XP_UNIX) && !defined(XP_MACOSX) michael@0: static const char kUpdaterPNG[] = "updater.png"; michael@0: #endif michael@0: michael@0: #if defined(MOZ_WIDGET_GONK) michael@0: #include michael@0: michael@0: static const int kB2GServiceArgc = 2; michael@0: static const char *kB2GServiceArgv[] = { "/system/bin/start", "b2g" }; michael@0: michael@0: static const char kAppUpdaterPrio[] = "app.update.updater.prio"; michael@0: static const char kAppUpdaterOomScoreAdj[] = "app.update.updater.oom_score_adj"; michael@0: static const char kAppUpdaterIOPrioClass[] = "app.update.updater.ioprio.class"; michael@0: static const char kAppUpdaterIOPrioLevel[] = "app.update.updater.ioprio.level"; michael@0: michael@0: static const int kAppUpdaterPrioDefault = 19; // -20..19 where 19 = lowest priority michael@0: static const int kAppUpdaterOomScoreAdjDefault = -1000; // -1000 = Never kill michael@0: static const int kAppUpdaterIOPrioClassDefault = IOPRIO_CLASS_IDLE; michael@0: static const int kAppUpdaterIOPrioLevelDefault = 0; // Doesn't matter for CLASS IDLE michael@0: #endif michael@0: michael@0: static nsresult michael@0: GetCurrentWorkingDir(char *buf, size_t size) michael@0: { michael@0: // Cannot use NS_GetSpecialDirectory because XPCOM is not yet initialized. michael@0: // This code is duplicated from xpcom/io/SpecialSystemDirectory.cpp: michael@0: michael@0: #if defined(XP_WIN) michael@0: wchar_t wpath[MAX_PATH]; michael@0: if (!_wgetcwd(wpath, size)) michael@0: return NS_ERROR_FAILURE; michael@0: NS_ConvertUTF16toUTF8 path(wpath); michael@0: strncpy(buf, path.get(), size); michael@0: #else michael@0: if(!getcwd(buf, size)) michael@0: return NS_ERROR_FAILURE; michael@0: #endif michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: #if defined(XP_WIN) michael@0: #define PATH_SEPARATOR ";" michael@0: michael@0: // In Tor Browser, updater.exe depends on some DLLs that are located in the michael@0: // app directory. To allow the updater to run when it has been copied into michael@0: // the update directory, we append the app directory to the PATH. michael@0: static nsresult michael@0: AdjustPathForUpdater(nsIFile *appDir) michael@0: { michael@0: nsAutoCString appPath; michael@0: nsresult rv = appDir->GetNativePath(appPath); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: char *s = nullptr; michael@0: char *pathValue = PR_GetEnv("PATH"); michael@0: if ((nullptr == pathValue) || ('\0' == *pathValue)) { michael@0: s = PR_smprintf("PATH=%s", appPath.get()); michael@0: } else { michael@0: s = PR_smprintf("PATH=%s" PATH_SEPARATOR "%s", pathValue, appPath.get()); michael@0: } michael@0: michael@0: // We intentionally leak the value that is passed into PR_SetEnv() because michael@0: // the environment will hold a pointer to it. michael@0: if ((nullptr == s) || (PR_SUCCESS != PR_SetEnv(s))) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: #endif michael@0: michael@0: #ifdef DEBUG michael@0: static void michael@0: dump_argv(const char *aPrefix, char **argv, int argc) michael@0: { michael@0: printf("%s - %d args\n", aPrefix, argc); michael@0: for (int i = 0; i < argc; ++i) michael@0: printf(" %d: %s\n", i, argv[i]); michael@0: } michael@0: #endif michael@0: michael@0: michael@0: #if defined(XP_MACOSX) michael@0: // This is a copy of OS X's XRE_GetBinaryPath from nsAppRunner.cpp with the michael@0: // gBinaryPath check removed so that the updater can reload the stub executable michael@0: // instead of xulrunner-bin. See bug 349737. michael@0: static nsresult michael@0: GetXULRunnerStubPath(const char* argv0, nsIFile* *aResult) michael@0: { michael@0: // Works even if we're not bundled. michael@0: CFBundleRef appBundle = ::CFBundleGetMainBundle(); michael@0: if (!appBundle) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: CFURLRef bundleURL = ::CFBundleCopyExecutableURL(appBundle); michael@0: if (!bundleURL) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr lfm; michael@0: nsresult rv = NS_NewLocalFileWithCFURL(bundleURL, true, getter_AddRefs(lfm)); michael@0: michael@0: ::CFRelease(bundleURL); michael@0: michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: NS_ADDREF(*aResult = static_cast(lfm.get())); michael@0: return NS_OK; michael@0: } michael@0: #endif /* XP_MACOSX */ michael@0: michael@0: static bool michael@0: GetFile(nsIFile *dir, const nsCSubstring &name, nsCOMPtr &result) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr file; michael@0: rv = dir->Clone(getter_AddRefs(file)); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: rv = file->AppendNative(name); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: result = do_QueryInterface(file, &rv); michael@0: return NS_SUCCEEDED(rv); michael@0: } michael@0: michael@0: static bool michael@0: GetStatusFile(nsIFile *dir, nsCOMPtr &result) michael@0: { michael@0: return GetFile(dir, NS_LITERAL_CSTRING("update.status"), result); michael@0: } michael@0: michael@0: /** michael@0: * Get the contents of the update.status file. michael@0: * michael@0: * @param statusFile the status file object. michael@0: * @param buf the buffer holding the file contents michael@0: * michael@0: * @return true if successful, false otherwise. michael@0: */ michael@0: template michael@0: static bool michael@0: GetStatusFileContents(nsIFile *statusFile, char (&buf)[Size]) michael@0: { michael@0: // The buffer needs to be large enough to hold the known status codes michael@0: PR_STATIC_ASSERT(Size > 16); michael@0: michael@0: PRFileDesc *fd = nullptr; michael@0: nsresult rv = statusFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: const int32_t n = PR_Read(fd, buf, Size); michael@0: PR_Close(fd); michael@0: michael@0: return (n >= 0); michael@0: } michael@0: michael@0: typedef enum { michael@0: eNoUpdateAction, michael@0: ePendingUpdate, michael@0: ePendingService, michael@0: eAppliedUpdate, michael@0: eAppliedService michael@0: } UpdateStatus; michael@0: michael@0: /** michael@0: * Returns a value indicating what needs to be done in order to handle an update. michael@0: * michael@0: * @param dir the directory in which we should look for an update.status file. michael@0: * @param statusFile the update.status file found in the directory. michael@0: * michael@0: * @return the update action to be performed. michael@0: */ michael@0: static UpdateStatus michael@0: GetUpdateStatus(nsIFile* dir, nsCOMPtr &statusFile) michael@0: { michael@0: if (GetStatusFile(dir, statusFile)) { michael@0: char buf[32]; michael@0: if (GetStatusFileContents(statusFile, buf)) { michael@0: const char kPending[] = "pending"; michael@0: const char kPendingService[] = "pending-service"; michael@0: const char kApplied[] = "applied"; michael@0: const char kAppliedService[] = "applied-service"; michael@0: if (!strncmp(buf, kPendingService, sizeof(kPendingService) - 1)) { michael@0: return ePendingService; michael@0: } michael@0: if (!strncmp(buf, kPending, sizeof(kPending) - 1)) { michael@0: return ePendingUpdate; michael@0: } michael@0: if (!strncmp(buf, kAppliedService, sizeof(kAppliedService) - 1)) { michael@0: return eAppliedService; michael@0: } michael@0: if (!strncmp(buf, kApplied, sizeof(kApplied) - 1)) { michael@0: return eAppliedUpdate; michael@0: } michael@0: } michael@0: } michael@0: return eNoUpdateAction; michael@0: } michael@0: michael@0: static bool michael@0: GetVersionFile(nsIFile *dir, nsCOMPtr &result) michael@0: { michael@0: return GetFile(dir, NS_LITERAL_CSTRING("update.version"), result); michael@0: } michael@0: michael@0: // Compares the current application version with the update's application michael@0: // version. michael@0: static bool michael@0: IsOlderVersion(nsIFile *versionFile, const char *appVersion) michael@0: { michael@0: PRFileDesc *fd = nullptr; michael@0: nsresult rv = versionFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd); michael@0: if (NS_FAILED(rv)) michael@0: return true; michael@0: michael@0: char buf[32]; michael@0: const int32_t n = PR_Read(fd, buf, sizeof(buf)); michael@0: PR_Close(fd); michael@0: michael@0: if (n < 0) michael@0: return false; michael@0: michael@0: // Trim off the trailing newline michael@0: if (buf[n - 1] == '\n') michael@0: buf[n - 1] = '\0'; michael@0: michael@0: // If the update xml doesn't provide the application version the file will michael@0: // contain the string "null" and it is assumed that the update is not older. michael@0: const char kNull[] = "null"; michael@0: if (strncmp(buf, kNull, sizeof(kNull) - 1) == 0) michael@0: return false; michael@0: michael@0: #ifdef DEBUG michael@0: printf("IsOlderVersion checking appVersion %s against updateVersion %s\n", michael@0: appVersion, buf); michael@0: #endif michael@0: if (mozilla::Version(appVersion) > buf) michael@0: return true; michael@0: michael@0: return false; michael@0: } michael@0: michael@0: #if defined(XP_WIN) && defined(MOZ_METRO) michael@0: static bool michael@0: IsWindowsMetroUpdateRequest(int appArgc, char **appArgv) michael@0: { michael@0: for (int index = 0; index < appArgc; index++) { michael@0: if (!strcmp(appArgv[index], "--metro-update")) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: #endif michael@0: michael@0: static bool michael@0: CopyFileIntoUpdateDir(nsIFile *parentDir, const char *leafName, nsIFile *updateDir) michael@0: { michael@0: nsDependentCString leaf(leafName); michael@0: nsCOMPtr file; michael@0: michael@0: // Make sure there is not an existing file in the target location. michael@0: nsresult rv = updateDir->Clone(getter_AddRefs(file)); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: rv = file->AppendNative(leaf); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: file->Remove(true); michael@0: michael@0: // Now, copy into the target location. michael@0: rv = parentDir->Clone(getter_AddRefs(file)); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: rv = file->AppendNative(leaf); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: rv = file->CopyToNative(updateDir, EmptyCString()); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: CopyUpdaterIntoUpdateDir(nsIFile *greDir, nsIFile *appDir, nsIFile *updateDir, michael@0: nsCOMPtr &updater) michael@0: { michael@0: // Copy the updater application from the GRE and the updater ini from the app michael@0: #if defined(XP_MACOSX) michael@0: if (!CopyFileIntoUpdateDir(greDir, kUpdaterApp, updateDir)) michael@0: return false; michael@0: #else michael@0: if (!CopyFileIntoUpdateDir(greDir, kUpdaterBin, updateDir)) michael@0: return false; michael@0: #endif michael@0: CopyFileIntoUpdateDir(appDir, kUpdaterINI, updateDir); michael@0: #if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(ANDROID) michael@0: nsCOMPtr iconDir; michael@0: appDir->Clone(getter_AddRefs(iconDir)); michael@0: iconDir->AppendNative(NS_LITERAL_CSTRING("icons")); michael@0: if (!CopyFileIntoUpdateDir(iconDir, kUpdaterPNG, updateDir)) michael@0: return false; michael@0: #endif michael@0: // Finally, return the location of the updater binary. michael@0: nsresult rv = updateDir->Clone(getter_AddRefs(updater)); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: #if defined(XP_MACOSX) michael@0: rv = updater->AppendNative(NS_LITERAL_CSTRING(kUpdaterApp)); michael@0: nsresult tmp = updater->AppendNative(NS_LITERAL_CSTRING("Contents")); michael@0: if (NS_FAILED(tmp)) { michael@0: rv = tmp; michael@0: } michael@0: tmp = updater->AppendNative(NS_LITERAL_CSTRING("MacOS")); michael@0: if (NS_FAILED(tmp) || NS_FAILED(rv)) michael@0: return false; michael@0: #endif michael@0: rv = updater->AppendNative(NS_LITERAL_CSTRING(kUpdaterBin)); michael@0: return NS_SUCCEEDED(rv); michael@0: } michael@0: michael@0: /** michael@0: * Switch an existing application directory to an updated version that has been michael@0: * staged. michael@0: * michael@0: * @param greDir the GRE dir michael@0: * @param updateDir the update root dir michael@0: * @param statusFile the update.status file michael@0: * @param appDir the app dir michael@0: * @param appArgc the number of args to the application michael@0: * @param appArgv the args to the application, used for restarting if needed michael@0: */ michael@0: static void michael@0: SwitchToUpdatedApp(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, michael@0: nsIFile *appDir, int appArgc, char **appArgv) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // Steps: michael@0: // - copy updater into temp dir michael@0: // - run updater with the correct arguments michael@0: michael@0: nsCOMPtr tmpDir; michael@0: GetSpecialSystemDirectory(OS_TemporaryDirectory, michael@0: getter_AddRefs(tmpDir)); michael@0: if (!tmpDir) { michael@0: LOG(("failed getting a temp dir\n")); michael@0: return; michael@0: } michael@0: michael@0: // Try to create our own new temp directory in case there is already an michael@0: // updater binary in the OS temporary location which we cannot write to. michael@0: // Note that we don't check for errors here, as if this directory can't michael@0: // be created, the following CopyUpdaterIntoUpdateDir call will fail. michael@0: // We create the unique directory inside a subfolder of MozUpdater instead michael@0: // of directly in the temp directory so we can efficiently delete everything michael@0: // after updates. michael@0: tmpDir->Append(NS_LITERAL_STRING("MozUpdater")); michael@0: tmpDir->Append(NS_LITERAL_STRING("bgupdate")); michael@0: tmpDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0755); michael@0: michael@0: nsCOMPtr updater; michael@0: if (!CopyUpdaterIntoUpdateDir(greDir, appDir, tmpDir, updater)) { michael@0: LOG(("failed copying updater\n")); michael@0: return; michael@0: } michael@0: michael@0: // We need to use the value returned from XRE_GetBinaryPath when attempting michael@0: // to restart the running application. michael@0: nsCOMPtr appFile; michael@0: michael@0: #if defined(XP_MACOSX) michael@0: // On OS X we need to pass the location of the xulrunner-stub executable michael@0: // rather than xulrunner-bin. See bug 349737. michael@0: GetXULRunnerStubPath(appArgv[0], getter_AddRefs(appFile)); michael@0: #else michael@0: XRE_GetBinaryPath(appArgv[0], getter_AddRefs(appFile)); michael@0: #endif michael@0: michael@0: if (!appFile) michael@0: return; michael@0: michael@0: #ifdef XP_WIN michael@0: nsAutoString appFilePathW; michael@0: rv = appFile->GetPath(appFilePathW); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: NS_ConvertUTF16toUTF8 appFilePath(appFilePathW); michael@0: michael@0: nsAutoString updaterPathW; michael@0: rv = updater->GetPath(updaterPathW); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: NS_ConvertUTF16toUTF8 updaterPath(updaterPathW); michael@0: #else michael@0: michael@0: nsAutoCString appFilePath; michael@0: #if defined(MOZ_WIDGET_GONK) michael@0: appFilePath.Assign(kB2GServiceArgv[0]); michael@0: appArgc = kB2GServiceArgc; michael@0: appArgv = const_cast(kB2GServiceArgv); michael@0: #else michael@0: rv = appFile->GetNativePath(appFilePath); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: #endif michael@0: michael@0: nsAutoCString updaterPath; michael@0: rv = updater->GetNativePath(updaterPath); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: #endif michael@0: michael@0: // Get the directory to which the update will be applied. On Mac OSX we need michael@0: // to apply the update to the Updated.app directory under the Foo.app michael@0: // directory which is the parent of the parent of the appDir. On other michael@0: // platforms we will just apply to the appDir/updated. michael@0: nsCOMPtr updatedDir; michael@0: #if defined(XP_MACOSX) michael@0: nsAutoCString applyToDir; michael@0: { michael@0: nsCOMPtr parentDir1, parentDir2; michael@0: rv = appDir->GetParent(getter_AddRefs(parentDir1)); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: rv = parentDir1->GetParent(getter_AddRefs(parentDir2)); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: if (!GetFile(parentDir2, NS_LITERAL_CSTRING("Updated.app"), updatedDir)) michael@0: return; michael@0: rv = updatedDir->GetNativePath(applyToDir); michael@0: } michael@0: #else michael@0: if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) michael@0: return; michael@0: #if defined(XP_WIN) michael@0: nsAutoString applyToDirW; michael@0: rv = updatedDir->GetPath(applyToDirW); michael@0: michael@0: NS_ConvertUTF16toUTF8 applyToDir(applyToDirW); michael@0: #else michael@0: nsAutoCString applyToDir; michael@0: rv = updatedDir->GetNativePath(applyToDir); michael@0: #endif michael@0: #endif michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: // Make sure that the updated directory exists michael@0: bool updatedDirExists = false; michael@0: updatedDir->Exists(&updatedDirExists); michael@0: if (!updatedDirExists) { michael@0: return; michael@0: } michael@0: michael@0: #if defined(XP_WIN) michael@0: nsAutoString updateDirPathW; michael@0: rv = updateDir->GetPath(updateDirPathW); michael@0: michael@0: NS_ConvertUTF16toUTF8 updateDirPath(updateDirPathW); michael@0: #else michael@0: nsAutoCString updateDirPath; michael@0: rv = updateDir->GetNativePath(updateDirPath); michael@0: #endif michael@0: michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: // Get the current working directory. michael@0: char workingDirPath[MAXPATHLEN]; michael@0: rv = GetCurrentWorkingDir(workingDirPath, sizeof(workingDirPath)); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: // Construct the PID argument for this process. If we are using execv, then michael@0: // we pass "0" which is then ignored by the updater. michael@0: #if defined(USE_EXECV) michael@0: nsAutoCString pid("0"); michael@0: #else michael@0: nsAutoCString pid; michael@0: pid.AppendInt((int32_t) getpid()); michael@0: #endif michael@0: michael@0: // Append a special token to the PID in order to let the updater know that it michael@0: // just needs to replace the update directory. michael@0: pid.AppendLiteral("/replace"); michael@0: michael@0: int immersiveArgc = 0; michael@0: #if defined(XP_WIN) && defined(MOZ_METRO) michael@0: // If this is desktop doing an update for metro, or if we're the metro browser michael@0: // we want to launch the metro browser after we're finished. michael@0: if (IsWindowsMetroUpdateRequest(appArgc, appArgv) || IsRunningInWindowsMetro()) { michael@0: immersiveArgc = 1; michael@0: } michael@0: #endif michael@0: int argc = appArgc + 5 + immersiveArgc; michael@0: char **argv = new char*[argc + 1]; michael@0: if (!argv) michael@0: return; michael@0: argv[0] = (char*) updaterPath.get(); michael@0: argv[1] = (char*) updateDirPath.get(); michael@0: argv[2] = (char*) applyToDir.get(); michael@0: argv[3] = (char*) pid.get(); michael@0: if (appArgc) { michael@0: argv[4] = workingDirPath; michael@0: argv[5] = (char*) appFilePath.get(); michael@0: for (int i = 1; i < appArgc; ++i) michael@0: argv[5 + i] = appArgv[i]; michael@0: #ifdef XP_WIN michael@0: if (immersiveArgc) { michael@0: argv[argc - 1] = "-ServerName:DefaultBrowserServer"; michael@0: } michael@0: #endif michael@0: argv[argc] = nullptr; michael@0: } else { michael@0: argc = 4; michael@0: argv[4] = nullptr; michael@0: } michael@0: michael@0: if (gSafeMode) { michael@0: PR_SetEnv("MOZ_SAFE_MODE_RESTART=1"); michael@0: } michael@0: michael@0: #if defined(XP_WIN) michael@0: nsresult rv2 = AdjustPathForUpdater(appDir); michael@0: if (NS_FAILED(rv2)) { michael@0: LOG(("SwitchToUpdatedApp -- AdjustPathForUpdater failed (0x%x)\n", rv2)); michael@0: } michael@0: #endif michael@0: michael@0: LOG(("spawning updater process for replacing [%s]\n", updaterPath.get())); michael@0: michael@0: #if defined(USE_EXECV) michael@0: # if defined(MOZ_WIDGET_GONK) michael@0: // In Gonk, we preload libmozglue, which the updater process doesn't need. michael@0: // Since the updater will move and delete libmozglue.so, this can actually michael@0: // stop the /system mount from correctly being remounted as read-only. michael@0: unsetenv("LD_PRELOAD"); michael@0: # endif michael@0: execv(updaterPath.get(), argv); michael@0: #elif defined(XP_WIN) michael@0: // Switch the application using updater.exe michael@0: if (!WinLaunchChild(updaterPathW.get(), argc, argv)) { michael@0: return; michael@0: } michael@0: _exit(0); michael@0: #elif defined(XP_MACOSX) michael@0: CommandLineServiceMac::SetupMacCommandLine(argc, argv, true); michael@0: // LaunchChildMac uses posix_spawnp and prefers the current michael@0: // architecture when launching. It doesn't require a michael@0: // null-terminated string but it doesn't matter if we pass one. michael@0: LaunchChildMac(argc, argv); michael@0: exit(0); michael@0: #else michael@0: PR_CreateProcessDetached(updaterPath.get(), argv, nullptr, nullptr); michael@0: exit(0); michael@0: #endif michael@0: } michael@0: michael@0: #if defined(MOZ_WIDGET_GONK) michael@0: static nsresult michael@0: GetOSApplyToDir(nsACString& applyToDir) michael@0: { michael@0: nsCOMPtr ds = michael@0: do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); michael@0: NS_ASSERTION(ds, "Can't get directory service"); michael@0: michael@0: nsCOMPtr osApplyToDir; michael@0: nsresult rv = ds->Get(XRE_OS_UPDATE_APPLY_TO_DIR, NS_GET_IID(nsIFile), michael@0: getter_AddRefs(osApplyToDir)); michael@0: if (NS_FAILED(rv)) { michael@0: LOG(("Can't get the OS applyTo dir")); michael@0: return rv; michael@0: } michael@0: michael@0: return osApplyToDir->GetNativePath(applyToDir); michael@0: } michael@0: michael@0: static void michael@0: SetOSApplyToDir(nsIUpdate* update, const nsACString& osApplyToDir) michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr updateProperties = michael@0: do_QueryInterface(update, &rv); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr variant = michael@0: do_CreateInstance("@mozilla.org/variant;1", &rv); michael@0: if (NS_FAILED(rv)) { michael@0: return; michael@0: } michael@0: michael@0: rv = variant->SetAsACString(osApplyToDir); michael@0: if (NS_FAILED(rv)) { michael@0: return; michael@0: } michael@0: michael@0: updateProperties->SetProperty(NS_LITERAL_STRING("osApplyToDir"), variant); michael@0: } michael@0: #endif michael@0: michael@0: /** michael@0: * Apply an update. This applies to both normal and staged updates. michael@0: * michael@0: * @param greDir the GRE dir michael@0: * @param updateDir the update root dir michael@0: * @param statusFile the update.status file michael@0: * @param appDir the app dir michael@0: * @param appArgc the number of args to the application michael@0: * @param appArgv the args to the application, used for restarting if needed michael@0: * @param restart if true, apply the update in the foreground and restart the michael@0: * application when done. otherwise, stage the update and don't michael@0: * restart the application. michael@0: * @param outpid out parameter holding the handle to the updater application for michael@0: * staging updates. michael@0: */ michael@0: static void michael@0: ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, michael@0: nsIFile *appDir, int appArgc, char **appArgv, michael@0: bool restart, bool isOSUpdate, nsIFile *osApplyToDir, michael@0: ProcessType *outpid) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // Steps: michael@0: // - mark update as 'applying' michael@0: // - copy updater into update dir michael@0: // - run updater w/ appDir as the current working dir michael@0: michael@0: nsCOMPtr updater; michael@0: if (!CopyUpdaterIntoUpdateDir(greDir, appDir, updateDir, updater)) { michael@0: LOG(("failed copying updater\n")); michael@0: return; michael@0: } michael@0: michael@0: // We need to use the value returned from XRE_GetBinaryPath when attempting michael@0: // to restart the running application. michael@0: nsCOMPtr appFile; michael@0: michael@0: #if defined(XP_MACOSX) michael@0: // On OS X we need to pass the location of the xulrunner-stub executable michael@0: // rather than xulrunner-bin. See bug 349737. michael@0: GetXULRunnerStubPath(appArgv[0], getter_AddRefs(appFile)); michael@0: #else michael@0: XRE_GetBinaryPath(appArgv[0], getter_AddRefs(appFile)); michael@0: #endif michael@0: michael@0: if (!appFile) michael@0: return; michael@0: michael@0: #ifdef XP_WIN michael@0: nsAutoString appFilePathW; michael@0: rv = appFile->GetPath(appFilePathW); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: NS_ConvertUTF16toUTF8 appFilePath(appFilePathW); michael@0: michael@0: nsAutoString updaterPathW; michael@0: rv = updater->GetPath(updaterPathW); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: NS_ConvertUTF16toUTF8 updaterPath(updaterPathW); michael@0: michael@0: #else michael@0: nsAutoCString appFilePath; michael@0: rv = appFile->GetNativePath(appFilePath); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: nsAutoCString updaterPath; michael@0: rv = updater->GetNativePath(updaterPath); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: #endif michael@0: michael@0: // Get the directory to which the update will be applied. On Mac OSX we need michael@0: // to apply the update to the Updated.app directory under the Foo.app michael@0: // directory which is the parent of the parent of the appDir. On other michael@0: // platforms we will just apply to the appDir/updated. michael@0: nsCOMPtr updatedDir; michael@0: #if defined(XP_MACOSX) michael@0: nsAutoCString applyToDir; michael@0: { michael@0: nsCOMPtr parentDir1, parentDir2; michael@0: rv = appDir->GetParent(getter_AddRefs(parentDir1)); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: rv = parentDir1->GetParent(getter_AddRefs(parentDir2)); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: if (restart) { michael@0: // Use the correct directory if we're not staging the update. michael@0: rv = parentDir2->GetNativePath(applyToDir); michael@0: } else { michael@0: if (!GetFile(parentDir2, NS_LITERAL_CSTRING("Updated.app"), updatedDir)) michael@0: return; michael@0: rv = updatedDir->GetNativePath(applyToDir); michael@0: } michael@0: } michael@0: #else michael@0: if (restart) { michael@0: // Use the correct directory if we're not staging the update. michael@0: updatedDir = do_QueryInterface(appDir); michael@0: } else if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) { michael@0: return; michael@0: } michael@0: #if defined(XP_WIN) michael@0: nsAutoString applyToDirW; michael@0: rv = updatedDir->GetPath(applyToDirW); michael@0: michael@0: NS_ConvertUTF16toUTF8 applyToDir(applyToDirW); michael@0: #else michael@0: nsAutoCString applyToDir; michael@0: michael@0: #if defined(MOZ_WIDGET_GONK) michael@0: if (isOSUpdate) { michael@0: if (!osApplyToDir) { michael@0: return; michael@0: } michael@0: michael@0: rv = osApplyToDir->GetNativePath(applyToDir); michael@0: } else { michael@0: #endif // defined(MOZ_WIDGET_GONK) michael@0: michael@0: rv = updatedDir->GetNativePath(applyToDir); michael@0: michael@0: #if defined(MOZ_WIDGET_GONK) michael@0: } michael@0: #endif // defined(MOZ_WIDGET_GONK) michael@0: michael@0: #endif // defined(XP_WIN) michael@0: #endif michael@0: michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: #if defined(XP_WIN) michael@0: nsAutoString updateDirPathW; michael@0: rv = updateDir->GetPath(updateDirPathW); michael@0: michael@0: NS_ConvertUTF16toUTF8 updateDirPath(updateDirPathW); michael@0: #else michael@0: nsAutoCString updateDirPath; michael@0: rv = updateDir->GetNativePath(updateDirPath); michael@0: #endif michael@0: michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: // Get the current working directory. michael@0: char workingDirPath[MAXPATHLEN]; michael@0: rv = GetCurrentWorkingDir(workingDirPath, sizeof(workingDirPath)); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: // We used to write out "Applying" to the update.status file here. michael@0: // Instead we do this from within the updater application now. michael@0: // This is so that we don't overwrite the status of pending-service michael@0: // in the Windows case. This change was made for all platforms so michael@0: // that it stays consistent across all OS. michael@0: michael@0: // Construct the PID argument for this process. If we are using execv, then michael@0: // we pass "0" which is then ignored by the updater. michael@0: nsAutoCString pid; michael@0: if (!restart) { michael@0: // Signal the updater application that it should stage the update. michael@0: pid.AssignASCII("-1"); michael@0: } else { michael@0: #if defined(USE_EXECV) michael@0: pid.AssignASCII("0"); michael@0: #else michael@0: pid.AppendInt((int32_t) getpid()); michael@0: #endif michael@0: } michael@0: michael@0: int immersiveArgc = 0; michael@0: #if defined(XP_WIN) && defined(MOZ_METRO) michael@0: // If this is desktop doing an update for metro, or if we're the metro browser michael@0: // we want to launch the metro browser after we're finished. michael@0: if (IsWindowsMetroUpdateRequest(appArgc, appArgv) || IsRunningInWindowsMetro()) { michael@0: immersiveArgc = 1; michael@0: } michael@0: #endif michael@0: int argc = appArgc + 5 + immersiveArgc; michael@0: char **argv = new char*[argc + 1 ]; michael@0: if (!argv) michael@0: return; michael@0: argv[0] = (char*) updaterPath.get(); michael@0: argv[1] = (char*) updateDirPath.get(); michael@0: argv[2] = (char*) applyToDir.get(); michael@0: argv[3] = (char*) pid.get(); michael@0: if (restart && appArgc) { michael@0: argv[4] = workingDirPath; michael@0: argv[5] = (char*) appFilePath.get(); michael@0: for (int i = 1; i < appArgc; ++i) michael@0: argv[5 + i] = appArgv[i]; michael@0: #ifdef XP_WIN michael@0: if (immersiveArgc) { michael@0: argv[argc - 1] = "-ServerName:DefaultBrowserServer"; michael@0: } michael@0: #endif michael@0: argv[argc] = nullptr; michael@0: } else { michael@0: argc = 4; michael@0: argv[4] = nullptr; michael@0: } michael@0: michael@0: if (gSafeMode) { michael@0: PR_SetEnv("MOZ_SAFE_MODE_RESTART=1"); michael@0: } michael@0: michael@0: if (isOSUpdate) { michael@0: PR_SetEnv("MOZ_OS_UPDATE=1"); michael@0: } michael@0: michael@0: #if defined(XP_WIN) michael@0: nsresult rv2 = AdjustPathForUpdater(appDir); michael@0: if (NS_FAILED(rv2)) { michael@0: LOG(("ApplyUpdate -- AdjustPathForUpdater failed (0x%x)\n", rv2)); michael@0: } michael@0: #endif michael@0: michael@0: #if defined(MOZ_WIDGET_GONK) michael@0: // We want the updater to be CPU friendly and not subject to being killed by michael@0: // the low memory killer, so we pass in some preferences to allow it to michael@0: // adjust its priority. michael@0: michael@0: int32_t prioVal = Preferences::GetInt(kAppUpdaterPrio, michael@0: kAppUpdaterPrioDefault); michael@0: int32_t oomScoreAdj = Preferences::GetInt(kAppUpdaterOomScoreAdj, michael@0: kAppUpdaterOomScoreAdjDefault); michael@0: int32_t ioprioClass = Preferences::GetInt(kAppUpdaterIOPrioClass, michael@0: kAppUpdaterIOPrioClassDefault); michael@0: int32_t ioprioLevel = Preferences::GetInt(kAppUpdaterIOPrioLevel, michael@0: kAppUpdaterIOPrioLevelDefault); michael@0: nsPrintfCString prioEnv("MOZ_UPDATER_PRIO=%d/%d/%d/%d", michael@0: prioVal, oomScoreAdj, ioprioClass, ioprioLevel); michael@0: PR_SetEnv(prioEnv.get()); michael@0: #endif michael@0: michael@0: LOG(("spawning updater process [%s]\n", updaterPath.get())); michael@0: #ifdef DEBUG michael@0: dump_argv("ApplyUpdate updater", argv, argc); michael@0: #endif michael@0: michael@0: #if defined(USE_EXECV) michael@0: // Don't use execv when staging updates. michael@0: if (restart) { michael@0: execv(updaterPath.get(), argv); michael@0: } else { michael@0: *outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr); michael@0: } michael@0: #elif defined(XP_WIN) michael@0: // Launch the update using updater.exe michael@0: if (!WinLaunchChild(updaterPathW.get(), argc, argv, nullptr, outpid)) { michael@0: return; michael@0: } michael@0: michael@0: if (restart) { michael@0: // We are going to process an update so we should exit now michael@0: _exit(0); michael@0: } michael@0: #elif defined(XP_MACOSX) michael@0: CommandLineServiceMac::SetupMacCommandLine(argc, argv, true); michael@0: // LaunchChildMac uses posix_spawnp and prefers the current michael@0: // architecture when launching. It doesn't require a michael@0: // null-terminated string but it doesn't matter if we pass one. michael@0: #ifdef DEBUG michael@0: dump_argv("ApplyUpdate after SetupMacCommandLine", argv, argc); michael@0: #endif michael@0: LaunchChildMac(argc, argv, 0, outpid); michael@0: if (restart) { michael@0: exit(0); michael@0: } michael@0: #else michael@0: *outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr); michael@0: if (restart) { michael@0: exit(0); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: /** michael@0: * Wait for a process until it terminates. This call is blocking. michael@0: */ michael@0: static void michael@0: WaitForProcess(ProcessType pt) michael@0: { michael@0: #if defined(XP_WIN) michael@0: WaitForSingleObject(pt, INFINITE); michael@0: CloseHandle(pt); michael@0: #elif defined(XP_MACOSX) michael@0: waitpid(pt, 0, 0); michael@0: #else michael@0: int32_t exitCode; michael@0: PR_WaitProcess(pt, &exitCode); michael@0: if (exitCode != 0) { michael@0: LOG(("Error while running the updater process, check update.log")); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: nsresult michael@0: ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir, michael@0: int argc, char **argv, const char *appVersion, michael@0: bool restart, bool isOSUpdate, nsIFile *osApplyToDir, michael@0: ProcessType *pid) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr updatesDir; michael@0: #ifdef DEBUG michael@0: nsAutoCString path; michael@0: updRootDir->GetNativePath(path); michael@0: printf("ProcessUpdates updateRootDir: %s appVersion: %s\n", michael@0: path.get(), appVersion); michael@0: #endif michael@0: rv = updRootDir->Clone(getter_AddRefs(updatesDir)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("updates")); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("0")); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: ProcessType dummyPID; // this will only be used for MOZ_UPDATE_STAGING michael@0: const char *processingUpdates = PR_GetEnv("MOZ_PROCESS_UPDATES"); michael@0: if (processingUpdates && *processingUpdates) { michael@0: // Enable the tests to request an update to be staged. michael@0: const char *stagingUpdate = PR_GetEnv("MOZ_UPDATE_STAGING"); michael@0: if (stagingUpdate && *stagingUpdate) { michael@0: restart = false; michael@0: pid = &dummyPID; michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr statusFile; michael@0: UpdateStatus status = GetUpdateStatus(updatesDir, statusFile); michael@0: #ifdef DEBUG michael@0: printf("ProcessUpdates status: %d\n", status); michael@0: updatesDir->GetNativePath(path); michael@0: printf("ProcessUpdates updatesDir: %s\n", path.get()); michael@0: #endif michael@0: switch (status) { michael@0: case ePendingUpdate: michael@0: case ePendingService: { michael@0: nsCOMPtr versionFile; michael@0: // Remove the update if the update application version file doesn't exist michael@0: // or if the update's application version is less than the current michael@0: // application version. michael@0: if (!GetVersionFile(updatesDir, versionFile) || michael@0: IsOlderVersion(versionFile, appVersion)) { michael@0: updatesDir->Remove(true); michael@0: } else { michael@0: ApplyUpdate(greDir, updatesDir, statusFile, michael@0: appDir, argc, argv, restart, isOSUpdate, osApplyToDir, pid); michael@0: } michael@0: break; michael@0: } michael@0: case eAppliedUpdate: michael@0: case eAppliedService: michael@0: // An update was staged and needs to be switched so the updated application michael@0: // is used. michael@0: SwitchToUpdatedApp(greDir, updatesDir, statusFile, michael@0: appDir, argc, argv); michael@0: break; michael@0: case eNoUpdateAction: michael@0: // We don't need to do any special processing here, we'll just continue to michael@0: // startup the application. michael@0: break; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: michael@0: NS_IMPL_ISUPPORTS(nsUpdateProcessor, nsIUpdateProcessor) michael@0: michael@0: nsUpdateProcessor::nsUpdateProcessor() michael@0: : mUpdaterPID(0) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsUpdateProcessor::ProcessUpdate(nsIUpdate* aUpdate) michael@0: { michael@0: nsCOMPtr greDir, appDir, updRoot; michael@0: nsAutoCString appVersion; michael@0: int argc; michael@0: char **argv; michael@0: michael@0: nsAutoCString binPath; michael@0: nsXREDirProvider* dirProvider = nsXREDirProvider::GetSingleton(); michael@0: if (dirProvider) { // Normal code path michael@0: // Check for and process any available updates michael@0: bool persistent; michael@0: nsresult rv = NS_ERROR_FAILURE; // Take the NS_FAILED path when non-GONK michael@0: #ifdef MOZ_WIDGET_GONK michael@0: // Check in the sdcard for updates first, since that's our preferred michael@0: // download location. michael@0: rv = dirProvider->GetFile(XRE_UPDATE_ARCHIVE_DIR, &persistent, michael@0: getter_AddRefs(updRoot)); michael@0: #endif michael@0: if (NS_FAILED(rv)) { michael@0: rv = dirProvider->GetFile(XRE_UPDATE_ROOT_DIR, &persistent, michael@0: getter_AddRefs(updRoot)); michael@0: } michael@0: // XRE_UPDATE_ROOT_DIR may fail. Fallback to appDir if failed michael@0: if (NS_FAILED(rv)) michael@0: updRoot = dirProvider->GetAppDir(); michael@0: michael@0: greDir = dirProvider->GetGREDir(); michael@0: nsCOMPtr exeFile; michael@0: rv = dirProvider->GetFile(XRE_EXECUTABLE_FILE, &persistent, michael@0: getter_AddRefs(exeFile)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = exeFile->GetParent(getter_AddRefs(appDir)); michael@0: michael@0: if (NS_FAILED(rv)) michael@0: appDir = dirProvider->GetAppDir(); michael@0: michael@0: #ifdef TOR_BROWSER_UPDATE michael@0: appVersion = TOR_BROWSER_VERSION; michael@0: #else michael@0: appVersion = gAppData->version; michael@0: #endif michael@0: argc = gRestartArgc; michael@0: argv = gRestartArgv; michael@0: } else { michael@0: // In the xpcshell environment, the usual XRE_main is not run, so things michael@0: // like dirProvider and gAppData do not exist. This code path accesses michael@0: // XPCOM (which is not available in the previous code path) in order to get michael@0: // the same information. michael@0: nsCOMPtr ds = michael@0: do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); michael@0: if (!ds) { michael@0: NS_ABORT(); // There's nothing which we can do if this fails! michael@0: } michael@0: michael@0: nsresult rv = ds->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), michael@0: getter_AddRefs(greDir)); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the GRE dir"); michael@0: appDir = greDir; michael@0: michael@0: rv = ds->Get(XRE_UPDATE_ROOT_DIR, NS_GET_IID(nsIFile), michael@0: getter_AddRefs(updRoot)); michael@0: if (NS_FAILED(rv)) michael@0: updRoot = appDir; michael@0: michael@0: // To support Tor Browser Bundle updates from xpcshell, modify the michael@0: // following code to use the TBB version fron the configure process. michael@0: nsCOMPtr appInfo = michael@0: do_GetService("@mozilla.org/xre/app-info;1"); michael@0: if (appInfo) { michael@0: rv = appInfo->GetVersion(appVersion); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } else { michael@0: appVersion = MOZ_APP_VERSION; michael@0: } michael@0: michael@0: // We need argv[0] to point to the current executable's name. The rest of michael@0: // the entries in this array will be ignored if argc<2. Therefore, for michael@0: // xpcshell, we only fill out that item, and leave the rest empty. michael@0: argc = 1; michael@0: nsCOMPtr binary; michael@0: rv = ds->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile), michael@0: getter_AddRefs(binary)); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the binary path"); michael@0: binary->GetNativePath(binPath); michael@0: } michael@0: michael@0: // Copy the parameters to the StagedUpdateInfo structure shared with the michael@0: // watcher thread. michael@0: mInfo.mGREDir = greDir; michael@0: mInfo.mAppDir = appDir; michael@0: mInfo.mUpdateRoot = updRoot; michael@0: mInfo.mArgc = argc; michael@0: mInfo.mArgv = new char*[argc]; michael@0: if (dirProvider) { michael@0: for (int i = 0; i < argc; ++i) { michael@0: const size_t length = strlen(argv[i]); michael@0: mInfo.mArgv[i] = new char[length + 1]; michael@0: strcpy(mInfo.mArgv[i], argv[i]); michael@0: } michael@0: } else { michael@0: MOZ_ASSERT(argc == 1); // see above michael@0: const size_t length = binPath.Length(); michael@0: mInfo.mArgv[0] = new char[length + 1]; michael@0: strcpy(mInfo.mArgv[0], binPath.get()); michael@0: } michael@0: mInfo.mAppVersion = appVersion; michael@0: michael@0: #if defined(MOZ_WIDGET_GONK) michael@0: NS_ENSURE_ARG_POINTER(aUpdate); michael@0: michael@0: bool isOSUpdate; michael@0: if (NS_SUCCEEDED(aUpdate->GetIsOSUpdate(&isOSUpdate)) && michael@0: isOSUpdate) { michael@0: nsAutoCString osApplyToDir; michael@0: michael@0: // This needs to be done on the main thread, so we pass it along in michael@0: // BackgroundThreadInfo michael@0: nsresult rv = GetOSApplyToDir(osApplyToDir); michael@0: if (NS_FAILED(rv)) { michael@0: LOG(("Can't get the OS apply to dir")); michael@0: return rv; michael@0: } michael@0: michael@0: SetOSApplyToDir(aUpdate, osApplyToDir); michael@0: michael@0: mInfo.mIsOSUpdate = true; michael@0: rv = NS_NewNativeLocalFile(osApplyToDir, false, michael@0: getter_AddRefs(mInfo.mOSApplyToDir)); michael@0: if (NS_FAILED(rv)) { michael@0: LOG(("Can't create nsIFile for OS apply to dir")); michael@0: return rv; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: mUpdate = aUpdate; michael@0: michael@0: NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread"); michael@0: return NS_NewThread(getter_AddRefs(mProcessWatcher), michael@0: NS_NewRunnableMethod(this, &nsUpdateProcessor::StartStagedUpdate)); michael@0: } michael@0: michael@0: michael@0: michael@0: void michael@0: nsUpdateProcessor::StartStagedUpdate() michael@0: { michael@0: NS_ABORT_IF_FALSE(!NS_IsMainThread(), "main thread"); michael@0: michael@0: nsresult rv = ProcessUpdates(mInfo.mGREDir, michael@0: mInfo.mAppDir, michael@0: mInfo.mUpdateRoot, michael@0: mInfo.mArgc, michael@0: mInfo.mArgv, michael@0: mInfo.mAppVersion.get(), michael@0: false, michael@0: mInfo.mIsOSUpdate, michael@0: mInfo.mOSApplyToDir, michael@0: &mUpdaterPID); michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: michael@0: if (mUpdaterPID) { michael@0: // Track the state of the updater process while it is staging an update. michael@0: rv = NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::WaitForProcess)); michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: } else { michael@0: // Failed to launch the updater process for some reason. michael@0: // We need to shutdown the current thread as there isn't anything more for michael@0: // us to do... michael@0: rv = NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::ShutdownWatcherThread)); michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsUpdateProcessor::ShutdownWatcherThread() michael@0: { michael@0: NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread"); michael@0: mProcessWatcher->Shutdown(); michael@0: mProcessWatcher = nullptr; michael@0: mUpdate = nullptr; michael@0: } michael@0: michael@0: void michael@0: nsUpdateProcessor::WaitForProcess() michael@0: { michael@0: NS_ABORT_IF_FALSE(!NS_IsMainThread(), "main thread"); michael@0: ::WaitForProcess(mUpdaterPID); michael@0: NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::UpdateDone)); michael@0: } michael@0: michael@0: void michael@0: nsUpdateProcessor::UpdateDone() michael@0: { michael@0: NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread"); michael@0: michael@0: nsCOMPtr um = michael@0: do_GetService("@mozilla.org/updates/update-manager;1"); michael@0: if (um && mUpdate) { michael@0: um->RefreshUpdateStatus(mUpdate); michael@0: } michael@0: michael@0: ShutdownWatcherThread(); michael@0: } michael@0: