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