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
);
259 * Returns a value indicating what needs to be done in order to handle an update.
261 * @param dir the directory in which we should look for an update.status file.
262 * @param statusFile the update.status file found in the directory.
264 * @return the update action to be performed.
267 GetUpdateStatus(nsIFile
* dir
, nsCOMPtr
<nsIFile
> &statusFile
)
269 if (GetStatusFile(dir
, statusFile
)) {
271 if (GetStatusFileContents(statusFile
, buf
)) {
272 const char kPending
[] = "pending";
273 const char kApplied
[] = "applied";
274 if (!strncmp(buf
, kPending
, sizeof(kPending
) - 1)) {
275 return ePendingUpdate
;
277 if (!strncmp(buf
, kApplied
, sizeof(kApplied
) - 1)) {
278 return eAppliedUpdate
;
282 return eNoUpdateAction
;
286 GetVersionFile(nsIFile
*dir
, nsCOMPtr
<nsIFile
> &result
)
288 return GetFile(dir
, NS_LITERAL_CSTRING("update.version"), result
);
291 // Compares the current application version with the update's application
294 IsOlderVersion(nsIFile
*versionFile
, const char *appVersion
)
296 PRFileDesc
*fd
= nullptr;
297 nsresult rv
= versionFile
->OpenNSPRFileDesc(PR_RDONLY
, 0660, &fd
);
302 const int32_t n
= PR_Read(fd
, buf
, sizeof(buf
));
308 // Trim off the trailing newline
309 if (buf
[n
- 1] == '\n')
312 // If the update xml doesn't provide the application version the file will
313 // contain the string "null" and it is assumed that the update is not older.
314 const char kNull
[] = "null";
315 if (strncmp(buf
, kNull
, sizeof(kNull
) - 1) == 0)
318 if (mozilla::Version(appVersion
) > buf
)
325 CopyFileIntoUpdateDir(nsIFile
*parentDir
, const char *leafName
, nsIFile
*updateDir
)
327 nsDependentCString
leaf(leafName
);
328 nsCOMPtr
<nsIFile
> file
;
330 // Make sure there is not an existing file in the target location.
331 nsresult rv
= updateDir
->Clone(getter_AddRefs(file
));
334 rv
= file
->AppendNative(leaf
);
339 // Now, copy into the target location.
340 rv
= parentDir
->Clone(getter_AddRefs(file
));
343 rv
= file
->AppendNative(leaf
);
346 rv
= file
->CopyToNative(updateDir
, EmptyCString());
354 CopyUpdaterIntoUpdateDir(nsIFile
*greDir
, nsIFile
*appDir
, nsIFile
*updateDir
,
355 nsCOMPtr
<nsIFile
> &updater
)
357 // Copy the updater application from the GRE and the updater ini from the app
358 #if defined(XP_MACOSX)
359 if (!CopyFileIntoUpdateDir(appDir
, kUpdaterApp
, updateDir
))
361 CopyFileIntoUpdateDir(greDir
, kUpdaterINI
, updateDir
);
363 if (!CopyFileIntoUpdateDir(greDir
, kUpdaterBin
, updateDir
))
365 CopyFileIntoUpdateDir(appDir
, kUpdaterINI
, updateDir
);
367 #if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(ANDROID)
368 nsCOMPtr
<nsIFile
> iconDir
;
369 appDir
->Clone(getter_AddRefs(iconDir
));
370 iconDir
->AppendNative(NS_LITERAL_CSTRING("icons"));
371 if (!CopyFileIntoUpdateDir(iconDir
, kUpdaterPNG
, updateDir
))
374 // Finally, return the location of the updater binary.
375 nsresult rv
= updateDir
->Clone(getter_AddRefs(updater
));
378 #if defined(XP_MACOSX)
379 rv
= updater
->AppendNative(NS_LITERAL_CSTRING(kUpdaterApp
));
380 nsresult tmp
= updater
->AppendNative(NS_LITERAL_CSTRING("Contents"));
381 if (NS_FAILED(tmp
)) {
384 tmp
= updater
->AppendNative(NS_LITERAL_CSTRING("MacOS"));
385 if (NS_FAILED(tmp
) || NS_FAILED(rv
))
388 rv
= updater
->AppendNative(NS_LITERAL_CSTRING(kUpdaterBin
));
389 return NS_SUCCEEDED(rv
);
393 * Switch an existing application directory to an updated version that has been
396 * @param greDir the GRE dir
397 * @param updateDir the update dir where the mar file is located
398 * @param appDir the app dir
399 * @param appArgc the number of args to the application
400 * @param appArgv the args to the application, used for restarting if needed
403 SwitchToUpdatedApp(nsIFile
*greDir
, nsIFile
*updateDir
,
404 nsIFile
*appDir
, int appArgc
, char **appArgv
)
409 // - copy updater into updates/0/MozUpdater/bgupdate/ dir on all platforms
411 // - run updater with the correct arguments
413 nsCOMPtr
<nsIFile
> mozUpdaterDir
;
414 rv
= updateDir
->Clone(getter_AddRefs(mozUpdaterDir
));
416 LOG(("failed cloning update dir\n"));
420 // Create a new directory named MozUpdater in the updates/0 directory to copy
421 // the updater files to that will be used to replace the installation with the
422 // staged application that has been updated. Note that we don't check for
423 // directory creation errors since the call to CopyUpdaterIntoUpdateDir will
424 // fail if the creation of the directory fails. A unique directory is created
425 // in MozUpdater in case a previous attempt locked the directory or files.
426 mozUpdaterDir
->Append(NS_LITERAL_STRING("MozUpdater"));
427 mozUpdaterDir
->Append(NS_LITERAL_STRING("bgupdate"));
428 mozUpdaterDir
->CreateUnique(nsIFile::DIRECTORY_TYPE
, 0755);
430 nsCOMPtr
<nsIFile
> updater
;
431 if (!CopyUpdaterIntoUpdateDir(greDir
, appDir
, mozUpdaterDir
, updater
)) {
432 LOG(("failed copying updater\n"));
437 // We need to use the value returned from XRE_GetBinaryPath when attempting
438 // to restart the running application.
439 nsCOMPtr
<nsIFile
> appFile
;
441 #if defined(XP_MACOSX)
442 // On OS X we need to pass the location of the xulrunner-stub executable
443 // rather than xulrunner-bin. See bug 349737.
444 GetXULRunnerStubPath(appArgv
[0], getter_AddRefs(appFile
));
446 XRE_GetBinaryPath(appArgv
[0], getter_AddRefs(appFile
));
453 nsAutoString appFilePathW
;
454 rv
= appFile
->GetPath(appFilePathW
);
458 NS_ConvertUTF16toUTF8
appFilePath(appFilePathW
);
460 nsCOMPtr
<nsIFile
> updater
;
461 rv
= greDir
->Clone(getter_AddRefs(updater
));
466 nsDependentCString
leaf(kUpdaterBin
);
467 rv
= updater
->AppendNative(leaf
);
472 nsAutoString updaterPathW
;
473 rv
= updater
->GetPath(updaterPathW
);
477 NS_ConvertUTF16toUTF8
updaterPath(updaterPathW
);
480 nsAutoCString appFilePath
;
481 #if defined(MOZ_WIDGET_GONK)
482 appFilePath
.Assign(kB2GServiceArgv
[0]);
483 appArgc
= kB2GServiceArgc
;
484 appArgv
= const_cast<char**>(kB2GServiceArgv
);
486 rv
= appFile
->GetNativePath(appFilePath
);
491 nsAutoCString updaterPath
;
492 rv
= updater
->GetNativePath(updaterPath
);
497 nsAutoCString installDirPath
;
498 rv
= GetInstallDirPath(appDir
, installDirPath
);
503 // Get the directory where the update will be staged.
504 nsAutoCString applyToDir
;
505 nsCOMPtr
<nsIFile
> updatedDir
;
507 if (!GetFile(updateDir
, NS_LITERAL_CSTRING("Updated.app"), updatedDir
)) {
509 if (!GetFile(appDir
, NS_LITERAL_CSTRING("updated"), updatedDir
)) {
514 nsAutoString applyToDirW
;
515 rv
= updatedDir
->GetPath(applyToDirW
);
519 applyToDir
= NS_ConvertUTF16toUTF8(applyToDirW
);
521 rv
= updatedDir
->GetNativePath(applyToDir
);
527 // Make sure that the updated directory exists
528 bool updatedDirExists
= false;
529 updatedDir
->Exists(&updatedDirExists
);
530 if (!updatedDirExists
) {
535 nsAutoString updateDirPathW
;
536 rv
= updateDir
->GetPath(updateDirPathW
);
537 NS_ConvertUTF16toUTF8
updateDirPath(updateDirPathW
);
539 nsAutoCString updateDirPath
;
540 rv
= updateDir
->GetNativePath(updateDirPath
);
545 // Get the current working directory.
546 char workingDirPath
[MAXPATHLEN
];
547 rv
= GetCurrentWorkingDir(workingDirPath
, sizeof(workingDirPath
));
551 // Construct the PID argument for this process. If we are using execv, then
552 // we pass "0" which is then ignored by the updater.
553 #if defined(USE_EXECV)
554 nsAutoCString
pid("0");
557 pid
.AppendInt((int32_t) getpid());
560 // Append a special token to the PID in order to let the updater know that it
561 // just needs to replace the update directory.
562 pid
.AppendLiteral("/replace");
564 int argc
= appArgc
+ 6;
565 char **argv
= new char*[argc
+ 1];
568 argv
[0] = (char*) updaterPath
.get();
569 argv
[1] = (char*) updateDirPath
.get();
570 argv
[2] = (char*) installDirPath
.get();
571 argv
[3] = (char*) applyToDir
.get();
572 argv
[4] = (char*) pid
.get();
574 argv
[5] = workingDirPath
;
575 argv
[6] = (char*) appFilePath
.get();
576 for (int i
= 1; i
< appArgc
; ++i
)
577 argv
[6 + i
] = appArgv
[i
];
578 argv
[argc
] = nullptr;
585 PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
588 LOG(("spawning updater process for replacing [%s]\n", updaterPath
.get()));
590 #if defined(USE_EXECV)
591 # if defined(MOZ_WIDGET_GONK)
592 // In Gonk, we preload libmozglue, which the updater process doesn't need.
593 // Since the updater will move and delete libmozglue.so, this can actually
594 // stop the /system mount from correctly being remounted as read-only.
595 unsetenv("LD_PRELOAD");
597 execv(updaterPath
.get(), argv
);
598 #elif defined(XP_WIN)
599 // Switch the application using updater.exe
600 if (!WinLaunchChild(updaterPathW
.get(), argc
, argv
)) {
604 #elif defined(XP_MACOSX)
605 CommandLineServiceMac::SetupMacCommandLine(argc
, argv
, true);
606 // LaunchChildMac uses posix_spawnp and prefers the current
607 // architecture when launching. It doesn't require a
608 // null-terminated string but it doesn't matter if we pass one.
609 LaunchChildMac(argc
, argv
);
612 PR_CreateProcessDetached(updaterPath
.get(), argv
, nullptr, nullptr);
617 #if defined(MOZ_WIDGET_GONK)
619 GetOSApplyToDir(nsACString
& applyToDir
)
621 nsCOMPtr
<nsIProperties
> ds
=
622 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID
);
623 NS_ASSERTION(ds
, "Can't get directory service");
625 nsCOMPtr
<nsIFile
> osApplyToDir
;
626 nsresult rv
= ds
->Get(XRE_OS_UPDATE_APPLY_TO_DIR
, NS_GET_IID(nsIFile
),
627 getter_AddRefs(osApplyToDir
));
629 LOG(("Can't get the OS applyTo dir"));
633 return osApplyToDir
->GetNativePath(applyToDir
);
637 SetOSApplyToDir(nsIUpdate
* update
, const nsACString
& osApplyToDir
)
640 nsCOMPtr
<nsIWritablePropertyBag
> updateProperties
=
641 do_QueryInterface(update
, &rv
);
647 nsCOMPtr
<nsIWritableVariant
> variant
=
648 do_CreateInstance("@mozilla.org/variant;1", &rv
);
653 rv
= variant
->SetAsACString(osApplyToDir
);
658 updateProperties
->SetProperty(NS_LITERAL_STRING("osApplyToDir"), variant
);
663 * Apply an update. This applies to both normal and staged updates.
665 * @param greDir the GRE dir
666 * @param updateDir the update root dir
667 * @param statusFile the update.status file
668 * @param appDir the app dir
669 * @param appArgc the number of args to the application
670 * @param appArgv the args to the application, used for restarting if needed
671 * @param restart if true, apply the update in the foreground and restart the
672 * application when done. otherwise, stage the update and don't
673 * restart the application.
674 * @param outpid out parameter holding the handle to the updater application for
678 ApplyUpdate(nsIFile
*greDir
, nsIFile
*updateDir
, nsIFile
*statusFile
,
679 nsIFile
*appDir
, int appArgc
, char **appArgv
,
680 bool restart
, bool isOSUpdate
, nsIFile
*osApplyToDir
,
686 // - mark update as 'applying'
687 // - copy updater into update dir on all platforms except Windows
688 // - run updater w/ appDir as the current working dir
690 nsCOMPtr
<nsIFile
> updater
;
691 if (!CopyUpdaterIntoUpdateDir(greDir
, appDir
, updateDir
, updater
)) {
692 LOG(("failed copying updater\n"));
697 // We need to use the value returned from XRE_GetBinaryPath when attempting
698 // to restart the running application.
699 nsCOMPtr
<nsIFile
> appFile
;
701 #if defined(XP_MACOSX)
702 // On OS X we need to pass the location of the xulrunner-stub executable
703 // rather than xulrunner-bin. See bug 349737.
704 GetXULRunnerStubPath(appArgv
[0], getter_AddRefs(appFile
));
706 XRE_GetBinaryPath(appArgv
[0], getter_AddRefs(appFile
));
713 nsAutoString appFilePathW
;
714 rv
= appFile
->GetPath(appFilePathW
);
718 NS_ConvertUTF16toUTF8
appFilePath(appFilePathW
);
720 nsCOMPtr
<nsIFile
> updater
;
721 rv
= greDir
->Clone(getter_AddRefs(updater
));
726 nsDependentCString
leaf(kUpdaterBin
);
727 rv
= updater
->AppendNative(leaf
);
732 nsAutoString updaterPathW
;
733 rv
= updater
->GetPath(updaterPathW
);
737 NS_ConvertUTF16toUTF8
updaterPath(updaterPathW
);
739 nsAutoCString appFilePath
;
740 rv
= appFile
->GetNativePath(appFilePath
);
744 nsAutoCString updaterPath
;
745 rv
= updater
->GetNativePath(updaterPath
);
751 nsAutoCString installDirPath
;
752 rv
= GetInstallDirPath(appDir
, installDirPath
);
756 // Get the directory where the update was staged for replace and GONK OS
757 // Updates or where it will be applied.
758 #ifndef MOZ_WIDGET_GONK
759 // OS Updates are only supported on GONK so force it to false on everything
760 // but GONK to simplify the following logic.
763 nsAutoCString applyToDir
;
764 nsCOMPtr
<nsIFile
> updatedDir
;
765 if (restart
&& !isOSUpdate
) {
766 applyToDir
.Assign(installDirPath
);
769 if (!GetFile(updateDir
, NS_LITERAL_CSTRING("Updated.app"), updatedDir
)) {
771 if (!GetFile(appDir
, NS_LITERAL_CSTRING("updated"), updatedDir
)) {
776 nsAutoString applyToDirW
;
777 rv
= updatedDir
->GetPath(applyToDirW
);
781 applyToDir
= NS_ConvertUTF16toUTF8(applyToDirW
);
782 #elif MOZ_WIDGET_GONK
787 rv
= osApplyToDir
->GetNativePath(applyToDir
);
789 rv
= updatedDir
->GetNativePath(applyToDir
);
792 rv
= updatedDir
->GetNativePath(applyToDir
);
799 nsAutoString updateDirPathW
;
800 rv
= updateDir
->GetPath(updateDirPathW
);
801 NS_ConvertUTF16toUTF8
updateDirPath(updateDirPathW
);
803 nsAutoCString updateDirPath
;
804 rv
= updateDir
->GetNativePath(updateDirPath
);
810 // Get the current working directory.
811 char workingDirPath
[MAXPATHLEN
];
812 rv
= GetCurrentWorkingDir(workingDirPath
, sizeof(workingDirPath
));
816 // We used to write out "Applying" to the update.status file here.
817 // Instead we do this from within the updater application now.
818 // This is so that we don't overwrite the status of pending-service
819 // in the Windows case. This change was made for all platforms so
820 // that it stays consistent across all OS.
822 // Construct the PID argument for this process. If we are using execv, then
823 // we pass "0" which is then ignored by the updater.
826 // Signal the updater application that it should stage the update.
827 pid
.AssignASCII("-1");
829 #if defined(USE_EXECV)
830 pid
.AssignASCII("0");
832 pid
.AppendInt((int32_t) getpid());
836 int argc
= appArgc
+ 6;
837 char **argv
= new char*[argc
+ 1 ];
840 argv
[0] = (char*) updaterPath
.get();
841 argv
[1] = (char*) updateDirPath
.get();
842 argv
[2] = (char*) installDirPath
.get();
843 argv
[3] = (char*) applyToDir
.get();
844 argv
[4] = (char*) pid
.get();
845 if (restart
&& appArgc
) {
846 argv
[5] = workingDirPath
;
847 argv
[6] = (char*) appFilePath
.get();
848 for (int i
= 1; i
< appArgc
; ++i
)
849 argv
[6 + i
] = appArgv
[i
];
850 argv
[argc
] = nullptr;
857 PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
861 PR_SetEnv("MOZ_OS_UPDATE=1");
863 #if defined(MOZ_WIDGET_GONK)
864 // We want the updater to be CPU friendly and not subject to being killed by
865 // the low memory killer, so we pass in some preferences to allow it to
866 // adjust its priority.
868 int32_t prioVal
= Preferences::GetInt(kAppUpdaterPrio
,
869 kAppUpdaterPrioDefault
);
870 int32_t oomScoreAdj
= Preferences::GetInt(kAppUpdaterOomScoreAdj
,
871 kAppUpdaterOomScoreAdjDefault
);
872 int32_t ioprioClass
= Preferences::GetInt(kAppUpdaterIOPrioClass
,
873 kAppUpdaterIOPrioClassDefault
);
874 int32_t ioprioLevel
= Preferences::GetInt(kAppUpdaterIOPrioLevel
,
875 kAppUpdaterIOPrioLevelDefault
);
876 nsPrintfCString
prioEnv("MOZ_UPDATER_PRIO=%d/%d/%d/%d",
877 prioVal
, oomScoreAdj
, ioprioClass
, ioprioLevel
);
878 PR_SetEnv(prioEnv
.get());
881 LOG(("spawning updater process [%s]\n", updaterPath
.get()));
883 #if defined(USE_EXECV)
884 // Don't use execv when staging updates.
886 execv(updaterPath
.get(), argv
);
888 *outpid
= PR_CreateProcess(updaterPath
.get(), argv
, nullptr, nullptr);
890 #elif defined(XP_WIN)
891 // Launch the update using updater.exe
892 if (!WinLaunchChild(updaterPathW
.get(), argc
, argv
, nullptr, outpid
)) {
897 // We are going to process an update so we should exit now
900 #elif defined(XP_MACOSX)
901 CommandLineServiceMac::SetupMacCommandLine(argc
, argv
, true);
902 // LaunchChildMac uses posix_spawnp and prefers the current
903 // architecture when launching. It doesn't require a
904 // null-terminated string but it doesn't matter if we pass one.
905 LaunchChildMac(argc
, argv
, 0, outpid
);
910 *outpid
= PR_CreateProcess(updaterPath
.get(), argv
, nullptr, nullptr);
918 * Wait for a process until it terminates. This call is blocking.
921 WaitForProcess(ProcessType pt
)
924 WaitForSingleObject(pt
, INFINITE
);
926 #elif defined(XP_MACOSX)
930 PR_WaitProcess(pt
, &exitCode
);
932 LOG(("Error while running the updater process, check update.log"));
938 ProcessUpdates(nsIFile
*greDir
, nsIFile
*appDir
, nsIFile
*updRootDir
,
939 int argc
, char **argv
, const char *appVersion
,
940 bool restart
, bool isOSUpdate
, nsIFile
*osApplyToDir
,
945 nsCOMPtr
<nsIFile
> updatesDir
;
946 rv
= updRootDir
->Clone(getter_AddRefs(updatesDir
));
950 rv
= updatesDir
->AppendNative(NS_LITERAL_CSTRING("updates"));
954 rv
= updatesDir
->AppendNative(NS_LITERAL_CSTRING("0"));
958 ProcessType dummyPID
; // this will only be used for MOZ_UPDATE_STAGING
959 const char *processingUpdates
= PR_GetEnv("MOZ_PROCESS_UPDATES");
960 if (processingUpdates
&& *processingUpdates
) {
961 // Enable the tests to request an update to be staged.
962 const char *stagingUpdate
= PR_GetEnv("MOZ_UPDATE_STAGING");
963 if (stagingUpdate
&& *stagingUpdate
) {
969 nsCOMPtr
<nsIFile
> statusFile
;
970 UpdateStatus status
= GetUpdateStatus(updatesDir
, statusFile
);
972 case ePendingUpdate
: {
973 nsCOMPtr
<nsIFile
> versionFile
;
974 // Remove the update if the update application version file doesn't exist
975 // or if the update's application version is less than the current
976 // application version.
977 if (!GetVersionFile(updatesDir
, versionFile
) ||
978 IsOlderVersion(versionFile
, appVersion
)) {
979 updatesDir
->Remove(true);
981 ApplyUpdate(greDir
, updatesDir
, statusFile
,
982 appDir
, argc
, argv
, restart
, isOSUpdate
, osApplyToDir
, pid
);
987 // An update was staged and needs to be switched so the updated application
989 SwitchToUpdatedApp(greDir
, updatesDir
, appDir
, argc
, argv
);
991 case eNoUpdateAction
:
992 // We don't need to do any special processing here, we'll just continue to
993 // startup the application.
1002 NS_IMPL_ISUPPORTS(nsUpdateProcessor
, nsIUpdateProcessor
)
1004 nsUpdateProcessor::nsUpdateProcessor()
1009 nsUpdateProcessor::~nsUpdateProcessor()
1014 nsUpdateProcessor::ProcessUpdate(nsIUpdate
* aUpdate
)
1016 nsCOMPtr
<nsIFile
> greDir
, appDir
, updRoot
;
1017 nsAutoCString appVersion
;
1021 nsAutoCString binPath
;
1022 nsXREDirProvider
* dirProvider
= nsXREDirProvider::GetSingleton();
1023 if (dirProvider
) { // Normal code path
1024 // Check for and process any available updates
1026 nsresult rv
= NS_ERROR_FAILURE
; // Take the NS_FAILED path when non-GONK
1027 #ifdef MOZ_WIDGET_GONK
1028 // Check in the sdcard for updates first, since that's our preferred
1029 // download location.
1030 rv
= dirProvider
->GetFile(XRE_UPDATE_ARCHIVE_DIR
, &persistent
,
1031 getter_AddRefs(updRoot
));
1033 if (NS_FAILED(rv
)) {
1034 rv
= dirProvider
->GetFile(XRE_UPDATE_ROOT_DIR
, &persistent
,
1035 getter_AddRefs(updRoot
));
1037 // XRE_UPDATE_ROOT_DIR may fail. Fallback to appDir if failed
1039 updRoot
= dirProvider
->GetAppDir();
1041 greDir
= dirProvider
->GetGREDir();
1042 nsCOMPtr
<nsIFile
> exeFile
;
1043 rv
= dirProvider
->GetFile(XRE_EXECUTABLE_FILE
, &persistent
,
1044 getter_AddRefs(exeFile
));
1045 if (NS_SUCCEEDED(rv
))
1046 rv
= exeFile
->GetParent(getter_AddRefs(appDir
));
1049 appDir
= dirProvider
->GetAppDir();
1051 appVersion
= gAppData
->version
;
1052 argc
= gRestartArgc
;
1053 argv
= gRestartArgv
;
1055 // In the xpcshell environment, the usual XRE_main is not run, so things
1056 // like dirProvider and gAppData do not exist. This code path accesses
1057 // XPCOM (which is not available in the previous code path) in order to get
1058 // the same information.
1059 nsCOMPtr
<nsIProperties
> ds
=
1060 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID
);
1062 NS_ABORT(); // There's nothing which we can do if this fails!
1065 nsresult rv
= ds
->Get(NS_GRE_DIR
, NS_GET_IID(nsIFile
),
1066 getter_AddRefs(greDir
));
1067 NS_ASSERTION(NS_SUCCEEDED(rv
), "Can't get the GRE dir");
1069 nsCOMPtr
<nsIFile
> exeFile
;
1070 rv
= ds
->Get(XRE_EXECUTABLE_FILE
, NS_GET_IID(nsIFile
),
1071 getter_AddRefs(exeFile
));
1072 if (NS_SUCCEEDED(rv
))
1073 rv
= exeFile
->GetParent(getter_AddRefs(appDir
));
1075 NS_ASSERTION(NS_SUCCEEDED(rv
), "Can't get the XREExeF parent dir");
1077 rv
= ds
->Get(XRE_UPDATE_ROOT_DIR
, NS_GET_IID(nsIFile
),
1078 getter_AddRefs(updRoot
));
1079 NS_ASSERTION(NS_SUCCEEDED(rv
), "Can't get the UpdRootD dir");
1081 nsCOMPtr
<nsIXULAppInfo
> appInfo
=
1082 do_GetService("@mozilla.org/xre/app-info;1");
1084 rv
= appInfo
->GetVersion(appVersion
);
1085 NS_ENSURE_SUCCESS(rv
, rv
);
1087 appVersion
= MOZ_APP_VERSION
;
1090 // We need argv[0] to point to the current executable's name. The rest of
1091 // the entries in this array will be ignored if argc<2. Therefore, for
1092 // xpcshell, we only fill out that item, and leave the rest empty.
1094 nsCOMPtr
<nsIFile
> binary
;
1095 rv
= ds
->Get(XRE_EXECUTABLE_FILE
, NS_GET_IID(nsIFile
),
1096 getter_AddRefs(binary
));
1097 NS_ASSERTION(NS_SUCCEEDED(rv
), "Can't get the binary path");
1098 binary
->GetNativePath(binPath
);
1101 // Copy the parameters to the StagedUpdateInfo structure shared with the
1103 mInfo
.mGREDir
= greDir
;
1104 mInfo
.mAppDir
= appDir
;
1105 mInfo
.mUpdateRoot
= updRoot
;
1107 mInfo
.mArgv
= new char*[argc
];
1109 for (int i
= 0; i
< argc
; ++i
) {
1110 const size_t length
= strlen(argv
[i
]);
1111 mInfo
.mArgv
[i
] = new char[length
+ 1];
1112 strcpy(mInfo
.mArgv
[i
], argv
[i
]);
1115 MOZ_ASSERT(argc
== 1); // see above
1116 const size_t length
= binPath
.Length();
1117 mInfo
.mArgv
[0] = new char[length
+ 1];
1118 strcpy(mInfo
.mArgv
[0], binPath
.get());
1120 mInfo
.mAppVersion
= appVersion
;
1122 #if defined(MOZ_WIDGET_GONK)
1123 NS_ENSURE_ARG_POINTER(aUpdate
);
1126 if (NS_SUCCEEDED(aUpdate
->GetIsOSUpdate(&isOSUpdate
)) &&
1128 nsAutoCString osApplyToDir
;
1130 // This needs to be done on the main thread, so we pass it along in
1131 // BackgroundThreadInfo
1132 nsresult rv
= GetOSApplyToDir(osApplyToDir
);
1133 if (NS_FAILED(rv
)) {
1134 LOG(("Can't get the OS apply to dir"));
1138 SetOSApplyToDir(aUpdate
, osApplyToDir
);
1140 mInfo
.mIsOSUpdate
= true;
1141 rv
= NS_NewNativeLocalFile(osApplyToDir
, false,
1142 getter_AddRefs(mInfo
.mOSApplyToDir
));
1143 if (NS_FAILED(rv
)) {
1144 LOG(("Can't create nsIFile for OS apply to dir"));
1150 MOZ_ASSERT(NS_IsMainThread(), "not main thread");
1151 return NS_NewThread(getter_AddRefs(mProcessWatcher
),
1152 NS_NewRunnableMethod(this, &nsUpdateProcessor::StartStagedUpdate
));
1158 nsUpdateProcessor::StartStagedUpdate()
1160 MOZ_ASSERT(!NS_IsMainThread(), "main thread");
1162 nsresult rv
= ProcessUpdates(mInfo
.mGREDir
,
1167 mInfo
.mAppVersion
.get(),
1170 mInfo
.mOSApplyToDir
,
1172 NS_ENSURE_SUCCESS_VOID(rv
);
1175 // Track the state of the updater process while it is staging an update.
1176 rv
= NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::WaitForProcess
));
1177 NS_ENSURE_SUCCESS_VOID(rv
);
1179 // Failed to launch the updater process for some reason.
1180 // We need to shutdown the current thread as there isn't anything more for
1182 rv
= NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::ShutdownWatcherThread
));
1183 NS_ENSURE_SUCCESS_VOID(rv
);
1188 nsUpdateProcessor::ShutdownWatcherThread()
1190 MOZ_ASSERT(NS_IsMainThread(), "not main thread");
1191 mProcessWatcher
->Shutdown();
1192 mProcessWatcher
= nullptr;
1196 nsUpdateProcessor::WaitForProcess()
1198 MOZ_ASSERT(!NS_IsMainThread(), "main thread");
1199 ::WaitForProcess(mUpdaterPID
);
1200 NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::UpdateDone
));
1204 nsUpdateProcessor::UpdateDone()
1206 MOZ_ASSERT(NS_IsMainThread(), "not main thread");
1208 nsCOMPtr
<nsIUpdateManager
> um
=
1209 do_GetService("@mozilla.org/updates/update-manager;1");
1211 nsCOMPtr
<nsIUpdate
> update
;
1212 um
->GetActiveUpdate(getter_AddRefs(update
));
1214 um
->RefreshUpdateStatus(update
);
1218 ShutdownWatcherThread();