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/. */
9 #include "nsUpdateDriver.h"
10 #include "nsXULAppAPI.h"
11 #include "nsAppRunner.h"
12 #include "nsIWritablePropertyBag.h"
14 #include "nsIVariant.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"
31 #include "nsILocalFileMac.h"
32 #include "nsCommandLineServiceMac.h"
33 #include "MacLaunchHelper.h"
41 # include "nsWindowsHelpers.h"
42 # define getcwd(path, size) _getcwd(path, size)
43 # define getpid() GetCurrentProcessId()
46 # define INCL_DOSFILEMGR
48 #elif defined(XP_UNIX)
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
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
72 // A similar #define lives in updater.cpp and should be kept in sync with this.
74 #if defined(XP_UNIX) && !defined(XP_MACOSX)
79 static PRLogModuleInfo
*
82 static PRLogModuleInfo
*sUpdateLog
;
84 sUpdateLog
= PR_NewLogModule("updatedriver");
88 #define LOG(args) PR_LOG(GetUpdateLog(), PR_LOG_DEBUG, args)
91 static const char kUpdaterBin
[] = "updater.exe";
93 static const char kUpdaterBin
[] = "updater";
95 static const char kUpdaterINI
[] = "updater.ini";
97 static const char kUpdaterApp
[] = "updater.app";
99 #if defined(XP_UNIX) && !defined(XP_MACOSX)
100 static const char kUpdaterPNG
[] = "updater.png";
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
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:
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
);
136 if(!getcwd(buf
, size
))
137 return NS_ERROR_FAILURE
;
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.
147 GetXULRunnerStubPath(const char* argv0
, nsIFile
* *aResult
)
149 // Works even if we're not bundled.
150 CFBundleRef appBundle
= ::CFBundleGetMainBundle();
152 return NS_ERROR_FAILURE
;
154 CFURLRef bundleURL
= ::CFBundleCopyExecutableURL(appBundle
);
156 return NS_ERROR_FAILURE
;
158 nsCOMPtr
<nsILocalFileMac
> lfm
;
159 nsresult rv
= NS_NewLocalFileWithCFURL(bundleURL
, true, getter_AddRefs(lfm
));
161 ::CFRelease(bundleURL
);
166 NS_ADDREF(*aResult
= static_cast<nsIFile
*>(lfm
.get()));
169 #endif /* XP_MACOSX */
172 GetFile(nsIFile
*dir
, const nsCSubstring
&name
, nsCOMPtr
<nsIFile
> &result
)
176 nsCOMPtr
<nsIFile
> file
;
177 rv
= dir
->Clone(getter_AddRefs(file
));
181 rv
= file
->AppendNative(name
);
185 result
= do_QueryInterface(file
, &rv
);
186 return NS_SUCCEEDED(rv
);
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
>
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
);
215 const int32_t n
= PR_Read(fd
, buf
, Size
);
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.
238 GetUpdateStatus(nsIFile
* dir
, nsCOMPtr
<nsIFile
> &statusFile
)
240 if (GetStatusFile(dir
, statusFile
)) {
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
;
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
273 IsOlderVersion(nsIFile
*versionFile
, const char *appVersion
)
275 PRFileDesc
*fd
= nullptr;
276 nsresult rv
= versionFile
->OpenNSPRFileDesc(PR_RDONLY
, 0660, &fd
);
281 const int32_t n
= PR_Read(fd
, buf
, sizeof(buf
));
287 // Trim off the trailing newline
288 if (buf
[n
- 1] == '\n')
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)
297 if (mozilla::Version(appVersion
) > buf
)
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
));
313 rv
= file
->AppendNative(leaf
);
318 // Now, copy into the target location.
319 rv
= parentDir
->Clone(getter_AddRefs(file
));
322 rv
= file
->AppendNative(leaf
);
325 rv
= file
->CopyToNative(updateDir
, EmptyCString());
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
))
341 if (!CopyFileIntoUpdateDir(greDir
, kUpdaterBin
, updateDir
))
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
))
352 // Finally, return the location of the updater binary.
353 nsresult rv
= updateDir
->Clone(getter_AddRefs(updater
));
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
)) {
362 tmp
= updater
->AppendNative(NS_LITERAL_CSTRING("MacOS"));
363 if (NS_FAILED(tmp
) || NS_FAILED(rv
))
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
382 SwitchToUpdatedApp(nsIFile
*greDir
, nsIFile
*updateDir
, nsIFile
*statusFile
,
383 nsIFile
*appDir
, int appArgc
, char **appArgv
)
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
));
395 LOG(("failed getting a temp dir\n"));
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
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"));
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
));
425 XRE_GetBinaryPath(appArgv
[0], getter_AddRefs(appFile
));
432 nsAutoString appFilePathW
;
433 rv
= appFile
->GetPath(appFilePathW
);
436 NS_ConvertUTF16toUTF8
appFilePath(appFilePathW
);
438 nsAutoString updaterPathW
;
439 rv
= updater
->GetPath(updaterPathW
);
443 NS_ConvertUTF16toUTF8
updaterPath(updaterPathW
);
446 nsAutoCString appFilePath
;
447 #if defined(MOZ_WIDGET_GONK)
448 appFilePath
.Assign(kB2GServiceArgv
[0]);
449 appArgc
= kB2GServiceArgc
;
450 appArgv
= const_cast<char**>(kB2GServiceArgv
);
452 rv
= appFile
->GetNativePath(appFilePath
);
457 nsAutoCString updaterPath
;
458 rv
= updater
->GetNativePath(updaterPath
);
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
));
475 rv
= parentDir1
->GetParent(getter_AddRefs(parentDir2
));
478 if (!GetFile(parentDir2
, NS_LITERAL_CSTRING("Updated.app"), updatedDir
))
480 rv
= updatedDir
->GetNativePath(applyToDir
);
483 if (!GetFile(appDir
, NS_LITERAL_CSTRING("updated"), updatedDir
))
486 nsAutoString applyToDirW
;
487 rv
= updatedDir
->GetPath(applyToDirW
);
489 NS_ConvertUTF16toUTF8
applyToDir(applyToDirW
);
491 nsAutoCString applyToDir
;
492 rv
= updatedDir
->GetNativePath(applyToDir
);
498 // Make sure that the updated directory exists
499 bool updatedDirExists
= false;
500 updatedDir
->Exists(&updatedDirExists
);
501 if (!updatedDirExists
) {
506 nsAutoString updateDirPathW
;
507 rv
= updateDir
->GetPath(updateDirPathW
);
509 NS_ConvertUTF16toUTF8
updateDirPath(updateDirPathW
);
511 nsAutoCString updateDirPath
;
512 rv
= updateDir
->GetNativePath(updateDirPath
);
518 // Get the current working directory.
519 char workingDirPath
[MAXPATHLEN
];
520 rv
= GetCurrentWorkingDir(workingDirPath
, sizeof(workingDirPath
));
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");
530 pid
.AppendInt((int32_t) getpid());
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;
539 if (IsRunningInWindowsMetro()) {
543 int argc
= appArgc
+ 5 + immersiveArgc
;
544 char **argv
= new char*[argc
+ 1];
547 argv
[0] = (char*) updaterPath
.get();
548 argv
[1] = (char*) updateDirPath
.get();
549 argv
[2] = (char*) applyToDir
.get();
550 argv
[3] = (char*) pid
.get();
552 argv
[4] = workingDirPath
;
553 argv
[5] = (char*) appFilePath
.get();
554 for (int i
= 1; i
< appArgc
; ++i
)
555 argv
[5 + i
] = appArgv
[i
];
558 argv
[argc
- 1] = "-ServerName:DefaultBrowserServer";
561 argv
[argc
] = nullptr;
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");
580 execv(updaterPath
.get(), argv
);
581 #elif defined(XP_WIN)
582 // Switch the application using updater.exe
583 if (!WinLaunchChild(updaterPathW
.get(), argc
, argv
)) {
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
);
595 PR_CreateProcessDetached(updaterPath
.get(), argv
, nullptr, nullptr);
600 #if defined(MOZ_WIDGET_GONK)
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
);
617 SetOSApplyToDir(nsIUpdate
* update
, const nsACString
& osApplyToDir
)
620 nsCOMPtr
<nsIWritablePropertyBag
> updateProperties
=
621 do_QueryInterface(update
, &rv
);
627 nsCOMPtr
<nsIWritableVariant
> variant
=
628 do_CreateInstance("@mozilla.org/variant;1", &rv
);
633 rv
= variant
->SetAsACString(osApplyToDir
);
638 updateProperties
->SetProperty(NS_LITERAL_STRING("osApplyToDir"), variant
);
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.
658 ApplyUpdate(nsIFile
*greDir
, nsIFile
*updateDir
, nsIFile
*statusFile
,
659 nsIFile
*appDir
, int appArgc
, char **appArgv
,
660 bool restart
, bool isOSUpdate
, nsIFile
*osApplyToDir
,
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"));
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
));
685 XRE_GetBinaryPath(appArgv
[0], getter_AddRefs(appFile
));
692 nsAutoString appFilePathW
;
693 rv
= appFile
->GetPath(appFilePathW
);
696 NS_ConvertUTF16toUTF8
appFilePath(appFilePathW
);
698 nsAutoString updaterPathW
;
699 rv
= updater
->GetPath(updaterPathW
);
703 NS_ConvertUTF16toUTF8
updaterPath(updaterPathW
);
706 nsAutoCString appFilePath
;
707 rv
= appFile
->GetNativePath(appFilePath
);
711 nsAutoCString updaterPath
;
712 rv
= updater
->GetNativePath(updaterPath
);
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
));
730 rv
= parentDir1
->GetParent(getter_AddRefs(parentDir2
));
734 // Use the correct directory if we're not applying the update in the
736 rv
= parentDir2
->GetNativePath(applyToDir
);
738 if (!GetFile(parentDir2
, NS_LITERAL_CSTRING("Updated.app"), updatedDir
))
740 rv
= updatedDir
->GetNativePath(applyToDir
);
745 // Use the correct directory if we're not applying the update in the
747 updatedDir
= do_QueryInterface(appDir
);
748 } else if (!GetFile(appDir
, NS_LITERAL_CSTRING("updated"), updatedDir
)) {
752 nsAutoString applyToDirW
;
753 rv
= updatedDir
->GetPath(applyToDirW
);
755 NS_ConvertUTF16toUTF8
applyToDir(applyToDirW
);
757 nsAutoCString applyToDir
;
759 #if defined(MOZ_WIDGET_GONK)
765 rv
= osApplyToDir
->GetNativePath(applyToDir
);
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)
782 nsAutoString updateDirPathW
;
783 rv
= updateDir
->GetPath(updateDirPathW
);
785 NS_ConvertUTF16toUTF8
updateDirPath(updateDirPathW
);
787 nsAutoCString updateDirPath
;
788 rv
= updateDir
->GetNativePath(updateDirPath
);
794 // Get the current working directory.
795 char workingDirPath
[MAXPATHLEN
];
796 rv
= GetCurrentWorkingDir(workingDirPath
, sizeof(workingDirPath
));
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.
810 // Signal the updater application that it should apply the update in the
812 pid
.AssignASCII("-1");
814 #if defined(USE_EXECV)
815 pid
.AssignASCII("0");
817 pid
.AppendInt((int32_t) getpid());
821 int immersiveArgc
= 0;
823 if (IsRunningInWindowsMetro()) {
827 int argc
= appArgc
+ 5 + immersiveArgc
;
828 char **argv
= new char*[argc
+ 1 ];
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
];
842 argv
[argc
- 1] = "-ServerName:DefaultBrowserServer";
845 argv
[argc
] = nullptr;
852 PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
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());
876 LOG(("spawning updater process [%s]\n", updaterPath
.get()));
878 #if defined(USE_EXECV)
879 // Don't use execv for background updates.
881 execv(updaterPath
.get(), argv
);
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
)) {
892 // We are going to process an update so we should exit now
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
);
905 *outpid
= PR_CreateProcess(updaterPath
.get(), argv
, nullptr, nullptr);
913 * Wait for a process until it terminates. This call is blocking.
916 WaitForProcess(ProcessType pt
)
919 WaitForSingleObject(pt
, INFINITE
);
921 #elif defined(XP_MACOSX)
925 PR_WaitProcess(pt
, &exitCode
);
927 LOG(("Error while running the updater process, check update.log"));
933 ProcessUpdates(nsIFile
*greDir
, nsIFile
*appDir
, nsIFile
*updRootDir
,
934 int argc
, char **argv
, const char *appVersion
,
935 bool restart
, bool isOSUpdate
, nsIFile
*osApplyToDir
,
940 nsCOMPtr
<nsIFile
> updatesDir
;
941 rv
= updRootDir
->Clone(getter_AddRefs(updatesDir
));
945 rv
= updatesDir
->AppendNative(NS_LITERAL_CSTRING("updates"));
949 rv
= updatesDir
->AppendNative(NS_LITERAL_CSTRING("0"));
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
));
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
));
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
) {
986 nsCOMPtr
<nsIFile
> statusFile
;
987 UpdateStatus status
= GetUpdateStatus(updatesDir
, statusFile
);
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);
999 ApplyUpdate(greDir
, updatesDir
, statusFile
,
1000 appDir
, argc
, argv
, restart
, isOSUpdate
, osApplyToDir
, pid
);
1004 case eAppliedUpdate
:
1005 case eAppliedService
:
1006 // An update was applied in the background, so we need to switch to using
1008 SwitchToUpdatedApp(greDir
, updatesDir
, statusFile
,
1009 appDir
, argc
, argv
);
1011 case eNoUpdateAction
:
1012 // We don't need to do any special processing here, we'll just continue to
1013 // startup the application.
1022 NS_IMPL_ISUPPORTS1(nsUpdateProcessor
, nsIUpdateProcessor
)
1024 nsUpdateProcessor::nsUpdateProcessor()
1030 nsUpdateProcessor::ProcessUpdate(nsIUpdate
* aUpdate
)
1032 nsCOMPtr
<nsIFile
> greDir
, appDir
, updRoot
;
1033 nsAutoCString appVersion
;
1037 nsAutoCString binPath
;
1038 nsXREDirProvider
* dirProvider
= nsXREDirProvider::GetSingleton();
1039 if (dirProvider
) { // Normal code path
1040 // Check for and process any available updates
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
));
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
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
));
1065 appDir
= dirProvider
->GetAppDir();
1067 appVersion
= gAppData
->version
;
1068 argc
= gRestartArgc
;
1069 argv
= gRestartArgv
;
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
);
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");
1086 rv
= ds
->Get(XRE_UPDATE_ROOT_DIR
, NS_GET_IID(nsIFile
),
1087 getter_AddRefs(updRoot
));
1091 nsCOMPtr
<nsIXULAppInfo
> appInfo
=
1092 do_GetService("@mozilla.org/xre/app-info;1");
1094 rv
= appInfo
->GetVersion(appVersion
);
1095 NS_ENSURE_SUCCESS(rv
, rv
);
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.
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
1113 mInfo
.mGREDir
= greDir
;
1114 mInfo
.mAppDir
= appDir
;
1115 mInfo
.mUpdateRoot
= updRoot
;
1117 mInfo
.mArgv
= new char*[argc
];
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
]);
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
);
1136 if (NS_SUCCEEDED(aUpdate
->GetIsOSUpdate(&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");
1156 NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
1157 return NS_NewThread(getter_AddRefs(mProcessWatcher
),
1158 NS_NewRunnableMethod(this, &nsUpdateProcessor::StartBackgroundUpdate
));
1164 nsUpdateProcessor::StartBackgroundUpdate()
1166 NS_ABORT_IF_FALSE(!NS_IsMainThread(), "main thread");
1168 nsresult rv
= ProcessUpdates(mInfo
.mGREDir
,
1173 mInfo
.mAppVersion
.get(),
1176 mInfo
.mOSApplyToDir
,
1178 NS_ENSURE_SUCCESS_VOID(rv
);
1181 // Track the state of the background updater process
1182 rv
= NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::WaitForProcess
));
1183 NS_ENSURE_SUCCESS_VOID(rv
);
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
1188 rv
= NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::ShutdownWatcherThread
));
1189 NS_ENSURE_SUCCESS_VOID(rv
);
1194 nsUpdateProcessor::ShutdownWatcherThread()
1196 NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
1197 mProcessWatcher
->Shutdown();
1198 mProcessWatcher
= nullptr;
1203 nsUpdateProcessor::WaitForProcess()
1205 NS_ABORT_IF_FALSE(!NS_IsMainThread(), "main thread");
1206 ::WaitForProcess(mUpdaterPID
);
1207 NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::UpdateDone
));
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();