Commit 2614ed92 authored by grt@chromium.org's avatar grt@chromium.org

Launch Google Update on uninstall.

So it can self-destruct if Chrome (or Chrome Frame) was the last Omaha-managed product on the system.

BUG=114786
TEST=Install Google Update 1.3.21.105 or later and Chrome w/ --verbose-logging.  Uninstall Chrome and see that Google Update vanishes immediately.  Take a look at chrome_installer.log and look for messages related to Launching Google Update's uninstaller.


Review URL: http://codereview.chromium.org/9693055

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@126888 0039d316-1c4b-4281-b951-d872f2087c98
parent 1b3f6b7b
......@@ -68,6 +68,7 @@ using installer::MasterPreferences;
const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices";
const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\";
const wchar_t kSystemPrincipalSid[] = L"S-1-5-18";
const int kGoogleUpdateTimeoutMs = 20 * 1000;
const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
MiniDumpWithProcessThreadData | // Get PEB and TEB.
......@@ -834,6 +835,41 @@ installer::InstallStatus UninstallProduct(
cmd_line.GetProgram(), product, remove_all, force_uninstall, cmd_line);
}
// Tell Google Update that an uninstall has taken place. This gives it a chance
// to uninstall itself straight away if no more products are installed on the
// system rather than waiting for the next time the scheduled task runs.
// Success or failure of Google Update has no bearing on the success or failure
// of Chrome's uninstallation.
void UninstallGoogleUpdate(bool system_install) {
string16 uninstall_cmd(
GoogleUpdateSettings::GetUninstallCommandLine(system_install));
if (!uninstall_cmd.empty()) {
base::win::ScopedHandle process;
LOG(INFO) << "Launching Google Update's uninstaller: " << uninstall_cmd;
if (base::LaunchProcess(uninstall_cmd, base::LaunchOptions(),
process.Receive())) {
int exit_code = 0;
if (base::WaitForExitCodeWithTimeout(process, &exit_code,
kGoogleUpdateTimeoutMs)) {
if (exit_code == 0) {
LOG(INFO) << " normal exit.";
} else {
LOG(ERROR) << "Google Update uninstaller (" << uninstall_cmd
<< ") exited with code " << exit_code << ".";
}
} else {
// The process didn't finish in time, or GetExitCodeProcess failed.
LOG(ERROR) << "Google Update uninstaller (" << uninstall_cmd
<< ") is taking more than " << kGoogleUpdateTimeoutMs
<< " milliseconds to complete.";
}
} else {
PLOG(ERROR) << "Failed to launch Google Update uninstaller ("
<< uninstall_cmd << ")";
}
}
}
installer::InstallStatus UninstallProducts(
const InstallationState& original_state,
const InstallerState& installer_state,
......@@ -860,6 +896,8 @@ installer::InstallStatus UninstallProducts(
install_status = prod_status;
}
UninstallGoogleUpdate(installer_state.system_install());
return install_status;
}
......
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
......@@ -12,6 +12,7 @@ const wchar_t kRegPathClients[] = L"Software\\Google\\Update\\Clients";
const wchar_t kRegPathClientState[] = L"Software\\Google\\Update\\ClientState";
const wchar_t kRegPathClientStateMedium[]
= L"Software\\Google\\Update\\ClientStateMedium";
const wchar_t kRegPathGoogleUpdate[] = L"Software\\Google\\Update";
const wchar_t kRegCommandsKey[] = L"Commands";
......@@ -41,6 +42,7 @@ const wchar_t kRegRLZReactivationBrandField[] = L"reactivationbrand";
const wchar_t kRegReferralField[] = L"referral";
const wchar_t kRegRenameCmdField[] = L"cmd";
const wchar_t kRegSendsPingsField[] = L"SendsPings";
const wchar_t kRegUninstallCmdLine[] = L"UninstallCmdLine";
const wchar_t kRegUsageStatsField[] = L"usagestats";
const wchar_t kRegVersionField[] = L"pv";
const wchar_t kRegWebAccessibleField[] = L"WebAccessible";
......
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
......@@ -16,11 +16,14 @@ extern const wchar_t kChromeUpgradeCode[];
extern const wchar_t kRegPathClients[];
// The difference between ClientState and ClientStateMedium is that the former
// lives on HKCU or HKLM and the later always lives in HKLM. The only use of
// the ClientStateMedium is for the EULA consent. See bug 1594565.
// lives on HKCU or HKLM and the later always lives in HKLM. ClientStateMedium
// is primarily used for consent of the EULA and stats collection. See bug
// 1594565.
extern const wchar_t kRegPathClientState[];
extern const wchar_t kRegPathClientStateMedium[];
extern const wchar_t kRegPathGoogleUpdate[];
// The name of the "Commands" key that lives in an app's Clients key
// (a.k.a. "Version" key).
extern const wchar_t kRegCommandsKey[];
......@@ -50,6 +53,7 @@ extern const wchar_t kRegRLZReactivationBrandField[];
extern const wchar_t kRegReferralField[];
extern const wchar_t kRegRenameCmdField[];
extern const wchar_t kRegSendsPingsField[];
extern const wchar_t kRegUninstallCmdLine[];
extern const wchar_t kRegUsageStatsField[];
extern const wchar_t kRegVersionField[];
extern const wchar_t kRegWebAccessibleField[];
......
......@@ -531,3 +531,16 @@ GoogleUpdateSettings::UpdatePolicy GoogleUpdateSettings::GetAppUpdatePolicy(
return update_policy;
}
string16 GoogleUpdateSettings::GetUninstallCommandLine(bool system_install) {
const HKEY root_key = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
string16 cmd_line;
RegKey update_key;
if (update_key.Open(root_key, google_update::kRegPathGoogleUpdate,
KEY_QUERY_VALUE) == ERROR_SUCCESS) {
update_key.ReadValue(google_update::kRegUninstallCmdLine, &cmd_line);
}
return cmd_line;
}
......@@ -196,6 +196,10 @@ class GoogleUpdateSettings {
static UpdatePolicy GetAppUpdatePolicy(const std::wstring& app_guid,
bool* is_overridden);
// Returns Google Update's uninstall command line, or an empty string if none
// is found.
static string16 GetUninstallCommandLine(bool system_install);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(GoogleUpdateSettings);
};
......
......@@ -23,10 +23,6 @@ using installer::ChannelInfo;
namespace {
const wchar_t kHKCUReplacement[] =
L"Software\\Google\\InstallUtilUnittest\\HKCU";
const wchar_t kHKLMReplacement[] =
L"Software\\Google\\InstallUtilUnittest\\HKLM";
const wchar_t kGoogleUpdatePoliciesKey[] =
L"SOFTWARE\\Policies\\Google\\Update";
const wchar_t kGoogleUpdateUpdateDefault[] = L"UpdateDefault";
......@@ -45,38 +41,9 @@ const wchar_t kTestProductGuid[] = L"{89F1B351-B15D-48D4-8F10-1298721CF13D}";
// and user settings.
class GoogleUpdateSettingsTest: public testing::Test {
protected:
virtual void SetUp() {
// Wipe the keys we redirect to.
// This gives us a stable run, even in the presence of previous
// crashes or failures.
LSTATUS err = SHDeleteKey(HKEY_CURRENT_USER, kHKCUReplacement);
EXPECT_TRUE(err == ERROR_SUCCESS || err == ERROR_FILE_NOT_FOUND);
err = SHDeleteKey(HKEY_CURRENT_USER, kHKLMReplacement);
EXPECT_TRUE(err == ERROR_SUCCESS || err == ERROR_FILE_NOT_FOUND);
// Create the keys we're redirecting HKCU and HKLM to.
ASSERT_EQ(ERROR_SUCCESS,
hkcu_.Create(HKEY_CURRENT_USER, kHKCUReplacement, KEY_READ));
ASSERT_EQ(ERROR_SUCCESS,
hklm_.Create(HKEY_CURRENT_USER, kHKLMReplacement, KEY_READ));
// And do the switcharoo.
ASSERT_EQ(ERROR_SUCCESS,
::RegOverridePredefKey(HKEY_CURRENT_USER, hkcu_.Handle()));
ASSERT_EQ(ERROR_SUCCESS,
::RegOverridePredefKey(HKEY_LOCAL_MACHINE, hklm_.Handle()));
}
virtual void TearDown() {
// Undo the redirection.
EXPECT_EQ(ERROR_SUCCESS, ::RegOverridePredefKey(HKEY_CURRENT_USER, NULL));
EXPECT_EQ(ERROR_SUCCESS, ::RegOverridePredefKey(HKEY_LOCAL_MACHINE, NULL));
// Close our handles and delete the temp keys we redirected to.
hkcu_.Close();
hklm_.Close();
EXPECT_EQ(ERROR_SUCCESS, SHDeleteKey(HKEY_CURRENT_USER, kHKCUReplacement));
EXPECT_EQ(ERROR_SUCCESS, SHDeleteKey(HKEY_CURRENT_USER, kHKLMReplacement));
virtual void SetUp() OVERRIDE {
registry_overrides_.OverrideRegistry(HKEY_LOCAL_MACHINE, L"HKLM_pit");
registry_overrides_.OverrideRegistry(HKEY_CURRENT_USER, L"HKCU_pit");
}
enum SystemUserInstall {
......@@ -181,8 +148,7 @@ class GoogleUpdateSettingsTest: public testing::Test {
return ap_key_value;
}
RegKey hkcu_;
RegKey hklm_;
registry_util::RegistryOverrideManager registry_overrides_;
};
} // namespace
......@@ -590,6 +556,65 @@ TEST_F(GoogleUpdateSettingsTest, GetAppUpdatePolicyAppOverride) {
#endif // defined(GOOGLE_CHROME_BUILD)
// Test GoogleUpdateSettings::GetUninstallCommandLine at system- or user-level,
// according to the param.
class GetUninstallCommandLine : public GoogleUpdateSettingsTest,
public testing::WithParamInterface<bool> {
protected:
static const wchar_t kDummyCommand[];
virtual void SetUp() OVERRIDE {
GoogleUpdateSettingsTest::SetUp();
system_install_ = GetParam();
root_key_ = system_install_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
}
HKEY root_key_;
bool system_install_;
};
const wchar_t GetUninstallCommandLine::kDummyCommand[] =
L"\"goopdate.exe\" /spam";
// Tests that GetUninstallCommandLine returns an empty string if there's no
// Software\Google\Update key.
TEST_P(GetUninstallCommandLine, TestNoKey) {
EXPECT_EQ(string16(),
GoogleUpdateSettings::GetUninstallCommandLine(system_install_));
}
// Tests that GetUninstallCommandLine returns an empty string if there's no
// UninstallCmdLine value in the Software\Google\Update key.
TEST_P(GetUninstallCommandLine, TestNoValue) {
RegKey(root_key_, google_update::kRegPathGoogleUpdate, KEY_SET_VALUE);
EXPECT_EQ(string16(),
GoogleUpdateSettings::GetUninstallCommandLine(system_install_));
}
// Tests that GetUninstallCommandLine returns an empty string if there's an
// empty UninstallCmdLine value in the Software\Google\Update key.
TEST_P(GetUninstallCommandLine, TestEmptyValue) {
RegKey(root_key_, google_update::kRegPathGoogleUpdate, KEY_SET_VALUE)
.WriteValue(google_update::kRegUninstallCmdLine, L"");
EXPECT_EQ(string16(),
GoogleUpdateSettings::GetUninstallCommandLine(system_install_));
}
// Tests that GetUninstallCommandLine returns the correct string if there's an
// UninstallCmdLine value in the Software\Google\Update key.
TEST_P(GetUninstallCommandLine, TestRealValue) {
RegKey(root_key_, google_update::kRegPathGoogleUpdate, KEY_SET_VALUE)
.WriteValue(google_update::kRegUninstallCmdLine, kDummyCommand);
EXPECT_EQ(string16(kDummyCommand),
GoogleUpdateSettings::GetUninstallCommandLine(system_install_));
// Make sure that there's no value in the other level (user or system).
EXPECT_EQ(string16(),
GoogleUpdateSettings::GetUninstallCommandLine(!system_install_));
}
INSTANTIATE_TEST_CASE_P(GetUninstallCommandLineAtLevel, GetUninstallCommandLine,
testing::Bool());
// Test values for use by the CollectStatsConsent test fixture.
class StatsState {
public:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment