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_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/installer_util_strings.h"
60 #include "chrome/installer/util/l10n_string_util.h"
61 #include "chrome/installer/util/logging_installer.h"
62 #include "chrome/installer/util/lzma_util.h"
63 #include "chrome/installer/util/master_preferences.h"
64 #include "chrome/installer/util/master_preferences_constants.h"
65 #include "chrome/installer/util/self_cleaning_temp_dir.h"
66 #include "chrome/installer/util/shell_util.h"
67 #include "chrome/installer/util/user_experiment.h"
69 #if defined(GOOGLE_CHROME_BUILD)
70 #include "chrome/installer/util/updating_app_registration_data.h"
73 using installer::InstallerState
;
74 using installer::InstallationState
;
75 using installer::InstallationValidator
;
76 using installer::MasterPreferences
;
77 using installer::Product
;
78 using installer::ProductState
;
79 using installer::Products
;
81 const wchar_t kChromePipeName
[] = L
"\\\\.\\pipe\\ChromeCrashServices";
82 const wchar_t kGoogleUpdatePipeName
[] = L
"\\\\.\\pipe\\GoogleCrashServices\\";
83 const wchar_t kSystemPrincipalSid
[] = L
"S-1-5-18";
85 const MINIDUMP_TYPE kLargerDumpType
= static_cast<MINIDUMP_TYPE
>(
86 MiniDumpWithProcessThreadData
| // Get PEB and TEB.
87 MiniDumpWithUnloadedModules
| // Get unloaded modules when available.
88 MiniDumpWithIndirectlyReferencedMemory
); // Get memory referenced by stack.
92 // Returns NULL if no compressed archive is available for processing, otherwise
93 // returns a patch helper configured to uncompress and patch.
94 scoped_ptr
<installer::ArchivePatchHelper
> CreateChromeArchiveHelper(
95 const base::FilePath
& setup_exe
,
96 const base::CommandLine
& command_line
,
97 const installer::InstallerState
& installer_state
,
98 const base::FilePath
& working_directory
) {
99 // A compressed archive is ordinarily given on the command line by the mini
100 // installer. If one was not given, look for chrome.packed.7z next to the
102 base::FilePath
compressed_archive(
103 command_line
.GetSwitchValuePath(installer::switches::kInstallArchive
));
104 bool compressed_archive_specified
= !compressed_archive
.empty();
105 if (!compressed_archive_specified
) {
106 compressed_archive
= setup_exe
.DirName().Append(
107 installer::kChromeCompressedArchive
);
110 // Fail if no compressed archive is found.
111 if (!base::PathExists(compressed_archive
)) {
112 if (compressed_archive_specified
) {
113 LOG(ERROR
) << installer::switches::kInstallArchive
<< "="
114 << compressed_archive
.value() << " not found.";
116 return scoped_ptr
<installer::ArchivePatchHelper
>();
119 // chrome.7z is either extracted directly from the compressed archive into the
120 // working dir or is the target of patching in the working dir.
121 base::FilePath
target(working_directory
.Append(installer::kChromeArchive
));
122 DCHECK(!base::PathExists(target
));
124 // Specify an empty path for the patch source since it isn't yet known that
125 // one is needed. It will be supplied in UncompressAndPatchChromeArchive if it
127 return scoped_ptr
<installer::ArchivePatchHelper
>(
128 new installer::ArchivePatchHelper(working_directory
,
134 // Workhorse for producing an uncompressed archive (chrome.7z) given a
135 // chrome.packed.7z containing either a patch file based on the version of
136 // chrome being updated or the full uncompressed archive. Returns true on
137 // success, in which case |archive_type| is populated based on what was found.
138 // Returns false on failure, in which case |install_status| contains the error
139 // code and the result is written to the registry (via WriteInstallerResult).
140 bool UncompressAndPatchChromeArchive(
141 const installer::InstallationState
& original_state
,
142 const installer::InstallerState
& installer_state
,
143 installer::ArchivePatchHelper
* archive_helper
,
144 installer::ArchiveType
* archive_type
,
145 installer::InstallStatus
* install_status
) {
146 installer_state
.UpdateStage(installer::UNCOMPRESSING
);
147 if (!archive_helper
->Uncompress(NULL
)) {
148 *install_status
= installer::UNCOMPRESSION_FAILED
;
149 installer_state
.WriteInstallerResult(*install_status
,
150 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE
,
155 // Short-circuit if uncompression produced the uncompressed archive rather
156 // than a patch file.
157 if (base::PathExists(archive_helper
->target())) {
158 *archive_type
= installer::FULL_ARCHIVE_TYPE
;
162 // Find the installed version's archive to serve as the source for patching.
163 base::FilePath
patch_source(installer::FindArchiveToPatch(original_state
,
165 if (patch_source
.empty()) {
166 LOG(ERROR
) << "Failed to find archive to patch.";
167 *install_status
= installer::DIFF_PATCH_SOURCE_MISSING
;
168 installer_state
.WriteInstallerResult(*install_status
,
169 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE
,
173 archive_helper
->set_patch_source(patch_source
);
175 // Try courgette first. Failing that, try bspatch.
176 installer_state
.UpdateStage(installer::ENSEMBLE_PATCHING
);
177 if (!archive_helper
->EnsemblePatch()) {
178 installer_state
.UpdateStage(installer::BINARY_PATCHING
);
179 if (!archive_helper
->BinaryPatch()) {
180 *install_status
= installer::APPLY_DIFF_PATCH_FAILED
;
181 installer_state
.WriteInstallerResult(
182 *install_status
, IDS_INSTALL_UNCOMPRESSION_FAILED_BASE
, NULL
);
187 *archive_type
= installer::INCREMENTAL_ARCHIVE_TYPE
;
191 // In multi-install, adds all products to |installer_state| that are
192 // multi-installed and must be updated along with the products already present
193 // in |installer_state|.
194 void AddExistingMultiInstalls(const InstallationState
& original_state
,
195 InstallerState
* installer_state
) {
196 if (installer_state
->is_multi_install()) {
197 for (size_t i
= 0; i
< BrowserDistribution::NUM_TYPES
; ++i
) {
198 BrowserDistribution::Type type
=
199 static_cast<BrowserDistribution::Type
>(i
);
201 if (!installer_state
->FindProduct(type
)) {
202 const ProductState
* state
=
203 original_state
.GetProductState(installer_state
->system_install(),
205 if ((state
!= NULL
) && state
->is_multi_install()) {
206 installer_state
->AddProductFromState(type
, *state
);
207 VLOG(1) << "Product already installed and must be included: "
208 << BrowserDistribution::GetSpecificDistribution(type
)->
216 // This function is called when --rename-chrome-exe option is specified on
217 // setup.exe command line. This function assumes an in-use update has happened
218 // for Chrome so there should be a file called new_chrome.exe on the file
219 // system and a key called 'opv' in the registry. This function will move
220 // new_chrome.exe to chrome.exe and delete 'opv' key in one atomic operation.
221 // This function also deletes elevation policies associated with the old version
223 installer::InstallStatus
RenameChromeExecutables(
224 const InstallationState
& original_state
,
225 InstallerState
* installer_state
) {
226 // See what products are already installed in multi mode. When we do the
227 // rename for multi installs, we must update all installations since they
228 // share the binaries.
229 AddExistingMultiInstalls(original_state
, installer_state
);
230 const base::FilePath
&target_path
= installer_state
->target_path();
231 base::FilePath
chrome_exe(target_path
.Append(installer::kChromeExe
));
232 base::FilePath
chrome_new_exe(target_path
.Append(installer::kChromeNewExe
));
233 base::FilePath
chrome_old_exe(target_path
.Append(installer::kChromeOldExe
));
235 // Create a temporary backup directory on the same volume as chrome.exe so
236 // that moving in-use files doesn't lead to trouble.
237 installer::SelfCleaningTempDir temp_path
;
238 if (!temp_path
.Initialize(target_path
.DirName(),
239 installer::kInstallTempDir
)) {
240 PLOG(ERROR
) << "Failed to create Temp directory "
241 << target_path
.DirName()
242 .Append(installer::kInstallTempDir
).value();
243 return installer::RENAME_FAILED
;
245 scoped_ptr
<WorkItemList
> install_list(WorkItem::CreateWorkItemList());
246 // Move chrome.exe to old_chrome.exe, then move new_chrome.exe to chrome.exe.
247 install_list
->AddMoveTreeWorkItem(chrome_exe
.value(),
248 chrome_old_exe
.value(),
249 temp_path
.path().value(),
250 WorkItem::ALWAYS_MOVE
);
251 install_list
->AddMoveTreeWorkItem(chrome_new_exe
.value(),
253 temp_path
.path().value(),
254 WorkItem::ALWAYS_MOVE
);
255 install_list
->AddDeleteTreeWorkItem(chrome_new_exe
, temp_path
.path());
256 // old_chrome.exe is still in use in most cases, so ignore failures here.
257 install_list
->AddDeleteTreeWorkItem(chrome_old_exe
, temp_path
.path())->
258 set_ignore_failure(true);
260 // Add work items to delete the "opv", "cpv", and "cmd" values from all
261 // products we're operating on (which including the multi-install binaries).
262 const Products
& products
= installer_state
->products();
263 HKEY reg_root
= installer_state
->root_key();
264 base::string16 version_key
;
265 for (Products::const_iterator it
= products
.begin(); it
< products
.end();
267 version_key
= (*it
)->distribution()->GetVersionKey();
268 install_list
->AddDeleteRegValueWorkItem(reg_root
,
271 google_update::kRegOldVersionField
);
272 install_list
->AddDeleteRegValueWorkItem(
276 google_update::kRegCriticalVersionField
);
277 install_list
->AddDeleteRegValueWorkItem(reg_root
,
280 google_update::kRegRenameCmdField
);
282 installer::InstallStatus ret
= installer::RENAME_SUCCESSFUL
;
283 if (!install_list
->Do()) {
284 LOG(ERROR
) << "Renaming of executables failed. Rolling back any changes.";
285 install_list
->Rollback();
286 ret
= installer::RENAME_FAILED
;
288 // temp_path's dtor will take care of deleting or scheduling itself for
289 // deletion at reboot when this scope closes.
290 VLOG(1) << "Deleting temporary directory " << temp_path
.path().value();
295 // If only the binaries are being updated, fail.
296 // If any product is being installed in single-mode that already exists in
298 bool CheckMultiInstallConditions(const InstallationState
& original_state
,
299 InstallerState
* installer_state
,
300 installer::InstallStatus
* status
) {
301 const Products
& products
= installer_state
->products();
302 DCHECK(products
.size());
304 const bool system_level
= installer_state
->system_install();
306 if (installer_state
->is_multi_install()) {
307 const Product
* chrome
=
308 installer_state
->FindProduct(BrowserDistribution::CHROME_BROWSER
);
309 const Product
* binaries
=
310 installer_state
->FindProduct(BrowserDistribution::CHROME_BINARIES
);
311 const ProductState
* chrome_state
=
312 original_state
.GetProductState(system_level
,
313 BrowserDistribution::CHROME_BROWSER
);
316 if (products
.size() == 1) {
317 // There are no products aside from the binaries, so there is no update
318 // to be applied. This can happen after multi-install Chrome Frame is
319 // migrated to single-install. This is treated as an update failure
320 // unless the binaries are not in-use, in which case they will be
321 // uninstalled and success will be reported (see handling in wWinMain).
322 VLOG(1) << "No products to be updated.";
323 *status
= installer::UNUSED_BINARIES
;
324 installer_state
->WriteInstallerResult(*status
, 0, NULL
);
328 // This will only be hit if --multi-install is given with no products.
332 if (!chrome
&& chrome_state
) {
333 // A product other than Chrome is being installed in multi-install mode,
334 // and Chrome is already present. Add Chrome to the set of products
335 // (making it multi-install in the process) so that it is updated, too.
336 scoped_ptr
<Product
> multi_chrome(new Product(
337 BrowserDistribution::GetSpecificDistribution(
338 BrowserDistribution::CHROME_BROWSER
)));
339 multi_chrome
->SetOption(installer::kOptionMultiInstall
, true);
340 chrome
= installer_state
->AddProduct(&multi_chrome
);
341 VLOG(1) << "Upgrading existing Chrome browser in multi-install mode.";
343 } // else migrate multi-install Chrome to single-install.
348 // Checks for compatibility between the current state of the system and the
349 // desired operation. Also applies policy that mutates the desired operation;
350 // specifically, the |installer_state| object.
351 // Also blocks simultaneous user-level and system-level installs. In the case
352 // of trying to install user-level Chrome when system-level exists, the
353 // existing system-level Chrome is launched.
354 // When the pre-install conditions are not satisfied, the result is written to
355 // the registry (via WriteInstallerResult), |status| is set appropriately, and
356 // false is returned.
357 bool CheckPreInstallConditions(const InstallationState
& original_state
,
358 InstallerState
* installer_state
,
359 installer::InstallStatus
* status
) {
360 // See what products are already installed in multi mode. When we do multi
361 // installs, we must upgrade all installations since they share the binaries.
362 AddExistingMultiInstalls(original_state
, installer_state
);
364 if (!CheckMultiInstallConditions(original_state
, installer_state
, status
)) {
365 DCHECK_NE(*status
, installer::UNKNOWN_STATUS
);
369 const Products
& products
= installer_state
->products();
370 if (products
.empty()) {
371 // We haven't been given any products on which to operate.
373 << "Not given any products to install and no products found to update.";
374 *status
= installer::CHROME_NOT_INSTALLED
;
375 installer_state
->WriteInstallerResult(*status
,
376 IDS_INSTALL_NO_PRODUCTS_TO_UPDATE_BASE
, NULL
);
380 if (!installer_state
->system_install()) {
381 // This is a user-level installation. Make sure that we are not installing
382 // on top of an existing system-level installation.
383 for (Products::const_iterator it
= products
.begin(); it
< products
.end();
385 const Product
& product
= **it
;
386 BrowserDistribution
* browser_dist
= product
.distribution();
388 // Skip over the binaries, as it's okay for them to be at both levels
389 // for different products.
390 if (browser_dist
->GetType() == BrowserDistribution::CHROME_BINARIES
)
393 const ProductState
* user_level_product_state
=
394 original_state
.GetProductState(false, browser_dist
->GetType());
395 const ProductState
* system_level_product_state
=
396 original_state
.GetProductState(true, browser_dist
->GetType());
398 // Allow upgrades to proceed so that out-of-date versions are not left
400 if (user_level_product_state
)
403 // This is a new user-level install...
405 if (system_level_product_state
) {
406 // ... and the product already exists at system-level.
407 LOG(ERROR
) << "Already installed version "
408 << system_level_product_state
->version().GetString()
409 << " at system-level conflicts with this one at user-level.";
410 if (product
.is_chrome()) {
411 // Instruct Google Update to launch the existing system-level Chrome.
412 // There should be no error dialog.
413 base::FilePath
install_path(installer::GetChromeInstallPath(
416 if (install_path
.empty()) {
417 // Give up if we failed to construct the install path.
418 *status
= installer::OS_ERROR
;
419 installer_state
->WriteInstallerResult(*status
,
420 IDS_INSTALL_OS_ERROR_BASE
,
423 *status
= installer::EXISTING_VERSION_LAUNCHED
;
424 base::FilePath chrome_exe
=
425 install_path
.Append(installer::kChromeExe
);
426 base::CommandLine
cmd(chrome_exe
);
427 cmd
.AppendSwitch(switches::kForceFirstRun
);
428 installer_state
->WriteInstallerResult(*status
, 0, NULL
);
429 VLOG(1) << "Launching existing system-level chrome instead.";
430 base::LaunchProcess(cmd
, base::LaunchOptions());
433 // It's no longer possible for |product| to be anything other than
445 // Initializes |temp_path| to "Temp" within the target directory, and
446 // |unpack_path| to a random directory beginning with "source" within
447 // |temp_path|. Returns false on error.
448 bool CreateTemporaryAndUnpackDirectories(
449 const InstallerState
& installer_state
,
450 installer::SelfCleaningTempDir
* temp_path
,
451 base::FilePath
* unpack_path
) {
452 DCHECK(temp_path
&& unpack_path
);
454 if (!temp_path
->Initialize(installer_state
.target_path().DirName(),
455 installer::kInstallTempDir
)) {
456 PLOG(ERROR
) << "Could not create temporary path.";
459 VLOG(1) << "Created path " << temp_path
->path().value();
461 if (!base::CreateTemporaryDirInDir(temp_path
->path(),
462 installer::kInstallSourceDir
,
464 PLOG(ERROR
) << "Could not create temporary path for unpacked archive.";
471 installer::InstallStatus
UninstallProduct(
472 const InstallationState
& original_state
,
473 const InstallerState
& installer_state
,
474 const base::FilePath
& setup_exe
,
475 const base::CommandLine
& cmd_line
,
477 bool force_uninstall
,
478 const Product
& product
) {
479 const ProductState
* product_state
=
480 original_state
.GetProductState(installer_state
.system_install(),
481 product
.distribution()->GetType());
482 if (product_state
!= NULL
) {
483 VLOG(1) << "version on the system: "
484 << product_state
->version().GetString();
485 } else if (!force_uninstall
) {
486 LOG(ERROR
) << product
.distribution()->GetDisplayName()
487 << " not found for uninstall.";
488 return installer::CHROME_NOT_INSTALLED
;
491 return installer::UninstallProduct(
492 original_state
, installer_state
, setup_exe
, product
, remove_all
,
493 force_uninstall
, cmd_line
);
496 installer::InstallStatus
UninstallProducts(
497 const InstallationState
& original_state
,
498 const InstallerState
& installer_state
,
499 const base::FilePath
& setup_exe
,
500 const base::CommandLine
& cmd_line
) {
501 const Products
& products
= installer_state
.products();
503 // System-level Chrome will be launched via this command if its program gets
505 base::CommandLine
system_level_cmd(base::CommandLine::NO_PROGRAM
);
507 const Product
* chrome
=
508 installer_state
.FindProduct(BrowserDistribution::CHROME_BROWSER
);
510 // InstallerState::Initialize always puts Chrome first, and we rely on that
511 // here for this reason: if Chrome is in-use, the user will be prompted to
512 // confirm uninstallation. Upon cancel, we should not continue with the
514 DCHECK(products
[0]->is_chrome());
516 if (cmd_line
.HasSwitch(installer::switches::kSelfDestruct
) &&
517 !installer_state
.system_install()) {
518 BrowserDistribution
* dist
= chrome
->distribution();
519 const base::FilePath
system_exe_path(
520 installer::GetChromeInstallPath(true, dist
)
521 .Append(installer::kChromeExe
));
522 system_level_cmd
.SetProgram(system_exe_path
);
525 if (installer_state
.FindProduct(BrowserDistribution::CHROME_BINARIES
)) {
526 // Chrome Binaries should be last; if something else is cancelled, they
528 DCHECK(products
[products
.size() - 1]->is_chrome_binaries());
531 installer::InstallStatus install_status
= installer::UNINSTALL_SUCCESSFUL
;
532 installer::InstallStatus prod_status
= installer::UNKNOWN_STATUS
;
533 const bool force
= cmd_line
.HasSwitch(installer::switches::kForceUninstall
);
534 const bool remove_all
= !cmd_line
.HasSwitch(
535 installer::switches::kDoNotRemoveSharedItems
);
537 for (Products::const_iterator it
= products
.begin();
538 install_status
!= installer::UNINSTALL_CANCELLED
&& it
< products
.end();
540 prod_status
= UninstallProduct(original_state
, installer_state
, setup_exe
,
541 cmd_line
, remove_all
, force
, **it
);
542 if (prod_status
!= installer::UNINSTALL_SUCCESSFUL
)
543 install_status
= prod_status
;
546 installer::CleanUpInstallationDirectoryAfterUninstall(
547 original_state
, installer_state
, setup_exe
, &install_status
);
549 // The app and vendor dirs may now be empty. Make a last-ditch attempt to
551 installer::DeleteChromeDirectoriesIfEmpty(installer_state
.target_path());
553 // Trigger Active Setup if it was requested for the chrome product. This needs
554 // to be done after the UninstallProduct calls as some of them might
555 // otherwise terminate the process launched by TriggerActiveSetupCommand().
556 if (chrome
&& cmd_line
.HasSwitch(installer::switches::kTriggerActiveSetup
))
557 InstallUtil::TriggerActiveSetupCommand();
559 if (!system_level_cmd
.GetProgram().empty())
560 base::LaunchProcess(system_level_cmd
, base::LaunchOptions());
562 // Tell Google Update that an uninstall has taken place.
563 // Ignore the return value: success or failure of Google Update
564 // has no bearing on the success or failure of Chrome's uninstallation.
565 google_update::UninstallGoogleUpdate(installer_state
.system_install());
567 return install_status
;
570 // Uninstall the binaries if they are the only product present and they're not
572 void UninstallBinariesIfUnused(
573 const InstallationState
& original_state
,
574 const InstallerState
& installer_state
,
575 installer::InstallStatus
* install_status
) {
576 // Early exit if the binaries are still in use.
577 if (*install_status
!= installer::UNUSED_BINARIES
||
578 installer_state
.AreBinariesInUse(original_state
)) {
582 LOG(INFO
) << "Uninstalling unused binaries";
583 installer_state
.UpdateStage(installer::UNINSTALLING_BINARIES
);
585 // Simulate the uninstall as coming from the installed version.
586 const ProductState
* binaries_state
=
587 original_state
.GetProductState(installer_state
.system_install(),
588 BrowserDistribution::CHROME_BINARIES
);
589 const base::CommandLine
& uninstall_cmd(binaries_state
->uninstall_command());
590 MasterPreferences
uninstall_prefs(uninstall_cmd
);
591 InstallerState uninstall_state
;
592 uninstall_state
.Initialize(uninstall_cmd
, uninstall_prefs
, original_state
);
594 *install_status
= UninstallProducts(original_state
, uninstall_state
,
595 uninstall_cmd
.GetProgram(),
598 // Report that the binaries were uninstalled if they were. This translates
599 // into a successful install return code.
600 if (IsUninstallSuccess(*install_status
)) {
601 *install_status
= installer::UNUSED_BINARIES_UNINSTALLED
;
602 installer_state
.WriteInstallerResult(*install_status
, 0, NULL
);
606 // This function is a short-term repair for the damage documented in
607 // http://crbug.com/456602. Briefly: canaries from 42.0.2293.0 through
608 // 42.0.2302.0 (inclusive) contained a bug that broke normal Chrome installed at
609 // user-level. This function detects the broken state during a canary update and
610 // repairs it by calling on the existing Chrome's installer to fix itself.
611 // TODO(grt): Remove this once the majority of impacted canary clients have
613 void RepairChromeIfBroken(const InstallationState
& original_state
,
614 const InstallerState
& installer_state
) {
615 #if !defined(GOOGLE_CHROME_BUILD)
616 // Chromium does not support SxS installation, so there is no work to be done.
618 #else // GOOGLE_CHROME_BUILD
619 // Nothing to do if not a per-user SxS install/update.
620 if (!InstallUtil::IsChromeSxSProcess() ||
621 installer_state
.system_install() ||
622 installer_state
.is_multi_install()) {
626 // When running a side-by-side install, BrowserDistribution provides no way
627 // to create or access a GoogleChromeDistribution (by design).
628 static const base::char16 kChromeGuid
[] =
629 L
"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
630 static const base::char16 kChromeBinariesGuid
[] =
631 L
"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}";
633 UpdatingAppRegistrationData
chrome_reg_data(kChromeGuid
);
634 UpdatingAppRegistrationData
binaries_reg_data(kChromeBinariesGuid
);
636 // Nothing to do if the binaries are installed.
637 base::win::RegKey key
;
638 base::string16 version_str
;
639 if (key
.Open(HKEY_CURRENT_USER
,
640 binaries_reg_data
.GetVersionKey().c_str(),
641 KEY_QUERY_VALUE
| KEY_WOW64_32KEY
) == ERROR_SUCCESS
&&
642 key
.ReadValue(google_update::kRegVersionField
,
643 &version_str
) == ERROR_SUCCESS
) {
647 // Nothing to do if Chrome is not installed.
648 if (key
.Open(HKEY_CURRENT_USER
,
649 chrome_reg_data
.GetVersionKey().c_str(),
650 KEY_QUERY_VALUE
| KEY_WOW64_32KEY
) != ERROR_SUCCESS
||
651 key
.ReadValue(google_update::kRegVersionField
,
652 &version_str
) != ERROR_SUCCESS
) {
656 // Nothing to do if Chrome is not multi-install.
657 base::string16 setup_args
;
658 if (key
.Open(HKEY_CURRENT_USER
,
659 chrome_reg_data
.GetStateKey().c_str(),
660 KEY_QUERY_VALUE
| KEY_WOW64_32KEY
) != ERROR_SUCCESS
) {
661 LOG(ERROR
) << "RepairChrome: Failed to open Chrome's ClientState key.";
664 if (key
.ReadValue(installer::kUninstallArgumentsField
,
665 &setup_args
) != ERROR_SUCCESS
) {
666 LOG(ERROR
) << "RepairChrome: Failed to read Chrome's UninstallArguments.";
669 if (setup_args
.find(base::UTF8ToUTF16(installer::switches::kMultiInstall
)) ==
670 base::string16::npos
) {
671 LOG(INFO
) << "RepairChrome: Not repairing single-install Chrome.";
675 // Generate a command line to run Chrome's installer.
676 base::string16 setup_path
;
677 if (key
.ReadValue(installer::kUninstallStringField
,
678 &setup_path
) != ERROR_SUCCESS
) {
679 LOG(ERROR
) << "RepairChrome: Failed to read Chrome's UninstallString.";
683 // Replace --uninstall with --do-not-launch-chrome to cause chrome to
685 ReplaceFirstSubstringAfterOffset(
686 &setup_args
, 0, base::UTF8ToUTF16(installer::switches::kUninstall
),
687 base::UTF8ToUTF16(installer::switches::kDoNotLaunchChrome
));
688 base::CommandLine
setup_command(base::CommandLine::NO_PROGRAM
);
689 InstallUtil::ComposeCommandLine(setup_path
, setup_args
, &setup_command
);
691 // Run Chrome's installer so that it repairs itself. Break away from any job
692 // in which this operation is running so that Google Update doesn't wait
693 // around for the repair. Retry once without the attempt to break away in case
694 // this process doesn't have JOB_OBJECT_LIMIT_BREAKAWAY_OK.
695 base::LaunchOptions launch_options
;
696 launch_options
.force_breakaway_from_job_
= true;
698 if (base::LaunchProcess(setup_command
, launch_options
).IsValid()) {
699 LOG(INFO
) << "RepairChrome: Launched repair command \""
700 << setup_command
.GetCommandLineString() << "\"";
703 PLOG(ERROR
) << "RepairChrome: Failed launching repair command \""
704 << setup_command
.GetCommandLineString() << "\"";
705 if (launch_options
.force_breakaway_from_job_
) {
706 LOG(ERROR
) << "RepairChrome: Will retry without breakaway.";
707 launch_options
.force_breakaway_from_job_
= false;
713 #endif // GOOGLE_CHROME_BUILD
716 installer::InstallStatus
InstallProducts(
717 const InstallationState
& original_state
,
718 const base::FilePath
& setup_exe
,
719 const base::CommandLine
& cmd_line
,
720 const MasterPreferences
& prefs
,
721 InstallerState
* installer_state
,
722 base::FilePath
* installer_directory
) {
723 DCHECK(installer_state
);
724 const bool system_install
= installer_state
->system_install();
725 installer::InstallStatus install_status
= installer::UNKNOWN_STATUS
;
726 installer::ArchiveType archive_type
= installer::UNKNOWN_ARCHIVE_TYPE
;
727 bool delegated_to_existing
= false;
728 installer_state
->UpdateStage(installer::PRECONDITIONS
);
729 // The stage provides more fine-grained information than -multifail, so remove
730 // the -multifail suffix from the Google Update "ap" value.
731 BrowserDistribution::GetSpecificDistribution(installer_state
->state_type())->
732 UpdateInstallStatus(system_install
, archive_type
, install_status
);
733 if (CheckPreInstallConditions(original_state
, installer_state
,
735 VLOG(1) << "Installing to " << installer_state
->target_path().value();
736 install_status
= InstallProductsHelper(
737 original_state
, setup_exe
, cmd_line
, prefs
, *installer_state
,
738 installer_directory
, &archive_type
, &delegated_to_existing
);
740 // CheckPreInstallConditions must set the status on failure.
741 DCHECK_NE(install_status
, installer::UNKNOWN_STATUS
);
744 // Delete the master preferences file if present. Note that we do not care
745 // about rollback here and we schedule for deletion on reboot if the delete
746 // fails. As such, we do not use DeleteTreeWorkItem.
747 if (cmd_line
.HasSwitch(installer::switches::kInstallerData
)) {
748 base::FilePath
prefs_path(cmd_line
.GetSwitchValuePath(
749 installer::switches::kInstallerData
));
750 if (!base::DeleteFile(prefs_path
, false)) {
751 LOG(ERROR
) << "Failed deleting master preferences file "
752 << prefs_path
.value()
753 << ", scheduling for deletion after reboot.";
754 ScheduleFileSystemEntityForDeletion(prefs_path
);
758 // Early exit if this setup.exe delegated to another, since that one would
759 // have taken care of UpdateInstallStatus and UpdateStage.
760 if (delegated_to_existing
)
761 return install_status
;
763 const Products
& products
= installer_state
->products();
764 for (Products::const_iterator it
= products
.begin(); it
< products
.end();
766 (*it
)->distribution()->UpdateInstallStatus(
767 system_install
, archive_type
, install_status
);
770 UninstallBinariesIfUnused(original_state
, *installer_state
, &install_status
);
772 RepairChromeIfBroken(original_state
, *installer_state
);
774 installer_state
->UpdateStage(installer::NO_STAGE
);
775 return install_status
;
778 installer::InstallStatus
ShowEULADialog(const base::string16
& inner_frame
) {
779 VLOG(1) << "About to show EULA";
780 base::string16 eula_path
= installer::GetLocalizedEulaResource();
781 if (eula_path
.empty()) {
782 LOG(ERROR
) << "No EULA path available";
783 return installer::EULA_REJECTED
;
785 // Newer versions of the caller pass an inner frame parameter that must
786 // be given to the html page being launched.
787 installer::EulaHTMLDialog
dlg(eula_path
, inner_frame
);
788 installer::EulaHTMLDialog::Outcome outcome
= dlg
.ShowModal();
789 if (installer::EulaHTMLDialog::REJECTED
== outcome
) {
790 LOG(ERROR
) << "EULA rejected or EULA failure";
791 return installer::EULA_REJECTED
;
793 if (installer::EulaHTMLDialog::ACCEPTED_OPT_IN
== outcome
) {
794 VLOG(1) << "EULA accepted (opt-in)";
795 return installer::EULA_ACCEPTED_OPT_IN
;
797 VLOG(1) << "EULA accepted (no opt-in)";
798 return installer::EULA_ACCEPTED
;
801 // Creates the sentinel indicating that the EULA was required and has been
803 bool CreateEULASentinel(BrowserDistribution
* dist
) {
804 base::FilePath eula_sentinel
;
805 if (!InstallUtil::GetEULASentinelFilePath(&eula_sentinel
))
808 return (base::CreateDirectory(eula_sentinel
.DirName()) &&
809 base::WriteFile(eula_sentinel
, "", 0) != -1);
812 void ActivateMetroChrome() {
813 // Check to see if we're per-user or not. Need to do this since we may
814 // not have been invoked with --system-level even for a machine install.
815 base::FilePath exe_path
;
816 PathService::Get(base::FILE_EXE
, &exe_path
);
817 bool is_per_user_install
= InstallUtil::IsPerUserInstall(exe_path
);
819 base::string16 app_model_id
= ShellUtil::GetBrowserModelId(
820 BrowserDistribution::GetDistribution(), is_per_user_install
);
822 base::win::ScopedComPtr
<IApplicationActivationManager
> activator
;
823 HRESULT hr
= activator
.CreateInstance(CLSID_ApplicationActivationManager
);
826 hr
= activator
->ActivateApplication(
827 app_model_id
.c_str(), L
"open", AO_NONE
, &pid
);
830 LOG_IF(ERROR
, FAILED(hr
)) << "Tried and failed to launch Metro Chrome. "
831 << "hr=" << std::hex
<< hr
;
834 installer::InstallStatus
RegisterDevChrome(
835 const InstallationState
& original_state
,
836 const InstallerState
& installer_state
,
837 const base::FilePath
& setup_exe
,
838 const base::CommandLine
& cmd_line
) {
839 BrowserDistribution
* chrome_dist
=
840 BrowserDistribution::GetSpecificDistribution(
841 BrowserDistribution::CHROME_BROWSER
);
843 // Only proceed with registering a dev chrome if no real Chrome installation
844 // of the same distribution are present on this system.
845 const ProductState
* existing_chrome
=
846 original_state
.GetProductState(false,
847 BrowserDistribution::CHROME_BROWSER
);
848 if (!existing_chrome
) {
850 original_state
.GetProductState(true, BrowserDistribution::CHROME_BROWSER
);
852 if (existing_chrome
) {
853 static const wchar_t kPleaseUninstallYourChromeMessage
[] =
854 L
"You already have a full-installation (non-dev) of %1ls, please "
855 L
"uninstall it first using Add/Remove Programs in the control panel.";
856 base::string16
name(chrome_dist
->GetDisplayName());
857 base::string16
message(
858 base::StringPrintf(kPleaseUninstallYourChromeMessage
, name
.c_str()));
860 LOG(ERROR
) << "Aborting operation: another installation of " << name
861 << " was found, as a last resort (if the product is not present "
862 "in Add/Remove Programs), try executing: "
863 << existing_chrome
->uninstall_command().GetCommandLineString();
864 MessageBox(NULL
, message
.c_str(), NULL
, MB_ICONERROR
);
865 return installer::INSTALL_FAILED
;
868 base::FilePath
chrome_exe(
869 cmd_line
.GetSwitchValuePath(installer::switches::kRegisterDevChrome
));
870 if (chrome_exe
.empty())
871 chrome_exe
= setup_exe
.DirName().Append(installer::kChromeExe
);
872 if (!chrome_exe
.IsAbsolute())
873 chrome_exe
= base::MakeAbsoluteFilePath(chrome_exe
);
875 installer::InstallStatus status
= installer::FIRST_INSTALL_SUCCESS
;
876 if (base::PathExists(chrome_exe
)) {
877 Product
chrome(chrome_dist
);
879 // Create the Start menu shortcut and pin it to the taskbar.
880 ShellUtil::ShortcutProperties
shortcut_properties(ShellUtil::CURRENT_USER
);
881 chrome
.AddDefaultShortcutProperties(chrome_exe
, &shortcut_properties
);
882 shortcut_properties
.set_dual_mode(true);
883 shortcut_properties
.set_pin_to_taskbar(true);
884 ShellUtil::CreateOrUpdateShortcut(
885 ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR
, chrome_dist
,
886 shortcut_properties
, ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS
);
888 // Register Chrome at user-level and make it default.
889 scoped_ptr
<WorkItemList
> delegate_execute_list(
890 WorkItem::CreateWorkItemList());
891 installer::AddDelegateExecuteWorkItems(
892 installer_state
, chrome_exe
.DirName(), Version(), chrome
,
893 delegate_execute_list
.get());
894 delegate_execute_list
->Do();
895 if (ShellUtil::CanMakeChromeDefaultUnattended()) {
896 ShellUtil::MakeChromeDefault(chrome_dist
, ShellUtil::CURRENT_USER
,
899 ShellUtil::ShowMakeChromeDefaultSystemUI(chrome_dist
, chrome_exe
);
902 LOG(ERROR
) << "Path not found: " << chrome_exe
.value();
903 status
= installer::INSTALL_FAILED
;
908 // This method processes any command line options that make setup.exe do
909 // various tasks other than installation (renaming chrome.exe, showing eula
910 // among others). This function returns true if any such command line option
911 // has been found and processed (so setup.exe should exit at that point).
912 bool HandleNonInstallCmdLineOptions(const InstallationState
& original_state
,
913 const base::FilePath
& setup_exe
,
914 const base::CommandLine
& cmd_line
,
915 InstallerState
* installer_state
,
917 // TODO(gab): Add a local |status| variable which each block below sets;
918 // only determine the |exit_code| from |status| at the end (this will allow
919 // this method to validate that
920 // (!handled || status != installer::UNKNOWN_STATUS)).
922 // TODO(tommi): Split these checks up into functions and use a data driven
923 // map of switch->function.
924 if (cmd_line
.HasSwitch(installer::switches::kUpdateSetupExe
)) {
925 installer::InstallStatus status
= installer::SETUP_PATCH_FAILED
;
926 // If --update-setup-exe command line option is given, we apply the given
927 // patch to current exe, and store the resulting binary in the path
928 // specified by --new-setup-exe. But we need to first unpack the file
929 // given in --update-setup-exe.
930 base::ScopedTempDir temp_path
;
931 if (!temp_path
.CreateUniqueTempDir()) {
932 PLOG(ERROR
) << "Could not create temporary path.";
934 base::FilePath
compressed_archive(cmd_line
.GetSwitchValuePath(
935 installer::switches::kUpdateSetupExe
));
936 VLOG(1) << "Opening archive " << compressed_archive
.value();
937 if (installer::ArchivePatchHelper::UncompressAndPatch(
941 cmd_line
.GetSwitchValuePath(installer::switches::kNewSetupExe
))) {
942 status
= installer::NEW_VERSION_UPDATED
;
944 if (!temp_path
.Delete()) {
945 // PLOG would be nice, but Delete() doesn't leave a meaningful value in
946 // the Windows last-error code.
947 LOG(WARNING
) << "Scheduling temporary path " << temp_path
.path().value()
948 << " for deletion at reboot.";
949 ScheduleDirectoryForDeletion(temp_path
.path());
953 *exit_code
= InstallUtil::GetInstallReturnCode(status
);
955 LOG(WARNING
) << "setup.exe patching failed.";
956 installer_state
->WriteInstallerResult(
957 status
, IDS_SETUP_PATCH_FAILED_BASE
, NULL
);
959 // We will be exiting normally, so clear the stage indicator.
960 installer_state
->UpdateStage(installer::NO_STAGE
);
961 } else if (cmd_line
.HasSwitch(installer::switches::kShowEula
)) {
962 // Check if we need to show the EULA. If it is passed as a command line
963 // then the dialog is shown and regardless of the outcome setup exits here.
964 base::string16 inner_frame
=
965 cmd_line
.GetSwitchValueNative(installer::switches::kShowEula
);
966 *exit_code
= ShowEULADialog(inner_frame
);
968 if (installer::EULA_REJECTED
!= *exit_code
) {
969 if (GoogleUpdateSettings::SetEULAConsent(
970 original_state
, BrowserDistribution::GetDistribution(), true)) {
971 CreateEULASentinel(BrowserDistribution::GetDistribution());
973 // For a metro-originated launch, we now need to launch back into metro.
974 if (cmd_line
.HasSwitch(installer::switches::kShowEulaForMetro
))
975 ActivateMetroChrome();
977 } else if (cmd_line
.HasSwitch(installer::switches::kConfigureUserSettings
)) {
978 // NOTE: Should the work done here, on kConfigureUserSettings, change:
979 // kActiveSetupVersion in install_worker.cc needs to be increased for Active
980 // Setup to invoke this again for all users of this install.
981 const Product
* chrome_install
=
982 installer_state
->FindProduct(BrowserDistribution::CHROME_BROWSER
);
983 installer::InstallStatus status
= installer::INVALID_STATE_FOR_OPTION
;
984 if (chrome_install
&& installer_state
->system_install()) {
986 cmd_line
.HasSwitch(installer::switches::kForceConfigureUserSettings
);
987 installer::HandleActiveSetupForBrowser(installer_state
->target_path(),
988 *chrome_install
, force
);
989 status
= installer::INSTALL_REPAIRED
;
991 LOG(DFATAL
) << "chrome_install:" << chrome_install
992 << ", system_install:" << installer_state
->system_install();
994 *exit_code
= InstallUtil::GetInstallReturnCode(status
);
995 } else if (cmd_line
.HasSwitch(installer::switches::kRegisterDevChrome
)) {
996 installer::InstallStatus status
= RegisterDevChrome(
997 original_state
, *installer_state
, setup_exe
, cmd_line
);
998 *exit_code
= InstallUtil::GetInstallReturnCode(status
);
999 } else if (cmd_line
.HasSwitch(installer::switches::kRegisterChromeBrowser
)) {
1000 installer::InstallStatus status
= installer::UNKNOWN_STATUS
;
1001 const Product
* chrome_install
=
1002 installer_state
->FindProduct(BrowserDistribution::CHROME_BROWSER
);
1003 if (chrome_install
) {
1004 // If --register-chrome-browser option is specified, register all
1005 // Chrome protocol/file associations, as well as register it as a valid
1006 // browser for Start Menu->Internet shortcut. This switch will also
1007 // register Chrome as a valid handler for a set of URL protocols that
1008 // Chrome may become the default handler for, either by the user marking
1009 // Chrome as the default browser, through the Windows Default Programs
1010 // control panel settings, or through website use of
1011 // registerProtocolHandler. These protocols are found in
1012 // ShellUtil::kPotentialProtocolAssociations.
1013 // The --register-url-protocol will additionally register Chrome as a
1014 // potential handler for the supplied protocol, and is used if a website
1015 // registers a handler for a protocol not found in
1016 // ShellUtil::kPotentialProtocolAssociations.
1017 // These options should only be used when setup.exe is launched with admin
1018 // rights. We do not make any user specific changes with this option.
1019 DCHECK(IsUserAnAdmin());
1020 base::FilePath
chrome_exe(cmd_line
.GetSwitchValuePath(
1021 installer::switches::kRegisterChromeBrowser
));
1022 base::string16 suffix
;
1023 if (cmd_line
.HasSwitch(
1024 installer::switches::kRegisterChromeBrowserSuffix
)) {
1025 suffix
= cmd_line
.GetSwitchValueNative(
1026 installer::switches::kRegisterChromeBrowserSuffix
);
1028 if (cmd_line
.HasSwitch(installer::switches::kRegisterURLProtocol
)) {
1029 base::string16 protocol
= cmd_line
.GetSwitchValueNative(
1030 installer::switches::kRegisterURLProtocol
);
1031 // ShellUtil::RegisterChromeForProtocol performs all registration
1032 // done by ShellUtil::RegisterChromeBrowser, as well as registering
1033 // with Windows as capable of handling the supplied protocol.
1034 if (ShellUtil::RegisterChromeForProtocol(
1035 chrome_install
->distribution(), chrome_exe
, suffix
, protocol
,
1037 status
= installer::IN_USE_UPDATED
;
1039 if (ShellUtil::RegisterChromeBrowser(chrome_install
->distribution(),
1040 chrome_exe
, suffix
, false))
1041 status
= installer::IN_USE_UPDATED
;
1044 LOG(DFATAL
) << "Can't register browser - Chrome distribution not found";
1046 *exit_code
= InstallUtil::GetInstallReturnCode(status
);
1047 } else if (cmd_line
.HasSwitch(installer::switches::kRenameChromeExe
)) {
1048 // If --rename-chrome-exe is specified, we want to rename the executables
1050 *exit_code
= RenameChromeExecutables(original_state
, installer_state
);
1051 } else if (cmd_line
.HasSwitch(
1052 installer::switches::kRemoveChromeRegistration
)) {
1053 // This is almost reverse of --register-chrome-browser option above.
1054 // Here we delete Chrome browser registration. This option should only
1055 // be used when setup.exe is launched with admin rights. We do not
1056 // make any user specific changes in this option.
1057 base::string16 suffix
;
1058 if (cmd_line
.HasSwitch(
1059 installer::switches::kRegisterChromeBrowserSuffix
)) {
1060 suffix
= cmd_line
.GetSwitchValueNative(
1061 installer::switches::kRegisterChromeBrowserSuffix
);
1063 installer::InstallStatus tmp
= installer::UNKNOWN_STATUS
;
1064 const Product
* chrome_install
=
1065 installer_state
->FindProduct(BrowserDistribution::CHROME_BROWSER
);
1066 DCHECK(chrome_install
);
1067 if (chrome_install
) {
1068 installer::DeleteChromeRegistrationKeys(*installer_state
,
1069 chrome_install
->distribution(), HKEY_LOCAL_MACHINE
, suffix
, &tmp
);
1072 } else if (cmd_line
.HasSwitch(installer::switches::kOnOsUpgrade
)) {
1073 const Product
* chrome_install
=
1074 installer_state
->FindProduct(BrowserDistribution::CHROME_BROWSER
);
1075 installer::InstallStatus status
= installer::INVALID_STATE_FOR_OPTION
;
1076 if (chrome_install
) {
1077 installer::HandleOsUpgradeForBrowser(*installer_state
,
1079 status
= installer::INSTALL_REPAIRED
;
1081 LOG(DFATAL
) << "Chrome product not found.";
1083 *exit_code
= InstallUtil::GetInstallReturnCode(status
);
1084 } else if (cmd_line
.HasSwitch(installer::switches::kInactiveUserToast
)) {
1085 // Launch the inactive user toast experiment.
1087 base::StringToInt(cmd_line
.GetSwitchValueNative(
1088 installer::switches::kInactiveUserToast
), &flavor
);
1089 std::string experiment_group
=
1090 cmd_line
.GetSwitchValueASCII(installer::switches::kExperimentGroup
);
1091 DCHECK_NE(-1, flavor
);
1093 *exit_code
= installer::UNKNOWN_STATUS
;
1095 // This code is called (via setup.exe relaunch) only if a product is known
1096 // to run user experiments, so no check is required.
1097 const Products
& products
= installer_state
->products();
1098 for (Products::const_iterator it
= products
.begin(); it
< products
.end();
1100 const Product
& product
= **it
;
1101 installer::InactiveUserToastExperiment(
1102 flavor
, base::ASCIIToUTF16(experiment_group
), product
,
1103 installer_state
->target_path());
1106 } else if (cmd_line
.HasSwitch(installer::switches::kSystemLevelToast
)) {
1107 const Products
& products
= installer_state
->products();
1108 for (Products::const_iterator it
= products
.begin(); it
< products
.end();
1110 const Product
& product
= **it
;
1111 BrowserDistribution
* browser_dist
= product
.distribution();
1112 // We started as system-level and have been re-launched as user level
1113 // to continue with the toast experiment.
1114 Version installed_version
;
1115 InstallUtil::GetChromeVersion(browser_dist
, true, &installed_version
);
1116 if (!installed_version
.IsValid()) {
1117 LOG(ERROR
) << "No installation of "
1118 << browser_dist
->GetDisplayName()
1119 << " found for system-level toast.";
1121 product
.LaunchUserExperiment(
1122 setup_exe
, installer::REENTRY_SYS_UPDATE
, true);
1125 } else if (cmd_line
.HasSwitch(installer::switches::kPatch
)) {
1126 const std::string
patch_type_str(
1127 cmd_line
.GetSwitchValueASCII(installer::switches::kPatch
));
1128 const base::FilePath
input_file(
1129 cmd_line
.GetSwitchValuePath(installer::switches::kInputFile
));
1130 const base::FilePath
patch_file(
1131 cmd_line
.GetSwitchValuePath(installer::switches::kPatchFile
));
1132 const base::FilePath
output_file(
1133 cmd_line
.GetSwitchValuePath(installer::switches::kOutputFile
));
1135 if (patch_type_str
== installer::kCourgette
) {
1136 *exit_code
= installer::CourgettePatchFiles(input_file
,
1139 } else if (patch_type_str
== installer::kBsdiff
) {
1140 *exit_code
= installer::BsdiffPatchFiles(input_file
,
1144 *exit_code
= installer::PATCH_INVALID_ARGUMENTS
;
1146 } else if (cmd_line
.HasSwitch(installer::switches::kReenableAutoupdates
)) {
1147 // setup.exe has been asked to attempt to reenable updates for Chrome.
1148 bool updates_enabled
= GoogleUpdateSettings::ReenableAutoupdates();
1149 *exit_code
= updates_enabled
? installer::REENABLE_UPDATES_SUCCEEDED
:
1150 installer::REENABLE_UPDATES_FAILED
;
1158 bool ShowRebootDialog() {
1159 // Get a token for this process.
1161 if (!OpenProcessToken(GetCurrentProcess(),
1162 TOKEN_ADJUST_PRIVILEGES
| TOKEN_QUERY
,
1164 LOG(ERROR
) << "Failed to open token.";
1168 // Use a ScopedHandle to keep track of and eventually close our handle.
1169 // TODO(robertshield): Add a Receive() method to base's ScopedHandle.
1170 base::win::ScopedHandle
scoped_handle(token
);
1172 // Get the LUID for the shutdown privilege.
1173 TOKEN_PRIVILEGES tkp
= {0};
1174 LookupPrivilegeValue(NULL
, SE_SHUTDOWN_NAME
, &tkp
.Privileges
[0].Luid
);
1175 tkp
.PrivilegeCount
= 1;
1176 tkp
.Privileges
[0].Attributes
= SE_PRIVILEGE_ENABLED
;
1178 // Get the shutdown privilege for this process.
1179 AdjustTokenPrivileges(token
, FALSE
, &tkp
, 0,
1180 reinterpret_cast<PTOKEN_PRIVILEGES
>(NULL
), 0);
1181 if (GetLastError() != ERROR_SUCCESS
) {
1182 LOG(ERROR
) << "Unable to get shutdown privileges.";
1186 // Popup a dialog that will prompt to reboot using the default system message.
1187 // TODO(robertshield): Add a localized, more specific string to the prompt.
1188 RestartDialog(NULL
, NULL
, EWX_REBOOT
| EWX_FORCEIFHUNG
);
1192 // Returns the Custom information for the client identified by the exe path
1193 // passed in. This information is used for crash reporting.
1194 google_breakpad::CustomClientInfo
* GetCustomInfo(const wchar_t* exe_path
) {
1195 base::string16 product
;
1196 base::string16 version
;
1197 scoped_ptr
<FileVersionInfo
> version_info(
1198 FileVersionInfo::CreateFileVersionInfo(base::FilePath(exe_path
)));
1199 if (version_info
.get()) {
1200 version
= version_info
->product_version();
1201 product
= version_info
->product_short_name();
1204 if (version
.empty())
1205 version
= L
"0.1.0.0";
1207 if (product
.empty())
1208 product
= L
"Chrome Installer";
1210 static google_breakpad::CustomInfoEntry
ver_entry(L
"ver", version
.c_str());
1211 static google_breakpad::CustomInfoEntry
prod_entry(L
"prod", product
.c_str());
1212 static google_breakpad::CustomInfoEntry
plat_entry(L
"plat", L
"Win32");
1213 static google_breakpad::CustomInfoEntry
type_entry(L
"ptype",
1214 L
"Chrome Installer");
1215 static google_breakpad::CustomInfoEntry entries
[] = {
1216 ver_entry
, prod_entry
, plat_entry
, type_entry
};
1217 static google_breakpad::CustomClientInfo custom_info
= {
1218 entries
, arraysize(entries
) };
1219 return &custom_info
;
1222 // Initialize crash reporting for this process. This involves connecting to
1224 scoped_ptr
<google_breakpad::ExceptionHandler
> InitializeCrashReporting(
1225 bool system_install
) {
1226 // Only report crashes if the user allows it.
1227 if (!GoogleUpdateSettings::GetCollectStatsConsent())
1228 return scoped_ptr
<google_breakpad::ExceptionHandler
>();
1230 // Get the alternate dump directory. We use the temp path.
1231 base::FilePath temp_directory
;
1232 if (!base::GetTempDir(&temp_directory
) || temp_directory
.empty())
1233 return scoped_ptr
<google_breakpad::ExceptionHandler
>();
1235 wchar_t exe_path
[MAX_PATH
* 2] = {0};
1236 GetModuleFileName(NULL
, exe_path
, arraysize(exe_path
));
1238 // Build the pipe name. It can be either:
1239 // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18"
1240 // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>"
1241 base::string16 user_sid
= kSystemPrincipalSid
;
1243 if (!system_install
) {
1244 if (!base::win::GetUserSidString(&user_sid
)) {
1245 return scoped_ptr
<google_breakpad::ExceptionHandler
>();
1249 base::string16 pipe_name
= kGoogleUpdatePipeName
;
1250 pipe_name
+= user_sid
;
1252 return scoped_ptr
<google_breakpad::ExceptionHandler
>(
1253 new google_breakpad::ExceptionHandler(
1254 temp_directory
.value(), NULL
, NULL
, NULL
,
1255 google_breakpad::ExceptionHandler::HANDLER_ALL
, kLargerDumpType
,
1256 pipe_name
.c_str(), GetCustomInfo(exe_path
)));
1259 // Uninstalls multi-install Chrome Frame if the current operation is a
1260 // multi-install install or update. The operation is performed directly rather
1261 // than delegated to the existing install since there is no facility in older
1262 // versions of setup.exe to uninstall GCF without touching the binaries. The
1263 // binaries will be uninstalled during later processing if they are not in-use
1264 // (see UninstallBinariesIfUnused). |original_state| and |installer_state| are
1265 // updated to reflect the state of the world following the operation.
1266 void UninstallMultiChromeFrameIfPresent(const base::CommandLine
& cmd_line
,
1267 const MasterPreferences
& prefs
,
1268 InstallationState
* original_state
,
1269 InstallerState
* installer_state
) {
1270 // Early exit if not installing or updating.
1271 if (installer_state
->operation() == InstallerState::UNINSTALL
)
1274 // Early exit if Chrome Frame is not present as multi-install.
1275 const ProductState
* chrome_frame_state
=
1276 original_state
->GetProductState(installer_state
->system_install(),
1277 BrowserDistribution::CHROME_FRAME
);
1278 if (!chrome_frame_state
|| !chrome_frame_state
->is_multi_install())
1281 LOG(INFO
) << "Uninstalling multi-install Chrome Frame.";
1282 installer_state
->UpdateStage(installer::UNINSTALLING_CHROME_FRAME
);
1284 // Uninstall Chrome Frame without touching the multi-install binaries.
1285 // Simulate the uninstall as coming from the installed version.
1286 const base::CommandLine
& uninstall_cmd(
1287 chrome_frame_state
->uninstall_command());
1288 MasterPreferences
uninstall_prefs(uninstall_cmd
);
1289 InstallerState uninstall_state
;
1290 uninstall_state
.Initialize(uninstall_cmd
, uninstall_prefs
, *original_state
);
1291 // Post M32, uninstall_prefs and uninstall_state won't have Chrome Frame in
1292 // them since they've lost the power to do Chrome Frame installs.
1293 const Product
* chrome_frame_product
= uninstall_state
.AddProductFromState(
1294 BrowserDistribution::CHROME_FRAME
, *chrome_frame_state
);
1295 if (chrome_frame_product
) {
1296 // No shared state should be left behind.
1297 const bool remove_all
= true;
1298 // Don't accept no for an answer.
1299 const bool force_uninstall
= true;
1300 installer::InstallStatus uninstall_status
=
1301 installer::UninstallProduct(*original_state
, uninstall_state
,
1302 uninstall_cmd
.GetProgram(),
1303 *chrome_frame_product
, remove_all
,
1304 force_uninstall
, cmd_line
);
1306 VLOG(1) << "Uninstallation of Chrome Frame returned status "
1307 << uninstall_status
;
1309 LOG(ERROR
) << "Chrome Frame not found for uninstall.";
1312 // Refresh state for the continuation of the original install/update.
1313 original_state
->Initialize();
1314 installer_state
->Initialize(cmd_line
, prefs
, *original_state
);
1319 namespace installer
{
1321 InstallStatus
InstallProductsHelper(const InstallationState
& original_state
,
1322 const base::FilePath
& setup_exe
,
1323 const base::CommandLine
& cmd_line
,
1324 const MasterPreferences
& prefs
,
1325 const InstallerState
& installer_state
,
1326 base::FilePath
* installer_directory
,
1327 ArchiveType
* archive_type
,
1328 bool* delegated_to_existing
) {
1329 DCHECK(archive_type
);
1330 DCHECK(delegated_to_existing
);
1331 const bool system_install
= installer_state
.system_install();
1332 InstallStatus install_status
= UNKNOWN_STATUS
;
1334 // Drop to background processing mode if the process was started below the
1335 // normal process priority class.
1336 bool entered_background_mode
= AdjustProcessPriority();
1337 VLOG_IF(1, entered_background_mode
) << "Entered background processing mode.";
1339 // Create a temp folder where we will unpack Chrome archive. If it fails,
1340 // then we are doomed, so return immediately and no cleanup is required.
1341 SelfCleaningTempDir temp_path
;
1342 base::FilePath unpack_path
;
1343 if (!CreateTemporaryAndUnpackDirectories(installer_state
, &temp_path
,
1345 installer_state
.WriteInstallerResult(TEMP_DIR_FAILED
,
1346 IDS_INSTALL_TEMP_DIR_FAILED_BASE
,
1348 return TEMP_DIR_FAILED
;
1351 // Uncompress and optionally patch the archive if an uncompressed archive was
1352 // not specified on the command line and a compressed archive is found.
1353 *archive_type
= UNKNOWN_ARCHIVE_TYPE
;
1354 base::FilePath
uncompressed_archive(cmd_line
.GetSwitchValuePath(
1355 switches::kUncompressedArchive
));
1356 if (uncompressed_archive
.empty()) {
1357 scoped_ptr
<ArchivePatchHelper
> archive_helper(
1358 CreateChromeArchiveHelper(setup_exe
, cmd_line
, installer_state
,
1360 if (archive_helper
) {
1361 VLOG(1) << "Installing Chrome from compressed archive "
1362 << archive_helper
->compressed_archive().value();
1363 if (!UncompressAndPatchChromeArchive(original_state
,
1365 archive_helper
.get(),
1368 DCHECK_NE(install_status
, UNKNOWN_STATUS
);
1369 return install_status
;
1371 uncompressed_archive
= archive_helper
->target();
1372 DCHECK(!uncompressed_archive
.empty());
1376 // Check for an uncompressed archive alongside the current executable if one
1377 // was not given or generated.
1378 if (uncompressed_archive
.empty())
1379 uncompressed_archive
= setup_exe
.DirName().Append(kChromeArchive
);
1381 if (*archive_type
== UNKNOWN_ARCHIVE_TYPE
) {
1382 // An archive was not uncompressed or patched above.
1383 if (uncompressed_archive
.empty() ||
1384 !base::PathExists(uncompressed_archive
)) {
1385 LOG(ERROR
) << "Cannot install Chrome without an uncompressed archive.";
1386 installer_state
.WriteInstallerResult(
1387 INVALID_ARCHIVE
, IDS_INSTALL_INVALID_ARCHIVE_BASE
, NULL
);
1388 return INVALID_ARCHIVE
;
1390 *archive_type
= FULL_ARCHIVE_TYPE
;
1393 // Unpack the uncompressed archive.
1394 if (LzmaUtil::UnPackArchive(uncompressed_archive
.value(),
1395 unpack_path
.value(),
1397 installer_state
.WriteInstallerResult(
1399 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE
,
1401 return UNPACKING_FAILED
;
1404 VLOG(1) << "unpacked to " << unpack_path
.value();
1405 base::FilePath
src_path(
1406 unpack_path
.Append(kInstallSourceChromeDir
));
1408 installer_version(GetMaxVersionFromArchiveDir(src_path
));
1409 if (!installer_version
.get()) {
1410 LOG(ERROR
) << "Did not find any valid version in installer.";
1411 install_status
= INVALID_ARCHIVE
;
1412 installer_state
.WriteInstallerResult(install_status
,
1413 IDS_INSTALL_INVALID_ARCHIVE_BASE
, NULL
);
1415 VLOG(1) << "version to install: " << installer_version
->GetString();
1416 bool proceed_with_installation
= true;
1418 if (installer_state
.operation() == InstallerState::MULTI_INSTALL
) {
1419 // This is a new install of a multi-install product. Rather than give up
1420 // in case a higher version of the binaries (including a single-install
1421 // of Chrome, which can safely be migrated to multi-install by way of
1422 // CheckMultiInstallConditions) is already installed, delegate to the
1423 // installed setup.exe to install the product at hand.
1424 base::FilePath existing_setup_exe
;
1425 if (GetExistingHigherInstaller(original_state
, system_install
,
1426 *installer_version
, &existing_setup_exe
)) {
1427 VLOG(1) << "Deferring to existing installer.";
1428 installer_state
.UpdateStage(DEFERRING_TO_HIGHER_VERSION
);
1429 if (DeferToExistingInstall(existing_setup_exe
, cmd_line
,
1430 installer_state
, temp_path
.path(),
1432 *delegated_to_existing
= true;
1433 return install_status
;
1438 uint32 higher_products
= 0;
1440 sizeof(higher_products
) * 8 > BrowserDistribution::NUM_TYPES
,
1441 too_many_distribution_types_
);
1442 const Products
& products
= installer_state
.products();
1443 for (Products::const_iterator it
= products
.begin(); it
< products
.end();
1445 const Product
& product
= **it
;
1446 const ProductState
* product_state
=
1447 original_state
.GetProductState(system_install
,
1448 product
.distribution()->GetType());
1449 if (product_state
!= NULL
&&
1450 (product_state
->version().CompareTo(*installer_version
) > 0)) {
1451 LOG(ERROR
) << "Higher version of "
1452 << product
.distribution()->GetDisplayName()
1453 << " is already installed.";
1454 higher_products
|= (1 << product
.distribution()->GetType());
1458 if (higher_products
!= 0) {
1459 COMPILE_ASSERT(BrowserDistribution::NUM_TYPES
== 3,
1460 add_support_for_new_products_here_
);
1461 int message_id
= IDS_INSTALL_HIGHER_VERSION_BASE
;
1462 proceed_with_installation
= false;
1463 install_status
= HIGHER_VERSION_EXISTS
;
1464 installer_state
.WriteInstallerResult(install_status
, message_id
, NULL
);
1467 if (proceed_with_installation
) {
1468 base::FilePath
prefs_source_path(cmd_line
.GetSwitchValueNative(
1469 switches::kInstallerData
));
1470 install_status
= InstallOrUpdateProduct(
1471 original_state
, installer_state
, setup_exe
, uncompressed_archive
,
1472 temp_path
.path(), src_path
, prefs_source_path
, prefs
,
1473 *installer_version
);
1475 int install_msg_base
= IDS_INSTALL_FAILED_BASE
;
1476 base::FilePath chrome_exe
;
1477 base::string16 quoted_chrome_exe
;
1478 if (install_status
== SAME_VERSION_REPAIR_FAILED
) {
1479 install_msg_base
= IDS_SAME_VERSION_REPAIR_FAILED_BASE
;
1480 } else if (install_status
!= INSTALL_FAILED
) {
1481 if (installer_state
.target_path().empty()) {
1482 // If we failed to construct install path, it means the OS call to
1483 // get %ProgramFiles% or %AppData% failed. Report this as failure.
1484 install_msg_base
= IDS_INSTALL_OS_ERROR_BASE
;
1485 install_status
= OS_ERROR
;
1487 chrome_exe
= installer_state
.target_path().Append(kChromeExe
);
1488 quoted_chrome_exe
= L
"\"" + chrome_exe
.value() + L
"\"";
1489 install_msg_base
= 0;
1493 installer_state
.UpdateStage(FINISHING
);
1495 // Only do Chrome-specific stuff (like launching the browser) if
1496 // Chrome was specifically requested (rather than being upgraded as
1497 // part of a multi-install).
1498 const Product
* chrome_install
= prefs
.install_chrome() ?
1499 installer_state
.FindProduct(BrowserDistribution::CHROME_BROWSER
) :
1502 bool do_not_register_for_update_launch
= false;
1503 if (chrome_install
) {
1504 prefs
.GetBool(master_preferences::kDoNotRegisterForUpdateLaunch
,
1505 &do_not_register_for_update_launch
);
1507 do_not_register_for_update_launch
= true; // Never register.
1510 bool write_chrome_launch_string
=
1511 (!do_not_register_for_update_launch
&&
1512 install_status
!= IN_USE_UPDATED
);
1514 installer_state
.WriteInstallerResult(install_status
, install_msg_base
,
1515 write_chrome_launch_string
? "ed_chrome_exe
: NULL
);
1517 if (install_status
== FIRST_INSTALL_SUCCESS
) {
1518 VLOG(1) << "First install successful.";
1519 if (chrome_install
) {
1520 // We never want to launch Chrome in system level install mode.
1521 bool do_not_launch_chrome
= false;
1522 prefs
.GetBool(master_preferences::kDoNotLaunchChrome
,
1523 &do_not_launch_chrome
);
1524 if (!system_install
&& !do_not_launch_chrome
)
1525 chrome_install
->LaunchChrome(installer_state
.target_path());
1527 } else if ((install_status
== NEW_VERSION_UPDATED
) ||
1528 (install_status
== IN_USE_UPDATED
)) {
1529 const Product
* chrome
= installer_state
.FindProduct(
1530 BrowserDistribution::CHROME_BROWSER
);
1531 if (chrome
!= NULL
) {
1532 DCHECK_NE(chrome_exe
.value(), base::string16());
1533 RemoveChromeLegacyRegistryKeys(chrome
->distribution(), chrome_exe
);
1539 // There might be an experiment (for upgrade usually) that needs to happen.
1540 // An experiment's outcome can include chrome's uninstallation. If that is
1541 // the case we would not do that directly at this point but in another
1542 // instance of setup.exe
1544 // There is another way to reach this same function if this is a system
1545 // level install. See HandleNonInstallCmdLineOptions().
1547 // If installation failed, use the path to the currently running setup.
1548 // If installation succeeded, use the path to setup in the installer dir.
1549 base::FilePath
setup_path(setup_exe
);
1550 if (InstallUtil::GetInstallReturnCode(install_status
) == 0) {
1551 setup_path
= installer_state
.GetInstallerDirectory(*installer_version
)
1552 .Append(setup_path
.BaseName());
1554 const Products
& products
= installer_state
.products();
1555 for (Products::const_iterator it
= products
.begin(); it
< products
.end();
1557 const Product
& product
= **it
;
1558 product
.LaunchUserExperiment(setup_path
, install_status
, system_install
);
1562 // If installation completed successfully, return the path to the directory
1563 // containing the newly installed setup.exe and uncompressed archive if the
1564 // caller requested it.
1565 if (installer_directory
&&
1566 InstallUtil::GetInstallReturnCode(install_status
) == 0) {
1567 *installer_directory
=
1568 installer_state
.GetInstallerDirectory(*installer_version
);
1571 // temp_path's dtor will take care of deleting or scheduling itself for
1572 // deletion at reboot when this scope closes.
1573 VLOG(1) << "Deleting temporary directory " << temp_path
.path().value();
1575 return install_status
;
1578 } // namespace installer
1580 int WINAPI
wWinMain(HINSTANCE instance
, HINSTANCE prev_instance
,
1581 wchar_t* command_line
, int show_command
) {
1582 // Check to see if the CPU is supported before doing anything else. There's
1583 // very little than can safely be accomplished if the CPU isn't supported
1584 // since dependent libraries (e.g., base) may use invalid instructions.
1585 if (!installer::IsProcessorSupported())
1586 return installer::CPU_NOT_SUPPORTED
;
1588 // The exit manager is in charge of calling the dtors of singletons.
1589 base::AtExitManager exit_manager
;
1590 base::CommandLine::Init(0, NULL
);
1592 // install_util uses chrome paths.
1593 chrome::RegisterPathProvider();
1595 const MasterPreferences
& prefs
= MasterPreferences::ForCurrentProcess();
1596 installer::InitInstallerLogging(prefs
);
1598 const base::CommandLine
& cmd_line
= *base::CommandLine::ForCurrentProcess();
1599 VLOG(1) << "Command Line: " << cmd_line
.GetCommandLineString();
1601 VLOG(1) << "multi install is " << prefs
.is_multi_install();
1602 bool system_install
= false;
1603 prefs
.GetBool(installer::master_preferences::kSystemLevel
, &system_install
);
1604 VLOG(1) << "system install is " << system_install
;
1606 scoped_ptr
<google_breakpad::ExceptionHandler
> breakpad(
1607 InitializeCrashReporting(system_install
));
1609 InstallationState original_state
;
1610 original_state
.Initialize();
1612 InstallerState installer_state
;
1613 installer_state
.Initialize(cmd_line
, prefs
, original_state
);
1614 const bool is_uninstall
= cmd_line
.HasSwitch(installer::switches::kUninstall
);
1616 // Check to make sure current system is WinXP or later. If not, log
1617 // error message and get out.
1618 if (!InstallUtil::IsOSSupported()) {
1619 LOG(ERROR
) << "Chrome only supports Windows XP or later.";
1620 installer_state
.WriteInstallerResult(
1621 installer::OS_NOT_SUPPORTED
, IDS_INSTALL_OS_NOT_SUPPORTED_BASE
, NULL
);
1622 return installer::OS_NOT_SUPPORTED
;
1625 // Initialize COM for use later.
1626 base::win::ScopedCOMInitializer com_initializer
;
1627 if (!com_initializer
.succeeded()) {
1628 installer_state
.WriteInstallerResult(
1629 installer::OS_ERROR
, IDS_INSTALL_OS_ERROR_BASE
, NULL
);
1630 return installer::OS_ERROR
;
1633 // Some command line options don't work with SxS install/uninstall
1634 if (InstallUtil::IsChromeSxSProcess()) {
1635 if (system_install
||
1636 prefs
.is_multi_install() ||
1637 cmd_line
.HasSwitch(installer::switches::kSelfDestruct
) ||
1638 cmd_line
.HasSwitch(installer::switches::kMakeChromeDefault
) ||
1639 cmd_line
.HasSwitch(installer::switches::kRegisterChromeBrowser
) ||
1640 cmd_line
.HasSwitch(installer::switches::kRemoveChromeRegistration
) ||
1641 cmd_line
.HasSwitch(installer::switches::kInactiveUserToast
) ||
1642 cmd_line
.HasSwitch(installer::switches::kSystemLevelToast
)) {
1643 return installer::SXS_OPTION_NOT_SUPPORTED
;
1647 // Some command line options are no longer supported and must error out.
1648 if (installer::ContainsUnsupportedSwitch(cmd_line
))
1649 return installer::UNSUPPORTED_OPTION
;
1651 // A variety of installer operations require the path to the current
1652 // executable. Get it once here for use throughout these operations. Note that
1653 // the path service is the authoritative source for this path. One might think
1654 // that CommandLine::GetProgram would suffice, but it won't since
1655 // CreateProcess may have been called with a command line that is somewhat
1656 // ambiguous (e.g., an unquoted path with spaces, or a path lacking the file
1657 // extension), in which case CommandLineToArgv will not yield an argv with the
1658 // true path to the program at position 0.
1659 base::FilePath setup_exe
;
1660 PathService::Get(base::FILE_EXE
, &setup_exe
);
1663 if (HandleNonInstallCmdLineOptions(
1664 original_state
, setup_exe
, cmd_line
, &installer_state
, &exit_code
)) {
1668 if (system_install
&& !IsUserAnAdmin()) {
1669 if (base::win::GetVersion() >= base::win::VERSION_VISTA
&&
1670 !cmd_line
.HasSwitch(installer::switches::kRunAsAdmin
)) {
1671 base::CommandLine
new_cmd(base::CommandLine::NO_PROGRAM
);
1672 new_cmd
.AppendArguments(cmd_line
, true);
1673 // Append --run-as-admin flag to let the new instance of setup.exe know
1674 // that we already tried to launch ourselves as admin.
1675 new_cmd
.AppendSwitch(installer::switches::kRunAsAdmin
);
1676 // If system_install became true due to an environment variable, append
1677 // it to the command line here since env vars may not propagate past the
1679 if (!new_cmd
.HasSwitch(installer::switches::kSystemLevel
))
1680 new_cmd
.AppendSwitch(installer::switches::kSystemLevel
);
1682 DWORD exit_code
= installer::UNKNOWN_STATUS
;
1683 InstallUtil::ExecuteExeAsAdmin(new_cmd
, &exit_code
);
1686 LOG(ERROR
) << "Non admin user can not install system level Chrome.";
1687 installer_state
.WriteInstallerResult(installer::INSUFFICIENT_RIGHTS
,
1688 IDS_INSTALL_INSUFFICIENT_RIGHTS_BASE
, NULL
);
1689 return installer::INSUFFICIENT_RIGHTS
;
1693 UninstallMultiChromeFrameIfPresent(cmd_line
, prefs
,
1694 &original_state
, &installer_state
);
1696 base::FilePath installer_directory
;
1697 installer::InstallStatus install_status
= installer::UNKNOWN_STATUS
;
1698 // If --uninstall option is given, uninstall the identified product(s)
1701 UninstallProducts(original_state
, installer_state
, setup_exe
, cmd_line
);
1703 // If --uninstall option is not specified, we assume it is install case.
1705 InstallProducts(original_state
, setup_exe
, cmd_line
, prefs
,
1706 &installer_state
, &installer_directory
);
1709 // Validate that the machine is now in a good state following the operation.
1710 // TODO(grt): change this to log at DFATAL once we're convinced that the
1711 // validator handles all cases properly.
1712 InstallationValidator::InstallationType installation_type
=
1713 InstallationValidator::NO_PRODUCTS
;
1715 !InstallationValidator::ValidateInstallationType(system_install
,
1716 &installation_type
));
1718 int return_code
= 0;
1719 // MSI demands that custom actions always return 0 (ERROR_SUCCESS) or it will
1720 // rollback the action. If we're uninstalling we want to avoid this, so always
1721 // report success, squashing any more informative return codes.
1722 if (!(installer_state
.is_msi() && is_uninstall
)) {
1723 // Note that we allow the status installer::UNINSTALL_REQUIRES_REBOOT
1724 // to pass through, since this is only returned on uninstall which is
1725 // never invoked directly by Google Update.
1726 return_code
= InstallUtil::GetInstallReturnCode(install_status
);
1729 VLOG(1) << "Installation complete, returning: " << return_code
;