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 +