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"
12 #include "nsXULAppAPI.h"
13 #include "nsAppRunner.h"
15 #include "nsVariant.h"
19 #include "mozilla/Logging.h"
21 #include "nsVersionComparator.h"
22 #include "nsDirectoryServiceDefs.h"
23 #include "nsThreadUtils.h"
24 #include "nsIXULAppInfo.h"
25 #include "mozilla/Preferences.h"
26 #include "nsPrintfCString.h"
27 #include "mozilla/DebugOnly.h"
28 #include "mozilla/ErrorNames.h"
29 #include "mozilla/Printf.h"
30 #include "mozilla/UniquePtr.h"
31 #include "nsIObserverService.h"
33 #include "mozilla/ScopeExit.h"
34 #include "mozilla/Services.h"
35 #include "mozilla/dom/Promise.h"
36 #include "mozilla/CmdLineAndEnvUtils.h"
39 # include "nsILocalFileMac.h"
40 # include "nsCommandLineServiceMac.h"
41 # include "MacLaunchHelper.h"
42 # include "updaterfileutils_osx.h"
43 # include "mozilla/Monitor.h"
44 # include "gfxPlatformMac.h"
53 # include <shellapi.h>
54 # include "commonupdatedir.h"
55 # include "nsWindowsHelpers.h"
56 # include "pathhash.h"
57 # include "WinUtils.h"
58 # define getcwd(path, size) _getcwd(path, size)
59 # define getpid() GetCurrentProcessId()
60 #elif defined(XP_UNIX)
62 # include <sys/wait.h>
65 using namespace mozilla
;
67 static LazyLogModule
sUpdateLog("updatedriver");
68 // Some other file in our unified batch might have defined LOG already.
72 #define LOG(args) MOZ_LOG(sUpdateLog, mozilla::LogLevel::Debug, args)
75 static void UpdateDriverSetupMacCommandLine(int& argc
, char**& argv
,
77 if (NS_IsMainThread()) {
78 CommandLineServiceMac::SetupMacCommandLine(argc
, argv
, restart
);
81 // Bug 1335916: SetupMacCommandLine calls a CoreFoundation function that
82 // asserts that it was called from the main thread, so if we are not the main
83 // thread, we have to dispatch that call to there. But we also have to get the
84 // result from it, so we can't just dispatch and return, we have to wait
85 // until the dispatched operation actually completes. So we also set up a
86 // monitor to signal us when that happens, and block until then.
87 Monitor monitor
MOZ_UNANNOTATED("nsUpdateDriver SetupMacCommandLine");
89 nsresult rv
= NS_DispatchToMainThread(NS_NewRunnableFunction(
90 "UpdateDriverSetupMacCommandLine",
91 [&argc
, &argv
, restart
, &monitor
]() -> void {
92 CommandLineServiceMac::SetupMacCommandLine(argc
, argv
, restart
);
93 MonitorAutoLock(monitor
).Notify();
98 ("Update driver error dispatching SetupMacCommandLine to main thread: "
104 // The length of this wait is arbitrary, but should be long enough that having
105 // it expire means something is seriously wrong.
107 MonitorAutoLock(monitor
).Wait(TimeDuration::FromSeconds(60));
108 if (status
== CVStatus::Timeout
) {
109 LOG(("Update driver timed out waiting for SetupMacCommandLine\n"));
114 static nsresult
GetCurrentWorkingDir(nsACString
& aOutPath
) {
115 // Cannot use NS_GetSpecialDirectory because XPCOM is not yet initialized.
116 // This code is duplicated from xpcom/io/SpecialSystemDirectory.cpp:
121 wchar_t wpath
[MAX_PATH
];
122 if (!_wgetcwd(wpath
, ArrayLength(wpath
))) {
123 return NS_ERROR_FAILURE
;
125 CopyUTF16toUTF8(nsDependentString(wpath
), aOutPath
);
127 char path
[MAXPATHLEN
];
128 if (!getcwd(path
, ArrayLength(path
))) {
129 return NS_ERROR_FAILURE
;
138 * Get the path to the installation directory. For Mac OS X this will be the
141 * @param appDir the application directory file object
142 * @param installDirPath the path to the installation directory
144 static nsresult
GetInstallDirPath(nsIFile
* appDir
, nsACString
& installDirPath
) {
147 nsCOMPtr
<nsIFile
> parentDir1
, parentDir2
;
148 rv
= appDir
->GetParent(getter_AddRefs(parentDir1
));
149 NS_ENSURE_SUCCESS(rv
, rv
);
150 rv
= parentDir1
->GetParent(getter_AddRefs(parentDir2
));
151 NS_ENSURE_SUCCESS(rv
, rv
);
152 rv
= parentDir2
->GetNativePath(installDirPath
);
153 NS_ENSURE_SUCCESS(rv
, rv
);
155 nsAutoString installDirPathW
;
156 rv
= appDir
->GetPath(installDirPathW
);
157 NS_ENSURE_SUCCESS(rv
, rv
);
158 CopyUTF16toUTF8(installDirPathW
, installDirPath
);
160 rv
= appDir
->GetNativePath(installDirPath
);
161 NS_ENSURE_SUCCESS(rv
, rv
);
166 static bool GetFile(nsIFile
* dir
, const nsACString
& name
,
167 nsCOMPtr
<nsIFile
>& result
) {
170 nsCOMPtr
<nsIFile
> file
;
171 rv
= dir
->Clone(getter_AddRefs(file
));
176 rv
= file
->AppendNative(name
);
185 static bool GetStatusFile(nsIFile
* dir
, nsCOMPtr
<nsIFile
>& result
) {
186 return GetFile(dir
, "update.status"_ns
, result
);
190 * Get the contents of the update.status file when the update.status file can
191 * be opened with read and write access. The reason it is opened for both read
192 * and write is to prevent trying to update when the user doesn't have write
193 * access to the update directory.
195 * @param statusFile the status file object.
196 * @param buf the buffer holding the file contents
198 * @return true if successful, false otherwise.
200 template <size_t Size
>
201 static bool GetStatusFileContents(nsIFile
* statusFile
, char (&buf
)[Size
]) {
204 "Buffer needs to be large enough to hold the known status codes");
206 PRFileDesc
* fd
= nullptr;
207 nsresult rv
= statusFile
->OpenNSPRFileDesc(PR_RDWR
, 0660, &fd
);
212 const int32_t n
= PR_Read(fd
, buf
, Size
);
228 * Returns a value indicating what needs to be done in order to handle an
231 * @param dir the directory in which we should look for an update.status file.
232 * @param statusFile the update.status file found in the directory.
234 * @return the update action to be performed.
236 static UpdateStatus
GetUpdateStatus(nsIFile
* dir
,
237 nsCOMPtr
<nsIFile
>& statusFile
) {
238 if (GetStatusFile(dir
, statusFile
)) {
240 if (GetStatusFileContents(statusFile
, buf
)) {
241 const char kPending
[] = "pending";
242 const char kPendingService
[] = "pending-service";
243 const char kPendingElevate
[] = "pending-elevate";
244 const char kApplied
[] = "applied";
245 const char kAppliedService
[] = "applied-service";
246 if (!strncmp(buf
, kPendingElevate
, sizeof(kPendingElevate
) - 1)) {
247 return ePendingElevate
;
249 if (!strncmp(buf
, kPendingService
, sizeof(kPendingService
) - 1)) {
250 return ePendingService
;
252 if (!strncmp(buf
, kPending
, sizeof(kPending
) - 1)) {
253 return ePendingUpdate
;
255 if (!strncmp(buf
, kAppliedService
, sizeof(kAppliedService
) - 1)) {
256 return eAppliedService
;
258 if (!strncmp(buf
, kApplied
, sizeof(kApplied
) - 1)) {
259 return eAppliedUpdate
;
263 return eNoUpdateAction
;
266 static bool GetVersionFile(nsIFile
* dir
, nsCOMPtr
<nsIFile
>& result
) {
267 return GetFile(dir
, "update.version"_ns
, result
);
270 // Compares the current application version with the update's application
272 static bool IsOlderVersion(nsIFile
* versionFile
, const char* appVersion
) {
273 PRFileDesc
* fd
= nullptr;
274 nsresult rv
= versionFile
->OpenNSPRFileDesc(PR_RDONLY
, 0660, &fd
);
280 const int32_t n
= PR_Read(fd
, buf
, sizeof(buf
));
287 // Trim off the trailing newline
288 if (buf
[n
- 1] == '\n') {
292 // If the update xml doesn't provide the application version the file will
293 // contain the string "null" and it is assumed that the update is not older.
294 const char kNull
[] = "null";
295 if (strncmp(buf
, kNull
, sizeof(kNull
) - 1) == 0) {
299 return mozilla::Version(appVersion
) > buf
;
303 * Applies, switches, or stages an update.
305 * @param greDir the GRE directory
306 * @param updateDir the update root directory
307 * @param appDir the application directory
308 * @param appArgc the number of args passed to the application
309 * @param appArgv the args passed to the application
310 * (used for restarting the application when necessary)
311 * @param restart true when a restart is necessary.
312 * @param isStaged true when the update has already been staged
313 * @param outpid (out) parameter holding the handle to the updater application
314 * when staging updates
316 static void ApplyUpdate(nsIFile
* greDir
, nsIFile
* updateDir
, nsIFile
* appDir
,
317 int appArgc
, char** appArgv
, bool restart
,
318 bool isStaged
, ProcessType
* outpid
) {
319 // The following determines the update operation to perform.
320 // 1. When restart is false the update will be staged.
321 // 2. When restart is true and isStaged is false the update will apply the mar
322 // file to the installation directory.
323 // 3. When restart is true and isStaged is true the update will switch the
324 // staged update with the installation directory.
328 nsCOMPtr
<nsIFile
> updater
;
329 nsAutoCString updaterPath
;
330 nsAutoCString updateDirPath
;
332 // Get an nsIFile reference for the updater in the installation dir.
333 if (!GetFile(greDir
, nsLiteralCString(UPDATER_BIN
), updater
)) {
337 // Get the path to the updater.
338 nsAutoString updaterPathW
;
339 rv
= updater
->GetPath(updaterPathW
);
343 CopyUTF16toUTF8(updaterPathW
, updaterPath
);
345 // Get the path to the update dir.
346 nsAutoString updateDirPathW
;
347 rv
= updateDir
->GetPath(updateDirPathW
);
351 CopyUTF16toUTF8(updateDirPathW
, updateDirPath
);
352 #elif defined(XP_MACOSX)
353 // Get an nsIFile reference for the updater in the installation dir.
354 if (!GetFile(appDir
, nsLiteralCString(UPDATER_APP
), updater
)) {
357 rv
= updater
->AppendNative("Contents"_ns
);
361 rv
= updater
->AppendNative("MacOS"_ns
);
365 rv
= updater
->AppendNative(nsLiteralCString(UPDATER_BIN
));
370 // Get the path to the updater.
371 rv
= updater
->GetNativePath(updaterPath
);
376 // Get the path to the update dir.
377 rv
= updateDir
->GetNativePath(updateDirPath
);
382 // Get an nsIFile reference for the updater in the installation dir.
383 if (!GetFile(greDir
, nsLiteralCString(UPDATER_BIN
), updater
)) {
387 // Get the path to the updater.
388 rv
= updater
->GetNativePath(updaterPath
);
393 // Get the path to the update dir.
394 rv
= updateDir
->GetNativePath(updateDirPath
);
400 // appFilePath and workingDirPath are only used when the application will be
402 nsAutoCString appFilePath
;
403 nsAutoCString workingDirPath
;
405 // Get the path to the current working directory.
406 rv
= GetCurrentWorkingDir(workingDirPath
);
411 // Get the application file path used by the updater to restart the
412 // application after the update has finished.
413 nsCOMPtr
<nsIFile
> appFile
;
414 XRE_GetBinaryPath(getter_AddRefs(appFile
));
420 nsAutoString appFilePathW
;
421 rv
= appFile
->GetPath(appFilePathW
);
425 CopyUTF16toUTF8(appFilePathW
, appFilePath
);
427 rv
= appFile
->GetNativePath(appFilePath
);
434 // Get the installation directory path.
435 nsAutoCString installDirPath
;
436 rv
= GetInstallDirPath(appDir
, installDirPath
);
441 #if defined(XP_MACOSX)
442 // If we're going to do a restart, we need to make sure the font registration
443 // thread has finished before this process exits (bug 1777332).
445 gfxPlatformMac::WaitForFontRegistration();
448 // We need to detect whether elevation is required for this update. This can
449 // occur when an admin user installs the application, but another admin
450 // user attempts to update (see bug 394984).
451 // We only check if we need elevation if we are restarting. We don't attempt
452 // to stage if elevation is required. Staging happens without the user knowing
453 // about it, and we don't want to ask for elevation for seemingly no reason.
454 bool needElevation
= false;
456 needElevation
= !IsRecursivelyWritable(installDirPath
.get());
458 // Normally we would check this via nsIAppStartup::wasSilentlyStarted,
459 // but nsIAppStartup isn't available yet.
460 char* mozAppSilentStart
= PR_GetEnv("MOZ_APP_SILENT_START");
461 bool wasSilentlyStarted
=
462 mozAppSilentStart
&& (strcmp(mozAppSilentStart
, "") != 0);
463 if (wasSilentlyStarted
) {
464 // Elevation always requires prompting for credentials on macOS. If we
465 // are trying to restart silently, we must not display UI such as this
467 // We make this check here rather than in the updater, because it is
468 // actually Firefox that shows the elevation prompt (via
469 // InstallPrivilegedHelper), not the updater.
476 nsAutoCString applyToDirPath
;
477 nsCOMPtr
<nsIFile
> updatedDir
;
478 if (restart
&& !isStaged
) {
479 // The install directory is the same as the apply to directory.
480 applyToDirPath
.Assign(installDirPath
);
482 // Get the directory where the update is staged or will be staged. This is
483 // `updateDir` for macOS and `appDir` for all other platforms. macOS cannot
484 // stage updates inside the .app bundle (`appDir`) without breaking the code
485 // signature on the bundle, so we use `updateDir` instead.
486 #if defined(XP_MACOSX)
487 if (!GetFile(updateDir
, "Updated.app"_ns
, updatedDir
)) {
489 if (!GetFile(appDir
, "updated"_ns
, updatedDir
)) {
494 nsAutoString applyToDirPathW
;
495 rv
= updatedDir
->GetPath(applyToDirPathW
);
499 CopyUTF16toUTF8(applyToDirPathW
, applyToDirPath
);
501 rv
= updatedDir
->GetNativePath(applyToDirPath
);
508 if (restart
&& isStaged
) {
509 // When the update should already be staged make sure that the updated
511 bool updatedDirExists
= false;
512 if (NS_FAILED(updatedDir
->Exists(&updatedDirExists
)) || !updatedDirExists
) {
517 // On platforms where we are not calling execv, we may need to make the
518 // updater executable wait for the calling process to exit. Otherwise, the
519 // updater may have trouble modifying our executable image (because it might
520 // still be in use). This is accomplished by passing our PID to the updater
521 // so that it can wait for us to exit. This is not perfect as there is a race
522 // condition that could bite us. It's possible that the calling process could
523 // exit before the updater waits on the specified PID, and in the meantime a
524 // new process with the same PID could be created. This situation is
525 // unlikely, however, given the way most operating systems recycle PIDs. We'll
526 // take our chances ;-) Construct the PID argument for this process to pass to
530 #if defined(XP_UNIX) & !defined(XP_MACOSX)
531 // When execv is used for an update that requires a restart 0 is passed
532 // which is ignored by the updater.
533 pid
.AssignLiteral("0");
535 pid
.AppendInt((int32_t)getpid());
538 // Append a special token to the PID in order to inform the updater that
539 // it should replace install with the updated directory.
540 pid
.AppendLiteral("/replace");
543 // Signal the updater application that it should stage the update.
544 pid
.AssignLiteral("-1");
550 if (gRestartedByOS
) {
554 char** argv
= static_cast<char**>(malloc((argc
+ 1) * sizeof(char*)));
558 argv
[0] = (char*)updaterPath
.get();
559 argv
[1] = (char*)updateDirPath
.get();
560 argv
[2] = (char*)installDirPath
.get();
561 argv
[3] = (char*)applyToDirPath
.get();
562 argv
[4] = (char*)pid
.get();
563 if (restart
&& appArgc
) {
564 argv
[5] = (char*)workingDirPath
.get();
565 argv
[6] = (char*)appFilePath
.get();
566 for (int i
= 1; i
< appArgc
; ++i
) {
567 argv
[6 + i
] = appArgv
[i
];
569 if (gRestartedByOS
) {
570 // We haven't truly started up, restore this argument so that we will have
572 argv
[6 + appArgc
] = const_cast<char*>("-os-restarted");
575 argv
[argc
] = nullptr;
577 if (restart
&& gSafeMode
) {
578 PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
581 LOG(("spawning updater process [%s]\n", updaterPath
.get()));
583 #if defined(XP_UNIX) && !defined(XP_MACOSX)
584 // We use execv to spawn the updater process on all UNIX systems except Mac
585 // OSX since it is known to cause problems on the Mac. Windows has execv, but
586 // it is a faked implementation that doesn't really replace the current
587 // process. Instead it spawns a new process, so we gain nothing from using
590 int execResult
= execv(updaterPath
.get(), argv
);
600 int execResult
= execv(updaterPath
.get(), argv
);
604 #elif defined(XP_WIN)
606 // Launch the updater to replace the installation with the staged updated.
607 if (!WinLaunchChild(updaterPathW
.get(), argc
, argv
)) {
612 // Launch the updater to either stage or apply an update.
613 if (!WinLaunchChild(updaterPathW
.get(), argc
, argv
, nullptr, outpid
)) {
618 #elif defined(XP_MACOSX)
619 UpdateDriverSetupMacCommandLine(argc
, argv
, restart
);
620 if (restart
&& needElevation
) {
621 bool hasLaunched
= LaunchElevatedUpdate(argc
, argv
, outpid
);
624 LOG(("Failed to launch elevated update!"));
631 // Launch the updater to replace the installation with the staged updated.
632 LaunchChildMac(argc
, argv
);
634 // Launch the updater to either stage or apply an update.
635 LaunchChildMac(argc
, argv
, outpid
);
639 // Launch the updater to replace the installation with the staged updated.
640 PR_CreateProcessDetached(updaterPath
.get(), argv
, nullptr, nullptr);
642 // Launch the updater to either stage or apply an update.
643 *outpid
= PR_CreateProcess(updaterPath
.get(), argv
, nullptr, nullptr);
654 * Wait briefly to see if a process terminates, then return true if it has.
656 * (Not implemented on Windows, where HandleWatcher is used instead.)
658 static bool ProcessHasTerminated(ProcessType pt
) {
659 # if defined(XP_MACOSX)
660 // We're waiting for the process to terminate in LaunchChildMac.
662 # elif defined(XP_UNIX)
664 pid_t exited
= waitpid(pt
, &exitStatus
, WNOHANG
);
666 // Process is still running.
671 LOG(("Error while checking if the updater process is finished"));
672 // This shouldn't happen, but if it does, the updater process is lost to us,
673 // so the best we can do is pretend that it's exited.
676 // If we get here, the process has exited; make sure it exited normally.
677 if (WIFEXITED(exitStatus
) && (WEXITSTATUS(exitStatus
) != 0)) {
678 LOG(("Error while running the updater process, check update.log"));
682 // No way to have a non-blocking implementation on these platforms,
683 // because we're using NSPR and it only provides a blocking wait.
685 PR_WaitProcess(pt
, &exitCode
);
687 LOG(("Error while running the updater process, check update.log"));
694 nsresult
ProcessUpdates(nsIFile
* greDir
, nsIFile
* appDir
, nsIFile
* updRootDir
,
695 int argc
, char** argv
, const char* appVersion
,
696 bool restart
, ProcessType
* pid
) {
700 // If we're in a package, we know any updates that we find are not for us.
701 if (mozilla::widget::WinUtils::HasPackageIdentity()) {
706 nsCOMPtr
<nsIFile
> updatesDir
;
707 rv
= updRootDir
->Clone(getter_AddRefs(updatesDir
));
708 NS_ENSURE_SUCCESS(rv
, rv
);
709 rv
= updatesDir
->AppendNative("updates"_ns
);
710 NS_ENSURE_SUCCESS(rv
, rv
);
711 rv
= updatesDir
->AppendNative("0"_ns
);
712 NS_ENSURE_SUCCESS(rv
, rv
);
714 // Return early since there isn't a valid update when the update application
715 // version file doesn't exist or if the update's application version is less
716 // than the current application version. The cleanup of the update will happen
717 // during post update processing in nsUpdateService.js.
718 nsCOMPtr
<nsIFile
> versionFile
;
719 if (!GetVersionFile(updatesDir
, versionFile
) ||
720 IsOlderVersion(versionFile
, appVersion
)) {
724 nsCOMPtr
<nsIFile
> statusFile
;
725 UpdateStatus status
= GetUpdateStatus(updatesDir
, statusFile
);
728 case ePendingService
: {
729 ApplyUpdate(greDir
, updatesDir
, appDir
, argc
, argv
, restart
, false, pid
);
733 case eAppliedService
:
734 // An update was staged and needs to be switched so the updated
735 // application is used.
736 ApplyUpdate(greDir
, updatesDir
, appDir
, argc
, argv
, restart
, true, pid
);
738 case ePendingElevate
:
739 // No action should be performed since the user hasn't opted into
740 // elevating for the update so continue application startup.
741 case eNoUpdateAction
:
742 // We don't need to do any special processing here, we'll just continue to
743 // startup the application.
750 NS_IMPL_ISUPPORTS(nsUpdateProcessor
, nsIUpdateProcessor
)
752 nsUpdateProcessor::nsUpdateProcessor() : mUpdaterPID(0) {}
755 nsUpdateProcessor::~nsUpdateProcessor() { mProcessWatcher
.Stop(); }
757 nsUpdateProcessor::~nsUpdateProcessor() = default;
761 nsUpdateProcessor::ProcessUpdate() {
764 nsCOMPtr
<nsIProperties
> ds
=
765 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID
, &rv
);
766 NS_ENSURE_SUCCESS(rv
, rv
);
768 nsCOMPtr
<nsIFile
> exeFile
;
769 rv
= ds
->Get(XRE_EXECUTABLE_FILE
, NS_GET_IID(nsIFile
),
770 getter_AddRefs(exeFile
));
771 NS_ENSURE_SUCCESS(rv
, rv
);
773 nsCOMPtr
<nsIFile
> appDir
;
774 rv
= exeFile
->GetParent(getter_AddRefs(appDir
));
775 NS_ENSURE_SUCCESS(rv
, rv
);
777 nsCOMPtr
<nsIFile
> greDir
;
778 rv
= ds
->Get(NS_GRE_DIR
, NS_GET_IID(nsIFile
), getter_AddRefs(greDir
));
779 NS_ENSURE_SUCCESS(rv
, rv
);
781 nsCOMPtr
<nsIFile
> updRoot
;
782 rv
= ds
->Get(XRE_UPDATE_ROOT_DIR
, NS_GET_IID(nsIFile
),
783 getter_AddRefs(updRoot
));
784 NS_ASSERTION(NS_SUCCEEDED(rv
), "Can't get the UpdRootD dir");
786 // XRE_UPDATE_ROOT_DIR should not fail but if it does fallback to the
787 // application directory just to be safe.
789 rv
= appDir
->Clone(getter_AddRefs(updRoot
));
790 NS_ENSURE_SUCCESS(rv
, rv
);
793 nsCOMPtr
<nsIXULAppInfo
> appInfo
=
794 do_GetService("@mozilla.org/xre/app-info;1", &rv
);
795 NS_ENSURE_SUCCESS(rv
, rv
);
797 nsAutoCString appVersion
;
798 rv
= appInfo
->GetVersion(appVersion
);
799 NS_ENSURE_SUCCESS(rv
, rv
);
801 // Copy the parameters to the StagedUpdateInfo structure shared with the
803 mInfo
.mGREDir
= greDir
;
804 mInfo
.mAppDir
= appDir
;
805 mInfo
.mUpdateRoot
= updRoot
;
807 mInfo
.mArgv
= nullptr;
808 mInfo
.mAppVersion
= appVersion
;
810 MOZ_ASSERT(NS_IsMainThread(), "not main thread");
811 nsCOMPtr
<nsIRunnable
> r
=
812 NewRunnableMethod("nsUpdateProcessor::StartStagedUpdate", this,
813 &nsUpdateProcessor::StartStagedUpdate
);
814 return NS_NewNamedThread("UpdateProcessor", getter_AddRefs(mWorkerThread
), r
);
817 void nsUpdateProcessor::StartStagedUpdate() {
818 MOZ_ASSERT(!NS_IsMainThread(), "main thread");
820 // If we fail to launch the updater process or its monitor for some reason, we
821 // need to shut down the worker thread, as there isn't anything more for us to
823 auto onExitStopThread
= mozilla::MakeScopeExit([&] {
824 nsresult rv
= NS_DispatchToMainThread(
825 NewRunnableMethod("nsUpdateProcessor::ShutdownWorkerThread", this,
826 &nsUpdateProcessor::ShutdownWorkerThread
));
827 NS_ENSURE_SUCCESS_VOID(rv
);
830 // Launch updater. (We do this on a worker thread to avoid blocking the main
831 // thread with file I/O.)
832 nsresult rv
= ProcessUpdates(mInfo
.mGREDir
, mInfo
.mAppDir
, mInfo
.mUpdateRoot
,
833 mInfo
.mArgc
, mInfo
.mArgv
,
834 mInfo
.mAppVersion
.get(), false, &mUpdaterPID
);
836 MOZ_LOG(sUpdateLog
, mozilla::LogLevel::Error
,
837 ("could not start updater process: %s", GetStaticErrorName(rv
)));
843 MOZ_LOG(sUpdateLog
, mozilla::LogLevel::Verbose
,
844 ("ProcessUpdates() indicated nothing to do"));
849 // Set up a HandleWatcher to report to the main thread when we're done.
850 RefPtr
<nsIThread
> mainThread
;
851 NS_GetMainThread(getter_AddRefs(mainThread
));
852 mProcessWatcher
.Watch(mUpdaterPID
, mainThread
,
853 NewRunnableMethod("nsUpdateProcessor::UpdateDone", this,
854 &nsUpdateProcessor::UpdateDone
));
856 // On Windows, that's all we need the worker thread for. Let
857 // `onExitStopThread` shut us down.
859 // Monitor the state of the updater process while it is staging an update.
860 rv
= NS_DispatchToCurrentThread(
861 NewRunnableMethod("nsUpdateProcessor::WaitForProcess", this,
862 &nsUpdateProcessor::WaitForProcess
));
864 MOZ_LOG(sUpdateLog
, mozilla::LogLevel::Error
,
865 ("could not start updater process poll: error %s",
866 GetStaticErrorName(rv
)));
870 // Leave the worker thread alive to run WaitForProcess. Either it or its
871 // successors will be responsible for shutting down the worker thread.
872 onExitStopThread
.release();
876 void nsUpdateProcessor::ShutdownWorkerThread() {
877 MOZ_ASSERT(NS_IsMainThread(), "not main thread");
878 mWorkerThread
->Shutdown();
879 mWorkerThread
= nullptr;
883 void nsUpdateProcessor::WaitForProcess() {
884 MOZ_ASSERT(!NS_IsMainThread(), "main thread");
885 if (ProcessHasTerminated(mUpdaterPID
)) {
886 NS_DispatchToMainThread(NewRunnableMethod(
887 "nsUpdateProcessor::UpdateDone", this, &nsUpdateProcessor::UpdateDone
));
889 NS_DispatchToCurrentThread(
890 NewRunnableMethod("nsUpdateProcessor::WaitForProcess", this,
891 &nsUpdateProcessor::WaitForProcess
));
896 void nsUpdateProcessor::UpdateDone() {
897 MOZ_ASSERT(NS_IsMainThread(), "not main thread");
899 nsCOMPtr
<nsIUpdateManager
> um
=
900 do_GetService("@mozilla.org/updates/update-manager;1");
902 // This completes asynchronously, but nothing else that we are doing in this
903 // function requires waiting for this to complete.
904 RefPtr
<mozilla::dom::Promise
> outPromise
;
905 um
->RefreshUpdateStatus(getter_AddRefs(outPromise
));
908 // On Windows, shutting down the worker thread is taken care of by another task.
909 // (Which may not have run yet, so we can't assert.)
911 ShutdownWorkerThread();
916 nsUpdateProcessor::GetServiceRegKeyExists(bool* aResult
) {
918 return NS_ERROR_NOT_IMPLEMENTED
;
919 #else // #ifdef XP_WIN
920 nsCOMPtr
<nsIProperties
> dirSvc(
921 do_GetService("@mozilla.org/file/directory_service;1"));
922 NS_ENSURE_TRUE(dirSvc
, NS_ERROR_SERVICE_NOT_AVAILABLE
);
924 nsCOMPtr
<nsIFile
> installBin
;
925 nsresult rv
= dirSvc
->Get(XRE_EXECUTABLE_FILE
, NS_GET_IID(nsIFile
),
926 getter_AddRefs(installBin
));
927 NS_ENSURE_SUCCESS(rv
, rv
);
929 nsCOMPtr
<nsIFile
> installDir
;
930 rv
= installBin
->GetParent(getter_AddRefs(installDir
));
931 NS_ENSURE_SUCCESS(rv
, rv
);
933 nsAutoString installPath
;
934 rv
= installDir
->GetPath(installPath
);
935 NS_ENSURE_SUCCESS(rv
, rv
);
937 wchar_t maintenanceServiceKey
[MAX_PATH
+ 1];
938 BOOL success
= CalculateRegistryPathFromFilePath(
939 PromiseFlatString(installPath
).get(), maintenanceServiceKey
);
940 NS_ENSURE_TRUE(success
, NS_ERROR_FAILURE
);
943 LSTATUS ls
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, maintenanceServiceKey
, 0,
944 KEY_QUERY_VALUE
| KEY_WOW64_64KEY
, ®Handle
);
945 if (ls
== ERROR_SUCCESS
) {
946 RegCloseKey(regHandle
);
950 if (ls
== ERROR_FILE_NOT_FOUND
) {
954 // We got an error we weren't expecting reading the registry.
955 return NS_ERROR_NOT_AVAILABLE
;
956 #endif // #ifdef XP_WIN
960 nsUpdateProcessor::RegisterApplicationRestartWithLaunchArgs(
961 const nsTArray
<nsString
>& argvExtra
) {
963 return NS_ERROR_NOT_IMPLEMENTED
;
965 // Retrieve current command line arguments for restart
966 // GetCommandLineW() returns a read only pointer to
967 // the arguments the process was launched with.
968 LPWSTR currentCommandLine
= GetCommandLineW();
970 // Register a restart flag for the application based on the current
971 // command line. The program will then automatically restart
973 // The application must have been running for a minimum of 60
974 // seconds for a restart to be correctly registered.
975 if (currentCommandLine
) {
976 // Append additional command line arguments to current command line for
978 nsTArray
<const wchar_t*> additionalArgv(argvExtra
.Length());
979 for (const nsString
& arg
: argvExtra
) {
980 additionalArgv
.AppendElement(static_cast<const wchar_t*>(arg
.get()));
984 LPWSTR
* currentCommandLineArgv
=
985 CommandLineToArgvW(currentCommandLine
, ¤tArgc
);
986 UniquePtr
<LPWSTR
, LocalFreeDeleter
> uniqueCurrentArgv(
987 currentCommandLineArgv
);
988 mozilla::UniquePtr
<wchar_t[]> restartCommandLine
= mozilla::MakeCommandLine(
989 currentArgc
, uniqueCurrentArgv
.get(), additionalArgv
.Length(),
990 additionalArgv
.Elements());
991 ::RegisterApplicationRestart(restartCommandLine
.get(),
992 RESTART_NO_CRASH
| RESTART_NO_HANG
);
994 MOZ_LOG(sUpdateLog
, mozilla::LogLevel::Debug
,
995 ("register application restart succeeded"));
997 MOZ_LOG(sUpdateLog
, mozilla::LogLevel::Error
,
998 ("could not register application restart"));
999 return NS_ERROR_NOT_AVAILABLE
;
1002 #endif // #ifndef XP_WIN