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()
44 #elif defined(XP_UNIX)
48 using namespace mozilla
;
51 // We use execv to spawn the updater process on all UNIX systems except Mac OSX
52 // since it is known to cause problems on the Mac. Windows has execv, but it
53 // is a faked implementation that doesn't really replace the current process.
54 // Instead it spawns a new process, so we gain nothing from using execv on
57 // On platforms where we are not calling execv, we may need to make the
58 // updater executable wait for the calling process to exit. Otherwise, the
59 // updater may have trouble modifying our executable image (because it might
60 // still be in use). This is accomplished by passing our PID to the updater so
61 // that it can wait for us to exit. This is not perfect as there is a race
62 // condition that could bite us. It's possible that the calling process could
63 // exit before the updater waits on the specified PID, and in the meantime a
64 // new process with the same PID could be created. This situation is unlikely,
65 // however, given the way most operating systems recycle PIDs. We'll take our
68 // A similar #define lives in updater.cpp and should be kept in sync with this.
70 #if defined(XP_UNIX) && !defined(XP_MACOSX)
75 static PRLogModuleInfo
*
78 static PRLogModuleInfo
*sUpdateLog
;
80 sUpdateLog
= PR_NewLogModule("updatedriver");
84 #define LOG(args) PR_LOG(GetUpdateLog(), PR_LOG_DEBUG, args)
87 static const char kUpdaterBin
[] = "updater.exe";
89 static const char kUpdaterBin
[] = "updater";
91 static const char kUpdaterINI
[] = "updater.ini";
93 static const char kUpdaterApp
[] = "updater.app";
95 #if defined(XP_UNIX) && !defined(XP_MACOSX)
96 static const char kUpdaterPNG
[] = "updater.png";
99 #if defined(MOZ_WIDGET_GONK)
100 #include <linux/ioprio.h>
102 static const int kB2GServiceArgc
= 2;
103 static const char *kB2GServiceArgv
[] = { "/system/bin/start", "b2g" };
105 static const char kAppUpdaterPrio
[] = "app.update.updater.prio";
106 static const char kAppUpdaterOomScoreAdj
[] = "app.update.updater.oom_score_adj";
107 static const char kAppUpdaterIOPrioClass
[] = "app.update.updater.ioprio.class";
108 static const char kAppUpdaterIOPrioLevel
[] = "app.update.updater.ioprio.level";
110 static const int kAppUpdaterPrioDefault
= 19; // -20..19 where 19 = lowest priority
111 static const int kAppUpdaterOomScoreAdjDefault
= -1000; // -1000 = Never kill
112 static const int kAppUpdaterIOPrioClassDefault
= IOPRIO_CLASS_IDLE
;
113 static const int kAppUpdaterIOPrioLevelDefault
= 0; // Doesn't matter for CLASS IDLE
117 GetCurrentWorkingDir(char *buf
, size_t size
)
119 // Cannot use NS_GetSpecialDirectory because XPCOM is not yet initialized.
120 // This code is duplicated from xpcom/io/SpecialSystemDirectory.cpp:
123 wchar_t wpath
[MAX_PATH
];
124 if (!_wgetcwd(wpath
, size
))
125 return NS_ERROR_FAILURE
;
126 NS_ConvertUTF16toUTF8
path(wpath
);
127 strncpy(buf
, path
.get(), size
);
129 if(!getcwd(buf
, size
))
130 return NS_ERROR_FAILURE
;
136 * Get the path to the installation directory. For Mac OS X this will be the
139 * @param appDir the application directory file object
140 * @param installDirPath the path to the installation directory
143 GetInstallDirPath(nsIFile
*appDir
, nsACString
& installDirPath
)
147 nsCOMPtr
<nsIFile
> parentDir1
, parentDir2
;
148 rv
= appDir
->GetParent(getter_AddRefs(parentDir1
));
152 rv
= parentDir1
->GetParent(getter_AddRefs(parentDir2
));
156 rv
= parentDir2
->GetNativePath(installDirPath
);
158 nsAutoString installDirPathW
;
159 rv
= appDir
->GetPath(installDirPathW
);
163 installDirPath
= NS_ConvertUTF16toUTF8(installDirPathW
);
165 rv
= appDir
->GetNativePath(installDirPath
);
173 #if defined(XP_MACOSX)
174 // This is a copy of OS X's XRE_GetBinaryPath from nsAppRunner.cpp with the
175 // gBinaryPath check removed so that the updater can reload the stub executable
176 // instead of xulrunner-bin. See bug 349737.
178 GetXULRunnerStubPath(const char* argv0
, nsIFile
* *aResult
)
180 // Works even if we're not bundled.
181 CFBundleRef appBundle
= ::CFBundleGetMainBundle();
183 return NS_ERROR_FAILURE
;
185 CFURLRef bundleURL
= ::CFBundleCopyExecutableURL(appBundle
);
187 return NS_ERROR_FAILURE
;
189 nsCOMPtr
<nsILocalFileMac
> lfm
;
190 nsresult rv
= NS_NewLocalFileWithCFURL(bundleURL
, true, getter_AddRefs(lfm
));
192 ::CFRelease(bundleURL
);
197 NS_ADDREF(*aResult
= static_cast<nsIFile
*>(lfm
.get()));
200 #endif /* XP_MACOSX */
203 GetFile(nsIFile
*dir
, const nsCSubstring
&name
, nsCOMPtr
<nsIFile
> &result
)
207 nsCOMPtr
<nsIFile
> file
;
208 rv
= dir
->Clone(getter_AddRefs(file
));
212 rv
= file
->AppendNative(name
);
216 result
= do_QueryInterface(file
, &rv
);
217 return NS_SUCCEEDED(rv
);
221 GetStatusFile(nsIFile
*dir
, nsCOMPtr
<nsIFile
> &result
)
223 return GetFile(dir
, NS_LITERAL_CSTRING("update.status"), result
);
227 * Get the contents of the update.status file.
229 * @param statusFile the status file object.
230 * @param buf the buffer holding the file contents
232 * @return true if successful, false otherwise.
234 template <size_t Size
>
236 GetStatusFileContents(nsIFile
*statusFile
, char (&buf
)[Size
])
238 // The buffer needs to be large enough to hold the known status codes
239 PR_STATIC_ASSERT(Size
> 16);
241 PRFileDesc
*fd
= nullptr;
242 nsresult rv
= statusFile
->OpenNSPRFileDesc(PR_RDONLY
, 0660, &fd
);
246 const int32_t n
= PR_Read(fd
, buf
, Size
);
261 * Returns a value indicating what needs to be done in order to handle an update.
263 * @param dir the directory in which we should look for an update.status file.
264 * @param statusFile the update.status file found in the directory.
266 * @return the update action to be performed.
269 GetUpdateStatus(nsIFile
* dir
, nsCOMPtr
<nsIFile
> &statusFile
)
271 if (GetStatusFile(dir
, statusFile
)) {
273 if (GetStatusFileContents(statusFile
, buf
)) {
274 const char kPending
[] = "pending";
275 const char kPendingService
[] = "pending-service";
276 const char kApplied
[] = "applied";
277 const char kAppliedService
[] = "applied-service";
278 if (!strncmp(buf
, kPendingService
, sizeof(kPendingService
) - 1)) {
279 return ePendingService
;
281 if (!strncmp(buf
, kPending
, sizeof(kPending
) - 1)) {
282 return ePendingUpdate
;
284 if (!strncmp(buf
, kAppliedService
, sizeof(kAppliedService
) - 1)) {
285 return eAppliedService
;
287 if (!strncmp(buf
, kApplied
, sizeof(kApplied
) - 1)) {
288 return eAppliedUpdate
;
292 return eNoUpdateAction
;
296 GetVersionFile(nsIFile
*dir
, nsCOMPtr
<nsIFile
> &result
)
298 return GetFile(dir
, NS_LITERAL_CSTRING("update.version"), result
);
301 // Compares the current application version with the update's application
304 IsOlderVersion(nsIFile
*versionFile
, const char *appVersion
)
306 PRFileDesc
*fd
= nullptr;
307 nsresult rv
= versionFile
->OpenNSPRFileDesc(PR_RDONLY
, 0660, &fd
);
312 const int32_t n
= PR_Read(fd
, buf
, sizeof(buf
));
318 // Trim off the trailing newline
319 if (buf
[n
- 1] == '\n')
322 // If the update xml doesn't provide the application version the file will
323 // contain the string "null" and it is assumed that the update is not older.
324 const char kNull
[] = "null";
325 if (strncmp(buf
, kNull
, sizeof(kNull
) - 1) == 0)
328 if (mozilla::Version(appVersion
) > buf
)
334 #if defined(XP_WIN) && defined(MOZ_METRO)
336 IsWindowsMetroUpdateRequest(int appArgc
, char **appArgv
)
338 for (int index
= 0; index
< appArgc
; index
++) {
339 if (!strcmp(appArgv
[index
], "--metro-update")) {
348 CopyFileIntoUpdateDir(nsIFile
*parentDir
, const char *leafName
, nsIFile
*updateDir
)
350 nsDependentCString
leaf(leafName
);
351 nsCOMPtr
<nsIFile
> file
;
353 // Make sure there is not an existing file in the target location.
354 nsresult rv
= updateDir
->Clone(getter_AddRefs(file
));
357 rv
= file
->AppendNative(leaf
);
362 // Now, copy into the target location.
363 rv
= parentDir
->Clone(getter_AddRefs(file
));
366 rv
= file
->AppendNative(leaf
);
369 rv
= file
->CopyToNative(updateDir
, EmptyCString());
377 CopyUpdaterIntoUpdateDir(nsIFile
*greDir
, nsIFile
*appDir
, nsIFile
*updateDir
,
378 nsCOMPtr
<nsIFile
> &updater
)
380 // Copy the updater application from the GRE and the updater ini from the app
381 #if defined(XP_MACOSX)
382 if (!CopyFileIntoUpdateDir(appDir
, kUpdaterApp
, updateDir
))
384 CopyFileIntoUpdateDir(greDir
, kUpdaterINI
, updateDir
);
386 if (!CopyFileIntoUpdateDir(greDir
, kUpdaterBin
, updateDir
))
388 CopyFileIntoUpdateDir(appDir
, kUpdaterINI
, updateDir
);
390 #if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(ANDROID)
391 nsCOMPtr
<nsIFile
> iconDir
;
392 appDir
->Clone(getter_AddRefs(iconDir
));
393 iconDir
->AppendNative(NS_LITERAL_CSTRING("icons"));
394 if (!CopyFileIntoUpdateDir(iconDir
, kUpdaterPNG
, updateDir
))
397 // Finally, return the location of the updater binary.
398 nsresult rv
= updateDir
->Clone(getter_AddRefs(updater
));
401 #if defined(XP_MACOSX)
402 rv
= updater
->AppendNative(NS_LITERAL_CSTRING(kUpdaterApp
));
403 nsresult tmp
= updater
->AppendNative(NS_LITERAL_CSTRING("Contents"));
404 if (NS_FAILED(tmp
)) {
407 tmp
= updater
->AppendNative(NS_LITERAL_CSTRING("MacOS"));
408 if (NS_FAILED(tmp
) || NS_FAILED(rv
))
411 rv
= updater
->AppendNative(NS_LITERAL_CSTRING(kUpdaterBin
));
412 return NS_SUCCEEDED(rv
);
416 * Switch an existing application directory to an updated version that has been
419 * @param greDir the GRE dir
420 * @param updateDir the update root dir
421 * @param statusFile the update.status file
422 * @param appDir the app dir
423 * @param appArgc the number of args to the application
424 * @param appArgv the args to the application, used for restarting if needed
427 SwitchToUpdatedApp(nsIFile
*greDir
, nsIFile
*updateDir
, nsIFile
*statusFile
,
428 nsIFile
*appDir
, int appArgc
, char **appArgv
)
433 // - copy updater into temp dir
434 // - run updater with the correct arguments
436 nsCOMPtr
<nsIFile
> tmpDir
;
437 GetSpecialSystemDirectory(OS_TemporaryDirectory
,
438 getter_AddRefs(tmpDir
));
440 LOG(("failed getting a temp dir\n"));
444 // Try to create our own new temp directory in case there is already an
445 // updater binary in the OS temporary location which we cannot write to.
446 // Note that we don't check for errors here, as if this directory can't
447 // be created, the following CopyUpdaterIntoUpdateDir call will fail.
448 // We create the unique directory inside a subfolder of MozUpdater instead
449 // of directly in the temp directory so we can efficiently delete everything
451 tmpDir
->Append(NS_LITERAL_STRING("MozUpdater"));
452 tmpDir
->Append(NS_LITERAL_STRING("bgupdate"));
453 tmpDir
->CreateUnique(nsIFile::DIRECTORY_TYPE
, 0755);
455 nsCOMPtr
<nsIFile
> updater
;
456 if (!CopyUpdaterIntoUpdateDir(greDir
, appDir
, tmpDir
, updater
)) {
457 LOG(("failed copying updater\n"));
461 // We need to use the value returned from XRE_GetBinaryPath when attempting
462 // to restart the running application.
463 nsCOMPtr
<nsIFile
> appFile
;
465 #if defined(XP_MACOSX)
466 // On OS X we need to pass the location of the xulrunner-stub executable
467 // rather than xulrunner-bin. See bug 349737.
468 GetXULRunnerStubPath(appArgv
[0], getter_AddRefs(appFile
));
470 XRE_GetBinaryPath(appArgv
[0], getter_AddRefs(appFile
));
477 nsAutoString appFilePathW
;
478 rv
= appFile
->GetPath(appFilePathW
);
481 NS_ConvertUTF16toUTF8
appFilePath(appFilePathW
);
483 nsAutoString updaterPathW
;
484 rv
= updater
->GetPath(updaterPathW
);
488 NS_ConvertUTF16toUTF8
updaterPath(updaterPathW
);
491 nsAutoCString appFilePath
;
492 #if defined(MOZ_WIDGET_GONK)
493 appFilePath
.Assign(kB2GServiceArgv
[0]);
494 appArgc
= kB2GServiceArgc
;
495 appArgv
= const_cast<char**>(kB2GServiceArgv
);
497 rv
= appFile
->GetNativePath(appFilePath
);
502 nsAutoCString updaterPath
;
503 rv
= updater
->GetNativePath(updaterPath
);
508 nsAutoCString installDirPath
;
509 rv
= GetInstallDirPath(appDir
, installDirPath
);
514 // Get the directory where the update will be staged.
515 nsAutoCString applyToDir
;
516 nsCOMPtr
<nsIFile
> updatedDir
;
518 if (!GetFile(updateDir
, NS_LITERAL_CSTRING("Updated.app"), updatedDir
)) {
520 if (!GetFile(appDir
, NS_LITERAL_CSTRING("updated"), updatedDir
)) {
525 nsAutoString applyToDirW
;
526 rv
= updatedDir
->GetPath(applyToDirW
);
530 applyToDir
= NS_ConvertUTF16toUTF8(applyToDirW
);
532 rv
= updatedDir
->GetNativePath(applyToDir
);
538 // Make sure that the updated directory exists
539 bool updatedDirExists
= false;
540 updatedDir
->Exists(&updatedDirExists
);
541 if (!updatedDirExists
) {
546 nsAutoString updateDirPathW
;
547 rv
= updateDir
->GetPath(updateDirPathW
);
548 NS_ConvertUTF16toUTF8
updateDirPath(updateDirPathW
);
550 nsAutoCString updateDirPath
;
551 rv
= updateDir
->GetNativePath(updateDirPath
);
556 // Get the current working directory.
557 char workingDirPath
[MAXPATHLEN
];
558 rv
= GetCurrentWorkingDir(workingDirPath
, sizeof(workingDirPath
));
562 // Construct the PID argument for this process. If we are using execv, then
563 // we pass "0" which is then ignored by the updater.
564 #if defined(USE_EXECV)
565 nsAutoCString
pid("0");
568 pid
.AppendInt((int32_t) getpid());
571 // Append a special token to the PID in order to let the updater know that it
572 // just needs to replace the update directory.
573 pid
.AppendLiteral("/replace");
575 int immersiveArgc
= 0;
576 #if defined(XP_WIN) && defined(MOZ_METRO)
577 // If this is desktop doing an update for metro, or if we're the metro browser
578 // we want to launch the metro browser after we're finished.
579 if (IsWindowsMetroUpdateRequest(appArgc
, appArgv
) || IsRunningInWindowsMetro()) {
583 int argc
= appArgc
+ 6 + immersiveArgc
;
584 char **argv
= new char*[argc
+ 1];
587 argv
[0] = (char*) updaterPath
.get();
588 argv
[1] = (char*) updateDirPath
.get();
589 argv
[2] = (char*) installDirPath
.get();
590 argv
[3] = (char*) applyToDir
.get();
591 argv
[4] = (char*) pid
.get();
593 argv
[5] = workingDirPath
;
594 argv
[6] = (char*) appFilePath
.get();
595 for (int i
= 1; i
< appArgc
; ++i
)
596 argv
[6 + i
] = appArgv
[i
];
599 argv
[argc
- 1] = "-ServerName:DefaultBrowserServer";
602 argv
[argc
] = nullptr;
609 PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
612 LOG(("spawning updater process for replacing [%s]\n", updaterPath
.get()));
614 #if defined(USE_EXECV)
615 # if defined(MOZ_WIDGET_GONK)
616 // In Gonk, we preload libmozglue, which the updater process doesn't need.
617 // Since the updater will move and delete libmozglue.so, this can actually
618 // stop the /system mount from correctly being remounted as read-only.
619 unsetenv("LD_PRELOAD");
621 execv(updaterPath
.get(), argv
);
622 #elif defined(XP_WIN)
623 // Switch the application using updater.exe
624 if (!WinLaunchChild(updaterPathW
.get(), argc
, argv
)) {
628 #elif defined(XP_MACOSX)
629 CommandLineServiceMac::SetupMacCommandLine(argc
, argv
, true);
630 // LaunchChildMac uses posix_spawnp and prefers the current
631 // architecture when launching. It doesn't require a
632 // null-terminated string but it doesn't matter if we pass one.
633 LaunchChildMac(argc
, argv
);
636 PR_CreateProcessDetached(updaterPath
.get(), argv
, nullptr, nullptr);
641 #if defined(MOZ_WIDGET_GONK)
643 GetOSApplyToDir(nsACString
& applyToDir
)
645 nsCOMPtr
<nsIProperties
> ds
=
646 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID
);
647 NS_ASSERTION(ds
, "Can't get directory service");
649 nsCOMPtr
<nsIFile
> osApplyToDir
;
650 nsresult rv
= ds
->Get(XRE_OS_UPDATE_APPLY_TO_DIR
, NS_GET_IID(nsIFile
),
651 getter_AddRefs(osApplyToDir
));
653 LOG(("Can't get the OS applyTo dir"));
657 return osApplyToDir
->GetNativePath(applyToDir
);
661 SetOSApplyToDir(nsIUpdate
* update
, const nsACString
& osApplyToDir
)
664 nsCOMPtr
<nsIWritablePropertyBag
> updateProperties
=
665 do_QueryInterface(update
, &rv
);
671 nsCOMPtr
<nsIWritableVariant
> variant
=
672 do_CreateInstance("@mozilla.org/variant;1", &rv
);
677 rv
= variant
->SetAsACString(osApplyToDir
);
682 updateProperties
->SetProperty(NS_LITERAL_STRING("osApplyToDir"), variant
);
687 * Apply an update. This applies to both normal and staged updates.
689 * @param greDir the GRE dir
690 * @param updateDir the update root dir
691 * @param statusFile the update.status file
692 * @param appDir the app dir
693 * @param appArgc the number of args to the application
694 * @param appArgv the args to the application, used for restarting if needed
695 * @param restart if true, apply the update in the foreground and restart the
696 * application when done. otherwise, stage the update and don't
697 * restart the application.
698 * @param outpid out parameter holding the handle to the updater application for
702 ApplyUpdate(nsIFile
*greDir
, nsIFile
*updateDir
, nsIFile
*statusFile
,
703 nsIFile
*appDir
, int appArgc
, char **appArgv
,
704 bool restart
, bool isOSUpdate
, nsIFile
*osApplyToDir
,
710 // - mark update as 'applying'
711 // - copy updater into update dir
712 // - run updater w/ appDir as the current working dir
714 nsCOMPtr
<nsIFile
> updater
;
715 if (!CopyUpdaterIntoUpdateDir(greDir
, appDir
, updateDir
, updater
)) {
716 LOG(("failed copying updater\n"));
720 // We need to use the value returned from XRE_GetBinaryPath when attempting
721 // to restart the running application.
722 nsCOMPtr
<nsIFile
> appFile
;
724 #if defined(XP_MACOSX)
725 // On OS X we need to pass the location of the xulrunner-stub executable
726 // rather than xulrunner-bin. See bug 349737.
727 GetXULRunnerStubPath(appArgv
[0], getter_AddRefs(appFile
));
729 XRE_GetBinaryPath(appArgv
[0], getter_AddRefs(appFile
));
736 nsAutoString appFilePathW
;
737 rv
= appFile
->GetPath(appFilePathW
);
740 NS_ConvertUTF16toUTF8
appFilePath(appFilePathW
);
742 nsAutoString updaterPathW
;
743 rv
= updater
->GetPath(updaterPathW
);
747 NS_ConvertUTF16toUTF8
updaterPath(updaterPathW
);
750 nsAutoCString appFilePath
;
751 rv
= appFile
->GetNativePath(appFilePath
);
755 nsAutoCString updaterPath
;
756 rv
= updater
->GetNativePath(updaterPath
);
762 nsAutoCString installDirPath
;
763 rv
= GetInstallDirPath(appDir
, installDirPath
);
767 // Get the directory where the update was staged for replace and GONK OS
768 // Updates or where it will be applied.
769 #ifndef MOZ_WIDGET_GONK
770 // OS Updates are only supported on GONK so force it to false on everything
771 // but GONK to simplify the following logic.
774 nsAutoCString applyToDir
;
775 nsCOMPtr
<nsIFile
> updatedDir
;
776 if (restart
&& !isOSUpdate
) {
777 applyToDir
.Assign(installDirPath
);
780 if (!GetFile(updateDir
, NS_LITERAL_CSTRING("Updated.app"), updatedDir
)) {
782 if (!GetFile(appDir
, NS_LITERAL_CSTRING("updated"), updatedDir
)) {
787 nsAutoString applyToDirW
;
788 rv
= updatedDir
->GetPath(applyToDirW
);
792 applyToDir
= NS_ConvertUTF16toUTF8(applyToDirW
);
793 #elif MOZ_WIDGET_GONK
798 rv
= osApplyToDir
->GetNativePath(applyToDir
);
800 rv
= updatedDir
->GetNativePath(applyToDir
);
803 rv
= updatedDir
->GetNativePath(applyToDir
);
810 nsAutoString updateDirPathW
;
811 rv
= updateDir
->GetPath(updateDirPathW
);
812 NS_ConvertUTF16toUTF8
updateDirPath(updateDirPathW
);
814 nsAutoCString updateDirPath
;
815 rv
= updateDir
->GetNativePath(updateDirPath
);
821 // Get the current working directory.
822 char workingDirPath
[MAXPATHLEN
];
823 rv
= GetCurrentWorkingDir(workingDirPath
, sizeof(workingDirPath
));
827 // We used to write out "Applying" to the update.status file here.
828 // Instead we do this from within the updater application now.
829 // This is so that we don't overwrite the status of pending-service
830 // in the Windows case. This change was made for all platforms so
831 // that it stays consistent across all OS.
833 // Construct the PID argument for this process. If we are using execv, then
834 // we pass "0" which is then ignored by the updater.
837 // Signal the updater application that it should stage the update.
838 pid
.AssignASCII("-1");
840 #if defined(USE_EXECV)
841 pid
.AssignASCII("0");
843 pid
.AppendInt((int32_t) getpid());
847 int immersiveArgc
= 0;
848 #if defined(XP_WIN) && defined(MOZ_METRO)
849 // If this is desktop doing an update for metro, or if we're the metro browser
850 // we want to launch the metro browser after we're finished.
851 if (IsWindowsMetroUpdateRequest(appArgc
, appArgv
) || IsRunningInWindowsMetro()) {
855 int argc
= appArgc
+ 6 + immersiveArgc
;
856 char **argv
= new char*[argc
+ 1 ];
859 argv
[0] = (char*) updaterPath
.get();
860 argv
[1] = (char*) updateDirPath
.get();
861 argv
[2] = (char*) installDirPath
.get();
862 argv
[3] = (char*) applyToDir
.get();
863 argv
[4] = (char*) pid
.get();
864 if (restart
&& appArgc
) {
865 argv
[5] = workingDirPath
;
866 argv
[6] = (char*) appFilePath
.get();
867 for (int i
= 1; i
< appArgc
; ++i
)
868 argv
[6 + i
] = appArgv
[i
];
871 argv
[argc
- 1] = "-ServerName:DefaultBrowserServer";
874 argv
[argc
] = nullptr;
881 PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
885 PR_SetEnv("MOZ_OS_UPDATE=1");
887 #if defined(MOZ_WIDGET_GONK)
888 // We want the updater to be CPU friendly and not subject to being killed by
889 // the low memory killer, so we pass in some preferences to allow it to
890 // adjust its priority.
892 int32_t prioVal
= Preferences::GetInt(kAppUpdaterPrio
,
893 kAppUpdaterPrioDefault
);
894 int32_t oomScoreAdj
= Preferences::GetInt(kAppUpdaterOomScoreAdj
,
895 kAppUpdaterOomScoreAdjDefault
);
896 int32_t ioprioClass
= Preferences::GetInt(kAppUpdaterIOPrioClass
,
897 kAppUpdaterIOPrioClassDefault
);
898 int32_t ioprioLevel
= Preferences::GetInt(kAppUpdaterIOPrioLevel
,
899 kAppUpdaterIOPrioLevelDefault
);
900 nsPrintfCString
prioEnv("MOZ_UPDATER_PRIO=%d/%d/%d/%d",
901 prioVal
, oomScoreAdj
, ioprioClass
, ioprioLevel
);
902 PR_SetEnv(prioEnv
.get());
905 LOG(("spawning updater process [%s]\n", updaterPath
.get()));
907 #if defined(USE_EXECV)
908 // Don't use execv when staging updates.
910 execv(updaterPath
.get(), argv
);
912 *outpid
= PR_CreateProcess(updaterPath
.get(), argv
, nullptr, nullptr);
914 #elif defined(XP_WIN)
915 // Launch the update using updater.exe
916 if (!WinLaunchChild(updaterPathW
.get(), argc
, argv
, nullptr, outpid
)) {
921 // We are going to process an update so we should exit now
924 #elif defined(XP_MACOSX)
925 CommandLineServiceMac::SetupMacCommandLine(argc
, argv
, true);
926 // LaunchChildMac uses posix_spawnp and prefers the current
927 // architecture when launching. It doesn't require a
928 // null-terminated string but it doesn't matter if we pass one.
929 LaunchChildMac(argc
, argv
, 0, outpid
);
934 *outpid
= PR_CreateProcess(updaterPath
.get(), argv
, nullptr, nullptr);
942 * Wait for a process until it terminates. This call is blocking.
945 WaitForProcess(ProcessType pt
)
948 WaitForSingleObject(pt
, INFINITE
);
950 #elif defined(XP_MACOSX)
954 PR_WaitProcess(pt
, &exitCode
);
956 LOG(("Error while running the updater process, check update.log"));
962 ProcessUpdates(nsIFile
*greDir
, nsIFile
*appDir
, nsIFile
*updRootDir
,
963 int argc
, char **argv
, const char *appVersion
,
964 bool restart
, bool isOSUpdate
, nsIFile
*osApplyToDir
,
969 nsCOMPtr
<nsIFile
> updatesDir
;
970 rv
= updRootDir
->Clone(getter_AddRefs(updatesDir
));
974 rv
= updatesDir
->AppendNative(NS_LITERAL_CSTRING("updates"));
978 rv
= updatesDir
->AppendNative(NS_LITERAL_CSTRING("0"));
982 ProcessType dummyPID
; // this will only be used for MOZ_UPDATE_STAGING
983 const char *processingUpdates
= PR_GetEnv("MOZ_PROCESS_UPDATES");
984 if (processingUpdates
&& *processingUpdates
) {
985 // Enable the tests to request an update to be staged.
986 const char *stagingUpdate
= PR_GetEnv("MOZ_UPDATE_STAGING");
987 if (stagingUpdate
&& *stagingUpdate
) {
993 nsCOMPtr
<nsIFile
> statusFile
;
994 UpdateStatus status
= GetUpdateStatus(updatesDir
, statusFile
);
997 case ePendingService
: {
998 nsCOMPtr
<nsIFile
> versionFile
;
999 // Remove the update if the update application version file doesn't exist
1000 // or if the update's application version is less than the current
1001 // application version.
1002 if (!GetVersionFile(updatesDir
, versionFile
) ||
1003 IsOlderVersion(versionFile
, appVersion
)) {
1004 updatesDir
->Remove(true);
1006 ApplyUpdate(greDir
, updatesDir
, statusFile
,
1007 appDir
, argc
, argv
, restart
, isOSUpdate
, osApplyToDir
, pid
);
1011 case eAppliedUpdate
:
1012 case eAppliedService
:
1013 // An update was staged and needs to be switched so the updated application
1015 SwitchToUpdatedApp(greDir
, updatesDir
, statusFile
,
1016 appDir
, argc
, argv
);
1018 case eNoUpdateAction
:
1019 // We don't need to do any special processing here, we'll just continue to
1020 // startup the application.
1029 NS_IMPL_ISUPPORTS(nsUpdateProcessor
, nsIUpdateProcessor
)
1031 nsUpdateProcessor::nsUpdateProcessor()
1036 nsUpdateProcessor::~nsUpdateProcessor()
1041 nsUpdateProcessor::ProcessUpdate(nsIUpdate
* aUpdate
)
1043 nsCOMPtr
<nsIFile
> greDir
, appDir
, updRoot
;
1044 nsAutoCString appVersion
;
1048 nsAutoCString binPath
;
1049 nsXREDirProvider
* dirProvider
= nsXREDirProvider::GetSingleton();
1050 if (dirProvider
) { // Normal code path
1051 // Check for and process any available updates
1053 nsresult rv
= NS_ERROR_FAILURE
; // Take the NS_FAILED path when non-GONK
1054 #ifdef MOZ_WIDGET_GONK
1055 // Check in the sdcard for updates first, since that's our preferred
1056 // download location.
1057 rv
= dirProvider
->GetFile(XRE_UPDATE_ARCHIVE_DIR
, &persistent
,
1058 getter_AddRefs(updRoot
));
1060 if (NS_FAILED(rv
)) {
1061 rv
= dirProvider
->GetFile(XRE_UPDATE_ROOT_DIR
, &persistent
,
1062 getter_AddRefs(updRoot
));
1064 // XRE_UPDATE_ROOT_DIR may fail. Fallback to appDir if failed
1066 updRoot
= dirProvider
->GetAppDir();
1068 greDir
= dirProvider
->GetGREDir();
1069 nsCOMPtr
<nsIFile
> exeFile
;
1070 rv
= dirProvider
->GetFile(XRE_EXECUTABLE_FILE
, &persistent
,
1071 getter_AddRefs(exeFile
));
1072 if (NS_SUCCEEDED(rv
))
1073 rv
= exeFile
->GetParent(getter_AddRefs(appDir
));
1076 appDir
= dirProvider
->GetAppDir();
1078 appVersion
= gAppData
->version
;
1079 argc
= gRestartArgc
;
1080 argv
= gRestartArgv
;
1082 // In the xpcshell environment, the usual XRE_main is not run, so things
1083 // like dirProvider and gAppData do not exist. This code path accesses
1084 // XPCOM (which is not available in the previous code path) in order to get
1085 // the same information.
1086 nsCOMPtr
<nsIProperties
> ds
=
1087 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID
);
1089 NS_ABORT(); // There's nothing which we can do if this fails!
1092 nsresult rv
= ds
->Get(NS_GRE_DIR
, NS_GET_IID(nsIFile
),
1093 getter_AddRefs(greDir
));
1094 NS_ASSERTION(NS_SUCCEEDED(rv
), "Can't get the GRE dir");
1096 nsCOMPtr
<nsIFile
> exeFile
;
1097 rv
= ds
->Get(XRE_EXECUTABLE_FILE
, NS_GET_IID(nsIFile
),
1098 getter_AddRefs(exeFile
));
1099 if (NS_SUCCEEDED(rv
))
1100 rv
= exeFile
->GetParent(getter_AddRefs(appDir
));
1102 NS_ASSERTION(NS_SUCCEEDED(rv
), "Can't get the XREExeF parent dir");
1104 rv
= ds
->Get(XRE_UPDATE_ROOT_DIR
, NS_GET_IID(nsIFile
),
1105 getter_AddRefs(updRoot
));
1106 NS_ASSERTION(NS_SUCCEEDED(rv
), "Can't get the UpdRootD dir");
1108 nsCOMPtr
<nsIXULAppInfo
> appInfo
=
1109 do_GetService("@mozilla.org/xre/app-info;1");
1111 rv
= appInfo
->GetVersion(appVersion
);
1112 NS_ENSURE_SUCCESS(rv
, rv
);
1114 appVersion
= MOZ_APP_VERSION
;
1117 // We need argv[0] to point to the current executable's name. The rest of
1118 // the entries in this array will be ignored if argc<2. Therefore, for
1119 // xpcshell, we only fill out that item, and leave the rest empty.
1121 nsCOMPtr
<nsIFile
> binary
;
1122 rv
= ds
->Get(XRE_EXECUTABLE_FILE
, NS_GET_IID(nsIFile
),
1123 getter_AddRefs(binary
));
1124 NS_ASSERTION(NS_SUCCEEDED(rv
), "Can't get the binary path");
1125 binary
->GetNativePath(binPath
);
1128 // Copy the parameters to the StagedUpdateInfo structure shared with the
1130 mInfo
.mGREDir
= greDir
;
1131 mInfo
.mAppDir
= appDir
;
1132 mInfo
.mUpdateRoot
= updRoot
;
1134 mInfo
.mArgv
= new char*[argc
];
1136 for (int i
= 0; i
< argc
; ++i
) {
1137 const size_t length
= strlen(argv
[i
]);
1138 mInfo
.mArgv
[i
] = new char[length
+ 1];
1139 strcpy(mInfo
.mArgv
[i
], argv
[i
]);
1142 MOZ_ASSERT(argc
== 1); // see above
1143 const size_t length
= binPath
.Length();
1144 mInfo
.mArgv
[0] = new char[length
+ 1];
1145 strcpy(mInfo
.mArgv
[0], binPath
.get());
1147 mInfo
.mAppVersion
= appVersion
;
1149 #if defined(MOZ_WIDGET_GONK)
1150 NS_ENSURE_ARG_POINTER(aUpdate
);
1153 if (NS_SUCCEEDED(aUpdate
->GetIsOSUpdate(&isOSUpdate
)) &&
1155 nsAutoCString osApplyToDir
;
1157 // This needs to be done on the main thread, so we pass it along in
1158 // BackgroundThreadInfo
1159 nsresult rv
= GetOSApplyToDir(osApplyToDir
);
1160 if (NS_FAILED(rv
)) {
1161 LOG(("Can't get the OS apply to dir"));
1165 SetOSApplyToDir(aUpdate
, osApplyToDir
);
1167 mInfo
.mIsOSUpdate
= true;
1168 rv
= NS_NewNativeLocalFile(osApplyToDir
, false,
1169 getter_AddRefs(mInfo
.mOSApplyToDir
));
1170 if (NS_FAILED(rv
)) {
1171 LOG(("Can't create nsIFile for OS apply to dir"));
1179 NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
1180 return NS_NewThread(getter_AddRefs(mProcessWatcher
),
1181 NS_NewRunnableMethod(this, &nsUpdateProcessor::StartStagedUpdate
));
1187 nsUpdateProcessor::StartStagedUpdate()
1189 NS_ABORT_IF_FALSE(!NS_IsMainThread(), "main thread");
1191 nsresult rv
= ProcessUpdates(mInfo
.mGREDir
,
1196 mInfo
.mAppVersion
.get(),
1199 mInfo
.mOSApplyToDir
,
1201 NS_ENSURE_SUCCESS_VOID(rv
);
1204 // Track the state of the updater process while it is staging an update.
1205 rv
= NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::WaitForProcess
));
1206 NS_ENSURE_SUCCESS_VOID(rv
);
1208 // Failed to launch the updater process for some reason.
1209 // We need to shutdown the current thread as there isn't anything more for
1211 rv
= NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::ShutdownWatcherThread
));
1212 NS_ENSURE_SUCCESS_VOID(rv
);
1217 nsUpdateProcessor::ShutdownWatcherThread()
1219 NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
1220 mProcessWatcher
->Shutdown();
1221 mProcessWatcher
= nullptr;
1226 nsUpdateProcessor::WaitForProcess()
1228 NS_ABORT_IF_FALSE(!NS_IsMainThread(), "main thread");
1229 ::WaitForProcess(mUpdaterPID
);
1230 NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::UpdateDone
));
1234 nsUpdateProcessor::UpdateDone()
1236 NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
1238 nsCOMPtr
<nsIUpdateManager
> um
=
1239 do_GetService("@mozilla.org/updates/update-manager;1");
1240 if (um
&& mUpdate
) {
1241 um
->RefreshUpdateStatus(mUpdate
);
1244 ShutdownWatcherThread();