Home | History | Annotate | Download | only in util
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/installer/util/google_update_util.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/files/file_path.h"
      9 #include "base/files/file_util.h"
     10 #include "base/logging.h"
     11 #include "base/path_service.h"
     12 #include "base/process/kill.h"
     13 #include "base/process/launch.h"
     14 #include "base/strings/string16.h"
     15 #include "base/time/time.h"
     16 #include "base/win/registry.h"
     17 #include "base/win/scoped_handle.h"
     18 #include "base/win/win_util.h"
     19 #include "base/win/windows_version.h"
     20 #include "chrome/installer/util/browser_distribution.h"
     21 #include "chrome/installer/util/google_update_constants.h"
     22 #include "chrome/installer/util/google_update_settings.h"
     23 #include "chrome/installer/util/install_util.h"
     24 #include "chrome/installer/util/installation_state.h"
     25 #include "chrome/installer/util/product.h"
     26 
     27 using base::win::RegKey;
     28 
     29 namespace google_update {
     30 
     31 namespace {
     32 
     33 const int kGoogleUpdateTimeoutMs = 20 * 1000;
     34 
     35 // Returns true if Google Update is present at the given level.
     36 bool IsGoogleUpdatePresent(bool system_install) {
     37   // Using the existence of version key in the registry to decide.
     38   return GoogleUpdateSettings::GetGoogleUpdateVersion(system_install).IsValid();
     39 }
     40 
     41 // Returns GoogleUpdateSetup.exe's executable path at specified level.
     42 // or an empty path if none is found.
     43 base::FilePath GetGoogleUpdateSetupExe(bool system_install) {
     44   const HKEY root_key = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
     45   RegKey update_key;
     46 
     47   if (update_key.Open(root_key, kRegPathGoogleUpdate, KEY_QUERY_VALUE) ==
     48           ERROR_SUCCESS) {
     49     base::string16 path_str;
     50     base::string16 version_str;
     51     if ((update_key.ReadValue(kRegPathField, &path_str) == ERROR_SUCCESS) &&
     52         (update_key.ReadValue(kRegGoogleUpdateVersion, &version_str) ==
     53              ERROR_SUCCESS)) {
     54       return base::FilePath(path_str).DirName().Append(version_str).
     55           Append(kGoogleUpdateSetupExe);
     56     }
     57   }
     58   return base::FilePath();
     59 }
     60 
     61 // If Google Update is present at system-level, sets |cmd_string| to the command
     62 // line to install Google Update at user-level and returns true.
     63 // Otherwise, clears |cmd_string| and returns false.
     64 bool GetUserLevelGoogleUpdateInstallCommandLine(base::string16* cmd_string) {
     65   cmd_string->clear();
     66   base::FilePath google_update_setup(
     67       GetGoogleUpdateSetupExe(true));  // system-level.
     68   if (!google_update_setup.empty()) {
     69     CommandLine cmd(google_update_setup);
     70     // Appends "/install runtime=true&needsadmin=false /silent /nomitag".
     71     // NB: /nomitag needs to be at the end.
     72     // Constants are found in code.google.com/p/omaha/common/const_cmd_line.h.
     73     cmd.AppendArg("/install");
     74     // The "&" can be used in base::LaunchProcess() without quotation
     75     // (this is problematic only if run from command prompt).
     76     cmd.AppendArg("runtime=true&needsadmin=false");
     77     cmd.AppendArg("/silent");
     78     cmd.AppendArg("/nomitag");
     79     *cmd_string = cmd.GetCommandLineString();
     80   }
     81   return !cmd_string->empty();
     82 }
     83 
     84 // Launches command |cmd_string|, and waits for |timeout| milliseconds before
     85 // timing out.  To wait indefinitely, one can set
     86 // |timeout| to be base::TimeDelta::FromMilliseconds(INFINITE).
     87 // Returns true if this executes successfully.
     88 // Returns false if command execution fails to execute, or times out.
     89 bool LaunchProcessAndWaitWithTimeout(const base::string16& cmd_string,
     90                                      base::TimeDelta timeout) {
     91   bool success = false;
     92   base::win::ScopedHandle process;
     93   int exit_code = 0;
     94   VLOG(0) << "Launching: " << cmd_string;
     95   if (!base::LaunchProcess(cmd_string, base::LaunchOptions(),
     96                            &process)) {
     97     PLOG(ERROR) << "Failed to launch (" << cmd_string << ")";
     98   } else if (!base::WaitForExitCodeWithTimeout(process.Get(), &exit_code,
     99                                                timeout)) {
    100     // The GetExitCodeProcess failed or timed-out.
    101     LOG(ERROR) <<"Command (" << cmd_string << ") is taking more than "
    102                << timeout.InMilliseconds() << " milliseconds to complete.";
    103   } else if (exit_code != 0) {
    104     LOG(ERROR) << "Command (" << cmd_string << ") exited with code "
    105                << exit_code;
    106   } else {
    107     success = true;
    108   }
    109   return success;
    110 }
    111 
    112 }  // namespace
    113 
    114 bool EnsureUserLevelGoogleUpdatePresent() {
    115   VLOG(0) << "Ensuring Google Update is present at user-level.";
    116 
    117   bool success = false;
    118   if (IsGoogleUpdatePresent(false)) {
    119     success = true;
    120   } else {
    121     base::string16 cmd_string;
    122     if (!GetUserLevelGoogleUpdateInstallCommandLine(&cmd_string)) {
    123       LOG(ERROR) << "Cannot find Google Update at system-level.";
    124       // Ideally we should return false. However, this case should not be
    125       // encountered by regular users, and developers (who often installs
    126       // Chrome without Google Update) may be unduly impeded by this case.
    127       // Therefore we return true.
    128       success = true;
    129     } else {
    130       success = LaunchProcessAndWaitWithTimeout(cmd_string,
    131           base::TimeDelta::FromMilliseconds(INFINITE));
    132     }
    133   }
    134   return success;
    135 }
    136 
    137 bool UninstallGoogleUpdate(bool system_install) {
    138   bool success = false;
    139   base::string16 cmd_string(
    140       GoogleUpdateSettings::GetUninstallCommandLine(system_install));
    141   if (cmd_string.empty()) {
    142     success = true;  // Nothing to; vacuous success.
    143   } else {
    144     success = LaunchProcessAndWaitWithTimeout(cmd_string,
    145         base::TimeDelta::FromMilliseconds(kGoogleUpdateTimeoutMs));
    146   }
    147   return success;
    148 }
    149 
    150 void ElevateIfNeededToReenableUpdates() {
    151   base::FilePath chrome_exe;
    152   if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
    153     NOTREACHED();
    154     return;
    155   }
    156   installer::ProductState product_state;
    157   BrowserDistribution* dist = BrowserDistribution::GetDistribution();
    158   const bool system_install = !InstallUtil::IsPerUserInstall(
    159       chrome_exe.value().c_str());
    160   if (!product_state.Initialize(system_install, dist))
    161     return;
    162   base::FilePath exe_path(product_state.GetSetupPath());
    163   if (exe_path.empty() || !base::PathExists(exe_path)) {
    164     LOG(ERROR) << "Could not find setup.exe to reenable updates.";
    165     return;
    166   }
    167 
    168   CommandLine cmd(exe_path);
    169   cmd.AppendSwitch(installer::switches::kReenableAutoupdates);
    170   installer::Product product(dist);
    171   product.InitializeFromUninstallCommand(product_state.uninstall_command());
    172   product.AppendProductFlags(&cmd);
    173   if (system_install)
    174     cmd.AppendSwitch(installer::switches::kSystemLevel);
    175   if (product_state.uninstall_command().HasSwitch(
    176           installer::switches::kVerboseLogging)) {
    177     cmd.AppendSwitch(installer::switches::kVerboseLogging);
    178   }
    179 
    180   base::LaunchOptions launch_options;
    181   launch_options.force_breakaway_from_job_ = true;
    182 
    183   if (base::win::GetVersion() >= base::win::VERSION_VISTA &&
    184       base::win::UserAccountControlIsEnabled()) {
    185     base::LaunchElevatedProcess(cmd, launch_options, NULL);
    186   } else {
    187     base::LaunchProcess(cmd, launch_options, NULL);
    188   }
    189 }
    190 
    191 }  // namespace google_update
    192