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.
5 #include "chrome/installer/setup/setup_main.h"
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/path_service.h"
22 #include "base/process/launch.h"
23 #include "base/strings/string16.h"
24 #include "base/strings/string_number_conversions.h"
25 #include "base/strings/string_util.h"
26 #include "base/strings/stringprintf.h"
27 #include "base/strings/utf_string_conversions.h"
28 #include "base/values.h"
29 #include "base/win/registry.h"
30 #include "base/win/scoped_com_initializer.h"
31 #include "base/win/scoped_comptr.h"
32 #include "base/win/scoped_handle.h"
33 #include "base/win/win_util.h"
34 #include "base/win/windows_version.h"
35 #include "breakpad/src/client/windows/handler/exception_handler.h"
36 #include "chrome/common/chrome_constants.h"
37 #include "chrome/common/chrome_switches.h"
38 #include "chrome/installer/setup/archive_patch_helper.h"
39 #include "chrome/installer/setup/cf_migration.h"
40 #include "chrome/installer/setup/chrome_frame_quick_enable.h"
41 #include "chrome/installer/setup/chrome_frame_ready_mode.h"
42 #include "chrome/installer/setup/install.h"
43 #include "chrome/installer/setup/install_worker.h"
44 #include "chrome/installer/setup/setup_constants.h"
45 #include "chrome/installer/setup/setup_util.h"
46 #include "chrome/installer/setup/uninstall.h"
47 #include "chrome/installer/util/browser_distribution.h"
48 #include "chrome/installer/util/channel_info.h"
49 #include "chrome/installer/util/delete_after_reboot_helper.h"
50 #include "chrome/installer/util/delete_tree_work_item.h"
51 #include "chrome/installer/util/eula_util.h"
52 #include "chrome/installer/util/google_update_constants.h"
53 #include "chrome/installer/util/google_update_settings.h"
54 #include "chrome/installer/util/google_update_util.h"
55 #include "chrome/installer/util/helper.h"
56 #include "chrome/installer/util/html_dialog.h"
57 #include "chrome/installer/util/install_util.h"
58 #include "chrome/installer/util/installation_state.h"
59 #include "chrome/installer/util/installation_validator.h"
60 #include "chrome/installer/util/installer_state.h"
61 #include "chrome/installer/util/l10n_string_util.h"
62 #include "chrome/installer/util/logging_installer.h"
63 #include "chrome/installer/util/lzma_util.h"
64 #include "chrome/installer/util/master_preferences.h"
65 #include "chrome/installer/util/master_preferences_constants.h"
66 #include "chrome/installer/util/self_cleaning_temp_dir.h"
67 #include "chrome/installer/util/shell_util.h"
68 #include "chrome/installer/util/user_experiment.h"
70 #include "installer_util_strings.h" // NOLINT
72 using installer::InstallerState;
73 using installer::InstallationState;
74 using installer::InstallationValidator;
75 using installer::MasterPreferences;
76 using installer::Product;
77 using installer::ProductState;
78 using installer::Products;
80 const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices";
81 const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\";
82 const wchar_t kSystemPrincipalSid[] = L"S-1-5-18";
84 const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
85 MiniDumpWithProcessThreadData | // Get PEB and TEB.
86 MiniDumpWithUnloadedModules | // Get unloaded modules when available.
87 MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack.
91 // Returns NULL if no compressed archive is available for processing, otherwise
92 // returns a patch helper configured to uncompress and patch.
93 scoped_ptr<installer::ArchivePatchHelper> CreateChromeArchiveHelper(
94 const CommandLine& command_line,
95 const installer::InstallerState& installer_state,
96 const base::FilePath& working_directory) {
97 // A compressed archive is ordinarily given on the command line by the mini
98 // installer. If one was not given, look for chrome.packed.7z next to the
100 base::FilePath compressed_archive(
101 command_line.GetSwitchValuePath(installer::switches::kInstallArchive));
102 bool compressed_archive_specified = !compressed_archive.empty();
103 if (!compressed_archive_specified) {
105 command_line.GetProgram().DirName().Append(
106 installer::kChromeCompressedArchive);
109 // Fail if no compressed archive is found.
110 if (!base::PathExists(compressed_archive)) {
111 if (compressed_archive_specified) {
112 LOG(ERROR) << installer::switches::kInstallArchive << "="
113 << compressed_archive.value() << " not found.";
115 return scoped_ptr<installer::ArchivePatchHelper>();
118 // chrome.7z is either extracted directly from the compressed archive into the
119 // working dir or is the target of patching in the working dir.
120 base::FilePath target(working_directory.Append(installer::kChromeArchive));
121 DCHECK(!base::PathExists(target));
123 // Specify an empty path for the patch source since it isn't yet known that
124 // one is needed. It will be supplied in UncompressAndPatchChromeArchive if it
126 return scoped_ptr<installer::ArchivePatchHelper>(
127 new installer::ArchivePatchHelper(working_directory,
133 // Workhorse for producing an uncompressed archive (chrome.7z) given a
134 // chrome.packed.7z containing either a patch file based on the version of
135 // chrome being updated or the full uncompressed archive. Returns true on
136 // success, in which case |archive_type| is populated based on what was found.
137 // Returns false on failure, in which case |install_status| contains the error
138 // code and the result is written to the registry (via WriteInstallerResult).
139 bool UncompressAndPatchChromeArchive(
140 const installer::InstallationState& original_state,
141 const installer::InstallerState& installer_state,
142 installer::ArchivePatchHelper* archive_helper,
143 installer::ArchiveType* archive_type,
144 installer::InstallStatus* install_status) {
145 installer_state.UpdateStage(installer::UNCOMPRESSING);
146 if (!archive_helper->Uncompress(NULL)) {
147 *install_status = installer::UNCOMPRESSION_FAILED;
148 installer_state.WriteInstallerResult(*install_status,
149 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
154 // Short-circuit if uncompression produced the uncompressed archive rather
155 // than a patch file.
156 if (base::PathExists(archive_helper->target())) {
157 *archive_type = installer::FULL_ARCHIVE_TYPE;
161 // Find the installed version's archive to serve as the source for patching.
162 base::FilePath patch_source(installer::FindArchiveToPatch(original_state,
164 if (patch_source.empty()) {
165 LOG(ERROR) << "Failed to find archive to patch.";
166 *install_status = installer::DIFF_PATCH_SOURCE_MISSING;
167 installer_state.WriteInstallerResult(*install_status,
168 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
172 archive_helper->set_patch_source(patch_source);
174 // Try courgette first. Failing that, try bspatch.
175 if ((installer_state.UpdateStage(installer::ENSEMBLE_PATCHING),
176 !archive_helper->EnsemblePatch()) &&
177 (installer_state.UpdateStage(installer::BINARY_PATCHING),
178 !archive_helper->BinaryPatch())) {
179 *install_status = installer::APPLY_DIFF_PATCH_FAILED;
180 installer_state.WriteInstallerResult(*install_status,
181 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
186 *archive_type = installer::INCREMENTAL_ARCHIVE_TYPE;
190 // In multi-install, adds all products to |installer_state| that are
191 // multi-installed and must be updated along with the products already present
192 // in |installer_state|.
193 void AddExistingMultiInstalls(const InstallationState& original_state,
194 InstallerState* installer_state) {
195 if (installer_state->is_multi_install()) {
196 for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
197 BrowserDistribution::Type type =
198 static_cast<BrowserDistribution::Type>(i);
200 if (!installer_state->FindProduct(type)) {
201 const ProductState* state =
202 original_state.GetProductState(installer_state->system_install(),
204 if ((state != NULL) && state->is_multi_install()) {
205 installer_state->AddProductFromState(type, *state);
206 VLOG(1) << "Product already installed and must be included: "
207 << BrowserDistribution::GetSpecificDistribution(type)->
215 // This function is called when --rename-chrome-exe option is specified on
216 // setup.exe command line. This function assumes an in-use update has happened
217 // for Chrome so there should be a file called new_chrome.exe on the file
218 // system and a key called 'opv' in the registry. This function will move
219 // new_chrome.exe to chrome.exe and delete 'opv' key in one atomic operation.
220 // This function also deletes elevation policies associated with the old version
222 installer::InstallStatus RenameChromeExecutables(
223 const InstallationState& original_state,
224 InstallerState* installer_state) {
225 // See what products are already installed in multi mode. When we do the
226 // rename for multi installs, we must update all installations since they
227 // share the binaries.
228 AddExistingMultiInstalls(original_state, installer_state);
229 const base::FilePath &target_path = installer_state->target_path();
230 base::FilePath chrome_exe(target_path.Append(installer::kChromeExe));
231 base::FilePath chrome_new_exe(target_path.Append(installer::kChromeNewExe));
232 base::FilePath chrome_old_exe(target_path.Append(installer::kChromeOldExe));
234 // Create a temporary backup directory on the same volume as chrome.exe so
235 // that moving in-use files doesn't lead to trouble.
236 installer::SelfCleaningTempDir temp_path;
237 if (!temp_path.Initialize(target_path.DirName(),
238 installer::kInstallTempDir)) {
239 PLOG(ERROR) << "Failed to create Temp directory "
240 << target_path.DirName()
241 .Append(installer::kInstallTempDir).value();
242 return installer::RENAME_FAILED;
244 scoped_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList());
245 // Move chrome.exe to old_chrome.exe, then move new_chrome.exe to chrome.exe.
246 install_list->AddMoveTreeWorkItem(chrome_exe.value(),
247 chrome_old_exe.value(),
248 temp_path.path().value(),
249 WorkItem::ALWAYS_MOVE);
250 install_list->AddMoveTreeWorkItem(chrome_new_exe.value(),
252 temp_path.path().value(),
253 WorkItem::ALWAYS_MOVE);
254 install_list->AddDeleteTreeWorkItem(chrome_new_exe, temp_path.path());
255 // Delete an elevation policy associated with the old version, should one
257 if (installer_state->FindProduct(BrowserDistribution::CHROME_FRAME)) {
258 installer::AddDeleteOldIELowRightsPolicyWorkItems(*installer_state,
261 // old_chrome.exe is still in use in most cases, so ignore failures here.
262 install_list->AddDeleteTreeWorkItem(chrome_old_exe, temp_path.path())->
263 set_ignore_failure(true);
265 // Add work items to delete the "opv", "cpv", and "cmd" values from all
266 // products we're operating on (which including the multi-install binaries).
267 const Products& products = installer_state->products();
268 HKEY reg_root = installer_state->root_key();
269 string16 version_key;
270 for (Products::const_iterator it = products.begin(); it < products.end();
272 version_key = (*it)->distribution()->GetVersionKey();
273 install_list->AddDeleteRegValueWorkItem(
274 reg_root, version_key, google_update::kRegOldVersionField);
275 install_list->AddDeleteRegValueWorkItem(
276 reg_root, version_key, google_update::kRegCriticalVersionField);
277 install_list->AddDeleteRegValueWorkItem(
278 reg_root, version_key, google_update::kRegRenameCmdField);
280 installer::InstallStatus ret = installer::RENAME_SUCCESSFUL;
281 if (!install_list->Do()) {
282 LOG(ERROR) << "Renaming of executables failed. Rolling back any changes.";
283 install_list->Rollback();
284 ret = installer::RENAME_FAILED;
286 // temp_path's dtor will take care of deleting or scheduling itself for
287 // deletion at reboot when this scope closes.
288 VLOG(1) << "Deleting temporary directory " << temp_path.path().value();
293 // For each product that is being updated (i.e., already installed at an earlier
294 // version), see if that product has an update policy override that differs from
295 // that for the binaries. If any are found, fail with an error indicating that
296 // the Group Policy settings are in an inconsistent state. Do not do this test
297 // for same-version installs, since it would be unkind to block attempts to
298 // repair a corrupt installation. This function returns false when installation
299 // should be halted, in which case |status| contains the relevant exit code and
300 // the proper installer result has been written to the registry.
301 bool CheckGroupPolicySettings(const InstallationState& original_state,
302 const InstallerState& installer_state,
303 const Version& new_version,
304 installer::InstallStatus* status) {
305 #if !defined(GOOGLE_CHROME_BUILD)
306 // Chromium builds are not updated via Google Update, so there are no
307 // Group Policy settings to consult.
312 // Single installs are always in good shape.
313 if (!installer_state.is_multi_install())
316 bool settings_are_valid = true;
317 const bool is_system_install = installer_state.system_install();
318 BrowserDistribution* const binaries_dist =
319 installer_state.multi_package_binaries_distribution();
321 // Get the update policy for the binaries.
322 const GoogleUpdateSettings::UpdatePolicy binaries_policy =
323 GoogleUpdateSettings::GetAppUpdatePolicy(binaries_dist->GetAppGuid(),
326 // Check for differing update policies for all of the products being updated.
327 const Products& products = installer_state.products();
328 Products::const_iterator scan = products.begin();
329 for (Products::const_iterator end = products.end(); scan != end; ++scan) {
330 BrowserDistribution* dist = (*scan)->distribution();
331 const ProductState* product_state =
332 original_state.GetProductState(is_system_install, dist->GetType());
333 // Is an earlier version of this product already installed?
334 if (product_state != NULL &&
335 product_state->version().CompareTo(new_version) < 0) {
336 bool is_overridden = false;
337 GoogleUpdateSettings::UpdatePolicy app_policy =
338 GoogleUpdateSettings::GetAppUpdatePolicy(dist->GetAppGuid(),
340 if (is_overridden && app_policy != binaries_policy) {
341 LOG(ERROR) << "Found legacy Group Policy setting for "
342 << dist->GetDisplayName() << " (value: " << app_policy
343 << ") that does not match the setting for "
344 << binaries_dist->GetDisplayName()
345 << " (value: " << binaries_policy << ").";
346 settings_are_valid = false;
351 if (!settings_are_valid) {
352 // TODO(grt): add " See http://goo.gl/+++ for details." to the end of this
353 // log message and to the IDS_INSTALL_INCONSISTENT_UPDATE_POLICY string once
354 // we have a help center article that explains why this error is being
355 // reported and how to resolve it.
356 LOG(ERROR) << "Cannot apply update on account of inconsistent "
357 "Google Update Group Policy settings. Use the Group Policy "
358 "Editor to set the update policy override for the "
359 << binaries_dist->GetDisplayName()
360 << " application and try again.";
361 *status = installer::INCONSISTENT_UPDATE_POLICY;
362 installer_state.WriteInstallerResult(
363 *status, IDS_INSTALL_INCONSISTENT_UPDATE_POLICY_BASE, NULL);
366 return settings_are_valid;
367 #endif // defined(GOOGLE_CHROME_BUILD)
370 // If only the binaries are being updated, fail.
371 // If Chrome Frame is being installed by itself in multi-mode, non-ready-mode:
372 // - If a non-multi Chrome Frame installation is present, fail.
373 // If Chrome Frame is being installed by itself in multi-mode, ready-mode:
374 // - If no Chrome installation is present, fail.
375 // - If a Chrome installation is present, add it to the set of products to
377 // If Chrome Frame is being installed with Chrome in multi-mode, ready-mode:
378 // - If a non-multi Chrome Frame installation is present, Chrome Frame is
379 // removed from |installer_state|'s list of products (thereby preserving
380 // the existing SxS install).
381 // - If a multi Chrome Frame installation is present, its options are
382 // preserved (i.e., the --ready-mode command-line option is ignored).
383 // If any product is being installed in single-mode that already exists in
385 bool CheckMultiInstallConditions(const InstallationState& original_state,
386 InstallerState* installer_state,
387 installer::InstallStatus* status) {
388 const Products& products = installer_state->products();
389 DCHECK(products.size());
391 const bool system_level = installer_state->system_install();
393 if (installer_state->is_multi_install()) {
394 const Product* chrome =
395 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
396 const Product* app_host =
397 installer_state->FindProduct(BrowserDistribution::CHROME_APP_HOST);
398 const Product* binaries =
399 installer_state->FindProduct(BrowserDistribution::CHROME_BINARIES);
400 const Product* chrome_frame =
401 installer_state->FindProduct(BrowserDistribution::CHROME_FRAME);
402 const ProductState* cf_state =
403 original_state.GetProductState(system_level,
404 BrowserDistribution::CHROME_FRAME);
405 const ProductState* chrome_state =
406 original_state.GetProductState(system_level,
407 BrowserDistribution::CHROME_BROWSER);
410 if (products.size() == 1) {
411 // There are no products aside from the binaries, so there is no update
412 // to be applied. This can happen after multi-install Chrome Frame is
413 // migrated to single-install. This is treated as an update failure
414 // unless the binaries are not in-use, in which case they will be
415 // uninstalled and success will be reported (see handling in wWinMain).
416 LOG(INFO) << "No products to be updated.";
417 *status = installer::UNUSED_BINARIES;
418 installer_state->WriteInstallerResult(*status, 0, NULL);
422 // This will only be hit if --multi-install is given with no products, or
423 // if the app host is being installed and doesn't need the binaries at
425 // The former case might be due to a request by an orphaned Application
426 // Host to re-install the binaries. Thus we add them to the installation.
427 // The latter case is fine and we let it be.
428 // If this is not an app host install and the binaries are not already
429 // present, the installation will fail later due to a lack of products to
431 if (app_host && !chrome && !chrome_frame && !cf_state && !chrome_state) {
432 DCHECK(!system_level);
433 // App Host may use Chrome/Chrome binaries at system-level.
434 if (original_state.GetProductState(
436 BrowserDistribution::CHROME_BROWSER) ||
437 original_state.GetProductState(
439 BrowserDistribution::CHROME_BINARIES)) {
440 VLOG(1) << "Installing/updating App Launcher without binaries.";
442 // Somehow the binaries were present when the quick-enable app host
443 // command was run, but now they appear to be missing.
444 // Force binaries to be installed/updated.
445 scoped_ptr<Product> binaries_to_add(new Product(
446 BrowserDistribution::GetSpecificDistribution(
447 BrowserDistribution::CHROME_BINARIES)));
448 binaries_to_add->SetOption(installer::kOptionMultiInstall, true);
449 binaries = installer_state->AddProduct(&binaries_to_add);
451 "Adding binaries for pre-existing App Launcher installation.";
460 chrome_frame->HasOption(installer::kOptionReadyMode)) {
461 // We're being asked to install Chrome with Chrome Frame in ready-mode.
462 // This is an optimistic operation: if a SxS install of Chrome Frame
463 // is already present, don't touch it; if a multi-install of Chrome
464 // Frame is present, preserve its settings (ready-mode).
466 installer_state->RemoveProduct(chrome_frame);
468 if (cf_state->is_multi_install()) {
469 chrome_frame = installer_state->AddProductFromState(
470 BrowserDistribution::CHROME_FRAME, *cf_state);
471 VLOG(1) << "Upgrading existing multi-install Chrome Frame rather "
472 "than installing in ready-mode.";
474 VLOG(1) << "Skipping upgrade of single-install Chrome Frame rather "
475 "than installing in ready-mode.";
478 VLOG(1) << "Performing initial install of Chrome Frame ready-mode.";
481 } else if (chrome_state) {
482 // A product other than Chrome is being installed in multi-install mode,
483 // and Chrome is already present. Add Chrome to the set of products
484 // (making it multi-install in the process) so that it is updated, too.
485 scoped_ptr<Product> multi_chrome(new Product(
486 BrowserDistribution::GetSpecificDistribution(
487 BrowserDistribution::CHROME_BROWSER)));
488 multi_chrome->SetOption(installer::kOptionMultiInstall, true);
489 chrome = installer_state->AddProduct(&multi_chrome);
490 VLOG(1) << "Upgrading existing Chrome browser in multi-install mode.";
491 } else if (chrome_frame &&
492 chrome_frame->HasOption(installer::kOptionReadyMode)) {
493 // Chrome Frame with ready-mode is to be installed, yet Chrome is
494 // neither installed nor being installed. Fail.
495 LOG(ERROR) << "Cannot install Chrome Frame in ready mode without Chrome.";
496 *status = installer::READY_MODE_REQUIRES_CHROME;
497 installer_state->WriteInstallerResult(
498 *status, IDS_INSTALL_READY_MODE_REQUIRES_CHROME_BASE, NULL);
502 // Fail if we're installing Chrome Frame when a single-install of it is
503 // already installed.
504 if (chrome_frame && cf_state && !cf_state->is_multi_install()) {
505 LOG(ERROR) << "Cannot migrate existing Chrome Frame installation to "
507 *status = installer::NON_MULTI_INSTALLATION_EXISTS;
508 installer_state->WriteInstallerResult(*status,
509 IDS_INSTALL_NON_MULTI_INSTALLATION_EXISTS_BASE, NULL);
513 // This is a non-multi installation.
515 // Check for an existing installation of the product.
516 const ProductState* product_state = original_state.GetProductState(
517 system_level, products[0]->distribution()->GetType());
518 if (product_state != NULL) {
519 // Block downgrades from multi-install to single-install.
520 if (product_state->is_multi_install()) {
521 LOG(ERROR) << "Multi-install "
522 << products[0]->distribution()->GetDisplayName()
523 << " exists; aborting single install.";
524 *status = installer::MULTI_INSTALLATION_EXISTS;
525 installer_state->WriteInstallerResult(*status,
526 IDS_INSTALL_MULTI_INSTALLATION_EXISTS_BASE, NULL);
535 // Checks app host pre-install conditions, specifically that this is a
536 // user-level multi-install. When the pre-install conditions are not
537 // satisfied, the result is written to the registry (via WriteInstallerResult),
538 // |status| is set appropriately, and false is returned.
539 bool CheckAppHostPreconditions(const InstallationState& original_state,
540 InstallerState* installer_state,
541 installer::InstallStatus* status) {
542 if (installer_state->FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
543 if (!installer_state->is_multi_install()) {
544 LOG(DFATAL) << "App Launcher requires multi install";
545 *status = installer::APP_HOST_REQUIRES_MULTI_INSTALL;
546 // No message string since there is nothing a user can do.
547 installer_state->WriteInstallerResult(*status, 0, NULL);
551 if (installer_state->system_install()) {
552 LOG(DFATAL) << "App Launcher may only be installed at user-level.";
553 *status = installer::APP_HOST_REQUIRES_USER_LEVEL;
554 // No message string since there is nothing a user can do.
555 installer_state->WriteInstallerResult(*status, 0, NULL);
563 // Checks for compatibility between the current state of the system and the
564 // desired operation. Also applies policy that mutates the desired operation;
565 // specifically, the |installer_state| object.
566 // Also blocks simultaneous user-level and system-level installs. In the case
567 // of trying to install user-level Chrome when system-level exists, the
568 // existing system-level Chrome is launched.
569 // When the pre-install conditions are not satisfied, the result is written to
570 // the registry (via WriteInstallerResult), |status| is set appropriately, and
571 // false is returned.
572 bool CheckPreInstallConditions(const InstallationState& original_state,
573 InstallerState* installer_state,
574 installer::InstallStatus* status) {
575 if (!CheckAppHostPreconditions(original_state, installer_state, status)) {
576 DCHECK_NE(*status, installer::UNKNOWN_STATUS);
580 // See what products are already installed in multi mode. When we do multi
581 // installs, we must upgrade all installations since they share the binaries.
582 AddExistingMultiInstalls(original_state, installer_state);
584 if (!CheckMultiInstallConditions(original_state, installer_state, status)) {
585 DCHECK_NE(*status, installer::UNKNOWN_STATUS);
589 const Products& products = installer_state->products();
590 if (products.empty()) {
591 // We haven't been given any products on which to operate.
593 << "Not given any products to install and no products found to update.";
594 *status = installer::CHROME_NOT_INSTALLED;
595 installer_state->WriteInstallerResult(*status,
596 IDS_INSTALL_NO_PRODUCTS_TO_UPDATE_BASE, NULL);
600 if (!installer_state->system_install()) {
601 // This is a user-level installation. Make sure that we are not installing
602 // on top of an existing system-level installation.
603 for (Products::const_iterator it = products.begin(); it < products.end();
605 const Product& product = **it;
606 BrowserDistribution* browser_dist = product.distribution();
608 // Skip over the binaries, as it's okay for them to be at both levels
609 // for different products.
610 if (browser_dist->GetType() == BrowserDistribution::CHROME_BINARIES)
613 const ProductState* user_level_product_state =
614 original_state.GetProductState(false, browser_dist->GetType());
615 const ProductState* system_level_product_state =
616 original_state.GetProductState(true, browser_dist->GetType());
618 // Allow upgrades to proceed so that out-of-date versions are not left
620 if (user_level_product_state)
623 // This is a new user-level install...
625 if (system_level_product_state) {
626 // ... and the product already exists at system-level.
627 LOG(ERROR) << "Already installed version "
628 << system_level_product_state->version().GetString()
629 << " at system-level conflicts with this one at user-level.";
630 if (product.is_chrome()) {
631 // Instruct Google Update to launch the existing system-level Chrome.
632 // There should be no error dialog.
633 base::FilePath install_path(installer::GetChromeInstallPath(
636 if (install_path.empty()) {
637 // Give up if we failed to construct the install path.
638 *status = installer::OS_ERROR;
639 installer_state->WriteInstallerResult(*status,
640 IDS_INSTALL_OS_ERROR_BASE,
643 *status = installer::EXISTING_VERSION_LAUNCHED;
644 base::FilePath chrome_exe =
645 install_path.Append(installer::kChromeExe);
646 CommandLine cmd(chrome_exe);
647 cmd.AppendSwitch(switches::kForceFirstRun);
648 installer_state->WriteInstallerResult(*status, 0, NULL);
649 VLOG(1) << "Launching existing system-level chrome instead.";
650 base::LaunchProcess(cmd, base::LaunchOptions(), NULL);
653 // Display an error message for other products.
654 *status = installer::SYSTEM_LEVEL_INSTALL_EXISTS;
655 installer_state->WriteInstallerResult(
656 *status, IDS_INSTALL_SYSTEM_LEVEL_EXISTS_BASE, NULL);
662 } else { // System-level install.
663 // --ensure-google-update-present is supported for user-level only.
664 // The flag is generic, but its primary use case involves App Host.
665 if (installer_state->ensure_google_update_present()) {
666 LOG(DFATAL) << "--" << installer::switches::kEnsureGoogleUpdatePresent
667 << " is supported for user-level only.";
668 *status = installer::APP_HOST_REQUIRES_USER_LEVEL;
669 // No message string since there is nothing a user can do.
670 installer_state->WriteInstallerResult(*status, 0, NULL);
678 // Initializes |temp_path| to "Temp" within the target directory, and
679 // |unpack_path| to a random directory beginning with "source" within
680 // |temp_path|. Returns false on error.
681 bool CreateTemporaryAndUnpackDirectories(
682 const InstallerState& installer_state,
683 installer::SelfCleaningTempDir* temp_path,
684 base::FilePath* unpack_path) {
685 DCHECK(temp_path && unpack_path);
687 if (!temp_path->Initialize(installer_state.target_path().DirName(),
688 installer::kInstallTempDir)) {
689 PLOG(ERROR) << "Could not create temporary path.";
692 VLOG(1) << "Created path " << temp_path->path().value();
694 if (!file_util::CreateTemporaryDirInDir(temp_path->path(),
695 installer::kInstallSourceDir,
697 PLOG(ERROR) << "Could not create temporary path for unpacked archive.";
704 installer::InstallStatus InstallProducts(
705 const InstallationState& original_state,
706 const CommandLine& cmd_line,
707 const MasterPreferences& prefs,
708 InstallerState* installer_state,
709 base::FilePath* installer_directory) {
710 DCHECK(installer_state);
711 const bool system_install = installer_state->system_install();
712 installer::InstallStatus install_status = installer::UNKNOWN_STATUS;
713 installer::ArchiveType archive_type = installer::UNKNOWN_ARCHIVE_TYPE;
714 bool delegated_to_existing = false;
715 installer_state->UpdateStage(installer::PRECONDITIONS);
716 // The stage provides more fine-grained information than -multifail, so remove
717 // the -multifail suffix from the Google Update "ap" value.
718 BrowserDistribution::GetSpecificDistribution(installer_state->state_type())->
719 UpdateInstallStatus(system_install, archive_type, install_status);
720 if (CheckPreInstallConditions(original_state, installer_state,
722 VLOG(1) << "Installing to " << installer_state->target_path().value();
723 install_status = InstallProductsHelper(
724 original_state, cmd_line, prefs, *installer_state,
725 installer_directory, &archive_type, &delegated_to_existing);
727 // CheckPreInstallConditions must set the status on failure.
728 DCHECK_NE(install_status, installer::UNKNOWN_STATUS);
731 // Delete the master preferences file if present. Note that we do not care
732 // about rollback here and we schedule for deletion on reboot if the delete
733 // fails. As such, we do not use DeleteTreeWorkItem.
734 if (cmd_line.HasSwitch(installer::switches::kInstallerData)) {
735 base::FilePath prefs_path(cmd_line.GetSwitchValuePath(
736 installer::switches::kInstallerData));
737 if (!base::DeleteFile(prefs_path, false)) {
738 LOG(ERROR) << "Failed deleting master preferences file "
739 << prefs_path.value()
740 << ", scheduling for deletion after reboot.";
741 ScheduleFileSystemEntityForDeletion(prefs_path);
745 // Early exit if this setup.exe delegated to another, since that one would
746 // have taken care of UpdateInstallStatus and UpdateStage.
747 if (delegated_to_existing)
748 return install_status;
750 const Products& products = installer_state->products();
751 for (Products::const_iterator it = products.begin(); it < products.end();
753 (*it)->distribution()->UpdateInstallStatus(
754 system_install, archive_type, install_status);
757 installer_state->UpdateStage(installer::NO_STAGE);
758 return install_status;
761 installer::InstallStatus UninstallProduct(
762 const InstallationState& original_state,
763 const InstallerState& installer_state,
764 const CommandLine& cmd_line,
766 bool force_uninstall,
767 const Product& product) {
768 const ProductState* product_state =
769 original_state.GetProductState(installer_state.system_install(),
770 product.distribution()->GetType());
771 if (product_state != NULL) {
772 VLOG(1) << "version on the system: "
773 << product_state->version().GetString();
774 } else if (!force_uninstall) {
775 LOG(ERROR) << product.distribution()->GetDisplayName()
776 << " not found for uninstall.";
777 return installer::CHROME_NOT_INSTALLED;
780 return installer::UninstallProduct(
781 original_state, installer_state, cmd_line.GetProgram(), product,
782 remove_all, force_uninstall, cmd_line);
785 installer::InstallStatus UninstallProducts(
786 const InstallationState& original_state,
787 const InstallerState& installer_state,
788 const CommandLine& cmd_line) {
789 const Products& products = installer_state.products();
791 // Decide whether Active Setup should be triggered and/or system-level Chrome
792 // should be launched post-uninstall. This needs to be done outside the
793 // UninstallProduct calls as some of them might terminate the processes
794 // launched by a previous one otherwise...
795 bool trigger_active_setup = false;
796 // System-level Chrome will be launched via this command if its program gets
798 CommandLine system_level_cmd(CommandLine::NO_PROGRAM);
800 const Product* chrome =
801 installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER);
803 // InstallerState::Initialize always puts Chrome first, and we rely on that
804 // here for this reason: if Chrome is in-use, the user will be prompted to
805 // confirm uninstallation. Upon cancel, we should not continue with the
807 DCHECK(products[0]->is_chrome());
809 if (cmd_line.HasSwitch(installer::switches::kSelfDestruct) &&
810 !installer_state.system_install()) {
811 BrowserDistribution* dist = chrome->distribution();
812 const base::FilePath system_exe_path(
813 installer::GetChromeInstallPath(true, dist)
814 .Append(installer::kChromeExe));
815 system_level_cmd.SetProgram(system_exe_path);
817 base::FilePath first_run_sentinel;
818 InstallUtil::GetSentinelFilePath(
819 chrome::kFirstRunSentinel, dist, &first_run_sentinel);
820 if (base::PathExists(first_run_sentinel)) {
821 // If the Chrome being self-destructed has already undergone First Run,
822 // trigger Active Setup and make sure the system-level Chrome doesn't go
823 // through first run.
824 trigger_active_setup = true;
825 system_level_cmd.AppendSwitch(::switches::kCancelFirstRun);
829 if (installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)) {
830 // Chrome Binaries should be last; if something else is cancelled, they
832 DCHECK(products[products.size() - 1]->is_chrome_binaries());
835 installer::InstallStatus install_status = installer::UNINSTALL_SUCCESSFUL;
836 installer::InstallStatus prod_status = installer::UNKNOWN_STATUS;
837 const bool force = cmd_line.HasSwitch(installer::switches::kForceUninstall);
838 const bool remove_all = !cmd_line.HasSwitch(
839 installer::switches::kDoNotRemoveSharedItems);
841 for (Products::const_iterator it = products.begin();
842 install_status != installer::UNINSTALL_CANCELLED && it < products.end();
844 prod_status = UninstallProduct(original_state, installer_state,
845 cmd_line, remove_all, force, **it);
846 if (prod_status != installer::UNINSTALL_SUCCESSFUL)
847 install_status = prod_status;
850 installer::CleanUpInstallationDirectoryAfterUninstall(
851 original_state, installer_state, cmd_line, &install_status);
853 // The app and vendor dirs may now be empty. Make a last-ditch attempt to
855 installer::DeleteChromeDirectoriesIfEmpty(installer_state.target_path());
857 if (trigger_active_setup)
858 InstallUtil::TriggerActiveSetupCommand();
860 if (!system_level_cmd.GetProgram().empty())
861 base::LaunchProcess(system_level_cmd, base::LaunchOptions(), NULL);
863 // Tell Google Update that an uninstall has taken place.
864 // Ignore the return value: success or failure of Google Update
865 // has no bearing on the success or failure of Chrome's uninstallation.
866 google_update::UninstallGoogleUpdate(installer_state.system_install());
868 return install_status;
871 installer::InstallStatus ShowEULADialog(const string16& inner_frame) {
872 VLOG(1) << "About to show EULA";
873 string16 eula_path = installer::GetLocalizedEulaResource();
874 if (eula_path.empty()) {
875 LOG(ERROR) << "No EULA path available";
876 return installer::EULA_REJECTED;
878 // Newer versions of the caller pass an inner frame parameter that must
879 // be given to the html page being launched.
880 installer::EulaHTMLDialog dlg(eula_path, inner_frame);
881 installer::EulaHTMLDialog::Outcome outcome = dlg.ShowModal();
882 if (installer::EulaHTMLDialog::REJECTED == outcome) {
883 LOG(ERROR) << "EULA rejected or EULA failure";
884 return installer::EULA_REJECTED;
886 if (installer::EulaHTMLDialog::ACCEPTED_OPT_IN == outcome) {
887 VLOG(1) << "EULA accepted (opt-in)";
888 return installer::EULA_ACCEPTED_OPT_IN;
890 VLOG(1) << "EULA accepted (no opt-in)";
891 return installer::EULA_ACCEPTED;
894 // Creates the sentinel indicating that the EULA was required and has been
896 bool CreateEULASentinel(BrowserDistribution* dist) {
897 base::FilePath eula_sentinel;
898 if (!InstallUtil::GetSentinelFilePath(installer::kEULASentinelFile,
899 dist, &eula_sentinel)) {
903 return (file_util::CreateDirectory(eula_sentinel.DirName()) &&
904 file_util::WriteFile(eula_sentinel, "", 0) != -1);
907 void ActivateMetroChrome() {
908 // Check to see if we're per-user or not. Need to do this since we may
909 // not have been invoked with --system-level even for a machine install.
910 wchar_t exe_path[MAX_PATH * 2] = {};
911 GetModuleFileName(NULL, exe_path, arraysize(exe_path));
912 bool is_per_user_install = InstallUtil::IsPerUserInstall(exe_path);
914 string16 app_model_id =
915 ShellUtil::GetBrowserModelId(BrowserDistribution::GetDistribution(),
916 is_per_user_install);
918 base::win::ScopedComPtr<IApplicationActivationManager> activator;
919 HRESULT hr = activator.CreateInstance(CLSID_ApplicationActivationManager);
922 hr = activator->ActivateApplication(
923 app_model_id.c_str(), L"open", AO_NONE, &pid);
926 LOG_IF(ERROR, FAILED(hr)) << "Tried and failed to launch Metro Chrome. "
927 << "hr=" << std::hex << hr;
930 installer::InstallStatus RegisterDevChrome(
931 const InstallationState& original_state,
932 const InstallerState& installer_state,
933 const CommandLine& cmd_line) {
934 BrowserDistribution* chrome_dist =
935 BrowserDistribution::GetSpecificDistribution(
936 BrowserDistribution::CHROME_BROWSER);
938 // Only proceed with registering a dev chrome if no real Chrome installation
939 // of the same distribution are present on this system.
940 const ProductState* existing_chrome =
941 original_state.GetProductState(false,
942 BrowserDistribution::CHROME_BROWSER);
943 if (!existing_chrome) {
945 original_state.GetProductState(true, BrowserDistribution::CHROME_BROWSER);
947 if (existing_chrome) {
948 static const wchar_t kPleaseUninstallYourChromeMessage[] =
949 L"You already have a full-installation (non-dev) of %1ls, please "
950 L"uninstall it first using Add/Remove Programs in the control panel.";
951 string16 name(chrome_dist->GetDisplayName());
952 string16 message(base::StringPrintf(kPleaseUninstallYourChromeMessage,
955 LOG(ERROR) << "Aborting operation: another installation of " << name
956 << " was found, as a last resort (if the product is not present "
957 "in Add/Remove Programs), try executing: "
958 << existing_chrome->uninstall_command().GetCommandLineString();
959 MessageBox(NULL, message.c_str(), NULL, MB_ICONERROR);
960 return installer::INSTALL_FAILED;
963 base::FilePath chrome_exe(
964 cmd_line.GetSwitchValuePath(installer::switches::kRegisterDevChrome));
965 if (chrome_exe.empty())
966 chrome_exe = cmd_line.GetProgram().DirName().Append(installer::kChromeExe);
967 if (!chrome_exe.IsAbsolute())
968 chrome_exe = base::MakeAbsoluteFilePath(chrome_exe);
970 installer::InstallStatus status = installer::FIRST_INSTALL_SUCCESS;
971 if (base::PathExists(chrome_exe)) {
972 Product chrome(chrome_dist);
974 // Create the Start menu shortcut and pin it to the taskbar.
975 ShellUtil::ShortcutProperties shortcut_properties(ShellUtil::CURRENT_USER);
976 chrome.AddDefaultShortcutProperties(chrome_exe, &shortcut_properties);
977 shortcut_properties.set_dual_mode(true);
978 shortcut_properties.set_pin_to_taskbar(true);
979 ShellUtil::CreateOrUpdateShortcut(
980 ShellUtil::SHORTCUT_LOCATION_START_MENU, chrome_dist,
981 shortcut_properties, ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS);
983 // Register Chrome at user-level and make it default.
984 scoped_ptr<WorkItemList> delegate_execute_list(
985 WorkItem::CreateWorkItemList());
986 installer::AddDelegateExecuteWorkItems(
987 installer_state, chrome_exe.DirName(), Version(), chrome,
988 delegate_execute_list.get());
989 delegate_execute_list->Do();
990 if (ShellUtil::CanMakeChromeDefaultUnattended()) {
991 ShellUtil::MakeChromeDefault(
992 chrome_dist, ShellUtil::CURRENT_USER, chrome_exe.value(), true);
994 ShellUtil::ShowMakeChromeDefaultSystemUI(chrome_dist, chrome_exe.value());
997 LOG(ERROR) << "Path not found: " << chrome_exe.value();
998 status = installer::INSTALL_FAILED;
1003 // This method processes any command line options that make setup.exe do
1004 // various tasks other than installation (renaming chrome.exe, showing eula
1005 // among others). This function returns true if any such command line option
1006 // has been found and processed (so setup.exe should exit at that point).
1007 bool HandleNonInstallCmdLineOptions(const InstallationState& original_state,
1008 const CommandLine& cmd_line,
1009 InstallerState* installer_state,
1011 // TODO(gab): Add a local |status| variable which each block below sets;
1012 // only determine the |exit_code| from |status| at the end (this will allow
1013 // this method to validate that
1014 // (!handled || status != installer::UNKNOWN_STATUS)).
1015 bool handled = true;
1016 // TODO(tommi): Split these checks up into functions and use a data driven
1017 // map of switch->function.
1018 if (cmd_line.HasSwitch(installer::switches::kUpdateSetupExe)) {
1019 installer::InstallStatus status = installer::SETUP_PATCH_FAILED;
1020 // If --update-setup-exe command line option is given, we apply the given
1021 // patch to current exe, and store the resulting binary in the path
1022 // specified by --new-setup-exe. But we need to first unpack the file
1023 // given in --update-setup-exe.
1024 base::ScopedTempDir temp_path;
1025 if (!temp_path.CreateUniqueTempDir()) {
1026 PLOG(ERROR) << "Could not create temporary path.";
1028 base::FilePath compressed_archive(cmd_line.GetSwitchValuePath(
1029 installer::switches::kUpdateSetupExe));
1030 VLOG(1) << "Opening archive " << compressed_archive.value();
1031 if (installer::ArchivePatchHelper::UncompressAndPatch(
1034 cmd_line.GetProgram(),
1035 cmd_line.GetSwitchValuePath(installer::switches::kNewSetupExe))) {
1036 status = installer::NEW_VERSION_UPDATED;
1038 if (!temp_path.Delete()) {
1039 // PLOG would be nice, but Delete() doesn't leave a meaningful value in
1040 // the Windows last-error code.
1041 LOG(WARNING) << "Scheduling temporary path " << temp_path.path().value()
1042 << " for deletion at reboot.";
1043 ScheduleDirectoryForDeletion(temp_path.path());
1047 *exit_code = InstallUtil::GetInstallReturnCode(status);
1049 LOG(WARNING) << "setup.exe patching failed.";
1050 installer_state->WriteInstallerResult(
1051 status, IDS_SETUP_PATCH_FAILED_BASE, NULL);
1053 // We will be exiting normally, so clear the stage indicator.
1054 installer_state->UpdateStage(installer::NO_STAGE);
1055 } else if (cmd_line.HasSwitch(installer::switches::kShowEula)) {
1056 // Check if we need to show the EULA. If it is passed as a command line
1057 // then the dialog is shown and regardless of the outcome setup exits here.
1058 string16 inner_frame =
1059 cmd_line.GetSwitchValueNative(installer::switches::kShowEula);
1060 *exit_code = ShowEULADialog(inner_frame);
1062 if (installer::EULA_REJECTED != *exit_code) {
1063 if (GoogleUpdateSettings::SetEULAConsent(
1064 original_state, BrowserDistribution::GetDistribution(), true)) {
1065 CreateEULASentinel(BrowserDistribution::GetDistribution());
1067 // For a metro-originated launch, we now need to launch back into metro.
1068 if (cmd_line.HasSwitch(installer::switches::kShowEulaForMetro))
1069 ActivateMetroChrome();
1071 } else if (cmd_line.HasSwitch(installer::switches::kConfigureUserSettings)) {
1072 // NOTE: Should the work done here, on kConfigureUserSettings, change:
1073 // kActiveSetupVersion in install_worker.cc needs to be increased for Active
1074 // Setup to invoke this again for all users of this install.
1075 const Product* chrome_install =
1076 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
1077 installer::InstallStatus status = installer::INVALID_STATE_FOR_OPTION;
1078 if (chrome_install && installer_state->system_install()) {
1080 cmd_line.HasSwitch(installer::switches::kForceConfigureUserSettings);
1081 installer::HandleActiveSetupForBrowser(installer_state->target_path(),
1082 *chrome_install, force);
1083 status = installer::INSTALL_REPAIRED;
1085 LOG(DFATAL) << "chrome_install:" << chrome_install
1086 << ", system_install:" << installer_state->system_install();
1088 *exit_code = InstallUtil::GetInstallReturnCode(status);
1089 } else if (cmd_line.HasSwitch(installer::switches::kRegisterDevChrome)) {
1090 installer::InstallStatus status = RegisterDevChrome(
1091 original_state, *installer_state, cmd_line);
1092 *exit_code = InstallUtil::GetInstallReturnCode(status);
1093 } else if (cmd_line.HasSwitch(installer::switches::kRegisterChromeBrowser)) {
1094 installer::InstallStatus status = installer::UNKNOWN_STATUS;
1095 const Product* chrome_install =
1096 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
1097 if (chrome_install) {
1098 // If --register-chrome-browser option is specified, register all
1099 // Chrome protocol/file associations, as well as register it as a valid
1100 // browser for Start Menu->Internet shortcut. This switch will also
1101 // register Chrome as a valid handler for a set of URL protocols that
1102 // Chrome may become the default handler for, either by the user marking
1103 // Chrome as the default browser, through the Windows Default Programs
1104 // control panel settings, or through website use of
1105 // registerProtocolHandler. These protocols are found in
1106 // ShellUtil::kPotentialProtocolAssociations.
1107 // The --register-url-protocol will additionally register Chrome as a
1108 // potential handler for the supplied protocol, and is used if a website
1109 // registers a handler for a protocol not found in
1110 // ShellUtil::kPotentialProtocolAssociations.
1111 // These options should only be used when setup.exe is launched with admin
1112 // rights. We do not make any user specific changes with this option.
1113 DCHECK(IsUserAnAdmin());
1114 string16 chrome_exe(cmd_line.GetSwitchValueNative(
1115 installer::switches::kRegisterChromeBrowser));
1117 if (cmd_line.HasSwitch(
1118 installer::switches::kRegisterChromeBrowserSuffix)) {
1119 suffix = cmd_line.GetSwitchValueNative(
1120 installer::switches::kRegisterChromeBrowserSuffix);
1122 if (cmd_line.HasSwitch(installer::switches::kRegisterURLProtocol)) {
1123 string16 protocol = cmd_line.GetSwitchValueNative(
1124 installer::switches::kRegisterURLProtocol);
1125 // ShellUtil::RegisterChromeForProtocol performs all registration
1126 // done by ShellUtil::RegisterChromeBrowser, as well as registering
1127 // with Windows as capable of handling the supplied protocol.
1128 if (ShellUtil::RegisterChromeForProtocol(
1129 chrome_install->distribution(), chrome_exe, suffix, protocol,
1131 status = installer::IN_USE_UPDATED;
1133 if (ShellUtil::RegisterChromeBrowser(chrome_install->distribution(),
1134 chrome_exe, suffix, false))
1135 status = installer::IN_USE_UPDATED;
1138 LOG(DFATAL) << "Can't register browser - Chrome distribution not found";
1140 *exit_code = InstallUtil::GetInstallReturnCode(status);
1141 } else if (cmd_line.HasSwitch(installer::switches::kRenameChromeExe)) {
1142 // If --rename-chrome-exe is specified, we want to rename the executables
1144 *exit_code = RenameChromeExecutables(original_state, installer_state);
1145 } else if (cmd_line.HasSwitch(
1146 installer::switches::kRemoveChromeRegistration)) {
1147 // This is almost reverse of --register-chrome-browser option above.
1148 // Here we delete Chrome browser registration. This option should only
1149 // be used when setup.exe is launched with admin rights. We do not
1150 // make any user specific changes in this option.
1152 if (cmd_line.HasSwitch(
1153 installer::switches::kRegisterChromeBrowserSuffix)) {
1154 suffix = cmd_line.GetSwitchValueNative(
1155 installer::switches::kRegisterChromeBrowserSuffix);
1157 installer::InstallStatus tmp = installer::UNKNOWN_STATUS;
1158 const Product* chrome_install =
1159 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
1160 DCHECK(chrome_install);
1161 if (chrome_install) {
1162 installer::DeleteChromeRegistrationKeys(*installer_state,
1163 chrome_install->distribution(), HKEY_LOCAL_MACHINE, suffix, &tmp);
1166 } else if (cmd_line.HasSwitch(installer::switches::kOnOsUpgrade)) {
1167 const Product* chrome_install =
1168 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
1169 installer::InstallStatus status = installer::INVALID_STATE_FOR_OPTION;
1170 if (chrome_install) {
1171 installer::HandleOsUpgradeForBrowser(*installer_state,
1173 status = installer::INSTALL_REPAIRED;
1175 LOG(DFATAL) << "Chrome product not found.";
1177 *exit_code = InstallUtil::GetInstallReturnCode(status);
1178 } else if (cmd_line.HasSwitch(installer::switches::kQueryEULAAcceptance)) {
1179 *exit_code = installer::IsEULAAccepted(installer_state->system_install());
1180 } else if (cmd_line.HasSwitch(installer::switches::kInactiveUserToast)) {
1181 // Launch the inactive user toast experiment.
1183 base::StringToInt(cmd_line.GetSwitchValueNative(
1184 installer::switches::kInactiveUserToast), &flavor);
1185 std::string experiment_group =
1186 cmd_line.GetSwitchValueASCII(installer::switches::kExperimentGroup);
1187 DCHECK_NE(-1, flavor);
1189 *exit_code = installer::UNKNOWN_STATUS;
1191 // This code is called (via setup.exe relaunch) only if a product is known
1192 // to run user experiments, so no check is required.
1193 const Products& products = installer_state->products();
1194 for (Products::const_iterator it = products.begin(); it < products.end();
1196 const Product& product = **it;
1197 installer::InactiveUserToastExperiment(
1198 flavor, ASCIIToUTF16(experiment_group), product,
1199 installer_state->target_path());
1202 } else if (cmd_line.HasSwitch(installer::switches::kSystemLevelToast)) {
1203 const Products& products = installer_state->products();
1204 for (Products::const_iterator it = products.begin(); it < products.end();
1206 const Product& product = **it;
1207 BrowserDistribution* browser_dist = product.distribution();
1208 // We started as system-level and have been re-launched as user level
1209 // to continue with the toast experiment.
1210 Version installed_version;
1211 InstallUtil::GetChromeVersion(browser_dist, true, &installed_version);
1212 if (!installed_version.IsValid()) {
1213 LOG(ERROR) << "No installation of "
1214 << browser_dist->GetDisplayName()
1215 << " found for system-level toast.";
1217 product.LaunchUserExperiment(
1218 cmd_line.GetProgram(), installer::REENTRY_SYS_UPDATE, true);
1221 } else if (cmd_line.HasSwitch(
1222 installer::switches::kChromeFrameReadyModeOptIn)) {
1223 *exit_code = InstallUtil::GetInstallReturnCode(
1224 installer::ChromeFrameReadyModeOptIn(original_state, *installer_state));
1225 } else if (cmd_line.HasSwitch(
1226 installer::switches::kChromeFrameReadyModeTempOptOut)) {
1227 *exit_code = InstallUtil::GetInstallReturnCode(
1228 installer::ChromeFrameReadyModeTempOptOut(original_state,
1230 } else if (cmd_line.HasSwitch(
1231 installer::switches::kChromeFrameReadyModeEndTempOptOut)) {
1232 *exit_code = InstallUtil::GetInstallReturnCode(
1233 installer::ChromeFrameReadyModeEndTempOptOut(original_state,
1235 } else if (cmd_line.HasSwitch(installer::switches::kChromeFrameQuickEnable)) {
1236 *exit_code = installer::ChromeFrameQuickEnable(original_state,
1238 } else if (cmd_line.HasSwitch(installer::switches::kPatch)) {
1239 const std::string patch_type_str(
1240 cmd_line.GetSwitchValueASCII(installer::switches::kPatch));
1241 const base::FilePath input_file(
1242 cmd_line.GetSwitchValuePath(installer::switches::kInputFile));
1243 const base::FilePath patch_file(
1244 cmd_line.GetSwitchValuePath(installer::switches::kPatchFile));
1245 const base::FilePath output_file(
1246 cmd_line.GetSwitchValuePath(installer::switches::kOutputFile));
1248 if (patch_type_str == installer::kCourgette) {
1249 *exit_code = installer::CourgettePatchFiles(input_file,
1252 } else if (patch_type_str == installer::kBsdiff) {
1253 *exit_code = installer::BsdiffPatchFiles(input_file,
1257 *exit_code = installer::PATCH_INVALID_ARGUMENTS;
1259 } else if (cmd_line.HasSwitch(installer::switches::kMigrateChromeFrame)) {
1260 *exit_code = MigrateChromeFrame(original_state, installer_state);
1268 bool ShowRebootDialog() {
1269 // Get a token for this process.
1271 if (!OpenProcessToken(GetCurrentProcess(),
1272 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
1274 LOG(ERROR) << "Failed to open token.";
1278 // Use a ScopedHandle to keep track of and eventually close our handle.
1279 // TODO(robertshield): Add a Receive() method to base's ScopedHandle.
1280 base::win::ScopedHandle scoped_handle(token);
1282 // Get the LUID for the shutdown privilege.
1283 TOKEN_PRIVILEGES tkp = {0};
1284 LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
1285 tkp.PrivilegeCount = 1;
1286 tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1288 // Get the shutdown privilege for this process.
1289 AdjustTokenPrivileges(token, FALSE, &tkp, 0,
1290 reinterpret_cast<PTOKEN_PRIVILEGES>(NULL), 0);
1291 if (GetLastError() != ERROR_SUCCESS) {
1292 LOG(ERROR) << "Unable to get shutdown privileges.";
1296 // Popup a dialog that will prompt to reboot using the default system message.
1297 // TODO(robertshield): Add a localized, more specific string to the prompt.
1298 RestartDialog(NULL, NULL, EWX_REBOOT | EWX_FORCEIFHUNG);
1302 // Returns the Custom information for the client identified by the exe path
1303 // passed in. This information is used for crash reporting.
1304 google_breakpad::CustomClientInfo* GetCustomInfo(const wchar_t* exe_path) {
1307 scoped_ptr<FileVersionInfo> version_info(
1308 FileVersionInfo::CreateFileVersionInfo(base::FilePath(exe_path)));
1309 if (version_info.get()) {
1310 version = version_info->product_version();
1311 product = version_info->product_short_name();
1314 if (version.empty())
1315 version = L"0.1.0.0";
1317 if (product.empty())
1318 product = L"Chrome Installer";
1320 static google_breakpad::CustomInfoEntry ver_entry(L"ver", version.c_str());
1321 static google_breakpad::CustomInfoEntry prod_entry(L"prod", product.c_str());
1322 static google_breakpad::CustomInfoEntry plat_entry(L"plat", L"Win32");
1323 static google_breakpad::CustomInfoEntry type_entry(L"ptype",
1324 L"Chrome Installer");
1325 static google_breakpad::CustomInfoEntry entries[] = {
1326 ver_entry, prod_entry, plat_entry, type_entry };
1327 static google_breakpad::CustomClientInfo custom_info = {
1328 entries, arraysize(entries) };
1329 return &custom_info;
1332 // Initialize crash reporting for this process. This involves connecting to
1334 google_breakpad::ExceptionHandler* InitializeCrashReporting(
1335 bool system_install) {
1336 // Only report crashes if the user allows it.
1337 if (!GoogleUpdateSettings::GetCollectStatsConsent())
1340 // Get the alternate dump directory. We use the temp path.
1341 base::FilePath temp_directory;
1342 if (!file_util::GetTempDir(&temp_directory) || temp_directory.empty())
1345 wchar_t exe_path[MAX_PATH * 2] = {0};
1346 GetModuleFileName(NULL, exe_path, arraysize(exe_path));
1348 // Build the pipe name. It can be either:
1349 // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18"
1350 // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>"
1351 string16 user_sid = kSystemPrincipalSid;
1353 if (!system_install) {
1354 if (!base::win::GetUserSidString(&user_sid)) {
1359 string16 pipe_name = kGoogleUpdatePipeName;
1360 pipe_name += user_sid;
1362 google_breakpad::ExceptionHandler* breakpad =
1363 new google_breakpad::ExceptionHandler(
1364 temp_directory.value(), NULL, NULL, NULL,
1365 google_breakpad::ExceptionHandler::HANDLER_ALL, kLargerDumpType,
1366 pipe_name.c_str(), GetCustomInfo(exe_path));
1372 namespace installer {
1374 InstallStatus InstallProductsHelper(
1375 const InstallationState& original_state,
1376 const CommandLine& cmd_line,
1377 const MasterPreferences& prefs,
1378 const InstallerState& installer_state,
1379 base::FilePath* installer_directory,
1380 ArchiveType* archive_type,
1381 bool* delegated_to_existing) {
1382 DCHECK(archive_type);
1383 DCHECK(delegated_to_existing);
1384 const bool system_install = installer_state.system_install();
1385 InstallStatus install_status = UNKNOWN_STATUS;
1387 // Drop to background processing mode if the process was started below the
1388 // normal process priority class.
1389 bool entered_background_mode = AdjustProcessPriority();
1390 VLOG_IF(1, entered_background_mode) << "Entered background processing mode.";
1392 // Create a temp folder where we will unpack Chrome archive. If it fails,
1393 // then we are doomed, so return immediately and no cleanup is required.
1394 SelfCleaningTempDir temp_path;
1395 base::FilePath unpack_path;
1396 if (!CreateTemporaryAndUnpackDirectories(installer_state, &temp_path,
1398 installer_state.WriteInstallerResult(TEMP_DIR_FAILED,
1399 IDS_INSTALL_TEMP_DIR_FAILED_BASE,
1401 return TEMP_DIR_FAILED;
1404 // Uncompress and optionally patch the archive if an uncompressed archive was
1405 // not specified on the command line and a compressed archive is found.
1406 *archive_type = UNKNOWN_ARCHIVE_TYPE;
1407 base::FilePath uncompressed_archive(cmd_line.GetSwitchValuePath(
1408 switches::kUncompressedArchive));
1409 if (uncompressed_archive.empty()) {
1410 scoped_ptr<ArchivePatchHelper> archive_helper(
1411 CreateChromeArchiveHelper(cmd_line, installer_state, unpack_path));
1412 if (archive_helper) {
1413 VLOG(1) << "Installing Chrome from compressed archive "
1414 << archive_helper->compressed_archive().value();
1415 if (!UncompressAndPatchChromeArchive(original_state,
1417 archive_helper.get(),
1420 DCHECK_NE(install_status, UNKNOWN_STATUS);
1421 return install_status;
1423 uncompressed_archive = archive_helper->target();
1424 DCHECK(!uncompressed_archive.empty());
1428 // Check for an uncompressed archive alongside the current executable if one
1429 // was not given or generated.
1430 if (uncompressed_archive.empty()) {
1431 uncompressed_archive =
1432 cmd_line.GetProgram().DirName().Append(kChromeArchive);
1435 if (*archive_type == UNKNOWN_ARCHIVE_TYPE) {
1436 // An archive was not uncompressed or patched above.
1437 if (uncompressed_archive.empty() ||
1438 !base::PathExists(uncompressed_archive)) {
1439 LOG(ERROR) << "Cannot install Chrome without an uncompressed archive.";
1440 installer_state.WriteInstallerResult(
1441 INVALID_ARCHIVE, IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL);
1442 return INVALID_ARCHIVE;
1444 *archive_type = FULL_ARCHIVE_TYPE;
1447 // Unpack the uncompressed archive.
1448 if (LzmaUtil::UnPackArchive(uncompressed_archive.value(),
1449 unpack_path.value(),
1451 installer_state.WriteInstallerResult(
1452 UNCOMPRESSION_FAILED,
1453 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
1455 return UNCOMPRESSION_FAILED;
1458 VLOG(1) << "unpacked to " << unpack_path.value();
1459 base::FilePath src_path(
1460 unpack_path.Append(kInstallSourceChromeDir));
1462 installer_version(GetMaxVersionFromArchiveDir(src_path));
1463 if (!installer_version.get()) {
1464 LOG(ERROR) << "Did not find any valid version in installer.";
1465 install_status = INVALID_ARCHIVE;
1466 installer_state.WriteInstallerResult(install_status,
1467 IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL);
1469 VLOG(1) << "version to install: " << installer_version->GetString();
1470 bool proceed_with_installation = true;
1472 if (installer_state.operation() == InstallerState::MULTI_INSTALL) {
1473 // This is a new install of a multi-install product. Rather than give up
1474 // in case a higher version of the binaries (including a single-install
1475 // of Chrome, which can safely be migrated to multi-install by way of
1476 // CheckMultiInstallConditions) is already installed, delegate to the
1477 // installed setup.exe to install the product at hand.
1478 base::FilePath setup_exe;
1479 if (GetExistingHigherInstaller(original_state, system_install,
1480 *installer_version, &setup_exe)) {
1481 VLOG(1) << "Deferring to existing installer.";
1482 installer_state.UpdateStage(DEFERRING_TO_HIGHER_VERSION);
1483 if (DeferToExistingInstall(setup_exe, cmd_line, installer_state,
1484 temp_path.path(), &install_status)) {
1485 *delegated_to_existing = true;
1486 return install_status;
1491 uint32 higher_products = 0;
1493 sizeof(higher_products) * 8 > BrowserDistribution::NUM_TYPES,
1494 too_many_distribution_types_);
1495 const Products& products = installer_state.products();
1496 for (Products::const_iterator it = products.begin(); it < products.end();
1498 const Product& product = **it;
1499 const ProductState* product_state =
1500 original_state.GetProductState(system_install,
1501 product.distribution()->GetType());
1502 if (product_state != NULL &&
1503 (product_state->version().CompareTo(*installer_version) > 0)) {
1504 LOG(ERROR) << "Higher version of "
1505 << product.distribution()->GetDisplayName()
1506 << " is already installed.";
1507 higher_products |= (1 << product.distribution()->GetType());
1511 if (higher_products != 0) {
1512 COMPILE_ASSERT(BrowserDistribution::NUM_TYPES == 4,
1513 add_support_for_new_products_here_);
1514 const uint32 kBrowserBit = 1 << BrowserDistribution::CHROME_BROWSER;
1515 const uint32 kGCFBit = 1 << BrowserDistribution::CHROME_FRAME;
1516 const uint32 kAppHostBit = 1 << BrowserDistribution::CHROME_APP_HOST;
1519 proceed_with_installation = false;
1520 install_status = HIGHER_VERSION_EXISTS;
1521 switch (higher_products) {
1523 message_id = IDS_INSTALL_HIGHER_VERSION_BASE;
1526 message_id = IDS_INSTALL_HIGHER_VERSION_CF_BASE;
1528 case kGCFBit | kBrowserBit:
1529 message_id = IDS_INSTALL_HIGHER_VERSION_CB_CF_BASE;
1532 message_id = IDS_INSTALL_HIGHER_VERSION_APP_LAUNCHER_BASE;
1536 installer_state.WriteInstallerResult(install_status, message_id, NULL);
1539 proceed_with_installation =
1540 proceed_with_installation &&
1541 CheckGroupPolicySettings(original_state, installer_state,
1542 *installer_version, &install_status);
1544 if (proceed_with_installation) {
1545 // If Google Update is absent at user-level, install it using the
1546 // Google Update installer from an existing system-level installation.
1547 // This is for quick-enable App Host install from a system-level
1548 // Chrome Binaries installation.
1549 if (!system_install && installer_state.ensure_google_update_present()) {
1550 if (!google_update::EnsureUserLevelGoogleUpdatePresent()) {
1551 LOG(ERROR) << "Failed to install Google Update";
1552 proceed_with_installation = false;
1553 install_status = INSTALL_OF_GOOGLE_UPDATE_FAILED;
1554 installer_state.WriteInstallerResult(install_status, 0, NULL);
1560 if (proceed_with_installation) {
1561 base::FilePath prefs_source_path(cmd_line.GetSwitchValueNative(
1562 switches::kInstallerData));
1563 install_status = InstallOrUpdateProduct(
1564 original_state, installer_state, cmd_line.GetProgram(),
1565 uncompressed_archive, temp_path.path(), src_path, prefs_source_path,
1566 prefs, *installer_version);
1568 int install_msg_base = IDS_INSTALL_FAILED_BASE;
1569 string16 chrome_exe;
1570 string16 quoted_chrome_exe;
1571 if (install_status == SAME_VERSION_REPAIR_FAILED) {
1572 if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) {
1573 install_msg_base = IDS_SAME_VERSION_REPAIR_FAILED_CF_BASE;
1575 install_msg_base = IDS_SAME_VERSION_REPAIR_FAILED_BASE;
1577 } else if (install_status != INSTALL_FAILED) {
1578 if (installer_state.target_path().empty()) {
1579 // If we failed to construct install path, it means the OS call to
1580 // get %ProgramFiles% or %AppData% failed. Report this as failure.
1581 install_msg_base = IDS_INSTALL_OS_ERROR_BASE;
1582 install_status = OS_ERROR;
1584 chrome_exe = installer_state.target_path().Append(kChromeExe).value();
1585 quoted_chrome_exe = L"\"" + chrome_exe + L"\"";
1586 install_msg_base = 0;
1590 installer_state.UpdateStage(FINISHING);
1592 // Only do Chrome-specific stuff (like launching the browser) if
1593 // Chrome was specifically requested (rather than being upgraded as
1594 // part of a multi-install).
1595 const Product* chrome_install = prefs.install_chrome() ?
1596 installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) :
1599 bool do_not_register_for_update_launch = false;
1600 if (chrome_install) {
1601 prefs.GetBool(master_preferences::kDoNotRegisterForUpdateLaunch,
1602 &do_not_register_for_update_launch);
1604 do_not_register_for_update_launch = true; // Never register.
1607 bool write_chrome_launch_string =
1608 (!do_not_register_for_update_launch &&
1609 install_status != IN_USE_UPDATED);
1611 installer_state.WriteInstallerResult(install_status, install_msg_base,
1612 write_chrome_launch_string ? "ed_chrome_exe : NULL);
1614 if (install_status == FIRST_INSTALL_SUCCESS) {
1615 VLOG(1) << "First install successful.";
1616 if (chrome_install) {
1617 // We never want to launch Chrome in system level install mode.
1618 bool do_not_launch_chrome = false;
1619 prefs.GetBool(master_preferences::kDoNotLaunchChrome,
1620 &do_not_launch_chrome);
1621 if (!system_install && !do_not_launch_chrome)
1622 chrome_install->LaunchChrome(installer_state.target_path());
1624 } else if ((install_status == NEW_VERSION_UPDATED) ||
1625 (install_status == IN_USE_UPDATED)) {
1626 const Product* chrome = installer_state.FindProduct(
1627 BrowserDistribution::CHROME_BROWSER);
1628 if (chrome != NULL) {
1629 DCHECK_NE(chrome_exe, string16());
1630 RemoveChromeLegacyRegistryKeys(chrome->distribution(), chrome_exe);
1634 if (prefs.install_chrome_app_launcher() &&
1635 InstallUtil::GetInstallReturnCode(install_status) == 0) {
1636 std::string webstore_item(google_update::GetUntrustedDataValue(
1637 kInstallFromWebstore));
1638 if (!webstore_item.empty()) {
1639 bool success = InstallFromWebstore(webstore_item);
1640 VLOG_IF(1, !success) << "Failed to launch app installation.";
1646 // There might be an experiment (for upgrade usually) that needs to happen.
1647 // An experiment's outcome can include chrome's uninstallation. If that is
1648 // the case we would not do that directly at this point but in another
1649 // instance of setup.exe
1651 // There is another way to reach this same function if this is a system
1652 // level install. See HandleNonInstallCmdLineOptions().
1654 // If installation failed, use the path to the currently running setup.
1655 // If installation succeeded, use the path to setup in the installer dir.
1656 base::FilePath setup_path(cmd_line.GetProgram());
1657 if (InstallUtil::GetInstallReturnCode(install_status) == 0) {
1658 setup_path = installer_state.GetInstallerDirectory(*installer_version)
1659 .Append(setup_path.BaseName());
1661 const Products& products = installer_state.products();
1662 for (Products::const_iterator it = products.begin(); it < products.end();
1664 const Product& product = **it;
1665 product.LaunchUserExperiment(setup_path, install_status,
1670 // If installation completed successfully, return the path to the directory
1671 // containing the newly installed setup.exe and uncompressed archive if the
1672 // caller requested it.
1673 if (installer_directory &&
1674 InstallUtil::GetInstallReturnCode(install_status) == 0) {
1675 *installer_directory =
1676 installer_state.GetInstallerDirectory(*installer_version);
1679 // temp_path's dtor will take care of deleting or scheduling itself for
1680 // deletion at reboot when this scope closes.
1681 VLOG(1) << "Deleting temporary directory " << temp_path.path().value();
1683 return install_status;
1686 } // namespace installer
1688 int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance,
1689 wchar_t* command_line, int show_command) {
1690 // The exit manager is in charge of calling the dtors of singletons.
1691 base::AtExitManager exit_manager;
1692 CommandLine::Init(0, NULL);
1694 const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess();
1695 installer::InitInstallerLogging(prefs);
1697 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
1698 VLOG(1) << "Command Line: " << cmd_line.GetCommandLineString();
1700 VLOG(1) << "multi install is " << prefs.is_multi_install();
1701 bool system_install = false;
1702 prefs.GetBool(installer::master_preferences::kSystemLevel, &system_install);
1703 VLOG(1) << "system install is " << system_install;
1705 google_breakpad::scoped_ptr<google_breakpad::ExceptionHandler> breakpad(
1706 InitializeCrashReporting(system_install));
1708 InstallationState original_state;
1709 original_state.Initialize();
1711 InstallerState installer_state;
1712 installer_state.Initialize(cmd_line, prefs, original_state);
1713 const bool is_uninstall = cmd_line.HasSwitch(installer::switches::kUninstall);
1715 // Check to make sure current system is WinXP or later. If not, log
1716 // error message and get out.
1717 if (!InstallUtil::IsOSSupported()) {
1718 LOG(ERROR) << "Chrome only supports Windows XP or later.";
1719 installer_state.WriteInstallerResult(
1720 installer::OS_NOT_SUPPORTED, IDS_INSTALL_OS_NOT_SUPPORTED_BASE, NULL);
1721 return installer::OS_NOT_SUPPORTED;
1724 // Initialize COM for use later.
1725 base::win::ScopedCOMInitializer com_initializer;
1726 if (!com_initializer.succeeded()) {
1727 installer_state.WriteInstallerResult(
1728 installer::OS_ERROR, IDS_INSTALL_OS_ERROR_BASE, NULL);
1729 return installer::OS_ERROR;
1732 // Some command line options don't work with SxS install/uninstall
1733 if (InstallUtil::IsChromeSxSProcess()) {
1734 if (system_install ||
1735 prefs.is_multi_install() ||
1736 cmd_line.HasSwitch(installer::switches::kForceUninstall) ||
1737 cmd_line.HasSwitch(installer::switches::kMakeChromeDefault) ||
1738 cmd_line.HasSwitch(installer::switches::kRegisterChromeBrowser) ||
1739 cmd_line.HasSwitch(installer::switches::kRemoveChromeRegistration) ||
1740 cmd_line.HasSwitch(installer::switches::kInactiveUserToast) ||
1741 cmd_line.HasSwitch(installer::switches::kSystemLevelToast) ||
1742 cmd_line.HasSwitch(installer::switches::kChromeFrameQuickEnable)) {
1743 return installer::SXS_OPTION_NOT_SUPPORTED;
1748 if (HandleNonInstallCmdLineOptions(
1749 original_state, cmd_line, &installer_state, &exit_code)) {
1753 if (system_install && !IsUserAnAdmin()) {
1754 if (base::win::GetVersion() >= base::win::VERSION_VISTA &&
1755 !cmd_line.HasSwitch(installer::switches::kRunAsAdmin)) {
1756 CommandLine new_cmd(CommandLine::NO_PROGRAM);
1757 new_cmd.AppendArguments(cmd_line, true);
1758 // Append --run-as-admin flag to let the new instance of setup.exe know
1759 // that we already tried to launch ourselves as admin.
1760 new_cmd.AppendSwitch(installer::switches::kRunAsAdmin);
1761 // If system_install became true due to an environment variable, append
1762 // it to the command line here since env vars may not propagate past the
1764 if (!new_cmd.HasSwitch(installer::switches::kSystemLevel))
1765 new_cmd.AppendSwitch(installer::switches::kSystemLevel);
1767 DWORD exit_code = installer::UNKNOWN_STATUS;
1768 InstallUtil::ExecuteExeAsAdmin(new_cmd, &exit_code);
1771 LOG(ERROR) << "Non admin user can not install system level Chrome.";
1772 installer_state.WriteInstallerResult(installer::INSUFFICIENT_RIGHTS,
1773 IDS_INSTALL_INSUFFICIENT_RIGHTS_BASE, NULL);
1774 return installer::INSUFFICIENT_RIGHTS;
1778 base::FilePath installer_directory;
1779 installer::InstallStatus install_status = installer::UNKNOWN_STATUS;
1780 // If --uninstall option is not specified, we assume it is install case.
1781 if (!is_uninstall) {
1783 InstallProducts(original_state, cmd_line, prefs, &installer_state,
1784 &installer_directory);
1786 // If --uninstall option is given or only the binaries are present and they're
1787 // not in-use, uninstall the identified product(s).
1788 if (is_uninstall || (install_status == installer::UNUSED_BINARIES &&
1789 !installer_state.AreBinariesInUse(original_state))) {
1791 UninstallProducts(original_state, installer_state, cmd_line);
1792 // Report that the binaries were uninstalled if they were. This translates
1793 // into a successful install return code.
1794 if (!is_uninstall && IsUninstallSuccess(install_status)) {
1795 install_status = installer::UNUSED_BINARIES_UNINSTALLED;
1796 installer_state.WriteInstallerResult(install_status, 0, NULL);
1800 // Validate that the machine is now in a good state following the operation.
1801 // TODO(grt): change this to log at DFATAL once we're convinced that the
1802 // validator handles all cases properly.
1803 InstallationValidator::InstallationType installation_type =
1804 InstallationValidator::NO_PRODUCTS;
1806 !InstallationValidator::ValidateInstallationType(system_install,
1807 &installation_type));
1809 const Product* cf_install =
1810 installer_state.FindProduct(BrowserDistribution::CHROME_FRAME);
1812 !cmd_line.HasSwitch(installer::switches::kForceUninstall)) {
1813 if (install_status == installer::UNINSTALL_REQUIRES_REBOOT) {
1815 } else if (is_uninstall) {
1816 // Only show the message box if Chrome Frame was the only product being
1818 const Products& products = installer_state.products();
1819 int num_products = 0;
1820 for (Products::const_iterator it = products.begin(); it < products.end();
1822 if (!(*it)->is_chrome_binaries())
1825 if (num_products == 1U) {
1827 installer::GetLocalizedString(
1828 IDS_UNINSTALL_COMPLETE_BASE).c_str(),
1829 cf_install->distribution()->GetDisplayName().c_str(),
1835 int return_code = 0;
1836 // MSI demands that custom actions always return 0 (ERROR_SUCCESS) or it will
1837 // rollback the action. If we're uninstalling we want to avoid this, so always
1838 // report success, squashing any more informative return codes.
1839 if (!(installer_state.is_msi() && is_uninstall)) {
1840 // Note that we allow the status installer::UNINSTALL_REQUIRES_REBOOT
1841 // to pass through, since this is only returned on uninstall which is
1842 // never invoked directly by Google Update.
1843 return_code = InstallUtil::GetInstallReturnCode(install_status);
1846 // Reinitialize original_state to make sure it reflects the now-current
1847 // state of the system.
1848 original_state.Initialize();
1850 // If multi Chrome Frame was just updated, migrate the installation to a SxS
1851 // install. Do this right before quitting.
1852 const ProductState* chrome_frame_state =
1853 original_state.GetProductState(system_install,
1854 BrowserDistribution::CHROME_FRAME);
1855 if ((install_status == installer::NEW_VERSION_UPDATED ||
1856 install_status == installer::IN_USE_UPDATED) &&
1857 chrome_frame_state &&
1858 installer_state.operation() == InstallerState::MULTI_UPDATE) {
1859 // Call the newly updated setup.exe with kUncompressedArchive and
1860 // kMigrateChromeFrame to perform the migration.
1861 LaunchChromeFrameMigrationProcess(*chrome_frame_state, cmd_line,
1862 installer_directory, system_install);
1865 VLOG(1) << "Installation complete, returning: " << return_code;