| Index: client/install.cc
|
| diff --git a/client/install.cc b/client/install.cc
|
| deleted file mode 100644
|
| index 7e27ce3838c88f896955cad3940ceb68a3e2e903..0000000000000000000000000000000000000000
|
| --- a/client/install.cc
|
| +++ /dev/null
|
| @@ -1,830 +0,0 @@
|
| -// Copyright 2007-2010 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.
|
| -// ========================================================================
|
| -
|
| -#include "omaha/client/install.h"
|
| -#include "omaha/client/install_internal.h"
|
| -#include "omaha/base/app_util.h"
|
| -#include "omaha/base/const_object_names.h"
|
| -#include "omaha/base/debug.h"
|
| -#include "omaha/base/error.h"
|
| -#include "omaha/base/file.h"
|
| -#include "omaha/base/logging.h"
|
| -#include "omaha/base/omaha_version.h"
|
| -#include "omaha/base/path.h"
|
| -#include "omaha/base/process.h"
|
| -#include "omaha/base/reg_key.h"
|
| -#include "omaha/base/scoped_any.h"
|
| -#include "omaha/base/string.h"
|
| -#include "omaha/base/time.h"
|
| -#include "omaha/base/utils.h"
|
| -#include "omaha/base/vistautil.h"
|
| -#include "omaha/client/client_utils.h"
|
| -#include "omaha/client/install_self.h"
|
| -#include "omaha/client/resource.h"
|
| -#include "omaha/common/app_registry_utils.h"
|
| -#include "omaha/common/command_line_builder.h"
|
| -#include "omaha/common/config_manager.h"
|
| -#include "omaha/common/const_cmd_line.h"
|
| -#include "omaha/common/const_goopdate.h"
|
| -#include "omaha/common/goopdate_utils.h"
|
| -#include "omaha/common/oem_install_utils.h"
|
| -#include "omaha/common/ping.h"
|
| -#include "omaha/setup/setup_metrics.h"
|
| -#include "omaha/ui/splash_screen.h"
|
| -
|
| -namespace omaha {
|
| -
|
| -namespace {
|
| -
|
| -// Returns whether elevation is required.
|
| -bool IsElevationRequired(bool is_machine) {
|
| - return is_machine && !vista_util::IsUserAdmin();
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -namespace internal {
|
| -
|
| -// TODO(omaha3): Make this elevate the metainstaller instead of
|
| -// GoogleUpdate.exe so the files are extracted to a secure location.
|
| -// May need to add all languages to the metainstaller's version resources so
|
| -// the user sees the localized string in the UAC.
|
| -// TODO(omaha3): We will need to save the metainstaller for OneClick
|
| -// cross-installs. We may need to change the metainstaller behavior to not
|
| -// use the tag if any command line args are provided. This will allow us to
|
| -// reuse a tagged metainstaller that we saved for other purposes, such as
|
| -// OneClick cross-installs.
|
| -// TODO(omaha3): The "metainstaller" may also be some type of wrapper around
|
| -// a differential update. We'll address that later. This wrapper should not
|
| -// need localized resource strings since it always runs silently.
|
| -HRESULT DoElevation(bool is_interactive,
|
| - bool is_install_elevated_instance,
|
| - const CString& cmd_line,
|
| - DWORD* exit_code) {
|
| - ASSERT1(exit_code);
|
| - ASSERT1(IsElevationRequired(true));
|
| -
|
| - if (!is_interactive) {
|
| - return GOOPDATE_E_SILENT_INSTALL_NEEDS_ELEVATION;
|
| - }
|
| -
|
| - if (is_install_elevated_instance) {
|
| - // This can happen if UAC is disabled. See http://b/1187784.
|
| - CORE_LOG(LE, (_T("[Install elevated process requires elevation]")));
|
| - return GOOPDATE_E_INSTALL_ELEVATED_PROCESS_NEEDS_ELEVATION;
|
| - }
|
| -
|
| - HRESULT hr = ElevateAndWait(cmd_line, exit_code);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[ElevateAndWait failed][%s][0x%08x]"), cmd_line, hr));
|
| - }
|
| -
|
| - return hr;
|
| -}
|
| -
|
| -// Assumes it is running with the necessary privileges.
|
| -HRESULT DoInstall(bool is_machine,
|
| - bool is_app_install,
|
| - bool is_eula_required,
|
| - bool is_oem_install,
|
| - const CString& current_version,
|
| - const CommandLineArgs& args,
|
| - const CString& session_id,
|
| - SplashScreen* splash_screen,
|
| - int* extra_code1,
|
| - bool* has_setup_succeeded,
|
| - bool* has_launched_handoff,
|
| - bool* has_ui_been_displayed) {
|
| - ASSERT1(!IsElevationRequired(is_machine));
|
| - ASSERT1(extra_code1);
|
| - ASSERT1(splash_screen);
|
| - ASSERT1(has_setup_succeeded);
|
| - ASSERT1(has_launched_handoff);
|
| - ASSERT1(has_ui_been_displayed);
|
| -
|
| - *extra_code1 = 0;
|
| -
|
| - // TODO(omaha3): We may need to take an "installing apps" lock here if
|
| - // is_app_install. There was code in Omaha 2 that relied on the fact that
|
| - // the Setup lock was held while the install worker was launched, even in
|
| - // handoff scenarios. This allowed checking for the Setup lock and running
|
| - // install workers to be sufficient to ensure that the number of Clients keys
|
| - // was stable. Note that Omaha 2 also held the shutdown event while the worker
|
| - // was launched.
|
| - // TODO(omaha3): Consider taking the Setup lock in this file (via a public
|
| - // method in Setup) and releasing it. This would allow us to address the above
|
| - // issue and maybe the EULA not accepted issue.
|
| - // TODO(omaha3): We may also want to call ShouldInstall after a Lock(0) to
|
| - // determine whether we even need to display the splash screen or we can skip
|
| - // it and Setup altogether and just launch the /handoff process.
|
| -
|
| - HRESULT hr = install_self::InstallSelf(is_machine,
|
| - is_eula_required,
|
| - is_oem_install,
|
| - current_version,
|
| - args.install_source,
|
| - args.extra,
|
| - session_id,
|
| - extra_code1);
|
| - *has_setup_succeeded = SUCCEEDED(hr);
|
| - if (FAILED(hr)) {
|
| - OPT_LOG(LE, (_T("[InstallSelf failed][0x%08x]"), hr));
|
| - return hr;
|
| - }
|
| -
|
| - if (!is_app_install) {
|
| - return S_OK;
|
| - }
|
| -
|
| - hr = InstallApplications(is_machine,
|
| - is_eula_required,
|
| - args,
|
| - session_id,
|
| - splash_screen,
|
| - has_ui_been_displayed,
|
| - has_launched_handoff);
|
| - if (FAILED(hr)) {
|
| - OPT_LOG(LE, (_T("[InstallApplications failed][0x%08x]"), hr));
|
| - return hr;
|
| - }
|
| -
|
| - return S_OK;
|
| -};
|
| -
|
| -HRESULT InstallApplications(bool is_machine,
|
| - bool is_eula_required,
|
| - const CommandLineArgs& args,
|
| - const CString& session_id,
|
| - SplashScreen* splash_screen,
|
| - bool* has_ui_been_displayed,
|
| - bool* has_launched_handoff) {
|
| - ASSERT1(has_ui_been_displayed);
|
| - ASSERT1(has_launched_handoff);
|
| -
|
| - *has_launched_handoff = false;
|
| -
|
| - CString offline_dir;
|
| - const bool is_offline_install = CopyOfflineFiles(is_machine,
|
| - args.extra.apps,
|
| - &offline_dir);
|
| - if (!is_offline_install && oem_install_utils::IsOemInstalling(is_machine)) {
|
| - return GOOPDATE_E_OEM_WITH_ONLINE_INSTALLER;
|
| - }
|
| - if (!is_offline_install && is_eula_required) {
|
| - return GOOPDATE_E_EULA_REQURED_WITH_ONLINE_INSTALLER;
|
| - }
|
| -
|
| - // Start the handoff to install the app.
|
| - scoped_process handoff_process;
|
| - HRESULT hr = LaunchHandoffProcess(is_machine,
|
| - offline_dir,
|
| - args,
|
| - session_id,
|
| - address(handoff_process));
|
| - if (FAILED(hr)) {
|
| - OPT_LOG(LE, (_T("[Failed to launch installed instance][0x%08x]"), hr));
|
| - return hr;
|
| - }
|
| -
|
| - *has_launched_handoff = true;
|
| -
|
| - OPT_LOG(L1, (_T("[Waiting for application install to complete]")));
|
| - uint32 exit_code(0);
|
| - hr = WaitForProcessExit(get(handoff_process),
|
| - splash_screen,
|
| - has_ui_been_displayed,
|
| - &exit_code);
|
| -
|
| - if (FAILED(hr)) {
|
| - OPT_LOG(LE, (_T("[Failed waiting for app install][0x%08x]"), hr));
|
| - return hr;
|
| - }
|
| - if (exit_code) {
|
| - OPT_LOG(LE, (_T("[Handoff exited with error][0x%08x]"), exit_code));
|
| - ASSERT1(FAILED(exit_code));
|
| - return exit_code;
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -// The behavior depends on the OS:
|
| -// 1. OS < Vista : Fail.
|
| -// 2. OS >= Vista : Try to elevate - causes a UAC dialog.
|
| -// We should be here only in case of initial machine installs when the user is
|
| -// not an elevated admin.
|
| -HRESULT ElevateAndWait(const CString& cmd_line, DWORD* exit_code) {
|
| - OPT_LOG(L1, (_T("[Elevating][%s]"), cmd_line));
|
| - ASSERT1(!vista_util::IsUserAdmin());
|
| - ASSERT1(exit_code);
|
| -
|
| - if (!vista_util::IsVistaOrLater()) {
|
| - // TODO(omaha): We could consider to ask for credentials here.
|
| - // This TODO existed in Omaha 1. How would we even do this?
|
| - CORE_LOG(LE, (_T("[Non Admin trying to install admin app]")));
|
| - ++metric_setup_machine_app_non_admin;
|
| - return GOOPDATE_E_NONADMIN_INSTALL_ADMIN_APP;
|
| - }
|
| -
|
| - CString cmd_line_elevated(GetCmdLineTail(cmd_line));
|
| - cmd_line_elevated.AppendFormat(_T(" /%s"), kCmdLineInstallElevated);
|
| -
|
| - HRESULT hr = goopdate_utils::StartElevatedSelfWithArgsAndWait(
|
| - cmd_line_elevated, exit_code);
|
| - if (FAILED(hr)) {
|
| - OPT_LOG(LE, (_T("[Starting elevated GoogleUpdate.exe failed][%s][0x%08x]"),
|
| - cmd_line, hr));
|
| -
|
| - // TODO(omaha3): Report hr somehow. Was reported in extra code in Omaha 2.
|
| - if (vista_util::IsUserNonElevatedAdmin()) {
|
| - return GOOPDATE_E_ELEVATION_FAILED_ADMIN;
|
| - } else {
|
| - return GOOPDATE_E_ELEVATION_FAILED_NON_ADMIN;
|
| - }
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -bool CopyOfflineFiles(bool is_machine,
|
| - const std::vector<CommandLineAppArgs>& apps,
|
| - CString* offline_dir) {
|
| - ASSERT1(offline_dir);
|
| - offline_dir->Empty();
|
| -
|
| - if (apps.empty()) {
|
| - return false;
|
| - }
|
| -
|
| - GUID guid(GUID_NULL);
|
| - VERIFY1(SUCCEEDED(::CoCreateGuid(&guid)));
|
| - CString parent_offline_dir(
|
| - is_machine ?
|
| - ConfigManager::Instance()->GetMachineSecureOfflineStorageDir() :
|
| - ConfigManager::Instance()->GetUserOfflineStorageDir());
|
| - CString unique_offline_dir =
|
| - ConcatenatePath(parent_offline_dir, GuidToString(guid));
|
| - VERIFY1(SUCCEEDED(CreateDir(unique_offline_dir, NULL)));
|
| -
|
| - HRESULT hr = CopyOfflineManifest(unique_offline_dir);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(L3, (_T("[CopyOfflineManifest failed][0x%08x]"), hr));
|
| - return false;
|
| - }
|
| -
|
| - for (size_t i = 0; i < apps.size(); ++i) {
|
| - const GUID& app_id = apps[i].app_guid;
|
| - HRESULT hr = CopyOfflineFilesForApp(GuidToString(app_id),
|
| - unique_offline_dir);
|
| - if (FAILED(hr)) {
|
| - ASSERT(false, (_T("[CopyOfflineFilesForApp failed][0x%08x]"), hr));
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - CORE_LOG(L3, (_T("[CopyOfflineFiles done][%s]"), unique_offline_dir));
|
| - *offline_dir = unique_offline_dir;
|
| - return true;
|
| -}
|
| -
|
| -HRESULT CopyOfflineManifest(const CString& offline_dir) {
|
| - CORE_LOG(L3, (_T("[CopyOfflineManifest][%s]"), offline_dir));
|
| -
|
| - // Copy offline manifest into "<offline_dir>\<kOfflineManifestFileName>".
|
| - CString setup_temp_dir(app_util::GetCurrentModuleDirectory());
|
| - CString source_manifest_path = ConcatenatePath(setup_temp_dir,
|
| - kOfflineManifestFileName);
|
| - if (!File::Exists(source_manifest_path)) {
|
| - return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
| - }
|
| - CString dest_manifest_path = ConcatenatePath(offline_dir,
|
| - kOfflineManifestFileName);
|
| - HRESULT hr = File::Copy(source_manifest_path, dest_manifest_path, true);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[File copy failed][%s][%s][0x%08x]"),
|
| - source_manifest_path, dest_manifest_path, hr));
|
| - return hr;
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT CopyOfflineFilesForApp(const CString& app_id,
|
| - const CString& offline_dir) {
|
| - CORE_LOG(L3, (_T("[CopyOfflineFilesForApp][%s][%s]"),
|
| - app_id, offline_dir));
|
| -
|
| - CString setup_temp_dir(app_util::GetCurrentModuleDirectory());
|
| - CString pattern;
|
| - // Copy the installer files that are named with the pattern "*.<app_id>" to
|
| - // the directory "<offline_dir>\<app_id>".
|
| - pattern.Format(_T("*.%s"), app_id);
|
| - std::vector<CString> files;
|
| - HRESULT hr = FindFiles(setup_temp_dir, pattern, &files);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[FindFiles failed][0x%08x]"), hr));
|
| - return hr;
|
| - }
|
| - if (files.empty()) {
|
| - CORE_LOG(LE, (_T("[FindFiles found no files]")));
|
| - return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
| - }
|
| -
|
| - CString offline_app_dir = ConcatenatePath(offline_dir, app_id);
|
| - if (File::IsDirectory(offline_app_dir)) {
|
| - VERIFY1(SUCCEEDED(DeleteDirectoryFiles(offline_app_dir)));
|
| - } else {
|
| - hr = CreateDir(offline_app_dir, NULL);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[CreateDir failed][%s]"), offline_app_dir));
|
| - return hr;
|
| - }
|
| - }
|
| -
|
| - for (size_t i = 0; i < files.size(); ++i) {
|
| - const CString& file_name = files[i];
|
| - ASSERT1(file_name.GetLength() > app_id.GetLength());
|
| -
|
| - CPath renamed_file_name(file_name);
|
| - renamed_file_name.RemoveExtension();
|
| - ASSERT1(file_name.Left(file_name.GetLength() - app_id.GetLength() - 1) ==
|
| - static_cast<const CString&>(renamed_file_name));
|
| - CString new_file_path = ConcatenatePath(offline_app_dir, renamed_file_name);
|
| - CORE_LOG(L4, (_T("[new_file_path][%s]"), new_file_path));
|
| -
|
| - CString source_file_path = ConcatenatePath(setup_temp_dir, file_name);
|
| - hr = File::Copy(source_file_path, new_file_path, true);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[Copy failed][%s][%s]"),
|
| - source_file_path, new_file_path));
|
| - return hr;
|
| - }
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -
|
| -// TODO(omaha3): This implementation requires updating this code whenever a
|
| -// new option is added. We could do a string replace of "/install" with
|
| -// "/handoff", but we'd need to remove things such as /oem. Alternatively,
|
| -// allow a CommandLineBuilder to be populated with existing args. Doing
|
| -// replacement would allow us to only pass one copy of the command line to
|
| -// Install() instead of passing both the string and struct. Be sure to handle
|
| -// /appargs when making any changes.
|
| -// TODO(omaha): Extract the command line building and unit test it.
|
| -HRESULT LaunchHandoffProcess(bool is_machine,
|
| - const CString& offline_dir,
|
| - const CommandLineArgs& install_args,
|
| - const CString& session_id,
|
| - HANDLE* process) { // process can be NULL.
|
| - CORE_LOG(L2, (_T("[LaunchHandoffProcess]")));
|
| -
|
| - // The install source has been either specified or set to a default value by
|
| - // the time the execution flow reaches this function. The install source is
|
| - // propagated to the handoff process except in certain offline install cases,
|
| - // such as a tagged offline installer or an offline installer that did not
|
| - // have an install source.
|
| - //
|
| - // TODO(omaha): refactor so that the handling of the install source is
|
| - // encapsulated in just one module.
|
| - ASSERT1(!install_args.install_source.IsEmpty());
|
| - ASSERT1(!install_args.extra_args_str.IsEmpty());
|
| -
|
| - CommandLineBuilder builder(COMMANDLINE_MODE_HANDOFF_INSTALL);
|
| -
|
| - builder.set_is_silent_set(install_args.is_silent_set);
|
| - builder.set_is_eula_required_set(install_args.is_eula_required_set);
|
| - builder.set_extra_args(install_args.extra_args_str);
|
| - builder.set_app_args(install_args.app_args_str);
|
| - builder.set_install_source(install_args.install_source);
|
| - builder.set_session_id(session_id);
|
| -
|
| - if (!offline_dir.IsEmpty()) {
|
| - builder.SetOfflineDir(offline_dir);
|
| - const CString& install_source(install_args.install_source);
|
| - if (install_source == kCmdLineInstallSource_InstallDefault ||
|
| - install_source == kCmdLineInstallSource_TaggedMetainstaller) {
|
| - builder.set_install_source(kCmdLineInstallSource_Offline);
|
| - }
|
| - }
|
| -
|
| - CString cmd_line = builder.GetCommandLineArgs();
|
| -
|
| - HRESULT hr = goopdate_utils::StartGoogleUpdateWithArgs(is_machine,
|
| - cmd_line,
|
| - process);
|
| - if (FAILED(hr)) {
|
| - OPT_LOG(LE, (_T("[Google Update hand off failed][%s][0x%08x]"),
|
| - cmd_line, hr));
|
| - // TODO(omaha3): Report hr somehow. Was reported in extra code in Omaha 2.
|
| - return GOOPDATE_E_HANDOFF_FAILED;
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT WaitForProcessExit(HANDLE process,
|
| - SplashScreen* splash_screen,
|
| - bool* has_ui_been_displayed,
|
| - uint32* exit_code) {
|
| - CORE_LOG(L3, (_T("[WaitForProcessExit]")));
|
| - ASSERT1(process);
|
| - ASSERT1(exit_code);
|
| - ASSERT1(has_ui_been_displayed);
|
| - *exit_code = 0;
|
| -
|
| - int res = ::WaitForInputIdle(process, INFINITE);
|
| - if (res == 0) {
|
| - *has_ui_been_displayed = true;
|
| - }
|
| - if (splash_screen) {
|
| - splash_screen->Dismiss();
|
| - }
|
| -
|
| - res = ::WaitForSingleObject(process, INFINITE);
|
| - ASSERT1(WAIT_OBJECT_0 == res);
|
| - if (WAIT_FAILED == res) {
|
| - DWORD error = ::GetLastError();
|
| - CORE_LOG(LE, (_T("[::WaitForMultipleObjects failed][%u]"), error));
|
| - return HRESULT_FROM_WIN32(error);
|
| - }
|
| -
|
| - // Get the exit code.
|
| - DWORD local_exit_code = 0;
|
| - if (::GetExitCodeProcess(process, &local_exit_code)) {
|
| - CORE_LOG(L2, (_T("[process exited][PID %u][exit code 0x%08x]"),
|
| - Process::GetProcessIdFromHandle(process), local_exit_code));
|
| - *exit_code = local_exit_code;
|
| - } else {
|
| - DWORD error = ::GetLastError();
|
| - CORE_LOG(LE, (_T("[::GetExitCodeProcess failed][%u]"), error));
|
| - return HRESULT_FROM_WIN32(error);
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -// TODO(omaha): needs to handle errors when loading the strings.
|
| -CString GetErrorText(HRESULT error, const CString& bundle_name) {
|
| - ASSERT1(!bundle_name.IsEmpty());
|
| -
|
| - CString error_text;
|
| -
|
| - switch (error) {
|
| - case GOOPDATE_E_INSTALL_ELEVATED_PROCESS_NEEDS_ELEVATION:
|
| - case GOOPDATE_E_NONADMIN_INSTALL_ADMIN_APP:
|
| - error_text.FormatMessage(IDS_NEED_ADMIN_TO_INSTALL, bundle_name);
|
| - break;
|
| - case GOOPDATE_E_ELEVATION_FAILED_ADMIN:
|
| - case GOOPDATE_E_ELEVATION_FAILED_NON_ADMIN:
|
| - error_text.FormatMessage(IDS_ELEVATION_FAILED, bundle_name);
|
| - break;
|
| - case GOOPDATE_E_FAILED_TO_GET_LOCK:
|
| - case GOOPDATE_E_FAILED_TO_GET_LOCK_MATCHING_INSTALL_PROCESS_RUNNING:
|
| - case GOOPDATE_E_FAILED_TO_GET_LOCK_NONMATCHING_INSTALL_PROCESS_RUNNING:
|
| - case GOOPDATE_E_FAILED_TO_GET_LOCK_UPDATE_PROCESS_RUNNING:
|
| - {
|
| - CString company_name;
|
| - VERIFY1(company_name.LoadString(IDS_FRIENDLY_COMPANY_NAME));
|
| - error_text.FormatMessage(IDS_APPLICATION_INSTALLING_GOOGLE_UPDATE,
|
| - company_name,
|
| - bundle_name);
|
| - }
|
| - break;
|
| - case GOOPDATE_E_INSTANCES_RUNNING:
|
| - {
|
| - CString company_name;
|
| - VERIFY1(company_name.LoadString(IDS_FRIENDLY_COMPANY_NAME));
|
| - error_text.FormatMessage(IDS_INSTANCES_RUNNING_AFTER_SHUTDOWN,
|
| - company_name,
|
| - bundle_name);
|
| - }
|
| - break;
|
| - case GOOPDATE_E_RUNNING_INFERIOR_MSXML:
|
| - error_text.FormatMessage(IDS_WINDOWS_IS_NOT_UP_TO_DATE, bundle_name);
|
| - break;
|
| - case GOOPDATE_E_HANDOFF_FAILED:
|
| - error_text.FormatMessage(IDS_HANDOFF_FAILED, bundle_name);
|
| - break;
|
| - default:
|
| - {
|
| - CString product_name;
|
| - VERIFY1(product_name.LoadString(IDS_PRODUCT_DISPLAY_NAME));
|
| - error_text.FormatMessage(IDS_SETUP_FAILED, product_name, error);
|
| - }
|
| - break;
|
| - }
|
| -
|
| - return error_text;
|
| -}
|
| -
|
| -// Displays an error in the Google Update UI and sends a ping if allowed.
|
| -void HandleInstallError(HRESULT error,
|
| - int extra_code1,
|
| - const CString& session_id,
|
| - bool is_machine,
|
| - bool is_interactive,
|
| - bool is_eula_required,
|
| - bool is_oem_install,
|
| - const CString& current_version,
|
| - const CString& install_source,
|
| - const CommandLineExtraArgs& extra_args,
|
| - bool has_setup_succeeded,
|
| - bool has_launched_handoff,
|
| - bool* has_ui_been_displayed) {
|
| - ASSERT1(FAILED(error));
|
| - ASSERT1(has_ui_been_displayed);
|
| -
|
| - const CString& bundle_name(extra_args.bundle_name);
|
| - const CString error_text(GetErrorText(error, bundle_name));
|
| -
|
| - OPT_LOG(LE, (_T("[Failed to install][0x%08x][%s]"), error, error_text));
|
| -
|
| - if (is_interactive && !*has_ui_been_displayed) {
|
| - CString primary_app_id;
|
| - if (!extra_args.apps.empty()) {
|
| - primary_app_id = GuidToString(extra_args.apps[0].app_guid);
|
| - }
|
| - *has_ui_been_displayed = client_utils::DisplayError(
|
| - is_machine,
|
| - bundle_name,
|
| - error,
|
| - extra_code1,
|
| - error_text,
|
| - primary_app_id,
|
| - extra_args.language,
|
| - extra_args.installation_id,
|
| - extra_args.brand_code);
|
| - }
|
| -
|
| - // Send an EVENT_INSTALL_COMPLETE ping for Omaha and wait for the ping to be
|
| - // sent. This ping may cause a firewall prompt since the process sending the
|
| - // ping may run from a temporary directory.
|
| - //
|
| - // An EVENT_INSTALL_COMPLETE ping for the apps is also sent in the case the
|
| - /// handoff process did not launch successfully, otherwise, the handoff
|
| - // process is responsible for sending this ping.
|
| - //
|
| - // Setup can fail before setting either eula or oem states in the
|
| - // registry. Therefore, ConfigManager::CanUseNetwork can't be called yet.
|
| - if (is_eula_required || is_oem_install) {
|
| - return;
|
| - }
|
| - Ping ping(is_machine, session_id, install_source);
|
| - ping.LoadAppDataFromExtraArgs(extra_args);
|
| - if (!has_setup_succeeded) {
|
| - PingEventPtr setup_install_complete_ping_event(
|
| - new PingEvent(PingEvent::EVENT_INSTALL_COMPLETE, // Type 2.
|
| - PingEvent::EVENT_RESULT_ERROR,
|
| - error,
|
| - extra_code1));
|
| - const CString next_version(GetVersionString());
|
| - ping.BuildOmahaPing(current_version,
|
| - next_version,
|
| - setup_install_complete_ping_event);
|
| - }
|
| - if (!has_launched_handoff) {
|
| - PingEventPtr install_complete_ping_event(
|
| - new PingEvent(PingEvent::EVENT_INSTALL_COMPLETE, // Type 2.
|
| - PingEvent::EVENT_RESULT_ERROR,
|
| - error,
|
| - extra_code1));
|
| - ping.BuildAppsPing(install_complete_ping_event);
|
| - }
|
| - HRESULT hr = ping.Send(false);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LW, (_T("[Ping::Send failed][0x%x]"), hr));
|
| - }
|
| -}
|
| -
|
| -} // namespace internal
|
| -
|
| -// This function handles command-line installs. This is the only function that
|
| -// can install Omaha in non-update cases. If elevation is required, the function
|
| -// elevates an instance of Omaha and waits for that instance to exit before
|
| -// returning. If needed, the function starts a handoff process to install the
|
| -// applications and waits for the handoff process to exit.
|
| -//
|
| -// 'install_cmd_line' parameter is the command line for the current instance and
|
| -// used to elevate if necessary.
|
| -// 'args' parameter is the parsed args corresponding to install_cmd_line and it
|
| -// is used to build the handoff command line if necessary.
|
| -HRESULT Install(bool is_interactive,
|
| - bool is_app_install,
|
| - bool is_eula_required,
|
| - bool is_oem_install,
|
| - bool is_install_elevated_instance,
|
| - const CString& install_cmd_line,
|
| - const CommandLineArgs& args,
|
| - bool* is_machine,
|
| - bool* has_ui_been_displayed) {
|
| - ASSERT1(!install_cmd_line.IsEmpty());
|
| - ASSERT1(is_machine);
|
| - ASSERT1(has_ui_been_displayed);
|
| -
|
| - CORE_LOG(L2, (_T("[Install][%d][%d][%s]"),
|
| - *is_machine, is_interactive, install_cmd_line));
|
| - ++metric_setup_install_total;
|
| - if (is_install_elevated_instance) {
|
| - ++metric_setup_uac_succeeded;
|
| - }
|
| -
|
| - if (!*is_machine &&
|
| - vista_util::IsVistaOrLater() &&
|
| - vista_util::IsUserAdmin()) {
|
| - ++metric_setup_user_app_admin;
|
| - }
|
| -
|
| - // Allocate a session ID to connect all update checks and pings involved
|
| - // with this run of Omaha. This will need to be passed along to any child
|
| - // processes we create.
|
| - CString session_id;
|
| - VERIFY1(SUCCEEDED(GetGuid(&session_id)));
|
| -
|
| - // 'current_version' corresponds to the value of 'pv' read from
|
| - // registry. This value is either empty, in the case of a new install or
|
| - // the version of the installed Omaha before the setup code ran.
|
| - CString current_version;
|
| - app_registry_utils::GetAppVersion(*is_machine,
|
| - kGoogleUpdateAppId,
|
| - ¤t_version);
|
| -
|
| - bool has_setup_succeeded = false;
|
| - bool has_launched_handoff = false;
|
| -
|
| - if (IsElevationRequired(*is_machine)) {
|
| - ASSERT1(!ConfigManager::Instance()->IsWindowsInstalling());
|
| -
|
| - DWORD exit_code = 0;
|
| - HRESULT hr = internal::DoElevation(is_interactive,
|
| - is_install_elevated_instance,
|
| - install_cmd_line,
|
| - &exit_code);
|
| -
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[DoElevation failed][%s][0x%08x]"),
|
| - install_cmd_line, hr));
|
| -
|
| - bool attempt_per_user_install = false;
|
| - if (is_interactive &&
|
| - args.extra.apps[0].needs_admin == NEEDS_ADMIN_PREFERS) {
|
| - if (hr == GOOPDATE_E_NONADMIN_INSTALL_ADMIN_APP ||
|
| - hr == GOOPDATE_E_INSTALL_ELEVATED_PROCESS_NEEDS_ELEVATION) {
|
| - attempt_per_user_install = true;
|
| - } else {
|
| - *has_ui_been_displayed = client_utils::DisplayContinueAsNonAdmin(
|
| - args.extra.bundle_name, &attempt_per_user_install);
|
| - }
|
| - }
|
| -
|
| - if (attempt_per_user_install) {
|
| - *is_machine = false;
|
| - CString no_admin_cmd_line(String_ReplaceIgnoreCase(install_cmd_line,
|
| - kNeedsAdminPrefers,
|
| - kNeedsAdminNo));
|
| - CommandLineArgs no_admin_args = args;
|
| - no_admin_args.extra.apps[0].needs_admin = NEEDS_ADMIN_NO;
|
| - no_admin_args.extra_args_str = String_ReplaceIgnoreCase(
|
| - no_admin_args.extra_args_str, kNeedsAdminPrefers, kNeedsAdminNo);
|
| -
|
| - return Install(is_interactive,
|
| - is_app_install,
|
| - is_eula_required,
|
| - is_oem_install,
|
| - is_install_elevated_instance,
|
| - no_admin_cmd_line,
|
| - no_admin_args,
|
| - is_machine,
|
| - has_ui_been_displayed);
|
| - }
|
| -
|
| - internal::HandleInstallError(hr,
|
| - 0,
|
| - session_id,
|
| - *is_machine,
|
| - is_interactive,
|
| - is_eula_required,
|
| - is_oem_install,
|
| - current_version,
|
| - args.install_source,
|
| - args.extra,
|
| - has_setup_succeeded,
|
| - has_launched_handoff,
|
| - has_ui_been_displayed);
|
| - return hr;
|
| - }
|
| -
|
| - // TODO(omaha): waiting for input idle of an elevated process fails if the
|
| - // caller is not elevated. The code assumes that the elevated process
|
| - // displays UI if the elevation succeeded. It is possible that the elevated
|
| - // process ran but had terminated before displaying UI. This is an uncommon
|
| - // case and for simplicity, it is not handled here.
|
| - *has_ui_been_displayed = true;
|
| -
|
| - return exit_code;
|
| - }
|
| -
|
| - SplashScreen splash_screen(args.extra.bundle_name);
|
| - if (is_interactive) {
|
| - splash_screen.Show();
|
| - }
|
| -
|
| - int extra_code1 = 0;
|
| - HRESULT hr = internal::DoInstall(*is_machine,
|
| - is_app_install,
|
| - is_eula_required,
|
| - is_oem_install,
|
| - current_version,
|
| - args,
|
| - session_id,
|
| - &splash_screen,
|
| - &extra_code1,
|
| - &has_setup_succeeded,
|
| - &has_launched_handoff,
|
| - has_ui_been_displayed);
|
| - if (is_interactive) {
|
| - splash_screen.Dismiss();
|
| - }
|
| -
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[DoInstall failed][0x%08x]"), hr));
|
| - internal::HandleInstallError(hr,
|
| - extra_code1,
|
| - session_id,
|
| - *is_machine,
|
| - is_interactive,
|
| - is_eula_required,
|
| - is_oem_install,
|
| - current_version,
|
| - args.install_source,
|
| - args.extra,
|
| - has_setup_succeeded,
|
| - has_launched_handoff,
|
| - has_ui_been_displayed);
|
| - return hr;
|
| - }
|
| -
|
| - if (!is_app_install) {
|
| - // TODO(omaha): Display UI if we want to support interactive Omaha-only
|
| - // installs.
|
| - ASSERT1(!is_interactive);
|
| - // TODO(omaha3): Figure out a way to send a ping from the installed location
|
| - // for Omaha-only install success.
|
| - }
|
| -
|
| - ++metric_setup_install_succeeded;
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT OemInstall(bool is_interactive,
|
| - bool is_app_install,
|
| - bool is_eula_required,
|
| - bool is_install_elevated_instance,
|
| - const CString& install_cmd_line,
|
| - const CommandLineArgs& args,
|
| - bool* is_machine,
|
| - bool* has_ui_been_displayed) {
|
| - ASSERT1(is_machine);
|
| - ASSERT1(has_ui_been_displayed);
|
| -
|
| - // OEM is handled as a special case, and the state must be correct before
|
| - // calling Install().
|
| - HRESULT hr = oem_install_utils::SetOemInstallState(*is_machine);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[SetOemInstallState failed][0x%x]"), hr));
|
| - return hr;
|
| - }
|
| -
|
| - hr = Install(is_interactive,
|
| - is_app_install,
|
| - is_eula_required,
|
| - true,
|
| - is_install_elevated_instance,
|
| - install_cmd_line,
|
| - args,
|
| - is_machine,
|
| - has_ui_been_displayed);
|
| -
|
| - if (FAILED(hr)) {
|
| - VERIFY1(SUCCEEDED(oem_install_utils::ResetOemInstallState(*is_machine)));
|
| - return hr;
|
| - }
|
| -
|
| - ASSERT1(oem_install_utils::IsOemInstalling(*is_machine));
|
| - return S_OK;
|
| -}
|
| -
|
| -} // namespace omaha
|
|
|