LCOV - code coverage report
Current view: top level - toolkit/xre - nsUpdateDriver.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 4 277 1.4 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
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             :   CVStatus status = MonitorAutoLock(monitor).Wait(TimeDuration::FromSeconds(60));
     101             :   if (status == CVStatus::Timeout) {
     102             :     LOG(("Update driver timed out waiting for SetupMacCommandLine\n"));
     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             :     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             :     return rv;
     160             :   }
     161           0 :   return NS_OK;
     162             : }
     163             : 
     164             : static bool
     165           0 : GetFile(nsIFile* dir, const nsACString& name, nsCOMPtr<nsIFile>& result)
     166             : {
     167             :   nsresult rv;
     168             : 
     169           0 :   nsCOMPtr<nsIFile> file;
     170           0 :   rv = dir->Clone(getter_AddRefs(file));
     171           0 :   if (NS_FAILED(rv))
     172             :     return false;
     173             : 
     174           0 :   rv = file->AppendNative(name);
     175           0 :   if (NS_FAILED(rv))
     176             :     return false;
     177             : 
     178           0 :   result = do_QueryInterface(file, &rv);
     179           0 :   return NS_SUCCEEDED(rv);
     180             : }
     181             : 
     182             : static bool
     183           0 : GetStatusFile(nsIFile *dir, nsCOMPtr<nsIFile> &result)
     184             : {
     185           0 :   return GetFile(dir, NS_LITERAL_CSTRING("update.status"), result);
     186             : }
     187             : 
     188             : /**
     189             :  * Get the contents of the update.status file.
     190             :  *
     191             :  * @param statusFile the status file object.
     192             :  * @param buf        the buffer holding the file contents
     193             :  *
     194             :  * @return true if successful, false otherwise.
     195             :  */
     196             : template <size_t Size>
     197             : static bool
     198           0 : GetStatusFileContents(nsIFile *statusFile, char (&buf)[Size])
     199             : {
     200             :   static_assert(Size > 16, "Buffer needs to be large enough to hold the known status codes");
     201             : 
     202           0 :   PRFileDesc *fd = nullptr;
     203           0 :   nsresult rv = statusFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd);
     204           0 :   if (NS_FAILED(rv))
     205             :     return false;
     206             : 
     207           0 :   const int32_t n = PR_Read(fd, buf, Size);
     208           0 :   PR_Close(fd);
     209             : 
     210           0 :   return (n >= 0);
     211             : }
     212             : 
     213             : typedef enum {
     214             :   eNoUpdateAction,
     215             :   ePendingUpdate,
     216             :   ePendingService,
     217             :   ePendingElevate,
     218             :   eAppliedUpdate,
     219             :   eAppliedService,
     220             : } UpdateStatus;
     221             : 
     222             : /**
     223             :  * Returns a value indicating what needs to be done in order to handle an update.
     224             :  *
     225             :  * @param dir the directory in which we should look for an update.status file.
     226             :  * @param statusFile the update.status file found in the directory.
     227             :  *
     228             :  * @return the update action to be performed.
     229             :  */
     230             : static UpdateStatus
     231           0 : GetUpdateStatus(nsIFile* dir, nsCOMPtr<nsIFile> &statusFile)
     232             : {
     233           0 :   if (GetStatusFile(dir, statusFile)) {
     234             :     char buf[32];
     235           0 :     if (GetStatusFileContents(statusFile, buf)) {
     236           0 :       const char kPending[] = "pending";
     237           0 :       const char kPendingService[] = "pending-service";
     238           0 :       const char kPendingElevate[] = "pending-elevate";
     239           0 :       const char kApplied[] = "applied";
     240           0 :       const char kAppliedService[] = "applied-service";
     241           0 :       if (!strncmp(buf, kPendingElevate, sizeof(kPendingElevate) - 1)) {
     242           0 :         return ePendingElevate;
     243             :       }
     244           0 :       if (!strncmp(buf, kPendingService, sizeof(kPendingService) - 1)) {
     245             :         return ePendingService;
     246             :       }
     247           0 :       if (!strncmp(buf, kPending, sizeof(kPending) - 1)) {
     248             :         return ePendingUpdate;
     249             :       }
     250           0 :       if (!strncmp(buf, kAppliedService, sizeof(kAppliedService) - 1)) {
     251             :         return eAppliedService;
     252             :       }
     253           0 :       if (!strncmp(buf, kApplied, sizeof(kApplied) - 1)) {
     254             :         return eAppliedUpdate;
     255             :       }
     256             :     }
     257             :   }
     258             :   return eNoUpdateAction;
     259             : }
     260             : 
     261             : static bool
     262           0 : GetVersionFile(nsIFile *dir, nsCOMPtr<nsIFile> &result)
     263             : {
     264           0 :   return GetFile(dir, NS_LITERAL_CSTRING("update.version"), result);
     265             : }
     266             : 
     267             : // Compares the current application version with the update's application
     268             : // version.
     269             : static bool
     270           0 : IsOlderVersion(nsIFile *versionFile, const char *appVersion)
     271             : {
     272           0 :   PRFileDesc *fd = nullptr;
     273           0 :   nsresult rv = versionFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd);
     274           0 :   if (NS_FAILED(rv))
     275             :     return true;
     276             : 
     277             :   char buf[32];
     278           0 :   const int32_t n = PR_Read(fd, buf, sizeof(buf));
     279           0 :   PR_Close(fd);
     280             : 
     281           0 :   if (n < 0)
     282             :     return false;
     283             : 
     284             :   // Trim off the trailing newline
     285           0 :   if (buf[n - 1] == '\n')
     286           0 :     buf[n - 1] = '\0';
     287             : 
     288             :   // If the update xml doesn't provide the application version the file will
     289             :   // contain the string "null" and it is assumed that the update is not older.
     290           0 :   const char kNull[] = "null";
     291           0 :   if (strncmp(buf, kNull, sizeof(kNull) - 1) == 0)
     292             :     return false;
     293             : 
     294           0 :   if (mozilla::Version(appVersion) > buf)
     295             :     return true;
     296             : 
     297           0 :   return false;
     298             : }
     299             : 
     300             : static bool
     301           0 : CopyFileIntoUpdateDir(nsIFile *parentDir, const nsACString& leaf, nsIFile *updateDir)
     302             : {
     303           0 :   nsCOMPtr<nsIFile> file;
     304             : 
     305             :   // Make sure there is not an existing file in the target location.
     306           0 :   nsresult rv = updateDir->Clone(getter_AddRefs(file));
     307           0 :   if (NS_FAILED(rv))
     308             :     return false;
     309           0 :   rv = file->AppendNative(leaf);
     310           0 :   if (NS_FAILED(rv))
     311             :     return false;
     312           0 :   file->Remove(true);
     313             : 
     314             :   // Now, copy into the target location.
     315           0 :   rv = parentDir->Clone(getter_AddRefs(file));
     316           0 :   if (NS_FAILED(rv))
     317             :     return false;
     318           0 :   rv = file->AppendNative(leaf);
     319           0 :   if (NS_FAILED(rv))
     320             :     return false;
     321           0 :   rv = file->CopyToNative(updateDir, EmptyCString());
     322           0 :   if (NS_FAILED(rv))
     323             :     return false;
     324             : 
     325           0 :   return true;
     326             : }
     327             : 
     328             : static bool
     329           0 : CopyUpdaterIntoUpdateDir(nsIFile *greDir, nsIFile *appDir, nsIFile *updateDir,
     330             :                          nsCOMPtr<nsIFile> &updater)
     331             : {
     332             :   // Copy the updater application from the GRE and the updater ini from the app.
     333             : #if defined(XP_MACOSX)
     334             :   if (!CopyFileIntoUpdateDir(appDir, NS_LITERAL_CSTRING(UPDATER_APP), updateDir))
     335             :     return false;
     336             :   CopyFileIntoUpdateDir(greDir, NS_LITERAL_CSTRING(UPDATER_INI), updateDir);
     337             : #else
     338           0 :   if (!CopyFileIntoUpdateDir(greDir, NS_LITERAL_CSTRING(UPDATER_BIN), updateDir))
     339             :     return false;
     340           0 :   CopyFileIntoUpdateDir(appDir, NS_LITERAL_CSTRING(UPDATER_INI), updateDir);
     341             : #endif
     342             : #if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(ANDROID)
     343           0 :   nsCOMPtr<nsIFile> iconDir;
     344           0 :   appDir->Clone(getter_AddRefs(iconDir));
     345           0 :   iconDir->AppendNative(NS_LITERAL_CSTRING("icons"));
     346           0 :   if (!CopyFileIntoUpdateDir(iconDir, NS_LITERAL_CSTRING(UPDATER_PNG), updateDir))
     347             :     return false;
     348             : #endif
     349             :   // Finally, return the location of the updater binary.
     350           0 :   nsresult rv = updateDir->Clone(getter_AddRefs(updater));
     351           0 :   if (NS_FAILED(rv))
     352             :     return false;
     353             : #if defined(XP_MACOSX)
     354             :   rv  = updater->AppendNative(NS_LITERAL_CSTRING(UPDATER_APP));
     355             :   nsresult tmp = updater->AppendNative(NS_LITERAL_CSTRING("Contents"));
     356             :   if (NS_FAILED(tmp)) {
     357             :     rv = tmp;
     358             :   }
     359             :   tmp = updater->AppendNative(NS_LITERAL_CSTRING("MacOS"));
     360             :   if (NS_FAILED(tmp) || NS_FAILED(rv))
     361             :     return false;
     362             : #endif
     363           0 :   rv = updater->AppendNative(NS_LITERAL_CSTRING(UPDATER_BIN));
     364           0 :   return NS_SUCCEEDED(rv);
     365             : }
     366             : 
     367             : /**
     368             :  * Appends the specified path to the library path.
     369             :  * This is used so that updater can find libmozsqlite3.so and other shared libs.
     370             :  *
     371             :  * @param pathToAppend A new library path to prepend to LD_LIBRARY_PATH
     372             :  */
     373             : #if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && !defined(XP_MACOSX)
     374             : #include "prprf.h"
     375             : #define PATH_SEPARATOR ":"
     376             : #define LD_LIBRARY_PATH_ENVVAR_NAME "LD_LIBRARY_PATH"
     377             : static void
     378           0 : AppendToLibPath(const char *pathToAppend)
     379             : {
     380           0 :   char *pathValue = getenv(LD_LIBRARY_PATH_ENVVAR_NAME);
     381           0 :   if (nullptr == pathValue || '\0' == *pathValue) {
     382             :     // Leak the string because that is required by PR_SetEnv.
     383           0 :     char *s = Smprintf("%s=%s", LD_LIBRARY_PATH_ENVVAR_NAME, pathToAppend).release();
     384           0 :     PR_SetEnv(s);
     385           0 :   } else if (!strstr(pathValue, pathToAppend)) {
     386             :     // Leak the string because that is required by PR_SetEnv.
     387           0 :     char *s = Smprintf("%s=%s" PATH_SEPARATOR "%s",
     388           0 :                        LD_LIBRARY_PATH_ENVVAR_NAME, pathToAppend, pathValue).release();
     389           0 :     PR_SetEnv(s);
     390             :   }
     391           0 : }
     392             : #endif
     393             : 
     394             : /**
     395             :  * Applies, switches, or stages an update.
     396             :  *
     397             :  * @param greDir       the GRE directory
     398             :  * @param updateDir    the update root directory
     399             :  * @param appDir       the application directory
     400             :  * @param appArgc      the number of args passed to the application
     401             :  * @param appArgv      the args passed to the application
     402             :  *                     (used for restarting the application when necessary)
     403             :  * @param restart      true when a restart is necessary.
     404             :  * @param isStaged     true when the update has already been staged
     405             :  * @param outpid (out) parameter holding the handle to the updater application
     406             :  *                     when staging updates
     407             :  */
     408             : static void
     409           0 : ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *appDir, int appArgc,
     410             :             char **appArgv, bool restart, bool isStaged, ProcessType *outpid)
     411             : {
     412             :   // The following determines the update operation to perform.
     413             :   // 1. When restart is false the update will be staged.
     414             :   // 2. When restart is true and isStaged is false the update will apply the mar
     415             :   //    file to the installation directory.
     416             :   // 3. When restart is true and isStaged is true the update will switch the
     417             :   //    staged update with the installation directory.
     418             : 
     419             :   nsresult rv;
     420             : 
     421           0 :   nsCOMPtr<nsIFile> updater;
     422           0 :   nsAutoCString updaterPath;
     423           0 :   nsAutoCString updateDirPath;
     424             : #if defined(XP_WIN)
     425             :   // Get an nsIFile reference for the updater in the installation dir.
     426             :   if (!GetFile(greDir, NS_LITERAL_CSTRING(UPDATER_BIN), updater)) {
     427             :     return;
     428             :   }
     429             : 
     430             :   // Get the path to the updater.
     431             :   nsAutoString updaterPathW;
     432             :   rv = updater->GetPath(updaterPathW);
     433             :   if (NS_FAILED(rv)) {
     434             :     return;
     435             :   }
     436             :   updaterPath = NS_ConvertUTF16toUTF8(updaterPathW);
     437             : 
     438             :   // Get the path to the update dir.
     439             :   nsAutoString updateDirPathW;
     440             :   rv = updateDir->GetPath(updateDirPathW);
     441             :   if (NS_FAILED(rv)) {
     442             :     return;
     443             :   }
     444             :   updateDirPath = NS_ConvertUTF16toUTF8(updateDirPathW);
     445             : #else
     446           0 :   if (isStaged) {
     447           0 :     nsCOMPtr<nsIFile> mozUpdaterDir;
     448           0 :     rv = updateDir->Clone(getter_AddRefs(mozUpdaterDir));
     449           0 :     if (NS_FAILED(rv)) {
     450           0 :       LOG(("failed cloning update dir\n"));
     451           0 :       return;
     452             :     }
     453             : 
     454             :     // Create a new directory named MozUpdater in the updates/0 directory to copy
     455             :     // the updater files to that will be used to replace the installation with the
     456             :     // staged application that has been updated. Note that we don't check for
     457             :     // directory creation errors since the call to CopyUpdaterIntoUpdateDir will
     458             :     // fail if the creation of the directory fails. A unique directory is created
     459             :     // in MozUpdater in case a previous attempt locked the directory or files.
     460           0 :     mozUpdaterDir->Append(NS_LITERAL_STRING("MozUpdater"));
     461           0 :     mozUpdaterDir->Append(NS_LITERAL_STRING("bgupdate"));
     462           0 :     rv = mozUpdaterDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0755);
     463           0 :     if (NS_FAILED(rv)) {
     464           0 :       LOG(("failed creating unique dir\n"));
     465             :       return;
     466             :     }
     467             : 
     468             :     // Copy the updater and files needed to update into the MozUpdater/bgupdate
     469             :     // directory in the update dir and get an nsIFile reference to the copied
     470             :     // updater.
     471           0 :     if (!CopyUpdaterIntoUpdateDir(greDir, appDir, mozUpdaterDir, updater)) {
     472           0 :       LOG(("failed copying updater\n"));
     473             :       return;
     474             :     }
     475             :   } else {
     476             :     // Copy the updater and files needed to update into the update directory and
     477             :     // get an nsIFile reference to the copied updater.
     478           0 :     if (!CopyUpdaterIntoUpdateDir(greDir, appDir, updateDir, updater)) {
     479           0 :       LOG(("failed copying updater\n"));
     480             :       return;
     481             :     }
     482             :   }
     483             : 
     484             :   // Get the path to the updater that will be used.
     485           0 :   rv = updater->GetNativePath(updaterPath);
     486           0 :   if (NS_FAILED(rv)) {
     487             :     return;
     488             :   }
     489             : 
     490             :   // Get the path to the update dir.
     491           0 :   rv = updateDir->GetNativePath(updateDirPath);
     492           0 :   if (NS_FAILED(rv)) {
     493             :      return;
     494             :   }
     495             : #endif
     496             : 
     497             :   // appFilePath and workingDirPath are only used when the application will be
     498             :   // restarted.
     499           0 :   nsAutoCString appFilePath;
     500             :   char workingDirPath[MAXPATHLEN];
     501           0 :   if (restart) {
     502             :     // Get the path to the current working directory.
     503           0 :     rv = GetCurrentWorkingDir(workingDirPath, sizeof(workingDirPath));
     504           0 :     if (NS_FAILED(rv)) {
     505           0 :       return;
     506             :     }
     507             : 
     508             :     // Get the application file path used by the updater to restart the
     509             :     // application after the update has finished.
     510           0 :     nsCOMPtr<nsIFile> appFile;
     511           0 :     XRE_GetBinaryPath(getter_AddRefs(appFile));
     512           0 :     if (!appFile) {
     513           0 :       return;
     514             :     }
     515             : 
     516             : #if defined(XP_WIN)
     517             :     nsAutoString appFilePathW;
     518             :     rv = appFile->GetPath(appFilePathW);
     519             :     if (NS_FAILED(rv)) {
     520             :       return;
     521             :     }
     522             :     appFilePath = NS_ConvertUTF16toUTF8(appFilePathW);
     523             : #else
     524           0 :     rv = appFile->GetNativePath(appFilePath);
     525           0 :     if (NS_FAILED(rv)) {
     526             :       return;
     527             :     }
     528             : #endif
     529             :   }
     530             : 
     531             :   // Get the installation directory path.
     532           0 :   nsAutoCString installDirPath;
     533           0 :   rv = GetInstallDirPath(appDir, installDirPath);
     534           0 :   if (NS_FAILED(rv)) {
     535             :     return;
     536             :   }
     537             : 
     538           0 :   nsAutoCString applyToDirPath;
     539           0 :   nsCOMPtr<nsIFile> updatedDir;
     540           0 :   if (restart && !isStaged) {
     541             :     // The install directory is the same as the apply to directory.
     542           0 :     applyToDirPath.Assign(installDirPath);
     543             :   } else {
     544             :     // Get the directory where the update is staged or will be staged.
     545             : #if defined(XP_MACOSX)
     546             :     if (!GetFile(updateDir, NS_LITERAL_CSTRING("Updated.app"), updatedDir)) {
     547             : #else
     548           0 :     if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) {
     549             : #endif
     550           0 :       return;
     551             :     }
     552             : #if defined(XP_WIN)
     553             :     nsAutoString applyToDirPathW;
     554             :     rv = updatedDir->GetPath(applyToDirPathW);
     555             :     if (NS_FAILED(rv)) {
     556             :       return;
     557             :     }
     558             :     applyToDirPath = NS_ConvertUTF16toUTF8(applyToDirPathW);
     559             : #else
     560           0 :     rv = updatedDir->GetNativePath(applyToDirPath);
     561             : #endif
     562             :   }
     563           0 :   if (NS_FAILED(rv)) {
     564             :      return;
     565             :   }
     566             : 
     567           0 :   if (restart && isStaged) {
     568             :     // When the update should already be staged make sure that the updated
     569             :     // directory exists.
     570           0 :     bool updatedDirExists = false;
     571           0 :     if (NS_FAILED(updatedDir->Exists(&updatedDirExists)) || !updatedDirExists) {
     572           0 :       return;
     573             :     }
     574             :   }
     575             : 
     576             :   // On platforms where we are not calling execv, we may need to make the
     577             :   // updater executable wait for the calling process to exit.  Otherwise, the
     578             :   // updater may have trouble modifying our executable image (because it might
     579             :   // still be in use).  This is accomplished by passing our PID to the updater so
     580             :   // that it can wait for us to exit.  This is not perfect as there is a race
     581             :   // condition that could bite us.  It's possible that the calling process could
     582             :   // exit before the updater waits on the specified PID, and in the meantime a
     583             :   // new process with the same PID could be created.  This situation is unlikely,
     584             :   // however, given the way most operating systems recycle PIDs.  We'll take our
     585             :   // chances ;-)
     586             :   // Construct the PID argument for this process to pass to the updater.
     587           0 :   nsAutoCString pid;
     588           0 :   if (restart) {
     589             : #if defined(XP_UNIX) & !defined(XP_MACOSX)
     590             :     // When execv is used for an update that requires a restart 0 is passed
     591             :     // which is ignored by the updater.
     592           0 :     pid.AssignASCII("0");
     593             : #else
     594             :     pid.AppendInt((int32_t) getpid());
     595             : #endif
     596           0 :     if (isStaged) {
     597             :       // Append a special token to the PID in order to inform the updater that
     598             :       // it should replace install with the updated directory.
     599           0 :       pid.AppendLiteral("/replace");
     600             :     }
     601             :   } else {
     602             :     // Signal the updater application that it should stage the update.
     603           0 :     pid.AssignASCII("-1");
     604             :   }
     605             : 
     606           0 :   int argc = 5;
     607           0 :   if (restart) {
     608           0 :     argc = appArgc + 6;
     609           0 :     if (gRestartedByOS) {
     610           0 :       argc += 1;
     611             :     }
     612             :   }
     613           0 :   char **argv = new char*[argc + 1];
     614           0 :   if (!argv) {
     615             :     return;
     616             :   }
     617           0 :   argv[0] = (char*) updaterPath.get();
     618           0 :   argv[1] = (char*) updateDirPath.get();
     619           0 :   argv[2] = (char*) installDirPath.get();
     620           0 :   argv[3] = (char*) applyToDirPath.get();
     621           0 :   argv[4] = (char*) pid.get();
     622           0 :   if (restart && appArgc) {
     623           0 :     argv[5] = workingDirPath;
     624           0 :     argv[6] = (char*) appFilePath.get();
     625           0 :     for (int i = 1; i < appArgc; ++i) {
     626           0 :       argv[6 + i] = appArgv[i];
     627             :     }
     628           0 :     if (gRestartedByOS) {
     629             :       // We haven't truly started up, restore this argument so that we will have
     630             :       // it upon restart.
     631           0 :       argv[6 + appArgc] = const_cast<char*>("-os-restarted");
     632             :     }
     633             :   }
     634           0 :   argv[argc] = nullptr;
     635             : 
     636           0 :   if (restart && gSafeMode) {
     637           0 :     PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
     638             :   }
     639             : 
     640             : #if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && !defined(XP_MACOSX)
     641           0 :   AppendToLibPath(installDirPath.get());
     642             : #endif
     643             : 
     644           0 :   LOG(("spawning updater process [%s]\n", updaterPath.get()));
     645             : 
     646             : #if defined(XP_UNIX) && !defined(XP_MACOSX)
     647             :   // We use execv to spawn the updater process on all UNIX systems except Mac OSX
     648             :   // since it is known to cause problems on the Mac.  Windows has execv, but it
     649             :   // is a faked implementation that doesn't really replace the current process.
     650             :   // Instead it spawns a new process, so we gain nothing from using execv on
     651             :   // Windows.
     652           0 :   if (restart) {
     653           0 :     exit(execv(updaterPath.get(), argv));
     654             :   }
     655           0 :   *outpid = fork();
     656           0 :   if (*outpid == -1) {
     657             :     return;
     658           0 :   } else if (*outpid == 0) {
     659           0 :     exit(execv(updaterPath.get(), argv));
     660             :   }
     661             : #elif defined(XP_WIN)
     662             :   if (isStaged) {
     663             :     // Launch the updater to replace the installation with the staged updated.
     664             :     if (!WinLaunchChild(updaterPathW.get(), argc, argv)) {
     665             :       return;
     666             :     }
     667             :   } else {
     668             :     // Launch the updater to either stage or apply an update.
     669             :     if (!WinLaunchChild(updaterPathW.get(), argc, argv, nullptr, outpid)) {
     670             :       return;
     671             :     }
     672             :   }
     673             : #elif defined(XP_MACOSX)
     674             :   UpdateDriverSetupMacCommandLine(argc, argv, restart);
     675             :   // We need to detect whether elevation is required for this update. This can
     676             :   // occur when an admin user installs the application, but another admin
     677             :   // user attempts to update (see bug 394984).
     678             :   if (restart && !IsRecursivelyWritable(installDirPath.get())) {
     679             :     if (!LaunchElevatedUpdate(argc, argv, outpid)) {
     680             :       LOG(("Failed to launch elevated update!"));
     681             :       exit(1);
     682             :     }
     683             :     exit(0);
     684             :   }
     685             : 
     686             :   if (isStaged) {
     687             :     // Launch the updater to replace the installation with the staged updated.
     688             :     LaunchChildMac(argc, argv);
     689             :   } else {
     690             :     // Launch the updater to either stage or apply an update.
     691             :     LaunchChildMac(argc, argv, outpid);
     692             :   }
     693             :   if (restart) {
     694             :     exit(0);
     695             :   }
     696             : #else
     697             :   if (isStaged) {
     698             :     // Launch the updater to replace the installation with the staged updated.
     699             :     PR_CreateProcessDetached(updaterPath.get(), argv, nullptr, nullptr);
     700             :   } else {
     701             :     // Launch the updater to either stage or apply an update.
     702             :     *outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr);
     703             :   }
     704             : #endif
     705             : #if !defined(USE_EXECV)
     706             :   if (restart) {
     707             :     exit(0);
     708             :   }
     709             : #endif
     710             : }
     711             : 
     712             : /**
     713             :  * Wait briefly to see if a process terminates, then return true if it has.
     714             :  */
     715             : static bool
     716           0 : ProcessHasTerminated(ProcessType pt)
     717             : {
     718             : #if defined(XP_WIN)
     719             :   if (WaitForSingleObject(pt, 1000)) {
     720             :     return false;
     721             :   }
     722             :   CloseHandle(pt);
     723             :   return true;
     724             : #elif defined(XP_MACOSX)
     725             :   // We're waiting for the process to terminate in LaunchChildMac.
     726             :   return true;
     727             : #elif defined(XP_UNIX)
     728             :   int exitStatus;
     729           0 :   pid_t exited = waitpid(pt, &exitStatus, WNOHANG);
     730           0 :   if (exited == 0) {
     731             :     // Process is still running.
     732           0 :     sleep(1);
     733           0 :     return false;
     734             :   }
     735           0 :   if (exited == -1) {
     736           0 :     LOG(("Error while checking if the updater process is finished"));
     737             :     // This shouldn't happen, but if it does, the updater process is lost to us,
     738             :     // so the best we can do is pretend that it's exited.
     739             :     return true;
     740             :   }
     741             :   // If we get here, the process has exited; make sure it exited normally.
     742           0 :   if (WIFEXITED(exitStatus) && (WEXITSTATUS(exitStatus) != 0)) {
     743           0 :     LOG(("Error while running the updater process, check update.log"));
     744             :   }
     745             :   return true;
     746             : #else
     747             :   // No way to have a non-blocking implementation on these platforms,
     748             :   // because we're using NSPR and it only provides a blocking wait.
     749             :   int32_t exitCode;
     750             :   PR_WaitProcess(pt, &exitCode);
     751             :   if (exitCode != 0) {
     752             :     LOG(("Error while running the updater process, check update.log"));
     753             :   }
     754             :   return true;
     755             : #endif
     756             : }
     757             : 
     758             : nsresult
     759           0 : ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir,
     760             :                int argc, char **argv, const char *appVersion,
     761             :                bool restart, ProcessType *pid)
     762             : {
     763             :   nsresult rv;
     764             : 
     765           0 :   nsCOMPtr<nsIFile> updatesDir;
     766           0 :   rv = updRootDir->Clone(getter_AddRefs(updatesDir));
     767           1 :   if (NS_FAILED(rv)) {
     768             :     return rv;
     769             :   }
     770             : 
     771           0 :   rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("updates"));
     772           1 :   if (NS_FAILED(rv)) {
     773             :     return rv;
     774             :   }
     775             : 
     776           3 :   rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("0"));
     777           1 :   if (NS_FAILED(rv)) {
     778             :     return rv;
     779             :   }
     780             : 
     781             :   // Return early since there isn't a valid update when the update application
     782             :   // version file doesn't exist or if the update's application version is less
     783             :   // than the current application version. The cleanup of the update will happen
     784             :   // during post update processing in nsUpdateService.js.
     785           0 :   nsCOMPtr<nsIFile> versionFile;
     786           0 :   if (!GetVersionFile(updatesDir, versionFile) ||
     787           0 :       IsOlderVersion(versionFile, appVersion)) {
     788             :     return NS_OK;
     789             :   }
     790             : 
     791           0 :   nsCOMPtr<nsIFile> statusFile;
     792           0 :   UpdateStatus status = GetUpdateStatus(updatesDir, statusFile);
     793           0 :   switch (status) {
     794             :   case ePendingElevate: {
     795           0 :     if (NS_IsMainThread()) {
     796             :       // Only do this if we're called from the main thread.
     797             :       nsCOMPtr<nsIUpdatePrompt> up =
     798           0 :         do_GetService("@mozilla.org/updates/update-prompt;1");
     799           0 :       if (up) {
     800           0 :         up->ShowUpdateElevationRequired();
     801             :       }
     802             :       break;
     803             :     }
     804             :     // Intentional fallthrough to ePendingUpdate and ePendingService.
     805             :     MOZ_FALLTHROUGH;
     806             :   }
     807             :   case ePendingUpdate:
     808             :   case ePendingService: {
     809           0 :     ApplyUpdate(greDir, updatesDir, appDir, argc, argv, restart, false, pid);
     810           0 :     break;
     811             :   }
     812             :   case eAppliedUpdate:
     813             :   case eAppliedService:
     814             :     // An update was staged and needs to be switched so the updated application
     815             :     // is used.
     816           0 :     ApplyUpdate(greDir, updatesDir, appDir, argc, argv, restart, true, pid);
     817           0 :     break;
     818             :   case eNoUpdateAction:
     819             :     // We don't need to do any special processing here, we'll just continue to
     820             :     // startup the application.
     821             :     break;
     822             :   }
     823             : 
     824             :   return NS_OK;
     825             : }
     826             : 
     827             : 
     828             : 
     829           0 : NS_IMPL_ISUPPORTS(nsUpdateProcessor, nsIUpdateProcessor)
     830             : 
     831           0 : nsUpdateProcessor::nsUpdateProcessor()
     832           0 :   : mUpdaterPID(0)
     833             : {
     834           0 : }
     835             : 
     836           0 : nsUpdateProcessor::~nsUpdateProcessor()
     837             : {
     838           0 : }
     839             : 
     840             : NS_IMETHODIMP
     841           0 : nsUpdateProcessor::ProcessUpdate(nsIUpdate* aUpdate)
     842             : {
     843             :   nsresult rv;
     844             : 
     845             :   nsCOMPtr<nsIProperties> ds =
     846           0 :     do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
     847           0 :   NS_ENSURE_SUCCESS(rv, rv);
     848             : 
     849           0 :   nsCOMPtr<nsIFile> exeFile;
     850           0 :   rv = ds->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile),
     851           0 :                getter_AddRefs(exeFile));
     852           0 :   NS_ENSURE_SUCCESS(rv, rv);
     853             : 
     854           0 :   nsCOMPtr<nsIFile> appDir;
     855           0 :   rv = exeFile->GetParent(getter_AddRefs(appDir));
     856           0 :   NS_ENSURE_SUCCESS(rv, rv);
     857             : 
     858           0 :   nsCOMPtr<nsIFile> greDir;
     859           0 :   rv = ds->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir));
     860           0 :   NS_ENSURE_SUCCESS(rv, rv);
     861             : 
     862           0 :   nsCOMPtr<nsIFile> updRoot;
     863           0 :   rv = ds->Get(XRE_UPDATE_ROOT_DIR, NS_GET_IID(nsIFile),
     864           0 :                getter_AddRefs(updRoot));
     865           0 :   NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the UpdRootD dir");
     866             : 
     867             :   // XRE_UPDATE_ROOT_DIR should not fail but if it does fallback to the
     868             :   // application directory just to be safe.
     869           0 :   if (NS_FAILED(rv)) {
     870           0 :     rv = appDir->Clone(getter_AddRefs(updRoot));
     871           0 :     NS_ENSURE_SUCCESS(rv, rv);
     872             :   }
     873             : 
     874             :   nsCOMPtr<nsIXULAppInfo> appInfo =
     875           0 :     do_GetService("@mozilla.org/xre/app-info;1", &rv);
     876           0 :   NS_ENSURE_SUCCESS(rv, rv);
     877             : 
     878           0 :   nsAutoCString appVersion;
     879           0 :   rv = appInfo->GetVersion(appVersion);
     880           0 :   NS_ENSURE_SUCCESS(rv, rv);
     881             : 
     882             :   // Copy the parameters to the StagedUpdateInfo structure shared with the
     883             :   // watcher thread.
     884           0 :   mInfo.mGREDir = greDir;
     885           0 :   mInfo.mAppDir = appDir;
     886           0 :   mInfo.mUpdateRoot = updRoot;
     887           0 :   mInfo.mArgc = 0;
     888           0 :   mInfo.mArgv = nullptr;
     889           0 :   mInfo.mAppVersion = appVersion;
     890             : 
     891           0 :   MOZ_ASSERT(NS_IsMainThread(), "not main thread");
     892             :   nsCOMPtr<nsIRunnable> r =
     893           0 :     NewRunnableMethod("nsUpdateProcessor::StartStagedUpdate",
     894             :                       this,
     895           0 :                       &nsUpdateProcessor::StartStagedUpdate);
     896           0 :   return NS_NewNamedThread("Update Watcher", getter_AddRefs(mProcessWatcher),
     897           0 :                            r);
     898             : }
     899             : 
     900             : 
     901             : 
     902             : void
     903           0 : nsUpdateProcessor::StartStagedUpdate()
     904             : {
     905           0 :   MOZ_ASSERT(!NS_IsMainThread(), "main thread");
     906             : 
     907           0 :   nsresult rv = ProcessUpdates(mInfo.mGREDir,
     908             :                                mInfo.mAppDir,
     909             :                                mInfo.mUpdateRoot,
     910             :                                mInfo.mArgc,
     911             :                                mInfo.mArgv,
     912             :                                mInfo.mAppVersion.get(),
     913             :                                false,
     914           0 :                                &mUpdaterPID);
     915           0 :   NS_ENSURE_SUCCESS_VOID(rv);
     916             : 
     917           0 :   if (mUpdaterPID) {
     918             :     // Track the state of the updater process while it is staging an update.
     919           0 :     rv = NS_DispatchToCurrentThread(
     920           0 :       NewRunnableMethod("nsUpdateProcessor::WaitForProcess",
     921             :                         this,
     922           0 :                         &nsUpdateProcessor::WaitForProcess));
     923           0 :     NS_ENSURE_SUCCESS_VOID(rv);
     924             :   } else {
     925             :     // Failed to launch the updater process for some reason.
     926             :     // We need to shutdown the current thread as there isn't anything more for
     927             :     // us to do...
     928           0 :     rv = NS_DispatchToMainThread(
     929           0 :       NewRunnableMethod("nsUpdateProcessor::ShutdownWatcherThread",
     930             :                         this,
     931           0 :                         &nsUpdateProcessor::ShutdownWatcherThread));
     932           0 :     NS_ENSURE_SUCCESS_VOID(rv);
     933             :   }
     934             : }
     935             : 
     936             : void
     937           0 : nsUpdateProcessor::ShutdownWatcherThread()
     938             : {
     939           0 :   MOZ_ASSERT(NS_IsMainThread(), "not main thread");
     940           0 :   mProcessWatcher->Shutdown();
     941           0 :   mProcessWatcher = nullptr;
     942           0 : }
     943             : 
     944             : void
     945           0 : nsUpdateProcessor::WaitForProcess()
     946             : {
     947           0 :   MOZ_ASSERT(!NS_IsMainThread(), "main thread");
     948           0 :   if (ProcessHasTerminated(mUpdaterPID)) {
     949           0 :     NS_DispatchToMainThread(NewRunnableMethod(
     950           0 :       "nsUpdateProcessor::UpdateDone", this, &nsUpdateProcessor::UpdateDone));
     951             :   } else {
     952           0 :     NS_DispatchToCurrentThread(
     953           0 :       NewRunnableMethod("nsUpdateProcessor::WaitForProcess",
     954             :                         this,
     955           0 :                         &nsUpdateProcessor::WaitForProcess));
     956             :   }
     957           0 : }
     958             : 
     959             : void
     960           0 : nsUpdateProcessor::UpdateDone()
     961             : {
     962           0 :   MOZ_ASSERT(NS_IsMainThread(), "not main thread");
     963             : 
     964             :   nsCOMPtr<nsIUpdateManager> um =
     965           0 :     do_GetService("@mozilla.org/updates/update-manager;1");
     966             :   if (um) {
     967             :     um->RefreshUpdateStatus();
     968             :   }
     969             : 
     970             :   ShutdownWatcherThread();
     971             : }

Generated by: LCOV version 1.13-14-ga5dd952