toolkit/xre/nsUpdateDriver.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/xre/nsUpdateDriver.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1302 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include <stdlib.h>
    1.11 +#include <stdio.h>
    1.12 +#include "nsUpdateDriver.h"
    1.13 +#include "nsXULAppAPI.h"
    1.14 +#include "nsAppRunner.h"
    1.15 +#include "nsIWritablePropertyBag.h"
    1.16 +#include "nsIFile.h"
    1.17 +#include "nsIVariant.h"
    1.18 +#include "nsCOMPtr.h"
    1.19 +#include "nsString.h"
    1.20 +#include "prproces.h"
    1.21 +#include "prlog.h"
    1.22 +#include "prenv.h"
    1.23 +#include "nsVersionComparator.h"
    1.24 +#include "nsXREDirProvider.h"
    1.25 +#include "SpecialSystemDirectory.h"
    1.26 +#include "nsDirectoryServiceDefs.h"
    1.27 +#include "nsThreadUtils.h"
    1.28 +#include "nsIXULAppInfo.h"
    1.29 +#include "mozilla/Preferences.h"
    1.30 +#include "nsPrintfCString.h"
    1.31 +#include "mozilla/DebugOnly.h"
    1.32 +
    1.33 +#ifdef XP_MACOSX
    1.34 +#include "nsILocalFileMac.h"
    1.35 +#include "nsCommandLineServiceMac.h"
    1.36 +#include "MacLaunchHelper.h"
    1.37 +#endif
    1.38 +
    1.39 +#if defined(XP_WIN)
    1.40 +# include <direct.h>
    1.41 +# include <process.h>
    1.42 +# include <windows.h>
    1.43 +# include <shlwapi.h>
    1.44 +# include "nsWindowsHelpers.h"
    1.45 +# include "prprf.h"
    1.46 +# define getcwd(path, size) _getcwd(path, size)
    1.47 +# define getpid() GetCurrentProcessId()
    1.48 +#elif defined(XP_UNIX)
    1.49 +# include <unistd.h>
    1.50 +#endif
    1.51 +
    1.52 +using namespace mozilla;
    1.53 +
    1.54 +//
    1.55 +// We use execv to spawn the updater process on all UNIX systems except Mac OSX
    1.56 +// since it is known to cause problems on the Mac.  Windows has execv, but it
    1.57 +// is a faked implementation that doesn't really replace the current process.
    1.58 +// Instead it spawns a new process, so we gain nothing from using execv on
    1.59 +// Windows.
    1.60 +//
    1.61 +// On platforms where we are not calling execv, we may need to make the
    1.62 +// updater executable wait for the calling process to exit.  Otherwise, the
    1.63 +// updater may have trouble modifying our executable image (because it might
    1.64 +// still be in use).  This is accomplished by passing our PID to the updater so
    1.65 +// that it can wait for us to exit.  This is not perfect as there is a race
    1.66 +// condition that could bite us.  It's possible that the calling process could
    1.67 +// exit before the updater waits on the specified PID, and in the meantime a
    1.68 +// new process with the same PID could be created.  This situation is unlikely,
    1.69 +// however, given the way most operating systems recycle PIDs.  We'll take our
    1.70 +// chances ;-)
    1.71 +//
    1.72 +// A similar #define lives in updater.cpp and should be kept in sync with this.
    1.73 +//
    1.74 +#if defined(XP_UNIX) && !defined(XP_MACOSX)
    1.75 +#define USE_EXECV
    1.76 +#endif
    1.77 +
    1.78 +#ifdef PR_LOGGING
    1.79 +static PRLogModuleInfo *
    1.80 +GetUpdateLog()
    1.81 +{
    1.82 +  static PRLogModuleInfo *sUpdateLog;
    1.83 +  if (!sUpdateLog)
    1.84 +    sUpdateLog = PR_NewLogModule("updatedriver");
    1.85 +  return sUpdateLog;
    1.86 +}
    1.87 +#endif
    1.88 +#define LOG(args) PR_LOG(GetUpdateLog(), PR_LOG_DEBUG, args)
    1.89 +
    1.90 +#ifdef XP_WIN
    1.91 +static const char kUpdaterBin[] = "updater.exe";
    1.92 +#else
    1.93 +static const char kUpdaterBin[] = "updater";
    1.94 +#endif
    1.95 +static const char kUpdaterINI[] = "updater.ini";
    1.96 +#ifdef XP_MACOSX
    1.97 +static const char kUpdaterApp[] = "updater.app";
    1.98 +#endif
    1.99 +#if defined(XP_UNIX) && !defined(XP_MACOSX)
   1.100 +static const char kUpdaterPNG[] = "updater.png";
   1.101 +#endif
   1.102 +
   1.103 +#if defined(MOZ_WIDGET_GONK)
   1.104 +#include <linux/ioprio.h>
   1.105 +
   1.106 +static const int kB2GServiceArgc = 2;
   1.107 +static const char *kB2GServiceArgv[] = { "/system/bin/start", "b2g" };
   1.108 +
   1.109 +static const char kAppUpdaterPrio[]        = "app.update.updater.prio";
   1.110 +static const char kAppUpdaterOomScoreAdj[] = "app.update.updater.oom_score_adj";
   1.111 +static const char kAppUpdaterIOPrioClass[] = "app.update.updater.ioprio.class";
   1.112 +static const char kAppUpdaterIOPrioLevel[] = "app.update.updater.ioprio.level";
   1.113 +
   1.114 +static const int  kAppUpdaterPrioDefault        = 19;     // -20..19 where 19 = lowest priority
   1.115 +static const int  kAppUpdaterOomScoreAdjDefault = -1000;  // -1000 = Never kill
   1.116 +static const int  kAppUpdaterIOPrioClassDefault = IOPRIO_CLASS_IDLE;
   1.117 +static const int  kAppUpdaterIOPrioLevelDefault = 0;      // Doesn't matter for CLASS IDLE
   1.118 +#endif
   1.119 +
   1.120 +static nsresult
   1.121 +GetCurrentWorkingDir(char *buf, size_t size)
   1.122 +{
   1.123 +  // Cannot use NS_GetSpecialDirectory because XPCOM is not yet initialized.
   1.124 +  // This code is duplicated from xpcom/io/SpecialSystemDirectory.cpp:
   1.125 +
   1.126 +#if defined(XP_WIN)
   1.127 +  wchar_t wpath[MAX_PATH];
   1.128 +  if (!_wgetcwd(wpath, size))
   1.129 +    return NS_ERROR_FAILURE;
   1.130 +  NS_ConvertUTF16toUTF8 path(wpath);
   1.131 +  strncpy(buf, path.get(), size);
   1.132 +#else
   1.133 +  if(!getcwd(buf, size))
   1.134 +    return NS_ERROR_FAILURE;
   1.135 +#endif
   1.136 +  return NS_OK;
   1.137 +}
   1.138 +
   1.139 +
   1.140 +#if defined(XP_WIN)
   1.141 +#define PATH_SEPARATOR ";"
   1.142 +
   1.143 +// In Tor Browser, updater.exe depends on some DLLs that are located in the
   1.144 +// app directory.  To allow the updater to run when it has been copied into
   1.145 +// the update directory, we append the app directory to the PATH.
   1.146 +static nsresult
   1.147 +AdjustPathForUpdater(nsIFile *appDir)
   1.148 +{
   1.149 +  nsAutoCString appPath;
   1.150 +  nsresult rv = appDir->GetNativePath(appPath);
   1.151 +  NS_ENSURE_SUCCESS(rv, rv);
   1.152 +
   1.153 +  char *s = nullptr;
   1.154 +  char *pathValue = PR_GetEnv("PATH");
   1.155 +  if ((nullptr == pathValue) || ('\0' == *pathValue)) {
   1.156 +    s = PR_smprintf("PATH=%s", appPath.get());
   1.157 +  } else {
   1.158 +    s = PR_smprintf("PATH=%s" PATH_SEPARATOR "%s", pathValue, appPath.get());
   1.159 +  }
   1.160 +
   1.161 +  // We intentionally leak the value that is passed into PR_SetEnv() because
   1.162 +  // the environment will hold a pointer to it.
   1.163 +  if ((nullptr == s) || (PR_SUCCESS != PR_SetEnv(s)))
   1.164 +    return NS_ERROR_FAILURE;
   1.165 +
   1.166 +  return NS_OK;
   1.167 +}
   1.168 +#endif
   1.169 +
   1.170 +#ifdef DEBUG
   1.171 +static void
   1.172 +dump_argv(const char *aPrefix, char **argv, int argc)
   1.173 +{
   1.174 +  printf("%s - %d args\n", aPrefix, argc);
   1.175 +  for (int i = 0; i < argc; ++i)
   1.176 +    printf("  %d: %s\n", i, argv[i]);
   1.177 +}
   1.178 +#endif
   1.179 +
   1.180 +
   1.181 +#if defined(XP_MACOSX)
   1.182 +// This is a copy of OS X's XRE_GetBinaryPath from nsAppRunner.cpp with the
   1.183 +// gBinaryPath check removed so that the updater can reload the stub executable
   1.184 +// instead of xulrunner-bin. See bug 349737.
   1.185 +static nsresult
   1.186 +GetXULRunnerStubPath(const char* argv0, nsIFile* *aResult)
   1.187 +{
   1.188 +  // Works even if we're not bundled.
   1.189 +  CFBundleRef appBundle = ::CFBundleGetMainBundle();
   1.190 +  if (!appBundle)
   1.191 +    return NS_ERROR_FAILURE;
   1.192 +
   1.193 +  CFURLRef bundleURL = ::CFBundleCopyExecutableURL(appBundle);
   1.194 +  if (!bundleURL)
   1.195 +    return NS_ERROR_FAILURE;
   1.196 +
   1.197 +  nsCOMPtr<nsILocalFileMac> lfm;
   1.198 +  nsresult rv = NS_NewLocalFileWithCFURL(bundleURL, true, getter_AddRefs(lfm));
   1.199 +
   1.200 +  ::CFRelease(bundleURL);
   1.201 +
   1.202 +  if (NS_FAILED(rv))
   1.203 +    return rv;
   1.204 +
   1.205 +  NS_ADDREF(*aResult = static_cast<nsIFile*>(lfm.get()));
   1.206 +  return NS_OK;
   1.207 +}
   1.208 +#endif /* XP_MACOSX */
   1.209 +
   1.210 +static bool
   1.211 +GetFile(nsIFile *dir, const nsCSubstring &name, nsCOMPtr<nsIFile> &result)
   1.212 +{
   1.213 +  nsresult rv;
   1.214 +  
   1.215 +  nsCOMPtr<nsIFile> file;
   1.216 +  rv = dir->Clone(getter_AddRefs(file));
   1.217 +  if (NS_FAILED(rv))
   1.218 +    return false;
   1.219 +
   1.220 +  rv = file->AppendNative(name);
   1.221 +  if (NS_FAILED(rv))
   1.222 +    return false;
   1.223 +
   1.224 +  result = do_QueryInterface(file, &rv);
   1.225 +  return NS_SUCCEEDED(rv);
   1.226 +}
   1.227 +
   1.228 +static bool
   1.229 +GetStatusFile(nsIFile *dir, nsCOMPtr<nsIFile> &result)
   1.230 +{
   1.231 +  return GetFile(dir, NS_LITERAL_CSTRING("update.status"), result);
   1.232 +}
   1.233 +
   1.234 +/**
   1.235 + * Get the contents of the update.status file.
   1.236 + *
   1.237 + * @param statusFile the status file object.
   1.238 + * @param buf        the buffer holding the file contents
   1.239 + *
   1.240 + * @return true if successful, false otherwise.
   1.241 + */
   1.242 +template <size_t Size>
   1.243 +static bool
   1.244 +GetStatusFileContents(nsIFile *statusFile, char (&buf)[Size])
   1.245 +{
   1.246 +  // The buffer needs to be large enough to hold the known status codes
   1.247 +  PR_STATIC_ASSERT(Size > 16);
   1.248 +
   1.249 +  PRFileDesc *fd = nullptr;
   1.250 +  nsresult rv = statusFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd);
   1.251 +  if (NS_FAILED(rv))
   1.252 +    return false;
   1.253 +
   1.254 +  const int32_t n = PR_Read(fd, buf, Size);
   1.255 +  PR_Close(fd);
   1.256 +
   1.257 +  return (n >= 0);
   1.258 +}
   1.259 +
   1.260 +typedef enum {
   1.261 +  eNoUpdateAction,
   1.262 +  ePendingUpdate,
   1.263 +  ePendingService,
   1.264 +  eAppliedUpdate,
   1.265 +  eAppliedService
   1.266 +} UpdateStatus;
   1.267 +
   1.268 +/**
   1.269 + * Returns a value indicating what needs to be done in order to handle an update.
   1.270 + *
   1.271 + * @param dir the directory in which we should look for an update.status file.
   1.272 + * @param statusFile the update.status file found in the directory.
   1.273 + *
   1.274 + * @return the update action to be performed.
   1.275 + */
   1.276 +static UpdateStatus
   1.277 +GetUpdateStatus(nsIFile* dir, nsCOMPtr<nsIFile> &statusFile)
   1.278 +{
   1.279 +  if (GetStatusFile(dir, statusFile)) {
   1.280 +    char buf[32];
   1.281 +    if (GetStatusFileContents(statusFile, buf)) {
   1.282 +      const char kPending[] = "pending";
   1.283 +      const char kPendingService[] = "pending-service";
   1.284 +      const char kApplied[] = "applied";
   1.285 +      const char kAppliedService[] = "applied-service";
   1.286 +      if (!strncmp(buf, kPendingService, sizeof(kPendingService) - 1)) {
   1.287 +        return ePendingService;
   1.288 +      }
   1.289 +      if (!strncmp(buf, kPending, sizeof(kPending) - 1)) {
   1.290 +        return ePendingUpdate;
   1.291 +      }
   1.292 +      if (!strncmp(buf, kAppliedService, sizeof(kAppliedService) - 1)) {
   1.293 +        return eAppliedService;
   1.294 +      }
   1.295 +      if (!strncmp(buf, kApplied, sizeof(kApplied) - 1)) {
   1.296 +        return eAppliedUpdate;
   1.297 +      }
   1.298 +    }
   1.299 +  }
   1.300 +  return eNoUpdateAction;
   1.301 +}
   1.302 +
   1.303 +static bool
   1.304 +GetVersionFile(nsIFile *dir, nsCOMPtr<nsIFile> &result)
   1.305 +{
   1.306 +  return GetFile(dir, NS_LITERAL_CSTRING("update.version"), result);
   1.307 +}
   1.308 +
   1.309 +// Compares the current application version with the update's application
   1.310 +// version.
   1.311 +static bool
   1.312 +IsOlderVersion(nsIFile *versionFile, const char *appVersion)
   1.313 +{
   1.314 +  PRFileDesc *fd = nullptr;
   1.315 +  nsresult rv = versionFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd);
   1.316 +  if (NS_FAILED(rv))
   1.317 +    return true;
   1.318 +
   1.319 +  char buf[32];
   1.320 +  const int32_t n = PR_Read(fd, buf, sizeof(buf));
   1.321 +  PR_Close(fd);
   1.322 +
   1.323 +  if (n < 0)
   1.324 +    return false;
   1.325 +
   1.326 +  // Trim off the trailing newline
   1.327 +  if (buf[n - 1] == '\n')
   1.328 +    buf[n - 1] = '\0';
   1.329 +
   1.330 +  // If the update xml doesn't provide the application version the file will
   1.331 +  // contain the string "null" and it is assumed that the update is not older.
   1.332 +  const char kNull[] = "null";
   1.333 +  if (strncmp(buf, kNull, sizeof(kNull) - 1) == 0)
   1.334 +    return false;
   1.335 +
   1.336 +#ifdef DEBUG
   1.337 +  printf("IsOlderVersion checking appVersion %s against updateVersion %s\n",
   1.338 +         appVersion, buf);
   1.339 +#endif
   1.340 +  if (mozilla::Version(appVersion) > buf)
   1.341 +    return true;
   1.342 +
   1.343 +  return false;
   1.344 +}
   1.345 +
   1.346 +#if defined(XP_WIN) && defined(MOZ_METRO)
   1.347 +static bool
   1.348 +IsWindowsMetroUpdateRequest(int appArgc, char **appArgv)
   1.349 +{
   1.350 +  for (int index = 0; index < appArgc; index++) {
   1.351 +    if (!strcmp(appArgv[index], "--metro-update")) {
   1.352 +      return true;
   1.353 +    }
   1.354 +  }
   1.355 +  return false;
   1.356 +}
   1.357 +#endif
   1.358 +
   1.359 +static bool
   1.360 +CopyFileIntoUpdateDir(nsIFile *parentDir, const char *leafName, nsIFile *updateDir)
   1.361 +{
   1.362 +  nsDependentCString leaf(leafName);
   1.363 +  nsCOMPtr<nsIFile> file;
   1.364 +
   1.365 +  // Make sure there is not an existing file in the target location.
   1.366 +  nsresult rv = updateDir->Clone(getter_AddRefs(file));
   1.367 +  if (NS_FAILED(rv))
   1.368 +    return false;
   1.369 +  rv = file->AppendNative(leaf);
   1.370 +  if (NS_FAILED(rv))
   1.371 +    return false;
   1.372 +  file->Remove(true);
   1.373 +
   1.374 +  // Now, copy into the target location.
   1.375 +  rv = parentDir->Clone(getter_AddRefs(file));
   1.376 +  if (NS_FAILED(rv))
   1.377 +    return false;
   1.378 +  rv = file->AppendNative(leaf);
   1.379 +  if (NS_FAILED(rv))
   1.380 +    return false;
   1.381 +  rv = file->CopyToNative(updateDir, EmptyCString());
   1.382 +  if (NS_FAILED(rv))
   1.383 +    return false;
   1.384 +
   1.385 +  return true;
   1.386 +}
   1.387 +
   1.388 +static bool
   1.389 +CopyUpdaterIntoUpdateDir(nsIFile *greDir, nsIFile *appDir, nsIFile *updateDir,
   1.390 +                         nsCOMPtr<nsIFile> &updater)
   1.391 +{
   1.392 +  // Copy the updater application from the GRE and the updater ini from the app
   1.393 +#if defined(XP_MACOSX)
   1.394 +  if (!CopyFileIntoUpdateDir(greDir, kUpdaterApp, updateDir))
   1.395 +    return false;
   1.396 +#else
   1.397 +  if (!CopyFileIntoUpdateDir(greDir, kUpdaterBin, updateDir))
   1.398 +    return false;
   1.399 +#endif
   1.400 +  CopyFileIntoUpdateDir(appDir, kUpdaterINI, updateDir);
   1.401 +#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(ANDROID)
   1.402 +  nsCOMPtr<nsIFile> iconDir;
   1.403 +  appDir->Clone(getter_AddRefs(iconDir));
   1.404 +  iconDir->AppendNative(NS_LITERAL_CSTRING("icons"));
   1.405 +  if (!CopyFileIntoUpdateDir(iconDir, kUpdaterPNG, updateDir))
   1.406 +    return false;
   1.407 +#endif
   1.408 +  // Finally, return the location of the updater binary.
   1.409 +  nsresult rv = updateDir->Clone(getter_AddRefs(updater));
   1.410 +  if (NS_FAILED(rv))
   1.411 +    return false;
   1.412 +#if defined(XP_MACOSX)
   1.413 +  rv  = updater->AppendNative(NS_LITERAL_CSTRING(kUpdaterApp));
   1.414 +  nsresult tmp = updater->AppendNative(NS_LITERAL_CSTRING("Contents"));
   1.415 +  if (NS_FAILED(tmp)) {
   1.416 +    rv = tmp;
   1.417 +  }
   1.418 +  tmp = updater->AppendNative(NS_LITERAL_CSTRING("MacOS"));
   1.419 +  if (NS_FAILED(tmp) || NS_FAILED(rv))
   1.420 +    return false;
   1.421 +#endif
   1.422 +  rv = updater->AppendNative(NS_LITERAL_CSTRING(kUpdaterBin));
   1.423 +  return NS_SUCCEEDED(rv); 
   1.424 +}
   1.425 +
   1.426 +/**
   1.427 + * Switch an existing application directory to an updated version that has been
   1.428 + * staged.
   1.429 + *
   1.430 + * @param greDir the GRE dir
   1.431 + * @param updateDir the update root dir
   1.432 + * @param statusFile the update.status file
   1.433 + * @param appDir the app dir
   1.434 + * @param appArgc the number of args to the application
   1.435 + * @param appArgv the args to the application, used for restarting if needed
   1.436 + */
   1.437 +static void
   1.438 +SwitchToUpdatedApp(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile,
   1.439 +                   nsIFile *appDir, int appArgc, char **appArgv)
   1.440 +{
   1.441 +  nsresult rv;
   1.442 +
   1.443 +  // Steps:
   1.444 +  //  - copy updater into temp dir
   1.445 +  //  - run updater with the correct arguments
   1.446 +
   1.447 +  nsCOMPtr<nsIFile> tmpDir;
   1.448 +  GetSpecialSystemDirectory(OS_TemporaryDirectory,
   1.449 +                            getter_AddRefs(tmpDir));
   1.450 +  if (!tmpDir) {
   1.451 +    LOG(("failed getting a temp dir\n"));
   1.452 +    return;
   1.453 +  }
   1.454 +
   1.455 +  // Try to create our own new temp directory in case there is already an
   1.456 +  // updater binary in the OS temporary location which we cannot write to.
   1.457 +  // Note that we don't check for errors here, as if this directory can't
   1.458 +  // be created, the following CopyUpdaterIntoUpdateDir call will fail.
   1.459 +  // We create the unique directory inside a subfolder of MozUpdater instead
   1.460 +  // of directly in the temp directory so we can efficiently delete everything
   1.461 +  // after updates.
   1.462 +  tmpDir->Append(NS_LITERAL_STRING("MozUpdater"));
   1.463 +  tmpDir->Append(NS_LITERAL_STRING("bgupdate"));
   1.464 +  tmpDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0755);
   1.465 +
   1.466 +  nsCOMPtr<nsIFile> updater;
   1.467 +  if (!CopyUpdaterIntoUpdateDir(greDir, appDir, tmpDir, updater)) {
   1.468 +    LOG(("failed copying updater\n"));
   1.469 +    return;
   1.470 +  }
   1.471 +
   1.472 +  // We need to use the value returned from XRE_GetBinaryPath when attempting
   1.473 +  // to restart the running application.
   1.474 +  nsCOMPtr<nsIFile> appFile;
   1.475 +
   1.476 +#if defined(XP_MACOSX)
   1.477 +  // On OS X we need to pass the location of the xulrunner-stub executable
   1.478 +  // rather than xulrunner-bin. See bug 349737.
   1.479 +  GetXULRunnerStubPath(appArgv[0], getter_AddRefs(appFile));
   1.480 +#else
   1.481 +  XRE_GetBinaryPath(appArgv[0], getter_AddRefs(appFile));
   1.482 +#endif
   1.483 +
   1.484 +  if (!appFile)
   1.485 +    return;
   1.486 +
   1.487 +#ifdef XP_WIN
   1.488 +  nsAutoString appFilePathW;
   1.489 +  rv = appFile->GetPath(appFilePathW);
   1.490 +  if (NS_FAILED(rv))
   1.491 +    return;
   1.492 +  NS_ConvertUTF16toUTF8 appFilePath(appFilePathW);
   1.493 +
   1.494 +  nsAutoString updaterPathW;
   1.495 +  rv = updater->GetPath(updaterPathW);
   1.496 +  if (NS_FAILED(rv))
   1.497 +    return;
   1.498 +
   1.499 +  NS_ConvertUTF16toUTF8 updaterPath(updaterPathW);
   1.500 +#else
   1.501 +
   1.502 +  nsAutoCString appFilePath;
   1.503 +#if defined(MOZ_WIDGET_GONK)
   1.504 +  appFilePath.Assign(kB2GServiceArgv[0]);
   1.505 +  appArgc = kB2GServiceArgc;
   1.506 +  appArgv = const_cast<char**>(kB2GServiceArgv);
   1.507 +#else
   1.508 +  rv = appFile->GetNativePath(appFilePath);
   1.509 +  if (NS_FAILED(rv))
   1.510 +    return;
   1.511 +#endif
   1.512 +
   1.513 +  nsAutoCString updaterPath;
   1.514 +  rv = updater->GetNativePath(updaterPath);
   1.515 +  if (NS_FAILED(rv))
   1.516 +    return;
   1.517 +#endif
   1.518 +
   1.519 +  // Get the directory to which the update will be applied. On Mac OSX we need
   1.520 +  // to apply the update to the Updated.app directory under the Foo.app
   1.521 +  // directory which is the parent of the parent of the appDir. On other
   1.522 +  // platforms we will just apply to the appDir/updated.
   1.523 +  nsCOMPtr<nsIFile> updatedDir;
   1.524 +#if defined(XP_MACOSX)
   1.525 +  nsAutoCString applyToDir;
   1.526 +  {
   1.527 +    nsCOMPtr<nsIFile> parentDir1, parentDir2;
   1.528 +    rv = appDir->GetParent(getter_AddRefs(parentDir1));
   1.529 +    if (NS_FAILED(rv))
   1.530 +      return;
   1.531 +    rv = parentDir1->GetParent(getter_AddRefs(parentDir2));
   1.532 +    if (NS_FAILED(rv))
   1.533 +      return;
   1.534 +    if (!GetFile(parentDir2, NS_LITERAL_CSTRING("Updated.app"), updatedDir))
   1.535 +      return;
   1.536 +    rv = updatedDir->GetNativePath(applyToDir);
   1.537 +  }
   1.538 +#else
   1.539 +  if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir))
   1.540 +    return;
   1.541 +#if defined(XP_WIN)
   1.542 +  nsAutoString applyToDirW;
   1.543 +  rv = updatedDir->GetPath(applyToDirW);
   1.544 +
   1.545 +  NS_ConvertUTF16toUTF8 applyToDir(applyToDirW);
   1.546 +#else
   1.547 +  nsAutoCString applyToDir;
   1.548 +  rv = updatedDir->GetNativePath(applyToDir);
   1.549 +#endif
   1.550 +#endif
   1.551 +  if (NS_FAILED(rv))
   1.552 +    return;
   1.553 +
   1.554 +  // Make sure that the updated directory exists
   1.555 +  bool updatedDirExists = false;
   1.556 +  updatedDir->Exists(&updatedDirExists);
   1.557 +  if (!updatedDirExists) {
   1.558 +    return;
   1.559 +  }
   1.560 +
   1.561 +#if defined(XP_WIN)
   1.562 +  nsAutoString updateDirPathW;
   1.563 +  rv = updateDir->GetPath(updateDirPathW);
   1.564 +
   1.565 +  NS_ConvertUTF16toUTF8 updateDirPath(updateDirPathW);
   1.566 +#else
   1.567 +  nsAutoCString updateDirPath;
   1.568 +  rv = updateDir->GetNativePath(updateDirPath);
   1.569 +#endif
   1.570 +
   1.571 +  if (NS_FAILED(rv))
   1.572 +    return;
   1.573 +
   1.574 +  // Get the current working directory.
   1.575 +  char workingDirPath[MAXPATHLEN];
   1.576 +  rv = GetCurrentWorkingDir(workingDirPath, sizeof(workingDirPath));
   1.577 +  if (NS_FAILED(rv))
   1.578 +    return;
   1.579 +
   1.580 +  // Construct the PID argument for this process.  If we are using execv, then
   1.581 +  // we pass "0" which is then ignored by the updater.
   1.582 +#if defined(USE_EXECV)
   1.583 +  nsAutoCString pid("0");
   1.584 +#else
   1.585 +  nsAutoCString pid;
   1.586 +  pid.AppendInt((int32_t) getpid());
   1.587 +#endif
   1.588 +
   1.589 +  // Append a special token to the PID in order to let the updater know that it
   1.590 +  // just needs to replace the update directory.
   1.591 +  pid.AppendLiteral("/replace");
   1.592 +
   1.593 +  int immersiveArgc = 0;
   1.594 +#if defined(XP_WIN) && defined(MOZ_METRO)
   1.595 +  // If this is desktop doing an update for metro, or if we're the metro browser
   1.596 +  // we want to launch the metro browser after we're finished.
   1.597 +  if (IsWindowsMetroUpdateRequest(appArgc, appArgv) || IsRunningInWindowsMetro()) {
   1.598 +    immersiveArgc = 1;
   1.599 +  }
   1.600 +#endif
   1.601 +  int argc = appArgc + 5 + immersiveArgc;
   1.602 +  char **argv = new char*[argc + 1];
   1.603 +  if (!argv)
   1.604 +    return;
   1.605 +  argv[0] = (char*) updaterPath.get();
   1.606 +  argv[1] = (char*) updateDirPath.get();
   1.607 +  argv[2] = (char*) applyToDir.get();
   1.608 +  argv[3] = (char*) pid.get();
   1.609 +  if (appArgc) {
   1.610 +    argv[4] = workingDirPath;
   1.611 +    argv[5] = (char*) appFilePath.get();
   1.612 +    for (int i = 1; i < appArgc; ++i)
   1.613 +      argv[5 + i] = appArgv[i];
   1.614 +#ifdef XP_WIN
   1.615 +    if (immersiveArgc) {
   1.616 +      argv[argc - 1] = "-ServerName:DefaultBrowserServer";
   1.617 +    }
   1.618 +#endif
   1.619 +    argv[argc] = nullptr;
   1.620 +  } else {
   1.621 +    argc = 4;
   1.622 +    argv[4] = nullptr;
   1.623 +  }
   1.624 +
   1.625 +  if (gSafeMode) {
   1.626 +    PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
   1.627 +  }
   1.628 +
   1.629 +#if defined(XP_WIN)
   1.630 +  nsresult rv2 = AdjustPathForUpdater(appDir);
   1.631 +  if (NS_FAILED(rv2)) {
   1.632 +    LOG(("SwitchToUpdatedApp -- AdjustPathForUpdater failed (0x%x)\n", rv2));
   1.633 +  }
   1.634 +#endif
   1.635 +
   1.636 +  LOG(("spawning updater process for replacing [%s]\n", updaterPath.get()));
   1.637 +
   1.638 +#if defined(USE_EXECV)
   1.639 +# if defined(MOZ_WIDGET_GONK)
   1.640 +  // In Gonk, we preload libmozglue, which the updater process doesn't need.
   1.641 +  // Since the updater will move and delete libmozglue.so, this can actually
   1.642 +  // stop the /system mount from correctly being remounted as read-only.
   1.643 +  unsetenv("LD_PRELOAD");
   1.644 +# endif
   1.645 +  execv(updaterPath.get(), argv);
   1.646 +#elif defined(XP_WIN)
   1.647 +  // Switch the application using updater.exe
   1.648 +  if (!WinLaunchChild(updaterPathW.get(), argc, argv)) {
   1.649 +    return;
   1.650 +  }
   1.651 +  _exit(0);
   1.652 +#elif defined(XP_MACOSX)
   1.653 +  CommandLineServiceMac::SetupMacCommandLine(argc, argv, true);
   1.654 +  // LaunchChildMac uses posix_spawnp and prefers the current
   1.655 +  // architecture when launching. It doesn't require a
   1.656 +  // null-terminated string but it doesn't matter if we pass one.
   1.657 +  LaunchChildMac(argc, argv);
   1.658 +  exit(0);
   1.659 +#else
   1.660 +  PR_CreateProcessDetached(updaterPath.get(), argv, nullptr, nullptr);
   1.661 +  exit(0);
   1.662 +#endif
   1.663 +}
   1.664 +
   1.665 +#if defined(MOZ_WIDGET_GONK)
   1.666 +static nsresult
   1.667 +GetOSApplyToDir(nsACString& applyToDir)
   1.668 +{
   1.669 +  nsCOMPtr<nsIProperties> ds =
   1.670 +    do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
   1.671 +  NS_ASSERTION(ds, "Can't get directory service");
   1.672 +
   1.673 +  nsCOMPtr<nsIFile> osApplyToDir;
   1.674 +  nsresult rv = ds->Get(XRE_OS_UPDATE_APPLY_TO_DIR, NS_GET_IID(nsIFile),
   1.675 +                                   getter_AddRefs(osApplyToDir));
   1.676 +  if (NS_FAILED(rv)) {
   1.677 +    LOG(("Can't get the OS applyTo dir"));
   1.678 +    return rv;
   1.679 +  }
   1.680 +
   1.681 +  return osApplyToDir->GetNativePath(applyToDir);
   1.682 +}
   1.683 +
   1.684 +static void
   1.685 +SetOSApplyToDir(nsIUpdate* update, const nsACString& osApplyToDir)
   1.686 +{
   1.687 +  nsresult rv;
   1.688 +  nsCOMPtr<nsIWritablePropertyBag> updateProperties =
   1.689 +    do_QueryInterface(update, &rv);
   1.690 +
   1.691 +  if (NS_FAILED(rv)) {
   1.692 +    return;
   1.693 +  }
   1.694 +
   1.695 +  nsCOMPtr<nsIWritableVariant> variant =
   1.696 +    do_CreateInstance("@mozilla.org/variant;1", &rv);
   1.697 +  if (NS_FAILED(rv)) {
   1.698 +    return;
   1.699 +  }
   1.700 +
   1.701 +  rv = variant->SetAsACString(osApplyToDir);
   1.702 +  if (NS_FAILED(rv)) {
   1.703 +    return;
   1.704 +  }
   1.705 +
   1.706 +  updateProperties->SetProperty(NS_LITERAL_STRING("osApplyToDir"), variant);
   1.707 +}
   1.708 +#endif
   1.709 +
   1.710 +/**
   1.711 + * Apply an update. This applies to both normal and staged updates.
   1.712 + *
   1.713 + * @param greDir the GRE dir
   1.714 + * @param updateDir the update root dir
   1.715 + * @param statusFile the update.status file
   1.716 + * @param appDir the app dir
   1.717 + * @param appArgc the number of args to the application
   1.718 + * @param appArgv the args to the application, used for restarting if needed
   1.719 + * @param restart if true, apply the update in the foreground and restart the
   1.720 + *                application when done.  otherwise, stage the update and don't
   1.721 + *                restart the application.
   1.722 + * @param outpid out parameter holding the handle to the updater application for
   1.723 + *               staging updates.
   1.724 + */
   1.725 +static void
   1.726 +ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile,
   1.727 +            nsIFile *appDir, int appArgc, char **appArgv,
   1.728 +            bool restart, bool isOSUpdate, nsIFile *osApplyToDir,
   1.729 +            ProcessType *outpid)
   1.730 +{
   1.731 +  nsresult rv;
   1.732 +
   1.733 +  // Steps:
   1.734 +  //  - mark update as 'applying'
   1.735 +  //  - copy updater into update dir
   1.736 +  //  - run updater w/ appDir as the current working dir
   1.737 +
   1.738 +  nsCOMPtr<nsIFile> updater;
   1.739 +  if (!CopyUpdaterIntoUpdateDir(greDir, appDir, updateDir, updater)) {
   1.740 +    LOG(("failed copying updater\n"));
   1.741 +    return;
   1.742 +  }
   1.743 +
   1.744 +  // We need to use the value returned from XRE_GetBinaryPath when attempting
   1.745 +  // to restart the running application.
   1.746 +  nsCOMPtr<nsIFile> appFile;
   1.747 +
   1.748 +#if defined(XP_MACOSX)
   1.749 +  // On OS X we need to pass the location of the xulrunner-stub executable
   1.750 +  // rather than xulrunner-bin. See bug 349737.
   1.751 +  GetXULRunnerStubPath(appArgv[0], getter_AddRefs(appFile));
   1.752 +#else
   1.753 +  XRE_GetBinaryPath(appArgv[0], getter_AddRefs(appFile));
   1.754 +#endif
   1.755 +
   1.756 +  if (!appFile)
   1.757 +    return;
   1.758 +
   1.759 +#ifdef XP_WIN
   1.760 +  nsAutoString appFilePathW;
   1.761 +  rv = appFile->GetPath(appFilePathW);
   1.762 +  if (NS_FAILED(rv))
   1.763 +    return;
   1.764 +  NS_ConvertUTF16toUTF8 appFilePath(appFilePathW);
   1.765 +
   1.766 +  nsAutoString updaterPathW;
   1.767 +  rv = updater->GetPath(updaterPathW);
   1.768 +  if (NS_FAILED(rv))
   1.769 +    return;
   1.770 +
   1.771 +  NS_ConvertUTF16toUTF8 updaterPath(updaterPathW);
   1.772 +
   1.773 +#else
   1.774 +  nsAutoCString appFilePath;
   1.775 +  rv = appFile->GetNativePath(appFilePath);
   1.776 +  if (NS_FAILED(rv))
   1.777 +    return;
   1.778 +  
   1.779 +  nsAutoCString updaterPath;
   1.780 +  rv = updater->GetNativePath(updaterPath);
   1.781 +  if (NS_FAILED(rv))
   1.782 +    return;
   1.783 +
   1.784 +#endif
   1.785 +
   1.786 +  // Get the directory to which the update will be applied. On Mac OSX we need
   1.787 +  // to apply the update to the Updated.app directory under the Foo.app
   1.788 +  // directory which is the parent of the parent of the appDir. On other
   1.789 +  // platforms we will just apply to the appDir/updated.
   1.790 +  nsCOMPtr<nsIFile> updatedDir;
   1.791 +#if defined(XP_MACOSX)
   1.792 +  nsAutoCString applyToDir;
   1.793 +  {
   1.794 +    nsCOMPtr<nsIFile> parentDir1, parentDir2;
   1.795 +    rv = appDir->GetParent(getter_AddRefs(parentDir1));
   1.796 +    if (NS_FAILED(rv))
   1.797 +      return;
   1.798 +    rv = parentDir1->GetParent(getter_AddRefs(parentDir2));
   1.799 +    if (NS_FAILED(rv))
   1.800 +      return;
   1.801 +    if (restart) {
   1.802 +      // Use the correct directory if we're not staging the update.
   1.803 +      rv = parentDir2->GetNativePath(applyToDir);
   1.804 +    } else {
   1.805 +      if (!GetFile(parentDir2, NS_LITERAL_CSTRING("Updated.app"), updatedDir))
   1.806 +        return;
   1.807 +      rv = updatedDir->GetNativePath(applyToDir);
   1.808 +    }
   1.809 +  }
   1.810 +#else
   1.811 +  if (restart) {
   1.812 +    // Use the correct directory if we're not staging the update.
   1.813 +    updatedDir = do_QueryInterface(appDir);
   1.814 +  } else if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) {
   1.815 +    return;
   1.816 +  }
   1.817 +#if defined(XP_WIN)
   1.818 +  nsAutoString applyToDirW;
   1.819 +  rv = updatedDir->GetPath(applyToDirW);
   1.820 +
   1.821 +  NS_ConvertUTF16toUTF8 applyToDir(applyToDirW);
   1.822 +#else
   1.823 +  nsAutoCString applyToDir;
   1.824 +
   1.825 +#if defined(MOZ_WIDGET_GONK)
   1.826 +  if (isOSUpdate) {
   1.827 +    if (!osApplyToDir) {
   1.828 +      return;
   1.829 +    }
   1.830 +
   1.831 +    rv = osApplyToDir->GetNativePath(applyToDir);
   1.832 +  } else {
   1.833 +#endif // defined(MOZ_WIDGET_GONK)
   1.834 +
   1.835 +    rv = updatedDir->GetNativePath(applyToDir);
   1.836 +
   1.837 +#if defined(MOZ_WIDGET_GONK)
   1.838 +  }
   1.839 +#endif // defined(MOZ_WIDGET_GONK)
   1.840 +
   1.841 +#endif // defined(XP_WIN)
   1.842 +#endif
   1.843 +
   1.844 +  if (NS_FAILED(rv))
   1.845 +    return;
   1.846 +
   1.847 +#if defined(XP_WIN)
   1.848 +  nsAutoString updateDirPathW;
   1.849 +  rv = updateDir->GetPath(updateDirPathW);
   1.850 +
   1.851 +  NS_ConvertUTF16toUTF8 updateDirPath(updateDirPathW);
   1.852 +#else
   1.853 +  nsAutoCString updateDirPath;
   1.854 +  rv = updateDir->GetNativePath(updateDirPath);
   1.855 +#endif
   1.856 +
   1.857 +  if (NS_FAILED(rv))
   1.858 +    return;
   1.859 +
   1.860 +  // Get the current working directory.
   1.861 +  char workingDirPath[MAXPATHLEN];
   1.862 +  rv = GetCurrentWorkingDir(workingDirPath, sizeof(workingDirPath));
   1.863 +  if (NS_FAILED(rv))
   1.864 +    return;
   1.865 +
   1.866 +  // We used to write out "Applying" to the update.status file here.
   1.867 +  // Instead we do this from within the updater application now.
   1.868 +  // This is so that we don't overwrite the status of pending-service
   1.869 +  // in the Windows case.  This change was made for all platforms so
   1.870 +  // that it stays consistent across all OS.
   1.871 +
   1.872 +  // Construct the PID argument for this process.  If we are using execv, then
   1.873 +  // we pass "0" which is then ignored by the updater.
   1.874 +  nsAutoCString pid;
   1.875 +  if (!restart) {
   1.876 +    // Signal the updater application that it should stage the update.
   1.877 +    pid.AssignASCII("-1");
   1.878 +  } else {
   1.879 +#if defined(USE_EXECV)
   1.880 +    pid.AssignASCII("0");
   1.881 +#else
   1.882 +    pid.AppendInt((int32_t) getpid());
   1.883 +#endif
   1.884 +  }
   1.885 +
   1.886 +  int immersiveArgc = 0;
   1.887 +#if defined(XP_WIN) && defined(MOZ_METRO)
   1.888 +  // If this is desktop doing an update for metro, or if we're the metro browser
   1.889 +  // we want to launch the metro browser after we're finished.
   1.890 +  if (IsWindowsMetroUpdateRequest(appArgc, appArgv) || IsRunningInWindowsMetro()) {
   1.891 +    immersiveArgc = 1;
   1.892 +  }
   1.893 +#endif
   1.894 +  int argc = appArgc + 5 + immersiveArgc;
   1.895 +  char **argv = new char*[argc + 1 ];
   1.896 +  if (!argv)
   1.897 +    return;
   1.898 +  argv[0] = (char*) updaterPath.get();
   1.899 +  argv[1] = (char*) updateDirPath.get();
   1.900 +  argv[2] = (char*) applyToDir.get();
   1.901 +  argv[3] = (char*) pid.get();
   1.902 +  if (restart && appArgc) {
   1.903 +    argv[4] = workingDirPath;
   1.904 +    argv[5] = (char*) appFilePath.get();
   1.905 +    for (int i = 1; i < appArgc; ++i)
   1.906 +      argv[5 + i] = appArgv[i];
   1.907 +#ifdef XP_WIN
   1.908 +    if (immersiveArgc) {
   1.909 +      argv[argc - 1] = "-ServerName:DefaultBrowserServer";
   1.910 +    }
   1.911 +#endif
   1.912 +    argv[argc] = nullptr;
   1.913 +  } else {
   1.914 +    argc = 4;
   1.915 +    argv[4] = nullptr;
   1.916 +  }
   1.917 +
   1.918 +  if (gSafeMode) {
   1.919 +    PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
   1.920 +  }
   1.921 +
   1.922 +  if (isOSUpdate) {
   1.923 +    PR_SetEnv("MOZ_OS_UPDATE=1");
   1.924 +  }
   1.925 +
   1.926 +#if defined(XP_WIN)
   1.927 +  nsresult rv2 = AdjustPathForUpdater(appDir);
   1.928 +  if (NS_FAILED(rv2)) {
   1.929 +    LOG(("ApplyUpdate -- AdjustPathForUpdater failed (0x%x)\n", rv2));
   1.930 +  }
   1.931 +#endif
   1.932 +
   1.933 +#if defined(MOZ_WIDGET_GONK)
   1.934 +  // We want the updater to be CPU friendly and not subject to being killed by
   1.935 +  // the low memory killer, so we pass in some preferences to allow it to
   1.936 +  // adjust its priority.
   1.937 +
   1.938 +  int32_t prioVal = Preferences::GetInt(kAppUpdaterPrio,
   1.939 +                                        kAppUpdaterPrioDefault);
   1.940 +  int32_t oomScoreAdj = Preferences::GetInt(kAppUpdaterOomScoreAdj,
   1.941 +                                            kAppUpdaterOomScoreAdjDefault);
   1.942 +  int32_t ioprioClass = Preferences::GetInt(kAppUpdaterIOPrioClass,
   1.943 +                                            kAppUpdaterIOPrioClassDefault);
   1.944 +  int32_t ioprioLevel = Preferences::GetInt(kAppUpdaterIOPrioLevel,
   1.945 +                                            kAppUpdaterIOPrioLevelDefault);
   1.946 +  nsPrintfCString prioEnv("MOZ_UPDATER_PRIO=%d/%d/%d/%d",
   1.947 +                          prioVal, oomScoreAdj, ioprioClass, ioprioLevel);
   1.948 +  PR_SetEnv(prioEnv.get());
   1.949 +#endif
   1.950 +
   1.951 +  LOG(("spawning updater process [%s]\n", updaterPath.get()));
   1.952 +#ifdef DEBUG
   1.953 +  dump_argv("ApplyUpdate updater", argv, argc);
   1.954 +#endif
   1.955 +
   1.956 +#if defined(USE_EXECV)
   1.957 +  // Don't use execv when staging updates.
   1.958 +  if (restart) {
   1.959 +    execv(updaterPath.get(), argv);
   1.960 +  } else {
   1.961 +    *outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr);
   1.962 +  }
   1.963 +#elif defined(XP_WIN)
   1.964 +  // Launch the update using updater.exe
   1.965 +  if (!WinLaunchChild(updaterPathW.get(), argc, argv, nullptr, outpid)) {
   1.966 +    return;
   1.967 +  }
   1.968 +
   1.969 +  if (restart) {
   1.970 +    // We are going to process an update so we should exit now
   1.971 +    _exit(0);
   1.972 +  }
   1.973 +#elif defined(XP_MACOSX)
   1.974 +  CommandLineServiceMac::SetupMacCommandLine(argc, argv, true);
   1.975 +  // LaunchChildMac uses posix_spawnp and prefers the current
   1.976 +  // architecture when launching. It doesn't require a
   1.977 +  // null-terminated string but it doesn't matter if we pass one.
   1.978 +#ifdef DEBUG
   1.979 +  dump_argv("ApplyUpdate after SetupMacCommandLine", argv, argc);
   1.980 +#endif
   1.981 +  LaunchChildMac(argc, argv, 0, outpid);
   1.982 +  if (restart) {
   1.983 +    exit(0);
   1.984 +  }
   1.985 +#else
   1.986 +  *outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr);
   1.987 +  if (restart) {
   1.988 +    exit(0);
   1.989 +  }
   1.990 +#endif
   1.991 +}
   1.992 +
   1.993 +/**
   1.994 + * Wait for a process until it terminates.  This call is blocking.
   1.995 + */
   1.996 +static void
   1.997 +WaitForProcess(ProcessType pt)
   1.998 +{
   1.999 +#if defined(XP_WIN)
  1.1000 +  WaitForSingleObject(pt, INFINITE);
  1.1001 +  CloseHandle(pt);
  1.1002 +#elif defined(XP_MACOSX)
  1.1003 +  waitpid(pt, 0, 0);
  1.1004 +#else
  1.1005 +  int32_t exitCode;
  1.1006 +  PR_WaitProcess(pt, &exitCode);
  1.1007 +  if (exitCode != 0) {
  1.1008 +    LOG(("Error while running the updater process, check update.log"));
  1.1009 +  }
  1.1010 +#endif
  1.1011 +}
  1.1012 +
  1.1013 +nsresult
  1.1014 +ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir,
  1.1015 +               int argc, char **argv, const char *appVersion,
  1.1016 +               bool restart, bool isOSUpdate, nsIFile *osApplyToDir,
  1.1017 +               ProcessType *pid)
  1.1018 +{
  1.1019 +  nsresult rv;
  1.1020 +
  1.1021 +  nsCOMPtr<nsIFile> updatesDir;
  1.1022 +#ifdef DEBUG
  1.1023 +  nsAutoCString path;
  1.1024 +  updRootDir->GetNativePath(path);
  1.1025 +  printf("ProcessUpdates updateRootDir: %s appVersion: %s\n",
  1.1026 +         path.get(), appVersion);
  1.1027 +#endif
  1.1028 +  rv = updRootDir->Clone(getter_AddRefs(updatesDir));
  1.1029 +  if (NS_FAILED(rv))
  1.1030 +    return rv;
  1.1031 +
  1.1032 +  rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("updates"));
  1.1033 +  if (NS_FAILED(rv))
  1.1034 +    return rv;
  1.1035 +
  1.1036 +  rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("0"));
  1.1037 +  if (NS_FAILED(rv))
  1.1038 +    return rv;
  1.1039 + 
  1.1040 +  ProcessType dummyPID; // this will only be used for MOZ_UPDATE_STAGING
  1.1041 +  const char *processingUpdates = PR_GetEnv("MOZ_PROCESS_UPDATES");
  1.1042 +  if (processingUpdates && *processingUpdates) {
  1.1043 +    // Enable the tests to request an update to be staged.
  1.1044 +    const char *stagingUpdate = PR_GetEnv("MOZ_UPDATE_STAGING");
  1.1045 +    if (stagingUpdate && *stagingUpdate) {
  1.1046 +      restart = false;
  1.1047 +      pid = &dummyPID;
  1.1048 +    }
  1.1049 +  }
  1.1050 +
  1.1051 +  nsCOMPtr<nsIFile> statusFile;
  1.1052 +  UpdateStatus status = GetUpdateStatus(updatesDir, statusFile);
  1.1053 +#ifdef DEBUG
  1.1054 +  printf("ProcessUpdates status: %d\n", status);
  1.1055 +  updatesDir->GetNativePath(path);
  1.1056 +  printf("ProcessUpdates updatesDir: %s\n", path.get());
  1.1057 +#endif
  1.1058 +  switch (status) {
  1.1059 +  case ePendingUpdate:
  1.1060 +  case ePendingService: {
  1.1061 +    nsCOMPtr<nsIFile> versionFile;
  1.1062 +    // Remove the update if the update application version file doesn't exist
  1.1063 +    // or if the update's application version is less than the current
  1.1064 +    // application version.
  1.1065 +    if (!GetVersionFile(updatesDir, versionFile) ||
  1.1066 +        IsOlderVersion(versionFile, appVersion)) {
  1.1067 +      updatesDir->Remove(true);
  1.1068 +    } else {
  1.1069 +      ApplyUpdate(greDir, updatesDir, statusFile,
  1.1070 +                  appDir, argc, argv, restart, isOSUpdate, osApplyToDir, pid);
  1.1071 +    }
  1.1072 +    break;
  1.1073 +  }
  1.1074 +  case eAppliedUpdate:
  1.1075 +  case eAppliedService:
  1.1076 +    // An update was staged and needs to be switched so the updated application
  1.1077 +    // is used.
  1.1078 +    SwitchToUpdatedApp(greDir, updatesDir, statusFile,
  1.1079 +                       appDir, argc, argv);
  1.1080 +    break;
  1.1081 +  case eNoUpdateAction:
  1.1082 +    // We don't need to do any special processing here, we'll just continue to
  1.1083 +    // startup the application.
  1.1084 +    break;
  1.1085 +  }
  1.1086 +
  1.1087 +  return NS_OK;
  1.1088 +}
  1.1089 +
  1.1090 +
  1.1091 +
  1.1092 +NS_IMPL_ISUPPORTS(nsUpdateProcessor, nsIUpdateProcessor)
  1.1093 +
  1.1094 +nsUpdateProcessor::nsUpdateProcessor()
  1.1095 +  : mUpdaterPID(0)
  1.1096 +{
  1.1097 +}
  1.1098 +
  1.1099 +NS_IMETHODIMP
  1.1100 +nsUpdateProcessor::ProcessUpdate(nsIUpdate* aUpdate)
  1.1101 +{
  1.1102 +  nsCOMPtr<nsIFile> greDir, appDir, updRoot;
  1.1103 +  nsAutoCString appVersion;
  1.1104 +  int argc;
  1.1105 +  char **argv;
  1.1106 +
  1.1107 +  nsAutoCString binPath;
  1.1108 +  nsXREDirProvider* dirProvider = nsXREDirProvider::GetSingleton();
  1.1109 +  if (dirProvider) { // Normal code path
  1.1110 +    // Check for and process any available updates
  1.1111 +    bool persistent;
  1.1112 +    nsresult rv = NS_ERROR_FAILURE; // Take the NS_FAILED path when non-GONK
  1.1113 +#ifdef MOZ_WIDGET_GONK
  1.1114 +    // Check in the sdcard for updates first, since that's our preferred
  1.1115 +    // download location.
  1.1116 +    rv = dirProvider->GetFile(XRE_UPDATE_ARCHIVE_DIR, &persistent,
  1.1117 +                              getter_AddRefs(updRoot));
  1.1118 +#endif
  1.1119 +    if (NS_FAILED(rv)) {
  1.1120 +      rv = dirProvider->GetFile(XRE_UPDATE_ROOT_DIR, &persistent,
  1.1121 +                                getter_AddRefs(updRoot));
  1.1122 +    }
  1.1123 +    // XRE_UPDATE_ROOT_DIR may fail. Fallback to appDir if failed
  1.1124 +    if (NS_FAILED(rv))
  1.1125 +      updRoot = dirProvider->GetAppDir();
  1.1126 +
  1.1127 +    greDir = dirProvider->GetGREDir();
  1.1128 +    nsCOMPtr<nsIFile> exeFile;
  1.1129 +    rv = dirProvider->GetFile(XRE_EXECUTABLE_FILE, &persistent,
  1.1130 +                              getter_AddRefs(exeFile));
  1.1131 +    if (NS_SUCCEEDED(rv))
  1.1132 +      rv = exeFile->GetParent(getter_AddRefs(appDir));
  1.1133 +
  1.1134 +    if (NS_FAILED(rv))
  1.1135 +      appDir = dirProvider->GetAppDir();
  1.1136 +
  1.1137 +#ifdef TOR_BROWSER_UPDATE
  1.1138 +    appVersion = TOR_BROWSER_VERSION;
  1.1139 +#else
  1.1140 +    appVersion = gAppData->version;
  1.1141 +#endif
  1.1142 +    argc = gRestartArgc;
  1.1143 +    argv = gRestartArgv;
  1.1144 +  } else {
  1.1145 +    // In the xpcshell environment, the usual XRE_main is not run, so things
  1.1146 +    // like dirProvider and gAppData do not exist.  This code path accesses
  1.1147 +    // XPCOM (which is not available in the previous code path) in order to get
  1.1148 +    // the same information.
  1.1149 +    nsCOMPtr<nsIProperties> ds =
  1.1150 +      do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
  1.1151 +    if (!ds) {
  1.1152 +      NS_ABORT(); // There's nothing which we can do if this fails!
  1.1153 +    }
  1.1154 +
  1.1155 +    nsresult rv = ds->Get(NS_GRE_DIR, NS_GET_IID(nsIFile),
  1.1156 +                          getter_AddRefs(greDir));
  1.1157 +    NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the GRE dir");
  1.1158 +    appDir = greDir;
  1.1159 +
  1.1160 +    rv = ds->Get(XRE_UPDATE_ROOT_DIR, NS_GET_IID(nsIFile),
  1.1161 +                 getter_AddRefs(updRoot));
  1.1162 +    if (NS_FAILED(rv))
  1.1163 +      updRoot = appDir;
  1.1164 +
  1.1165 +    // To support Tor Browser Bundle updates from xpcshell, modify the
  1.1166 +    // following code to use the TBB version fron the configure process.
  1.1167 +    nsCOMPtr<nsIXULAppInfo> appInfo =
  1.1168 +      do_GetService("@mozilla.org/xre/app-info;1");
  1.1169 +    if (appInfo) {
  1.1170 +      rv = appInfo->GetVersion(appVersion);
  1.1171 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1172 +    } else {
  1.1173 +      appVersion = MOZ_APP_VERSION;
  1.1174 +    }
  1.1175 +
  1.1176 +    // We need argv[0] to point to the current executable's name.  The rest of
  1.1177 +    // the entries in this array will be ignored if argc<2.  Therefore, for
  1.1178 +    // xpcshell, we only fill out that item, and leave the rest empty.
  1.1179 +    argc = 1;
  1.1180 +    nsCOMPtr<nsIFile> binary;
  1.1181 +    rv = ds->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile),
  1.1182 +                 getter_AddRefs(binary));
  1.1183 +    NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the binary path");
  1.1184 +    binary->GetNativePath(binPath);
  1.1185 +  }
  1.1186 +
  1.1187 +  // Copy the parameters to the StagedUpdateInfo structure shared with the
  1.1188 +  // watcher thread.
  1.1189 +  mInfo.mGREDir = greDir;
  1.1190 +  mInfo.mAppDir = appDir;
  1.1191 +  mInfo.mUpdateRoot = updRoot;
  1.1192 +  mInfo.mArgc = argc;
  1.1193 +  mInfo.mArgv = new char*[argc];
  1.1194 +  if (dirProvider) {
  1.1195 +    for (int i = 0; i < argc; ++i) {
  1.1196 +      const size_t length = strlen(argv[i]);
  1.1197 +      mInfo.mArgv[i] = new char[length + 1];
  1.1198 +      strcpy(mInfo.mArgv[i], argv[i]);
  1.1199 +    }
  1.1200 +  } else {
  1.1201 +    MOZ_ASSERT(argc == 1); // see above
  1.1202 +    const size_t length = binPath.Length();
  1.1203 +    mInfo.mArgv[0] = new char[length + 1];
  1.1204 +    strcpy(mInfo.mArgv[0], binPath.get());
  1.1205 +  }
  1.1206 +  mInfo.mAppVersion = appVersion;
  1.1207 +
  1.1208 +#if defined(MOZ_WIDGET_GONK)
  1.1209 +  NS_ENSURE_ARG_POINTER(aUpdate);
  1.1210 +
  1.1211 +  bool isOSUpdate;
  1.1212 +  if (NS_SUCCEEDED(aUpdate->GetIsOSUpdate(&isOSUpdate)) &&
  1.1213 +      isOSUpdate) {
  1.1214 +    nsAutoCString osApplyToDir;
  1.1215 +
  1.1216 +    // This needs to be done on the main thread, so we pass it along in
  1.1217 +    // BackgroundThreadInfo
  1.1218 +    nsresult rv = GetOSApplyToDir(osApplyToDir);
  1.1219 +    if (NS_FAILED(rv)) {
  1.1220 +      LOG(("Can't get the OS apply to dir"));
  1.1221 +      return rv;
  1.1222 +    }
  1.1223 +
  1.1224 +    SetOSApplyToDir(aUpdate, osApplyToDir);
  1.1225 +
  1.1226 +    mInfo.mIsOSUpdate = true;
  1.1227 +    rv = NS_NewNativeLocalFile(osApplyToDir, false,
  1.1228 +                               getter_AddRefs(mInfo.mOSApplyToDir));
  1.1229 +    if (NS_FAILED(rv)) {
  1.1230 +      LOG(("Can't create nsIFile for OS apply to dir"));
  1.1231 +      return rv;
  1.1232 +    }
  1.1233 +  }
  1.1234 +#endif
  1.1235 +
  1.1236 +  mUpdate = aUpdate;
  1.1237 +
  1.1238 +  NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
  1.1239 +  return NS_NewThread(getter_AddRefs(mProcessWatcher),
  1.1240 +                      NS_NewRunnableMethod(this, &nsUpdateProcessor::StartStagedUpdate));
  1.1241 +}
  1.1242 +
  1.1243 +
  1.1244 +
  1.1245 +void
  1.1246 +nsUpdateProcessor::StartStagedUpdate()
  1.1247 +{
  1.1248 +  NS_ABORT_IF_FALSE(!NS_IsMainThread(), "main thread");
  1.1249 +
  1.1250 +  nsresult rv = ProcessUpdates(mInfo.mGREDir,
  1.1251 +                               mInfo.mAppDir,
  1.1252 +                               mInfo.mUpdateRoot,
  1.1253 +                               mInfo.mArgc,
  1.1254 +                               mInfo.mArgv,
  1.1255 +                               mInfo.mAppVersion.get(),
  1.1256 +                               false,
  1.1257 +                               mInfo.mIsOSUpdate,
  1.1258 +                               mInfo.mOSApplyToDir,
  1.1259 +                               &mUpdaterPID);
  1.1260 +  NS_ENSURE_SUCCESS_VOID(rv);
  1.1261 +
  1.1262 +  if (mUpdaterPID) {
  1.1263 +    // Track the state of the updater process while it is staging an update.
  1.1264 +    rv = NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::WaitForProcess));
  1.1265 +    NS_ENSURE_SUCCESS_VOID(rv);
  1.1266 +  } else {
  1.1267 +    // Failed to launch the updater process for some reason.
  1.1268 +    // We need to shutdown the current thread as there isn't anything more for
  1.1269 +    // us to do...
  1.1270 +    rv = NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::ShutdownWatcherThread));
  1.1271 +    NS_ENSURE_SUCCESS_VOID(rv);
  1.1272 +  }
  1.1273 +}
  1.1274 +
  1.1275 +void
  1.1276 +nsUpdateProcessor::ShutdownWatcherThread()
  1.1277 +{
  1.1278 +  NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
  1.1279 +  mProcessWatcher->Shutdown();
  1.1280 +  mProcessWatcher = nullptr;
  1.1281 +  mUpdate = nullptr;
  1.1282 +}
  1.1283 +
  1.1284 +void
  1.1285 +nsUpdateProcessor::WaitForProcess()
  1.1286 +{
  1.1287 +  NS_ABORT_IF_FALSE(!NS_IsMainThread(), "main thread");
  1.1288 +  ::WaitForProcess(mUpdaterPID);
  1.1289 +  NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::UpdateDone));
  1.1290 +}
  1.1291 +
  1.1292 +void
  1.1293 +nsUpdateProcessor::UpdateDone()
  1.1294 +{
  1.1295 +  NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
  1.1296 +
  1.1297 +  nsCOMPtr<nsIUpdateManager> um =
  1.1298 +    do_GetService("@mozilla.org/updates/update-manager;1");
  1.1299 +  if (um && mUpdate) {
  1.1300 +    um->RefreshUpdateStatus(mUpdate);
  1.1301 +  }
  1.1302 +
  1.1303 +  ShutdownWatcherThread();
  1.1304 +}
  1.1305 +

mercurial