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..ddf645f564e95ff089c75b946909b54c7ea9fdd9 100644 |
--- a/chrome/browser/component_updater/recovery_component_installer.cc |
+++ b/chrome/browser/component_updater/recovery_component_installer.cc |
@@ -13,13 +13,17 @@ |
#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/memory/scoped_ptr.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 "base/threading/worker_pool.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 +51,87 @@ const base::FilePath::CharType kRecoveryFileName[] = |
const char kRecoveryManifestName[] = "ChromeRecovery"; |
+// ChromeRecovery process exit codes. |
+enum ChromeRecoveryExitCode { |
+ 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/06 00:41:53
do you still want to have the local?
xiaoling
2014/12/06 00:56:27
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)); |
+ if (root.get() && root->IsType(base::Value::TYPE_DICTIONARY)) { |
+ return scoped_ptr<base::DictionaryValue>( |
+ static_cast<base::DictionaryValue*>(root.release())); |
+ } |
+ return scoped_ptr<base::DictionaryValue>(); |
+} |
+ |
+#if defined(OS_WIN) |
+void DoElevatedInstallRecoveryComponent(const base::FilePath& path) { |
+ const base::FilePath main_file = path.Append(kRecoveryFileName); |
+ const 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()); |
Sorin Jianu
2014/12/06 00:41:53
const?
xiaoling
2014/12/06 00:56:27
Done.
|
+ 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()); |
+ } |
+ |
+ base::LaunchOptions options; |
+ options.start_hidden = true; |
+ base::LaunchElevatedProcess(cmdline, options); |
+} |
+ |
+void ElevatedInstallRecoveryComponent(const base::FilePath& installer_path) { |
+ base::WorkerPool::PostTask( |
+ FROM_HERE, |
+ base::Bind(&DoElevatedInstallRecoveryComponent, installer_path), |
+ true); |
+} |
+#endif |
+ |
} // 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; |
@@ -65,10 +143,17 @@ class RecoveryComponentInstaller : public ComponentInstaller { |
base::FilePath* installed_file) override; |
private: |
+ bool RunInstallCommand(const CommandLine& cmdline, |
+ const base::FilePath& installer_path) const; |
+ |
Version current_version_; |
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 +177,18 @@ void RecoveryUpdateVersionHelper(const Version& version, PrefService* prefs) { |
prefs->SetString(prefs::kRecoveryComponentVersion, version.GetString()); |
} |
+void SetRecoveryComponentNeedsElevationPrefs( |
+ bool elevation_needed, PrefService* prefs) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ prefs->SetBoolean(prefs::kRecoveryComponentNeedsElevation, elevation_needed); |
+} |
+ |
+void SetRecoveryComponentNeedsUnpackPath(const base::FilePath& unpack_path, |
+ PrefService* prefs) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ prefs->SetFilePath(prefs::kRecoveryComponentUnpackPath, unpack_path); |
+} |
+ |
RecoveryComponentInstaller::RecoveryComponentInstaller(const Version& version, |
PrefService* prefs) |
: current_version_(version), prefs_(prefs) { |
@@ -102,6 +199,41 @@ void RecoveryComponentInstaller::OnUpdateError(int error) { |
NOTREACHED() << "Recovery component update error: " << error; |
} |
+#if defined(OS_WIN) |
+bool RecoveryComponentInstaller::RunInstallCommand( |
+ const CommandLine& cmdline, const base::FilePath& installer_folder) const { |
+ base::ProcessHandle process_handle; |
+ base::LaunchOptions options; |
+ options.start_hidden = true; |
+ if (!base::LaunchProcess(cmdline, options, &process_handle)) |
+ return false; |
+ |
+ int installer_exit_code = 0; |
+ const base::TimeDelta kMaxWaitTime = base::TimeDelta::FromSeconds(600); |
+ if (base::WaitForExitCodeWithTimeout(process_handle, |
+ &installer_exit_code, |
+ kMaxWaitTime) && |
+ installer_exit_code == EXIT_CODE_ELEVATION_NEEDED) { |
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
Sorin Jianu
2014/12/06 00:41:53
we could refactor this as two calls which occur he
xiaoling
2014/12/06 00:56:27
Done.
|
+ base::Bind(&SetRecoveryComponentNeedsUnpackPath, |
+ installer_folder, |
+ prefs_)); |
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
+ base::Bind(&SetRecoveryComponentNeedsElevationPrefs, true, prefs_)); |
+ } |
+ |
+ // Returns true regardless of exit code since from updater service |
+ // perspective the install is done, even we may need to do elevated |
+ // install later. |
+ return true; |
+} |
+#else |
+bool RecoveryComponentInstaller::RunInstallCommand( |
+ const CommandLine& cmdline, const base::FilePath&) const { |
+ return base::LaunchProcess(cmdline, base::LaunchOptions(), NULL); |
+} |
+#endif |
+ |
bool RecoveryComponentInstaller::Install(const base::DictionaryValue& manifest, |
const base::FilePath& unpack_path) { |
std::string name; |
@@ -120,11 +252,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,10 +271,15 @@ 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()); |
+ } |
+ |
+ if (!RunInstallCommand(cmdline, path)) { |
+ return false; |
} |
+ |
current_version_ = version; |
if (prefs_) { |
BrowserThread::PostTask( |
@@ -153,7 +287,7 @@ bool RecoveryComponentInstaller::Install(const base::DictionaryValue& manifest, |
FROM_HERE, |
base::Bind(&RecoveryUpdateVersionHelper, version, prefs_)); |
} |
- return base::LaunchProcess(cmdline, base::LaunchOptions(), NULL); |
+ return true; |
} |
bool RecoveryComponentInstaller::GetInstalledFile( |
@@ -164,6 +298,13 @@ bool RecoveryComponentInstaller::GetInstalledFile( |
void RegisterRecoveryComponent(ComponentUpdateService* cus, |
PrefService* prefs) { |
+ if (SimulatingElevatedRecovery()) { |
Sorin Jianu
2014/12/06 00:41:52
do we need to recovery for Chrome OS too?
xiaoling
2014/12/06 00:56:27
Done.
|
+ 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 +318,24 @@ 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)); |
+ |
+#if defined(OS_WIN) |
+ ElevatedInstallRecoveryComponent( |
+ prefs->GetFilePath(prefs::kRecoveryComponentUnpackPath)); |
+#endif |
+ prefs->SetBoolean(prefs::kRecoveryComponentNeedsElevation, false); |
} |
void DeclinedElevatedRecoveryInstall(PrefService* prefs) { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ prefs->SetBoolean(prefs::kRecoveryComponentNeedsElevation, false); |
} |
} // namespace component_updater |