installer_state.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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/installer_state.h" 6 7#include <algorithm> 8#include <functional> 9#include <utility> 10 11#include "base/command_line.h" 12#include "base/file_util.h" 13#include "base/file_version_info.h" 14#include "base/logging.h" 15#include "base/memory/scoped_ptr.h" 16#include "base/string_util.h" 17#include "base/utf_string_conversions.h" 18#include "base/win/registry.h" 19#include "base/win/scoped_handle.h" 20#include "chrome/installer/util/delete_tree_work_item.h" 21#include "chrome/installer/util/helper.h" 22#include "chrome/installer/util/install_util.h" 23#include "chrome/installer/util/installation_state.h" 24#include "chrome/installer/util/master_preferences.h" 25#include "chrome/installer/util/master_preferences_constants.h" 26#include "chrome/installer/util/product.h" 27#include "chrome/installer/util/work_item.h" 28#include "chrome/installer/util/work_item_list.h" 29 30namespace installer { 31 32bool InstallerState::IsMultiInstallUpdate(const MasterPreferences& prefs, 33 const InstallationState& machine_state) { 34 // First, is the package present? 35 const ProductState* package = 36 machine_state.GetProductState(level_ == SYSTEM_LEVEL, 37 BrowserDistribution::CHROME_BINARIES); 38 if (package == NULL) { 39 // The multi-install package has not been installed, so it certainly isn't 40 // being updated. 41 return false; 42 } 43 44 BrowserDistribution::Type types[2]; 45 size_t num_types = 0; 46 if (prefs.install_chrome()) 47 types[num_types++] = BrowserDistribution::CHROME_BROWSER; 48 if (prefs.install_chrome_frame()) 49 types[num_types++] = BrowserDistribution::CHROME_FRAME; 50 51 for (const BrowserDistribution::Type* scan = &types[0], 52 *end = &types[num_types]; scan != end; ++scan) { 53 const ProductState* product = 54 machine_state.GetProductState(level_ == SYSTEM_LEVEL, *scan); 55 if (product == NULL) { 56 VLOG(2) << "It seems that distribution type " << *scan 57 << " is being installed for the first time."; 58 return false; 59 } 60 if (!product->channel().Equals(package->channel())) { 61 VLOG(2) << "It seems that distribution type " << *scan 62 << " is being over installed."; 63 return false; 64 } 65 } 66 67 VLOG(2) << "It seems that the package is being updated."; 68 69 return true; 70} 71 72InstallerState::InstallerState() 73 : operation_(UNINITIALIZED), 74 multi_package_distribution_(NULL), 75 level_(UNKNOWN_LEVEL), 76 package_type_(UNKNOWN_PACKAGE_TYPE), 77 state_type_(BrowserDistribution::CHROME_BROWSER), 78 root_key_(NULL), 79 msi_(false), 80 verbose_logging_(false), 81 ensure_google_update_present_(false) { 82} 83 84InstallerState::InstallerState(Level level) 85 : operation_(UNINITIALIZED), 86 multi_package_distribution_(NULL), 87 level_(UNKNOWN_LEVEL), 88 package_type_(UNKNOWN_PACKAGE_TYPE), 89 state_type_(BrowserDistribution::CHROME_BROWSER), 90 root_key_(NULL), 91 msi_(false), 92 verbose_logging_(false), 93 ensure_google_update_present_(false) { 94 // Use set_level() so that root_key_ is updated properly. 95 set_level(level); 96} 97 98void InstallerState::Initialize(const CommandLine& command_line, 99 const MasterPreferences& prefs, 100 const InstallationState& machine_state) { 101 bool pref_bool; 102 if (!prefs.GetBool(master_preferences::kSystemLevel, &pref_bool)) 103 pref_bool = false; 104 set_level(pref_bool ? SYSTEM_LEVEL : USER_LEVEL); 105 106 if (!prefs.GetBool(master_preferences::kVerboseLogging, &verbose_logging_)) 107 verbose_logging_ = false; 108 109 if (!prefs.GetBool(master_preferences::kMultiInstall, &pref_bool)) 110 pref_bool = false; 111 set_package_type(pref_bool ? MULTI_PACKAGE : SINGLE_PACKAGE); 112 113 if (!prefs.GetBool(master_preferences::kMsi, &msi_)) 114 msi_ = false; 115 116 ensure_google_update_present_ = 117 command_line.HasSwitch(installer::switches::kEnsureGoogleUpdatePresent); 118 119 const bool is_uninstall = command_line.HasSwitch(switches::kUninstall); 120 121 if (prefs.install_chrome()) { 122 Product* p = 123 AddProductFromPreferences(BrowserDistribution::CHROME_BROWSER, prefs, 124 machine_state); 125 VLOG(1) << (is_uninstall ? "Uninstall" : "Install") 126 << " distribution: " << p->distribution()->GetAppShortCutName(); 127 } 128 if (prefs.install_chrome_frame()) { 129 Product* p = 130 AddProductFromPreferences(BrowserDistribution::CHROME_FRAME, prefs, 131 machine_state); 132 VLOG(1) << (is_uninstall ? "Uninstall" : "Install") 133 << " distribution: " << p->distribution()->GetAppShortCutName(); 134 } 135 if (prefs.install_chrome_app_host()) { 136 Product* p = 137 AddProductFromPreferences(BrowserDistribution::CHROME_APP_HOST, prefs, 138 machine_state); 139 VLOG(1) << (is_uninstall ? "Uninstall" : "Install") 140 << " distribution: " << p->distribution()->GetAppShortCutName(); 141 } 142 143 if (!is_uninstall && is_multi_install()) { 144 bool need_binaries = false; 145 if (FindProduct(BrowserDistribution::CHROME_APP_HOST)) { 146 // App Host will happily use Chrome at system level, or binaries at system 147 // level, even if app host is user level. 148 const ProductState* chrome_state = machine_state.GetProductState( 149 true, // system level 150 BrowserDistribution::CHROME_BROWSER); 151 // If Chrome is at system-level, multi- or otherwise. We'll use it. 152 if (!chrome_state) { 153 const ProductState* binaries_state = machine_state.GetProductState( 154 true, // system level 155 BrowserDistribution::CHROME_BINARIES); 156 if (!binaries_state) 157 need_binaries = true; 158 } 159 } 160 161 // Chrome/Chrome Frame multi need Binaries at their own level. 162 if (FindProduct(BrowserDistribution::CHROME_BROWSER)) 163 need_binaries = true; 164 165 if (FindProduct(BrowserDistribution::CHROME_FRAME)) 166 need_binaries = true; 167 168 if (need_binaries && !FindProduct(BrowserDistribution::CHROME_BINARIES)) { 169 // Force binaries to be installed/updated. 170 Product* p = 171 AddProductFromPreferences(BrowserDistribution::CHROME_BINARIES, 172 prefs, 173 machine_state); 174 VLOG(1) << "Install distribution: " 175 << p->distribution()->GetAppShortCutName(); 176 } 177 } 178 179 if (is_uninstall && prefs.is_multi_install()) { 180 if (FindProduct(BrowserDistribution::CHROME_BROWSER)) { 181 const ProductState* chrome_frame_state = machine_state.GetProductState( 182 system_install(), BrowserDistribution::CHROME_FRAME); 183 184 if (chrome_frame_state != NULL && 185 chrome_frame_state->uninstall_command().HasSwitch( 186 switches::kChromeFrameReadyMode) && 187 !FindProduct(BrowserDistribution::CHROME_FRAME)) { 188 // Chrome Frame is installed in Ready Mode. Remove it along with Chrome. 189 Product* p = AddProductFromPreferences( 190 BrowserDistribution::CHROME_FRAME, prefs, machine_state); 191 192 VLOG(1) << "Uninstall distribution: " 193 << p->distribution()->GetAppShortCutName(); 194 } 195 } 196 197 bool keep_binaries = false; 198 // Look for a product that is not the binaries and that is not being 199 // uninstalled. If not found, binaries are uninstalled too. 200 for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) { 201 BrowserDistribution::Type type = 202 static_cast<BrowserDistribution::Type>(i); 203 204 if (type == BrowserDistribution::CHROME_BINARIES) 205 continue; 206 207 if (machine_state.GetProductState(system_install(), type) == NULL) { 208 // The product is not installed. 209 continue; 210 } 211 212 // The product is installed. 213 214 if (!FindProduct(type)) { 215 // The product is not being uninstalled. 216 if (type != BrowserDistribution::CHROME_APP_HOST) { 217 keep_binaries = true; 218 break; 219 } else { 220 // If binaries/chrome are at system-level, we can discard them at 221 // user-level... 222 if (!machine_state.GetProductState( 223 true, // system-level 224 BrowserDistribution::CHROME_BROWSER) && 225 !machine_state.GetProductState( 226 true, // system-level 227 BrowserDistribution::CHROME_BINARIES)) { 228 // ... otherwise keep them. 229 keep_binaries = true; 230 break; 231 } 232 233 } 234 } 235 236 // The product is being uninstalled. 237 } 238 if (!keep_binaries) { 239 Product* p = 240 AddProductFromPreferences(BrowserDistribution::CHROME_BINARIES, prefs, 241 machine_state); 242 VLOG(1) << (is_uninstall ? "Uninstall" : "Install") 243 << " distribution: " << p->distribution()->GetAppShortCutName(); 244 } 245 } 246 247 BrowserDistribution* operand = NULL; 248 249 if (is_uninstall) { 250 operation_ = UNINSTALL; 251 } else if (!prefs.is_multi_install()) { 252 // For a single-install, the current browser dist is the operand. 253 operand = BrowserDistribution::GetDistribution(); 254 operation_ = SINGLE_INSTALL_OR_UPDATE; 255 } else if (IsMultiInstallUpdate(prefs, machine_state)) { 256 // Updates driven by Google Update take place under the multi-installer's 257 // app guid. 258 operand = multi_package_distribution_; 259 operation_ = MULTI_UPDATE; 260 } else { 261 operation_ = MULTI_INSTALL; 262 } 263 264 // Initial, over, and un-installs will take place under one of the 265 // product app guids (Chrome, Chrome Frame, App Host, or Binaries, in order of 266 // preference). 267 if (operand == NULL) { 268 BrowserDistribution::Type operand_distribution_type = 269 BrowserDistribution::CHROME_BINARIES; 270 if (prefs.install_chrome()) 271 operand_distribution_type = BrowserDistribution::CHROME_BROWSER; 272 else if (prefs.install_chrome_frame()) 273 operand_distribution_type = BrowserDistribution::CHROME_FRAME; 274 else if (prefs.install_chrome_app_host()) 275 operand_distribution_type = BrowserDistribution::CHROME_APP_HOST; 276 277 operand = BrowserDistribution::GetSpecificDistribution( 278 operand_distribution_type); 279 } 280 281 state_key_ = operand->GetStateKey(); 282 state_type_ = operand->GetType(); 283 284 // Parse --critical-update-version=W.X.Y.Z 285 std::string critical_version_value( 286 command_line.GetSwitchValueASCII(switches::kCriticalUpdateVersion)); 287 critical_update_version_ = Version(critical_version_value); 288} 289 290void InstallerState::set_level(Level level) { 291 level_ = level; 292 switch (level) { 293 case USER_LEVEL: 294 root_key_ = HKEY_CURRENT_USER; 295 break; 296 case SYSTEM_LEVEL: 297 root_key_ = HKEY_LOCAL_MACHINE; 298 break; 299 default: 300 DCHECK(level == UNKNOWN_LEVEL); 301 level_ = UNKNOWN_LEVEL; 302 root_key_ = NULL; 303 break; 304 } 305} 306 307void InstallerState::set_package_type(PackageType type) { 308 package_type_ = type; 309 switch (type) { 310 case SINGLE_PACKAGE: 311 multi_package_distribution_ = NULL; 312 break; 313 case MULTI_PACKAGE: 314 multi_package_distribution_ = 315 BrowserDistribution::GetSpecificDistribution( 316 BrowserDistribution::CHROME_BINARIES); 317 break; 318 default: 319 DCHECK(type == UNKNOWN_PACKAGE_TYPE); 320 package_type_ = UNKNOWN_PACKAGE_TYPE; 321 multi_package_distribution_ = NULL; 322 break; 323 } 324} 325 326// Returns the Chrome binaries directory for multi-install or |dist|'s directory 327// otherwise. 328FilePath InstallerState::GetDefaultProductInstallPath( 329 BrowserDistribution* dist) const { 330 DCHECK(dist); 331 DCHECK(package_type_ != UNKNOWN_PACKAGE_TYPE); 332 333 if (package_type_ == SINGLE_PACKAGE) { 334 return GetChromeInstallPath(system_install(), dist); 335 } else { 336 return GetChromeInstallPath(system_install(), 337 BrowserDistribution::GetSpecificDistribution( 338 BrowserDistribution::CHROME_BINARIES)); 339 } 340} 341 342// Evaluates a product's eligibility for participation in this operation. 343// We never expect these checks to fail, hence they all terminate the process in 344// debug builds. See the log messages for details. 345bool InstallerState::CanAddProduct(const Product& product, 346 const FilePath* product_dir) const { 347 switch (package_type_) { 348 case SINGLE_PACKAGE: 349 if (!products_.empty()) { 350 LOG(DFATAL) << "Cannot process more than one single-install product."; 351 return false; 352 } 353 break; 354 case MULTI_PACKAGE: 355 if (!product.HasOption(kOptionMultiInstall)) { 356 LOG(DFATAL) << "Cannot process a single-install product with a " 357 "multi-install state."; 358 return false; 359 } 360 if (FindProduct(product.distribution()->GetType()) != NULL) { 361 LOG(DFATAL) << "Cannot process more than one product of the same type."; 362 return false; 363 } 364 if (!target_path_.empty()) { 365 FilePath default_dir; 366 if (product_dir == NULL) 367 default_dir = GetDefaultProductInstallPath(product.distribution()); 368 if (!FilePath::CompareEqualIgnoreCase( 369 (product_dir == NULL ? default_dir : *product_dir).value(), 370 target_path_.value())) { 371 LOG(DFATAL) << "Cannot process products in different directories."; 372 return false; 373 } 374 } 375 break; 376 default: 377 DCHECK_EQ(UNKNOWN_PACKAGE_TYPE, package_type_); 378 break; 379 } 380 return true; 381} 382 383// Adds |product|, installed in |product_dir| to this object's collection. If 384// |product_dir| is NULL, the product's default install location is used. 385// Returns NULL if |product| is incompatible with this object. Otherwise, 386// returns a pointer to the product (ownership is held by this object). 387Product* InstallerState::AddProductInDirectory(const FilePath* product_dir, 388 scoped_ptr<Product>* product) { 389 DCHECK(product != NULL); 390 DCHECK(product->get() != NULL); 391 const Product& the_product = *product->get(); 392 393 if (!CanAddProduct(the_product, product_dir)) 394 return NULL; 395 396 if (package_type_ == UNKNOWN_PACKAGE_TYPE) { 397 set_package_type(the_product.HasOption(kOptionMultiInstall) ? 398 MULTI_PACKAGE : SINGLE_PACKAGE); 399 } 400 401 if (target_path_.empty()) { 402 if (product_dir == NULL) 403 target_path_ = GetDefaultProductInstallPath(the_product.distribution()); 404 else 405 target_path_ = *product_dir; 406 } 407 408 if (state_key_.empty()) 409 state_key_ = the_product.distribution()->GetStateKey(); 410 411 products_.push_back(product->release()); 412 return products_[products_.size() - 1]; 413} 414 415Product* InstallerState::AddProduct(scoped_ptr<Product>* product) { 416 return AddProductInDirectory(NULL, product); 417} 418 419// Adds a product of type |distribution_type| constructed on the basis of 420// |prefs|, setting this object's msi flag if the product is represented in 421// |machine_state| and is msi-installed. Returns the product that was added, 422// or NULL if |state| is incompatible with this object. Ownership is not passed 423// to the caller. 424Product* InstallerState::AddProductFromPreferences( 425 BrowserDistribution::Type distribution_type, 426 const MasterPreferences& prefs, 427 const InstallationState& machine_state) { 428 scoped_ptr<Product> product_ptr( 429 new Product(BrowserDistribution::GetSpecificDistribution( 430 distribution_type))); 431 product_ptr->InitializeFromPreferences(prefs); 432 433 Product* product = AddProductInDirectory(NULL, &product_ptr); 434 435 if (product != NULL && !msi_) { 436 const ProductState* product_state = machine_state.GetProductState( 437 system_install(), distribution_type); 438 if (product_state != NULL) 439 msi_ = product_state->is_msi(); 440 } 441 442 return product; 443} 444 445Product* InstallerState::AddProductFromState( 446 BrowserDistribution::Type type, 447 const ProductState& state) { 448 scoped_ptr<Product> product_ptr( 449 new Product(BrowserDistribution::GetSpecificDistribution(type))); 450 product_ptr->InitializeFromUninstallCommand(state.uninstall_command()); 451 452 // Strip off <version>/Installer/setup.exe; see GetInstallerDirectory(). 453 FilePath product_dir = state.GetSetupPath().DirName().DirName().DirName(); 454 455 Product* product = AddProductInDirectory(&product_dir, &product_ptr); 456 457 if (product != NULL) 458 msi_ |= state.is_msi(); 459 460 return product; 461} 462 463bool InstallerState::system_install() const { 464 DCHECK(level_ == USER_LEVEL || level_ == SYSTEM_LEVEL); 465 return level_ == SYSTEM_LEVEL; 466} 467 468bool InstallerState::is_multi_install() const { 469 DCHECK(package_type_ == SINGLE_PACKAGE || package_type_ == MULTI_PACKAGE); 470 return package_type_ != SINGLE_PACKAGE; 471} 472 473bool InstallerState::RemoveProduct(const Product* product) { 474 ScopedVector<Product>::iterator it = 475 std::find(products_.begin(), products_.end(), product); 476 if (it != products_.end()) { 477 products_.weak_erase(it); 478 return true; 479 } 480 return false; 481} 482 483const Product* InstallerState::FindProduct( 484 BrowserDistribution::Type distribution_type) const { 485 for (Products::const_iterator scan = products_.begin(), end = products_.end(); 486 scan != end; ++scan) { 487 if ((*scan)->is_type(distribution_type)) 488 return *scan; 489 } 490 return NULL; 491} 492 493Version* InstallerState::GetCurrentVersion( 494 const InstallationState& machine_state) const { 495 DCHECK(!products_.empty()); 496 scoped_ptr<Version> current_version; 497 // If we're doing a multi-install, the current version may be either an 498 // existing multi or an existing single product that is being migrated 499 // in place (i.e., Chrome). In the latter case, there is no existing 500 // CHROME_BINARIES installation so we need to search for the product. 501 BrowserDistribution::Type prod_type; 502 if (package_type_ == MULTI_PACKAGE) { 503 prod_type = BrowserDistribution::CHROME_BINARIES; 504 if (machine_state.GetProductState(level_ == SYSTEM_LEVEL, 505 prod_type) == NULL) { 506 // Search for a product on which we're operating that is installed in our 507 // target directory. 508 Products::const_iterator end = products().end(); 509 for (Products::const_iterator scan = products().begin(); scan != end; 510 ++scan) { 511 BrowserDistribution::Type product_type = 512 (*scan)->distribution()->GetType(); 513 const ProductState* state = 514 machine_state.GetProductState(level_ == SYSTEM_LEVEL, product_type); 515 if (state != NULL && target_path_.IsParent(state->GetSetupPath())) { 516 prod_type = product_type; 517 break; 518 } 519 } 520 } 521 } else { 522 prod_type = products_[0]->distribution()->GetType(); 523 } 524 const ProductState* product_state = 525 machine_state.GetProductState(level_ == SYSTEM_LEVEL, prod_type); 526 527 if (product_state != NULL) { 528 const Version* version = NULL; 529 530 // Be aware that there might be a pending "new_chrome.exe" already in the 531 // installation path. If so, we use old_version, which holds the version of 532 // "chrome.exe" itself. 533 if (file_util::PathExists(target_path().Append(kChromeNewExe))) 534 version = product_state->old_version(); 535 536 if (version == NULL) 537 version = &product_state->version(); 538 539 current_version.reset(new Version(*version)); 540 } 541 542 return current_version.release(); 543} 544 545Version InstallerState::DetermineCriticalVersion( 546 const Version* current_version, 547 const Version& new_version) const { 548 DCHECK(current_version == NULL || current_version->IsValid()); 549 DCHECK(new_version.IsValid()); 550 if (critical_update_version_.IsValid() && 551 (current_version == NULL || 552 (current_version->CompareTo(critical_update_version_) < 0)) && 553 new_version.CompareTo(critical_update_version_) >= 0) { 554 return critical_update_version_; 555 } 556 return Version(); 557} 558 559bool InstallerState::IsChromeFrameRunning( 560 const InstallationState& machine_state) const { 561 // We check only for the current version (e.g. the version we are upgrading 562 // _from_). We don't need to check interstitial versions if any (as would 563 // occur in the case of multiple updates) since if they are in use, we are 564 // guaranteed that the current version is in use too. 565 bool in_use = false; 566 scoped_ptr<Version> current_version(GetCurrentVersion(machine_state)); 567 if (current_version != NULL) { 568 FilePath cf_install_path( 569 target_path().AppendASCII(current_version->GetString()) 570 .Append(kChromeFrameDll)); 571 in_use = IsFileInUse(cf_install_path); 572 } 573 return in_use; 574} 575 576FilePath InstallerState::GetInstallerDirectory(const Version& version) const { 577 return target_path().Append(ASCIIToWide(version.GetString())) 578 .Append(kInstallerDir); 579} 580 581// static 582bool InstallerState::IsFileInUse(const FilePath& file) { 583 // Call CreateFile with a share mode of 0 which should cause this to fail 584 // with ERROR_SHARING_VIOLATION if the file exists and is in-use. 585 return !base::win::ScopedHandle(CreateFile(file.value().c_str(), 586 GENERIC_WRITE, 0, NULL, 587 OPEN_EXISTING, 0, 0)).IsValid(); 588} 589 590 591void InstallerState::GetExistingExeVersions( 592 std::set<std::string>* existing_versions) const { 593 594 static const wchar_t* const kChromeFilenames[] = { 595 installer::kChromeExe, 596 installer::kChromeNewExe, 597 installer::kChromeOldExe, 598 }; 599 600 for (int i = 0; i < arraysize(kChromeFilenames); ++i) { 601 FilePath chrome_exe(target_path().Append(kChromeFilenames[i])); 602 scoped_ptr<FileVersionInfo> file_version_info( 603 FileVersionInfo::CreateFileVersionInfo(chrome_exe)); 604 if (file_version_info) { 605 string16 version_string = file_version_info->file_version(); 606 if (!version_string.empty() && IsStringASCII(version_string)) 607 existing_versions->insert(WideToASCII(version_string)); 608 } 609 } 610} 611 612void InstallerState::RemoveOldVersionDirectories( 613 const Version& new_version, 614 Version* existing_version, 615 const FilePath& temp_path) const { 616 Version version; 617 std::vector<FilePath> key_files; 618 scoped_ptr<WorkItem> item; 619 620 std::set<std::string> existing_version_strings; 621 existing_version_strings.insert(new_version.GetString()); 622 if (existing_version) 623 existing_version_strings.insert(existing_version->GetString()); 624 625 // Make sure not to delete any version dir that is "referenced" by an existing 626 // Chrome executable. 627 GetExistingExeVersions(&existing_version_strings); 628 629 // Try to delete all directories that are not in the set we care to keep. 630 file_util::FileEnumerator version_enum(target_path(), false, 631 file_util::FileEnumerator::DIRECTORIES); 632 for (FilePath next_version = version_enum.Next(); !next_version.empty(); 633 next_version = version_enum.Next()) { 634 FilePath dir_name(next_version.BaseName()); 635 version = Version(WideToASCII(dir_name.value())); 636 // Delete the version folder if it is less than the new version and not 637 // equal to the old version (if we have an old version). 638 if (version.IsValid() && 639 existing_version_strings.count(version.GetString()) == 0) { 640 // Note: temporarily log old version deletion at ERROR level to make it 641 // more likely we see this in the installer log. 642 LOG(ERROR) << "Deleting old version directory: " << next_version.value(); 643 644 // Attempt to recursively delete the old version dir. 645 bool delete_succeeded = file_util::Delete(next_version, true); 646 647 // Note: temporarily log old version deletion at ERROR level to make it 648 // more likely we see this in the installer log. 649 LOG_IF(ERROR, !delete_succeeded) 650 << "Failed to delete old version directory: " << next_version.value(); 651 } 652 } 653} 654 655void InstallerState::AddComDllList(std::vector<FilePath>* com_dll_list) const { 656 std::for_each(products_.begin(), products_.end(), 657 std::bind2nd(std::mem_fun(&Product::AddComDllList), 658 com_dll_list)); 659} 660 661bool InstallerState::SetChannelFlags(bool set, 662 ChannelInfo* channel_info) const { 663 bool modified = false; 664 for (Products::const_iterator scan = products_.begin(), end = products_.end(); 665 scan != end; ++scan) { 666 modified |= (*scan)->SetChannelFlags(set, channel_info); 667 } 668 return modified; 669} 670 671void InstallerState::UpdateStage(installer::InstallerStage stage) const { 672 InstallUtil::UpdateInstallerStage(system_install(), state_key_, stage); 673} 674 675void InstallerState::UpdateChannels() const { 676 if (operation_ != MULTI_INSTALL && operation_ != MULTI_UPDATE) { 677 VLOG(1) << "InstallerState::UpdateChannels noop: " << operation_; 678 return; 679 } 680 681 // Update the "ap" value for the product being installed/updated. We get the 682 // current value from the registry since the InstallationState instance used 683 // by the bulk of the installer does not track changes made by UpdateStage. 684 // Create the app's ClientState key if it doesn't exist. 685 ChannelInfo channel_info; 686 base::win::RegKey state_key; 687 LONG result = state_key.Create(root_key_, state_key_.c_str(), 688 KEY_QUERY_VALUE | KEY_SET_VALUE); 689 if (result == ERROR_SUCCESS) { 690 channel_info.Initialize(state_key); 691 692 // This is a multi-install product. 693 bool modified = channel_info.SetMultiInstall(true); 694 695 // Add the appropriate modifiers for all products and their options. 696 modified |= SetChannelFlags(true, &channel_info); 697 698 VLOG(1) << "ap: " << channel_info.value(); 699 700 // Write the results if needed. 701 if (modified) 702 channel_info.Write(&state_key); 703 704 // Remove the -stage: modifier since we don't want to propagate that to the 705 // other app_guids. 706 channel_info.SetStage(NULL); 707 708 // Synchronize the other products and the package with this one. 709 ChannelInfo other_info; 710 for (int i = 0; i < BrowserDistribution::NUM_TYPES; ++i) { 711 BrowserDistribution::Type type = 712 static_cast<BrowserDistribution::Type>(i); 713 // Skip the app_guid we started with. 714 if (type == state_type_) 715 continue; 716 BrowserDistribution* dist = NULL; 717 // Always operate on the binaries. 718 if (i == BrowserDistribution::CHROME_BINARIES) { 719 dist = multi_package_distribution_; 720 } else { 721 const Product* product = FindProduct(type); 722 // Skip this one if it's for a product we're not operating on. 723 if (product == NULL) 724 continue; 725 dist = product->distribution(); 726 } 727 result = state_key.Create(root_key_, dist->GetStateKey().c_str(), 728 KEY_QUERY_VALUE | KEY_SET_VALUE); 729 if (result == ERROR_SUCCESS) { 730 other_info.Initialize(state_key); 731 if (!other_info.Equals(channel_info)) 732 channel_info.Write(&state_key); 733 } else { 734 LOG(ERROR) << "Failed opening key " << dist->GetStateKey() 735 << " to update app channels; result: " << result; 736 } 737 } 738 } else { 739 LOG(ERROR) << "Failed opening key " << state_key_ 740 << " to update app channels; result: " << result; 741 } 742} 743 744void InstallerState::WriteInstallerResult( 745 InstallStatus status, 746 int string_resource_id, 747 const std::wstring* const launch_cmd) const { 748 DWORD installer_result = 749 (InstallUtil::GetInstallReturnCode(status) == 0) ? 0 : 1; 750 // Use a no-rollback list since this is a best-effort deal. 751 scoped_ptr<WorkItemList> install_list( 752 WorkItem::CreateNoRollbackWorkItemList()); 753 const bool system_install = this->system_install(); 754 // Write the value for all products upon which we're operating. 755 Products::const_iterator end = products().end(); 756 for (Products::const_iterator scan = products().begin(); scan != end; 757 ++scan) { 758 InstallUtil::AddInstallerResultItems( 759 system_install, (*scan)->distribution()->GetStateKey(), status, 760 string_resource_id, launch_cmd, install_list.get()); 761 } 762 // And for the binaries if this is a multi-install. 763 if (is_multi_install()) { 764 InstallUtil::AddInstallerResultItems( 765 system_install, multi_package_binaries_distribution()->GetStateKey(), 766 status, string_resource_id, launch_cmd, install_list.get()); 767 } 768 if (!install_list->Do()) 769 LOG(ERROR) << "Failed to record installer error information in registry."; 770} 771 772} // namespace installer 773