Index: setup/setup.cc |
diff --git a/setup/setup.cc b/setup/setup.cc |
deleted file mode 100644 |
index fd1a6e686443f361a9d8018194f951c30c0f32e6..0000000000000000000000000000000000000000 |
--- a/setup/setup.cc |
+++ /dev/null |
@@ -1,1378 +0,0 @@ |
-// Copyright 2007-2009 Google Inc. |
-// |
-// Licensed under the Apache License, Version 2.0 (the "License"); |
-// you may not use this file except in compliance with the License. |
-// You may obtain a copy of the License at |
-// |
-// http://www.apache.org/licenses/LICENSE-2.0 |
-// |
-// Unless required by applicable law or agreed to in writing, software |
-// distributed under the License is distributed on an "AS IS" BASIS, |
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
-// See the License for the specific language governing permissions and |
-// limitations under the License. |
-// ======================================================================== |
-// |
-// This class handles the installation of Google Update when it is run with the |
-// install or update switch. It is invoked when Google Update is launched from |
-// the meta-installer and as part of self-update. |
- |
-#include "omaha/setup/setup.h" |
-#include <regstr.h> |
-#include <atlpath.h> |
-#include <algorithm> |
-#include <functional> |
-#include <vector> |
-#include "omaha/base/const_object_names.h" |
-#include "omaha/base/constants.h" |
-#include "omaha/base/debug.h" |
-#include "omaha/base/error.h" |
-#include "omaha/base/highres_timer-win32.h" |
-#include "omaha/base/logging.h" |
-#include "omaha/base/omaha_version.h" |
-#include "omaha/base/process.h" |
-#include "omaha/base/reg_key.h" |
-#include "omaha/base/scoped_any.h" |
-#include "omaha/base/scope_guard.h" |
-#include "omaha/base/synchronized.h" |
-#include "omaha/base/system.h" |
-#include "omaha/base/timer.h" |
-#include "omaha/base/user_info.h" |
-#include "omaha/base/utils.h" |
-#include "omaha/base/vistautil.h" |
-#include "omaha/common/app_registry_utils.h" |
-#include "omaha/common/command_line.h" |
-#include "omaha/common/config_manager.h" |
-#include "omaha/common/const_cmd_line.h" |
-#include "omaha/common/const_goopdate.h" |
-#include "omaha/common/event_logger.h" |
-#include "omaha/common/goopdate_utils.h" |
-#include "omaha/common/ping.h" |
-#include "omaha/common/scheduled_task_utils.h" |
-#include "omaha/common/stats_uploader.h" |
-#include "omaha/setup/setup_files.h" |
-#include "omaha/setup/setup_google_update.h" |
-#include "omaha/setup/setup_metrics.h" |
-#include "omaha/setup/setup_service.h" |
- |
-namespace omaha { |
- |
-namespace { |
- |
-const TCHAR* const kUninstallEventDescriptionFormat = _T("%s uninstall"); |
- |
-const int kVersion12 = 12; |
- |
-void GetShutdownEventAttributes(bool is_machine, NamedObjectAttributes* attr) { |
- ASSERT1(attr); |
- GetNamedObjectAttributes(kShutdownEvent, is_machine, attr); |
-} |
- |
-// Returns the process's mode based on its command line. |
-HRESULT GetProcessModeFromPid(uint32 pid, CommandLineMode* mode) { |
- ASSERT1(mode); |
- |
- CString cmd_line; |
- HRESULT hr = Process::GetCommandLine(pid, &cmd_line); |
- if (FAILED(hr)) { |
- SETUP_LOG(LE, (_T("[GetCommandLine failed][%u][0x%08x]"), pid, hr)); |
- return hr; |
- } |
- |
- CommandLineArgs args; |
- hr = ParseCommandLine(cmd_line, &args); |
- if (FAILED(hr)) { |
- SETUP_LOG(LE, (_T("[ParseCommandLine failed][%u][%s][0x%08x]"), |
- pid, cmd_line, hr)); |
- return hr; |
- } |
- |
- OPT_LOG(L2, (_T("[Process %u][cmd line %s][mode %u]"), |
- pid, cmd_line, args.mode)); |
- *mode = args.mode; |
- return S_OK; |
-} |
- |
-void IncrementProcessWaitFailCount(CommandLineMode mode) { |
- switch (mode) { |
- case COMMANDLINE_MODE_UNKNOWN: |
- ++metric_setup_process_wait_failed_unknown; |
- break; |
- case COMMANDLINE_MODE_CORE: |
- ++metric_setup_process_wait_failed_core; |
- break; |
- case COMMANDLINE_MODE_REPORTCRASH: |
- ++metric_setup_process_wait_failed_report; |
- break; |
- case COMMANDLINE_MODE_UPDATE: |
- ++metric_setup_process_wait_failed_update; |
- break; |
- case COMMANDLINE_MODE_HANDOFF_INSTALL: |
- ++metric_setup_process_wait_failed_handoff; |
- break; |
- case COMMANDLINE_MODE_UA: |
- ++metric_setup_process_wait_failed_ua; |
- break; |
- case COMMANDLINE_MODE_CODE_RED_CHECK: |
- ++metric_setup_process_wait_failed_cr; |
- break; |
- case COMMANDLINE_MODE_NOARGS: |
- case COMMANDLINE_MODE_SERVICE: |
- |
- case COMMANDLINE_MODE_REGSERVER: |
- case COMMANDLINE_MODE_UNREGSERVER: |
- case COMMANDLINE_MODE_NETDIAGS: |
- case COMMANDLINE_MODE_CRASH: |
- case COMMANDLINE_MODE_INSTALL: |
- case COMMANDLINE_MODE_RECOVER: |
- case COMMANDLINE_MODE_WEBPLUGIN: |
- case COMMANDLINE_MODE_COMSERVER: |
- case COMMANDLINE_MODE_REGISTER_PRODUCT: |
- case COMMANDLINE_MODE_UNREGISTER_PRODUCT: |
- case COMMANDLINE_MODE_SERVICE_REGISTER: |
- case COMMANDLINE_MODE_SERVICE_UNREGISTER: |
- case COMMANDLINE_MODE_CRASH_HANDLER: |
- case COMMANDLINE_MODE_COMBROKER: |
- case COMMANDLINE_MODE_ONDEMAND: |
- case COMMANDLINE_MODE_MEDIUM_SERVICE: |
- case COMMANDLINE_MODE_UNINSTALL: |
- case COMMANDLINE_MODE_PING: |
- default: |
- ++metric_setup_process_wait_failed_other; |
- break; |
- } |
-} |
- |
-// Returns the pids of all other GoogleUpdate.exe processes with the specified |
-// argument string. Checks processes for all users that it has privileges to |
-// access. |
-HRESULT GetPidsWithArgsForAllUsers(const CString& args, |
- std::vector<uint32>* pids) { |
- ASSERT1(pids); |
- |
- std::vector<CString> command_line; |
- command_line.push_back(args); |
- |
- DWORD flags = EXCLUDE_CURRENT_PROCESS | |
- INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING; |
- HRESULT hr = Process::FindProcesses(flags, |
- kOmahaShellFileName, |
- true, |
- CString(), |
- command_line, |
- pids); |
- if (FAILED(hr)) { |
- SETUP_LOG(LE, (_T("[FindProcesses failed][0x%08x]"), hr)); |
- return hr; |
- } |
- |
- return S_OK; |
-} |
- |
-void WriteGoogleUpdateUninstallEvent(bool is_machine) { |
- CString description; |
- description.Format(kUninstallEventDescriptionFormat, kAppName); |
- GoogleUpdateLogEvent uninstall_event(EVENTLOG_INFORMATION_TYPE, |
- kUninstallEventId, |
- is_machine); |
- uninstall_event.set_event_desc(description); |
- uninstall_event.WriteEvent(); |
-} |
- |
-// TODO(omaha3): Enable. Will we still need this method? |
-// Returns true if the mode can be determined and pid represents a "/c" process. |
-bool IsCoreProcess(uint32 pid) { |
- CommandLineMode mode(COMMANDLINE_MODE_UNKNOWN); |
- return SUCCEEDED(GetProcessModeFromPid(pid, &mode)) && |
- COMMANDLINE_MODE_CORE == mode; |
-} |
- |
-} // namespace |
- |
-bool Setup::did_uninstall_ = false; |
- |
-Setup::Setup(bool is_machine) |
- : is_machine_(is_machine), |
- is_self_update_(false), |
- extra_code1_(S_OK) { |
- SETUP_LOG(L2, (_T("[Setup::Setup]"))); |
-} |
- |
-Setup::~Setup() { |
- SETUP_LOG(L2, (_T("[Setup::~Setup]"))); |
-} |
- |
-// Protects all setup-related operations with: |
-// * Setup Lock - Prevents other instances from installing Google Update for |
-// this machine/user at the same time. |
-// * Shutdown Event - Tells existing other instances and any instances that may |
-// start during Setup to exit. |
-// Setup-related operations do not include installation of the app. |
-HRESULT Setup::Install(bool set_keepalive) { |
- SETUP_LOG(L3, |
- (_T("[Admin=%d, NEAdmin=%d, Update3Svc=%d, MedSvc=%d, Machine=%d"), |
- vista_util::IsUserAdmin(), |
- vista_util::IsUserNonElevatedAdmin(), |
- SetupUpdate3Service::IsServiceInstalled(), |
- SetupUpdateMediumService::IsServiceInstalled(), |
- is_machine_)); |
- |
- ASSERT1(!IsElevationRequired()); |
- |
- // Start the setup timer. |
- metrics_timer_.reset(new HighresTimer); |
- |
- GLock setup_lock; |
- |
- if (!InitSetupLock(is_machine_, &setup_lock)) { |
- SETUP_LOG(L2, (_T("[Setup::InitSetupLock failed]"))); |
- extra_code1_ = kVersion12; |
- return GOOPDATE_E_SETUP_LOCK_INIT_FAILED; |
- } |
- |
- HighresTimer lock_metrics_timer; |
- |
- if (!setup_lock.Lock(kSetupLockWaitMs)) { |
- OPT_LOG(LE, (_T("[Failed to acquire setup lock]"))); |
- return HandleLockFailed(kVersion12); |
- } |
- metric_setup_lock_acquire_ms.AddSample(lock_metrics_timer.GetElapsedMs()); |
- SETUP_LOG(L1, (_T("[Setup Locks acquired]"))); |
- |
- HRESULT hr = DoProtectedInstall(set_keepalive); |
- if (FAILED(hr)) { |
- SETUP_LOG(LE, (_T("[Setup::DoProtectedInstall failed][0x%08x]"), hr)); |
- } |
- |
- SETUP_LOG(L1, (_T("[Releasing Setup Lock]"))); |
- return hr; |
-} |
- |
-// TODO(omaha3): Eliminate lock_version if we do not fix http://b/1076207. |
-// Sets appropriate metrics and extra_code1_ value. It then tries to determine |
-// the scenario that caused this failure and returns an appropriate error. |
-// The detected processes may not actually be in conflict with this one, but are |
-// more than likely the cause of the lock failure. |
-HRESULT Setup::HandleLockFailed(int lock_version) { |
- ++metric_setup_locks_failed; |
- |
- switch (lock_version) { |
- case kVersion12: |
- extra_code1_ = kVersion12; |
- ++metric_setup_lock12_failed; |
- break; |
- default: |
- ASSERT1(false); |
- extra_code1_ = -1; |
- break; |
- } |
- |
- Pids matching_pids; |
- CString switch_to_include; |
- |
- switch_to_include.Format(_T("/%s"), kCmdLineUpdate); |
- HRESULT hr = GetPidsWithArgsForAllUsers(switch_to_include, &matching_pids); |
- if (FAILED(hr)) { |
- ASSERT1(false); |
- return GOOPDATE_E_FAILED_TO_GET_LOCK; |
- } |
- if (!matching_pids.empty()) { |
- return GOOPDATE_E_FAILED_TO_GET_LOCK_UPDATE_PROCESS_RUNNING; |
- } |
- |
- switch_to_include.Format(_T("/%s"), kCmdLineUninstall); |
- hr = GetPidsWithArgsForAllUsers(switch_to_include, &matching_pids); |
- if (FAILED(hr)) { |
- ASSERT1(false); |
- return GOOPDATE_E_FAILED_TO_GET_LOCK; |
- } |
- if (!matching_pids.empty()) { |
- return GOOPDATE_E_FAILED_TO_GET_LOCK_UNINSTALL_PROCESS_RUNNING; |
- } |
- |
- switch_to_include.Format(_T("/%s"), kCmdLineInstall); |
- hr = GetPidsWithArgsForAllUsers(switch_to_include, &matching_pids); |
- if (FAILED(hr)) { |
- ASSERT1(false); |
- return GOOPDATE_E_FAILED_TO_GET_LOCK; |
- } |
- if (matching_pids.empty()) { |
- return GOOPDATE_E_FAILED_TO_GET_LOCK; |
- } |
- |
- // Another /install process was found. Determine if it has the same cmd line. |
- const TCHAR* this_cmd_line = ::GetCommandLine(); |
- if (!this_cmd_line) { |
- ASSERT1(false); |
- return GOOPDATE_E_FAILED_TO_GET_LOCK; |
- } |
- const CString current_cmd_line(this_cmd_line); |
- if (current_cmd_line.IsEmpty()) { |
- ASSERT1(false); |
- return GOOPDATE_E_FAILED_TO_GET_LOCK; |
- } |
- |
- // Strip the directory path, which may vary, and executable name. |
- int exe_index = current_cmd_line.Find(kOmahaShellFileName); |
- if (-1 == exe_index) { |
- ASSERT(false, (_T("Unable to find %s in %s"), |
- kOmahaShellFileName, current_cmd_line)); |
- return GOOPDATE_E_FAILED_TO_GET_LOCK; |
- } |
- int args_start = exe_index + _tcslen(kOmahaShellFileName); |
- // Support enclosed paths; increment past closing double quote. |
- if (_T('"') == current_cmd_line.GetAt(args_start)) { |
- ++args_start; |
- } |
- const int args_length = current_cmd_line.GetLength() - args_start; |
- CString current_args = current_cmd_line.Right(args_length); |
- current_args.Trim(); |
- |
- for (size_t i = 0; i < matching_pids.size(); ++i) { |
- CString matching_pid_cmd_line; |
- if (FAILED(Process::GetCommandLine(matching_pids[i], |
- &matching_pid_cmd_line))) { |
- continue; |
- } |
- |
- if (-1 != matching_pid_cmd_line.Find(current_args)) { |
- // Assume that this is a match and not a subset. |
- return GOOPDATE_E_FAILED_TO_GET_LOCK_MATCHING_INSTALL_PROCESS_RUNNING; |
- } |
- } |
- |
- return GOOPDATE_E_FAILED_TO_GET_LOCK_NONMATCHING_INSTALL_PROCESS_RUNNING; |
-} |
- |
-// Assumes the necessary locks have been acquired. |
-HRESULT Setup::DoProtectedInstall(bool set_keepalive) { |
- SETUP_LOG(L2, (_T("[Setup::DoProtectedInstall]"))); |
- |
- SetupFiles setup_files(is_machine_); |
- |
- HRESULT hr = setup_files.Init(); |
- if (FAILED(hr)) { |
- SETUP_LOG(LE, (_T("[SetupFiles::Init failed][0x%08x]"), hr)); |
- return hr; |
- } |
- |
- if (ShouldInstall(&setup_files)) { |
- ++metric_setup_do_self_install_total; |
- |
- // TODO(omaha3): IMPORTANT: Try to avoid losing users due to firewall |
- // blocking caused by changing the constant shell. Try a simple ping using |
- // the new shell, and if it fails take one of the following actions: |
- // 1) Keep the old shell (if possible). |
- // 2) Fail the self-update. Leave the user on this version. Would need to |
- // figure out a way to avoid updating the user every 5 hours. |
- |
- HRESULT hr = DoProtectedGoogleUpdateInstall(&setup_files); |
- if (FAILED(hr)) { |
- SETUP_LOG(LE, (_T("[DoProtectedGoogleUpdateInstall fail][0x%08x]"), hr)); |
- // Do not return until rolling back and releasing the events. |
- } |
- |
- if (FAILED(hr)) { |
- RollBack(&setup_files); |
- } |
- |
- // We need to hold the shutdown events until phase 2 is complete. |
- // Phase 2 will release the current version's event. Here we release all |
- // shutdown events, including the one that should have already been released |
- // to be safe (i.e. in case phase 2 crashes). |
- // This will happen before the Setup Lock is released, preventing any races. |
- ReleaseShutdownEvents(); |
- |
- if (FAILED(hr)) { |
- return hr; |
- } |
- |
- // If we've been asked to defer uninstall (typically because we're doing |
- // an Omaha-only install to expose the COM API to a later process), set |
- // it on a successful install. |
- if (set_keepalive) { |
- SetDelayUninstall(true); |
- } |
- |
- ++metric_setup_do_self_install_succeeded; |
- } |
- |
- return S_OK; |
-} |
- |
-// Assumes that the shell is the correct version for the existing Omaha version. |
-bool Setup::ShouldInstall(SetupFiles* setup_files) { |
- SETUP_LOG(L2, (_T("[Setup::ShouldInstall]"))); |
- ASSERT1(setup_files); |
- |
- // TODO(omaha3): Figure out a different way to record these stats. |
- bool is_install = true; |
- |
- ++metric_setup_should_install_total; |
- |
- ULONGLONG my_version = GetVersion(); |
- |
- const ConfigManager* cm = ConfigManager::Instance(); |
- CString existing_version; |
- HRESULT hr = RegKey::GetValue(cm->registry_clients_goopdate(is_machine_), |
- kRegValueProductVersion, |
- &existing_version); |
- if (FAILED(hr)) { |
- OPT_LOG(L2, (_T("[fresh install]"))); |
- ++metric_setup_should_install_true_fresh_install; |
- return true; |
- } |
- |
- OPT_LOG(L2, (_T("[Existing version: %s][Running version: %s]"), |
- existing_version, GetVersionString())); |
- |
- // If running from the official install directory for this type of install |
- // (user/machine), it is most likely a OneClick install. Do not install self. |
- if (goopdate_utils::IsRunningFromOfficialGoopdateDir(is_machine_)) { |
- ++metric_setup_should_install_false_oc; |
- return false; |
- } |
- |
- if (is_install) { |
- ++metric_setup_subsequent_install_total; |
- } |
- |
- bool should_install(false); |
- |
- ULONGLONG cv = VersionFromString(existing_version); |
- if (cv > my_version) { |
- SETUP_LOG(L2, (_T("[not installing, newer version exists]"))); |
- ++metric_setup_should_install_false_older; |
- should_install = false; |
- } else if (cv < my_version) { |
- SETUP_LOG(L2, (_T("[installing with local build]"))); |
- ++metric_setup_should_install_true_newer; |
- should_install = true; |
- } else { |
- // Same version. |
- should_install = ShouldOverinstallSameVersion(setup_files); |
- if (should_install) { |
- ++metric_setup_should_install_true_same; |
- } else { |
- ++metric_setup_should_install_false_same; |
- } |
- } |
- |
- if (is_install && should_install) { |
- ++metric_setup_subsequent_install_should_install_true; |
- } |
- |
- OPT_LOG(L1, (_T("[machine = %d][existing version = %s][should_install = %d]"), |
- is_machine_, existing_version, should_install)); |
- |
- return should_install; |
-} |
- |
-// Checks the following: |
-// * OverInstall override. |
-// * The "installed" version in the registry equals this version. |
-// If not, this version was not fully installed even though "pv" says it is. |
-// * Files are properly installed. |
-bool Setup::ShouldOverinstallSameVersion(SetupFiles* setup_files) { |
- SETUP_LOG(L2, (_T("[Setup::ShouldOverinstallSameVersion]"))); |
- ASSERT1(setup_files); |
- |
- const ConfigManager* cm = ConfigManager::Instance(); |
- |
- bool should_over_install = cm->CanOverInstall(); |
- SETUP_LOG(L1, (_T("[should over install = %d]"), should_over_install)); |
- if (should_over_install) { |
- SETUP_LOG(L2, (_T("[overinstalling with local build]"))); |
- return true; |
- } |
- |
- CString installed_version; |
- HRESULT hr = RegKey::GetValue(cm->registry_update(is_machine_), |
- kRegValueInstalledVersion, |
- &installed_version); |
- if (FAILED(hr) || GetVersionString() != installed_version) { |
- SETUP_LOG(L1, (_T("[installed version missing or did not match][%s]"), |
- installed_version)); |
- ++metric_setup_should_install_true_same_completion_missing; |
- return true; |
- } |
- |
- if (setup_files->ShouldOverinstallSameVersion()) { |
- SETUP_LOG(L1, (_T("[files need over-install]"))); |
- return true; |
- } |
- |
- // TODO(omaha): Verify the current installation is complete and correct. |
- // For example, in Omaha 1, we would always set the run key to the version |
- // being installed. Now that code is in SetupGoogleUpdate, and it does not get |
- // called. |
- |
- return false; |
-} |
- |
-HRESULT Setup::DoProtectedGoogleUpdateInstall(SetupFiles* setup_files) { |
- ASSERT1(setup_files); |
- SETUP_LOG(L2, (_T("[Setup::DoProtectedGoogleUpdateInstall]"))); |
- |
- HRESULT hr = StopGoogleUpdateAndWait(); |
- if (FAILED(hr)) { |
- SETUP_LOG(LE, (_T("[StopGoogleUpdateAndWait failed][0x%08x]"), hr)); |
- if (E_ACCESSDENIED == hr) { |
- return GOOPDATE_E_ACCESSDENIED_STOP_PROCESSES; |
- } |
- return hr; |
- } |
- |
-// TODO(omaha3): Enable. Prefer to move out of Setup if possible. |
-#if 0 |
- VERIFY1(SUCCEEDED(ResetMetrics(is_machine_))); |
-#endif |
- |
- hr = RegKey::GetValue( |
- ConfigManager::Instance()->registry_clients_goopdate(is_machine_), |
- kRegValueProductVersion, |
- &saved_version_); |
- if (FAILED(hr)) { |
- SETUP_LOG(L3, (_T("[failed to get existing Omaha version][0x%08x]"), hr)); |
- // Continue as this is expected for first installs. |
- } |
- |
- hr = setup_files->Install(); |
- if (FAILED(hr)) { |
- SETUP_LOG(LE, (_T("[SetupFiles::Install failed][0x%08x]"), hr)); |
- extra_code1_ = |
- setup_files->extra_code1() | PingEvent::kSetupFilesExtraCodeMask; |
- return hr; |
- } |
- |
- hr = SetupGoogleUpdate(); |
- if (FAILED(hr)) { |
- SETUP_LOG(LE, (_T("[SetupGoogleUpdate failed][0x%08x]"), hr)); |
- return hr; |
- } |
- |
- // TODO(omaha3): Maybe move out of Setup. |
- metric_setup_install_google_update_total_ms.AddSample( |
- metrics_timer_->GetElapsedMs()); |
- |
- return S_OK; |
-} |
- |
-void Setup::RollBack(SetupFiles* setup_files) { |
- OPT_LOG(L1, (_T("[Roll back]"))); |
- ASSERT1(setup_files); |
- |
- // Restore the saved version. |
- if (!saved_version_.IsEmpty()) { |
- SETUP_LOG(L1, (_T("[Rolling back version to %s]"), saved_version_)); |
- ++metric_setup_rollback_version; |
- |
- VERIFY1(SUCCEEDED(RegKey::SetValue( |
- ConfigManager::Instance()->registry_clients_goopdate(is_machine_), |
- kRegValueProductVersion, |
- saved_version_))); |
- } |
- |
- // TODO(omaha3): Rollback SetupGoogleUpdate. |
- VERIFY1(SUCCEEDED(setup_files->RollBack())); |
-} |
- |
-// Assumes the caller is ensuring this is the only running instance of setup. |
-// The original process holds the lock while it waits for this one to complete. |
-HRESULT Setup::SetupGoogleUpdate() { |
- SETUP_LOG(L2, (_T("[Setup::SetupGoogleUpdate]"))); |
- |
- HighresTimer phase2_metrics_timer; |
- |
- omaha::SetupGoogleUpdate setup_google_update(is_machine_); |
- |
- HRESULT hr = setup_google_update.FinishInstall(); |
- if (FAILED(hr)) { |
- extra_code1_ = setup_google_update.extra_code1(); |
- SETUP_LOG(LE, (_T("[FinishInstall failed][0x%x][0x%x]"), hr, extra_code1_)); |
- return hr; |
- } |
- |
- // Release the shutdown event so that we can start the core if necessary, and |
- // we do not interfere with other app installs that may be waiting on the |
- // Setup Lock. |
- ASSERT1(shutdown_event_); |
- ReleaseShutdownEvents(); |
- |
- if (!scheduled_task_utils::IsUATaskHealthy(is_machine_)) { |
- HRESULT start_hr = StartCore(); |
- if (FAILED(start_hr)) { |
- SETUP_LOG(LW, (_T("[StartCore failed][0x%x]"), start_hr)); |
- } |
- } |
- |
- // Registration of browser plugins is only done after the shutdown event has |
- // been released; this prevents race conditions where a browser could start |
- // a new install while the shutdown event was still being held. |
- HRESULT plugin_hr = setup_google_update.InstallBrowserPlugins(); |
- if (FAILED(plugin_hr)) { |
- SETUP_LOG(LE, (_T("[InstallBrowserPlugins failed][0x%08x]"), plugin_hr)); |
- } |
- |
- // Setup is now complete. |
- |
- metric_setup_phase2_ms.AddSample(phase2_metrics_timer.GetElapsedMs()); |
- return S_OK; |
-} |
- |
-// Stops all user/machine instances including the service, unregisters using |
-// SetupGoogleUpdate, then deletes the files using SetupFiles. |
-// Does not wait for the processes to exit, except the service. |
-// Protects all operations with the setup lock. If MSI is found busy, Omaha |
-// won't uninstall. |
-HRESULT Setup::Uninstall(bool send_uninstall_ping) { |
- OPT_LOG(L1, (_T("[Setup::Uninstall]"))); |
- ASSERT1(!IsElevationRequired()); |
- |
- // Try to get the global setup lock; if the lock is taken, do not block |
- // waiting to uninstall; just return. |
- GLock setup_lock; |
- VERIFY1(InitSetupLock(is_machine_, &setup_lock)); |
- if (!setup_lock.Lock(0)) { |
- OPT_LOG(LE, (_T("[Failed to acquire setup lock]"))); |
- return E_FAIL; |
- } |
- |
- return DoProtectedUninstall(send_uninstall_ping); |
-} |
- |
-// Aggregates metrics regardless of whether uninstall is allowed. |
-// Foces reporting of the metrics if uninstall is allowed. |
-// Assumes that the current process holds the Setup Lock. |
-HRESULT Setup::DoProtectedUninstall(bool send_uninstall_ping) { |
- const bool can_uninstall = CanUninstallGoogleUpdate(); |
- OPT_LOG(L1, (_T("[CanUninstallGoogleUpdate returned %d]"), can_uninstall)); |
- |
- HRESULT hr = S_OK; |
- if (!can_uninstall) { |
- hr = GOOPDATE_E_CANT_UNINSTALL; |
- } else { |
- hr = StopGoogleUpdateAndWait(); |
- if (FAILED(hr)) { |
- // If there are any clients that don't listen to the shutdown event, |
- // such as the current Update3Web workers, we'll need to wait until |
- // they can be shut down. |
- // TODO(omaha3): We might want to add a count metric for this case, |
- // and maybe go through with the uninstall anyways after several tries. |
- SETUP_LOG(L1, (_T("[StopGoogleUpdateAndWait returned 0x%08x]"), hr)); |
- hr = GOOPDATE_E_CANT_UNINSTALL; |
- } |
- } |
- |
- if (FAILED(hr)) { |
- VERIFY1(SUCCEEDED(AggregateMetrics(is_machine_))); |
- return hr; |
- } |
- hr = AggregateAndReportMetrics(is_machine_, true); |
- ASSERT1(SUCCEEDED(hr) || GOOPDATE_E_CANNOT_USE_NETWORK == hr); |
- |
- bool can_use_network = ConfigManager::Instance()->CanUseNetwork(is_machine_); |
- if (can_use_network && send_uninstall_ping) { |
- SendUninstallPing(); |
- } |
- |
- // Write the event in the event log before uninstalling the program since |
- // the event contains version and language information, which are removed |
- // during the uninstall. |
- WriteGoogleUpdateUninstallEvent(is_machine_); |
- |
- omaha::SetupGoogleUpdate setup_google_update(is_machine_); |
- setup_google_update.Uninstall(); |
- |
- SetupFiles setup_files(is_machine_); |
- setup_files.Uninstall(); |
- |
- OPT_LOG(L1, (_T("[Uninstall complete]"))); |
- did_uninstall_ = true; |
- return S_OK; |
-} |
- |
-// Should only be called after the point where Uninstall would have been called. |
-// Works correctly in the case where the Setup Lock is not held but an app is |
-// being installed because it does not check the number of registered apps. |
-// Either Omaha is installed or has been cleaned up. |
-// Installed means Clients, ClientState, etc. sub keys exist. |
-// Cleaned up may mean the Update key does not exist or some values, such as |
-// mid and uid exist, but there are no subkeys. |
-// The Update key should never exist without any values. |
-// Does not take the Setup Lock because it is just a dbg check. It is possible |
-// for the value of did_uninstall_ to be changed in another thread or for |
-// another process to install or uninstall Google Update while this is running. |
-void Setup::CheckInstallStateConsistency(bool is_machine) { |
- UNREFERENCED_PARAMETER(is_machine); |
-#if DEBUG |
- CString key_name = ConfigManager::Instance()->registry_update(is_machine); |
- if (!RegKey::HasKey(key_name)) { |
- // Either this instance called uninstall or it is the non-elevated machine |
- // instance on Vista and later. Both cannot be true in the same instance. |
- ASSERT1(did_uninstall_ != (is_machine && !vista_util::IsUserAdmin())); |
- return; |
- } |
- |
- RegKey update_key; |
- ASSERT1(SUCCEEDED(update_key.Open(key_name, KEY_READ))); |
- |
- ASSERT1(0 != update_key.GetValueCount()); |
- |
- if (did_uninstall_) { |
- ASSERT1(0 == update_key.GetSubkeyCount()); |
- ASSERT1(!update_key.HasValue(kRegValueInstalledVersion)); |
- ASSERT1(!update_key.HasValue(kRegValueInstalledPath)); |
- } else { |
- ASSERT1(update_key.HasSubkey(_T("Clients"))); |
- ASSERT1(update_key.HasSubkey(_T("ClientState"))); |
- ASSERT1(update_key.HasSubkey(_T("network"))); |
- ASSERT1(update_key.HasValue(kRegValueInstalledVersion)); |
- ASSERT1(update_key.HasValue(kRegValueInstalledPath)); |
- |
- CString installed_version; |
- ASSERT1(SUCCEEDED(update_key.GetValue(kRegValueInstalledVersion, |
- &installed_version))); |
- const CString state_key_name = |
- ConfigManager::Instance()->registry_client_state_goopdate(is_machine); |
- CString pv; |
- ASSERT1(SUCCEEDED(RegKey::GetValue(state_key_name, |
- kRegValueProductVersion, |
- &pv))); |
- ASSERT1(installed_version == pv); |
- } |
-#endif |
-} |
- |
-// Stops both legacy and current instances. |
-// Holds the shutdown events so that other instances do not start running. |
-// The caller is responsible for releasing the events. |
-// Because this waiting occurs before a UI is generated, we do not want to wait |
-// too long. |
-HRESULT Setup::StopGoogleUpdate() { |
- OPT_LOG(L1, (_T("[Stopping other instances]"))); |
- |
- HRESULT hr = SignalShutdownEvent(); |
- if (FAILED(hr)) { |
- SETUP_LOG(LE, (_T("[SignalShutdownEvent failed][0x%08x]"), hr)); |
- return hr; |
- } |
- |
- return S_OK; |
-} |
- |
-HRESULT Setup::StopGoogleUpdateAndWait() { |
- HRESULT hr = StopGoogleUpdate(); |
- if (FAILED(hr)) { |
- SETUP_LOG(LE, (_T("[StopGoogleUpdate failed][0x%08x]"), hr)); |
- return hr; |
- } |
- |
- Pids pids; |
- hr = GetPidsToWaitFor(&pids); |
- if (FAILED(hr)) { |
- SETUP_LOG(LEVEL_ERROR, (_T("[GetPidsToWaitFor failed][0x%08x]"), hr)); |
- return hr; |
- } |
- |
- hr = WaitForOtherInstancesToExit(pids); |
- if (FAILED(hr)) { |
- SETUP_LOG(LE, (_T("[WaitForOtherInstancesToExit failed][0x%08x]"), hr)); |
- return hr; |
- } |
- |
- return S_OK; |
-} |
- |
-// Signals >= 1.2.x processes to exit. |
-HRESULT Setup::SignalShutdownEvent() { |
- SETUP_LOG(L1, (_T("[Setup::SignalShutdownEvent]"))); |
- NamedObjectAttributes event_attr; |
- GetShutdownEventAttributes(is_machine_, &event_attr); |
- |
- if (!shutdown_event_) { |
- HRESULT hr = goopdate_utils::CreateEvent(&event_attr, |
- address(shutdown_event_)); |
- if (FAILED(hr)) { |
- SETUP_LOG(LE, (_T("[CreateEvent current failed][0x%08x]"), hr)); |
- return hr; |
- } |
- } |
- |
- VERIFY1(::SetEvent(get(shutdown_event_))); |
- |
- return S_OK; |
-} |
- |
-void Setup::ReleaseShutdownEvents() { |
- if (!shutdown_event_) { |
- return; |
- } |
- |
- VERIFY1(::ResetEvent(get(shutdown_event_))); |
- reset(shutdown_event_); |
-} |
- |
-// Because this waiting can occur before a UI is generated, we do not want to |
-// wait too long. |
-// If a process fails to stop, its mode is stored in extra_code1_. |
-// Does not return until all opened handles have been closed. |
-// TODO(omaha): Add a parameter to specify the amount of time to wait to this |
-// method and StopGoogleUpdateAndWait after we unify Setup and always have a UI. |
-HRESULT Setup::WaitForOtherInstancesToExit(const Pids& pids) { |
- OPT_LOG(L1, (_T("[Waiting for other instances to exit]"))); |
- |
- // Wait for all the processes to exit. |
- std::vector<HANDLE> handles; |
- for (size_t i = 0; i < pids.size(); ++i) { |
- SETUP_LOG(L2, (_T("[Waiting for process][%u]"), pids[i])); |
- |
- DWORD desired_access = |
- PROCESS_QUERY_INFORMATION | SYNCHRONIZE | PROCESS_TERMINATE; |
- scoped_handle handle(::OpenProcess(desired_access, |
- FALSE, |
- pids[i])); |
- if (!handle) { |
- HRESULT hr = HRESULTFromLastError(); |
- SETUP_LOG(LE, (_T("[::OpenProcess failed][%u][0x%08x]"), pids[i], hr)); |
- continue; |
- } |
- |
- handles.push_back(release(handle)); |
- } |
- |
- HRESULT hr = S_OK; |
- if (!handles.empty()) { |
- SETUP_LOG(L2, (_T("[Calling ::WaitForMultipleObjects]"))); |
- |
- HighresTimer metrics_timer; |
- const int wait_ms = is_self_update_ ? kSetupUpdateShutdownWaitMs : |
- kSetupInstallShutdownWaitMs; |
- DWORD res = ::WaitForMultipleObjects(handles.size(), |
- &handles.front(), |
- true, // wait for all |
- wait_ms); |
- metric_setup_process_wait_ms.AddSample(metrics_timer.GetElapsedMs()); |
- |
- SETUP_LOG(L2, (_T("[::WaitForMultipleObjects returned]"))); |
- ASSERT1(WAIT_OBJECT_0 == res || WAIT_TIMEOUT == res); |
- if (WAIT_FAILED == res) { |
- const DWORD error = ::GetLastError(); |
- SETUP_LOG(LE, (_T("[::WaitForMultipleObjects failed][%u]"), error)); |
- hr = HRESULT_FROM_WIN32(error); |
- } else if (WAIT_OBJECT_0 != res) { |
- OPT_LOG(LEVEL_ERROR, (_T("[Other GoogleUpdate.exe instances failed to ") |
- _T("shutdown in time][%u]"), res)); |
- |
- extra_code1_ = COMMANDLINE_MODE_UNKNOWN; |
- |
- SETUP_LOG(L2, (_T("[Listing processes that did not exit. This may be ") |
- _T("incomplete if processes exited after ") |
- _T("WaitForMultipleObjects returned.]"))); |
- for (size_t i = 0; i < handles.size(); ++i) { |
- if (WAIT_TIMEOUT == ::WaitForSingleObject(handles[i], 0)) { |
- uint32 pid = Process::GetProcessIdFromHandle(handles[i]); |
- if (!pid) { |
- SETUP_LOG(LW, (_T(" [Process::GetProcessIdFromHandle failed][%u]"), |
- ::GetLastError())); |
- SETUP_LOG(L2, (_T(" [Process did not exit][unknown]"))); |
- continue; |
- } |
- SETUP_LOG(L2, (_T(" [Process did not exit][%u]"), pid)); |
- |
- CommandLineMode mode(COMMANDLINE_MODE_UNKNOWN); |
- if (SUCCEEDED(GetProcessModeFromPid(pid, &mode))) { |
- extra_code1_ = mode; |
- } |
- IncrementProcessWaitFailCount(mode); |
- } |
- } |
- |
- ++metric_setup_process_wait_failed; |
- hr = GOOPDATE_E_INSTANCES_RUNNING; |
- } |
- } |
- if (SUCCEEDED(hr)) { |
- SETUP_LOG(L3, (_T("[Wait for all processes to exit succeeded]"))); |
- } else { |
- for (size_t i = 0; i < handles.size(); ++i) { |
- if (!::TerminateProcess(handles[i], UINT_MAX)) { |
- const uint32 pid = Process::GetProcessIdFromHandle(handles[i]); |
- const DWORD error = ::GetLastError(); |
- SETUP_LOG(LW, (_T("[::TerminateProcess failed][%u][%u]"), pid, error)); |
- } |
- } |
- |
- const int kTerminateWaitMs = 500; |
- DWORD res = ::WaitForMultipleObjects(handles.size(), &handles.front(), |
- true, kTerminateWaitMs); |
- if (res != WAIT_OBJECT_0) { |
- const DWORD error = ::GetLastError(); |
- SETUP_LOG(LW, (_T("[Wait failed][%u][%u]"), res, error)); |
- } |
- } |
- |
- // Close the handles. |
- for (size_t i = 0; i < handles.size(); ++i) { |
- if (!::CloseHandle(handles[i])) { |
- HRESULT hr = HRESULTFromLastError(); |
- SETUP_LOG(LEVEL_WARNING, (_T("[CloseHandle failed][0x%08x]"), hr)); |
- } |
- } |
- |
- return S_OK; |
-} |
- |
-// Wait for all instances of Omaha running as the current user - or as any user |
-// in the case of machine installs - except "/install" or "/registerproduct" |
-// instances, which should be blocked by the Setup Lock, which we are holding. |
-HRESULT Setup::GetPidsToWaitFor(Pids* pids) const { |
- ASSERT1(pids); |
- |
- HRESULT hr = GetPidsToWaitForUsingCommandLine(pids); |
- if (FAILED(hr)) { |
- return hr; |
- } |
- |
- ASSERT1(pids->end() == std::find(pids->begin(), pids->end(), |
- ::GetCurrentProcessId())); |
- SETUP_LOG(L3, (_T("[found %d total processes to wait for]"), pids->size())); |
- |
- return S_OK; |
-} |
- |
-// Finds processes to wait for based on the command line. |
-// Differences between Omaha 2 and this code: |
-// * User's processes running outside AppData are not caught. This should only |
-// be /install or /registerproduct. |
-// * In the user case, "machine" processes running as the user are EXcluded |
-// based on path instead of mode and "needsadmin=true". This additionally |
-// excludes oneclick cross-installs (u-to-m). |
-// * In the machine case, "machine" processes running as the user are INcluded |
-// based on path instead of mode and "needsadmin=true". |
-// * /pi: m-to-u running in PF as user with needsadmin=false: now INcluded. |
-// This is a good idea, since we do not want to delete the in-use file. |
-// /pi: u-to-m running in appdata as user with needsadmin=true: now |
-// EXcluded. |
-HRESULT Setup::GetPidsToWaitForUsingCommandLine(Pids* pids) const { |
- CORE_LOG(L3, (_T("[Setup::GetPidsToWaitForUsingCommandLine]"))); |
- |
- ASSERT1(pids); |
- |
- // Get processes running as the current user in the case of user, and all |
- // users as well as SYSTEM in the case of machine, except those with |
- // * "/install" - must be excluded because may be waiting for the Setup Lock. |
- // * "/registerproduct" - same as for /install. |
- std::vector<CString> command_lines; |
- CString switch_to_exclude; |
- switch_to_exclude.Format(_T("/%s"), kCmdLineInstall); |
- command_lines.push_back(switch_to_exclude); |
- switch_to_exclude.Format(_T("/%s"), kCmdLineRegisterProduct); |
- |
- CString user_sid; |
- DWORD flags = EXCLUDE_CURRENT_PROCESS | |
- EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING; |
- |
- if (!is_machine_) { |
- // Search only the same sid as the current user. |
- flags |= INCLUDE_ONLY_PROCESS_OWNED_BY_USER; |
- |
- HRESULT hr = user_info::GetProcessUser(NULL, NULL, &user_sid); |
- if (FAILED(hr)) { |
- CORE_LOG(LE, (_T("[GetProcessUser failed][0x%x]"), hr)); |
- return hr; |
- } |
- } |
- |
- std::vector<uint32> google_update_process_ids; |
- HRESULT hr = Process::FindProcesses(flags, |
- kOmahaShellFileName, |
- true, |
- user_sid, |
- command_lines, |
- &google_update_process_ids); |
- if (FAILED(hr)) { |
- CORE_LOG(LE, (_T(" [FindProcesses failed][0x%08x]"), hr)); |
- return hr; |
- } |
- |
- const ConfigManager* cm = ConfigManager::Instance(); |
- CString official_path(is_machine_ ? |
- cm->GetMachineGoopdateInstallDirNoCreate() : |
- cm->GetUserGoopdateInstallDirNoCreate()); |
- ASSERT1(!official_path.IsEmpty()); |
- |
- // Only include processes running under the official path. |
- Pids pids_to_wait_for; |
- for (size_t i = 0; i < google_update_process_ids.size(); ++i) { |
- CString cmd_line; |
- const uint32 process_id = google_update_process_ids[i]; |
- if (SUCCEEDED(Process::GetCommandLine(process_id, &cmd_line))) { |
- cmd_line.MakeLower(); |
- |
- CString exe_path; |
- if (SUCCEEDED(GetExePathFromCommandLine(cmd_line, &exe_path)) && |
- String_StrNCmp(official_path, exe_path, official_path.GetLength(), |
- true) == 0) { |
- CORE_LOG(L4, (_T(" [Including pid][%u][%s]"), process_id, cmd_line)); |
- pids_to_wait_for.push_back(process_id); |
- } |
- } |
- } |
- |
- pids->swap(pids_to_wait_for); |
- return S_OK; |
-} |
- |
-// On Windows Vista, an admin must be elevated in order to install a machine app |
-// without elevating. On Vista, IsUserAdmin returns false unless the user is |
-// elevated. |
-bool Setup::IsElevationRequired() const { |
- return is_machine_ && !vista_util::IsUserAdmin(); |
-} |
- |
-// Start the machine core process using one of the launch mechanisms. |
-// We know that at least one of the service and scheduled task were installed |
-// because otherwise we would have exited fatally. |
-// If the service was not installed, starting it will just fail silently and we |
-// will start the scheduled task. |
-// do not call this method until the shutdown event has been released or the |
-// process may immediately exit. |
-// TODO(omaha): Provide service_hr and task_hr failures in a ping. |
-HRESULT Setup::StartMachineCoreProcess() const { |
- SETUP_LOG(L3, (_T("[Setup::StartMachineCoreProcess]"))); |
- |
- HighresTimer metrics_timer; |
- |
- // Start the service. |
- ++metric_setup_start_service_total; |
- HRESULT service_hr = SetupUpdate3Service::StartService(); |
- if (SUCCEEDED(service_hr)) { |
- metric_setup_start_service_ms.AddSample(metrics_timer.GetElapsedMs()); |
- OPT_LOG(L1, (_T("[Service started]"))); |
- ++metric_setup_start_service_succeeded; |
- return S_OK; |
- } |
- metric_setup_start_service_failed_ms.AddSample(metrics_timer.GetElapsedMs()); |
- OPT_LOG(LEVEL_ERROR, (_T("[Start service failed][0x%08x]"), service_hr)); |
- metric_setup_start_service_error = service_hr; |
- |
- // TODO(omaha): We should only skip this block when /install /silent fails |
- // and there are no other apps installed. Guarantee this somehow. |
- ++metric_setup_start_task_total; |
- const ULONGLONG start_task_start_ms = metrics_timer.GetElapsedMs(); |
- HRESULT task_hr = scheduled_task_utils::StartGoopdateTaskCore(true); |
- if (SUCCEEDED(task_hr)) { |
- const ULONGLONG start_task_end_ms = metrics_timer.GetElapsedMs(); |
- ASSERT1(start_task_end_ms >= start_task_start_ms); |
- metric_setup_start_task_ms.AddSample( |
- start_task_end_ms - start_task_start_ms); |
- OPT_LOG(L1, (_T("[run scheduled task succeeded]"))); |
- ++metric_setup_start_task_succeeded; |
- return S_OK; |
- } |
- OPT_LOG(LE, (_T("[Start scheduled task failed][0x%08x]"), task_hr)); |
- metric_setup_start_task_error = task_hr; |
- |
- return service_hr; |
-} |
- |
-// Start the user core process directly. |
-// Do not call this method until the shutdown event has been released or the |
-// process may immediately exit. |
-HRESULT Setup::StartUserCoreProcess(const CString& core_cmd_line) const { |
- HRESULT hr = System::StartCommandLine(core_cmd_line); |
- if (FAILED(hr)) { |
- SETUP_LOG(LE, (_T("[Could not start Google Update Core][0x%08x]"), hr)); |
- return hr; |
- } |
- |
- return S_OK; |
-} |
- |
-HRESULT Setup::FindCoreProcesses(Pids* found_core_pids) const { |
- SETUP_LOG(L3, (_T("[Setup::FindCoreProcesses]"))); |
- ASSERT1(found_core_pids); |
- |
- CString user_sid; |
- HRESULT hr = GetAppropriateSid(&user_sid); |
- if (FAILED(hr)) { |
- return hr; |
- } |
- |
- std::vector<CString> command_lines; |
- CString switch_to_include; |
- switch_to_include.Format(_T("/%s"), kCmdLineCore); |
- command_lines.push_back(switch_to_include); |
- |
- DWORD flags = INCLUDE_ONLY_PROCESS_OWNED_BY_USER | |
- EXCLUDE_CURRENT_PROCESS | |
- INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING; |
- hr = Process::FindProcesses(flags, |
- kOmahaShellFileName, |
- true, |
- user_sid, |
- command_lines, |
- found_core_pids); |
- if (FAILED(hr)) { |
- SETUP_LOG(LE, (_T("[FindProcesses failed][0x%08x]"), hr)); |
- return hr; |
- } |
- |
- // Remove PIDs where the command line is not actually the "/c" switch and is |
- // some other command line, such as "/cr". |
- const Pids::iterator new_end = std::remove_if( |
- found_core_pids->begin(), |
- found_core_pids->end(), |
- std::not1(std::ptr_fun(IsCoreProcess))); |
- if (new_end != found_core_pids->end()) { |
- found_core_pids->erase(new_end, found_core_pids->end()); |
- } |
- |
- SETUP_LOG(L2, (_T("[Core processes found][%u]"), found_core_pids->size())); |
- return S_OK; |
-} |
- |
-// Does not try to terminate legacy processes. |
-// Waits up to 500 ms for the terminated core processes to exit. |
-HRESULT Setup::TerminateCoreProcesses() const { |
- SETUP_LOG(L2, (_T("[Setup::TerminateCoreProcesses]"))); |
- Pids found_core_pids; |
- HRESULT hr = FindCoreProcesses(&found_core_pids); |
- if (FAILED(hr)) { |
- SETUP_LOG(LE, (_T("[FindCoreProcesses failed][0x%08x]"), hr)); |
- return hr; |
- } |
- |
- std::vector<HANDLE> terminated_processes; |
- for (size_t i = 0; i < found_core_pids.size(); ++i) { |
- uint32 pid = found_core_pids[i]; |
- |
- SETUP_LOG(L2, (_T("[Terminating core process][%u]"), pid)); |
- |
- HANDLE process(::OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE, pid)); |
- if (!process) { |
- SETUP_LOG(LW, (_T("[::OpenProcess failed][%u][%u]"), |
- pid, ::GetLastError())); |
- continue; |
- } |
- terminated_processes.push_back(process); |
- |
- if (!::TerminateProcess(process, static_cast<uint32>(-2))) { |
- SETUP_LOG(LW, (_T("[::TerminateProcess failed][%u][%u]"), |
- pid, ::GetLastError())); |
- } |
- } |
- |
- if (terminated_processes.empty()) { |
- return S_OK; |
- } |
- // Do not return until the handles have been closed. |
- |
- const int kCoreTerminateWaitMs = 500; |
- DWORD res = ::WaitForMultipleObjects(terminated_processes.size(), |
- &terminated_processes.front(), |
- true, // wait for all |
- kCoreTerminateWaitMs); |
- SETUP_LOG(L2, (_T("[::WaitForMultipleObjects returned]"))); |
- ASSERT1(WAIT_OBJECT_0 == res || WAIT_TIMEOUT == res); |
- if (WAIT_FAILED == res) { |
- const DWORD error = ::GetLastError(); |
- SETUP_LOG(LE, (_T("[::WaitForMultipleObjects failed][%u]"), error)); |
- hr = HRESULT_FROM_WIN32(error); |
- } else { |
- hr = HRESULT_FROM_WIN32(res); |
- } |
- |
- for (size_t i = 0; i < terminated_processes.size(); ++i) { |
- VERIFY1(::CloseHandle(terminated_processes[i])); |
- } |
- |
- return hr; |
-} |
- |
-// Tries to start the core using existing launch methods if present. |
-// Uses the service or the scheduled task for machine, and the Run key value for |
-// user. |
-HRESULT Setup::StartCore() const { |
- SETUP_LOG(L2, (_T("[Attempting to start core]"))); |
- |
- if (is_machine_) { |
- HRESULT hr = StartMachineCoreProcess(); |
- if (FAILED(hr)) { |
- SETUP_LOG(LW, (_T("[StartMachineCoreProcess failed][0x%08x]"), hr)); |
- return hr; |
- } |
- |
- return hr; |
- } |
- |
- CString installed_run_cmd_line; |
- HRESULT hr = RegKey::GetValue(USER_KEY REGSTR_PATH_RUN, kRunValueName, |
- &installed_run_cmd_line); |
- if (FAILED(hr)) { |
- SETUP_LOG(LW, (_T("[Failed to get Run val][%s][0x%x]"), kRunValueName, hr)); |
- return hr; |
- } |
- |
- hr = StartUserCoreProcess(installed_run_cmd_line); |
- if (FAILED(hr)) { |
- SETUP_LOG(LW, (_T("[StartUserCoreProcess failed][%s][0x%x]"), |
- installed_run_cmd_line, hr)); |
- return hr; |
- } |
- |
- return S_OK; |
-} |
- |
-// Returns Local System's SID for machine installs and the user's SID otherwise. |
-HRESULT Setup::GetAppropriateSid(CString* sid) const { |
- ASSERT1(sid); |
- if (is_machine_) { |
- *sid = kLocalSystemSid; |
- } else { |
- HRESULT hr = user_info::GetProcessUser(NULL, NULL, sid); |
- if (FAILED(hr)) { |
- SETUP_LOG(LEVEL_ERROR, (_T("[GetProcessUser failed][0x%08x]"), hr)); |
- return hr; |
- } |
- } |
- |
- return S_OK; |
-} |
- |
-bool Setup::InitSetupLock(bool is_machine, GLock* setup_lock) { |
- ASSERT1(setup_lock); |
- NamedObjectAttributes setup_lock_attr; |
- GetNamedObjectAttributes(kSetupMutex, is_machine, &setup_lock_attr); |
- return setup_lock->InitializeWithSecAttr(setup_lock_attr.name, |
- &setup_lock_attr.sa); |
-} |
- |
-// Assumes that the Setup Lock is held. |
-// This method is based on the assumption that if another install, which could |
-// be modifying the number of clients, is in progress, that it either: |
-// (a) Has the Setup Lock, which is not possible because this process has it. |
-// (b) Has started an install worker. |
-// TODO(omaha3): This is flawed: http://b/2764048. |
-bool Setup::CanUninstallGoogleUpdate() const { |
- CORE_LOG(L2, (_T("[Setup::CanUninstallGoogleUpdate]"))); |
- if (goopdate_utils::IsAppInstallWorkerRunning(is_machine_)) { |
- CORE_LOG(L2, (_T("[Found install workers. Not uninstalling]"))); |
- return false; |
- } |
- if (ShouldDelayUninstall()) { |
- // If the DelayUninstall flag is set, that implies that someone has |
- // installed us with the runtime=true flag, expecting that they can |
- // use our API later from another process. If 24 hours have passed |
- // since that initial Omaha install, we clear the flag but still |
- // return false for this check. (That way, if a machine has been |
- // suspended in mid-install, they still have a grace period until |
- // the next /ua to get something installed.) |
- CORE_LOG(L3, (_T("[DelayUninstall is set. Not uninstalling.]"))); |
- if (ConfigManager::Instance()->Is24HoursSinceInstall(is_machine_)) { |
- CORE_LOG(L4, (_T("[24 hours elapsed; clearing DelayUninstall.]"))); |
- SetDelayUninstall(false); |
- } |
- return false; |
- } |
- size_t num_clients(0); |
- if (SUCCEEDED(app_registry_utils::GetNumClients(is_machine_, &num_clients)) && |
- num_clients >= 2) { |
- CORE_LOG(L3, (_T("[Found products. Not uninstalling]"))); |
- return false; |
- } |
- |
- return true; |
-} |
- |
-bool Setup::ShouldDelayUninstall() const { |
- const TCHAR* key = ConfigManager::Instance()->registry_update(is_machine_); |
- if (!RegKey::HasValue(key, kRegValueDelayOmahaUninstall)) { |
- return false; |
- } |
- DWORD should_delay = 0; |
- if (FAILED(RegKey::GetValue(key, |
- kRegValueDelayOmahaUninstall, |
- &should_delay))) { |
- return false; |
- } |
- return should_delay != 0; |
-} |
- |
-HRESULT Setup::SetDelayUninstall(bool should_delay) const { |
- const TCHAR* key = ConfigManager::Instance()->registry_update(is_machine_); |
- if (should_delay) { |
- return RegKey::SetValue(key, kRegValueDelayOmahaUninstall, 1UL); |
- } else { |
- return RegKey::DeleteValue(key, kRegValueDelayOmahaUninstall); |
- } |
-} |
- |
-HRESULT Setup::SendUninstallPing() { |
- CORE_LOG(L3, (_T("[SendUninstallPing]"))); |
- |
- const bool is_eula_accepted = |
- app_registry_utils::IsAppEulaAccepted(is_machine_, |
- kGoogleUpdateAppId, |
- false); |
- |
- if (!is_eula_accepted) { |
- CORE_LOG(LE, (_T("[SendUninstallPing - eula not accepted]"))); |
- return E_FAIL; |
- } |
- |
- PingEventPtr uninstall_ping_event( |
- new PingEvent(PingEvent::EVENT_UNINSTALL, |
- PingEvent::EVENT_RESULT_SUCCESS, |
- 0, |
- 0)); |
- |
- // Generate a session ID for uninstall pings. NOTE: The assumption here is |
- // that if /uninstall was launched by /ua, we have no updates to check for, |
- // so we assume that /ua won't use its own session ID. If /ua does any |
- // network activity on a no-clients case, we will have to start passing the |
- // session ID from /ua to /uninstall in the future. |
- CString session_id; |
- VERIFY1(SUCCEEDED(GetGuid(&session_id))); |
- |
- // Send uninstall ping for uninstalled apps. |
- HRESULT hr = S_OK; |
- std::vector<CString> uninstalled_apps; |
- if (SUCCEEDED(app_registry_utils::GetUninstalledApps(is_machine_, |
- &uninstalled_apps))) { |
- Ping apps_uninstall_ping(is_machine_, session_id, kInstallSource_Uninstall); |
- apps_uninstall_ping.LoadAppDataFromRegistry(uninstalled_apps); |
- apps_uninstall_ping.BuildAppsPing(uninstall_ping_event); |
- |
- hr = apps_uninstall_ping.Send(false); |
- if (FAILED(hr)) { |
- CORE_LOG(LE, (_T("[SendUninstallPing: failed to send app uninstall ping]") |
- _T("[0x%08x]"), hr)); |
- } |
- } |
- |
- // Send uninstall ping for Omaha. |
- const CString current_omaha_version(GetVersionString()); |
- const CString next_omaha_version; // Empty, in the uninstall case. |
- Ping omaha_uninstall_ping(is_machine_, session_id, kInstallSource_Uninstall); |
- omaha_uninstall_ping.LoadOmahaDataFromRegistry(); |
- omaha_uninstall_ping.BuildOmahaPing(current_omaha_version, |
- next_omaha_version, |
- uninstall_ping_event); |
- hr = omaha_uninstall_ping.Send(false); |
- if (SUCCEEDED(hr)) { |
- // Clears the registry after ping is sent successfully. |
- std::vector<CString> uninstalled_apps; |
- app_registry_utils::GetUninstalledApps(is_machine_, &uninstalled_apps); |
- app_registry_utils::RemoveClientStateForApps(is_machine_, uninstalled_apps); |
- } else { |
- CORE_LOG(LE, (_T("[SendUninstallPing: failed to send Omaha uninstall ping]") |
- _T("[0x%08x]"), hr)); |
- } |
- |
- return hr; |
-} |
- |
- |
-} // namespace omaha |