toolkit/xre/nsUpdateDriver.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include <stdlib.h>
michael@0 8 #include <stdio.h>
michael@0 9 #include "nsUpdateDriver.h"
michael@0 10 #include "nsXULAppAPI.h"
michael@0 11 #include "nsAppRunner.h"
michael@0 12 #include "nsIWritablePropertyBag.h"
michael@0 13 #include "nsIFile.h"
michael@0 14 #include "nsIVariant.h"
michael@0 15 #include "nsCOMPtr.h"
michael@0 16 #include "nsString.h"
michael@0 17 #include "prproces.h"
michael@0 18 #include "prlog.h"
michael@0 19 #include "prenv.h"
michael@0 20 #include "nsVersionComparator.h"
michael@0 21 #include "nsXREDirProvider.h"
michael@0 22 #include "SpecialSystemDirectory.h"
michael@0 23 #include "nsDirectoryServiceDefs.h"
michael@0 24 #include "nsThreadUtils.h"
michael@0 25 #include "nsIXULAppInfo.h"
michael@0 26 #include "mozilla/Preferences.h"
michael@0 27 #include "nsPrintfCString.h"
michael@0 28 #include "mozilla/DebugOnly.h"
michael@0 29
michael@0 30 #ifdef XP_MACOSX
michael@0 31 #include "nsILocalFileMac.h"
michael@0 32 #include "nsCommandLineServiceMac.h"
michael@0 33 #include "MacLaunchHelper.h"
michael@0 34 #endif
michael@0 35
michael@0 36 #if defined(XP_WIN)
michael@0 37 # include <direct.h>
michael@0 38 # include <process.h>
michael@0 39 # include <windows.h>
michael@0 40 # include <shlwapi.h>
michael@0 41 # include "nsWindowsHelpers.h"
michael@0 42 # include "prprf.h"
michael@0 43 # define getcwd(path, size) _getcwd(path, size)
michael@0 44 # define getpid() GetCurrentProcessId()
michael@0 45 #elif defined(XP_UNIX)
michael@0 46 # include <unistd.h>
michael@0 47 #endif
michael@0 48
michael@0 49 using namespace mozilla;
michael@0 50
michael@0 51 //
michael@0 52 // We use execv to spawn the updater process on all UNIX systems except Mac OSX
michael@0 53 // since it is known to cause problems on the Mac. Windows has execv, but it
michael@0 54 // is a faked implementation that doesn't really replace the current process.
michael@0 55 // Instead it spawns a new process, so we gain nothing from using execv on
michael@0 56 // Windows.
michael@0 57 //
michael@0 58 // On platforms where we are not calling execv, we may need to make the
michael@0 59 // updater executable wait for the calling process to exit. Otherwise, the
michael@0 60 // updater may have trouble modifying our executable image (because it might
michael@0 61 // still be in use). This is accomplished by passing our PID to the updater so
michael@0 62 // that it can wait for us to exit. This is not perfect as there is a race
michael@0 63 // condition that could bite us. It's possible that the calling process could
michael@0 64 // exit before the updater waits on the specified PID, and in the meantime a
michael@0 65 // new process with the same PID could be created. This situation is unlikely,
michael@0 66 // however, given the way most operating systems recycle PIDs. We'll take our
michael@0 67 // chances ;-)
michael@0 68 //
michael@0 69 // A similar #define lives in updater.cpp and should be kept in sync with this.
michael@0 70 //
michael@0 71 #if defined(XP_UNIX) && !defined(XP_MACOSX)
michael@0 72 #define USE_EXECV
michael@0 73 #endif
michael@0 74
michael@0 75 #ifdef PR_LOGGING
michael@0 76 static PRLogModuleInfo *
michael@0 77 GetUpdateLog()
michael@0 78 {
michael@0 79 static PRLogModuleInfo *sUpdateLog;
michael@0 80 if (!sUpdateLog)
michael@0 81 sUpdateLog = PR_NewLogModule("updatedriver");
michael@0 82 return sUpdateLog;
michael@0 83 }
michael@0 84 #endif
michael@0 85 #define LOG(args) PR_LOG(GetUpdateLog(), PR_LOG_DEBUG, args)
michael@0 86
michael@0 87 #ifdef XP_WIN
michael@0 88 static const char kUpdaterBin[] = "updater.exe";
michael@0 89 #else
michael@0 90 static const char kUpdaterBin[] = "updater";
michael@0 91 #endif
michael@0 92 static const char kUpdaterINI[] = "updater.ini";
michael@0 93 #ifdef XP_MACOSX
michael@0 94 static const char kUpdaterApp[] = "updater.app";
michael@0 95 #endif
michael@0 96 #if defined(XP_UNIX) && !defined(XP_MACOSX)
michael@0 97 static const char kUpdaterPNG[] = "updater.png";
michael@0 98 #endif
michael@0 99
michael@0 100 #if defined(MOZ_WIDGET_GONK)
michael@0 101 #include <linux/ioprio.h>
michael@0 102
michael@0 103 static const int kB2GServiceArgc = 2;
michael@0 104 static const char *kB2GServiceArgv[] = { "/system/bin/start", "b2g" };
michael@0 105
michael@0 106 static const char kAppUpdaterPrio[] = "app.update.updater.prio";
michael@0 107 static const char kAppUpdaterOomScoreAdj[] = "app.update.updater.oom_score_adj";
michael@0 108 static const char kAppUpdaterIOPrioClass[] = "app.update.updater.ioprio.class";
michael@0 109 static const char kAppUpdaterIOPrioLevel[] = "app.update.updater.ioprio.level";
michael@0 110
michael@0 111 static const int kAppUpdaterPrioDefault = 19; // -20..19 where 19 = lowest priority
michael@0 112 static const int kAppUpdaterOomScoreAdjDefault = -1000; // -1000 = Never kill
michael@0 113 static const int kAppUpdaterIOPrioClassDefault = IOPRIO_CLASS_IDLE;
michael@0 114 static const int kAppUpdaterIOPrioLevelDefault = 0; // Doesn't matter for CLASS IDLE
michael@0 115 #endif
michael@0 116
michael@0 117 static nsresult
michael@0 118 GetCurrentWorkingDir(char *buf, size_t size)
michael@0 119 {
michael@0 120 // Cannot use NS_GetSpecialDirectory because XPCOM is not yet initialized.
michael@0 121 // This code is duplicated from xpcom/io/SpecialSystemDirectory.cpp:
michael@0 122
michael@0 123 #if defined(XP_WIN)
michael@0 124 wchar_t wpath[MAX_PATH];
michael@0 125 if (!_wgetcwd(wpath, size))
michael@0 126 return NS_ERROR_FAILURE;
michael@0 127 NS_ConvertUTF16toUTF8 path(wpath);
michael@0 128 strncpy(buf, path.get(), size);
michael@0 129 #else
michael@0 130 if(!getcwd(buf, size))
michael@0 131 return NS_ERROR_FAILURE;
michael@0 132 #endif
michael@0 133 return NS_OK;
michael@0 134 }
michael@0 135
michael@0 136
michael@0 137 #if defined(XP_WIN)
michael@0 138 #define PATH_SEPARATOR ";"
michael@0 139
michael@0 140 // In Tor Browser, updater.exe depends on some DLLs that are located in the
michael@0 141 // app directory. To allow the updater to run when it has been copied into
michael@0 142 // the update directory, we append the app directory to the PATH.
michael@0 143 static nsresult
michael@0 144 AdjustPathForUpdater(nsIFile *appDir)
michael@0 145 {
michael@0 146 nsAutoCString appPath;
michael@0 147 nsresult rv = appDir->GetNativePath(appPath);
michael@0 148 NS_ENSURE_SUCCESS(rv, rv);
michael@0 149
michael@0 150 char *s = nullptr;
michael@0 151 char *pathValue = PR_GetEnv("PATH");
michael@0 152 if ((nullptr == pathValue) || ('\0' == *pathValue)) {
michael@0 153 s = PR_smprintf("PATH=%s", appPath.get());
michael@0 154 } else {
michael@0 155 s = PR_smprintf("PATH=%s" PATH_SEPARATOR "%s", pathValue, appPath.get());
michael@0 156 }
michael@0 157
michael@0 158 // We intentionally leak the value that is passed into PR_SetEnv() because
michael@0 159 // the environment will hold a pointer to it.
michael@0 160 if ((nullptr == s) || (PR_SUCCESS != PR_SetEnv(s)))
michael@0 161 return NS_ERROR_FAILURE;
michael@0 162
michael@0 163 return NS_OK;
michael@0 164 }
michael@0 165 #endif
michael@0 166
michael@0 167 #ifdef DEBUG
michael@0 168 static void
michael@0 169 dump_argv(const char *aPrefix, char **argv, int argc)
michael@0 170 {
michael@0 171 printf("%s - %d args\n", aPrefix, argc);
michael@0 172 for (int i = 0; i < argc; ++i)
michael@0 173 printf(" %d: %s\n", i, argv[i]);
michael@0 174 }
michael@0 175 #endif
michael@0 176
michael@0 177
michael@0 178 #if defined(XP_MACOSX)
michael@0 179 // This is a copy of OS X's XRE_GetBinaryPath from nsAppRunner.cpp with the
michael@0 180 // gBinaryPath check removed so that the updater can reload the stub executable
michael@0 181 // instead of xulrunner-bin. See bug 349737.
michael@0 182 static nsresult
michael@0 183 GetXULRunnerStubPath(const char* argv0, nsIFile* *aResult)
michael@0 184 {
michael@0 185 // Works even if we're not bundled.
michael@0 186 CFBundleRef appBundle = ::CFBundleGetMainBundle();
michael@0 187 if (!appBundle)
michael@0 188 return NS_ERROR_FAILURE;
michael@0 189
michael@0 190 CFURLRef bundleURL = ::CFBundleCopyExecutableURL(appBundle);
michael@0 191 if (!bundleURL)
michael@0 192 return NS_ERROR_FAILURE;
michael@0 193
michael@0 194 nsCOMPtr<nsILocalFileMac> lfm;
michael@0 195 nsresult rv = NS_NewLocalFileWithCFURL(bundleURL, true, getter_AddRefs(lfm));
michael@0 196
michael@0 197 ::CFRelease(bundleURL);
michael@0 198
michael@0 199 if (NS_FAILED(rv))
michael@0 200 return rv;
michael@0 201
michael@0 202 NS_ADDREF(*aResult = static_cast<nsIFile*>(lfm.get()));
michael@0 203 return NS_OK;
michael@0 204 }
michael@0 205 #endif /* XP_MACOSX */
michael@0 206
michael@0 207 static bool
michael@0 208 GetFile(nsIFile *dir, const nsCSubstring &name, nsCOMPtr<nsIFile> &result)
michael@0 209 {
michael@0 210 nsresult rv;
michael@0 211
michael@0 212 nsCOMPtr<nsIFile> file;
michael@0 213 rv = dir->Clone(getter_AddRefs(file));
michael@0 214 if (NS_FAILED(rv))
michael@0 215 return false;
michael@0 216
michael@0 217 rv = file->AppendNative(name);
michael@0 218 if (NS_FAILED(rv))
michael@0 219 return false;
michael@0 220
michael@0 221 result = do_QueryInterface(file, &rv);
michael@0 222 return NS_SUCCEEDED(rv);
michael@0 223 }
michael@0 224
michael@0 225 static bool
michael@0 226 GetStatusFile(nsIFile *dir, nsCOMPtr<nsIFile> &result)
michael@0 227 {
michael@0 228 return GetFile(dir, NS_LITERAL_CSTRING("update.status"), result);
michael@0 229 }
michael@0 230
michael@0 231 /**
michael@0 232 * Get the contents of the update.status file.
michael@0 233 *
michael@0 234 * @param statusFile the status file object.
michael@0 235 * @param buf the buffer holding the file contents
michael@0 236 *
michael@0 237 * @return true if successful, false otherwise.
michael@0 238 */
michael@0 239 template <size_t Size>
michael@0 240 static bool
michael@0 241 GetStatusFileContents(nsIFile *statusFile, char (&buf)[Size])
michael@0 242 {
michael@0 243 // The buffer needs to be large enough to hold the known status codes
michael@0 244 PR_STATIC_ASSERT(Size > 16);
michael@0 245
michael@0 246 PRFileDesc *fd = nullptr;
michael@0 247 nsresult rv = statusFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd);
michael@0 248 if (NS_FAILED(rv))
michael@0 249 return false;
michael@0 250
michael@0 251 const int32_t n = PR_Read(fd, buf, Size);
michael@0 252 PR_Close(fd);
michael@0 253
michael@0 254 return (n >= 0);
michael@0 255 }
michael@0 256
michael@0 257 typedef enum {
michael@0 258 eNoUpdateAction,
michael@0 259 ePendingUpdate,
michael@0 260 ePendingService,
michael@0 261 eAppliedUpdate,
michael@0 262 eAppliedService
michael@0 263 } UpdateStatus;
michael@0 264
michael@0 265 /**
michael@0 266 * Returns a value indicating what needs to be done in order to handle an update.
michael@0 267 *
michael@0 268 * @param dir the directory in which we should look for an update.status file.
michael@0 269 * @param statusFile the update.status file found in the directory.
michael@0 270 *
michael@0 271 * @return the update action to be performed.
michael@0 272 */
michael@0 273 static UpdateStatus
michael@0 274 GetUpdateStatus(nsIFile* dir, nsCOMPtr<nsIFile> &statusFile)
michael@0 275 {
michael@0 276 if (GetStatusFile(dir, statusFile)) {
michael@0 277 char buf[32];
michael@0 278 if (GetStatusFileContents(statusFile, buf)) {
michael@0 279 const char kPending[] = "pending";
michael@0 280 const char kPendingService[] = "pending-service";
michael@0 281 const char kApplied[] = "applied";
michael@0 282 const char kAppliedService[] = "applied-service";
michael@0 283 if (!strncmp(buf, kPendingService, sizeof(kPendingService) - 1)) {
michael@0 284 return ePendingService;
michael@0 285 }
michael@0 286 if (!strncmp(buf, kPending, sizeof(kPending) - 1)) {
michael@0 287 return ePendingUpdate;
michael@0 288 }
michael@0 289 if (!strncmp(buf, kAppliedService, sizeof(kAppliedService) - 1)) {
michael@0 290 return eAppliedService;
michael@0 291 }
michael@0 292 if (!strncmp(buf, kApplied, sizeof(kApplied) - 1)) {
michael@0 293 return eAppliedUpdate;
michael@0 294 }
michael@0 295 }
michael@0 296 }
michael@0 297 return eNoUpdateAction;
michael@0 298 }
michael@0 299
michael@0 300 static bool
michael@0 301 GetVersionFile(nsIFile *dir, nsCOMPtr<nsIFile> &result)
michael@0 302 {
michael@0 303 return GetFile(dir, NS_LITERAL_CSTRING("update.version"), result);
michael@0 304 }
michael@0 305
michael@0 306 // Compares the current application version with the update's application
michael@0 307 // version.
michael@0 308 static bool
michael@0 309 IsOlderVersion(nsIFile *versionFile, const char *appVersion)
michael@0 310 {
michael@0 311 PRFileDesc *fd = nullptr;
michael@0 312 nsresult rv = versionFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd);
michael@0 313 if (NS_FAILED(rv))
michael@0 314 return true;
michael@0 315
michael@0 316 char buf[32];
michael@0 317 const int32_t n = PR_Read(fd, buf, sizeof(buf));
michael@0 318 PR_Close(fd);
michael@0 319
michael@0 320 if (n < 0)
michael@0 321 return false;
michael@0 322
michael@0 323 // Trim off the trailing newline
michael@0 324 if (buf[n - 1] == '\n')
michael@0 325 buf[n - 1] = '\0';
michael@0 326
michael@0 327 // If the update xml doesn't provide the application version the file will
michael@0 328 // contain the string "null" and it is assumed that the update is not older.
michael@0 329 const char kNull[] = "null";
michael@0 330 if (strncmp(buf, kNull, sizeof(kNull) - 1) == 0)
michael@0 331 return false;
michael@0 332
michael@0 333 #ifdef DEBUG
michael@0 334 printf("IsOlderVersion checking appVersion %s against updateVersion %s\n",
michael@0 335 appVersion, buf);
michael@0 336 #endif
michael@0 337 if (mozilla::Version(appVersion) > buf)
michael@0 338 return true;
michael@0 339
michael@0 340 return false;
michael@0 341 }
michael@0 342
michael@0 343 #if defined(XP_WIN) && defined(MOZ_METRO)
michael@0 344 static bool
michael@0 345 IsWindowsMetroUpdateRequest(int appArgc, char **appArgv)
michael@0 346 {
michael@0 347 for (int index = 0; index < appArgc; index++) {
michael@0 348 if (!strcmp(appArgv[index], "--metro-update")) {
michael@0 349 return true;
michael@0 350 }
michael@0 351 }
michael@0 352 return false;
michael@0 353 }
michael@0 354 #endif
michael@0 355
michael@0 356 static bool
michael@0 357 CopyFileIntoUpdateDir(nsIFile *parentDir, const char *leafName, nsIFile *updateDir)
michael@0 358 {
michael@0 359 nsDependentCString leaf(leafName);
michael@0 360 nsCOMPtr<nsIFile> file;
michael@0 361
michael@0 362 // Make sure there is not an existing file in the target location.
michael@0 363 nsresult rv = updateDir->Clone(getter_AddRefs(file));
michael@0 364 if (NS_FAILED(rv))
michael@0 365 return false;
michael@0 366 rv = file->AppendNative(leaf);
michael@0 367 if (NS_FAILED(rv))
michael@0 368 return false;
michael@0 369 file->Remove(true);
michael@0 370
michael@0 371 // Now, copy into the target location.
michael@0 372 rv = parentDir->Clone(getter_AddRefs(file));
michael@0 373 if (NS_FAILED(rv))
michael@0 374 return false;
michael@0 375 rv = file->AppendNative(leaf);
michael@0 376 if (NS_FAILED(rv))
michael@0 377 return false;
michael@0 378 rv = file->CopyToNative(updateDir, EmptyCString());
michael@0 379 if (NS_FAILED(rv))
michael@0 380 return false;
michael@0 381
michael@0 382 return true;
michael@0 383 }
michael@0 384
michael@0 385 static bool
michael@0 386 CopyUpdaterIntoUpdateDir(nsIFile *greDir, nsIFile *appDir, nsIFile *updateDir,
michael@0 387 nsCOMPtr<nsIFile> &updater)
michael@0 388 {
michael@0 389 // Copy the updater application from the GRE and the updater ini from the app
michael@0 390 #if defined(XP_MACOSX)
michael@0 391 if (!CopyFileIntoUpdateDir(greDir, kUpdaterApp, updateDir))
michael@0 392 return false;
michael@0 393 #else
michael@0 394 if (!CopyFileIntoUpdateDir(greDir, kUpdaterBin, updateDir))
michael@0 395 return false;
michael@0 396 #endif
michael@0 397 CopyFileIntoUpdateDir(appDir, kUpdaterINI, updateDir);
michael@0 398 #if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(ANDROID)
michael@0 399 nsCOMPtr<nsIFile> iconDir;
michael@0 400 appDir->Clone(getter_AddRefs(iconDir));
michael@0 401 iconDir->AppendNative(NS_LITERAL_CSTRING("icons"));
michael@0 402 if (!CopyFileIntoUpdateDir(iconDir, kUpdaterPNG, updateDir))
michael@0 403 return false;
michael@0 404 #endif
michael@0 405 // Finally, return the location of the updater binary.
michael@0 406 nsresult rv = updateDir->Clone(getter_AddRefs(updater));
michael@0 407 if (NS_FAILED(rv))
michael@0 408 return false;
michael@0 409 #if defined(XP_MACOSX)
michael@0 410 rv = updater->AppendNative(NS_LITERAL_CSTRING(kUpdaterApp));
michael@0 411 nsresult tmp = updater->AppendNative(NS_LITERAL_CSTRING("Contents"));
michael@0 412 if (NS_FAILED(tmp)) {
michael@0 413 rv = tmp;
michael@0 414 }
michael@0 415 tmp = updater->AppendNative(NS_LITERAL_CSTRING("MacOS"));
michael@0 416 if (NS_FAILED(tmp) || NS_FAILED(rv))
michael@0 417 return false;
michael@0 418 #endif
michael@0 419 rv = updater->AppendNative(NS_LITERAL_CSTRING(kUpdaterBin));
michael@0 420 return NS_SUCCEEDED(rv);
michael@0 421 }
michael@0 422
michael@0 423 /**
michael@0 424 * Switch an existing application directory to an updated version that has been
michael@0 425 * staged.
michael@0 426 *
michael@0 427 * @param greDir the GRE dir
michael@0 428 * @param updateDir the update root dir
michael@0 429 * @param statusFile the update.status file
michael@0 430 * @param appDir the app dir
michael@0 431 * @param appArgc the number of args to the application
michael@0 432 * @param appArgv the args to the application, used for restarting if needed
michael@0 433 */
michael@0 434 static void
michael@0 435 SwitchToUpdatedApp(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile,
michael@0 436 nsIFile *appDir, int appArgc, char **appArgv)
michael@0 437 {
michael@0 438 nsresult rv;
michael@0 439
michael@0 440 // Steps:
michael@0 441 // - copy updater into temp dir
michael@0 442 // - run updater with the correct arguments
michael@0 443
michael@0 444 nsCOMPtr<nsIFile> tmpDir;
michael@0 445 GetSpecialSystemDirectory(OS_TemporaryDirectory,
michael@0 446 getter_AddRefs(tmpDir));
michael@0 447 if (!tmpDir) {
michael@0 448 LOG(("failed getting a temp dir\n"));
michael@0 449 return;
michael@0 450 }
michael@0 451
michael@0 452 // Try to create our own new temp directory in case there is already an
michael@0 453 // updater binary in the OS temporary location which we cannot write to.
michael@0 454 // Note that we don't check for errors here, as if this directory can't
michael@0 455 // be created, the following CopyUpdaterIntoUpdateDir call will fail.
michael@0 456 // We create the unique directory inside a subfolder of MozUpdater instead
michael@0 457 // of directly in the temp directory so we can efficiently delete everything
michael@0 458 // after updates.
michael@0 459 tmpDir->Append(NS_LITERAL_STRING("MozUpdater"));
michael@0 460 tmpDir->Append(NS_LITERAL_STRING("bgupdate"));
michael@0 461 tmpDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0755);
michael@0 462
michael@0 463 nsCOMPtr<nsIFile> updater;
michael@0 464 if (!CopyUpdaterIntoUpdateDir(greDir, appDir, tmpDir, updater)) {
michael@0 465 LOG(("failed copying updater\n"));
michael@0 466 return;
michael@0 467 }
michael@0 468
michael@0 469 // We need to use the value returned from XRE_GetBinaryPath when attempting
michael@0 470 // to restart the running application.
michael@0 471 nsCOMPtr<nsIFile> appFile;
michael@0 472
michael@0 473 #if defined(XP_MACOSX)
michael@0 474 // On OS X we need to pass the location of the xulrunner-stub executable
michael@0 475 // rather than xulrunner-bin. See bug 349737.
michael@0 476 GetXULRunnerStubPath(appArgv[0], getter_AddRefs(appFile));
michael@0 477 #else
michael@0 478 XRE_GetBinaryPath(appArgv[0], getter_AddRefs(appFile));
michael@0 479 #endif
michael@0 480
michael@0 481 if (!appFile)
michael@0 482 return;
michael@0 483
michael@0 484 #ifdef XP_WIN
michael@0 485 nsAutoString appFilePathW;
michael@0 486 rv = appFile->GetPath(appFilePathW);
michael@0 487 if (NS_FAILED(rv))
michael@0 488 return;
michael@0 489 NS_ConvertUTF16toUTF8 appFilePath(appFilePathW);
michael@0 490
michael@0 491 nsAutoString updaterPathW;
michael@0 492 rv = updater->GetPath(updaterPathW);
michael@0 493 if (NS_FAILED(rv))
michael@0 494 return;
michael@0 495
michael@0 496 NS_ConvertUTF16toUTF8 updaterPath(updaterPathW);
michael@0 497 #else
michael@0 498
michael@0 499 nsAutoCString appFilePath;
michael@0 500 #if defined(MOZ_WIDGET_GONK)
michael@0 501 appFilePath.Assign(kB2GServiceArgv[0]);
michael@0 502 appArgc = kB2GServiceArgc;
michael@0 503 appArgv = const_cast<char**>(kB2GServiceArgv);
michael@0 504 #else
michael@0 505 rv = appFile->GetNativePath(appFilePath);
michael@0 506 if (NS_FAILED(rv))
michael@0 507 return;
michael@0 508 #endif
michael@0 509
michael@0 510 nsAutoCString updaterPath;
michael@0 511 rv = updater->GetNativePath(updaterPath);
michael@0 512 if (NS_FAILED(rv))
michael@0 513 return;
michael@0 514 #endif
michael@0 515
michael@0 516 // Get the directory to which the update will be applied. On Mac OSX we need
michael@0 517 // to apply the update to the Updated.app directory under the Foo.app
michael@0 518 // directory which is the parent of the parent of the appDir. On other
michael@0 519 // platforms we will just apply to the appDir/updated.
michael@0 520 nsCOMPtr<nsIFile> updatedDir;
michael@0 521 #if defined(XP_MACOSX)
michael@0 522 nsAutoCString applyToDir;
michael@0 523 {
michael@0 524 nsCOMPtr<nsIFile> parentDir1, parentDir2;
michael@0 525 rv = appDir->GetParent(getter_AddRefs(parentDir1));
michael@0 526 if (NS_FAILED(rv))
michael@0 527 return;
michael@0 528 rv = parentDir1->GetParent(getter_AddRefs(parentDir2));
michael@0 529 if (NS_FAILED(rv))
michael@0 530 return;
michael@0 531 if (!GetFile(parentDir2, NS_LITERAL_CSTRING("Updated.app"), updatedDir))
michael@0 532 return;
michael@0 533 rv = updatedDir->GetNativePath(applyToDir);
michael@0 534 }
michael@0 535 #else
michael@0 536 if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir))
michael@0 537 return;
michael@0 538 #if defined(XP_WIN)
michael@0 539 nsAutoString applyToDirW;
michael@0 540 rv = updatedDir->GetPath(applyToDirW);
michael@0 541
michael@0 542 NS_ConvertUTF16toUTF8 applyToDir(applyToDirW);
michael@0 543 #else
michael@0 544 nsAutoCString applyToDir;
michael@0 545 rv = updatedDir->GetNativePath(applyToDir);
michael@0 546 #endif
michael@0 547 #endif
michael@0 548 if (NS_FAILED(rv))
michael@0 549 return;
michael@0 550
michael@0 551 // Make sure that the updated directory exists
michael@0 552 bool updatedDirExists = false;
michael@0 553 updatedDir->Exists(&updatedDirExists);
michael@0 554 if (!updatedDirExists) {
michael@0 555 return;
michael@0 556 }
michael@0 557
michael@0 558 #if defined(XP_WIN)
michael@0 559 nsAutoString updateDirPathW;
michael@0 560 rv = updateDir->GetPath(updateDirPathW);
michael@0 561
michael@0 562 NS_ConvertUTF16toUTF8 updateDirPath(updateDirPathW);
michael@0 563 #else
michael@0 564 nsAutoCString updateDirPath;
michael@0 565 rv = updateDir->GetNativePath(updateDirPath);
michael@0 566 #endif
michael@0 567
michael@0 568 if (NS_FAILED(rv))
michael@0 569 return;
michael@0 570
michael@0 571 // Get the current working directory.
michael@0 572 char workingDirPath[MAXPATHLEN];
michael@0 573 rv = GetCurrentWorkingDir(workingDirPath, sizeof(workingDirPath));
michael@0 574 if (NS_FAILED(rv))
michael@0 575 return;
michael@0 576
michael@0 577 // Construct the PID argument for this process. If we are using execv, then
michael@0 578 // we pass "0" which is then ignored by the updater.
michael@0 579 #if defined(USE_EXECV)
michael@0 580 nsAutoCString pid("0");
michael@0 581 #else
michael@0 582 nsAutoCString pid;
michael@0 583 pid.AppendInt((int32_t) getpid());
michael@0 584 #endif
michael@0 585
michael@0 586 // Append a special token to the PID in order to let the updater know that it
michael@0 587 // just needs to replace the update directory.
michael@0 588 pid.AppendLiteral("/replace");
michael@0 589
michael@0 590 int immersiveArgc = 0;
michael@0 591 #if defined(XP_WIN) && defined(MOZ_METRO)
michael@0 592 // If this is desktop doing an update for metro, or if we're the metro browser
michael@0 593 // we want to launch the metro browser after we're finished.
michael@0 594 if (IsWindowsMetroUpdateRequest(appArgc, appArgv) || IsRunningInWindowsMetro()) {
michael@0 595 immersiveArgc = 1;
michael@0 596 }
michael@0 597 #endif
michael@0 598 int argc = appArgc + 5 + immersiveArgc;
michael@0 599 char **argv = new char*[argc + 1];
michael@0 600 if (!argv)
michael@0 601 return;
michael@0 602 argv[0] = (char*) updaterPath.get();
michael@0 603 argv[1] = (char*) updateDirPath.get();
michael@0 604 argv[2] = (char*) applyToDir.get();
michael@0 605 argv[3] = (char*) pid.get();
michael@0 606 if (appArgc) {
michael@0 607 argv[4] = workingDirPath;
michael@0 608 argv[5] = (char*) appFilePath.get();
michael@0 609 for (int i = 1; i < appArgc; ++i)
michael@0 610 argv[5 + i] = appArgv[i];
michael@0 611 #ifdef XP_WIN
michael@0 612 if (immersiveArgc) {
michael@0 613 argv[argc - 1] = "-ServerName:DefaultBrowserServer";
michael@0 614 }
michael@0 615 #endif
michael@0 616 argv[argc] = nullptr;
michael@0 617 } else {
michael@0 618 argc = 4;
michael@0 619 argv[4] = nullptr;
michael@0 620 }
michael@0 621
michael@0 622 if (gSafeMode) {
michael@0 623 PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
michael@0 624 }
michael@0 625
michael@0 626 #if defined(XP_WIN)
michael@0 627 nsresult rv2 = AdjustPathForUpdater(appDir);
michael@0 628 if (NS_FAILED(rv2)) {
michael@0 629 LOG(("SwitchToUpdatedApp -- AdjustPathForUpdater failed (0x%x)\n", rv2));
michael@0 630 }
michael@0 631 #endif
michael@0 632
michael@0 633 LOG(("spawning updater process for replacing [%s]\n", updaterPath.get()));
michael@0 634
michael@0 635 #if defined(USE_EXECV)
michael@0 636 # if defined(MOZ_WIDGET_GONK)
michael@0 637 // In Gonk, we preload libmozglue, which the updater process doesn't need.
michael@0 638 // Since the updater will move and delete libmozglue.so, this can actually
michael@0 639 // stop the /system mount from correctly being remounted as read-only.
michael@0 640 unsetenv("LD_PRELOAD");
michael@0 641 # endif
michael@0 642 execv(updaterPath.get(), argv);
michael@0 643 #elif defined(XP_WIN)
michael@0 644 // Switch the application using updater.exe
michael@0 645 if (!WinLaunchChild(updaterPathW.get(), argc, argv)) {
michael@0 646 return;
michael@0 647 }
michael@0 648 _exit(0);
michael@0 649 #elif defined(XP_MACOSX)
michael@0 650 CommandLineServiceMac::SetupMacCommandLine(argc, argv, true);
michael@0 651 // LaunchChildMac uses posix_spawnp and prefers the current
michael@0 652 // architecture when launching. It doesn't require a
michael@0 653 // null-terminated string but it doesn't matter if we pass one.
michael@0 654 LaunchChildMac(argc, argv);
michael@0 655 exit(0);
michael@0 656 #else
michael@0 657 PR_CreateProcessDetached(updaterPath.get(), argv, nullptr, nullptr);
michael@0 658 exit(0);
michael@0 659 #endif
michael@0 660 }
michael@0 661
michael@0 662 #if defined(MOZ_WIDGET_GONK)
michael@0 663 static nsresult
michael@0 664 GetOSApplyToDir(nsACString& applyToDir)
michael@0 665 {
michael@0 666 nsCOMPtr<nsIProperties> ds =
michael@0 667 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
michael@0 668 NS_ASSERTION(ds, "Can't get directory service");
michael@0 669
michael@0 670 nsCOMPtr<nsIFile> osApplyToDir;
michael@0 671 nsresult rv = ds->Get(XRE_OS_UPDATE_APPLY_TO_DIR, NS_GET_IID(nsIFile),
michael@0 672 getter_AddRefs(osApplyToDir));
michael@0 673 if (NS_FAILED(rv)) {
michael@0 674 LOG(("Can't get the OS applyTo dir"));
michael@0 675 return rv;
michael@0 676 }
michael@0 677
michael@0 678 return osApplyToDir->GetNativePath(applyToDir);
michael@0 679 }
michael@0 680
michael@0 681 static void
michael@0 682 SetOSApplyToDir(nsIUpdate* update, const nsACString& osApplyToDir)
michael@0 683 {
michael@0 684 nsresult rv;
michael@0 685 nsCOMPtr<nsIWritablePropertyBag> updateProperties =
michael@0 686 do_QueryInterface(update, &rv);
michael@0 687
michael@0 688 if (NS_FAILED(rv)) {
michael@0 689 return;
michael@0 690 }
michael@0 691
michael@0 692 nsCOMPtr<nsIWritableVariant> variant =
michael@0 693 do_CreateInstance("@mozilla.org/variant;1", &rv);
michael@0 694 if (NS_FAILED(rv)) {
michael@0 695 return;
michael@0 696 }
michael@0 697
michael@0 698 rv = variant->SetAsACString(osApplyToDir);
michael@0 699 if (NS_FAILED(rv)) {
michael@0 700 return;
michael@0 701 }
michael@0 702
michael@0 703 updateProperties->SetProperty(NS_LITERAL_STRING("osApplyToDir"), variant);
michael@0 704 }
michael@0 705 #endif
michael@0 706
michael@0 707 /**
michael@0 708 * Apply an update. This applies to both normal and staged updates.
michael@0 709 *
michael@0 710 * @param greDir the GRE dir
michael@0 711 * @param updateDir the update root dir
michael@0 712 * @param statusFile the update.status file
michael@0 713 * @param appDir the app dir
michael@0 714 * @param appArgc the number of args to the application
michael@0 715 * @param appArgv the args to the application, used for restarting if needed
michael@0 716 * @param restart if true, apply the update in the foreground and restart the
michael@0 717 * application when done. otherwise, stage the update and don't
michael@0 718 * restart the application.
michael@0 719 * @param outpid out parameter holding the handle to the updater application for
michael@0 720 * staging updates.
michael@0 721 */
michael@0 722 static void
michael@0 723 ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile,
michael@0 724 nsIFile *appDir, int appArgc, char **appArgv,
michael@0 725 bool restart, bool isOSUpdate, nsIFile *osApplyToDir,
michael@0 726 ProcessType *outpid)
michael@0 727 {
michael@0 728 nsresult rv;
michael@0 729
michael@0 730 // Steps:
michael@0 731 // - mark update as 'applying'
michael@0 732 // - copy updater into update dir
michael@0 733 // - run updater w/ appDir as the current working dir
michael@0 734
michael@0 735 nsCOMPtr<nsIFile> updater;
michael@0 736 if (!CopyUpdaterIntoUpdateDir(greDir, appDir, updateDir, updater)) {
michael@0 737 LOG(("failed copying updater\n"));
michael@0 738 return;
michael@0 739 }
michael@0 740
michael@0 741 // We need to use the value returned from XRE_GetBinaryPath when attempting
michael@0 742 // to restart the running application.
michael@0 743 nsCOMPtr<nsIFile> appFile;
michael@0 744
michael@0 745 #if defined(XP_MACOSX)
michael@0 746 // On OS X we need to pass the location of the xulrunner-stub executable
michael@0 747 // rather than xulrunner-bin. See bug 349737.
michael@0 748 GetXULRunnerStubPath(appArgv[0], getter_AddRefs(appFile));
michael@0 749 #else
michael@0 750 XRE_GetBinaryPath(appArgv[0], getter_AddRefs(appFile));
michael@0 751 #endif
michael@0 752
michael@0 753 if (!appFile)
michael@0 754 return;
michael@0 755
michael@0 756 #ifdef XP_WIN
michael@0 757 nsAutoString appFilePathW;
michael@0 758 rv = appFile->GetPath(appFilePathW);
michael@0 759 if (NS_FAILED(rv))
michael@0 760 return;
michael@0 761 NS_ConvertUTF16toUTF8 appFilePath(appFilePathW);
michael@0 762
michael@0 763 nsAutoString updaterPathW;
michael@0 764 rv = updater->GetPath(updaterPathW);
michael@0 765 if (NS_FAILED(rv))
michael@0 766 return;
michael@0 767
michael@0 768 NS_ConvertUTF16toUTF8 updaterPath(updaterPathW);
michael@0 769
michael@0 770 #else
michael@0 771 nsAutoCString appFilePath;
michael@0 772 rv = appFile->GetNativePath(appFilePath);
michael@0 773 if (NS_FAILED(rv))
michael@0 774 return;
michael@0 775
michael@0 776 nsAutoCString updaterPath;
michael@0 777 rv = updater->GetNativePath(updaterPath);
michael@0 778 if (NS_FAILED(rv))
michael@0 779 return;
michael@0 780
michael@0 781 #endif
michael@0 782
michael@0 783 // Get the directory to which the update will be applied. On Mac OSX we need
michael@0 784 // to apply the update to the Updated.app directory under the Foo.app
michael@0 785 // directory which is the parent of the parent of the appDir. On other
michael@0 786 // platforms we will just apply to the appDir/updated.
michael@0 787 nsCOMPtr<nsIFile> updatedDir;
michael@0 788 #if defined(XP_MACOSX)
michael@0 789 nsAutoCString applyToDir;
michael@0 790 {
michael@0 791 nsCOMPtr<nsIFile> parentDir1, parentDir2;
michael@0 792 rv = appDir->GetParent(getter_AddRefs(parentDir1));
michael@0 793 if (NS_FAILED(rv))
michael@0 794 return;
michael@0 795 rv = parentDir1->GetParent(getter_AddRefs(parentDir2));
michael@0 796 if (NS_FAILED(rv))
michael@0 797 return;
michael@0 798 if (restart) {
michael@0 799 // Use the correct directory if we're not staging the update.
michael@0 800 rv = parentDir2->GetNativePath(applyToDir);
michael@0 801 } else {
michael@0 802 if (!GetFile(parentDir2, NS_LITERAL_CSTRING("Updated.app"), updatedDir))
michael@0 803 return;
michael@0 804 rv = updatedDir->GetNativePath(applyToDir);
michael@0 805 }
michael@0 806 }
michael@0 807 #else
michael@0 808 if (restart) {
michael@0 809 // Use the correct directory if we're not staging the update.
michael@0 810 updatedDir = do_QueryInterface(appDir);
michael@0 811 } else if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) {
michael@0 812 return;
michael@0 813 }
michael@0 814 #if defined(XP_WIN)
michael@0 815 nsAutoString applyToDirW;
michael@0 816 rv = updatedDir->GetPath(applyToDirW);
michael@0 817
michael@0 818 NS_ConvertUTF16toUTF8 applyToDir(applyToDirW);
michael@0 819 #else
michael@0 820 nsAutoCString applyToDir;
michael@0 821
michael@0 822 #if defined(MOZ_WIDGET_GONK)
michael@0 823 if (isOSUpdate) {
michael@0 824 if (!osApplyToDir) {
michael@0 825 return;
michael@0 826 }
michael@0 827
michael@0 828 rv = osApplyToDir->GetNativePath(applyToDir);
michael@0 829 } else {
michael@0 830 #endif // defined(MOZ_WIDGET_GONK)
michael@0 831
michael@0 832 rv = updatedDir->GetNativePath(applyToDir);
michael@0 833
michael@0 834 #if defined(MOZ_WIDGET_GONK)
michael@0 835 }
michael@0 836 #endif // defined(MOZ_WIDGET_GONK)
michael@0 837
michael@0 838 #endif // defined(XP_WIN)
michael@0 839 #endif
michael@0 840
michael@0 841 if (NS_FAILED(rv))
michael@0 842 return;
michael@0 843
michael@0 844 #if defined(XP_WIN)
michael@0 845 nsAutoString updateDirPathW;
michael@0 846 rv = updateDir->GetPath(updateDirPathW);
michael@0 847
michael@0 848 NS_ConvertUTF16toUTF8 updateDirPath(updateDirPathW);
michael@0 849 #else
michael@0 850 nsAutoCString updateDirPath;
michael@0 851 rv = updateDir->GetNativePath(updateDirPath);
michael@0 852 #endif
michael@0 853
michael@0 854 if (NS_FAILED(rv))
michael@0 855 return;
michael@0 856
michael@0 857 // Get the current working directory.
michael@0 858 char workingDirPath[MAXPATHLEN];
michael@0 859 rv = GetCurrentWorkingDir(workingDirPath, sizeof(workingDirPath));
michael@0 860 if (NS_FAILED(rv))
michael@0 861 return;
michael@0 862
michael@0 863 // We used to write out "Applying" to the update.status file here.
michael@0 864 // Instead we do this from within the updater application now.
michael@0 865 // This is so that we don't overwrite the status of pending-service
michael@0 866 // in the Windows case. This change was made for all platforms so
michael@0 867 // that it stays consistent across all OS.
michael@0 868
michael@0 869 // Construct the PID argument for this process. If we are using execv, then
michael@0 870 // we pass "0" which is then ignored by the updater.
michael@0 871 nsAutoCString pid;
michael@0 872 if (!restart) {
michael@0 873 // Signal the updater application that it should stage the update.
michael@0 874 pid.AssignASCII("-1");
michael@0 875 } else {
michael@0 876 #if defined(USE_EXECV)
michael@0 877 pid.AssignASCII("0");
michael@0 878 #else
michael@0 879 pid.AppendInt((int32_t) getpid());
michael@0 880 #endif
michael@0 881 }
michael@0 882
michael@0 883 int immersiveArgc = 0;
michael@0 884 #if defined(XP_WIN) && defined(MOZ_METRO)
michael@0 885 // If this is desktop doing an update for metro, or if we're the metro browser
michael@0 886 // we want to launch the metro browser after we're finished.
michael@0 887 if (IsWindowsMetroUpdateRequest(appArgc, appArgv) || IsRunningInWindowsMetro()) {
michael@0 888 immersiveArgc = 1;
michael@0 889 }
michael@0 890 #endif
michael@0 891 int argc = appArgc + 5 + immersiveArgc;
michael@0 892 char **argv = new char*[argc + 1 ];
michael@0 893 if (!argv)
michael@0 894 return;
michael@0 895 argv[0] = (char*) updaterPath.get();
michael@0 896 argv[1] = (char*) updateDirPath.get();
michael@0 897 argv[2] = (char*) applyToDir.get();
michael@0 898 argv[3] = (char*) pid.get();
michael@0 899 if (restart && appArgc) {
michael@0 900 argv[4] = workingDirPath;
michael@0 901 argv[5] = (char*) appFilePath.get();
michael@0 902 for (int i = 1; i < appArgc; ++i)
michael@0 903 argv[5 + i] = appArgv[i];
michael@0 904 #ifdef XP_WIN
michael@0 905 if (immersiveArgc) {
michael@0 906 argv[argc - 1] = "-ServerName:DefaultBrowserServer";
michael@0 907 }
michael@0 908 #endif
michael@0 909 argv[argc] = nullptr;
michael@0 910 } else {
michael@0 911 argc = 4;
michael@0 912 argv[4] = nullptr;
michael@0 913 }
michael@0 914
michael@0 915 if (gSafeMode) {
michael@0 916 PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
michael@0 917 }
michael@0 918
michael@0 919 if (isOSUpdate) {
michael@0 920 PR_SetEnv("MOZ_OS_UPDATE=1");
michael@0 921 }
michael@0 922
michael@0 923 #if defined(XP_WIN)
michael@0 924 nsresult rv2 = AdjustPathForUpdater(appDir);
michael@0 925 if (NS_FAILED(rv2)) {
michael@0 926 LOG(("ApplyUpdate -- AdjustPathForUpdater failed (0x%x)\n", rv2));
michael@0 927 }
michael@0 928 #endif
michael@0 929
michael@0 930 #if defined(MOZ_WIDGET_GONK)
michael@0 931 // We want the updater to be CPU friendly and not subject to being killed by
michael@0 932 // the low memory killer, so we pass in some preferences to allow it to
michael@0 933 // adjust its priority.
michael@0 934
michael@0 935 int32_t prioVal = Preferences::GetInt(kAppUpdaterPrio,
michael@0 936 kAppUpdaterPrioDefault);
michael@0 937 int32_t oomScoreAdj = Preferences::GetInt(kAppUpdaterOomScoreAdj,
michael@0 938 kAppUpdaterOomScoreAdjDefault);
michael@0 939 int32_t ioprioClass = Preferences::GetInt(kAppUpdaterIOPrioClass,
michael@0 940 kAppUpdaterIOPrioClassDefault);
michael@0 941 int32_t ioprioLevel = Preferences::GetInt(kAppUpdaterIOPrioLevel,
michael@0 942 kAppUpdaterIOPrioLevelDefault);
michael@0 943 nsPrintfCString prioEnv("MOZ_UPDATER_PRIO=%d/%d/%d/%d",
michael@0 944 prioVal, oomScoreAdj, ioprioClass, ioprioLevel);
michael@0 945 PR_SetEnv(prioEnv.get());
michael@0 946 #endif
michael@0 947
michael@0 948 LOG(("spawning updater process [%s]\n", updaterPath.get()));
michael@0 949 #ifdef DEBUG
michael@0 950 dump_argv("ApplyUpdate updater", argv, argc);
michael@0 951 #endif
michael@0 952
michael@0 953 #if defined(USE_EXECV)
michael@0 954 // Don't use execv when staging updates.
michael@0 955 if (restart) {
michael@0 956 execv(updaterPath.get(), argv);
michael@0 957 } else {
michael@0 958 *outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr);
michael@0 959 }
michael@0 960 #elif defined(XP_WIN)
michael@0 961 // Launch the update using updater.exe
michael@0 962 if (!WinLaunchChild(updaterPathW.get(), argc, argv, nullptr, outpid)) {
michael@0 963 return;
michael@0 964 }
michael@0 965
michael@0 966 if (restart) {
michael@0 967 // We are going to process an update so we should exit now
michael@0 968 _exit(0);
michael@0 969 }
michael@0 970 #elif defined(XP_MACOSX)
michael@0 971 CommandLineServiceMac::SetupMacCommandLine(argc, argv, true);
michael@0 972 // LaunchChildMac uses posix_spawnp and prefers the current
michael@0 973 // architecture when launching. It doesn't require a
michael@0 974 // null-terminated string but it doesn't matter if we pass one.
michael@0 975 #ifdef DEBUG
michael@0 976 dump_argv("ApplyUpdate after SetupMacCommandLine", argv, argc);
michael@0 977 #endif
michael@0 978 LaunchChildMac(argc, argv, 0, outpid);
michael@0 979 if (restart) {
michael@0 980 exit(0);
michael@0 981 }
michael@0 982 #else
michael@0 983 *outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr);
michael@0 984 if (restart) {
michael@0 985 exit(0);
michael@0 986 }
michael@0 987 #endif
michael@0 988 }
michael@0 989
michael@0 990 /**
michael@0 991 * Wait for a process until it terminates. This call is blocking.
michael@0 992 */
michael@0 993 static void
michael@0 994 WaitForProcess(ProcessType pt)
michael@0 995 {
michael@0 996 #if defined(XP_WIN)
michael@0 997 WaitForSingleObject(pt, INFINITE);
michael@0 998 CloseHandle(pt);
michael@0 999 #elif defined(XP_MACOSX)
michael@0 1000 waitpid(pt, 0, 0);
michael@0 1001 #else
michael@0 1002 int32_t exitCode;
michael@0 1003 PR_WaitProcess(pt, &exitCode);
michael@0 1004 if (exitCode != 0) {
michael@0 1005 LOG(("Error while running the updater process, check update.log"));
michael@0 1006 }
michael@0 1007 #endif
michael@0 1008 }
michael@0 1009
michael@0 1010 nsresult
michael@0 1011 ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir,
michael@0 1012 int argc, char **argv, const char *appVersion,
michael@0 1013 bool restart, bool isOSUpdate, nsIFile *osApplyToDir,
michael@0 1014 ProcessType *pid)
michael@0 1015 {
michael@0 1016 nsresult rv;
michael@0 1017
michael@0 1018 nsCOMPtr<nsIFile> updatesDir;
michael@0 1019 #ifdef DEBUG
michael@0 1020 nsAutoCString path;
michael@0 1021 updRootDir->GetNativePath(path);
michael@0 1022 printf("ProcessUpdates updateRootDir: %s appVersion: %s\n",
michael@0 1023 path.get(), appVersion);
michael@0 1024 #endif
michael@0 1025 rv = updRootDir->Clone(getter_AddRefs(updatesDir));
michael@0 1026 if (NS_FAILED(rv))
michael@0 1027 return rv;
michael@0 1028
michael@0 1029 rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("updates"));
michael@0 1030 if (NS_FAILED(rv))
michael@0 1031 return rv;
michael@0 1032
michael@0 1033 rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("0"));
michael@0 1034 if (NS_FAILED(rv))
michael@0 1035 return rv;
michael@0 1036
michael@0 1037 ProcessType dummyPID; // this will only be used for MOZ_UPDATE_STAGING
michael@0 1038 const char *processingUpdates = PR_GetEnv("MOZ_PROCESS_UPDATES");
michael@0 1039 if (processingUpdates && *processingUpdates) {
michael@0 1040 // Enable the tests to request an update to be staged.
michael@0 1041 const char *stagingUpdate = PR_GetEnv("MOZ_UPDATE_STAGING");
michael@0 1042 if (stagingUpdate && *stagingUpdate) {
michael@0 1043 restart = false;
michael@0 1044 pid = &dummyPID;
michael@0 1045 }
michael@0 1046 }
michael@0 1047
michael@0 1048 nsCOMPtr<nsIFile> statusFile;
michael@0 1049 UpdateStatus status = GetUpdateStatus(updatesDir, statusFile);
michael@0 1050 #ifdef DEBUG
michael@0 1051 printf("ProcessUpdates status: %d\n", status);
michael@0 1052 updatesDir->GetNativePath(path);
michael@0 1053 printf("ProcessUpdates updatesDir: %s\n", path.get());
michael@0 1054 #endif
michael@0 1055 switch (status) {
michael@0 1056 case ePendingUpdate:
michael@0 1057 case ePendingService: {
michael@0 1058 nsCOMPtr<nsIFile> versionFile;
michael@0 1059 // Remove the update if the update application version file doesn't exist
michael@0 1060 // or if the update's application version is less than the current
michael@0 1061 // application version.
michael@0 1062 if (!GetVersionFile(updatesDir, versionFile) ||
michael@0 1063 IsOlderVersion(versionFile, appVersion)) {
michael@0 1064 updatesDir->Remove(true);
michael@0 1065 } else {
michael@0 1066 ApplyUpdate(greDir, updatesDir, statusFile,
michael@0 1067 appDir, argc, argv, restart, isOSUpdate, osApplyToDir, pid);
michael@0 1068 }
michael@0 1069 break;
michael@0 1070 }
michael@0 1071 case eAppliedUpdate:
michael@0 1072 case eAppliedService:
michael@0 1073 // An update was staged and needs to be switched so the updated application
michael@0 1074 // is used.
michael@0 1075 SwitchToUpdatedApp(greDir, updatesDir, statusFile,
michael@0 1076 appDir, argc, argv);
michael@0 1077 break;
michael@0 1078 case eNoUpdateAction:
michael@0 1079 // We don't need to do any special processing here, we'll just continue to
michael@0 1080 // startup the application.
michael@0 1081 break;
michael@0 1082 }
michael@0 1083
michael@0 1084 return NS_OK;
michael@0 1085 }
michael@0 1086
michael@0 1087
michael@0 1088
michael@0 1089 NS_IMPL_ISUPPORTS(nsUpdateProcessor, nsIUpdateProcessor)
michael@0 1090
michael@0 1091 nsUpdateProcessor::nsUpdateProcessor()
michael@0 1092 : mUpdaterPID(0)
michael@0 1093 {
michael@0 1094 }
michael@0 1095
michael@0 1096 NS_IMETHODIMP
michael@0 1097 nsUpdateProcessor::ProcessUpdate(nsIUpdate* aUpdate)
michael@0 1098 {
michael@0 1099 nsCOMPtr<nsIFile> greDir, appDir, updRoot;
michael@0 1100 nsAutoCString appVersion;
michael@0 1101 int argc;
michael@0 1102 char **argv;
michael@0 1103
michael@0 1104 nsAutoCString binPath;
michael@0 1105 nsXREDirProvider* dirProvider = nsXREDirProvider::GetSingleton();
michael@0 1106 if (dirProvider) { // Normal code path
michael@0 1107 // Check for and process any available updates
michael@0 1108 bool persistent;
michael@0 1109 nsresult rv = NS_ERROR_FAILURE; // Take the NS_FAILED path when non-GONK
michael@0 1110 #ifdef MOZ_WIDGET_GONK
michael@0 1111 // Check in the sdcard for updates first, since that's our preferred
michael@0 1112 // download location.
michael@0 1113 rv = dirProvider->GetFile(XRE_UPDATE_ARCHIVE_DIR, &persistent,
michael@0 1114 getter_AddRefs(updRoot));
michael@0 1115 #endif
michael@0 1116 if (NS_FAILED(rv)) {
michael@0 1117 rv = dirProvider->GetFile(XRE_UPDATE_ROOT_DIR, &persistent,
michael@0 1118 getter_AddRefs(updRoot));
michael@0 1119 }
michael@0 1120 // XRE_UPDATE_ROOT_DIR may fail. Fallback to appDir if failed
michael@0 1121 if (NS_FAILED(rv))
michael@0 1122 updRoot = dirProvider->GetAppDir();
michael@0 1123
michael@0 1124 greDir = dirProvider->GetGREDir();
michael@0 1125 nsCOMPtr<nsIFile> exeFile;
michael@0 1126 rv = dirProvider->GetFile(XRE_EXECUTABLE_FILE, &persistent,
michael@0 1127 getter_AddRefs(exeFile));
michael@0 1128 if (NS_SUCCEEDED(rv))
michael@0 1129 rv = exeFile->GetParent(getter_AddRefs(appDir));
michael@0 1130
michael@0 1131 if (NS_FAILED(rv))
michael@0 1132 appDir = dirProvider->GetAppDir();
michael@0 1133
michael@0 1134 #ifdef TOR_BROWSER_UPDATE
michael@0 1135 appVersion = TOR_BROWSER_VERSION;
michael@0 1136 #else
michael@0 1137 appVersion = gAppData->version;
michael@0 1138 #endif
michael@0 1139 argc = gRestartArgc;
michael@0 1140 argv = gRestartArgv;
michael@0 1141 } else {
michael@0 1142 // In the xpcshell environment, the usual XRE_main is not run, so things
michael@0 1143 // like dirProvider and gAppData do not exist. This code path accesses
michael@0 1144 // XPCOM (which is not available in the previous code path) in order to get
michael@0 1145 // the same information.
michael@0 1146 nsCOMPtr<nsIProperties> ds =
michael@0 1147 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
michael@0 1148 if (!ds) {
michael@0 1149 NS_ABORT(); // There's nothing which we can do if this fails!
michael@0 1150 }
michael@0 1151
michael@0 1152 nsresult rv = ds->Get(NS_GRE_DIR, NS_GET_IID(nsIFile),
michael@0 1153 getter_AddRefs(greDir));
michael@0 1154 NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the GRE dir");
michael@0 1155 appDir = greDir;
michael@0 1156
michael@0 1157 rv = ds->Get(XRE_UPDATE_ROOT_DIR, NS_GET_IID(nsIFile),
michael@0 1158 getter_AddRefs(updRoot));
michael@0 1159 if (NS_FAILED(rv))
michael@0 1160 updRoot = appDir;
michael@0 1161
michael@0 1162 // To support Tor Browser Bundle updates from xpcshell, modify the
michael@0 1163 // following code to use the TBB version fron the configure process.
michael@0 1164 nsCOMPtr<nsIXULAppInfo> appInfo =
michael@0 1165 do_GetService("@mozilla.org/xre/app-info;1");
michael@0 1166 if (appInfo) {
michael@0 1167 rv = appInfo->GetVersion(appVersion);
michael@0 1168 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1169 } else {
michael@0 1170 appVersion = MOZ_APP_VERSION;
michael@0 1171 }
michael@0 1172
michael@0 1173 // We need argv[0] to point to the current executable's name. The rest of
michael@0 1174 // the entries in this array will be ignored if argc<2. Therefore, for
michael@0 1175 // xpcshell, we only fill out that item, and leave the rest empty.
michael@0 1176 argc = 1;
michael@0 1177 nsCOMPtr<nsIFile> binary;
michael@0 1178 rv = ds->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile),
michael@0 1179 getter_AddRefs(binary));
michael@0 1180 NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the binary path");
michael@0 1181 binary->GetNativePath(binPath);
michael@0 1182 }
michael@0 1183
michael@0 1184 // Copy the parameters to the StagedUpdateInfo structure shared with the
michael@0 1185 // watcher thread.
michael@0 1186 mInfo.mGREDir = greDir;
michael@0 1187 mInfo.mAppDir = appDir;
michael@0 1188 mInfo.mUpdateRoot = updRoot;
michael@0 1189 mInfo.mArgc = argc;
michael@0 1190 mInfo.mArgv = new char*[argc];
michael@0 1191 if (dirProvider) {
michael@0 1192 for (int i = 0; i < argc; ++i) {
michael@0 1193 const size_t length = strlen(argv[i]);
michael@0 1194 mInfo.mArgv[i] = new char[length + 1];
michael@0 1195 strcpy(mInfo.mArgv[i], argv[i]);
michael@0 1196 }
michael@0 1197 } else {
michael@0 1198 MOZ_ASSERT(argc == 1); // see above
michael@0 1199 const size_t length = binPath.Length();
michael@0 1200 mInfo.mArgv[0] = new char[length + 1];
michael@0 1201 strcpy(mInfo.mArgv[0], binPath.get());
michael@0 1202 }
michael@0 1203 mInfo.mAppVersion = appVersion;
michael@0 1204
michael@0 1205 #if defined(MOZ_WIDGET_GONK)
michael@0 1206 NS_ENSURE_ARG_POINTER(aUpdate);
michael@0 1207
michael@0 1208 bool isOSUpdate;
michael@0 1209 if (NS_SUCCEEDED(aUpdate->GetIsOSUpdate(&isOSUpdate)) &&
michael@0 1210 isOSUpdate) {
michael@0 1211 nsAutoCString osApplyToDir;
michael@0 1212
michael@0 1213 // This needs to be done on the main thread, so we pass it along in
michael@0 1214 // BackgroundThreadInfo
michael@0 1215 nsresult rv = GetOSApplyToDir(osApplyToDir);
michael@0 1216 if (NS_FAILED(rv)) {
michael@0 1217 LOG(("Can't get the OS apply to dir"));
michael@0 1218 return rv;
michael@0 1219 }
michael@0 1220
michael@0 1221 SetOSApplyToDir(aUpdate, osApplyToDir);
michael@0 1222
michael@0 1223 mInfo.mIsOSUpdate = true;
michael@0 1224 rv = NS_NewNativeLocalFile(osApplyToDir, false,
michael@0 1225 getter_AddRefs(mInfo.mOSApplyToDir));
michael@0 1226 if (NS_FAILED(rv)) {
michael@0 1227 LOG(("Can't create nsIFile for OS apply to dir"));
michael@0 1228 return rv;
michael@0 1229 }
michael@0 1230 }
michael@0 1231 #endif
michael@0 1232
michael@0 1233 mUpdate = aUpdate;
michael@0 1234
michael@0 1235 NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
michael@0 1236 return NS_NewThread(getter_AddRefs(mProcessWatcher),
michael@0 1237 NS_NewRunnableMethod(this, &nsUpdateProcessor::StartStagedUpdate));
michael@0 1238 }
michael@0 1239
michael@0 1240
michael@0 1241
michael@0 1242 void
michael@0 1243 nsUpdateProcessor::StartStagedUpdate()
michael@0 1244 {
michael@0 1245 NS_ABORT_IF_FALSE(!NS_IsMainThread(), "main thread");
michael@0 1246
michael@0 1247 nsresult rv = ProcessUpdates(mInfo.mGREDir,
michael@0 1248 mInfo.mAppDir,
michael@0 1249 mInfo.mUpdateRoot,
michael@0 1250 mInfo.mArgc,
michael@0 1251 mInfo.mArgv,
michael@0 1252 mInfo.mAppVersion.get(),
michael@0 1253 false,
michael@0 1254 mInfo.mIsOSUpdate,
michael@0 1255 mInfo.mOSApplyToDir,
michael@0 1256 &mUpdaterPID);
michael@0 1257 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 1258
michael@0 1259 if (mUpdaterPID) {
michael@0 1260 // Track the state of the updater process while it is staging an update.
michael@0 1261 rv = NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::WaitForProcess));
michael@0 1262 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 1263 } else {
michael@0 1264 // Failed to launch the updater process for some reason.
michael@0 1265 // We need to shutdown the current thread as there isn't anything more for
michael@0 1266 // us to do...
michael@0 1267 rv = NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::ShutdownWatcherThread));
michael@0 1268 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 1269 }
michael@0 1270 }
michael@0 1271
michael@0 1272 void
michael@0 1273 nsUpdateProcessor::ShutdownWatcherThread()
michael@0 1274 {
michael@0 1275 NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
michael@0 1276 mProcessWatcher->Shutdown();
michael@0 1277 mProcessWatcher = nullptr;
michael@0 1278 mUpdate = nullptr;
michael@0 1279 }
michael@0 1280
michael@0 1281 void
michael@0 1282 nsUpdateProcessor::WaitForProcess()
michael@0 1283 {
michael@0 1284 NS_ABORT_IF_FALSE(!NS_IsMainThread(), "main thread");
michael@0 1285 ::WaitForProcess(mUpdaterPID);
michael@0 1286 NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::UpdateDone));
michael@0 1287 }
michael@0 1288
michael@0 1289 void
michael@0 1290 nsUpdateProcessor::UpdateDone()
michael@0 1291 {
michael@0 1292 NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
michael@0 1293
michael@0 1294 nsCOMPtr<nsIUpdateManager> um =
michael@0 1295 do_GetService("@mozilla.org/updates/update-manager;1");
michael@0 1296 if (um && mUpdate) {
michael@0 1297 um->RefreshUpdateStatus(mUpdate);
michael@0 1298 }
michael@0 1299
michael@0 1300 ShutdownWatcherThread();
michael@0 1301 }
michael@0 1302

mercurial