Merge m-c to b-i.
[gecko.git] / toolkit / xre / nsUpdateDriver.cpp
blobbe6b53b30a1c50a443ca28dc2114db7cb62dad1a
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 # define getcwd(path, size) _getcwd(path, size)
43 # define getpid() GetCurrentProcessId()
44 #elif defined(XP_OS2)
45 # include <unistd.h>
46 # define INCL_DOSFILEMGR
47 # include <os2.h>
48 #elif defined(XP_UNIX)
49 # include <unistd.h>
50 #endif
52 using namespace mozilla;
55 // We use execv to spawn the updater process on all UNIX systems except Mac OSX
56 // since it is known to cause problems on the Mac. Windows has execv, but it
57 // is a faked implementation that doesn't really replace the current process.
58 // Instead it spawns a new process, so we gain nothing from using execv on
59 // Windows.
61 // On platforms where we are not calling execv, we may need to make the
62 // updater executable wait for the calling process to exit. Otherwise, the
63 // updater may have trouble modifying our executable image (because it might
64 // still be in use). This is accomplished by passing our PID to the updater so
65 // that it can wait for us to exit. This is not perfect as there is a race
66 // condition that could bite us. It's possible that the calling process could
67 // exit before the updater waits on the specified PID, and in the meantime a
68 // new process with the same PID could be created. This situation is unlikely,
69 // however, given the way most operating systems recycle PIDs. We'll take our
70 // chances ;-)
72 // A similar #define lives in updater.cpp and should be kept in sync with this.
74 #if defined(XP_UNIX) && !defined(XP_MACOSX)
75 #define USE_EXECV
76 #endif
78 #ifdef PR_LOGGING
79 static PRLogModuleInfo *
80 GetUpdateLog()
82 static PRLogModuleInfo *sUpdateLog;
83 if (!sUpdateLog)
84 sUpdateLog = PR_NewLogModule("updatedriver");
85 return sUpdateLog;
87 #endif
88 #define LOG(args) PR_LOG(GetUpdateLog(), PR_LOG_DEBUG, args)
90 #ifdef XP_WIN
91 static const char kUpdaterBin[] = "updater.exe";
92 #else
93 static const char kUpdaterBin[] = "updater";
94 #endif
95 static const char kUpdaterINI[] = "updater.ini";
96 #ifdef XP_MACOSX
97 static const char kUpdaterApp[] = "updater.app";
98 #endif
99 #if defined(XP_UNIX) && !defined(XP_MACOSX)
100 static const char kUpdaterPNG[] = "updater.png";
101 #endif
103 #if defined(MOZ_WIDGET_GONK)
104 #include <linux/ioprio.h>
106 static const int kB2GServiceArgc = 2;
107 static const char *kB2GServiceArgv[] = { "/system/bin/start", "b2g" };
109 static const char kAppUpdaterPrio[] = "app.update.updater.prio";
110 static const char kAppUpdaterOomScoreAdj[] = "app.update.updater.oom_score_adj";
111 static const char kAppUpdaterIOPrioClass[] = "app.update.updater.ioprio.class";
112 static const char kAppUpdaterIOPrioLevel[] = "app.update.updater.ioprio.level";
114 static const int kAppUpdaterPrioDefault = 19; // -20..19 where 19 = lowest priority
115 static const int kAppUpdaterOomScoreAdjDefault = -1000; // -1000 = Never kill
116 static const int kAppUpdaterIOPrioClassDefault = IOPRIO_CLASS_IDLE;
117 static const int kAppUpdaterIOPrioLevelDefault = 0; // Doesn't matter for CLASS IDLE
118 #endif
120 static nsresult
121 GetCurrentWorkingDir(char *buf, size_t size)
123 // Cannot use NS_GetSpecialDirectory because XPCOM is not yet initialized.
124 // This code is duplicated from xpcom/io/SpecialSystemDirectory.cpp:
126 #if defined(XP_OS2)
127 if (DosQueryPathInfo( ".", FIL_QUERYFULLNAME, buf, size))
128 return NS_ERROR_FAILURE;
129 #elif defined(XP_WIN)
130 wchar_t wpath[MAX_PATH];
131 if (!_wgetcwd(wpath, size))
132 return NS_ERROR_FAILURE;
133 NS_ConvertUTF16toUTF8 path(wpath);
134 strncpy(buf, path.get(), size);
135 #else
136 if(!getcwd(buf, size))
137 return NS_ERROR_FAILURE;
138 #endif
139 return NS_OK;
142 #if defined(XP_MACOSX)
143 // This is a copy of OS X's XRE_GetBinaryPath from nsAppRunner.cpp with the
144 // gBinaryPath check removed so that the updater can reload the stub executable
145 // instead of xulrunner-bin. See bug 349737.
146 static nsresult
147 GetXULRunnerStubPath(const char* argv0, nsIFile* *aResult)
149 // Works even if we're not bundled.
150 CFBundleRef appBundle = ::CFBundleGetMainBundle();
151 if (!appBundle)
152 return NS_ERROR_FAILURE;
154 CFURLRef bundleURL = ::CFBundleCopyExecutableURL(appBundle);
155 if (!bundleURL)
156 return NS_ERROR_FAILURE;
158 nsCOMPtr<nsILocalFileMac> lfm;
159 nsresult rv = NS_NewLocalFileWithCFURL(bundleURL, true, getter_AddRefs(lfm));
161 ::CFRelease(bundleURL);
163 if (NS_FAILED(rv))
164 return rv;
166 NS_ADDREF(*aResult = static_cast<nsIFile*>(lfm.get()));
167 return NS_OK;
169 #endif /* XP_MACOSX */
171 static bool
172 GetFile(nsIFile *dir, const nsCSubstring &name, nsCOMPtr<nsIFile> &result)
174 nsresult rv;
176 nsCOMPtr<nsIFile> file;
177 rv = dir->Clone(getter_AddRefs(file));
178 if (NS_FAILED(rv))
179 return false;
181 rv = file->AppendNative(name);
182 if (NS_FAILED(rv))
183 return false;
185 result = do_QueryInterface(file, &rv);
186 return NS_SUCCEEDED(rv);
189 static bool
190 GetStatusFile(nsIFile *dir, nsCOMPtr<nsIFile> &result)
192 return GetFile(dir, NS_LITERAL_CSTRING("update.status"), result);
196 * Get the contents of the update.status file.
198 * @param statusFile the status file object.
199 * @param buf the buffer holding the file contents
201 * @return true if successful, false otherwise.
203 template <size_t Size>
204 static bool
205 GetStatusFileContents(nsIFile *statusFile, char (&buf)[Size])
207 // The buffer needs to be large enough to hold the known status codes
208 PR_STATIC_ASSERT(Size > 16);
210 PRFileDesc *fd = nullptr;
211 nsresult rv = statusFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd);
212 if (NS_FAILED(rv))
213 return false;
215 const int32_t n = PR_Read(fd, buf, Size);
216 PR_Close(fd);
218 return (n >= 0);
221 typedef enum {
222 eNoUpdateAction,
223 ePendingUpdate,
224 ePendingService,
225 eAppliedUpdate,
226 eAppliedService
227 } UpdateStatus;
230 * Returns a value indicating what needs to be done in order to handle an update.
232 * @param dir the directory in which we should look for an update.status file.
233 * @param statusFile the update.status file found in the directory.
235 * @return the update action to be performed.
237 static UpdateStatus
238 GetUpdateStatus(nsIFile* dir, nsCOMPtr<nsIFile> &statusFile)
240 if (GetStatusFile(dir, statusFile)) {
241 char buf[32];
242 if (GetStatusFileContents(statusFile, buf)) {
243 const char kPending[] = "pending";
244 const char kPendingService[] = "pending-service";
245 const char kApplied[] = "applied";
246 const char kAppliedService[] = "applied-service";
247 if (!strncmp(buf, kPendingService, sizeof(kPendingService) - 1)) {
248 return ePendingService;
250 if (!strncmp(buf, kPending, sizeof(kPending) - 1)) {
251 return ePendingUpdate;
253 if (!strncmp(buf, kAppliedService, sizeof(kAppliedService) - 1)) {
254 return eAppliedService;
256 if (!strncmp(buf, kApplied, sizeof(kApplied) - 1)) {
257 return eAppliedUpdate;
261 return eNoUpdateAction;
264 static bool
265 GetVersionFile(nsIFile *dir, nsCOMPtr<nsIFile> &result)
267 return GetFile(dir, NS_LITERAL_CSTRING("update.version"), result);
270 // Compares the current application version with the update's application
271 // version.
272 static bool
273 IsOlderVersion(nsIFile *versionFile, const char *appVersion)
275 PRFileDesc *fd = nullptr;
276 nsresult rv = versionFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd);
277 if (NS_FAILED(rv))
278 return true;
280 char buf[32];
281 const int32_t n = PR_Read(fd, buf, sizeof(buf));
282 PR_Close(fd);
284 if (n < 0)
285 return false;
287 // Trim off the trailing newline
288 if (buf[n - 1] == '\n')
289 buf[n - 1] = '\0';
291 // If the update xml doesn't provide the application version the file will
292 // contain the string "null" and it is assumed that the update is not older.
293 const char kNull[] = "null";
294 if (strncmp(buf, kNull, sizeof(kNull) - 1) == 0)
295 return false;
297 if (mozilla::Version(appVersion) > buf)
298 return true;
300 return false;
303 static bool
304 CopyFileIntoUpdateDir(nsIFile *parentDir, const char *leafName, nsIFile *updateDir)
306 nsDependentCString leaf(leafName);
307 nsCOMPtr<nsIFile> file;
309 // Make sure there is not an existing file in the target location.
310 nsresult rv = updateDir->Clone(getter_AddRefs(file));
311 if (NS_FAILED(rv))
312 return false;
313 rv = file->AppendNative(leaf);
314 if (NS_FAILED(rv))
315 return false;
316 file->Remove(true);
318 // Now, copy into the target location.
319 rv = parentDir->Clone(getter_AddRefs(file));
320 if (NS_FAILED(rv))
321 return false;
322 rv = file->AppendNative(leaf);
323 if (NS_FAILED(rv))
324 return false;
325 rv = file->CopyToNative(updateDir, EmptyCString());
326 if (NS_FAILED(rv))
327 return false;
329 return true;
332 static bool
333 CopyUpdaterIntoUpdateDir(nsIFile *greDir, nsIFile *appDir, nsIFile *updateDir,
334 nsCOMPtr<nsIFile> &updater)
336 // Copy the updater application from the GRE and the updater ini from the app
337 #if defined(XP_MACOSX)
338 if (!CopyFileIntoUpdateDir(greDir, kUpdaterApp, updateDir))
339 return false;
340 #else
341 if (!CopyFileIntoUpdateDir(greDir, kUpdaterBin, updateDir))
342 return false;
343 #endif
344 CopyFileIntoUpdateDir(appDir, kUpdaterINI, updateDir);
345 #if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(ANDROID)
346 nsCOMPtr<nsIFile> iconDir;
347 appDir->Clone(getter_AddRefs(iconDir));
348 iconDir->AppendNative(NS_LITERAL_CSTRING("icons"));
349 if (!CopyFileIntoUpdateDir(iconDir, kUpdaterPNG, updateDir))
350 return false;
351 #endif
352 // Finally, return the location of the updater binary.
353 nsresult rv = updateDir->Clone(getter_AddRefs(updater));
354 if (NS_FAILED(rv))
355 return false;
356 #if defined(XP_MACOSX)
357 rv = updater->AppendNative(NS_LITERAL_CSTRING(kUpdaterApp));
358 nsresult tmp = updater->AppendNative(NS_LITERAL_CSTRING("Contents"));
359 if (NS_FAILED(tmp)) {
360 rv = tmp;
362 tmp = updater->AppendNative(NS_LITERAL_CSTRING("MacOS"));
363 if (NS_FAILED(tmp) || NS_FAILED(rv))
364 return false;
365 #endif
366 rv = updater->AppendNative(NS_LITERAL_CSTRING(kUpdaterBin));
367 return NS_SUCCEEDED(rv);
371 * Switch an existing application directory to an updated version which has been
372 * previously constructed in the background.
374 * @param greDir the GRE dir
375 * @param updateDir the update root dir
376 * @param statusFile the update.status file
377 * @param appDir the app dir
378 * @param appArgc the number of args to the application
379 * @param appArgv the args to the application, used for restarting if needed
381 static void
382 SwitchToUpdatedApp(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile,
383 nsIFile *appDir, int appArgc, char **appArgv)
385 nsresult rv;
387 // Steps:
388 // - copy updater into temp dir
389 // - run updater with the correct arguments
391 nsCOMPtr<nsIFile> tmpDir;
392 GetSpecialSystemDirectory(OS_TemporaryDirectory,
393 getter_AddRefs(tmpDir));
394 if (!tmpDir) {
395 LOG(("failed getting a temp dir\n"));
396 return;
399 // Try to create our own new temp directory in case there is already an
400 // updater binary in the OS temporary location which we cannot write to.
401 // Note that we don't check for errors here, as if this directory can't
402 // be created, the following CopyUpdaterIntoUpdateDir call will fail.
403 // We create the unique directory inside a subfolder of MozUpdater instead
404 // of directly in the temp directory so we can efficiently delete everything
405 // after updates.
406 tmpDir->Append(NS_LITERAL_STRING("MozUpdater"));
407 tmpDir->Append(NS_LITERAL_STRING("bgupdate"));
408 tmpDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0755);
410 nsCOMPtr<nsIFile> updater;
411 if (!CopyUpdaterIntoUpdateDir(greDir, appDir, tmpDir, updater)) {
412 LOG(("failed copying updater\n"));
413 return;
416 // We need to use the value returned from XRE_GetBinaryPath when attempting
417 // to restart the running application.
418 nsCOMPtr<nsIFile> appFile;
420 #if defined(XP_MACOSX)
421 // On OS X we need to pass the location of the xulrunner-stub executable
422 // rather than xulrunner-bin. See bug 349737.
423 GetXULRunnerStubPath(appArgv[0], getter_AddRefs(appFile));
424 #else
425 XRE_GetBinaryPath(appArgv[0], getter_AddRefs(appFile));
426 #endif
428 if (!appFile)
429 return;
431 #ifdef XP_WIN
432 nsAutoString appFilePathW;
433 rv = appFile->GetPath(appFilePathW);
434 if (NS_FAILED(rv))
435 return;
436 NS_ConvertUTF16toUTF8 appFilePath(appFilePathW);
438 nsAutoString updaterPathW;
439 rv = updater->GetPath(updaterPathW);
440 if (NS_FAILED(rv))
441 return;
443 NS_ConvertUTF16toUTF8 updaterPath(updaterPathW);
444 #else
446 nsAutoCString appFilePath;
447 #if defined(MOZ_WIDGET_GONK)
448 appFilePath.Assign(kB2GServiceArgv[0]);
449 appArgc = kB2GServiceArgc;
450 appArgv = const_cast<char**>(kB2GServiceArgv);
451 #else
452 rv = appFile->GetNativePath(appFilePath);
453 if (NS_FAILED(rv))
454 return;
455 #endif
457 nsAutoCString updaterPath;
458 rv = updater->GetNativePath(updaterPath);
459 if (NS_FAILED(rv))
460 return;
461 #endif
463 // Get the directory to which the update will be applied. On Mac OSX we need
464 // to apply the update to the Updated.app directory under the Foo.app
465 // directory which is the parent of the parent of the appDir. On other
466 // platforms we will just apply to the appDir/updated.
467 nsCOMPtr<nsIFile> updatedDir;
468 #if defined(XP_MACOSX)
469 nsAutoCString applyToDir;
471 nsCOMPtr<nsIFile> parentDir1, parentDir2;
472 rv = appDir->GetParent(getter_AddRefs(parentDir1));
473 if (NS_FAILED(rv))
474 return;
475 rv = parentDir1->GetParent(getter_AddRefs(parentDir2));
476 if (NS_FAILED(rv))
477 return;
478 if (!GetFile(parentDir2, NS_LITERAL_CSTRING("Updated.app"), updatedDir))
479 return;
480 rv = updatedDir->GetNativePath(applyToDir);
482 #else
483 if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir))
484 return;
485 #if defined(XP_WIN)
486 nsAutoString applyToDirW;
487 rv = updatedDir->GetPath(applyToDirW);
489 NS_ConvertUTF16toUTF8 applyToDir(applyToDirW);
490 #else
491 nsAutoCString applyToDir;
492 rv = updatedDir->GetNativePath(applyToDir);
493 #endif
494 #endif
495 if (NS_FAILED(rv))
496 return;
498 // Make sure that the updated directory exists
499 bool updatedDirExists = false;
500 updatedDir->Exists(&updatedDirExists);
501 if (!updatedDirExists) {
502 return;
505 #if defined(XP_WIN)
506 nsAutoString updateDirPathW;
507 rv = updateDir->GetPath(updateDirPathW);
509 NS_ConvertUTF16toUTF8 updateDirPath(updateDirPathW);
510 #else
511 nsAutoCString updateDirPath;
512 rv = updateDir->GetNativePath(updateDirPath);
513 #endif
515 if (NS_FAILED(rv))
516 return;
518 // Get the current working directory.
519 char workingDirPath[MAXPATHLEN];
520 rv = GetCurrentWorkingDir(workingDirPath, sizeof(workingDirPath));
521 if (NS_FAILED(rv))
522 return;
524 // Construct the PID argument for this process. If we are using execv, then
525 // we pass "0" which is then ignored by the updater.
526 #if defined(USE_EXECV)
527 nsAutoCString pid("0");
528 #else
529 nsAutoCString pid;
530 pid.AppendInt((int32_t) getpid());
531 #endif
533 // Append a special token to the PID in order to let the updater know that it
534 // just needs to replace the update directory.
535 pid.AppendLiteral("/replace");
537 int immersiveArgc = 0;
538 #ifdef XP_WIN
539 if (IsRunningInWindowsMetro()) {
540 immersiveArgc = 1;
542 #endif
543 int argc = appArgc + 5 + immersiveArgc;
544 char **argv = new char*[argc + 1];
545 if (!argv)
546 return;
547 argv[0] = (char*) updaterPath.get();
548 argv[1] = (char*) updateDirPath.get();
549 argv[2] = (char*) applyToDir.get();
550 argv[3] = (char*) pid.get();
551 if (appArgc) {
552 argv[4] = workingDirPath;
553 argv[5] = (char*) appFilePath.get();
554 for (int i = 1; i < appArgc; ++i)
555 argv[5 + i] = appArgv[i];
556 #ifdef XP_WIN
557 if (immersiveArgc) {
558 argv[argc - 1] = "-ServerName:DefaultBrowserServer";
560 #endif
561 argv[argc] = nullptr;
562 } else {
563 argc = 4;
564 argv[4] = nullptr;
567 if (gSafeMode) {
568 PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
571 LOG(("spawning updater process for replacing [%s]\n", updaterPath.get()));
573 #if defined(USE_EXECV)
574 # if defined(MOZ_WIDGET_GONK)
575 // In Gonk, we preload libmozglue, which the updater process doesn't need.
576 // Since the updater will move and delete libmozglue.so, this can actually
577 // stop the /system mount from correctly being remounted as read-only.
578 unsetenv("LD_PRELOAD");
579 # endif
580 execv(updaterPath.get(), argv);
581 #elif defined(XP_WIN)
582 // Switch the application using updater.exe
583 if (!WinLaunchChild(updaterPathW.get(), argc, argv)) {
584 return;
586 _exit(0);
587 #elif defined(XP_MACOSX)
588 CommandLineServiceMac::SetupMacCommandLine(argc, argv, true);
589 // LaunchChildMac uses posix_spawnp and prefers the current
590 // architecture when launching. It doesn't require a
591 // null-terminated string but it doesn't matter if we pass one.
592 LaunchChildMac(argc, argv);
593 exit(0);
594 #else
595 PR_CreateProcessDetached(updaterPath.get(), argv, nullptr, nullptr);
596 exit(0);
597 #endif
600 #if defined(MOZ_WIDGET_GONK)
601 static nsresult
602 GetOSApplyToDir(nsACString& applyToDir)
604 nsCOMPtr<nsIProperties> ds =
605 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
606 NS_ASSERTION(ds, "Can't get directory service");
608 nsCOMPtr<nsIFile> osApplyToDir;
609 DebugOnly<nsresult> rv = ds->Get(XRE_OS_UPDATE_APPLY_TO_DIR, NS_GET_IID(nsIFile),
610 getter_AddRefs(osApplyToDir));
611 NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the OS applyTo dir");
613 return osApplyToDir->GetNativePath(applyToDir);
616 static void
617 SetOSApplyToDir(nsIUpdate* update, const nsACString& osApplyToDir)
619 nsresult rv;
620 nsCOMPtr<nsIWritablePropertyBag> updateProperties =
621 do_QueryInterface(update, &rv);
623 if (NS_FAILED(rv)) {
624 return;
627 nsCOMPtr<nsIWritableVariant> variant =
628 do_CreateInstance("@mozilla.org/variant;1", &rv);
629 if (NS_FAILED(rv)) {
630 return;
633 rv = variant->SetAsACString(osApplyToDir);
634 if (NS_FAILED(rv)) {
635 return;
638 updateProperties->SetProperty(NS_LITERAL_STRING("osApplyToDir"), variant);
640 #endif
643 * Apply an update, possibly in the background.
645 * @param greDir the GRE dir
646 * @param updateDir the update root dir
647 * @param statusFile the update.status file
648 * @param appDir the app dir
649 * @param appArgc the number of args to the application
650 * @param appArgv the args to the application, used for restarting if needed
651 * @param restart if true, apply the update in the foreground and restart the
652 * application when done. otherwise, apply the update in the
653 * background and don't restart the application.
654 * @param outpid out parameter holding the handle to the updater application for
655 * background updates.
657 static void
658 ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile,
659 nsIFile *appDir, int appArgc, char **appArgv,
660 bool restart, bool isOSUpdate, nsIFile *osApplyToDir,
661 ProcessType *outpid)
663 nsresult rv;
665 // Steps:
666 // - mark update as 'applying'
667 // - copy updater into update dir
668 // - run updater w/ appDir as the current working dir
670 nsCOMPtr<nsIFile> updater;
671 if (!CopyUpdaterIntoUpdateDir(greDir, appDir, updateDir, updater)) {
672 LOG(("failed copying updater\n"));
673 return;
676 // We need to use the value returned from XRE_GetBinaryPath when attempting
677 // to restart the running application.
678 nsCOMPtr<nsIFile> appFile;
680 #if defined(XP_MACOSX)
681 // On OS X we need to pass the location of the xulrunner-stub executable
682 // rather than xulrunner-bin. See bug 349737.
683 GetXULRunnerStubPath(appArgv[0], getter_AddRefs(appFile));
684 #else
685 XRE_GetBinaryPath(appArgv[0], getter_AddRefs(appFile));
686 #endif
688 if (!appFile)
689 return;
691 #ifdef XP_WIN
692 nsAutoString appFilePathW;
693 rv = appFile->GetPath(appFilePathW);
694 if (NS_FAILED(rv))
695 return;
696 NS_ConvertUTF16toUTF8 appFilePath(appFilePathW);
698 nsAutoString updaterPathW;
699 rv = updater->GetPath(updaterPathW);
700 if (NS_FAILED(rv))
701 return;
703 NS_ConvertUTF16toUTF8 updaterPath(updaterPathW);
705 #else
706 nsAutoCString appFilePath;
707 rv = appFile->GetNativePath(appFilePath);
708 if (NS_FAILED(rv))
709 return;
711 nsAutoCString updaterPath;
712 rv = updater->GetNativePath(updaterPath);
713 if (NS_FAILED(rv))
714 return;
716 #endif
718 // Get the directory to which the update will be applied. On Mac OSX we need
719 // to apply the update to the Updated.app directory under the Foo.app
720 // directory which is the parent of the parent of the appDir. On other
721 // platforms we will just apply to the appDir/updated.
722 nsCOMPtr<nsIFile> updatedDir;
723 #if defined(XP_MACOSX)
724 nsAutoCString applyToDir;
726 nsCOMPtr<nsIFile> parentDir1, parentDir2;
727 rv = appDir->GetParent(getter_AddRefs(parentDir1));
728 if (NS_FAILED(rv))
729 return;
730 rv = parentDir1->GetParent(getter_AddRefs(parentDir2));
731 if (NS_FAILED(rv))
732 return;
733 if (restart) {
734 // Use the correct directory if we're not applying the update in the
735 // background.
736 rv = parentDir2->GetNativePath(applyToDir);
737 } else {
738 if (!GetFile(parentDir2, NS_LITERAL_CSTRING("Updated.app"), updatedDir))
739 return;
740 rv = updatedDir->GetNativePath(applyToDir);
743 #else
744 if (restart) {
745 // Use the correct directory if we're not applying the update in the
746 // background.
747 updatedDir = do_QueryInterface(appDir);
748 } else if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) {
749 return;
751 #if defined(XP_WIN)
752 nsAutoString applyToDirW;
753 rv = updatedDir->GetPath(applyToDirW);
755 NS_ConvertUTF16toUTF8 applyToDir(applyToDirW);
756 #else
757 nsAutoCString applyToDir;
759 #if defined(MOZ_WIDGET_GONK)
760 if (isOSUpdate) {
761 if (!osApplyToDir) {
762 return;
765 rv = osApplyToDir->GetNativePath(applyToDir);
766 } else {
767 #endif // defined(MOZ_WIDGET_GONK)
769 rv = updatedDir->GetNativePath(applyToDir);
771 #if defined(MOZ_WIDGET_GONK)
773 #endif // defined(MOZ_WIDGET_GONK)
775 #endif // defined(XP_WIN)
776 #endif
778 if (NS_FAILED(rv))
779 return;
781 #if defined(XP_WIN)
782 nsAutoString updateDirPathW;
783 rv = updateDir->GetPath(updateDirPathW);
785 NS_ConvertUTF16toUTF8 updateDirPath(updateDirPathW);
786 #else
787 nsAutoCString updateDirPath;
788 rv = updateDir->GetNativePath(updateDirPath);
789 #endif
791 if (NS_FAILED(rv))
792 return;
794 // Get the current working directory.
795 char workingDirPath[MAXPATHLEN];
796 rv = GetCurrentWorkingDir(workingDirPath, sizeof(workingDirPath));
797 if (NS_FAILED(rv))
798 return;
800 // We used to write out "Applying" to the update.status file here.
801 // Instead we do this from within the updater application now.
802 // This is so that we don't overwrite the status of pending-service
803 // in the Windows case. This change was made for all platforms so
804 // that it stays consistent across all OS.
806 // Construct the PID argument for this process. If we are using execv, then
807 // we pass "0" which is then ignored by the updater.
808 nsAutoCString pid;
809 if (!restart) {
810 // Signal the updater application that it should apply the update in the
811 // background.
812 pid.AssignASCII("-1");
813 } else {
814 #if defined(USE_EXECV)
815 pid.AssignASCII("0");
816 #else
817 pid.AppendInt((int32_t) getpid());
818 #endif
821 int immersiveArgc = 0;
822 #ifdef XP_WIN
823 if (IsRunningInWindowsMetro()) {
824 immersiveArgc = 1;
826 #endif
827 int argc = appArgc + 5 + immersiveArgc;
828 char **argv = new char*[argc + 1 ];
829 if (!argv)
830 return;
831 argv[0] = (char*) updaterPath.get();
832 argv[1] = (char*) updateDirPath.get();
833 argv[2] = (char*) applyToDir.get();
834 argv[3] = (char*) pid.get();
835 if (restart && appArgc) {
836 argv[4] = workingDirPath;
837 argv[5] = (char*) appFilePath.get();
838 for (int i = 1; i < appArgc; ++i)
839 argv[5 + i] = appArgv[i];
840 #ifdef XP_WIN
841 if (immersiveArgc) {
842 argv[argc - 1] = "-ServerName:DefaultBrowserServer";
844 #endif
845 argv[argc] = nullptr;
846 } else {
847 argc = 4;
848 argv[4] = nullptr;
851 if (gSafeMode) {
852 PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
855 if (isOSUpdate) {
856 PR_SetEnv("MOZ_OS_UPDATE=1");
858 #if defined(MOZ_WIDGET_GONK)
859 // We want the updater to be CPU friendly and not subject to being killed by
860 // the low memory killer, so we pass in some preferences to allow it to
861 // adjust its priority.
863 int32_t prioVal = Preferences::GetInt(kAppUpdaterPrio,
864 kAppUpdaterPrioDefault);
865 int32_t oomScoreAdj = Preferences::GetInt(kAppUpdaterOomScoreAdj,
866 kAppUpdaterOomScoreAdjDefault);
867 int32_t ioprioClass = Preferences::GetInt(kAppUpdaterIOPrioClass,
868 kAppUpdaterIOPrioClassDefault);
869 int32_t ioprioLevel = Preferences::GetInt(kAppUpdaterIOPrioLevel,
870 kAppUpdaterIOPrioLevelDefault);
871 nsPrintfCString prioEnv("MOZ_UPDATER_PRIO=%d/%d/%d/%d",
872 prioVal, oomScoreAdj, ioprioClass, ioprioLevel);
873 PR_SetEnv(prioEnv.get());
874 #endif
876 LOG(("spawning updater process [%s]\n", updaterPath.get()));
878 #if defined(USE_EXECV)
879 // Don't use execv for background updates.
880 if (restart) {
881 execv(updaterPath.get(), argv);
882 } else {
883 *outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr);
885 #elif defined(XP_WIN)
886 // Launch the update using updater.exe
887 if (!WinLaunchChild(updaterPathW.get(), argc, argv, nullptr, outpid)) {
888 return;
891 if (restart) {
892 // We are going to process an update so we should exit now
893 _exit(0);
895 #elif defined(XP_MACOSX)
896 CommandLineServiceMac::SetupMacCommandLine(argc, argv, true);
897 // LaunchChildMac uses posix_spawnp and prefers the current
898 // architecture when launching. It doesn't require a
899 // null-terminated string but it doesn't matter if we pass one.
900 LaunchChildMac(argc, argv, 0, outpid);
901 if (restart) {
902 exit(0);
904 #else
905 *outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr);
906 if (restart) {
907 exit(0);
909 #endif
913 * Wait for a process until it terminates. This call is blocking.
915 static void
916 WaitForProcess(ProcessType pt)
918 #if defined(XP_WIN)
919 WaitForSingleObject(pt, INFINITE);
920 CloseHandle(pt);
921 #elif defined(XP_MACOSX)
922 waitpid(pt, 0, 0);
923 #else
924 int32_t exitCode;
925 PR_WaitProcess(pt, &exitCode);
926 if (exitCode != 0) {
927 LOG(("Error while running the updater process, check update.log"));
929 #endif
932 nsresult
933 ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir,
934 int argc, char **argv, const char *appVersion,
935 bool restart, bool isOSUpdate, nsIFile *osApplyToDir,
936 ProcessType *pid)
938 nsresult rv;
940 nsCOMPtr<nsIFile> updatesDir;
941 rv = updRootDir->Clone(getter_AddRefs(updatesDir));
942 if (NS_FAILED(rv))
943 return rv;
945 rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("updates"));
946 if (NS_FAILED(rv))
947 return rv;
949 rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("0"));
950 if (NS_FAILED(rv))
951 return rv;
953 ProcessType dummyPID; // this will only be used for MOZ_UPDATE_BACKGROUND
954 const char *processingUpdates = PR_GetEnv("MOZ_PROCESS_UPDATES");
955 if (processingUpdates && *processingUpdates) {
956 // Enable the tests to request us to use a different update root directory
957 const char *updRootOverride = PR_GetEnv("MOZ_UPDATE_ROOT_OVERRIDE");
958 if (updRootOverride && *updRootOverride) {
959 nsCOMPtr<nsIFile> overrideDir;
960 nsAutoCString path(updRootOverride);
961 rv = NS_NewNativeLocalFile(path, false, getter_AddRefs(overrideDir));
962 if (NS_FAILED(rv)) {
963 return rv;
965 updatesDir = do_QueryInterface(overrideDir);
967 // Enable the tests to request us to use a different app directory
968 const char *appDirOverride = PR_GetEnv("MOZ_UPDATE_APPDIR_OVERRIDE");
969 if (appDirOverride && *appDirOverride) {
970 nsCOMPtr<nsIFile> overrideDir;
971 nsAutoCString path(appDirOverride);
972 rv = NS_NewNativeLocalFile(path, false, getter_AddRefs(overrideDir));
973 if (NS_FAILED(rv)) {
974 return rv;
976 NS_ADDREF(appDir = overrideDir);
978 // Enable the tests to request us to perform a background update
979 const char *backgroundUpdate = PR_GetEnv("MOZ_UPDATE_BACKGROUND");
980 if (backgroundUpdate && *backgroundUpdate) {
981 restart = false;
982 pid = &dummyPID;
986 nsCOMPtr<nsIFile> statusFile;
987 UpdateStatus status = GetUpdateStatus(updatesDir, statusFile);
988 switch (status) {
989 case ePendingUpdate:
990 case ePendingService: {
991 nsCOMPtr<nsIFile> versionFile;
992 // Remove the update if the update application version file doesn't exist
993 // or if the update's application version is less than the current
994 // application version.
995 if (!GetVersionFile(updatesDir, versionFile) ||
996 IsOlderVersion(versionFile, appVersion)) {
997 updatesDir->Remove(true);
998 } else {
999 ApplyUpdate(greDir, updatesDir, statusFile,
1000 appDir, argc, argv, restart, isOSUpdate, osApplyToDir, pid);
1002 break;
1004 case eAppliedUpdate:
1005 case eAppliedService:
1006 // An update was applied in the background, so we need to switch to using
1007 // it now.
1008 SwitchToUpdatedApp(greDir, updatesDir, statusFile,
1009 appDir, argc, argv);
1010 break;
1011 case eNoUpdateAction:
1012 // We don't need to do any special processing here, we'll just continue to
1013 // startup the application.
1014 break;
1017 return NS_OK;
1022 NS_IMPL_ISUPPORTS1(nsUpdateProcessor, nsIUpdateProcessor)
1024 nsUpdateProcessor::nsUpdateProcessor()
1025 : mUpdaterPID(0)
1029 NS_IMETHODIMP
1030 nsUpdateProcessor::ProcessUpdate(nsIUpdate* aUpdate)
1032 nsCOMPtr<nsIFile> greDir, appDir, updRoot;
1033 nsAutoCString appVersion;
1034 int argc;
1035 char **argv;
1037 nsAutoCString binPath;
1038 nsXREDirProvider* dirProvider = nsXREDirProvider::GetSingleton();
1039 if (dirProvider) { // Normal code path
1040 // Check for and process any available updates
1041 bool persistent;
1042 nsresult rv = NS_ERROR_FAILURE; // Take the NS_FAILED path when non-GONK
1043 #ifdef MOZ_WIDGET_GONK
1044 // Check in the sdcard for updates first, since that's our preferred
1045 // download location.
1046 rv = dirProvider->GetFile(XRE_UPDATE_ARCHIVE_DIR, &persistent,
1047 getter_AddRefs(updRoot));
1048 #endif
1049 if (NS_FAILED(rv)) {
1050 rv = dirProvider->GetFile(XRE_UPDATE_ROOT_DIR, &persistent,
1051 getter_AddRefs(updRoot));
1053 // XRE_UPDATE_ROOT_DIR may fail. Fallback to appDir if failed
1054 if (NS_FAILED(rv))
1055 updRoot = dirProvider->GetAppDir();
1057 greDir = dirProvider->GetGREDir();
1058 nsCOMPtr<nsIFile> exeFile;
1059 rv = dirProvider->GetFile(XRE_EXECUTABLE_FILE, &persistent,
1060 getter_AddRefs(exeFile));
1061 if (NS_SUCCEEDED(rv))
1062 rv = exeFile->GetParent(getter_AddRefs(appDir));
1064 if (NS_FAILED(rv))
1065 appDir = dirProvider->GetAppDir();
1067 appVersion = gAppData->version;
1068 argc = gRestartArgc;
1069 argv = gRestartArgv;
1070 } else {
1071 // In the xpcshell environment, the usual XRE_main is not run, so things
1072 // like dirProvider and gAppData do not exist. This code path accesses
1073 // XPCOM (which is not available in the previous code path) in order to get
1074 // the same information.
1075 nsCOMPtr<nsIProperties> ds =
1076 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
1077 if (!ds) {
1078 NS_ABORT(); // There's nothing which we can do if this fails!
1081 nsresult rv = ds->Get(NS_GRE_DIR, NS_GET_IID(nsIFile),
1082 getter_AddRefs(greDir));
1083 NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the GRE dir");
1084 appDir = greDir;
1086 rv = ds->Get(XRE_UPDATE_ROOT_DIR, NS_GET_IID(nsIFile),
1087 getter_AddRefs(updRoot));
1088 if (NS_FAILED(rv))
1089 updRoot = appDir;
1091 nsCOMPtr<nsIXULAppInfo> appInfo =
1092 do_GetService("@mozilla.org/xre/app-info;1");
1093 if (appInfo) {
1094 rv = appInfo->GetVersion(appVersion);
1095 NS_ENSURE_SUCCESS(rv, rv);
1096 } else {
1097 appVersion = MOZ_APP_VERSION;
1100 // We need argv[0] to point to the current executable's name. The rest of
1101 // the entries in this array will be ignored if argc<2. Therefore, for
1102 // xpcshell, we only fill out that item, and leave the rest empty.
1103 argc = 1;
1104 nsCOMPtr<nsIFile> binary;
1105 rv = ds->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile),
1106 getter_AddRefs(binary));
1107 NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the binary path");
1108 binary->GetNativePath(binPath);
1111 // Copy the parameters to the BackgroundUpdateInfo structure shared with the
1112 // watcher thread.
1113 mInfo.mGREDir = greDir;
1114 mInfo.mAppDir = appDir;
1115 mInfo.mUpdateRoot = updRoot;
1116 mInfo.mArgc = argc;
1117 mInfo.mArgv = new char*[argc];
1118 if (dirProvider) {
1119 for (int i = 0; i < argc; ++i) {
1120 const size_t length = strlen(argv[i]);
1121 mInfo.mArgv[i] = new char[length + 1];
1122 strcpy(mInfo.mArgv[i], argv[i]);
1124 } else {
1125 MOZ_ASSERT(argc == 1); // see above
1126 const size_t length = binPath.Length();
1127 mInfo.mArgv[0] = new char[length + 1];
1128 strcpy(mInfo.mArgv[0], binPath.get());
1130 mInfo.mAppVersion = appVersion;
1132 #if defined(MOZ_WIDGET_GONK)
1133 NS_ENSURE_ARG_POINTER(aUpdate);
1135 bool isOSUpdate;
1136 if (NS_SUCCEEDED(aUpdate->GetIsOSUpdate(&isOSUpdate)) &&
1137 isOSUpdate) {
1138 nsAutoCString osApplyToDir;
1140 // This needs to be done on the main thread, so we pass it along in
1141 // BackgroundThreadInfo
1142 nsresult rv = GetOSApplyToDir(osApplyToDir);
1143 NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the OS apply to dir");
1145 SetOSApplyToDir(aUpdate, osApplyToDir);
1147 mInfo.mIsOSUpdate = true;
1148 rv = NS_NewNativeLocalFile(osApplyToDir, false,
1149 getter_AddRefs(mInfo.mOSApplyToDir));
1150 NS_ASSERTION(NS_SUCCEEDED(rv), "Can't create nsIFile for OS apply to dir");
1152 #endif
1154 mUpdate = aUpdate;
1156 NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
1157 return NS_NewThread(getter_AddRefs(mProcessWatcher),
1158 NS_NewRunnableMethod(this, &nsUpdateProcessor::StartBackgroundUpdate));
1163 void
1164 nsUpdateProcessor::StartBackgroundUpdate()
1166 NS_ABORT_IF_FALSE(!NS_IsMainThread(), "main thread");
1168 nsresult rv = ProcessUpdates(mInfo.mGREDir,
1169 mInfo.mAppDir,
1170 mInfo.mUpdateRoot,
1171 mInfo.mArgc,
1172 mInfo.mArgv,
1173 mInfo.mAppVersion.get(),
1174 false,
1175 mInfo.mIsOSUpdate,
1176 mInfo.mOSApplyToDir,
1177 &mUpdaterPID);
1178 NS_ENSURE_SUCCESS_VOID(rv);
1180 if (mUpdaterPID) {
1181 // Track the state of the background updater process
1182 rv = NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::WaitForProcess));
1183 NS_ENSURE_SUCCESS_VOID(rv);
1184 } else {
1185 // Failed to launch the background updater process for some reason.
1186 // We need to shutdown the current thread as there isn't anything more for
1187 // us to do...
1188 rv = NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::ShutdownWatcherThread));
1189 NS_ENSURE_SUCCESS_VOID(rv);
1193 void
1194 nsUpdateProcessor::ShutdownWatcherThread()
1196 NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
1197 mProcessWatcher->Shutdown();
1198 mProcessWatcher = nullptr;
1199 mUpdate = nullptr;
1202 void
1203 nsUpdateProcessor::WaitForProcess()
1205 NS_ABORT_IF_FALSE(!NS_IsMainThread(), "main thread");
1206 ::WaitForProcess(mUpdaterPID);
1207 NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::UpdateDone));
1210 void
1211 nsUpdateProcessor::UpdateDone()
1213 NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
1215 nsCOMPtr<nsIUpdateManager> um =
1216 do_GetService("@mozilla.org/updates/update-manager;1");
1217 if (um && mUpdate) {
1218 um->RefreshUpdateStatus(mUpdate);
1221 ShutdownWatcherThread();