setup_main.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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/setup/setup_main.h" 6 7#include <windows.h> 8#include <msi.h> 9#include <shellapi.h> 10#include <shlobj.h> 11 12#include <string> 13 14#include "base/at_exit.h" 15#include "base/basictypes.h" 16#include "base/command_line.h" 17#include "base/file_util.h" 18#include "base/file_version_info.h" 19#include "base/files/file_path.h" 20#include "base/files/scoped_temp_dir.h" 21#include "base/memory/scoped_ptr.h" 22#include "base/path_service.h" 23#include "base/process/launch.h" 24#include "base/strings/string16.h" 25#include "base/strings/string_number_conversions.h" 26#include "base/strings/string_util.h" 27#include "base/strings/stringprintf.h" 28#include "base/strings/utf_string_conversions.h" 29#include "base/values.h" 30#include "base/win/registry.h" 31#include "base/win/scoped_com_initializer.h" 32#include "base/win/scoped_comptr.h" 33#include "base/win/scoped_handle.h" 34#include "base/win/win_util.h" 35#include "base/win/windows_version.h" 36#include "breakpad/src/client/windows/handler/exception_handler.h" 37#include "chrome/common/chrome_constants.h" 38#include "chrome/common/chrome_switches.h" 39#include "chrome/installer/setup/archive_patch_helper.h" 40#include "chrome/installer/setup/install.h" 41#include "chrome/installer/setup/install_worker.h" 42#include "chrome/installer/setup/setup_constants.h" 43#include "chrome/installer/setup/setup_util.h" 44#include "chrome/installer/setup/uninstall.h" 45#include "chrome/installer/util/browser_distribution.h" 46#include "chrome/installer/util/channel_info.h" 47#include "chrome/installer/util/delete_after_reboot_helper.h" 48#include "chrome/installer/util/delete_tree_work_item.h" 49#include "chrome/installer/util/eula_util.h" 50#include "chrome/installer/util/google_update_constants.h" 51#include "chrome/installer/util/google_update_settings.h" 52#include "chrome/installer/util/google_update_util.h" 53#include "chrome/installer/util/helper.h" 54#include "chrome/installer/util/html_dialog.h" 55#include "chrome/installer/util/install_util.h" 56#include "chrome/installer/util/installation_state.h" 57#include "chrome/installer/util/installation_validator.h" 58#include "chrome/installer/util/installer_state.h" 59#include "chrome/installer/util/l10n_string_util.h" 60#include "chrome/installer/util/logging_installer.h" 61#include "chrome/installer/util/lzma_util.h" 62#include "chrome/installer/util/master_preferences.h" 63#include "chrome/installer/util/master_preferences_constants.h" 64#include "chrome/installer/util/self_cleaning_temp_dir.h" 65#include "chrome/installer/util/shell_util.h" 66#include "chrome/installer/util/user_experiment.h" 67 68#include "installer_util_strings.h" // NOLINT 69 70using installer::InstallerState; 71using installer::InstallationState; 72using installer::InstallationValidator; 73using installer::MasterPreferences; 74using installer::Product; 75using installer::ProductState; 76using installer::Products; 77 78const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices"; 79const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\"; 80const wchar_t kSystemPrincipalSid[] = L"S-1-5-18"; 81 82const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>( 83 MiniDumpWithProcessThreadData | // Get PEB and TEB. 84 MiniDumpWithUnloadedModules | // Get unloaded modules when available. 85 MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack. 86 87namespace { 88 89// Returns NULL if no compressed archive is available for processing, otherwise 90// returns a patch helper configured to uncompress and patch. 91scoped_ptr<installer::ArchivePatchHelper> CreateChromeArchiveHelper( 92 const base::FilePath& setup_exe, 93 const CommandLine& command_line, 94 const installer::InstallerState& installer_state, 95 const base::FilePath& working_directory) { 96 // A compressed archive is ordinarily given on the command line by the mini 97 // installer. If one was not given, look for chrome.packed.7z next to the 98 // running program. 99 base::FilePath compressed_archive( 100 command_line.GetSwitchValuePath(installer::switches::kInstallArchive)); 101 bool compressed_archive_specified = !compressed_archive.empty(); 102 if (!compressed_archive_specified) { 103 compressed_archive = setup_exe.DirName().Append( 104 installer::kChromeCompressedArchive); 105 } 106 107 // Fail if no compressed archive is found. 108 if (!base::PathExists(compressed_archive)) { 109 if (compressed_archive_specified) { 110 LOG(ERROR) << installer::switches::kInstallArchive << "=" 111 << compressed_archive.value() << " not found."; 112 } 113 return scoped_ptr<installer::ArchivePatchHelper>(); 114 } 115 116 // chrome.7z is either extracted directly from the compressed archive into the 117 // working dir or is the target of patching in the working dir. 118 base::FilePath target(working_directory.Append(installer::kChromeArchive)); 119 DCHECK(!base::PathExists(target)); 120 121 // Specify an empty path for the patch source since it isn't yet known that 122 // one is needed. It will be supplied in UncompressAndPatchChromeArchive if it 123 // is. 124 return scoped_ptr<installer::ArchivePatchHelper>( 125 new installer::ArchivePatchHelper(working_directory, 126 compressed_archive, 127 base::FilePath(), 128 target)); 129} 130 131// Workhorse for producing an uncompressed archive (chrome.7z) given a 132// chrome.packed.7z containing either a patch file based on the version of 133// chrome being updated or the full uncompressed archive. Returns true on 134// success, in which case |archive_type| is populated based on what was found. 135// Returns false on failure, in which case |install_status| contains the error 136// code and the result is written to the registry (via WriteInstallerResult). 137bool UncompressAndPatchChromeArchive( 138 const installer::InstallationState& original_state, 139 const installer::InstallerState& installer_state, 140 installer::ArchivePatchHelper* archive_helper, 141 installer::ArchiveType* archive_type, 142 installer::InstallStatus* install_status) { 143 installer_state.UpdateStage(installer::UNCOMPRESSING); 144 if (!archive_helper->Uncompress(NULL)) { 145 *install_status = installer::UNCOMPRESSION_FAILED; 146 installer_state.WriteInstallerResult(*install_status, 147 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, 148 NULL); 149 return false; 150 } 151 152 // Short-circuit if uncompression produced the uncompressed archive rather 153 // than a patch file. 154 if (base::PathExists(archive_helper->target())) { 155 *archive_type = installer::FULL_ARCHIVE_TYPE; 156 return true; 157 } 158 159 // Find the installed version's archive to serve as the source for patching. 160 base::FilePath patch_source(installer::FindArchiveToPatch(original_state, 161 installer_state)); 162 if (patch_source.empty()) { 163 LOG(ERROR) << "Failed to find archive to patch."; 164 *install_status = installer::DIFF_PATCH_SOURCE_MISSING; 165 installer_state.WriteInstallerResult(*install_status, 166 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, 167 NULL); 168 return false; 169 } 170 archive_helper->set_patch_source(patch_source); 171 172 // Try courgette first. Failing that, try bspatch. 173 if ((installer_state.UpdateStage(installer::ENSEMBLE_PATCHING), 174 !archive_helper->EnsemblePatch()) && 175 (installer_state.UpdateStage(installer::BINARY_PATCHING), 176 !archive_helper->BinaryPatch())) { 177 *install_status = installer::APPLY_DIFF_PATCH_FAILED; 178 installer_state.WriteInstallerResult(*install_status, 179 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, 180 NULL); 181 return false; 182 } 183 184 *archive_type = installer::INCREMENTAL_ARCHIVE_TYPE; 185 return true; 186} 187 188// In multi-install, adds all products to |installer_state| that are 189// multi-installed and must be updated along with the products already present 190// in |installer_state|. 191void AddExistingMultiInstalls(const InstallationState& original_state, 192 InstallerState* installer_state) { 193 if (installer_state->is_multi_install()) { 194 for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) { 195 BrowserDistribution::Type type = 196 static_cast<BrowserDistribution::Type>(i); 197 198 if (!installer_state->FindProduct(type)) { 199 const ProductState* state = 200 original_state.GetProductState(installer_state->system_install(), 201 type); 202 if ((state != NULL) && state->is_multi_install()) { 203 installer_state->AddProductFromState(type, *state); 204 VLOG(1) << "Product already installed and must be included: " 205 << BrowserDistribution::GetSpecificDistribution(type)-> 206 GetDisplayName(); 207 } 208 } 209 } 210 } 211} 212 213// This function is called when --rename-chrome-exe option is specified on 214// setup.exe command line. This function assumes an in-use update has happened 215// for Chrome so there should be a file called new_chrome.exe on the file 216// system and a key called 'opv' in the registry. This function will move 217// new_chrome.exe to chrome.exe and delete 'opv' key in one atomic operation. 218// This function also deletes elevation policies associated with the old version 219// if they exist. 220installer::InstallStatus RenameChromeExecutables( 221 const InstallationState& original_state, 222 InstallerState* installer_state) { 223 // See what products are already installed in multi mode. When we do the 224 // rename for multi installs, we must update all installations since they 225 // share the binaries. 226 AddExistingMultiInstalls(original_state, installer_state); 227 const base::FilePath &target_path = installer_state->target_path(); 228 base::FilePath chrome_exe(target_path.Append(installer::kChromeExe)); 229 base::FilePath chrome_new_exe(target_path.Append(installer::kChromeNewExe)); 230 base::FilePath chrome_old_exe(target_path.Append(installer::kChromeOldExe)); 231 232 // Create a temporary backup directory on the same volume as chrome.exe so 233 // that moving in-use files doesn't lead to trouble. 234 installer::SelfCleaningTempDir temp_path; 235 if (!temp_path.Initialize(target_path.DirName(), 236 installer::kInstallTempDir)) { 237 PLOG(ERROR) << "Failed to create Temp directory " 238 << target_path.DirName() 239 .Append(installer::kInstallTempDir).value(); 240 return installer::RENAME_FAILED; 241 } 242 scoped_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList()); 243 // Move chrome.exe to old_chrome.exe, then move new_chrome.exe to chrome.exe. 244 install_list->AddMoveTreeWorkItem(chrome_exe.value(), 245 chrome_old_exe.value(), 246 temp_path.path().value(), 247 WorkItem::ALWAYS_MOVE); 248 install_list->AddMoveTreeWorkItem(chrome_new_exe.value(), 249 chrome_exe.value(), 250 temp_path.path().value(), 251 WorkItem::ALWAYS_MOVE); 252 install_list->AddDeleteTreeWorkItem(chrome_new_exe, temp_path.path()); 253 // old_chrome.exe is still in use in most cases, so ignore failures here. 254 install_list->AddDeleteTreeWorkItem(chrome_old_exe, temp_path.path())-> 255 set_ignore_failure(true); 256 257 // Add work items to delete the "opv", "cpv", and "cmd" values from all 258 // products we're operating on (which including the multi-install binaries). 259 const Products& products = installer_state->products(); 260 HKEY reg_root = installer_state->root_key(); 261 base::string16 version_key; 262 for (Products::const_iterator it = products.begin(); it < products.end(); 263 ++it) { 264 version_key = (*it)->distribution()->GetVersionKey(); 265 install_list->AddDeleteRegValueWorkItem( 266 reg_root, version_key, google_update::kRegOldVersionField); 267 install_list->AddDeleteRegValueWorkItem( 268 reg_root, version_key, google_update::kRegCriticalVersionField); 269 install_list->AddDeleteRegValueWorkItem( 270 reg_root, version_key, google_update::kRegRenameCmdField); 271 } 272 installer::InstallStatus ret = installer::RENAME_SUCCESSFUL; 273 if (!install_list->Do()) { 274 LOG(ERROR) << "Renaming of executables failed. Rolling back any changes."; 275 install_list->Rollback(); 276 ret = installer::RENAME_FAILED; 277 } 278 // temp_path's dtor will take care of deleting or scheduling itself for 279 // deletion at reboot when this scope closes. 280 VLOG(1) << "Deleting temporary directory " << temp_path.path().value(); 281 282 return ret; 283} 284 285// For each product that is being updated (i.e., already installed at an earlier 286// version), see if that product has an update policy override that differs from 287// that for the binaries. If any are found, fail with an error indicating that 288// the Group Policy settings are in an inconsistent state. Do not do this test 289// for same-version installs, since it would be unkind to block attempts to 290// repair a corrupt installation. This function returns false when installation 291// should be halted, in which case |status| contains the relevant exit code and 292// the proper installer result has been written to the registry. 293bool CheckGroupPolicySettings(const InstallationState& original_state, 294 const InstallerState& installer_state, 295 const Version& new_version, 296 installer::InstallStatus* status) { 297#if !defined(GOOGLE_CHROME_BUILD) 298 // Chromium builds are not updated via Google Update, so there are no 299 // Group Policy settings to consult. 300 return true; 301#else 302 DCHECK(status); 303 304 // Single installs are always in good shape. 305 if (!installer_state.is_multi_install()) 306 return true; 307 308 bool settings_are_valid = true; 309 const bool is_system_install = installer_state.system_install(); 310 BrowserDistribution* const binaries_dist = 311 installer_state.multi_package_binaries_distribution(); 312 313 // Get the update policy for the binaries. 314 const GoogleUpdateSettings::UpdatePolicy binaries_policy = 315 GoogleUpdateSettings::GetAppUpdatePolicy(binaries_dist->GetAppGuid(), 316 NULL); 317 318 // Check for differing update policies for all of the products being updated. 319 const Products& products = installer_state.products(); 320 Products::const_iterator scan = products.begin(); 321 for (Products::const_iterator end = products.end(); scan != end; ++scan) { 322 BrowserDistribution* dist = (*scan)->distribution(); 323 const ProductState* product_state = 324 original_state.GetProductState(is_system_install, dist->GetType()); 325 // Is an earlier version of this product already installed? 326 if (product_state != NULL && 327 product_state->version().CompareTo(new_version) < 0) { 328 bool is_overridden = false; 329 GoogleUpdateSettings::UpdatePolicy app_policy = 330 GoogleUpdateSettings::GetAppUpdatePolicy(dist->GetAppGuid(), 331 &is_overridden); 332 if (is_overridden && app_policy != binaries_policy) { 333 LOG(ERROR) << "Found legacy Group Policy setting for " 334 << dist->GetDisplayName() << " (value: " << app_policy 335 << ") that does not match the setting for " 336 << binaries_dist->GetDisplayName() 337 << " (value: " << binaries_policy << ")."; 338 settings_are_valid = false; 339 } 340 } 341 } 342 343 if (!settings_are_valid) { 344 // TODO(grt): add " See http://goo.gl/+++ for details." to the end of this 345 // log message and to the IDS_INSTALL_INCONSISTENT_UPDATE_POLICY string once 346 // we have a help center article that explains why this error is being 347 // reported and how to resolve it. 348 LOG(ERROR) << "Cannot apply update on account of inconsistent " 349 "Google Update Group Policy settings. Use the Group Policy " 350 "Editor to set the update policy override for the " 351 << binaries_dist->GetDisplayName() 352 << " application and try again."; 353 *status = installer::INCONSISTENT_UPDATE_POLICY; 354 installer_state.WriteInstallerResult( 355 *status, IDS_INSTALL_INCONSISTENT_UPDATE_POLICY_BASE, NULL); 356 } 357 358 return settings_are_valid; 359#endif // defined(GOOGLE_CHROME_BUILD) 360} 361 362// If only the binaries are being updated, fail. 363// If any product is being installed in single-mode that already exists in 364// multi-mode, fail. 365bool CheckMultiInstallConditions(const InstallationState& original_state, 366 InstallerState* installer_state, 367 installer::InstallStatus* status) { 368 const Products& products = installer_state->products(); 369 DCHECK(products.size()); 370 371 const bool system_level = installer_state->system_install(); 372 373 if (installer_state->is_multi_install()) { 374 const Product* chrome = 375 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER); 376 const Product* app_host = 377 installer_state->FindProduct(BrowserDistribution::CHROME_APP_HOST); 378 const Product* binaries = 379 installer_state->FindProduct(BrowserDistribution::CHROME_BINARIES); 380 const ProductState* chrome_state = 381 original_state.GetProductState(system_level, 382 BrowserDistribution::CHROME_BROWSER); 383 384 if (binaries) { 385 if (products.size() == 1) { 386 // There are no products aside from the binaries, so there is no update 387 // to be applied. This can happen after multi-install Chrome Frame is 388 // migrated to single-install. This is treated as an update failure 389 // unless the binaries are not in-use, in which case they will be 390 // uninstalled and success will be reported (see handling in wWinMain). 391 VLOG(1) << "No products to be updated."; 392 *status = installer::UNUSED_BINARIES; 393 installer_state->WriteInstallerResult(*status, 0, NULL); 394 return false; 395 } 396 } else { 397 // This will only be hit if --multi-install is given with no products, or 398 // if the app host is being installed and doesn't need the binaries at 399 // user-level. 400 // The former case might be due to a request by an orphaned Application 401 // Host to re-install the binaries. Thus we add them to the installation. 402 // The latter case is fine and we let it be. 403 // If this is not an app host install and the binaries are not already 404 // present, the installation will fail later due to a lack of products to 405 // install. 406 if (app_host && !chrome && !chrome_state) { 407 DCHECK(!system_level); 408 // App Host may use Chrome/Chrome binaries at system-level. 409 if (original_state.GetProductState( 410 true, // system 411 BrowserDistribution::CHROME_BROWSER) || 412 original_state.GetProductState( 413 true, // system 414 BrowserDistribution::CHROME_BINARIES)) { 415 VLOG(1) << "Installing/updating App Launcher without binaries."; 416 } else { 417 // Somehow the binaries were present when the quick-enable app host 418 // command was run, but now they appear to be missing. 419 // Force binaries to be installed/updated. 420 scoped_ptr<Product> binaries_to_add(new Product( 421 BrowserDistribution::GetSpecificDistribution( 422 BrowserDistribution::CHROME_BINARIES))); 423 binaries_to_add->SetOption(installer::kOptionMultiInstall, true); 424 binaries = installer_state->AddProduct(&binaries_to_add); 425 VLOG(1) << 426 "Adding binaries for pre-existing App Launcher installation."; 427 } 428 } 429 430 return true; 431 } 432 433 if (!chrome && chrome_state) { 434 // A product other than Chrome is being installed in multi-install mode, 435 // and Chrome is already present. Add Chrome to the set of products 436 // (making it multi-install in the process) so that it is updated, too. 437 scoped_ptr<Product> multi_chrome(new Product( 438 BrowserDistribution::GetSpecificDistribution( 439 BrowserDistribution::CHROME_BROWSER))); 440 multi_chrome->SetOption(installer::kOptionMultiInstall, true); 441 chrome = installer_state->AddProduct(&multi_chrome); 442 VLOG(1) << "Upgrading existing Chrome browser in multi-install mode."; 443 } 444 } else { 445 // This is a non-multi installation. 446 447 // Check for an existing installation of the product. 448 const ProductState* product_state = original_state.GetProductState( 449 system_level, products[0]->distribution()->GetType()); 450 if (product_state != NULL) { 451 // Block downgrades from multi-install to single-install. 452 if (product_state->is_multi_install()) { 453 LOG(ERROR) << "Multi-install " 454 << products[0]->distribution()->GetDisplayName() 455 << " exists; aborting single install."; 456 *status = installer::MULTI_INSTALLATION_EXISTS; 457 installer_state->WriteInstallerResult(*status, 458 IDS_INSTALL_MULTI_INSTALLATION_EXISTS_BASE, NULL); 459 return false; 460 } 461 } 462 } 463 464 return true; 465} 466 467// Checks app host pre-install conditions, specifically that this is a 468// user-level multi-install. When the pre-install conditions are not 469// satisfied, the result is written to the registry (via WriteInstallerResult), 470// |status| is set appropriately, and false is returned. 471bool CheckAppHostPreconditions(const InstallationState& original_state, 472 InstallerState* installer_state, 473 installer::InstallStatus* status) { 474 if (installer_state->FindProduct(BrowserDistribution::CHROME_APP_HOST)) { 475 if (!installer_state->is_multi_install()) { 476 LOG(DFATAL) << "App Launcher requires multi install"; 477 *status = installer::APP_HOST_REQUIRES_MULTI_INSTALL; 478 // No message string since there is nothing a user can do. 479 installer_state->WriteInstallerResult(*status, 0, NULL); 480 return false; 481 } 482 483 if (installer_state->system_install()) { 484 LOG(DFATAL) << "App Launcher may only be installed at user-level."; 485 *status = installer::APP_HOST_REQUIRES_USER_LEVEL; 486 // No message string since there is nothing a user can do. 487 installer_state->WriteInstallerResult(*status, 0, NULL); 488 return false; 489 } 490 } 491 492 return true; 493} 494 495// Checks for compatibility between the current state of the system and the 496// desired operation. Also applies policy that mutates the desired operation; 497// specifically, the |installer_state| object. 498// Also blocks simultaneous user-level and system-level installs. In the case 499// of trying to install user-level Chrome when system-level exists, the 500// existing system-level Chrome is launched. 501// When the pre-install conditions are not satisfied, the result is written to 502// the registry (via WriteInstallerResult), |status| is set appropriately, and 503// false is returned. 504bool CheckPreInstallConditions(const InstallationState& original_state, 505 InstallerState* installer_state, 506 installer::InstallStatus* status) { 507 if (!CheckAppHostPreconditions(original_state, installer_state, status)) { 508 DCHECK_NE(*status, installer::UNKNOWN_STATUS); 509 return false; 510 } 511 512 // See what products are already installed in multi mode. When we do multi 513 // installs, we must upgrade all installations since they share the binaries. 514 AddExistingMultiInstalls(original_state, installer_state); 515 516 if (!CheckMultiInstallConditions(original_state, installer_state, status)) { 517 DCHECK_NE(*status, installer::UNKNOWN_STATUS); 518 return false; 519 } 520 521 const Products& products = installer_state->products(); 522 if (products.empty()) { 523 // We haven't been given any products on which to operate. 524 LOG(ERROR) 525 << "Not given any products to install and no products found to update."; 526 *status = installer::CHROME_NOT_INSTALLED; 527 installer_state->WriteInstallerResult(*status, 528 IDS_INSTALL_NO_PRODUCTS_TO_UPDATE_BASE, NULL); 529 return false; 530 } 531 532 if (!installer_state->system_install()) { 533 // This is a user-level installation. Make sure that we are not installing 534 // on top of an existing system-level installation. 535 for (Products::const_iterator it = products.begin(); it < products.end(); 536 ++it) { 537 const Product& product = **it; 538 BrowserDistribution* browser_dist = product.distribution(); 539 540 // Skip over the binaries, as it's okay for them to be at both levels 541 // for different products. 542 if (browser_dist->GetType() == BrowserDistribution::CHROME_BINARIES) 543 continue; 544 545 const ProductState* user_level_product_state = 546 original_state.GetProductState(false, browser_dist->GetType()); 547 const ProductState* system_level_product_state = 548 original_state.GetProductState(true, browser_dist->GetType()); 549 550 // Allow upgrades to proceed so that out-of-date versions are not left 551 // around. 552 if (user_level_product_state) 553 continue; 554 555 // This is a new user-level install... 556 557 if (system_level_product_state) { 558 // ... and the product already exists at system-level. 559 LOG(ERROR) << "Already installed version " 560 << system_level_product_state->version().GetString() 561 << " at system-level conflicts with this one at user-level."; 562 if (product.is_chrome()) { 563 // Instruct Google Update to launch the existing system-level Chrome. 564 // There should be no error dialog. 565 base::FilePath install_path(installer::GetChromeInstallPath( 566 true, // system 567 browser_dist)); 568 if (install_path.empty()) { 569 // Give up if we failed to construct the install path. 570 *status = installer::OS_ERROR; 571 installer_state->WriteInstallerResult(*status, 572 IDS_INSTALL_OS_ERROR_BASE, 573 NULL); 574 } else { 575 *status = installer::EXISTING_VERSION_LAUNCHED; 576 base::FilePath chrome_exe = 577 install_path.Append(installer::kChromeExe); 578 CommandLine cmd(chrome_exe); 579 cmd.AppendSwitch(switches::kForceFirstRun); 580 installer_state->WriteInstallerResult(*status, 0, NULL); 581 VLOG(1) << "Launching existing system-level chrome instead."; 582 base::LaunchProcess(cmd, base::LaunchOptions(), NULL); 583 } 584 } else { 585 // It's no longer possible for |product| to be anything other than 586 // Chrome. 587 NOTREACHED(); 588 } 589 return false; 590 } 591 } 592 593 } else { // System-level install. 594 // --ensure-google-update-present is supported for user-level only. 595 // The flag is generic, but its primary use case involves App Host. 596 if (installer_state->ensure_google_update_present()) { 597 LOG(DFATAL) << "--" << installer::switches::kEnsureGoogleUpdatePresent 598 << " is supported for user-level only."; 599 *status = installer::APP_HOST_REQUIRES_USER_LEVEL; 600 // No message string since there is nothing a user can do. 601 installer_state->WriteInstallerResult(*status, 0, NULL); 602 return false; 603 } 604 } 605 606 return true; 607} 608 609// Initializes |temp_path| to "Temp" within the target directory, and 610// |unpack_path| to a random directory beginning with "source" within 611// |temp_path|. Returns false on error. 612bool CreateTemporaryAndUnpackDirectories( 613 const InstallerState& installer_state, 614 installer::SelfCleaningTempDir* temp_path, 615 base::FilePath* unpack_path) { 616 DCHECK(temp_path && unpack_path); 617 618 if (!temp_path->Initialize(installer_state.target_path().DirName(), 619 installer::kInstallTempDir)) { 620 PLOG(ERROR) << "Could not create temporary path."; 621 return false; 622 } 623 VLOG(1) << "Created path " << temp_path->path().value(); 624 625 if (!base::CreateTemporaryDirInDir(temp_path->path(), 626 installer::kInstallSourceDir, 627 unpack_path)) { 628 PLOG(ERROR) << "Could not create temporary path for unpacked archive."; 629 return false; 630 } 631 632 return true; 633} 634 635installer::InstallStatus UninstallProduct( 636 const InstallationState& original_state, 637 const InstallerState& installer_state, 638 const base::FilePath& setup_exe, 639 const CommandLine& cmd_line, 640 bool remove_all, 641 bool force_uninstall, 642 const Product& product) { 643 const ProductState* product_state = 644 original_state.GetProductState(installer_state.system_install(), 645 product.distribution()->GetType()); 646 if (product_state != NULL) { 647 VLOG(1) << "version on the system: " 648 << product_state->version().GetString(); 649 } else if (!force_uninstall) { 650 LOG(ERROR) << product.distribution()->GetDisplayName() 651 << " not found for uninstall."; 652 return installer::CHROME_NOT_INSTALLED; 653 } 654 655 return installer::UninstallProduct( 656 original_state, installer_state, setup_exe, product, remove_all, 657 force_uninstall, cmd_line); 658} 659 660installer::InstallStatus UninstallProducts( 661 const InstallationState& original_state, 662 const InstallerState& installer_state, 663 const base::FilePath& setup_exe, 664 const CommandLine& cmd_line) { 665 const Products& products = installer_state.products(); 666 667 // System-level Chrome will be launched via this command if its program gets 668 // set below. 669 CommandLine system_level_cmd(CommandLine::NO_PROGRAM); 670 671 const Product* chrome = 672 installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER); 673 if (chrome) { 674 // InstallerState::Initialize always puts Chrome first, and we rely on that 675 // here for this reason: if Chrome is in-use, the user will be prompted to 676 // confirm uninstallation. Upon cancel, we should not continue with the 677 // other products. 678 DCHECK(products[0]->is_chrome()); 679 680 if (cmd_line.HasSwitch(installer::switches::kSelfDestruct) && 681 !installer_state.system_install()) { 682 BrowserDistribution* dist = chrome->distribution(); 683 const base::FilePath system_exe_path( 684 installer::GetChromeInstallPath(true, dist) 685 .Append(installer::kChromeExe)); 686 system_level_cmd.SetProgram(system_exe_path); 687 } 688 } 689 if (installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)) { 690 // Chrome Binaries should be last; if something else is cancelled, they 691 // should stay. 692 DCHECK(products[products.size() - 1]->is_chrome_binaries()); 693 } 694 695 installer::InstallStatus install_status = installer::UNINSTALL_SUCCESSFUL; 696 installer::InstallStatus prod_status = installer::UNKNOWN_STATUS; 697 const bool force = cmd_line.HasSwitch(installer::switches::kForceUninstall); 698 const bool remove_all = !cmd_line.HasSwitch( 699 installer::switches::kDoNotRemoveSharedItems); 700 701 for (Products::const_iterator it = products.begin(); 702 install_status != installer::UNINSTALL_CANCELLED && it < products.end(); 703 ++it) { 704 prod_status = UninstallProduct(original_state, installer_state, setup_exe, 705 cmd_line, remove_all, force, **it); 706 if (prod_status != installer::UNINSTALL_SUCCESSFUL) 707 install_status = prod_status; 708 } 709 710 installer::CleanUpInstallationDirectoryAfterUninstall( 711 original_state, installer_state, setup_exe, &install_status); 712 713 // The app and vendor dirs may now be empty. Make a last-ditch attempt to 714 // delete them. 715 installer::DeleteChromeDirectoriesIfEmpty(installer_state.target_path()); 716 717 // Trigger Active Setup if it was requested for the chrome product. This needs 718 // to be done after the UninstallProduct calls as some of them might 719 // otherwise terminate the process launched by TriggerActiveSetupCommand(). 720 if (chrome && cmd_line.HasSwitch(installer::switches::kTriggerActiveSetup)) 721 InstallUtil::TriggerActiveSetupCommand(); 722 723 if (!system_level_cmd.GetProgram().empty()) 724 base::LaunchProcess(system_level_cmd, base::LaunchOptions(), NULL); 725 726 // Tell Google Update that an uninstall has taken place. 727 // Ignore the return value: success or failure of Google Update 728 // has no bearing on the success or failure of Chrome's uninstallation. 729 google_update::UninstallGoogleUpdate(installer_state.system_install()); 730 731 return install_status; 732} 733 734// Uninstall the binaries if they are the only product present and they're not 735// in-use. 736void UninstallBinariesIfUnused( 737 const InstallationState& original_state, 738 const InstallerState& installer_state, 739 installer::InstallStatus* install_status) { 740 // Early exit if the binaries are still in use. 741 if (*install_status != installer::UNUSED_BINARIES || 742 installer_state.AreBinariesInUse(original_state)) { 743 return; 744 } 745 746 LOG(INFO) << "Uninstalling unused binaries"; 747 installer_state.UpdateStage(installer::UNINSTALLING_BINARIES); 748 749 // Simulate the uninstall as coming from the installed version. 750 const ProductState* binaries_state = 751 original_state.GetProductState(installer_state.system_install(), 752 BrowserDistribution::CHROME_BINARIES); 753 const CommandLine& uninstall_cmd(binaries_state->uninstall_command()); 754 MasterPreferences uninstall_prefs(uninstall_cmd); 755 InstallerState uninstall_state; 756 uninstall_state.Initialize(uninstall_cmd, uninstall_prefs, original_state); 757 758 *install_status = UninstallProducts(original_state, uninstall_state, 759 uninstall_cmd.GetProgram(), 760 uninstall_cmd); 761 762 // Report that the binaries were uninstalled if they were. This translates 763 // into a successful install return code. 764 if (IsUninstallSuccess(*install_status)) { 765 *install_status = installer::UNUSED_BINARIES_UNINSTALLED; 766 installer_state.WriteInstallerResult(*install_status, 0, NULL); 767 } 768} 769 770installer::InstallStatus InstallProducts( 771 const InstallationState& original_state, 772 const base::FilePath& setup_exe, 773 const CommandLine& cmd_line, 774 const MasterPreferences& prefs, 775 InstallerState* installer_state, 776 base::FilePath* installer_directory) { 777 DCHECK(installer_state); 778 const bool system_install = installer_state->system_install(); 779 installer::InstallStatus install_status = installer::UNKNOWN_STATUS; 780 installer::ArchiveType archive_type = installer::UNKNOWN_ARCHIVE_TYPE; 781 bool delegated_to_existing = false; 782 installer_state->UpdateStage(installer::PRECONDITIONS); 783 // The stage provides more fine-grained information than -multifail, so remove 784 // the -multifail suffix from the Google Update "ap" value. 785 BrowserDistribution::GetSpecificDistribution(installer_state->state_type())-> 786 UpdateInstallStatus(system_install, archive_type, install_status); 787 if (CheckPreInstallConditions(original_state, installer_state, 788 &install_status)) { 789 VLOG(1) << "Installing to " << installer_state->target_path().value(); 790 install_status = InstallProductsHelper( 791 original_state, setup_exe, cmd_line, prefs, *installer_state, 792 installer_directory, &archive_type, &delegated_to_existing); 793 } else { 794 // CheckPreInstallConditions must set the status on failure. 795 DCHECK_NE(install_status, installer::UNKNOWN_STATUS); 796 } 797 798 // Delete the master preferences file if present. Note that we do not care 799 // about rollback here and we schedule for deletion on reboot if the delete 800 // fails. As such, we do not use DeleteTreeWorkItem. 801 if (cmd_line.HasSwitch(installer::switches::kInstallerData)) { 802 base::FilePath prefs_path(cmd_line.GetSwitchValuePath( 803 installer::switches::kInstallerData)); 804 if (!base::DeleteFile(prefs_path, false)) { 805 LOG(ERROR) << "Failed deleting master preferences file " 806 << prefs_path.value() 807 << ", scheduling for deletion after reboot."; 808 ScheduleFileSystemEntityForDeletion(prefs_path); 809 } 810 } 811 812 // Early exit if this setup.exe delegated to another, since that one would 813 // have taken care of UpdateInstallStatus and UpdateStage. 814 if (delegated_to_existing) 815 return install_status; 816 817 const Products& products = installer_state->products(); 818 for (Products::const_iterator it = products.begin(); it < products.end(); 819 ++it) { 820 (*it)->distribution()->UpdateInstallStatus( 821 system_install, archive_type, install_status); 822 } 823 824 UninstallBinariesIfUnused(original_state, *installer_state, &install_status); 825 826 installer_state->UpdateStage(installer::NO_STAGE); 827 return install_status; 828} 829 830installer::InstallStatus ShowEULADialog(const base::string16& inner_frame) { 831 VLOG(1) << "About to show EULA"; 832 base::string16 eula_path = installer::GetLocalizedEulaResource(); 833 if (eula_path.empty()) { 834 LOG(ERROR) << "No EULA path available"; 835 return installer::EULA_REJECTED; 836 } 837 // Newer versions of the caller pass an inner frame parameter that must 838 // be given to the html page being launched. 839 installer::EulaHTMLDialog dlg(eula_path, inner_frame); 840 installer::EulaHTMLDialog::Outcome outcome = dlg.ShowModal(); 841 if (installer::EulaHTMLDialog::REJECTED == outcome) { 842 LOG(ERROR) << "EULA rejected or EULA failure"; 843 return installer::EULA_REJECTED; 844 } 845 if (installer::EulaHTMLDialog::ACCEPTED_OPT_IN == outcome) { 846 VLOG(1) << "EULA accepted (opt-in)"; 847 return installer::EULA_ACCEPTED_OPT_IN; 848 } 849 VLOG(1) << "EULA accepted (no opt-in)"; 850 return installer::EULA_ACCEPTED; 851} 852 853// Creates the sentinel indicating that the EULA was required and has been 854// accepted. 855bool CreateEULASentinel(BrowserDistribution* dist) { 856 base::FilePath eula_sentinel; 857 if (!InstallUtil::GetSentinelFilePath(installer::kEULASentinelFile, dist, 858 &eula_sentinel)) { 859 return false; 860 } 861 862 return (base::CreateDirectory(eula_sentinel.DirName()) && 863 base::WriteFile(eula_sentinel, "", 0) != -1); 864} 865 866void ActivateMetroChrome() { 867 // Check to see if we're per-user or not. Need to do this since we may 868 // not have been invoked with --system-level even for a machine install. 869 wchar_t exe_path[MAX_PATH * 2] = {}; 870 GetModuleFileName(NULL, exe_path, arraysize(exe_path)); 871 bool is_per_user_install = InstallUtil::IsPerUserInstall(exe_path); 872 873 base::string16 app_model_id = ShellUtil::GetBrowserModelId( 874 BrowserDistribution::GetDistribution(), is_per_user_install); 875 876 base::win::ScopedComPtr<IApplicationActivationManager> activator; 877 HRESULT hr = activator.CreateInstance(CLSID_ApplicationActivationManager); 878 if (SUCCEEDED(hr)) { 879 DWORD pid = 0; 880 hr = activator->ActivateApplication( 881 app_model_id.c_str(), L"open", AO_NONE, &pid); 882 } 883 884 LOG_IF(ERROR, FAILED(hr)) << "Tried and failed to launch Metro Chrome. " 885 << "hr=" << std::hex << hr; 886} 887 888installer::InstallStatus RegisterDevChrome( 889 const InstallationState& original_state, 890 const InstallerState& installer_state, 891 const base::FilePath& setup_exe, 892 const CommandLine& cmd_line) { 893 BrowserDistribution* chrome_dist = 894 BrowserDistribution::GetSpecificDistribution( 895 BrowserDistribution::CHROME_BROWSER); 896 897 // Only proceed with registering a dev chrome if no real Chrome installation 898 // of the same distribution are present on this system. 899 const ProductState* existing_chrome = 900 original_state.GetProductState(false, 901 BrowserDistribution::CHROME_BROWSER); 902 if (!existing_chrome) { 903 existing_chrome = 904 original_state.GetProductState(true, BrowserDistribution::CHROME_BROWSER); 905 } 906 if (existing_chrome) { 907 static const wchar_t kPleaseUninstallYourChromeMessage[] = 908 L"You already have a full-installation (non-dev) of %1ls, please " 909 L"uninstall it first using Add/Remove Programs in the control panel."; 910 base::string16 name(chrome_dist->GetDisplayName()); 911 base::string16 message( 912 base::StringPrintf(kPleaseUninstallYourChromeMessage, name.c_str())); 913 914 LOG(ERROR) << "Aborting operation: another installation of " << name 915 << " was found, as a last resort (if the product is not present " 916 "in Add/Remove Programs), try executing: " 917 << existing_chrome->uninstall_command().GetCommandLineString(); 918 MessageBox(NULL, message.c_str(), NULL, MB_ICONERROR); 919 return installer::INSTALL_FAILED; 920 } 921 922 base::FilePath chrome_exe( 923 cmd_line.GetSwitchValuePath(installer::switches::kRegisterDevChrome)); 924 if (chrome_exe.empty()) 925 chrome_exe = setup_exe.DirName().Append(installer::kChromeExe); 926 if (!chrome_exe.IsAbsolute()) 927 chrome_exe = base::MakeAbsoluteFilePath(chrome_exe); 928 929 installer::InstallStatus status = installer::FIRST_INSTALL_SUCCESS; 930 if (base::PathExists(chrome_exe)) { 931 Product chrome(chrome_dist); 932 933 // Create the Start menu shortcut and pin it to the taskbar. 934 ShellUtil::ShortcutProperties shortcut_properties(ShellUtil::CURRENT_USER); 935 chrome.AddDefaultShortcutProperties(chrome_exe, &shortcut_properties); 936 shortcut_properties.set_dual_mode(true); 937 shortcut_properties.set_pin_to_taskbar(true); 938 ShellUtil::CreateOrUpdateShortcut( 939 ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR, chrome_dist, 940 shortcut_properties, ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS); 941 942 // Register Chrome at user-level and make it default. 943 scoped_ptr<WorkItemList> delegate_execute_list( 944 WorkItem::CreateWorkItemList()); 945 installer::AddDelegateExecuteWorkItems( 946 installer_state, chrome_exe.DirName(), Version(), chrome, 947 delegate_execute_list.get()); 948 delegate_execute_list->Do(); 949 if (ShellUtil::CanMakeChromeDefaultUnattended()) { 950 ShellUtil::MakeChromeDefault( 951 chrome_dist, ShellUtil::CURRENT_USER, chrome_exe.value(), true); 952 } else { 953 ShellUtil::ShowMakeChromeDefaultSystemUI(chrome_dist, chrome_exe.value()); 954 } 955 } else { 956 LOG(ERROR) << "Path not found: " << chrome_exe.value(); 957 status = installer::INSTALL_FAILED; 958 } 959 return status; 960} 961 962// This method processes any command line options that make setup.exe do 963// various tasks other than installation (renaming chrome.exe, showing eula 964// among others). This function returns true if any such command line option 965// has been found and processed (so setup.exe should exit at that point). 966bool HandleNonInstallCmdLineOptions(const InstallationState& original_state, 967 const base::FilePath& setup_exe, 968 const CommandLine& cmd_line, 969 InstallerState* installer_state, 970 int* exit_code) { 971 // TODO(gab): Add a local |status| variable which each block below sets; 972 // only determine the |exit_code| from |status| at the end (this will allow 973 // this method to validate that 974 // (!handled || status != installer::UNKNOWN_STATUS)). 975 bool handled = true; 976 // TODO(tommi): Split these checks up into functions and use a data driven 977 // map of switch->function. 978 if (cmd_line.HasSwitch(installer::switches::kUpdateSetupExe)) { 979 installer::InstallStatus status = installer::SETUP_PATCH_FAILED; 980 // If --update-setup-exe command line option is given, we apply the given 981 // patch to current exe, and store the resulting binary in the path 982 // specified by --new-setup-exe. But we need to first unpack the file 983 // given in --update-setup-exe. 984 base::ScopedTempDir temp_path; 985 if (!temp_path.CreateUniqueTempDir()) { 986 PLOG(ERROR) << "Could not create temporary path."; 987 } else { 988 base::FilePath compressed_archive(cmd_line.GetSwitchValuePath( 989 installer::switches::kUpdateSetupExe)); 990 VLOG(1) << "Opening archive " << compressed_archive.value(); 991 if (installer::ArchivePatchHelper::UncompressAndPatch( 992 temp_path.path(), 993 compressed_archive, 994 setup_exe, 995 cmd_line.GetSwitchValuePath(installer::switches::kNewSetupExe))) { 996 status = installer::NEW_VERSION_UPDATED; 997 } 998 if (!temp_path.Delete()) { 999 // PLOG would be nice, but Delete() doesn't leave a meaningful value in 1000 // the Windows last-error code. 1001 LOG(WARNING) << "Scheduling temporary path " << temp_path.path().value() 1002 << " for deletion at reboot."; 1003 ScheduleDirectoryForDeletion(temp_path.path()); 1004 } 1005 } 1006 1007 *exit_code = InstallUtil::GetInstallReturnCode(status); 1008 if (*exit_code) { 1009 LOG(WARNING) << "setup.exe patching failed."; 1010 installer_state->WriteInstallerResult( 1011 status, IDS_SETUP_PATCH_FAILED_BASE, NULL); 1012 } 1013 // We will be exiting normally, so clear the stage indicator. 1014 installer_state->UpdateStage(installer::NO_STAGE); 1015 } else if (cmd_line.HasSwitch(installer::switches::kShowEula)) { 1016 // Check if we need to show the EULA. If it is passed as a command line 1017 // then the dialog is shown and regardless of the outcome setup exits here. 1018 base::string16 inner_frame = 1019 cmd_line.GetSwitchValueNative(installer::switches::kShowEula); 1020 *exit_code = ShowEULADialog(inner_frame); 1021 1022 if (installer::EULA_REJECTED != *exit_code) { 1023 if (GoogleUpdateSettings::SetEULAConsent( 1024 original_state, BrowserDistribution::GetDistribution(), true)) { 1025 CreateEULASentinel(BrowserDistribution::GetDistribution()); 1026 } 1027 // For a metro-originated launch, we now need to launch back into metro. 1028 if (cmd_line.HasSwitch(installer::switches::kShowEulaForMetro)) 1029 ActivateMetroChrome(); 1030 } 1031 } else if (cmd_line.HasSwitch(installer::switches::kConfigureUserSettings)) { 1032 // NOTE: Should the work done here, on kConfigureUserSettings, change: 1033 // kActiveSetupVersion in install_worker.cc needs to be increased for Active 1034 // Setup to invoke this again for all users of this install. 1035 const Product* chrome_install = 1036 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER); 1037 installer::InstallStatus status = installer::INVALID_STATE_FOR_OPTION; 1038 if (chrome_install && installer_state->system_install()) { 1039 bool force = 1040 cmd_line.HasSwitch(installer::switches::kForceConfigureUserSettings); 1041 installer::HandleActiveSetupForBrowser(installer_state->target_path(), 1042 *chrome_install, force); 1043 status = installer::INSTALL_REPAIRED; 1044 } else { 1045 LOG(DFATAL) << "chrome_install:" << chrome_install 1046 << ", system_install:" << installer_state->system_install(); 1047 } 1048 *exit_code = InstallUtil::GetInstallReturnCode(status); 1049 } else if (cmd_line.HasSwitch(installer::switches::kRegisterDevChrome)) { 1050 installer::InstallStatus status = RegisterDevChrome( 1051 original_state, *installer_state, setup_exe, cmd_line); 1052 *exit_code = InstallUtil::GetInstallReturnCode(status); 1053 } else if (cmd_line.HasSwitch(installer::switches::kRegisterChromeBrowser)) { 1054 installer::InstallStatus status = installer::UNKNOWN_STATUS; 1055 const Product* chrome_install = 1056 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER); 1057 if (chrome_install) { 1058 // If --register-chrome-browser option is specified, register all 1059 // Chrome protocol/file associations, as well as register it as a valid 1060 // browser for Start Menu->Internet shortcut. This switch will also 1061 // register Chrome as a valid handler for a set of URL protocols that 1062 // Chrome may become the default handler for, either by the user marking 1063 // Chrome as the default browser, through the Windows Default Programs 1064 // control panel settings, or through website use of 1065 // registerProtocolHandler. These protocols are found in 1066 // ShellUtil::kPotentialProtocolAssociations. 1067 // The --register-url-protocol will additionally register Chrome as a 1068 // potential handler for the supplied protocol, and is used if a website 1069 // registers a handler for a protocol not found in 1070 // ShellUtil::kPotentialProtocolAssociations. 1071 // These options should only be used when setup.exe is launched with admin 1072 // rights. We do not make any user specific changes with this option. 1073 DCHECK(IsUserAnAdmin()); 1074 base::string16 chrome_exe(cmd_line.GetSwitchValueNative( 1075 installer::switches::kRegisterChromeBrowser)); 1076 base::string16 suffix; 1077 if (cmd_line.HasSwitch( 1078 installer::switches::kRegisterChromeBrowserSuffix)) { 1079 suffix = cmd_line.GetSwitchValueNative( 1080 installer::switches::kRegisterChromeBrowserSuffix); 1081 } 1082 if (cmd_line.HasSwitch(installer::switches::kRegisterURLProtocol)) { 1083 base::string16 protocol = cmd_line.GetSwitchValueNative( 1084 installer::switches::kRegisterURLProtocol); 1085 // ShellUtil::RegisterChromeForProtocol performs all registration 1086 // done by ShellUtil::RegisterChromeBrowser, as well as registering 1087 // with Windows as capable of handling the supplied protocol. 1088 if (ShellUtil::RegisterChromeForProtocol( 1089 chrome_install->distribution(), chrome_exe, suffix, protocol, 1090 false)) 1091 status = installer::IN_USE_UPDATED; 1092 } else { 1093 if (ShellUtil::RegisterChromeBrowser(chrome_install->distribution(), 1094 chrome_exe, suffix, false)) 1095 status = installer::IN_USE_UPDATED; 1096 } 1097 } else { 1098 LOG(DFATAL) << "Can't register browser - Chrome distribution not found"; 1099 } 1100 *exit_code = InstallUtil::GetInstallReturnCode(status); 1101 } else if (cmd_line.HasSwitch(installer::switches::kRenameChromeExe)) { 1102 // If --rename-chrome-exe is specified, we want to rename the executables 1103 // and exit. 1104 *exit_code = RenameChromeExecutables(original_state, installer_state); 1105 } else if (cmd_line.HasSwitch( 1106 installer::switches::kRemoveChromeRegistration)) { 1107 // This is almost reverse of --register-chrome-browser option above. 1108 // Here we delete Chrome browser registration. This option should only 1109 // be used when setup.exe is launched with admin rights. We do not 1110 // make any user specific changes in this option. 1111 base::string16 suffix; 1112 if (cmd_line.HasSwitch( 1113 installer::switches::kRegisterChromeBrowserSuffix)) { 1114 suffix = cmd_line.GetSwitchValueNative( 1115 installer::switches::kRegisterChromeBrowserSuffix); 1116 } 1117 installer::InstallStatus tmp = installer::UNKNOWN_STATUS; 1118 const Product* chrome_install = 1119 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER); 1120 DCHECK(chrome_install); 1121 if (chrome_install) { 1122 installer::DeleteChromeRegistrationKeys(*installer_state, 1123 chrome_install->distribution(), HKEY_LOCAL_MACHINE, suffix, &tmp); 1124 } 1125 *exit_code = tmp; 1126 } else if (cmd_line.HasSwitch(installer::switches::kOnOsUpgrade)) { 1127 const Product* chrome_install = 1128 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER); 1129 installer::InstallStatus status = installer::INVALID_STATE_FOR_OPTION; 1130 if (chrome_install) { 1131 installer::HandleOsUpgradeForBrowser(*installer_state, 1132 *chrome_install); 1133 status = installer::INSTALL_REPAIRED; 1134 } else { 1135 LOG(DFATAL) << "Chrome product not found."; 1136 } 1137 *exit_code = InstallUtil::GetInstallReturnCode(status); 1138 } else if (cmd_line.HasSwitch(installer::switches::kQueryEULAAcceptance)) { 1139 *exit_code = installer::IsEULAAccepted(installer_state->system_install()); 1140 } else if (cmd_line.HasSwitch(installer::switches::kInactiveUserToast)) { 1141 // Launch the inactive user toast experiment. 1142 int flavor = -1; 1143 base::StringToInt(cmd_line.GetSwitchValueNative( 1144 installer::switches::kInactiveUserToast), &flavor); 1145 std::string experiment_group = 1146 cmd_line.GetSwitchValueASCII(installer::switches::kExperimentGroup); 1147 DCHECK_NE(-1, flavor); 1148 if (flavor == -1) { 1149 *exit_code = installer::UNKNOWN_STATUS; 1150 } else { 1151 // This code is called (via setup.exe relaunch) only if a product is known 1152 // to run user experiments, so no check is required. 1153 const Products& products = installer_state->products(); 1154 for (Products::const_iterator it = products.begin(); it < products.end(); 1155 ++it) { 1156 const Product& product = **it; 1157 installer::InactiveUserToastExperiment( 1158 flavor, base::ASCIIToUTF16(experiment_group), product, 1159 installer_state->target_path()); 1160 } 1161 } 1162 } else if (cmd_line.HasSwitch(installer::switches::kSystemLevelToast)) { 1163 const Products& products = installer_state->products(); 1164 for (Products::const_iterator it = products.begin(); it < products.end(); 1165 ++it) { 1166 const Product& product = **it; 1167 BrowserDistribution* browser_dist = product.distribution(); 1168 // We started as system-level and have been re-launched as user level 1169 // to continue with the toast experiment. 1170 Version installed_version; 1171 InstallUtil::GetChromeVersion(browser_dist, true, &installed_version); 1172 if (!installed_version.IsValid()) { 1173 LOG(ERROR) << "No installation of " 1174 << browser_dist->GetDisplayName() 1175 << " found for system-level toast."; 1176 } else { 1177 product.LaunchUserExperiment( 1178 setup_exe, installer::REENTRY_SYS_UPDATE, true); 1179 } 1180 } 1181 } else if (cmd_line.HasSwitch(installer::switches::kPatch)) { 1182 const std::string patch_type_str( 1183 cmd_line.GetSwitchValueASCII(installer::switches::kPatch)); 1184 const base::FilePath input_file( 1185 cmd_line.GetSwitchValuePath(installer::switches::kInputFile)); 1186 const base::FilePath patch_file( 1187 cmd_line.GetSwitchValuePath(installer::switches::kPatchFile)); 1188 const base::FilePath output_file( 1189 cmd_line.GetSwitchValuePath(installer::switches::kOutputFile)); 1190 1191 if (patch_type_str == installer::kCourgette) { 1192 *exit_code = installer::CourgettePatchFiles(input_file, 1193 patch_file, 1194 output_file); 1195 } else if (patch_type_str == installer::kBsdiff) { 1196 *exit_code = installer::BsdiffPatchFiles(input_file, 1197 patch_file, 1198 output_file); 1199 } else { 1200 *exit_code = installer::PATCH_INVALID_ARGUMENTS; 1201 } 1202 } else { 1203 handled = false; 1204 } 1205 1206 return handled; 1207} 1208 1209bool ShowRebootDialog() { 1210 // Get a token for this process. 1211 HANDLE token; 1212 if (!OpenProcessToken(GetCurrentProcess(), 1213 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 1214 &token)) { 1215 LOG(ERROR) << "Failed to open token."; 1216 return false; 1217 } 1218 1219 // Use a ScopedHandle to keep track of and eventually close our handle. 1220 // TODO(robertshield): Add a Receive() method to base's ScopedHandle. 1221 base::win::ScopedHandle scoped_handle(token); 1222 1223 // Get the LUID for the shutdown privilege. 1224 TOKEN_PRIVILEGES tkp = {0}; 1225 LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid); 1226 tkp.PrivilegeCount = 1; 1227 tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 1228 1229 // Get the shutdown privilege for this process. 1230 AdjustTokenPrivileges(token, FALSE, &tkp, 0, 1231 reinterpret_cast<PTOKEN_PRIVILEGES>(NULL), 0); 1232 if (GetLastError() != ERROR_SUCCESS) { 1233 LOG(ERROR) << "Unable to get shutdown privileges."; 1234 return false; 1235 } 1236 1237 // Popup a dialog that will prompt to reboot using the default system message. 1238 // TODO(robertshield): Add a localized, more specific string to the prompt. 1239 RestartDialog(NULL, NULL, EWX_REBOOT | EWX_FORCEIFHUNG); 1240 return true; 1241} 1242 1243// Returns the Custom information for the client identified by the exe path 1244// passed in. This information is used for crash reporting. 1245google_breakpad::CustomClientInfo* GetCustomInfo(const wchar_t* exe_path) { 1246 base::string16 product; 1247 base::string16 version; 1248 scoped_ptr<FileVersionInfo> version_info( 1249 FileVersionInfo::CreateFileVersionInfo(base::FilePath(exe_path))); 1250 if (version_info.get()) { 1251 version = version_info->product_version(); 1252 product = version_info->product_short_name(); 1253 } 1254 1255 if (version.empty()) 1256 version = L"0.1.0.0"; 1257 1258 if (product.empty()) 1259 product = L"Chrome Installer"; 1260 1261 static google_breakpad::CustomInfoEntry ver_entry(L"ver", version.c_str()); 1262 static google_breakpad::CustomInfoEntry prod_entry(L"prod", product.c_str()); 1263 static google_breakpad::CustomInfoEntry plat_entry(L"plat", L"Win32"); 1264 static google_breakpad::CustomInfoEntry type_entry(L"ptype", 1265 L"Chrome Installer"); 1266 static google_breakpad::CustomInfoEntry entries[] = { 1267 ver_entry, prod_entry, plat_entry, type_entry }; 1268 static google_breakpad::CustomClientInfo custom_info = { 1269 entries, arraysize(entries) }; 1270 return &custom_info; 1271} 1272 1273// Initialize crash reporting for this process. This involves connecting to 1274// breakpad, etc. 1275scoped_ptr<google_breakpad::ExceptionHandler> InitializeCrashReporting( 1276 bool system_install) { 1277 // Only report crashes if the user allows it. 1278 if (!GoogleUpdateSettings::GetCollectStatsConsent()) 1279 return scoped_ptr<google_breakpad::ExceptionHandler>(); 1280 1281 // Get the alternate dump directory. We use the temp path. 1282 base::FilePath temp_directory; 1283 if (!base::GetTempDir(&temp_directory) || temp_directory.empty()) 1284 return scoped_ptr<google_breakpad::ExceptionHandler>(); 1285 1286 wchar_t exe_path[MAX_PATH * 2] = {0}; 1287 GetModuleFileName(NULL, exe_path, arraysize(exe_path)); 1288 1289 // Build the pipe name. It can be either: 1290 // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18" 1291 // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>" 1292 base::string16 user_sid = kSystemPrincipalSid; 1293 1294 if (!system_install) { 1295 if (!base::win::GetUserSidString(&user_sid)) { 1296 return scoped_ptr<google_breakpad::ExceptionHandler>(); 1297 } 1298 } 1299 1300 base::string16 pipe_name = kGoogleUpdatePipeName; 1301 pipe_name += user_sid; 1302 1303 return scoped_ptr<google_breakpad::ExceptionHandler>( 1304 new google_breakpad::ExceptionHandler( 1305 temp_directory.value(), NULL, NULL, NULL, 1306 google_breakpad::ExceptionHandler::HANDLER_ALL, kLargerDumpType, 1307 pipe_name.c_str(), GetCustomInfo(exe_path))); 1308} 1309 1310// Uninstalls multi-install Chrome Frame if the current operation is a 1311// multi-install install or update. The operation is performed directly rather 1312// than delegated to the existing install since there is no facility in older 1313// versions of setup.exe to uninstall GCF without touching the binaries. The 1314// binaries will be uninstalled during later processing if they are not in-use 1315// (see UninstallBinariesIfUnused). |original_state| and |installer_state| are 1316// updated to reflect the state of the world following the operation. 1317void UninstallMultiChromeFrameIfPresent(const CommandLine& cmd_line, 1318 const MasterPreferences& prefs, 1319 InstallationState* original_state, 1320 InstallerState* installer_state) { 1321 // Early exit if not installing or updating multi-install product(s). 1322 if (installer_state->operation() != InstallerState::MULTI_INSTALL && 1323 installer_state->operation() != InstallerState::MULTI_UPDATE) { 1324 return; 1325 } 1326 1327 // Early exit if Chrome Frame is not present as multi-install. 1328 const ProductState* chrome_frame_state = 1329 original_state->GetProductState(installer_state->system_install(), 1330 BrowserDistribution::CHROME_FRAME); 1331 if (!chrome_frame_state || !chrome_frame_state->is_multi_install()) 1332 return; 1333 1334 LOG(INFO) << "Uninstalling multi-install Chrome Frame."; 1335 installer_state->UpdateStage(installer::UNINSTALLING_CHROME_FRAME); 1336 1337 // Uninstall Chrome Frame without touching the multi-install binaries. 1338 // Simulate the uninstall as coming from the installed version. 1339 const CommandLine& uninstall_cmd(chrome_frame_state->uninstall_command()); 1340 MasterPreferences uninstall_prefs(uninstall_cmd); 1341 InstallerState uninstall_state; 1342 uninstall_state.Initialize(uninstall_cmd, uninstall_prefs, *original_state); 1343 const Product* chrome_frame_product = uninstall_state.FindProduct( 1344 BrowserDistribution::CHROME_FRAME); 1345 if (chrome_frame_product) { 1346 // No shared state should be left behind. 1347 const bool remove_all = true; 1348 // Don't accept no for an answer. 1349 const bool force_uninstall = true; 1350 installer::InstallStatus uninstall_status = 1351 installer::UninstallProduct(*original_state, uninstall_state, 1352 uninstall_cmd.GetProgram(), 1353 *chrome_frame_product, remove_all, 1354 force_uninstall, cmd_line); 1355 1356 VLOG(1) << "Uninstallation of Chrome Frame returned status " 1357 << uninstall_status; 1358 } else { 1359 LOG(ERROR) << "Chrome Frame not found for uninstall."; 1360 } 1361 1362 // Refresh state for the continuation of the original install/update. 1363 original_state->Initialize(); 1364 installer_state->Initialize(cmd_line, prefs, *original_state); 1365} 1366 1367} // namespace 1368 1369namespace installer { 1370 1371InstallStatus InstallProductsHelper( 1372 const InstallationState& original_state, 1373 const base::FilePath& setup_exe, 1374 const CommandLine& cmd_line, 1375 const MasterPreferences& prefs, 1376 const InstallerState& installer_state, 1377 base::FilePath* installer_directory, 1378 ArchiveType* archive_type, 1379 bool* delegated_to_existing) { 1380 DCHECK(archive_type); 1381 DCHECK(delegated_to_existing); 1382 const bool system_install = installer_state.system_install(); 1383 InstallStatus install_status = UNKNOWN_STATUS; 1384 1385 // Drop to background processing mode if the process was started below the 1386 // normal process priority class. 1387 bool entered_background_mode = AdjustProcessPriority(); 1388 VLOG_IF(1, entered_background_mode) << "Entered background processing mode."; 1389 1390 // Create a temp folder where we will unpack Chrome archive. If it fails, 1391 // then we are doomed, so return immediately and no cleanup is required. 1392 SelfCleaningTempDir temp_path; 1393 base::FilePath unpack_path; 1394 if (!CreateTemporaryAndUnpackDirectories(installer_state, &temp_path, 1395 &unpack_path)) { 1396 installer_state.WriteInstallerResult(TEMP_DIR_FAILED, 1397 IDS_INSTALL_TEMP_DIR_FAILED_BASE, 1398 NULL); 1399 return TEMP_DIR_FAILED; 1400 } 1401 1402 // Uncompress and optionally patch the archive if an uncompressed archive was 1403 // not specified on the command line and a compressed archive is found. 1404 *archive_type = UNKNOWN_ARCHIVE_TYPE; 1405 base::FilePath uncompressed_archive(cmd_line.GetSwitchValuePath( 1406 switches::kUncompressedArchive)); 1407 if (uncompressed_archive.empty()) { 1408 scoped_ptr<ArchivePatchHelper> archive_helper( 1409 CreateChromeArchiveHelper(setup_exe, cmd_line, installer_state, 1410 unpack_path)); 1411 if (archive_helper) { 1412 VLOG(1) << "Installing Chrome from compressed archive " 1413 << archive_helper->compressed_archive().value(); 1414 if (!UncompressAndPatchChromeArchive(original_state, 1415 installer_state, 1416 archive_helper.get(), 1417 archive_type, 1418 &install_status)) { 1419 DCHECK_NE(install_status, UNKNOWN_STATUS); 1420 return install_status; 1421 } 1422 uncompressed_archive = archive_helper->target(); 1423 DCHECK(!uncompressed_archive.empty()); 1424 } 1425 } 1426 1427 // Check for an uncompressed archive alongside the current executable if one 1428 // was not given or generated. 1429 if (uncompressed_archive.empty()) 1430 uncompressed_archive = setup_exe.DirName().Append(kChromeArchive); 1431 1432 if (*archive_type == UNKNOWN_ARCHIVE_TYPE) { 1433 // An archive was not uncompressed or patched above. 1434 if (uncompressed_archive.empty() || 1435 !base::PathExists(uncompressed_archive)) { 1436 LOG(ERROR) << "Cannot install Chrome without an uncompressed archive."; 1437 installer_state.WriteInstallerResult( 1438 INVALID_ARCHIVE, IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL); 1439 return INVALID_ARCHIVE; 1440 } 1441 *archive_type = FULL_ARCHIVE_TYPE; 1442 } 1443 1444 // Unpack the uncompressed archive. 1445 if (LzmaUtil::UnPackArchive(uncompressed_archive.value(), 1446 unpack_path.value(), 1447 NULL)) { 1448 installer_state.WriteInstallerResult( 1449 UNCOMPRESSION_FAILED, 1450 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, 1451 NULL); 1452 return UNCOMPRESSION_FAILED; 1453 } 1454 1455 VLOG(1) << "unpacked to " << unpack_path.value(); 1456 base::FilePath src_path( 1457 unpack_path.Append(kInstallSourceChromeDir)); 1458 scoped_ptr<Version> 1459 installer_version(GetMaxVersionFromArchiveDir(src_path)); 1460 if (!installer_version.get()) { 1461 LOG(ERROR) << "Did not find any valid version in installer."; 1462 install_status = INVALID_ARCHIVE; 1463 installer_state.WriteInstallerResult(install_status, 1464 IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL); 1465 } else { 1466 VLOG(1) << "version to install: " << installer_version->GetString(); 1467 bool proceed_with_installation = true; 1468 1469 if (installer_state.operation() == InstallerState::MULTI_INSTALL) { 1470 // This is a new install of a multi-install product. Rather than give up 1471 // in case a higher version of the binaries (including a single-install 1472 // of Chrome, which can safely be migrated to multi-install by way of 1473 // CheckMultiInstallConditions) is already installed, delegate to the 1474 // installed setup.exe to install the product at hand. 1475 base::FilePath existing_setup_exe; 1476 if (GetExistingHigherInstaller(original_state, system_install, 1477 *installer_version, &existing_setup_exe)) { 1478 VLOG(1) << "Deferring to existing installer."; 1479 installer_state.UpdateStage(DEFERRING_TO_HIGHER_VERSION); 1480 if (DeferToExistingInstall(existing_setup_exe, cmd_line, 1481 installer_state, temp_path.path(), 1482 &install_status)) { 1483 *delegated_to_existing = true; 1484 return install_status; 1485 } 1486 } 1487 } 1488 1489 uint32 higher_products = 0; 1490 COMPILE_ASSERT( 1491 sizeof(higher_products) * 8 > BrowserDistribution::NUM_TYPES, 1492 too_many_distribution_types_); 1493 const Products& products = installer_state.products(); 1494 for (Products::const_iterator it = products.begin(); it < products.end(); 1495 ++it) { 1496 const Product& product = **it; 1497 const ProductState* product_state = 1498 original_state.GetProductState(system_install, 1499 product.distribution()->GetType()); 1500 if (product_state != NULL && 1501 (product_state->version().CompareTo(*installer_version) > 0)) { 1502 LOG(ERROR) << "Higher version of " 1503 << product.distribution()->GetDisplayName() 1504 << " is already installed."; 1505 higher_products |= (1 << product.distribution()->GetType()); 1506 } 1507 } 1508 1509 if (higher_products != 0) { 1510 COMPILE_ASSERT(BrowserDistribution::NUM_TYPES == 4, 1511 add_support_for_new_products_here_); 1512 const uint32 kBrowserBit = 1 << BrowserDistribution::CHROME_BROWSER; 1513 const uint32 kAppHostBit = 1 << BrowserDistribution::CHROME_APP_HOST; 1514 int message_id = 0; 1515 1516 proceed_with_installation = false; 1517 install_status = HIGHER_VERSION_EXISTS; 1518 switch (higher_products) { 1519 case kBrowserBit: 1520 message_id = IDS_INSTALL_HIGHER_VERSION_BASE; 1521 break; 1522 default: 1523 message_id = IDS_INSTALL_HIGHER_VERSION_APP_LAUNCHER_BASE; 1524 break; 1525 } 1526 1527 installer_state.WriteInstallerResult(install_status, message_id, NULL); 1528 } 1529 1530 proceed_with_installation = 1531 proceed_with_installation && 1532 CheckGroupPolicySettings(original_state, installer_state, 1533 *installer_version, &install_status); 1534 1535 if (proceed_with_installation) { 1536 // If Google Update is absent at user-level, install it using the 1537 // Google Update installer from an existing system-level installation. 1538 // This is for quick-enable App Host install from a system-level 1539 // Chrome Binaries installation. 1540 if (!system_install && installer_state.ensure_google_update_present()) { 1541 if (!google_update::EnsureUserLevelGoogleUpdatePresent()) { 1542 LOG(ERROR) << "Failed to install Google Update"; 1543 proceed_with_installation = false; 1544 install_status = INSTALL_OF_GOOGLE_UPDATE_FAILED; 1545 installer_state.WriteInstallerResult(install_status, 0, NULL); 1546 } 1547 } 1548 1549 } 1550 1551 if (proceed_with_installation) { 1552 base::FilePath prefs_source_path(cmd_line.GetSwitchValueNative( 1553 switches::kInstallerData)); 1554 install_status = InstallOrUpdateProduct( 1555 original_state, installer_state, setup_exe, uncompressed_archive, 1556 temp_path.path(), src_path, prefs_source_path, prefs, 1557 *installer_version); 1558 1559 int install_msg_base = IDS_INSTALL_FAILED_BASE; 1560 base::string16 chrome_exe; 1561 base::string16 quoted_chrome_exe; 1562 if (install_status == SAME_VERSION_REPAIR_FAILED) { 1563 install_msg_base = IDS_SAME_VERSION_REPAIR_FAILED_BASE; 1564 } else if (install_status != INSTALL_FAILED) { 1565 if (installer_state.target_path().empty()) { 1566 // If we failed to construct install path, it means the OS call to 1567 // get %ProgramFiles% or %AppData% failed. Report this as failure. 1568 install_msg_base = IDS_INSTALL_OS_ERROR_BASE; 1569 install_status = OS_ERROR; 1570 } else { 1571 chrome_exe = installer_state.target_path().Append(kChromeExe).value(); 1572 quoted_chrome_exe = L"\"" + chrome_exe + L"\""; 1573 install_msg_base = 0; 1574 } 1575 } 1576 1577 installer_state.UpdateStage(FINISHING); 1578 1579 // Only do Chrome-specific stuff (like launching the browser) if 1580 // Chrome was specifically requested (rather than being upgraded as 1581 // part of a multi-install). 1582 const Product* chrome_install = prefs.install_chrome() ? 1583 installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) : 1584 NULL; 1585 1586 bool do_not_register_for_update_launch = false; 1587 if (chrome_install) { 1588 prefs.GetBool(master_preferences::kDoNotRegisterForUpdateLaunch, 1589 &do_not_register_for_update_launch); 1590 } else { 1591 do_not_register_for_update_launch = true; // Never register. 1592 } 1593 1594 bool write_chrome_launch_string = 1595 (!do_not_register_for_update_launch && 1596 install_status != IN_USE_UPDATED); 1597 1598 installer_state.WriteInstallerResult(install_status, install_msg_base, 1599 write_chrome_launch_string ? "ed_chrome_exe : NULL); 1600 1601 if (install_status == FIRST_INSTALL_SUCCESS) { 1602 VLOG(1) << "First install successful."; 1603 if (chrome_install) { 1604 // We never want to launch Chrome in system level install mode. 1605 bool do_not_launch_chrome = false; 1606 prefs.GetBool(master_preferences::kDoNotLaunchChrome, 1607 &do_not_launch_chrome); 1608 if (!system_install && !do_not_launch_chrome) 1609 chrome_install->LaunchChrome(installer_state.target_path()); 1610 } 1611 } else if ((install_status == NEW_VERSION_UPDATED) || 1612 (install_status == IN_USE_UPDATED)) { 1613 const Product* chrome = installer_state.FindProduct( 1614 BrowserDistribution::CHROME_BROWSER); 1615 if (chrome != NULL) { 1616 DCHECK_NE(chrome_exe, base::string16()); 1617 RemoveChromeLegacyRegistryKeys(chrome->distribution(), chrome_exe); 1618 } 1619 } 1620 1621 if (prefs.install_chrome_app_launcher() && 1622 InstallUtil::GetInstallReturnCode(install_status) == 0) { 1623 std::string webstore_item(google_update::GetUntrustedDataValue( 1624 kInstallFromWebstore)); 1625 if (!webstore_item.empty()) { 1626 bool success = InstallFromWebstore(webstore_item); 1627 VLOG_IF(1, !success) << "Failed to launch app installation."; 1628 } 1629 } 1630 } 1631 } 1632 1633 // There might be an experiment (for upgrade usually) that needs to happen. 1634 // An experiment's outcome can include chrome's uninstallation. If that is 1635 // the case we would not do that directly at this point but in another 1636 // instance of setup.exe 1637 // 1638 // There is another way to reach this same function if this is a system 1639 // level install. See HandleNonInstallCmdLineOptions(). 1640 { 1641 // If installation failed, use the path to the currently running setup. 1642 // If installation succeeded, use the path to setup in the installer dir. 1643 base::FilePath setup_path(setup_exe); 1644 if (InstallUtil::GetInstallReturnCode(install_status) == 0) { 1645 setup_path = installer_state.GetInstallerDirectory(*installer_version) 1646 .Append(setup_path.BaseName()); 1647 } 1648 const Products& products = installer_state.products(); 1649 for (Products::const_iterator it = products.begin(); it < products.end(); 1650 ++it) { 1651 const Product& product = **it; 1652 product.LaunchUserExperiment(setup_path, install_status, system_install); 1653 } 1654 } 1655 1656 // If installation completed successfully, return the path to the directory 1657 // containing the newly installed setup.exe and uncompressed archive if the 1658 // caller requested it. 1659 if (installer_directory && 1660 InstallUtil::GetInstallReturnCode(install_status) == 0) { 1661 *installer_directory = 1662 installer_state.GetInstallerDirectory(*installer_version); 1663 } 1664 1665 // temp_path's dtor will take care of deleting or scheduling itself for 1666 // deletion at reboot when this scope closes. 1667 VLOG(1) << "Deleting temporary directory " << temp_path.path().value(); 1668 1669 return install_status; 1670} 1671 1672} // namespace installer 1673 1674int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, 1675 wchar_t* command_line, int show_command) { 1676 // Check to see if the CPU is supported before doing anything else. There's 1677 // very little than can safely be accomplished if the CPU isn't supported 1678 // since dependent libraries (e.g., base) may use invalid instructions. 1679 if (!installer::IsProcessorSupported()) 1680 return installer::CPU_NOT_SUPPORTED; 1681 1682 // The exit manager is in charge of calling the dtors of singletons. 1683 base::AtExitManager exit_manager; 1684 CommandLine::Init(0, NULL); 1685 1686 const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess(); 1687 installer::InitInstallerLogging(prefs); 1688 1689 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess(); 1690 VLOG(1) << "Command Line: " << cmd_line.GetCommandLineString(); 1691 1692 VLOG(1) << "multi install is " << prefs.is_multi_install(); 1693 bool system_install = false; 1694 prefs.GetBool(installer::master_preferences::kSystemLevel, &system_install); 1695 VLOG(1) << "system install is " << system_install; 1696 1697 scoped_ptr<google_breakpad::ExceptionHandler> breakpad( 1698 InitializeCrashReporting(system_install)); 1699 1700 InstallationState original_state; 1701 original_state.Initialize(); 1702 1703 InstallerState installer_state; 1704 installer_state.Initialize(cmd_line, prefs, original_state); 1705 const bool is_uninstall = cmd_line.HasSwitch(installer::switches::kUninstall); 1706 1707 // Check to make sure current system is WinXP or later. If not, log 1708 // error message and get out. 1709 if (!InstallUtil::IsOSSupported()) { 1710 LOG(ERROR) << "Chrome only supports Windows XP or later."; 1711 installer_state.WriteInstallerResult( 1712 installer::OS_NOT_SUPPORTED, IDS_INSTALL_OS_NOT_SUPPORTED_BASE, NULL); 1713 return installer::OS_NOT_SUPPORTED; 1714 } 1715 1716 // Initialize COM for use later. 1717 base::win::ScopedCOMInitializer com_initializer; 1718 if (!com_initializer.succeeded()) { 1719 installer_state.WriteInstallerResult( 1720 installer::OS_ERROR, IDS_INSTALL_OS_ERROR_BASE, NULL); 1721 return installer::OS_ERROR; 1722 } 1723 1724 // Some command line options don't work with SxS install/uninstall 1725 if (InstallUtil::IsChromeSxSProcess()) { 1726 if (system_install || 1727 prefs.is_multi_install() || 1728 cmd_line.HasSwitch(installer::switches::kForceUninstall) || 1729 cmd_line.HasSwitch(installer::switches::kMakeChromeDefault) || 1730 cmd_line.HasSwitch(installer::switches::kRegisterChromeBrowser) || 1731 cmd_line.HasSwitch(installer::switches::kRemoveChromeRegistration) || 1732 cmd_line.HasSwitch(installer::switches::kInactiveUserToast) || 1733 cmd_line.HasSwitch(installer::switches::kSystemLevelToast)) { 1734 return installer::SXS_OPTION_NOT_SUPPORTED; 1735 } 1736 } 1737 1738 // Some command line options are no longer supported and must error out. 1739 if (installer::ContainsUnsupportedSwitch(cmd_line)) 1740 return installer::UNSUPPORTED_OPTION; 1741 1742 // A variety of installer operations require the path to the current 1743 // executable. Get it once here for use throughout these operations. Note that 1744 // the path service is the authoritative source for this path. One might think 1745 // that CommandLine::GetProgram would suffice, but it won't since 1746 // CreateProcess may have been called with a command line that is somewhat 1747 // ambiguous (e.g., an unquoted path with spaces, or a path lacking the file 1748 // extension), in which case CommandLineToArgv will not yield an argv with the 1749 // true path to the program at position 0. 1750 base::FilePath setup_exe; 1751 if (!PathService::Get(base::FILE_EXE, &setup_exe)) { 1752 NOTREACHED(); 1753 return installer::OS_ERROR; 1754 } 1755 1756 int exit_code = 0; 1757 if (HandleNonInstallCmdLineOptions( 1758 original_state, setup_exe, cmd_line, &installer_state, &exit_code)) { 1759 return exit_code; 1760 } 1761 1762 if (system_install && !IsUserAnAdmin()) { 1763 if (base::win::GetVersion() >= base::win::VERSION_VISTA && 1764 !cmd_line.HasSwitch(installer::switches::kRunAsAdmin)) { 1765 CommandLine new_cmd(CommandLine::NO_PROGRAM); 1766 new_cmd.AppendArguments(cmd_line, true); 1767 // Append --run-as-admin flag to let the new instance of setup.exe know 1768 // that we already tried to launch ourselves as admin. 1769 new_cmd.AppendSwitch(installer::switches::kRunAsAdmin); 1770 // If system_install became true due to an environment variable, append 1771 // it to the command line here since env vars may not propagate past the 1772 // elevation. 1773 if (!new_cmd.HasSwitch(installer::switches::kSystemLevel)) 1774 new_cmd.AppendSwitch(installer::switches::kSystemLevel); 1775 1776 DWORD exit_code = installer::UNKNOWN_STATUS; 1777 InstallUtil::ExecuteExeAsAdmin(new_cmd, &exit_code); 1778 return exit_code; 1779 } else { 1780 LOG(ERROR) << "Non admin user can not install system level Chrome."; 1781 installer_state.WriteInstallerResult(installer::INSUFFICIENT_RIGHTS, 1782 IDS_INSTALL_INSUFFICIENT_RIGHTS_BASE, NULL); 1783 return installer::INSUFFICIENT_RIGHTS; 1784 } 1785 } 1786 1787 UninstallMultiChromeFrameIfPresent(cmd_line, prefs, 1788 &original_state, &installer_state); 1789 1790 base::FilePath installer_directory; 1791 installer::InstallStatus install_status = installer::UNKNOWN_STATUS; 1792 // If --uninstall option is given, uninstall the identified product(s) 1793 if (is_uninstall) { 1794 install_status = 1795 UninstallProducts(original_state, installer_state, setup_exe, cmd_line); 1796 } else { 1797 // If --uninstall option is not specified, we assume it is install case. 1798 install_status = 1799 InstallProducts(original_state, setup_exe, cmd_line, prefs, 1800 &installer_state, &installer_directory); 1801 } 1802 1803 // Validate that the machine is now in a good state following the operation. 1804 // TODO(grt): change this to log at DFATAL once we're convinced that the 1805 // validator handles all cases properly. 1806 InstallationValidator::InstallationType installation_type = 1807 InstallationValidator::NO_PRODUCTS; 1808 LOG_IF(ERROR, 1809 !InstallationValidator::ValidateInstallationType(system_install, 1810 &installation_type)); 1811 1812 int return_code = 0; 1813 // MSI demands that custom actions always return 0 (ERROR_SUCCESS) or it will 1814 // rollback the action. If we're uninstalling we want to avoid this, so always 1815 // report success, squashing any more informative return codes. 1816 if (!(installer_state.is_msi() && is_uninstall)) { 1817 // Note that we allow the status installer::UNINSTALL_REQUIRES_REBOOT 1818 // to pass through, since this is only returned on uninstall which is 1819 // never invoked directly by Google Update. 1820 return_code = InstallUtil::GetInstallReturnCode(install_status); 1821 } 1822 1823 VLOG(1) << "Installation complete, returning: " << return_code; 1824 1825 return return_code; 1826} 1827