Index: chrome/browser/component_updater/recovery_component_installer.cc |
diff --git a/chrome/browser/component_updater/recovery_component_installer.cc b/chrome/browser/component_updater/recovery_component_installer.cc |
index 9aadd24c0c7364422689bdcdcfbee828ce331041..4fc2231aed724753072b760e0f16729e2c4ba76d 100644 |
--- a/chrome/browser/component_updater/recovery_component_installer.cc |
+++ b/chrome/browser/component_updater/recovery_component_installer.cc |
@@ -13,13 +13,15 @@ |
#include "base/compiler_specific.h" |
#include "base/files/file_path.h" |
#include "base/files/file_util.h" |
+#include "base/json/json_file_value_serializer.h" |
#include "base/logging.h" |
#include "base/path_service.h" |
#include "base/prefs/pref_registry_simple.h" |
#include "base/prefs/pref_service.h" |
+#include "base/process/kill.h" |
#include "base/process/launch.h" |
-#include "base/strings/string_util.h" |
-#include "base/values.h" |
+#include "chrome/common/chrome_switches.h" |
+#include "chrome/common/pref_names.h" |
#include "components/component_updater/component_updater_paths.h" |
#include "components/component_updater/component_updater_service.h" |
#include "components/component_updater/pref_names.h" |
@@ -47,13 +49,90 @@ const base::FilePath::CharType kRecoveryFileName[] = |
const char kRecoveryManifestName[] = "ChromeRecovery"; |
+// This is the contract between ChromeRecovery and this installer for the |
+// meaning of ChromeRecovery process exit code. |
+enum ChromeRecvoeryExitCode { |
waffles
2014/12/05 00:59:48
typo: "Recvoery"
Sorin Jianu
2014/12/05 02:10:36
Just simply write:
// ChromeRecovery process exit
xiaoling
2014/12/05 19:45:56
Done.
xiaoling
2014/12/05 19:45:56
Done.
|
+ EXIT_CODE_RECOVERY_SUCCEEDED = 0, |
+ EXIT_CODE_RECOVERY_SKIPPED = 1, |
+ EXIT_CODE_ELEVATION_NEEDED = 2, |
+}; |
+ |
+// Checks if elevated recovery simulation switch was present on the command |
+// line. This is for testing purpose. |
+bool SimulatingElevatedRecovery() { |
+ const CommandLine& cmd_line = *CommandLine::ForCurrentProcess(); |
Sorin Jianu
2014/12/05 02:10:36
we could use a pointer type here, no need to deref
xiaoling
2014/12/05 19:45:56
Done.
|
+ return cmd_line.HasSwitch(switches::kSimulateElevatedRecovery); |
+} |
+ |
+scoped_ptr<base::DictionaryValue> ReadManifest(const base::FilePath& manifest) { |
+ JSONFileValueSerializer serializer(manifest); |
+ std::string error; |
+ scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error)); |
Sorin Jianu
2014/12/05 02:10:36
Are we including "base/memory/scoped_ptr.h" anywhe
xiaoling
2014/12/05 19:45:56
Done.
|
+ if (!root.get()) |
Sorin Jianu
2014/12/05 02:10:36
this is mostly a matter of preference but you coul
xiaoling
2014/12/05 19:45:56
Done.
|
+ return scoped_ptr<base::DictionaryValue>(); |
+ if (!root->IsType(base::Value::TYPE_DICTIONARY)) |
+ return scoped_ptr<base::DictionaryValue>(); |
+ return scoped_ptr<base::DictionaryValue>( |
+ static_cast<base::DictionaryValue*>(root.release())).Pass(); |
+} |
+ |
+void DoElevatedInstallRecoveryComponent(const base::FilePath& path) { |
+ base::FilePath main_file = path.Append(kRecoveryFileName); |
Sorin Jianu
2014/12/05 02:10:36
Can any of these locals in this function be declar
xiaoling
2014/12/05 19:45:56
Done.
|
+ base::FilePath manifest_file = |
+ path.Append(FILE_PATH_LITERAL("manifest.json")); |
+ if (!base::PathExists(main_file) || !base::PathExists(manifest_file)) |
+ return; |
+ |
+ scoped_ptr<base::DictionaryValue> manifest(ReadManifest(manifest_file)); |
+ std::string name; |
+ manifest->GetStringASCII("name", &name); |
+ if (name != kRecoveryManifestName) |
+ return; |
+ std::string proposed_version; |
+ manifest->GetStringASCII("version", &proposed_version); |
+ Version version(proposed_version.c_str()); |
+ if (!version.IsValid()) |
+ return; |
+ |
+ CommandLine cmdline(main_file); |
+ std::string arguments; |
+ if (manifest->GetStringASCII("x-recovery-args", &arguments)) |
+ cmdline.AppendArg(arguments); |
+ std::string add_version; |
+ if (manifest->GetStringASCII("x-recovery-add-version", &add_version) && |
+ add_version == "yes") |
+ cmdline.AppendSwitchASCII("version", version.GetString()); |
Sorin Jianu
2014/12/05 02:10:36
I always use {} in cases where the conditional and
xiaoling
2014/12/05 19:45:56
Done.
|
+ |
+ base::LaunchOptions options; |
+#if defined(OS_WIN) |
+ options.start_hidden = true; |
+ base::LaunchElevatedProcess(cmdline, options); |
+#else |
+ NOTREACHED(); |
+#endif |
+} |
+ |
+void ElevatedInstallRecoveryComponent(const base::FilePath& installer_path) { |
+ BrowserThread::PostTask( |
Sorin Jianu
2014/12/05 02:10:36
In general, we wanted to get rid of executing code
xiaoling
2014/12/05 19:45:56
Done. Used WorkerPool thread to avoid hassle of ma
|
+ BrowserThread::FILE, |
+ FROM_HERE, |
+ base::Bind(&DoElevatedInstallRecoveryComponent, installer_path)); |
+} |
+ |
} // namespace |
+// Component installer that is responsible to repair the chrome installation |
+// or repair the Google update installation. This is a last resort safety |
+// mechanism. |
+// For user Chrome, recovery component just installs silently. For machine |
+// Chrome, elevation may be needed. If that happens, the installer will set |
+// preference flag prefs::kRecoveryComponentNeedsElevation to request that. |
+// There is a global error service monitors this flag and will pop up |
+// bubble if the flag is set to true. |
+// See chrome/browser/recovery/recovery_install_global_error.cc for details. |
class RecoveryComponentInstaller : public ComponentInstaller { |
public: |
- explicit RecoveryComponentInstaller(const Version& version, |
- PrefService* prefs); |
- |
+ RecoveryComponentInstaller(const Version& version, PrefService* prefs); |
~RecoveryComponentInstaller() override {} |
void OnUpdateError(int error) override; |
@@ -69,6 +148,10 @@ class RecoveryComponentInstaller : public ComponentInstaller { |
PrefService* prefs_; |
}; |
+void SimulateElevatedRecoveryHelper(PrefService* prefs) { |
+ prefs->SetBoolean(prefs::kRecoveryComponentNeedsElevation, true); |
+} |
+ |
void RecoveryRegisterHelper(ComponentUpdateService* cus, PrefService* prefs) { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
Version version(prefs->GetString(prefs::kRecoveryComponentVersion)); |
@@ -92,6 +175,17 @@ void RecoveryUpdateVersionHelper(const Version& version, PrefService* prefs) { |
prefs->SetString(prefs::kRecoveryComponentVersion, version.GetString()); |
} |
+void RecoveryUpdateElevationHelper(bool elevation_needed, PrefService* prefs) { |
Sorin Jianu
2014/12/05 02:10:36
We can be more specific here and rename this funct
xiaoling
2014/12/05 19:45:56
Done.
|
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ prefs->SetBoolean(prefs::kRecoveryComponentNeedsElevation, elevation_needed); |
+} |
+ |
+void RecoveryUpdateUnpackPathHelper(const base::FilePath& unpack_path, |
+ PrefService* prefs) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ prefs->SetFilePath(prefs::kRecoveryComponentUnpackPath, unpack_path); |
Sorin Jianu
2014/12/05 02:10:36
Same as above.
SetRecoveryComponentNeedsUnpackPat
xiaoling
2014/12/05 19:45:56
Done.
|
+} |
+ |
RecoveryComponentInstaller::RecoveryComponentInstaller(const Version& version, |
PrefService* prefs) |
: current_version_(version), prefs_(prefs) { |
@@ -120,11 +214,8 @@ bool RecoveryComponentInstaller::Install(const base::DictionaryValue& manifest, |
base::FilePath path; |
if (!PathService::Get(DIR_RECOVERY_BASE, &path)) |
return false; |
- if (!base::PathExists(path)) { |
- if (!base::CreateDirectory(path)) { |
+ if (!base::PathExists(path) && !base::CreateDirectory(path)) |
return false; |
- } |
- } |
path = path.AppendASCII(version.GetString()); |
if (base::PathExists(path) && !base::DeleteFile(path, true)) |
return false; |
@@ -142,18 +233,48 @@ bool RecoveryComponentInstaller::Install(const base::DictionaryValue& manifest, |
if (manifest.GetStringASCII("x-recovery-args", &arguments)) |
cmdline.AppendArg(arguments); |
std::string add_version; |
- if (manifest.GetStringASCII("x-recovery-add-version", &add_version)) { |
- if (add_version == "yes") |
- cmdline.AppendSwitchASCII("version", current_version_.GetString()); |
+ if (manifest.GetStringASCII("x-recovery-add-version", &add_version) && |
+ add_version == "yes") |
+ cmdline.AppendSwitchASCII("version", current_version_.GetString()); |
+ |
+ bool install_result = false; |
+#if defined(OS_WIN) |
Sorin Jianu
2014/12/05 02:10:36
Refactor as discussed.
xiaoling
2014/12/05 19:45:56
Done.
|
+ base::ProcessHandle process_handle; |
+ base::LaunchOptions options; |
+ options.start_hidden = true; |
+ if (base::LaunchProcess(cmdline, options, &process_handle)) { |
+ int installer_exit_code = 0; |
+ const base::TimeDelta kMaxWaitTime = base::TimeDelta::FromSeconds(600); |
+ if (base::WaitForExitCodeWithTimeout(process_handle, |
+ &installer_exit_code, |
+ kMaxWaitTime)) { |
+ if (installer_exit_code == EXIT_CODE_ELEVATION_NEEDED) { |
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
+ base::Bind(&RecoveryUpdateUnpackPathHelper, path, prefs_)); |
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
+ base::Bind(&RecoveryUpdateElevationHelper, true, prefs_)); |
+ } |
+ |
+ // Mark install result as true regardless of exit code. We may need to do |
+ // elevated install further, but from updater service perspective, the |
+ // install is completed. |
+ install_result = true; |
+ } else { |
+ // Ensure that the process terminates. |
+ base::KillProcess(process_handle, -1, true); |
+ } |
} |
- current_version_ = version; |
- if (prefs_) { |
- BrowserThread::PostTask( |
- BrowserThread::UI, |
- FROM_HERE, |
- base::Bind(&RecoveryUpdateVersionHelper, version, prefs_)); |
+#else |
+ install_result = base::LaunchProcess(cmdline, base::LaunchOptions(), NULL); |
+#endif |
+ |
+ if (install_result) { |
+ current_version_ = version; |
+ if (prefs_) |
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
+ base::Bind(&RecoveryUpdateVersionHelper, version, prefs_)); |
} |
- return base::LaunchProcess(cmdline, base::LaunchOptions(), NULL); |
+ return install_result; |
} |
bool RecoveryComponentInstaller::GetInstalledFile( |
@@ -164,6 +285,13 @@ bool RecoveryComponentInstaller::GetInstalledFile( |
void RegisterRecoveryComponent(ComponentUpdateService* cus, |
PrefService* prefs) { |
+ if (SimulatingElevatedRecovery()) { |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, |
+ FROM_HERE, |
+ base::Bind(&SimulateElevatedRecoveryHelper, prefs)); |
+ } |
+ |
#if !defined(OS_CHROMEOS) |
// We delay execute the registration because we are not required in |
// the critical path during browser startup. |
@@ -177,14 +305,22 @@ void RegisterRecoveryComponent(ComponentUpdateService* cus, |
void RegisterPrefsForRecoveryComponent(PrefRegistrySimple* registry) { |
registry->RegisterStringPref(prefs::kRecoveryComponentVersion, "0.0.0.0"); |
+ registry->RegisterFilePathPref(prefs::kRecoveryComponentUnpackPath, |
+ base::FilePath()); |
+ registry->RegisterBooleanPref(prefs::kRecoveryComponentNeedsElevation, false); |
} |
void AcceptedElevatedRecoveryInstall(PrefService* prefs) { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
+ ElevatedInstallRecoveryComponent( |
+ prefs->GetFilePath(prefs::kRecoveryComponentUnpackPath)); |
+ prefs->SetBoolean(prefs::kRecoveryComponentNeedsElevation, false); |
} |
void DeclinedElevatedRecoveryInstall(PrefService* prefs) { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ prefs->SetBoolean(prefs::kRecoveryComponentNeedsElevation, false); |
} |
} // namespace component_updater |