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