LCOV - code coverage report
Current view: top level - toolkit/xre - nsUpdateDriver.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 27 310 8.7 %
Date: 2017-07-14 16:53:18 Functions: 4 23 17.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim:set ts=2 sw=2 sts=2 et cindent: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include <stdlib.h>
       8             : #include <stdio.h>
       9             : #include "nsUpdateDriver.h"
      10             : #include "nsXULAppAPI.h"
      11             : #include "nsAppRunner.h"
      12             : #include "nsIWritablePropertyBag.h"
      13             : #include "nsIFile.h"
      14             : #include "nsVariant.h"
      15             : #include "nsCOMPtr.h"
      16             : #include "nsString.h"
      17             : #include "prproces.h"
      18             : #include "mozilla/Logging.h"
      19             : #include "prenv.h"
      20             : #include "nsVersionComparator.h"
      21             : #include "nsDirectoryServiceDefs.h"
      22             : #include "nsThreadUtils.h"
      23             : #include "nsIXULAppInfo.h"
      24             : #include "mozilla/Preferences.h"
      25             : #include "nsPrintfCString.h"
      26             : #include "mozilla/DebugOnly.h"
      27             : #include "mozilla/Printf.h"
      28             : 
      29             : #ifdef XP_MACOSX
      30             : #include "nsILocalFileMac.h"
      31             : #include "nsCommandLineServiceMac.h"
      32             : #include "MacLaunchHelper.h"
      33             : #include "updaterfileutils_osx.h"
      34             : #include "mozilla/Monitor.h"
      35             : #endif
      36             : 
      37             : #if defined(XP_WIN)
      38             : # include <direct.h>
      39             : # include <process.h>
      40             : # include <windows.h>
      41             : # include <shlwapi.h>
      42             : # define getcwd(path, size) _getcwd(path, size)
      43             : # define getpid() GetCurrentProcessId()
      44             : #elif defined(XP_UNIX)
      45             : # include <unistd.h>
      46             : # include <sys/wait.h>
      47             : #endif
      48             : 
      49             : using namespace mozilla;
      50             : 
      51             : static LazyLogModule sUpdateLog("updatedriver");
      52             : #define LOG(args) MOZ_LOG(sUpdateLog, mozilla::LogLevel::Debug, args)
      53             : 
      54             : #ifdef XP_WIN
      55             : #define UPDATER_BIN "updater.exe"
      56             : #elif XP_MACOSX
      57             : #define UPDATER_BIN "org.mozilla.updater"
      58             : #else
      59             : #define UPDATER_BIN "updater"
      60             : #endif
      61             : #define UPDATER_INI "updater.ini"
      62             : #ifdef XP_MACOSX
      63             : #define UPDATER_APP "updater.app"
      64             : #endif
      65             : #if defined(XP_UNIX) && !defined(XP_MACOSX)
      66             : #define UPDATER_PNG "updater.png"
      67             : #endif
      68             : 
      69             : #ifdef XP_MACOSX
      70             : static void
      71             : UpdateDriverSetupMacCommandLine(int& argc, char**& argv, bool restart)
      72             : {
      73             :   if (NS_IsMainThread()) {
      74             :     CommandLineServiceMac::SetupMacCommandLine(argc, argv, restart);
      75             :     return;
      76             :   }
      77             :   // Bug 1335916: SetupMacCommandLine calls a CoreFoundation function that
      78             :   // asserts that it was called from the main thread, so if we are not the main
      79             :   // thread, we have to dispatch that call to there. But we also have to get the
      80             :   // result from it, so we can't just dispatch and return, we have to wait
      81             :   // until the dispatched operation actually completes. So we also set up a
      82             :   // monitor to signal us when that happens, and block until then.
      83             :   Monitor monitor("nsUpdateDriver SetupMacCommandLine");
      84             : 
      85             :   nsresult rv = NS_DispatchToMainThread(
      86             :     NS_NewRunnableFunction("UpdateDriverSetupMacCommandLine",
      87             :                            [&argc, &argv, restart, &monitor]() -> void
      88             :     {
      89             :       CommandLineServiceMac::SetupMacCommandLine(argc, argv, restart);
      90             :       MonitorAutoLock(monitor).Notify();
      91             :     }));
      92             : 
      93             :   if (NS_FAILED(rv)) {
      94             :     LOG(("Update driver error dispatching SetupMacCommandLine to main thread: %d\n", rv));
      95             :     return;
      96             :   }
      97             : 
      98             :   // The length of this wait is arbitrary, but should be long enough that having
      99             :   // it expire means something is seriously wrong.
     100             :   rv = MonitorAutoLock(monitor).Wait(PR_SecondsToInterval(60));
     101             :   if (NS_FAILED(rv)) {
     102             :     LOG(("Update driver timed out waiting for SetupMacCommandLine: %d\n", rv));
     103             :   }
     104             : }
     105             : #endif
     106             : 
     107             : static nsresult
     108           0 : GetCurrentWorkingDir(char *buf, size_t size)
     109             : {
     110             :   // Cannot use NS_GetSpecialDirectory because XPCOM is not yet initialized.
     111             :   // This code is duplicated from xpcom/io/SpecialSystemDirectory.cpp:
     112             : 
     113             : #if defined(XP_WIN)
     114             :   wchar_t wpath[MAX_PATH];
     115             :   if (!_wgetcwd(wpath, size))
     116             :     return NS_ERROR_FAILURE;
     117             :   NS_ConvertUTF16toUTF8 path(wpath);
     118             :   strncpy(buf, path.get(), size);
     119             : #else
     120           0 :   if(!getcwd(buf, size))
     121           0 :     return NS_ERROR_FAILURE;
     122             : #endif
     123           0 :   return NS_OK;
     124             : }
     125             : 
     126             : /**
     127             :  * Get the path to the installation directory. For Mac OS X this will be the
     128             :  * bundle directory.
     129             :  *
     130             :  * @param appDir         the application directory file object
     131             :  * @param installDirPath the path to the installation directory
     132             :  */
     133             : static nsresult
     134           0 : GetInstallDirPath(nsIFile *appDir, nsACString& installDirPath)
     135             : {
     136             :   nsresult rv;
     137             : #ifdef XP_MACOSX
     138             :   nsCOMPtr<nsIFile> parentDir1, parentDir2;
     139             :   rv = appDir->GetParent(getter_AddRefs(parentDir1));
     140             :   if (NS_FAILED(rv)) {
     141             :     return rv;
     142             :   }
     143             :   rv = parentDir1->GetParent(getter_AddRefs(parentDir2));
     144             :   if (NS_FAILED(rv)) {
     145             :     return rv;
     146             :   }
     147             :   rv = parentDir2->GetNativePath(installDirPath);
     148             : #elif XP_WIN
     149             :   nsAutoString installDirPathW;
     150             :   rv = appDir->GetPath(installDirPathW);
     151             :   if (NS_FAILED(rv)) {
     152             :     return rv;
     153             :   }
     154             :   installDirPath = NS_ConvertUTF16toUTF8(installDirPathW);
     155             : #else
     156           0 :   rv = appDir->GetNativePath(installDirPath);
     157             : #endif
     158           0 :   if (NS_FAILED(rv)) {
     159           0 :     return rv;
     160             :   }
     161           0 :   return NS_OK;
     162             : }
     163             : 
     164             : #if defined(XP_MACOSX)
     165             : // This is a copy of OS X's XRE_GetBinaryPath from nsAppRunner.cpp with the
     166             : // gBinaryPath check removed so that the updater can reload the stub executable
     167             : // instead of xulrunner-bin. See bug 349737.
     168             : static nsresult
     169             : GetXULRunnerStubPath(const char* argv0, nsIFile* *aResult)
     170             : {
     171             :   // Works even if we're not bundled.
     172             :   CFBundleRef appBundle = ::CFBundleGetMainBundle();
     173             :   if (!appBundle)
     174             :     return NS_ERROR_FAILURE;
     175             : 
     176             :   CFURLRef bundleURL = ::CFBundleCopyExecutableURL(appBundle);
     177             :   if (!bundleURL)
     178             :     return NS_ERROR_FAILURE;
     179             : 
     180             :   nsCOMPtr<nsILocalFileMac> lfm;
     181             :   nsresult rv = NS_NewLocalFileWithCFURL(bundleURL, true, getter_AddRefs(lfm));
     182             : 
     183             :   ::CFRelease(bundleURL);
     184             : 
     185             :   if (NS_FAILED(rv))
     186             :     return rv;
     187             : 
     188             :   lfm.forget(aResult);
     189             :   return NS_OK;
     190             : }
     191             : #endif /* XP_MACOSX */
     192             : 
     193             : static bool
     194           1 : GetFile(nsIFile* dir, const nsACString& name, nsCOMPtr<nsIFile>& result)
     195             : {
     196             :   nsresult rv;
     197             : 
     198           2 :   nsCOMPtr<nsIFile> file;
     199           1 :   rv = dir->Clone(getter_AddRefs(file));
     200           1 :   if (NS_FAILED(rv))
     201           0 :     return false;
     202             : 
     203           1 :   rv = file->AppendNative(name);
     204           1 :   if (NS_FAILED(rv))
     205           0 :     return false;
     206             : 
     207           1 :   result = do_QueryInterface(file, &rv);
     208           1 :   return NS_SUCCEEDED(rv);
     209             : }
     210             : 
     211             : static bool
     212           0 : GetStatusFile(nsIFile *dir, nsCOMPtr<nsIFile> &result)
     213             : {
     214           0 :   return GetFile(dir, NS_LITERAL_CSTRING("update.status"), result);
     215             : }
     216             : 
     217             : /**
     218             :  * Get the contents of the update.status file.
     219             :  *
     220             :  * @param statusFile the status file object.
     221             :  * @param buf        the buffer holding the file contents
     222             :  *
     223             :  * @return true if successful, false otherwise.
     224             :  */
     225             : template <size_t Size>
     226             : static bool
     227           0 : GetStatusFileContents(nsIFile *statusFile, char (&buf)[Size])
     228             : {
     229             :   static_assert(Size > 16, "Buffer needs to be large enough to hold the known status codes");
     230             : 
     231           0 :   PRFileDesc *fd = nullptr;
     232           0 :   nsresult rv = statusFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd);
     233           0 :   if (NS_FAILED(rv))
     234           0 :     return false;
     235             : 
     236           0 :   const int32_t n = PR_Read(fd, buf, Size);
     237           0 :   PR_Close(fd);
     238             : 
     239           0 :   return (n >= 0);
     240             : }
     241             : 
     242             : typedef enum {
     243             :   eNoUpdateAction,
     244             :   ePendingUpdate,
     245             :   ePendingService,
     246             :   ePendingElevate,
     247             :   eAppliedUpdate,
     248             :   eAppliedService,
     249             : } UpdateStatus;
     250             : 
     251             : /**
     252             :  * Returns a value indicating what needs to be done in order to handle an update.
     253             :  *
     254             :  * @param dir the directory in which we should look for an update.status file.
     255             :  * @param statusFile the update.status file found in the directory.
     256             :  *
     257             :  * @return the update action to be performed.
     258             :  */
     259             : static UpdateStatus
     260           0 : GetUpdateStatus(nsIFile* dir, nsCOMPtr<nsIFile> &statusFile)
     261             : {
     262           0 :   if (GetStatusFile(dir, statusFile)) {
     263             :     char buf[32];
     264           0 :     if (GetStatusFileContents(statusFile, buf)) {
     265           0 :       const char kPending[] = "pending";
     266           0 :       const char kPendingService[] = "pending-service";
     267           0 :       const char kPendingElevate[] = "pending-elevate";
     268           0 :       const char kApplied[] = "applied";
     269           0 :       const char kAppliedService[] = "applied-service";
     270           0 :       if (!strncmp(buf, kPendingElevate, sizeof(kPendingElevate) - 1)) {
     271           0 :         return ePendingElevate;
     272             :       }
     273           0 :       if (!strncmp(buf, kPendingService, sizeof(kPendingService) - 1)) {
     274           0 :         return ePendingService;
     275             :       }
     276           0 :       if (!strncmp(buf, kPending, sizeof(kPending) - 1)) {
     277           0 :         return ePendingUpdate;
     278             :       }
     279           0 :       if (!strncmp(buf, kAppliedService, sizeof(kAppliedService) - 1)) {
     280           0 :         return eAppliedService;
     281             :       }
     282           0 :       if (!strncmp(buf, kApplied, sizeof(kApplied) - 1)) {
     283           0 :         return eAppliedUpdate;
     284             :       }
     285             :     }
     286             :   }
     287           0 :   return eNoUpdateAction;
     288             : }
     289             : 
     290             : static bool
     291           1 : GetVersionFile(nsIFile *dir, nsCOMPtr<nsIFile> &result)
     292             : {
     293           1 :   return GetFile(dir, NS_LITERAL_CSTRING("update.version"), result);
     294             : }
     295             : 
     296             : // Compares the current application version with the update's application
     297             : // version.
     298             : static bool
     299           1 : IsOlderVersion(nsIFile *versionFile, const char *appVersion)
     300             : {
     301           1 :   PRFileDesc *fd = nullptr;
     302           1 :   nsresult rv = versionFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd);
     303           1 :   if (NS_FAILED(rv))
     304           1 :     return true;
     305             : 
     306             :   char buf[32];
     307           0 :   const int32_t n = PR_Read(fd, buf, sizeof(buf));
     308           0 :   PR_Close(fd);
     309             : 
     310           0 :   if (n < 0)
     311           0 :     return false;
     312             : 
     313             :   // Trim off the trailing newline
     314           0 :   if (buf[n - 1] == '\n')
     315           0 :     buf[n - 1] = '\0';
     316             : 
     317             :   // If the update xml doesn't provide the application version the file will
     318             :   // contain the string "null" and it is assumed that the update is not older.
     319           0 :   const char kNull[] = "null";
     320           0 :   if (strncmp(buf, kNull, sizeof(kNull) - 1) == 0)
     321           0 :     return false;
     322             : 
     323           0 :   if (mozilla::Version(appVersion) > buf)
     324           0 :     return true;
     325             : 
     326           0 :   return false;
     327             : }
     328             : 
     329             : static bool
     330           0 : CopyFileIntoUpdateDir(nsIFile *parentDir, const nsACString& leaf, nsIFile *updateDir)
     331             : {
     332           0 :   nsCOMPtr<nsIFile> file;
     333             : 
     334             :   // Make sure there is not an existing file in the target location.
     335           0 :   nsresult rv = updateDir->Clone(getter_AddRefs(file));
     336           0 :   if (NS_FAILED(rv))
     337           0 :     return false;
     338           0 :   rv = file->AppendNative(leaf);
     339           0 :   if (NS_FAILED(rv))
     340           0 :     return false;
     341           0 :   file->Remove(true);
     342             : 
     343             :   // Now, copy into the target location.
     344           0 :   rv = parentDir->Clone(getter_AddRefs(file));
     345           0 :   if (NS_FAILED(rv))
     346           0 :     return false;
     347           0 :   rv = file->AppendNative(leaf);
     348           0 :   if (NS_FAILED(rv))
     349           0 :     return false;
     350           0 :   rv = file->CopyToNative(updateDir, EmptyCString());
     351           0 :   if (NS_FAILED(rv))
     352           0 :     return false;
     353             : 
     354           0 :   return true;
     355             : }
     356             : 
     357             : static bool
     358           0 : CopyUpdaterIntoUpdateDir(nsIFile *greDir, nsIFile *appDir, nsIFile *updateDir,
     359             :                          nsCOMPtr<nsIFile> &updater)
     360             : {
     361             :   // Copy the updater application from the GRE and the updater ini from the app.
     362             : #if defined(XP_MACOSX)
     363             :   if (!CopyFileIntoUpdateDir(appDir, NS_LITERAL_CSTRING(UPDATER_APP), updateDir))
     364             :     return false;
     365             :   CopyFileIntoUpdateDir(greDir, NS_LITERAL_CSTRING(UPDATER_INI), updateDir);
     366             : #else
     367           0 :   if (!CopyFileIntoUpdateDir(greDir, NS_LITERAL_CSTRING(UPDATER_BIN), updateDir))
     368           0 :     return false;
     369           0 :   CopyFileIntoUpdateDir(appDir, NS_LITERAL_CSTRING(UPDATER_INI), updateDir);
     370             : #endif
     371             : #if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(ANDROID)
     372           0 :   nsCOMPtr<nsIFile> iconDir;
     373           0 :   appDir->Clone(getter_AddRefs(iconDir));
     374           0 :   iconDir->AppendNative(NS_LITERAL_CSTRING("icons"));
     375           0 :   if (!CopyFileIntoUpdateDir(iconDir, NS_LITERAL_CSTRING(UPDATER_PNG), updateDir))
     376           0 :     return false;
     377             : #endif
     378             :   // Finally, return the location of the updater binary.
     379           0 :   nsresult rv = updateDir->Clone(getter_AddRefs(updater));
     380           0 :   if (NS_FAILED(rv))
     381           0 :     return false;
     382             : #if defined(XP_MACOSX)
     383             :   rv  = updater->AppendNative(NS_LITERAL_CSTRING(UPDATER_APP));
     384             :   nsresult tmp = updater->AppendNative(NS_LITERAL_CSTRING("Contents"));
     385             :   if (NS_FAILED(tmp)) {
     386             :     rv = tmp;
     387             :   }
     388             :   tmp = updater->AppendNative(NS_LITERAL_CSTRING("MacOS"));
     389             :   if (NS_FAILED(tmp) || NS_FAILED(rv))
     390             :     return false;
     391             : #endif
     392           0 :   rv = updater->AppendNative(NS_LITERAL_CSTRING(UPDATER_BIN));
     393           0 :   return NS_SUCCEEDED(rv);
     394             : }
     395             : 
     396             : /**
     397             :  * Appends the specified path to the library path.
     398             :  * This is used so that updater can find libmozsqlite3.so and other shared libs.
     399             :  *
     400             :  * @param pathToAppend A new library path to prepend to LD_LIBRARY_PATH
     401             :  */
     402             : #if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && !defined(XP_MACOSX)
     403             : #include "prprf.h"
     404             : #define PATH_SEPARATOR ":"
     405             : #define LD_LIBRARY_PATH_ENVVAR_NAME "LD_LIBRARY_PATH"
     406             : static void
     407             : AppendToLibPath(const char *pathToAppend)
     408             : {
     409             :   char *pathValue = getenv(LD_LIBRARY_PATH_ENVVAR_NAME);
     410             :   if (nullptr == pathValue || '\0' == *pathValue) {
     411             :     // Leak the string because that is required by PR_SetEnv.
     412             :     char *s = Smprintf("%s=%s", LD_LIBRARY_PATH_ENVVAR_NAME, pathToAppend).release();
     413             :     PR_SetEnv(s);
     414             :   } else if (!strstr(pathValue, pathToAppend)) {
     415             :     // Leak the string because that is required by PR_SetEnv.
     416             :     char *s = Smprintf("%s=%s" PATH_SEPARATOR "%s",
     417             :                        LD_LIBRARY_PATH_ENVVAR_NAME, pathToAppend, pathValue).release();
     418             :     PR_SetEnv(s);
     419             :   }
     420             : }
     421             : #endif
     422             : 
     423             : /**
     424             :  * Applies, switches, or stages an update.
     425             :  *
     426             :  * @param greDir       the GRE directory
     427             :  * @param updateDir    the update root directory
     428             :  * @param appDir       the application directory
     429             :  * @param appArgc      the number of args passed to the application
     430             :  * @param appArgv      the args passed to the application
     431             :  *                     (used for restarting the application when necessary)
     432             :  * @param restart      true when a restart is necessary.
     433             :  * @param isStaged     true when the update has already been staged
     434             :  * @param outpid (out) parameter holding the handle to the updater application
     435             :  *                     when staging updates
     436             :  */
     437             : static void
     438           0 : ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *appDir, int appArgc,
     439             :             char **appArgv, bool restart, bool isStaged, ProcessType *outpid)
     440             : {
     441             :   // The following determines the update operation to perform.
     442             :   // 1. When restart is false the update will be staged.
     443             :   // 2. When restart is true and isStaged is false the update will apply the mar
     444             :   //    file to the installation directory.
     445             :   // 3. When restart is true and isStaged is true the update will switch the
     446             :   //    staged update with the installation directory.
     447             : 
     448             :   nsresult rv;
     449             : 
     450           0 :   nsCOMPtr<nsIFile> updater;
     451           0 :   nsAutoCString updaterPath;
     452           0 :   nsAutoCString updateDirPath;
     453             : #if defined(XP_WIN)
     454             :   // Get an nsIFile reference for the updater in the installation dir.
     455             :   if (!GetFile(greDir, NS_LITERAL_CSTRING(UPDATER_BIN), updater)) {
     456             :     return;
     457             :   }
     458             : 
     459             :   // Get the path to the updater.
     460             :   nsAutoString updaterPathW;
     461             :   rv = updater->GetPath(updaterPathW);
     462             :   if (NS_FAILED(rv)) {
     463             :     return;
     464             :   }
     465             :   updaterPath = NS_ConvertUTF16toUTF8(updaterPathW);
     466             : 
     467             :   // Get the path to the update dir.
     468             :   nsAutoString updateDirPathW;
     469             :   rv = updateDir->GetPath(updateDirPathW);
     470             :   if (NS_FAILED(rv)) {
     471             :     return;
     472             :   }
     473             :   updateDirPath = NS_ConvertUTF16toUTF8(updateDirPathW);
     474             : #else
     475           0 :   if (isStaged) {
     476           0 :     nsCOMPtr<nsIFile> mozUpdaterDir;
     477           0 :     rv = updateDir->Clone(getter_AddRefs(mozUpdaterDir));
     478           0 :     if (NS_FAILED(rv)) {
     479           0 :       LOG(("failed cloning update dir\n"));
     480           0 :       return;
     481             :     }
     482             : 
     483             :     // Create a new directory named MozUpdater in the updates/0 directory to copy
     484             :     // the updater files to that will be used to replace the installation with the
     485             :     // staged application that has been updated. Note that we don't check for
     486             :     // directory creation errors since the call to CopyUpdaterIntoUpdateDir will
     487             :     // fail if the creation of the directory fails. A unique directory is created
     488             :     // in MozUpdater in case a previous attempt locked the directory or files.
     489           0 :     mozUpdaterDir->Append(NS_LITERAL_STRING("MozUpdater"));
     490           0 :     mozUpdaterDir->Append(NS_LITERAL_STRING("bgupdate"));
     491           0 :     rv = mozUpdaterDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0755);
     492           0 :     if (NS_FAILED(rv)) {
     493           0 :       LOG(("failed creating unique dir\n"));
     494           0 :       return;
     495             :     }
     496             : 
     497             :     // Copy the updater and files needed to update into the MozUpdater/bgupdate
     498             :     // directory in the update dir and get an nsIFile reference to the copied
     499             :     // updater.
     500           0 :     if (!CopyUpdaterIntoUpdateDir(greDir, appDir, mozUpdaterDir, updater)) {
     501           0 :       LOG(("failed copying updater\n"));
     502           0 :       return;
     503             :     }
     504             :   } else {
     505             :     // Copy the updater and files needed to update into the update directory and
     506             :     // get an nsIFile reference to the copied updater.
     507           0 :     if (!CopyUpdaterIntoUpdateDir(greDir, appDir, updateDir, updater)) {
     508           0 :       LOG(("failed copying updater\n"));
     509           0 :       return;
     510             :     }
     511             :   }
     512             : 
     513             :   // Get the path to the updater that will be used.
     514           0 :   rv = updater->GetNativePath(updaterPath);
     515           0 :   if (NS_FAILED(rv)) {
     516           0 :     return;
     517             :   }
     518             : 
     519             :   // Get the path to the update dir.
     520           0 :   rv = updateDir->GetNativePath(updateDirPath);
     521           0 :   if (NS_FAILED(rv)) {
     522           0 :      return;
     523             :   }
     524             : #endif
     525             : 
     526             :   // appFilePath and workingDirPath are only used when the application will be
     527             :   // restarted.
     528           0 :   nsAutoCString appFilePath;
     529             :   char workingDirPath[MAXPATHLEN];
     530           0 :   if (restart) {
     531             :     // Get the path to the current working directory.
     532           0 :     rv = GetCurrentWorkingDir(workingDirPath, sizeof(workingDirPath));
     533           0 :     if (NS_FAILED(rv)) {
     534           0 :       return;
     535             :     }
     536             : 
     537             :     // Get the application file path used by the updater to restart the
     538             :     // application after the update has finished.
     539           0 :     nsCOMPtr<nsIFile> appFile;
     540             : #if defined(XP_MACOSX)
     541             :     // On OS X we need to pass the location of the xulrunner-stub executable
     542             :     // rather than xulrunner-bin. See bug 349737.
     543             :     GetXULRunnerStubPath(appArgv[0], getter_AddRefs(appFile));
     544             : #else
     545           0 :     XRE_GetBinaryPath(appArgv[0], getter_AddRefs(appFile));
     546             : #endif
     547           0 :     if (!appFile) {
     548           0 :       return;
     549             :     }
     550             : 
     551             : #if defined(XP_WIN)
     552             :     nsAutoString appFilePathW;
     553             :     rv = appFile->GetPath(appFilePathW);
     554             :     if (NS_FAILED(rv)) {
     555             :       return;
     556             :     }
     557             :     appFilePath = NS_ConvertUTF16toUTF8(appFilePathW);
     558             : #else
     559           0 :     rv = appFile->GetNativePath(appFilePath);
     560           0 :     if (NS_FAILED(rv)) {
     561           0 :       return;
     562             :     }
     563             : #endif
     564             :   }
     565             : 
     566             :   // Get the installation directory path.
     567           0 :   nsAutoCString installDirPath;
     568           0 :   rv = GetInstallDirPath(appDir, installDirPath);
     569           0 :   if (NS_FAILED(rv)) {
     570           0 :     return;
     571             :   }
     572             : 
     573           0 :   nsAutoCString applyToDirPath;
     574           0 :   nsCOMPtr<nsIFile> updatedDir;
     575           0 :   if (restart && !isStaged) {
     576             :     // The install directory is the same as the apply to directory.
     577           0 :     applyToDirPath.Assign(installDirPath);
     578             :   } else {
     579             :     // Get the directory where the update is staged or will be staged.
     580             : #if defined(XP_MACOSX)
     581             :     if (!GetFile(updateDir, NS_LITERAL_CSTRING("Updated.app"), updatedDir)) {
     582             : #else
     583           0 :     if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) {
     584             : #endif
     585           0 :       return;
     586             :     }
     587             : #if defined(XP_WIN)
     588             :     nsAutoString applyToDirPathW;
     589             :     rv = updatedDir->GetPath(applyToDirPathW);
     590             :     if (NS_FAILED(rv)) {
     591             :       return;
     592             :     }
     593             :     applyToDirPath = NS_ConvertUTF16toUTF8(applyToDirPathW);
     594             : #else
     595           0 :     rv = updatedDir->GetNativePath(applyToDirPath);
     596             : #endif
     597             :   }
     598           0 :   if (NS_FAILED(rv)) {
     599           0 :      return;
     600             :   }
     601             : 
     602           0 :   if (restart && isStaged) {
     603             :     // When the update should already be staged make sure that the updated
     604             :     // directory exists.
     605           0 :     bool updatedDirExists = false;
     606           0 :     if (NS_FAILED(updatedDir->Exists(&updatedDirExists)) || !updatedDirExists) {
     607           0 :       return;
     608             :     }
     609             :   }
     610             : 
     611             :   // On platforms where we are not calling execv, we may need to make the
     612             :   // updater executable wait for the calling process to exit.  Otherwise, the
     613             :   // updater may have trouble modifying our executable image (because it might
     614             :   // still be in use).  This is accomplished by passing our PID to the updater so
     615             :   // that it can wait for us to exit.  This is not perfect as there is a race
     616             :   // condition that could bite us.  It's possible that the calling process could
     617             :   // exit before the updater waits on the specified PID, and in the meantime a
     618             :   // new process with the same PID could be created.  This situation is unlikely,
     619             :   // however, given the way most operating systems recycle PIDs.  We'll take our
     620             :   // chances ;-)
     621             :   // Construct the PID argument for this process to pass to the updater.
     622           0 :   nsAutoCString pid;
     623           0 :   if (restart) {
     624             : #if defined(XP_UNIX) & !defined(XP_MACOSX)
     625             :     // When execv is used for an update that requires a restart 0 is passed
     626             :     // which is ignored by the updater.
     627           0 :     pid.AssignASCII("0");
     628             : #else
     629             :     pid.AppendInt((int32_t) getpid());
     630             : #endif
     631           0 :     if (isStaged) {
     632             :       // Append a special token to the PID in order to inform the updater that
     633             :       // it should replace install with the updated directory.
     634           0 :       pid.AppendLiteral("/replace");
     635             :     }
     636             :   } else {
     637             :     // Signal the updater application that it should stage the update.
     638           0 :     pid.AssignASCII("-1");
     639             :   }
     640             : 
     641           0 :   int argc = 5;
     642           0 :   if (restart) {
     643           0 :     argc = appArgc + 6;
     644             :   }
     645           0 :   char **argv = new char*[argc + 1];
     646           0 :   if (!argv) {
     647           0 :     return;
     648             :   }
     649           0 :   argv[0] = (char*) updaterPath.get();
     650           0 :   argv[1] = (char*) updateDirPath.get();
     651           0 :   argv[2] = (char*) installDirPath.get();
     652           0 :   argv[3] = (char*) applyToDirPath.get();
     653           0 :   argv[4] = (char*) pid.get();
     654           0 :   if (restart && appArgc) {
     655           0 :     argv[5] = workingDirPath;
     656           0 :     argv[6] = (char*) appFilePath.get();
     657           0 :     for (int i = 1; i < appArgc; ++i) {
     658           0 :       argv[6 + i] = appArgv[i];
     659             :     }
     660           0 :     argv[argc] = nullptr;
     661             :   } else {
     662           0 :     argv[5] = nullptr;
     663             :   }
     664             : 
     665           0 :   if (restart && gSafeMode) {
     666           0 :     PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
     667             :   }
     668             : 
     669             : #if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && !defined(XP_MACOSX)
     670             :   AppendToLibPath(installDirPath.get());
     671             : #endif
     672             : 
     673           0 :   LOG(("spawning updater process [%s]\n", updaterPath.get()));
     674             : 
     675             : #if defined(XP_UNIX) && !defined(XP_MACOSX)
     676             :   // We use execv to spawn the updater process on all UNIX systems except Mac OSX
     677             :   // since it is known to cause problems on the Mac.  Windows has execv, but it
     678             :   // is a faked implementation that doesn't really replace the current process.
     679             :   // Instead it spawns a new process, so we gain nothing from using execv on
     680             :   // Windows.
     681           0 :   if (restart) {
     682           0 :     exit(execv(updaterPath.get(), argv));
     683             :   }
     684           0 :   *outpid = fork();
     685           0 :   if (*outpid == -1) {
     686           0 :     return;
     687           0 :   } else if (*outpid == 0) {
     688           0 :     exit(execv(updaterPath.get(), argv));
     689             :   }
     690             : #elif defined(XP_WIN)
     691             :   if (isStaged) {
     692             :     // Launch the updater to replace the installation with the staged updated.
     693             :     if (!WinLaunchChild(updaterPathW.get(), argc, argv)) {
     694             :       return;
     695             :     }
     696             :   } else {
     697             :     // Launch the updater to either stage or apply an update.
     698             :     if (!WinLaunchChild(updaterPathW.get(), argc, argv, nullptr, outpid)) {
     699             :       return;
     700             :     }
     701             :   }
     702             : #elif defined(XP_MACOSX)
     703             :   UpdateDriverSetupMacCommandLine(argc, argv, restart);
     704             :   // We need to detect whether elevation is required for this update. This can
     705             :   // occur when an admin user installs the application, but another admin
     706             :   // user attempts to update (see bug 394984).
     707             :   if (restart && !IsRecursivelyWritable(installDirPath.get())) {
     708             :     if (!LaunchElevatedUpdate(argc, argv, outpid)) {
     709             :       LOG(("Failed to launch elevated update!"));
     710             :       exit(1);
     711             :     }
     712             :     exit(0);
     713             :   }
     714             : 
     715             :   if (isStaged) {
     716             :     // Launch the updater to replace the installation with the staged updated.
     717             :     LaunchChildMac(argc, argv);
     718             :   } else {
     719             :     // Launch the updater to either stage or apply an update.
     720             :     LaunchChildMac(argc, argv, outpid);
     721             :   }
     722             :   if (restart) {
     723             :     exit(0);
     724             :   }
     725             : #else
     726             :   if (isStaged) {
     727             :     // Launch the updater to replace the installation with the staged updated.
     728             :     PR_CreateProcessDetached(updaterPath.get(), argv, nullptr, nullptr);
     729             :   } else {
     730             :     // Launch the updater to either stage or apply an update.
     731             :     *outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr);
     732             :   }
     733             : #endif
     734             : #if !defined(USE_EXECV)
     735           0 :   if (restart) {
     736           0 :     exit(0);
     737             :   }
     738             : #endif
     739             : }
     740             : 
     741             : /**
     742             :  * Wait briefly to see if a process terminates, then return true if it has.
     743             :  */
     744             : static bool
     745           0 : ProcessHasTerminated(ProcessType pt)
     746             : {
     747             : #if defined(XP_WIN)
     748             :   if (WaitForSingleObject(pt, 1000)) {
     749             :     return false;
     750             :   }
     751             :   CloseHandle(pt);
     752             :   return true;
     753             : #elif defined(XP_MACOSX)
     754             :   // We're waiting for the process to terminate in LaunchChildMac.
     755             :   return true;
     756             : #elif defined(XP_UNIX)
     757             :   int exitStatus;
     758           0 :   pid_t exited = waitpid(pt, &exitStatus, WNOHANG);
     759           0 :   if (exited == 0) {
     760             :     // Process is still running.
     761           0 :     sleep(1);
     762           0 :     return false;
     763             :   }
     764           0 :   if (exited == -1) {
     765           0 :     LOG(("Error while checking if the updater process is finished"));
     766             :     // This shouldn't happen, but if it does, the updater process is lost to us,
     767             :     // so the best we can do is pretend that it's exited.
     768           0 :     return true;
     769             :   }
     770             :   // If we get here, the process has exited; make sure it exited normally.
     771           0 :   if (WIFEXITED(exitStatus) && (WEXITSTATUS(exitStatus) != 0)) {
     772           0 :     LOG(("Error while running the updater process, check update.log"));
     773             :   }
     774           0 :   return true;
     775             : #else
     776             :   // No way to have a non-blocking implementation on these platforms,
     777             :   // because we're using NSPR and it only provides a blocking wait.
     778             :   int32_t exitCode;
     779             :   PR_WaitProcess(pt, &exitCode);
     780             :   if (exitCode != 0) {
     781             :     LOG(("Error while running the updater process, check update.log"));
     782             :   }
     783             :   return true;
     784             : #endif
     785             : }
     786             : 
     787             : nsresult
     788           1 : ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir,
     789             :                int argc, char **argv, const char *appVersion,
     790             :                bool restart, ProcessType *pid)
     791             : {
     792             :   nsresult rv;
     793             : 
     794           2 :   nsCOMPtr<nsIFile> updatesDir;
     795           1 :   rv = updRootDir->Clone(getter_AddRefs(updatesDir));
     796           1 :   if (NS_FAILED(rv)) {
     797           0 :     return rv;
     798             :   }
     799             : 
     800           1 :   rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("updates"));
     801           1 :   if (NS_FAILED(rv)) {
     802           0 :     return rv;
     803             :   }
     804             : 
     805           1 :   rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("0"));
     806           1 :   if (NS_FAILED(rv)) {
     807           0 :     return rv;
     808             :   }
     809             : 
     810             :   // Return early since there isn't a valid update when the update application
     811             :   // version file doesn't exist or if the update's application version is less
     812             :   // than the current application version. The cleanup of the update will happen
     813             :   // during post update processing in nsUpdateService.js.
     814           2 :   nsCOMPtr<nsIFile> versionFile;
     815           2 :   if (!GetVersionFile(updatesDir, versionFile) ||
     816           1 :       IsOlderVersion(versionFile, appVersion)) {
     817           1 :     return NS_OK;
     818             :   }
     819             : 
     820           0 :   nsCOMPtr<nsIFile> statusFile;
     821           0 :   UpdateStatus status = GetUpdateStatus(updatesDir, statusFile);
     822           0 :   switch (status) {
     823             :   case ePendingElevate: {
     824           0 :     if (NS_IsMainThread()) {
     825             :       // Only do this if we're called from the main thread.
     826             :       nsCOMPtr<nsIUpdatePrompt> up =
     827           0 :         do_GetService("@mozilla.org/updates/update-prompt;1");
     828           0 :       if (up) {
     829           0 :         up->ShowUpdateElevationRequired();
     830             :       }
     831           0 :       break;
     832             :     }
     833             :     // Intentional fallthrough to ePendingUpdate and ePendingService.
     834             :     MOZ_FALLTHROUGH;
     835             :   }
     836             :   case ePendingUpdate:
     837             :   case ePendingService: {
     838           0 :     ApplyUpdate(greDir, updatesDir, appDir, argc, argv, restart, false, pid);
     839           0 :     break;
     840             :   }
     841             :   case eAppliedUpdate:
     842             :   case eAppliedService:
     843             :     // An update was staged and needs to be switched so the updated application
     844             :     // is used.
     845           0 :     ApplyUpdate(greDir, updatesDir, appDir, argc, argv, restart, true, pid);
     846           0 :     break;
     847             :   case eNoUpdateAction:
     848             :     // We don't need to do any special processing here, we'll just continue to
     849             :     // startup the application.
     850           0 :     break;
     851             :   }
     852             : 
     853           0 :   return NS_OK;
     854             : }
     855             : 
     856             : 
     857             : 
     858           0 : NS_IMPL_ISUPPORTS(nsUpdateProcessor, nsIUpdateProcessor)
     859             : 
     860           0 : nsUpdateProcessor::nsUpdateProcessor()
     861           0 :   : mUpdaterPID(0)
     862             : {
     863           0 : }
     864             : 
     865           0 : nsUpdateProcessor::~nsUpdateProcessor()
     866             : {
     867           0 : }
     868             : 
     869             : NS_IMETHODIMP
     870           0 : nsUpdateProcessor::ProcessUpdate(nsIUpdate* aUpdate)
     871             : {
     872             :   nsresult rv;
     873             : 
     874             :   nsCOMPtr<nsIProperties> ds =
     875           0 :     do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
     876           0 :   NS_ENSURE_SUCCESS(rv, rv);
     877             : 
     878           0 :   nsCOMPtr<nsIFile> exeFile;
     879           0 :   rv = ds->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile),
     880           0 :                getter_AddRefs(exeFile));
     881           0 :   NS_ENSURE_SUCCESS(rv, rv);
     882             : 
     883           0 :   nsCOMPtr<nsIFile> appDir;
     884           0 :   rv = exeFile->GetParent(getter_AddRefs(appDir));
     885           0 :   NS_ENSURE_SUCCESS(rv, rv);
     886             : 
     887           0 :   nsCOMPtr<nsIFile> greDir;
     888           0 :   rv = ds->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir));
     889           0 :   NS_ENSURE_SUCCESS(rv, rv);
     890             : 
     891           0 :   nsCOMPtr<nsIFile> updRoot;
     892           0 :   rv = ds->Get(XRE_UPDATE_ROOT_DIR, NS_GET_IID(nsIFile),
     893           0 :                getter_AddRefs(updRoot));
     894           0 :   NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the UpdRootD dir");
     895             : 
     896             :   // XRE_UPDATE_ROOT_DIR should not fail but if it does fallback to the
     897             :   // application directory just to be safe.
     898           0 :   if (NS_FAILED(rv)) {
     899           0 :     rv = appDir->Clone(getter_AddRefs(updRoot));
     900           0 :     NS_ENSURE_SUCCESS(rv, rv);
     901             :   }
     902             : 
     903             :   nsCOMPtr<nsIXULAppInfo> appInfo =
     904           0 :     do_GetService("@mozilla.org/xre/app-info;1", &rv);
     905           0 :   NS_ENSURE_SUCCESS(rv, rv);
     906             : 
     907           0 :   nsAutoCString appVersion;
     908           0 :   rv = appInfo->GetVersion(appVersion);
     909           0 :   NS_ENSURE_SUCCESS(rv, rv);
     910             : 
     911             :   // Copy the parameters to the StagedUpdateInfo structure shared with the
     912             :   // watcher thread.
     913           0 :   mInfo.mGREDir = greDir;
     914           0 :   mInfo.mAppDir = appDir;
     915           0 :   mInfo.mUpdateRoot = updRoot;
     916           0 :   mInfo.mArgc = 0;
     917           0 :   mInfo.mArgv = nullptr;
     918           0 :   mInfo.mAppVersion = appVersion;
     919             : 
     920           0 :   MOZ_ASSERT(NS_IsMainThread(), "not main thread");
     921             :   nsCOMPtr<nsIRunnable> r =
     922           0 :     NewRunnableMethod("nsUpdateProcessor::StartStagedUpdate",
     923             :                       this,
     924           0 :                       &nsUpdateProcessor::StartStagedUpdate);
     925           0 :   return NS_NewNamedThread("Update Watcher", getter_AddRefs(mProcessWatcher),
     926           0 :                            r);
     927             : }
     928             : 
     929             : 
     930             : 
     931             : void
     932           0 : nsUpdateProcessor::StartStagedUpdate()
     933             : {
     934           0 :   MOZ_ASSERT(!NS_IsMainThread(), "main thread");
     935             : 
     936           0 :   nsresult rv = ProcessUpdates(mInfo.mGREDir,
     937             :                                mInfo.mAppDir,
     938             :                                mInfo.mUpdateRoot,
     939             :                                mInfo.mArgc,
     940             :                                mInfo.mArgv,
     941             :                                mInfo.mAppVersion.get(),
     942             :                                false,
     943           0 :                                &mUpdaterPID);
     944           0 :   NS_ENSURE_SUCCESS_VOID(rv);
     945             : 
     946           0 :   if (mUpdaterPID) {
     947             :     // Track the state of the updater process while it is staging an update.
     948           0 :     rv = NS_DispatchToCurrentThread(
     949           0 :       NewRunnableMethod("nsUpdateProcessor::WaitForProcess",
     950             :                         this,
     951           0 :                         &nsUpdateProcessor::WaitForProcess));
     952           0 :     NS_ENSURE_SUCCESS_VOID(rv);
     953             :   } else {
     954             :     // Failed to launch the updater process for some reason.
     955             :     // We need to shutdown the current thread as there isn't anything more for
     956             :     // us to do...
     957           0 :     rv = NS_DispatchToMainThread(
     958           0 :       NewRunnableMethod("nsUpdateProcessor::ShutdownWatcherThread",
     959             :                         this,
     960           0 :                         &nsUpdateProcessor::ShutdownWatcherThread));
     961           0 :     NS_ENSURE_SUCCESS_VOID(rv);
     962             :   }
     963             : }
     964             : 
     965             : void
     966           0 : nsUpdateProcessor::ShutdownWatcherThread()
     967             : {
     968           0 :   MOZ_ASSERT(NS_IsMainThread(), "not main thread");
     969           0 :   mProcessWatcher->Shutdown();
     970           0 :   mProcessWatcher = nullptr;
     971           0 : }
     972             : 
     973             : void
     974           0 : nsUpdateProcessor::WaitForProcess()
     975             : {
     976           0 :   MOZ_ASSERT(!NS_IsMainThread(), "main thread");
     977           0 :   if (ProcessHasTerminated(mUpdaterPID)) {
     978           0 :     NS_DispatchToMainThread(NewRunnableMethod(
     979           0 :       "nsUpdateProcessor::UpdateDone", this, &nsUpdateProcessor::UpdateDone));
     980             :   } else {
     981           0 :     NS_DispatchToCurrentThread(
     982           0 :       NewRunnableMethod("nsUpdateProcessor::WaitForProcess",
     983             :                         this,
     984           0 :                         &nsUpdateProcessor::WaitForProcess));
     985             :   }
     986           0 : }
     987             : 
     988             : void
     989           0 : nsUpdateProcessor::UpdateDone()
     990             : {
     991           0 :   MOZ_ASSERT(NS_IsMainThread(), "not main thread");
     992             : 
     993             :   nsCOMPtr<nsIUpdateManager> um =
     994           0 :     do_GetService("@mozilla.org/updates/update-manager;1");
     995           0 :   if (um) {
     996           0 :     um->RefreshUpdateStatus();
     997             :   }
     998             : 
     999           0 :   ShutdownWatcherThread();
    1000           0 : }

Generated by: LCOV version 1.13