| Index: goopdate/installer_wrapper.cc
|
| diff --git a/goopdate/installer_wrapper.cc b/goopdate/installer_wrapper.cc
|
| deleted file mode 100644
|
| index ce9fea093ee01fc350d197a1b10850323e1bb3f6..0000000000000000000000000000000000000000
|
| --- a/goopdate/installer_wrapper.cc
|
| +++ /dev/null
|
| @@ -1,723 +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/goopdate/installer_wrapper.h"
|
| -#include "omaha/base/const_object_names.h"
|
| -#include "omaha/base/const_utils.h"
|
| -#include "omaha/base/debug.h"
|
| -#include "omaha/base/error.h"
|
| -#include "omaha/base/logging.h"
|
| -#include "omaha/base/path.h"
|
| -#include "omaha/base/process.h"
|
| -#include "omaha/base/safe_format.h"
|
| -#include "omaha/base/scope_guard.h"
|
| -#include "omaha/base/scoped_ptr_address.h"
|
| -#include "omaha/base/string.h"
|
| -#include "omaha/base/synchronized.h"
|
| -#include "omaha/base/system_info.h"
|
| -#include "omaha/base/utils.h"
|
| -#include "omaha/common/const_goopdate.h"
|
| -#include "omaha/common/goopdate_utils.h"
|
| -#include "omaha/goopdate/app_manager.h"
|
| -#include "omaha/goopdate/server_resource.h"
|
| -#include "omaha/goopdate/string_formatter.h"
|
| -#include "omaha/goopdate/worker_metrics.h"
|
| -
|
| -namespace omaha {
|
| -
|
| -namespace {
|
| -
|
| -CString BuildMsiCommandLine(const CString& arguments,
|
| - const CString& msi_file_path,
|
| - const CString& enclosed_installer_data_file_path) {
|
| - CORE_LOG(L3, (_T("[CreateMsiCommandLine]")));
|
| -
|
| - CString command_line;
|
| - // Suppressing reboots can lead to an inconsistent state until the user
|
| - // reboots, but automatically rebooting is unacceptable. The user will be
|
| - // informed by the string for ERROR_SUCCESS_REBOOT_REQUIRED that a reboot is
|
| - // necessary. See http://b/1184091 for details.
|
| -
|
| - if (!enclosed_installer_data_file_path.IsEmpty()) {
|
| - SafeCStringFormat(&command_line, _T("INSTALLERDATA=%s "),
|
| - enclosed_installer_data_file_path);
|
| - }
|
| -
|
| - SafeCStringAppendFormat(&command_line, _T("%s %s /qn /i \"%s\""),
|
| - arguments,
|
| - kMsiSuppressAllRebootsCmdLine,
|
| - msi_file_path);
|
| -
|
| - // The msiexec version in XP SP2 (V 3.01) and higher supports the /log switch.
|
| - if (SystemInfo::OSWinXPSP2OrLater()) {
|
| - CString logfile(msi_file_path);
|
| - logfile.Append(_T(".log"));
|
| -
|
| - SafeCStringAppendFormat(&command_line, _T(" /log \"%s\""), logfile);
|
| - }
|
| -
|
| - CORE_LOG(L2, (_T("[msiexec command line][%s]"), command_line));
|
| - return command_line;
|
| -}
|
| -
|
| -// Gets the installer exit code.
|
| -HRESULT GetInstallerExitCode(const Process& p, uint32* exit_code) {
|
| - ASSERT1(exit_code);
|
| -
|
| - if (p.Running()) {
|
| - ASSERT(false,
|
| - (_T("GetInstallerExitCode called while the process is running.")));
|
| - return GOOPDATEINSTALL_E_INSTALLER_INTERNAL_ERROR;
|
| - }
|
| -
|
| - if (!p.GetExitCode(exit_code)) {
|
| - ASSERT(false,
|
| - (_T("[Failed to get the installer exit code for some reason.]")));
|
| - return GOOPDATEINSTALL_E_INSTALLER_INTERNAL_ERROR;
|
| - }
|
| -
|
| - CORE_LOG(L2, (_T("[Installer exit code][%u]"), *exit_code));
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -// Gets the errors string for the specified system error.
|
| -// Assumes error_code represents a system error.
|
| -void GetSystemErrorString(uint32 error_code,
|
| - const CString& language,
|
| - CString* error_string) {
|
| - ASSERT1(error_string);
|
| - ASSERT1(ERROR_SUCCESS != error_code);
|
| -
|
| - const CString error_code_string = FormatErrorCode(error_code);
|
| -
|
| - StringFormatter formatter(language);
|
| -
|
| - const CString error_message(GetMessageForSystemErrorCode(error_code));
|
| - if (!error_message.IsEmpty()) {
|
| - VERIFY1(SUCCEEDED(formatter.FormatMessage(error_string,
|
| - IDS_INSTALLER_FAILED_WITH_MESSAGE,
|
| - error_code_string,
|
| - error_message)));
|
| - } else {
|
| - VERIFY1(SUCCEEDED(formatter.FormatMessage(error_string,
|
| - IDS_INSTALLER_FAILED_NO_MESSAGE,
|
| - error_code_string)));
|
| - }
|
| -
|
| - OPT_LOG(LEVEL_ERROR, (_T("[installer system error][%u][%s]"),
|
| - error_code, *error_string));
|
| - ASSERT1(!error_string->IsEmpty());
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -InstallerWrapper::InstallerWrapper(bool is_machine)
|
| - : is_machine_(is_machine),
|
| - num_tries_when_msi_busy_(1) {
|
| - CORE_LOG(L3, (_T("[InstallerWrapper::InstallerWrapper]")));
|
| -}
|
| -
|
| -InstallerWrapper::~InstallerWrapper() {
|
| - CORE_LOG(L3, (_T("[InstallerWrapper::~InstallerWrapper]")));
|
| -}
|
| -
|
| -HRESULT InstallerWrapper::Initialize() {
|
| - NamedObjectAttributes lock_attr;
|
| - // TODO(omaha3): We might want to move this lock to the InstallManager.
|
| - GetNamedObjectAttributes(kInstallManagerSerializer, is_machine_, &lock_attr);
|
| - if (!installer_lock_.InitializeWithSecAttr(lock_attr.name, &lock_attr.sa)) {
|
| - OPT_LOG(LEVEL_ERROR, (_T("[Could not init Install Manager lock]")));
|
| - return GOOPDATEINSTALL_E_FAILED_INIT_INSTALLER_LOCK;
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -// result_* will be populated if the installer ran and exited, regardless of the
|
| -// return value.
|
| -// Assumes the call is protected by some mechanism providing exclusive access
|
| -// to the app's registry keys in Clients and ClientState.
|
| -HRESULT InstallerWrapper::InstallApp(HANDLE user_token,
|
| - const GUID& app_guid,
|
| - const CString& installer_path,
|
| - const CString& arguments,
|
| - const CString& installer_data,
|
| - const CString& language,
|
| - InstallerResultInfo* result_info) {
|
| - ASSERT1(result_info);
|
| -
|
| - HRESULT hr = DoInstallApp(user_token,
|
| - app_guid,
|
| - installer_path,
|
| - arguments,
|
| - installer_data,
|
| - language,
|
| - result_info);
|
| -
|
| - ASSERT1((SUCCEEDED(hr) && result_info->type == INSTALLER_RESULT_SUCCESS) ||
|
| - (GOOPDATEINSTALL_E_INSTALLER_FAILED == hr &&
|
| - (result_info->type == INSTALLER_RESULT_ERROR_MSI ||
|
| - result_info->type == INSTALLER_RESULT_ERROR_SYSTEM ||
|
| - result_info->type == INSTALLER_RESULT_ERROR_OTHER)) ||
|
| - (GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING == hr &&
|
| - (result_info->type == INSTALLER_RESULT_ERROR_MSI ||
|
| - result_info->type == INSTALLER_RESULT_ERROR_SYSTEM)) ||
|
| - (FAILED(hr) && result_info->type == INSTALLER_RESULT_UNKNOWN));
|
| - ASSERT1(!result_info->text.IsEmpty() ==
|
| - (GOOPDATEINSTALL_E_INSTALLER_FAILED == hr ||
|
| - GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING == hr) ||
|
| - SUCCEEDED(hr)); // Successes may or may not have messages.
|
| -
|
| - CORE_LOG(L3, (_T("[InstallApp result][0x%x][%s][type: %d][code: %d][%s][%s]"),
|
| - hr, GuidToString(app_guid), result_info->type,
|
| - result_info->code, result_info->text,
|
| - result_info->post_install_launch_command_line));
|
| - return hr;
|
| -}
|
| -
|
| -CString InstallerWrapper::GetMessageForError(HRESULT error_code,
|
| - const CString& installer_filename,
|
| - const CString& language) {
|
| - CString message;
|
| - StringFormatter formatter(language);
|
| -
|
| - switch (error_code) {
|
| - case GOOPDATEINSTALL_E_FILENAME_INVALID:
|
| - VERIFY1(SUCCEEDED(formatter.FormatMessage(&message,
|
| - IDS_INVALID_INSTALLER_FILENAME,
|
| - installer_filename)));
|
| - break;
|
| - case GOOPDATEINSTALL_E_INSTALLER_FAILED_START:
|
| - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALLER_FAILED_TO_START,
|
| - &message)));
|
| - break;
|
| - case GOOPDATEINSTALL_E_INSTALLER_TIMED_OUT:
|
| - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALLER_TIMED_OUT,
|
| - &message)));
|
| - break;
|
| - case GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY:
|
| - case GOOPDATEINSTALL_E_INSTALLER_DID_NOT_CHANGE_VERSION:
|
| - case GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH:
|
| - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)));
|
| - break;
|
| - case GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING:
|
| - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_MSI_INSTALL_ALREADY_RUNNING,
|
| - &message)));
|
| - break;
|
| - case GOOPDATEINSTALL_E_INSTALLER_FAILED:
|
| - ASSERT(false,
|
| - (_T("[GetOmahaErrorTextToReport]")
|
| - _T("GOOPDATEINSTALL_E_INSTALLER_FAILED should never be reported ")
|
| - _T("directly. The installer error string should be reported.")));
|
| - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)));
|
| - break;
|
| - case GOOPDATEINSTALL_E_INSTALLER_INTERNAL_ERROR:
|
| - default:
|
| - ASSERT(false, (_T("[GetOmahaErrorTextToReport]")
|
| - _T("[An Omaha error occurred that this method does not ")
|
| - _T("know how to report.][0x%08x]"), error_code));
|
| - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)));
|
| - break;
|
| - }
|
| -
|
| - ASSERT1(!message.IsEmpty());
|
| - return message;
|
| -}
|
| -
|
| -void InstallerWrapper::set_num_tries_when_msi_busy(
|
| - int num_tries_when_msi_busy) {
|
| - ASSERT1(num_tries_when_msi_busy >= 1);
|
| - num_tries_when_msi_busy_ = num_tries_when_msi_busy;
|
| -}
|
| -
|
| -HRESULT InstallerWrapper::BuildCommandLineFromFilename(
|
| - const CString& file_path,
|
| - const CString& arguments,
|
| - const CString& installer_data,
|
| - CString* executable_path,
|
| - CString* command_line,
|
| - InstallerType* installer_type) {
|
| - CORE_LOG(L3, (_T("[BuildCommandLineFromFilename]")));
|
| -
|
| - ASSERT1(executable_path);
|
| - ASSERT1(command_line);
|
| - ASSERT1(installer_type);
|
| -
|
| - *executable_path = _T("");
|
| - *command_line = _T("");
|
| - *installer_type = UNKNOWN_INSTALLER;
|
| -
|
| - // The app's installer owns the lifetime of installer data file if it has been
|
| - // created, so Omaha does not delete it.
|
| - CString enclosed_installer_data_file_path;
|
| -
|
| - VERIFY1(SUCCEEDED(goopdate_utils::WriteInstallerDataToTempFile(
|
| - installer_data,
|
| - &enclosed_installer_data_file_path)));
|
| - if (!enclosed_installer_data_file_path.IsEmpty()) {
|
| - EnclosePath(&enclosed_installer_data_file_path);
|
| - }
|
| -
|
| - // PathFindExtension returns the address of the trailing NUL character if an
|
| - // extension is not found. It does not return NULL.
|
| - const TCHAR* ext = ::PathFindExtension(file_path);
|
| - ASSERT1(ext);
|
| - if (*ext != _T('\0')) {
|
| - ext++; // Skip the period.
|
| - if (0 == lstrcmpi(ext, _T("exe"))) {
|
| - *executable_path = file_path;
|
| - if (enclosed_installer_data_file_path.IsEmpty()) {
|
| - *command_line = arguments;
|
| - } else {
|
| - SafeCStringFormat(command_line, _T("%s /installerdata=%s"),
|
| - arguments,
|
| - enclosed_installer_data_file_path);
|
| - }
|
| - *installer_type = CUSTOM_INSTALLER;
|
| -
|
| - CORE_LOG(L2, (_T("[BuildCommandLineFromFilename][exe][%s][%s]"),
|
| - *executable_path, *command_line));
|
| - } else if (0 == lstrcmpi(ext, _T("msi"))) {
|
| - *executable_path = _T("msiexec");
|
| - *command_line = BuildMsiCommandLine(arguments,
|
| - file_path,
|
| - enclosed_installer_data_file_path);
|
| - *installer_type = MSI_INSTALLER;
|
| -
|
| - CORE_LOG(L2, (_T("[BuildCommandLineFromFilename][msi][%s]"),
|
| - *command_line));
|
| - } else {
|
| - *executable_path = _T("");
|
| - *command_line = _T("");
|
| - *installer_type = UNKNOWN_INSTALLER;
|
| -
|
| - OPT_LOG(LE, (_T("[Unsupported extension '%s' in %s]"), ext, file_path));
|
| - return GOOPDATEINSTALL_E_FILENAME_INVALID;
|
| - }
|
| - } else {
|
| - OPT_LOG(LE, (_T("[No extension found in %s]"), file_path));
|
| - return GOOPDATEINSTALL_E_FILENAME_INVALID;
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -// Calls DoExecuteAndWaitForInstaller to do the work. If an MSI installer
|
| -// returns, ERROR_INSTALL_ALREADY_RUNNING waits and retries several times or
|
| -// until the installation succeeds.
|
| -HRESULT InstallerWrapper::ExecuteAndWaitForInstaller(
|
| - HANDLE user_token,
|
| - const GUID& app_guid,
|
| - const CString& executable_path,
|
| - const CString& command_line,
|
| - InstallerType installer_type,
|
| - const CString& language,
|
| - InstallerResultInfo* result_info) {
|
| - CORE_LOG(L3, (_T("[InstallerWrapper::ExecuteAndWaitForInstaller]")));
|
| - ASSERT1(result_info);
|
| - ASSERT1(num_tries_when_msi_busy_ >= 1);
|
| -
|
| - ++metric_worker_install_execute_total;
|
| - if (MSI_INSTALLER == installer_type) {
|
| - ++metric_worker_install_execute_msi_total;
|
| - }
|
| -
|
| - // Run the installer, retrying if necessary.
|
| - int retry_delay = kMsiAlreadyRunningRetryDelayBaseMs;
|
| - int num_tries(0);
|
| - HRESULT hr = GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING;
|
| - for (num_tries = 0;
|
| - hr == GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING &&
|
| - num_tries < num_tries_when_msi_busy_;
|
| - ++num_tries) {
|
| - // Reset the result info - it contains the previous error when retrying.
|
| - *result_info = InstallerResultInfo();
|
| -
|
| - if (0 < num_tries) {
|
| - // Retrying - wait between attempts.
|
| - CORE_LOG(L1, (_T("[Retrying][%d]"), num_tries));
|
| - ::Sleep(retry_delay);
|
| - retry_delay *= 2; // Double the retry delay next time.
|
| - }
|
| -
|
| - hr = DoExecuteAndWaitForInstaller(user_token,
|
| - app_guid,
|
| - executable_path,
|
| - command_line,
|
| - installer_type,
|
| - language,
|
| - result_info);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[DoExecuteAndWaitForInstaller failed][0x%08x]"), hr));
|
| - return hr;
|
| - }
|
| - CORE_LOG(L1, (_T("[Installer result][%d][%d][%s]"),
|
| - result_info->type, result_info->code, result_info->text));
|
| - ASSERT1(result_info->type != INSTALLER_RESULT_UNKNOWN);
|
| -
|
| - if ((INSTALLER_RESULT_ERROR_MSI == result_info->type ||
|
| - INSTALLER_RESULT_ERROR_SYSTEM == result_info->type) &&
|
| - ERROR_INSTALL_ALREADY_RUNNING == result_info->code) {
|
| - hr = GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING;
|
| - }
|
| - }
|
| -
|
| - if (1 < num_tries) {
|
| - // Record metrics about the ERROR_INSTALL_ALREADY_RUNNING retries.
|
| -// TODO(omaha3): If we're willing to have a single metric for installs and
|
| -// updates, we can avoid knowing is_update.
|
| -#if 0
|
| - if (!app_version_->is_update()) {
|
| -#endif
|
| - ++metric_worker_install_msi_in_progress_detected_install;
|
| - if (result_info->type == INSTALLER_RESULT_SUCCESS) {
|
| - ++metric_worker_install_msi_in_progress_retry_succeeded_install;
|
| - metric_worker_install_msi_in_progress_retry_succeeded_tries_install
|
| - = num_tries;
|
| - }
|
| -#if 0
|
| - } else {
|
| - ++metric_worker_install_msi_in_progress_detected_update;
|
| - if (result_info->type == INSTALLER_RESULT_SUCCESS) {
|
| - ++metric_worker_install_msi_in_progress_retry_succeeded_update;
|
| - metric_worker_install_msi_in_progress_retry_succeeded_tries_update
|
| - = num_tries;
|
| - }
|
| - }
|
| -#endif
|
| - }
|
| -
|
| - return hr;
|
| -}
|
| -
|
| -HRESULT InstallerWrapper::DoExecuteAndWaitForInstaller(
|
| - HANDLE user_token,
|
| - const GUID& app_guid,
|
| - const CString& executable_path,
|
| - const CString& command_line,
|
| - InstallerType installer_type,
|
| - const CString& language,
|
| - InstallerResultInfo* result_info) {
|
| - OPT_LOG(L1, (_T("[Running installer][%s][%s][%s]"),
|
| - executable_path, command_line, GuidToString(app_guid)));
|
| - ASSERT1(result_info);
|
| -
|
| - AppManager::Instance()->ClearInstallerResultApiValues(app_guid);
|
| -
|
| - Process p(executable_path, NULL);
|
| - HRESULT hr = p.Start(command_line, user_token);
|
| - if (FAILED(hr)) {
|
| - OPT_LOG(LE, (_T("[p.Start fail][hr][%s][%s]"),
|
| - hr, executable_path, command_line));
|
| - set_error_extra_code1(static_cast<int>(hr));
|
| - return GOOPDATEINSTALL_E_INSTALLER_FAILED_START;
|
| - }
|
| -
|
| - // TODO(omaha): InstallerWrapper should not special case Omaha. It is better
|
| - // to have an abstraction such as waiting or not for the installer to
|
| - // exit and let the App state machine special case Omaha. It's too low level
|
| - // to make a decision like this in the InstallerWrapper. Same for all
|
| - // kinds of tests on the call stack above this call that the app_guid is
|
| - // Omaha's guid.
|
| - if (::IsEqualGUID(app_guid, kGoopdateGuid)) {
|
| - // Do not wait for the installer when installing Omaha.
|
| - result_info->type = INSTALLER_RESULT_SUCCESS;
|
| - return S_OK;
|
| - }
|
| -
|
| - if (!p.WaitUntilDead(kInstallerCompleteIntervalMs)) {
|
| - OPT_LOG(LEVEL_WARNING, (_T("[Installer has timed out]")
|
| - _T("[%s][%s]"), executable_path, command_line));
|
| - return GOOPDATEINSTALL_E_INSTALLER_TIMED_OUT;
|
| - }
|
| -
|
| - hr = GetInstallerResult(app_guid,
|
| - installer_type,
|
| - p,
|
| - language,
|
| - result_info);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LEVEL_ERROR, (_T("[GetInstallerResult failed][0x%08x]"), hr));
|
| - return hr;
|
| - }
|
| -
|
| - if (result_info->type != INSTALLER_RESULT_SUCCESS) {
|
| - OPT_LOG(LE, (_T("[Installer failed][%s][%s][%u]"),
|
| - executable_path, command_line, result_info->code));
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT InstallerWrapper::GetInstallerResult(const GUID& app_guid,
|
| - InstallerType installer_type,
|
| - const Process& p,
|
| - const CString& language,
|
| - InstallerResultInfo* result_info) {
|
| - CORE_LOG(L3, (_T("[InstallerWrapper::GetInstallerResult]")));
|
| - ASSERT1(result_info);
|
| -
|
| - uint32 exit_code = 0;
|
| - HRESULT hr = GetInstallerExitCode(p, &exit_code);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LEVEL_ERROR, (_T("[GetInstallerExitCode failed][0x%08x]"), hr));
|
| - return hr;
|
| - }
|
| -
|
| - GetInstallerResultHelper(app_guid,
|
| - installer_type,
|
| - exit_code,
|
| - language,
|
| - result_info);
|
| - return S_OK;
|
| -}
|
| -
|
| -// The default InstallerResult behavior can be overridden in the registry.
|
| -// By default, error_code is the exit code. For some InstallerResults, it
|
| -// can be overridden by InstallerError in the registry.
|
| -// The success string cannot be overridden.
|
| -void InstallerWrapper::GetInstallerResultHelper(
|
| - const GUID& app_guid,
|
| - InstallerType installer_type,
|
| - uint32 exit_code,
|
| - const CString& language,
|
| - InstallerResultInfo* result_info) {
|
| - ASSERT1(result_info);
|
| -
|
| - AppManager::InstallerResult installer_result =
|
| - AppManager::INSTALLER_RESULT_DEFAULT;
|
| - InstallerResultInfo result;
|
| -
|
| - result.code = exit_code;
|
| -
|
| - AppManager& app_manager = *AppManager::Instance();
|
| - app_manager.ReadInstallerResultApiValues(
|
| - app_guid,
|
| - &installer_result,
|
| - &result.code,
|
| - &result.extra_code1,
|
| - &result.text,
|
| - &result.post_install_launch_command_line);
|
| - OPT_LOG(L1, (_T("[InstallerResult][%s][%u]"),
|
| - GuidToString(app_guid), installer_result));
|
| -
|
| - switch (installer_result) {
|
| - case AppManager::INSTALLER_RESULT_SUCCESS:
|
| - result.type = INSTALLER_RESULT_SUCCESS;
|
| - // TODO(omaha3): Support custom success messages.
|
| - break;
|
| - case AppManager::INSTALLER_RESULT_FAILED_CUSTOM_ERROR:
|
| - result.type = INSTALLER_RESULT_ERROR_OTHER;
|
| - break;
|
| - case AppManager::INSTALLER_RESULT_FAILED_MSI_ERROR:
|
| - result.type = INSTALLER_RESULT_ERROR_MSI;
|
| - break;
|
| - case AppManager::INSTALLER_RESULT_FAILED_SYSTEM_ERROR:
|
| - result.type = INSTALLER_RESULT_ERROR_SYSTEM;
|
| - break;
|
| - case AppManager::INSTALLER_RESULT_EXIT_CODE:
|
| - ASSERT(result.code == exit_code, (_T("InstallerError overridden")));
|
| - if (0 == exit_code) {
|
| - result.type = INSTALLER_RESULT_SUCCESS;
|
| - result.code = 0;
|
| - } else {
|
| - switch (installer_type) {
|
| - case MSI_INSTALLER:
|
| - result.type = INSTALLER_RESULT_ERROR_MSI;
|
| - break;
|
| - case UNKNOWN_INSTALLER:
|
| - case CUSTOM_INSTALLER:
|
| - case MAX_INSTALLER:
|
| - default:
|
| - result.type = INSTALLER_RESULT_ERROR_OTHER;
|
| - break;
|
| - }
|
| - }
|
| - break;
|
| - case AppManager::INSTALLER_RESULT_MAX:
|
| - default:
|
| - ASSERT1(false);
|
| - break;
|
| - }
|
| -
|
| - // Handle the reboot required case.
|
| - if ((INSTALLER_RESULT_ERROR_MSI == result.type ||
|
| - INSTALLER_RESULT_ERROR_SYSTEM == result.type) &&
|
| - (ERROR_SUCCESS_REBOOT_REQUIRED == result.code)) {
|
| - // Reboot takes precedence over other actions.
|
| - result.type = INSTALLER_RESULT_SUCCESS;
|
| - result.code = 0;
|
| - result.post_install_action = POST_INSTALL_ACTION_REBOOT;
|
| - } else if (!result.post_install_launch_command_line.IsEmpty()) {
|
| - result.post_install_action = POST_INSTALL_ACTION_LAUNCH_COMMAND;
|
| - }
|
| -
|
| - // InstallerResultInfo status has been finalized. Make sure all errors
|
| - // have error strings.
|
| - switch (result.type) {
|
| - case INSTALLER_RESULT_SUCCESS:
|
| - break;
|
| -
|
| - case INSTALLER_RESULT_ERROR_MSI:
|
| - case INSTALLER_RESULT_ERROR_SYSTEM:
|
| - GetSystemErrorString(result.code, language, &result.text);
|
| - break;
|
| -
|
| - case INSTALLER_RESULT_ERROR_OTHER:
|
| - if (result.text.IsEmpty()) {
|
| - result.text.FormatMessage(
|
| - IDS_INSTALLER_FAILED_NO_MESSAGE,
|
| - FormatErrorCode(result.code));
|
| - }
|
| - break;
|
| -
|
| - case INSTALLER_RESULT_UNKNOWN:
|
| - default:
|
| - ASSERT1(false);
|
| - }
|
| -
|
| - // TODO(omaha3): Serialize InstallerResultInfo.
|
| - // OPT_LOG(L1, (_T("[%s]"), result_info->ToString()));
|
| - *result_info = result;
|
| -}
|
| -
|
| -// TODO(omaha3): Consider moving this method out of this class, maybe into
|
| -// InstallManager.
|
| -HRESULT InstallerWrapper::CheckApplicationRegistration(
|
| - const GUID& app_guid,
|
| - const CString& registered_version,
|
| - const CString& expected_version,
|
| - const CString& previous_version,
|
| - bool is_update) const {
|
| - const CString app_guid_string = GuidToString(app_guid);
|
| - CORE_LOG(L2, (_T("[InstallerWrapper::CheckApplicationRegistration][%s]"),
|
| - app_guid_string));
|
| - ASSERT(!::IsEqualGUID(kGoopdateGuid, app_guid),
|
| - (_T("Probably do not want to call this method for Omaha")));
|
| -
|
| - if (registered_version.IsEmpty()) {
|
| - return GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY;
|
| - }
|
| -
|
| - CORE_LOG(L2, (_T("[CheckApplicationRegistration]")
|
| - _T("[guid=%s][registered=%s][expected=%s][previous=%s]"),
|
| - app_guid_string, registered_version, expected_version,
|
| - previous_version));
|
| -
|
| - if (!expected_version.IsEmpty() && registered_version != expected_version) {
|
| - OPT_LOG(LE, (_T("[Registered version does not match expected][%s][%s][%s]"),
|
| - app_guid_string, registered_version, expected_version));
|
| - // This is expected if a newer version is already installed. Do not fail
|
| - // here in that case. This only works for four-element version strings.
|
| - // If the version format is not recognized, VersionFromString() returns 0.
|
| -
|
| - ULONGLONG registered_version_number = VersionFromString(registered_version);
|
| - ULONGLONG expected_version_number = VersionFromString(expected_version);
|
| -
|
| - // Check that the version did not change, the registered version is newer,
|
| - // and neither VersionFromString() call failed.
|
| - if (is_update && registered_version != previous_version ||
|
| - registered_version_number < expected_version_number ||
|
| - !registered_version_number ||
|
| - !expected_version_number) {
|
| - return GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH;
|
| - }
|
| -
|
| - CORE_LOG(L1, (_T("[Newer version already registered]")));
|
| - }
|
| -
|
| - if (is_update && previous_version == registered_version) {
|
| - ASSERT1(!previous_version.IsEmpty());
|
| - ASSERT(expected_version.IsEmpty() ||
|
| - VersionFromString(expected_version) >
|
| - VersionFromString(previous_version) ||
|
| - VersionFromString(expected_version) == 0 ||
|
| - VersionFromString(previous_version) == 0,
|
| - (_T("expected_version should be > previous_version when ")
|
| - _T("is_update - possibly a bad update rule.")));
|
| -
|
| - OPT_LOG(LE, (_T("[Installer did not change version][%s][%s]"),
|
| - app_guid_string, previous_version));
|
| - return GOOPDATEINSTALL_E_INSTALLER_DID_NOT_CHANGE_VERSION;
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -// Assumes installer_lock_ has been initialized.
|
| -HRESULT InstallerWrapper::DoInstallApp(HANDLE user_token,
|
| - const GUID& app_guid,
|
| - const CString& installer_path,
|
| - const CString& arguments,
|
| - const CString& installer_data,
|
| - const CString& language,
|
| - InstallerResultInfo* result_info) {
|
| - CORE_LOG(L1, (_T("[InstallerWrapper::DoInstallApp][%s][%s][%s]"),
|
| - GuidToString(app_guid), installer_path, arguments));
|
| - ASSERT1(result_info);
|
| -
|
| - CString executable_path;
|
| - CString command_line;
|
| - InstallerType installer_type = UNKNOWN_INSTALLER;
|
| -
|
| - // TODO(omaha): Remove when http://b/1443404 is addressed.
|
| - const TCHAR* const kChromeGuid = _T("{8A69D345-D564-463C-AFF1-A69D9E530F96}");
|
| - const TCHAR* const kChromePerMachineArg = _T("--system-level");
|
| - CString modified_arguments = arguments;
|
| - if (kChromeGuid == GuidToString(app_guid) && is_machine_) {
|
| - modified_arguments.AppendFormat(_T(" %s"), kChromePerMachineArg);
|
| - }
|
| -
|
| - HRESULT hr = BuildCommandLineFromFilename(installer_path,
|
| - modified_arguments,
|
| - installer_data,
|
| - &executable_path,
|
| - &command_line,
|
| - &installer_type);
|
| -
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LW, (_T("[BuildCommandLineFromFilename failed][0x%08x]"), hr));
|
| - ASSERT1(GOOPDATEINSTALL_E_FILENAME_INVALID == hr);
|
| - return hr;
|
| - }
|
| -
|
| - // Acquire the global lock here. This will ensure that we are the only
|
| - // installer running of the multiple goopdates.
|
| - __mutexBlock(installer_lock_) {
|
| - hr = ExecuteAndWaitForInstaller(user_token,
|
| - app_guid,
|
| - executable_path,
|
| - command_line,
|
| - installer_type,
|
| - language,
|
| - result_info);
|
| - }
|
| -
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[ExecuteAndWaitForInstaller failed][0x%08x][%s]"),
|
| - hr, GuidToString(app_guid)));
|
| - return hr;
|
| - }
|
| -
|
| - if (result_info->type != INSTALLER_RESULT_SUCCESS) {
|
| - CORE_LOG(LE, (_T("[Installer failed][%d]"), result_info->type));
|
| - return GOOPDATEINSTALL_E_INSTALLER_FAILED;
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -} // namespace omaha
|
|
|