1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/installer/setup/setup_main.h"
14 #include "base/at_exit.h"
15 #include "base/basictypes.h"
16 #include "base/command_line.h"
17 #include "base/file_util.h"
18 #include "base/file_version_info.h"
19 #include "base/files/file_path.h"
20 #include "base/files/scoped_temp_dir.h"
21 #include "base/memory/scoped_ptr.h"
22 #include "base/path_service.h"
23 #include "base/process/launch.h"
24 #include "base/strings/string16.h"
25 #include "base/strings/string_number_conversions.h"
26 #include "base/strings/string_util.h"
27 #include "base/strings/stringprintf.h"
28 #include "base/strings/utf_string_conversions.h"
29 #include "base/values.h"
30 #include "base/win/registry.h"
31 #include "base/win/scoped_com_initializer.h"
32 #include "base/win/scoped_comptr.h"
33 #include "base/win/scoped_handle.h"
34 #include "base/win/win_util.h"
35 #include "base/win/windows_version.h"
36 #include "breakpad/src/client/windows/handler/exception_handler.h"
37 #include "chrome/common/chrome_constants.h"
38 #include "chrome/common/chrome_switches.h"
39 #include "chrome/installer/setup/archive_patch_helper.h"
40 #include "chrome/installer/setup/install.h"
41 #include "chrome/installer/setup/install_worker.h"
42 #include "chrome/installer/setup/setup_constants.h"
43 #include "chrome/installer/setup/setup_util.h"
44 #include "chrome/installer/setup/uninstall.h"
45 #include "chrome/installer/util/browser_distribution.h"
46 #include "chrome/installer/util/channel_info.h"
47 #include "chrome/installer/util/delete_after_reboot_helper.h"
48 #include "chrome/installer/util/delete_tree_work_item.h"
49 #include "chrome/installer/util/eula_util.h"
50 #include "chrome/installer/util/google_update_constants.h"
51 #include "chrome/installer/util/google_update_settings.h"
52 #include "chrome/installer/util/google_update_util.h"
53 #include "chrome/installer/util/helper.h"
54 #include "chrome/installer/util/html_dialog.h"
55 #include "chrome/installer/util/install_util.h"
56 #include "chrome/installer/util/installation_state.h"
57 #include "chrome/installer/util/installation_validator.h"
58 #include "chrome/installer/util/installer_state.h"
59 #include "chrome/installer/util/l10n_string_util.h"
60 #include "chrome/installer/util/logging_installer.h"
61 #include "chrome/installer/util/lzma_util.h"
62 #include "chrome/installer/util/master_preferences.h"
63 #include "chrome/installer/util/master_preferences_constants.h"
64 #include "chrome/installer/util/self_cleaning_temp_dir.h"
65 #include "chrome/installer/util/shell_util.h"
66 #include "chrome/installer/util/user_experiment.h"
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.
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 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
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
124 return scoped_ptr
<installer::ArchivePatchHelper
>(
125 new installer::ArchivePatchHelper(working_directory
,
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
,
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
;
159 // Find the installed version's archive to serve as the source for patching.
160 base::FilePath
patch_source(installer::FindArchiveToPatch(original_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
,
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
,
184 *archive_type
= installer::INCREMENTAL_ARCHIVE_TYPE
;
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(),
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
)->
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
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(),
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();
264 version_key
= (*it
)->distribution()->GetVersionKey();
265 install_list
->AddDeleteRegValueWorkItem(
266 reg_root
, version_key
, google_update::kRegOldVersionField
);
267 install_list
->AddDeleteRegValueWorkItem(
268 reg_root
, version_key
, google_update::kRegCriticalVersionField
);
269 install_list
->AddDeleteRegValueWorkItem(
270 reg_root
, version_key
, google_update::kRegRenameCmdField
);
272 installer::InstallStatus ret
= installer::RENAME_SUCCESSFUL
;
273 if (!install_list
->Do()) {
274 LOG(ERROR
) << "Renaming of executables failed. Rolling back any changes.";
275 install_list
->Rollback();
276 ret
= installer::RENAME_FAILED
;
278 // temp_path's dtor will take care of deleting or scheduling itself for
279 // deletion at reboot when this scope closes.
280 VLOG(1) << "Deleting temporary directory " << temp_path
.path().value();
285 // For each product that is being updated (i.e., already installed at an earlier
286 // version), see if that product has an update policy override that differs from
287 // that for the binaries. If any are found, fail with an error indicating that
288 // the Group Policy settings are in an inconsistent state. Do not do this test
289 // for same-version installs, since it would be unkind to block attempts to
290 // repair a corrupt installation. This function returns false when installation
291 // should be halted, in which case |status| contains the relevant exit code and
292 // the proper installer result has been written to the registry.
293 bool CheckGroupPolicySettings(const InstallationState
& original_state
,
294 const InstallerState
& installer_state
,
295 const Version
& new_version
,
296 installer::InstallStatus
* status
) {
297 #if !defined(GOOGLE_CHROME_BUILD)
298 // Chromium builds are not updated via Google Update, so there are no
299 // Group Policy settings to consult.
304 // Single installs are always in good shape.
305 if (!installer_state
.is_multi_install())
308 bool settings_are_valid
= true;
309 const bool is_system_install
= installer_state
.system_install();
310 BrowserDistribution
* const binaries_dist
=
311 installer_state
.multi_package_binaries_distribution();
313 // Get the update policy for the binaries.
314 const GoogleUpdateSettings::UpdatePolicy binaries_policy
=
315 GoogleUpdateSettings::GetAppUpdatePolicy(binaries_dist
->GetAppGuid(),
318 // Check for differing update policies for all of the products being updated.
319 const Products
& products
= installer_state
.products();
320 Products::const_iterator scan
= products
.begin();
321 for (Products::const_iterator end
= products
.end(); scan
!= end
; ++scan
) {
322 BrowserDistribution
* dist
= (*scan
)->distribution();
323 const ProductState
* product_state
=
324 original_state
.GetProductState(is_system_install
, dist
->GetType());
325 // Is an earlier version of this product already installed?
326 if (product_state
!= NULL
&&
327 product_state
->version().CompareTo(new_version
) < 0) {
328 bool is_overridden
= false;
329 GoogleUpdateSettings::UpdatePolicy app_policy
=
330 GoogleUpdateSettings::GetAppUpdatePolicy(dist
->GetAppGuid(),
332 if (is_overridden
&& app_policy
!= binaries_policy
) {
333 LOG(ERROR
) << "Found legacy Group Policy setting for "
334 << dist
->GetDisplayName() << " (value: " << app_policy
335 << ") that does not match the setting for "
336 << binaries_dist
->GetDisplayName()
337 << " (value: " << binaries_policy
<< ").";
338 settings_are_valid
= false;
343 if (!settings_are_valid
) {
344 // TODO(grt): add " See http://goo.gl/+++ for details." to the end of this
345 // log message and to the IDS_INSTALL_INCONSISTENT_UPDATE_POLICY string once
346 // we have a help center article that explains why this error is being
347 // reported and how to resolve it.
348 LOG(ERROR
) << "Cannot apply update on account of inconsistent "
349 "Google Update Group Policy settings. Use the Group Policy "
350 "Editor to set the update policy override for the "
351 << binaries_dist
->GetDisplayName()
352 << " application and try again.";
353 *status
= installer::INCONSISTENT_UPDATE_POLICY
;
354 installer_state
.WriteInstallerResult(
355 *status
, IDS_INSTALL_INCONSISTENT_UPDATE_POLICY_BASE
, NULL
);
358 return settings_are_valid
;
359 #endif // defined(GOOGLE_CHROME_BUILD)
362 // If only the binaries are being updated, fail.
363 // If any product is being installed in single-mode that already exists in
365 bool CheckMultiInstallConditions(const InstallationState
& original_state
,
366 InstallerState
* installer_state
,
367 installer::InstallStatus
* status
) {
368 const Products
& products
= installer_state
->products();
369 DCHECK(products
.size());
371 const bool system_level
= installer_state
->system_install();
373 if (installer_state
->is_multi_install()) {
374 const Product
* chrome
=
375 installer_state
->FindProduct(BrowserDistribution::CHROME_BROWSER
);
376 const Product
* app_host
=
377 installer_state
->FindProduct(BrowserDistribution::CHROME_APP_HOST
);
378 const Product
* binaries
=
379 installer_state
->FindProduct(BrowserDistribution::CHROME_BINARIES
);
380 const ProductState
* chrome_state
=
381 original_state
.GetProductState(system_level
,
382 BrowserDistribution::CHROME_BROWSER
);
385 if (products
.size() == 1) {
386 // There are no products aside from the binaries, so there is no update
387 // to be applied. This can happen after multi-install Chrome Frame is
388 // migrated to single-install. This is treated as an update failure
389 // unless the binaries are not in-use, in which case they will be
390 // uninstalled and success will be reported (see handling in wWinMain).
391 VLOG(1) << "No products to be updated.";
392 *status
= installer::UNUSED_BINARIES
;
393 installer_state
->WriteInstallerResult(*status
, 0, NULL
);
397 // This will only be hit if --multi-install is given with no products, or
398 // if the app host is being installed and doesn't need the binaries at
400 // The former case might be due to a request by an orphaned Application
401 // Host to re-install the binaries. Thus we add them to the installation.
402 // The latter case is fine and we let it be.
403 // If this is not an app host install and the binaries are not already
404 // present, the installation will fail later due to a lack of products to
406 if (app_host
&& !chrome
&& !chrome_state
) {
407 DCHECK(!system_level
);
408 // App Host may use Chrome/Chrome binaries at system-level.
409 if (original_state
.GetProductState(
411 BrowserDistribution::CHROME_BROWSER
) ||
412 original_state
.GetProductState(
414 BrowserDistribution::CHROME_BINARIES
)) {
415 VLOG(1) << "Installing/updating App Launcher without binaries.";
417 // Somehow the binaries were present when the quick-enable app host
418 // command was run, but now they appear to be missing.
419 // Force binaries to be installed/updated.
420 scoped_ptr
<Product
> binaries_to_add(new Product(
421 BrowserDistribution::GetSpecificDistribution(
422 BrowserDistribution::CHROME_BINARIES
)));
423 binaries_to_add
->SetOption(installer::kOptionMultiInstall
, true);
424 binaries
= installer_state
->AddProduct(&binaries_to_add
);
426 "Adding binaries for pre-existing App Launcher installation.";
433 if (!chrome
&& chrome_state
) {
434 // A product other than Chrome is being installed in multi-install mode,
435 // and Chrome is already present. Add Chrome to the set of products
436 // (making it multi-install in the process) so that it is updated, too.
437 scoped_ptr
<Product
> multi_chrome(new Product(
438 BrowserDistribution::GetSpecificDistribution(
439 BrowserDistribution::CHROME_BROWSER
)));
440 multi_chrome
->SetOption(installer::kOptionMultiInstall
, true);
441 chrome
= installer_state
->AddProduct(&multi_chrome
);
442 VLOG(1) << "Upgrading existing Chrome browser in multi-install mode.";
445 // This is a non-multi installation.
447 // Check for an existing installation of the product.
448 const ProductState
* product_state
= original_state
.GetProductState(
449 system_level
, products
[0]->distribution()->GetType());
450 if (product_state
!= NULL
) {
451 // Block downgrades from multi-install to single-install.
452 if (product_state
->is_multi_install()) {
453 LOG(ERROR
) << "Multi-install "
454 << products
[0]->distribution()->GetDisplayName()
455 << " exists; aborting single install.";
456 *status
= installer::MULTI_INSTALLATION_EXISTS
;
457 installer_state
->WriteInstallerResult(*status
,
458 IDS_INSTALL_MULTI_INSTALLATION_EXISTS_BASE
, NULL
);
467 // Checks app host pre-install conditions, specifically that this is a
468 // user-level multi-install. When the pre-install conditions are not
469 // satisfied, the result is written to the registry (via WriteInstallerResult),
470 // |status| is set appropriately, and false is returned.
471 bool CheckAppHostPreconditions(const InstallationState
& original_state
,
472 InstallerState
* installer_state
,
473 installer::InstallStatus
* status
) {
474 if (installer_state
->FindProduct(BrowserDistribution::CHROME_APP_HOST
)) {
475 if (!installer_state
->is_multi_install()) {
476 LOG(DFATAL
) << "App Launcher requires multi install";
477 *status
= installer::APP_HOST_REQUIRES_MULTI_INSTALL
;
478 // No message string since there is nothing a user can do.
479 installer_state
->WriteInstallerResult(*status
, 0, NULL
);
483 if (installer_state
->system_install()) {
484 LOG(DFATAL
) << "App Launcher may only be installed at user-level.";
485 *status
= installer::APP_HOST_REQUIRES_USER_LEVEL
;
486 // No message string since there is nothing a user can do.
487 installer_state
->WriteInstallerResult(*status
, 0, NULL
);
495 // Checks for compatibility between the current state of the system and the
496 // desired operation. Also applies policy that mutates the desired operation;
497 // specifically, the |installer_state| object.
498 // Also blocks simultaneous user-level and system-level installs. In the case
499 // of trying to install user-level Chrome when system-level exists, the
500 // existing system-level Chrome is launched.
501 // When the pre-install conditions are not satisfied, the result is written to
502 // the registry (via WriteInstallerResult), |status| is set appropriately, and
503 // false is returned.
504 bool CheckPreInstallConditions(const InstallationState
& original_state
,
505 InstallerState
* installer_state
,
506 installer::InstallStatus
* status
) {
507 if (!CheckAppHostPreconditions(original_state
, installer_state
, status
)) {
508 DCHECK_NE(*status
, installer::UNKNOWN_STATUS
);
512 // See what products are already installed in multi mode. When we do multi
513 // installs, we must upgrade all installations since they share the binaries.
514 AddExistingMultiInstalls(original_state
, installer_state
);
516 if (!CheckMultiInstallConditions(original_state
, installer_state
, status
)) {
517 DCHECK_NE(*status
, installer::UNKNOWN_STATUS
);
521 const Products
& products
= installer_state
->products();
522 if (products
.empty()) {
523 // We haven't been given any products on which to operate.
525 << "Not given any products to install and no products found to update.";
526 *status
= installer::CHROME_NOT_INSTALLED
;
527 installer_state
->WriteInstallerResult(*status
,
528 IDS_INSTALL_NO_PRODUCTS_TO_UPDATE_BASE
, NULL
);
532 if (!installer_state
->system_install()) {
533 // This is a user-level installation. Make sure that we are not installing
534 // on top of an existing system-level installation.
535 for (Products::const_iterator it
= products
.begin(); it
< products
.end();
537 const Product
& product
= **it
;
538 BrowserDistribution
* browser_dist
= product
.distribution();
540 // Skip over the binaries, as it's okay for them to be at both levels
541 // for different products.
542 if (browser_dist
->GetType() == BrowserDistribution::CHROME_BINARIES
)
545 const ProductState
* user_level_product_state
=
546 original_state
.GetProductState(false, browser_dist
->GetType());
547 const ProductState
* system_level_product_state
=
548 original_state
.GetProductState(true, browser_dist
->GetType());
550 // Allow upgrades to proceed so that out-of-date versions are not left
552 if (user_level_product_state
)
555 // This is a new user-level install...
557 if (system_level_product_state
) {
558 // ... and the product already exists at system-level.
559 LOG(ERROR
) << "Already installed version "
560 << system_level_product_state
->version().GetString()
561 << " at system-level conflicts with this one at user-level.";
562 if (product
.is_chrome()) {
563 // Instruct Google Update to launch the existing system-level Chrome.
564 // There should be no error dialog.
565 base::FilePath
install_path(installer::GetChromeInstallPath(
568 if (install_path
.empty()) {
569 // Give up if we failed to construct the install path.
570 *status
= installer::OS_ERROR
;
571 installer_state
->WriteInstallerResult(*status
,
572 IDS_INSTALL_OS_ERROR_BASE
,
575 *status
= installer::EXISTING_VERSION_LAUNCHED
;
576 base::FilePath chrome_exe
=
577 install_path
.Append(installer::kChromeExe
);
578 CommandLine
cmd(chrome_exe
);
579 cmd
.AppendSwitch(switches::kForceFirstRun
);
580 installer_state
->WriteInstallerResult(*status
, 0, NULL
);
581 VLOG(1) << "Launching existing system-level chrome instead.";
582 base::LaunchProcess(cmd
, base::LaunchOptions(), NULL
);
585 // It's no longer possible for |product| to be anything other than
593 } else { // System-level install.
594 // --ensure-google-update-present is supported for user-level only.
595 // The flag is generic, but its primary use case involves App Host.
596 if (installer_state
->ensure_google_update_present()) {
597 LOG(DFATAL
) << "--" << installer::switches::kEnsureGoogleUpdatePresent
598 << " is supported for user-level only.";
599 *status
= installer::APP_HOST_REQUIRES_USER_LEVEL
;
600 // No message string since there is nothing a user can do.
601 installer_state
->WriteInstallerResult(*status
, 0, NULL
);
609 // Initializes |temp_path| to "Temp" within the target directory, and
610 // |unpack_path| to a random directory beginning with "source" within
611 // |temp_path|. Returns false on error.
612 bool CreateTemporaryAndUnpackDirectories(
613 const InstallerState
& installer_state
,
614 installer::SelfCleaningTempDir
* temp_path
,
615 base::FilePath
* unpack_path
) {
616 DCHECK(temp_path
&& unpack_path
);
618 if (!temp_path
->Initialize(installer_state
.target_path().DirName(),
619 installer::kInstallTempDir
)) {
620 PLOG(ERROR
) << "Could not create temporary path.";
623 VLOG(1) << "Created path " << temp_path
->path().value();
625 if (!base::CreateTemporaryDirInDir(temp_path
->path(),
626 installer::kInstallSourceDir
,
628 PLOG(ERROR
) << "Could not create temporary path for unpacked archive.";
635 installer::InstallStatus
UninstallProduct(
636 const InstallationState
& original_state
,
637 const InstallerState
& installer_state
,
638 const base::FilePath
& setup_exe
,
639 const CommandLine
& cmd_line
,
641 bool force_uninstall
,
642 const Product
& product
) {
643 const ProductState
* product_state
=
644 original_state
.GetProductState(installer_state
.system_install(),
645 product
.distribution()->GetType());
646 if (product_state
!= NULL
) {
647 VLOG(1) << "version on the system: "
648 << product_state
->version().GetString();
649 } else if (!force_uninstall
) {
650 LOG(ERROR
) << product
.distribution()->GetDisplayName()
651 << " not found for uninstall.";
652 return installer::CHROME_NOT_INSTALLED
;
655 return installer::UninstallProduct(
656 original_state
, installer_state
, setup_exe
, product
, remove_all
,
657 force_uninstall
, cmd_line
);
660 installer::InstallStatus
UninstallProducts(
661 const InstallationState
& original_state
,
662 const InstallerState
& installer_state
,
663 const base::FilePath
& setup_exe
,
664 const CommandLine
& cmd_line
) {
665 const Products
& products
= installer_state
.products();
667 // System-level Chrome will be launched via this command if its program gets
669 CommandLine
system_level_cmd(CommandLine::NO_PROGRAM
);
671 const Product
* chrome
=
672 installer_state
.FindProduct(BrowserDistribution::CHROME_BROWSER
);
674 // InstallerState::Initialize always puts Chrome first, and we rely on that
675 // here for this reason: if Chrome is in-use, the user will be prompted to
676 // confirm uninstallation. Upon cancel, we should not continue with the
678 DCHECK(products
[0]->is_chrome());
680 if (cmd_line
.HasSwitch(installer::switches::kSelfDestruct
) &&
681 !installer_state
.system_install()) {
682 BrowserDistribution
* dist
= chrome
->distribution();
683 const base::FilePath
system_exe_path(
684 installer::GetChromeInstallPath(true, dist
)
685 .Append(installer::kChromeExe
));
686 system_level_cmd
.SetProgram(system_exe_path
);
689 if (installer_state
.FindProduct(BrowserDistribution::CHROME_BINARIES
)) {
690 // Chrome Binaries should be last; if something else is cancelled, they
692 DCHECK(products
[products
.size() - 1]->is_chrome_binaries());
695 installer::InstallStatus install_status
= installer::UNINSTALL_SUCCESSFUL
;
696 installer::InstallStatus prod_status
= installer::UNKNOWN_STATUS
;
697 const bool force
= cmd_line
.HasSwitch(installer::switches::kForceUninstall
);
698 const bool remove_all
= !cmd_line
.HasSwitch(
699 installer::switches::kDoNotRemoveSharedItems
);
701 for (Products::const_iterator it
= products
.begin();
702 install_status
!= installer::UNINSTALL_CANCELLED
&& it
< products
.end();
704 prod_status
= UninstallProduct(original_state
, installer_state
, setup_exe
,
705 cmd_line
, remove_all
, force
, **it
);
706 if (prod_status
!= installer::UNINSTALL_SUCCESSFUL
)
707 install_status
= prod_status
;
710 installer::CleanUpInstallationDirectoryAfterUninstall(
711 original_state
, installer_state
, setup_exe
, &install_status
);
713 // The app and vendor dirs may now be empty. Make a last-ditch attempt to
715 installer::DeleteChromeDirectoriesIfEmpty(installer_state
.target_path());
717 // Trigger Active Setup if it was requested for the chrome product. This needs
718 // to be done after the UninstallProduct calls as some of them might
719 // otherwise terminate the process launched by TriggerActiveSetupCommand().
720 if (chrome
&& cmd_line
.HasSwitch(installer::switches::kTriggerActiveSetup
))
721 InstallUtil::TriggerActiveSetupCommand();
723 if (!system_level_cmd
.GetProgram().empty())
724 base::LaunchProcess(system_level_cmd
, base::LaunchOptions(), NULL
);
726 // Tell Google Update that an uninstall has taken place.
727 // Ignore the return value: success or failure of Google Update
728 // has no bearing on the success or failure of Chrome's uninstallation.
729 google_update::UninstallGoogleUpdate(installer_state
.system_install());
731 return install_status
;
734 // Uninstall the binaries if they are the only product present and they're not
736 void UninstallBinariesIfUnused(
737 const InstallationState
& original_state
,
738 const InstallerState
& installer_state
,
739 installer::InstallStatus
* install_status
) {
740 // Early exit if the binaries are still in use.
741 if (*install_status
!= installer::UNUSED_BINARIES
||
742 installer_state
.AreBinariesInUse(original_state
)) {
746 LOG(INFO
) << "Uninstalling unused binaries";
747 installer_state
.UpdateStage(installer::UNINSTALLING_BINARIES
);
749 // Simulate the uninstall as coming from the installed version.
750 const ProductState
* binaries_state
=
751 original_state
.GetProductState(installer_state
.system_install(),
752 BrowserDistribution::CHROME_BINARIES
);
753 const CommandLine
& uninstall_cmd(binaries_state
->uninstall_command());
754 MasterPreferences
uninstall_prefs(uninstall_cmd
);
755 InstallerState uninstall_state
;
756 uninstall_state
.Initialize(uninstall_cmd
, uninstall_prefs
, original_state
);
758 *install_status
= UninstallProducts(original_state
, uninstall_state
,
759 uninstall_cmd
.GetProgram(),
762 // Report that the binaries were uninstalled if they were. This translates
763 // into a successful install return code.
764 if (IsUninstallSuccess(*install_status
)) {
765 *install_status
= installer::UNUSED_BINARIES_UNINSTALLED
;
766 installer_state
.WriteInstallerResult(*install_status
, 0, NULL
);
770 installer::InstallStatus
InstallProducts(
771 const InstallationState
& original_state
,
772 const base::FilePath
& setup_exe
,
773 const CommandLine
& cmd_line
,
774 const MasterPreferences
& prefs
,
775 InstallerState
* installer_state
,
776 base::FilePath
* installer_directory
) {
777 DCHECK(installer_state
);
778 const bool system_install
= installer_state
->system_install();
779 installer::InstallStatus install_status
= installer::UNKNOWN_STATUS
;
780 installer::ArchiveType archive_type
= installer::UNKNOWN_ARCHIVE_TYPE
;
781 bool delegated_to_existing
= false;
782 installer_state
->UpdateStage(installer::PRECONDITIONS
);
783 // The stage provides more fine-grained information than -multifail, so remove
784 // the -multifail suffix from the Google Update "ap" value.
785 BrowserDistribution::GetSpecificDistribution(installer_state
->state_type())->
786 UpdateInstallStatus(system_install
, archive_type
, install_status
);
787 if (CheckPreInstallConditions(original_state
, installer_state
,
789 VLOG(1) << "Installing to " << installer_state
->target_path().value();
790 install_status
= InstallProductsHelper(
791 original_state
, setup_exe
, cmd_line
, prefs
, *installer_state
,
792 installer_directory
, &archive_type
, &delegated_to_existing
);
794 // CheckPreInstallConditions must set the status on failure.
795 DCHECK_NE(install_status
, installer::UNKNOWN_STATUS
);
798 // Delete the master preferences file if present. Note that we do not care
799 // about rollback here and we schedule for deletion on reboot if the delete
800 // fails. As such, we do not use DeleteTreeWorkItem.
801 if (cmd_line
.HasSwitch(installer::switches::kInstallerData
)) {
802 base::FilePath
prefs_path(cmd_line
.GetSwitchValuePath(
803 installer::switches::kInstallerData
));
804 if (!base::DeleteFile(prefs_path
, false)) {
805 LOG(ERROR
) << "Failed deleting master preferences file "
806 << prefs_path
.value()
807 << ", scheduling for deletion after reboot.";
808 ScheduleFileSystemEntityForDeletion(prefs_path
);
812 // Early exit if this setup.exe delegated to another, since that one would
813 // have taken care of UpdateInstallStatus and UpdateStage.
814 if (delegated_to_existing
)
815 return install_status
;
817 const Products
& products
= installer_state
->products();
818 for (Products::const_iterator it
= products
.begin(); it
< products
.end();
820 (*it
)->distribution()->UpdateInstallStatus(
821 system_install
, archive_type
, install_status
);
824 UninstallBinariesIfUnused(original_state
, *installer_state
, &install_status
);
826 installer_state
->UpdateStage(installer::NO_STAGE
);
827 return install_status
;
830 installer::InstallStatus
ShowEULADialog(const base::string16
& inner_frame
) {
831 VLOG(1) << "About to show EULA";
832 base::string16 eula_path
= installer::GetLocalizedEulaResource();
833 if (eula_path
.empty()) {
834 LOG(ERROR
) << "No EULA path available";
835 return installer::EULA_REJECTED
;
837 // Newer versions of the caller pass an inner frame parameter that must
838 // be given to the html page being launched.
839 installer::EulaHTMLDialog
dlg(eula_path
, inner_frame
);
840 installer::EulaHTMLDialog::Outcome outcome
= dlg
.ShowModal();
841 if (installer::EulaHTMLDialog::REJECTED
== outcome
) {
842 LOG(ERROR
) << "EULA rejected or EULA failure";
843 return installer::EULA_REJECTED
;
845 if (installer::EulaHTMLDialog::ACCEPTED_OPT_IN
== outcome
) {
846 VLOG(1) << "EULA accepted (opt-in)";
847 return installer::EULA_ACCEPTED_OPT_IN
;
849 VLOG(1) << "EULA accepted (no opt-in)";
850 return installer::EULA_ACCEPTED
;
853 // Creates the sentinel indicating that the EULA was required and has been
855 bool CreateEULASentinel(BrowserDistribution
* dist
) {
856 base::FilePath eula_sentinel
;
857 if (!InstallUtil::GetSentinelFilePath(installer::kEULASentinelFile
, dist
,
862 return (base::CreateDirectory(eula_sentinel
.DirName()) &&
863 base::WriteFile(eula_sentinel
, "", 0) != -1);
866 void ActivateMetroChrome() {
867 // Check to see if we're per-user or not. Need to do this since we may
868 // not have been invoked with --system-level even for a machine install.
869 wchar_t exe_path
[MAX_PATH
* 2] = {};
870 GetModuleFileName(NULL
, exe_path
, arraysize(exe_path
));
871 bool is_per_user_install
= InstallUtil::IsPerUserInstall(exe_path
);
873 base::string16 app_model_id
= ShellUtil::GetBrowserModelId(
874 BrowserDistribution::GetDistribution(), is_per_user_install
);
876 base::win::ScopedComPtr
<IApplicationActivationManager
> activator
;
877 HRESULT hr
= activator
.CreateInstance(CLSID_ApplicationActivationManager
);
880 hr
= activator
->ActivateApplication(
881 app_model_id
.c_str(), L
"open", AO_NONE
, &pid
);
884 LOG_IF(ERROR
, FAILED(hr
)) << "Tried and failed to launch Metro Chrome. "
885 << "hr=" << std::hex
<< hr
;
888 installer::InstallStatus
RegisterDevChrome(
889 const InstallationState
& original_state
,
890 const InstallerState
& installer_state
,
891 const base::FilePath
& setup_exe
,
892 const CommandLine
& cmd_line
) {
893 BrowserDistribution
* chrome_dist
=
894 BrowserDistribution::GetSpecificDistribution(
895 BrowserDistribution::CHROME_BROWSER
);
897 // Only proceed with registering a dev chrome if no real Chrome installation
898 // of the same distribution are present on this system.
899 const ProductState
* existing_chrome
=
900 original_state
.GetProductState(false,
901 BrowserDistribution::CHROME_BROWSER
);
902 if (!existing_chrome
) {
904 original_state
.GetProductState(true, BrowserDistribution::CHROME_BROWSER
);
906 if (existing_chrome
) {
907 static const wchar_t kPleaseUninstallYourChromeMessage
[] =
908 L
"You already have a full-installation (non-dev) of %1ls, please "
909 L
"uninstall it first using Add/Remove Programs in the control panel.";
910 base::string16
name(chrome_dist
->GetDisplayName());
911 base::string16
message(
912 base::StringPrintf(kPleaseUninstallYourChromeMessage
, name
.c_str()));
914 LOG(ERROR
) << "Aborting operation: another installation of " << name
915 << " was found, as a last resort (if the product is not present "
916 "in Add/Remove Programs), try executing: "
917 << existing_chrome
->uninstall_command().GetCommandLineString();
918 MessageBox(NULL
, message
.c_str(), NULL
, MB_ICONERROR
);
919 return installer::INSTALL_FAILED
;
922 base::FilePath
chrome_exe(
923 cmd_line
.GetSwitchValuePath(installer::switches::kRegisterDevChrome
));
924 if (chrome_exe
.empty())
925 chrome_exe
= setup_exe
.DirName().Append(installer::kChromeExe
);
926 if (!chrome_exe
.IsAbsolute())
927 chrome_exe
= base::MakeAbsoluteFilePath(chrome_exe
);
929 installer::InstallStatus status
= installer::FIRST_INSTALL_SUCCESS
;
930 if (base::PathExists(chrome_exe
)) {
931 Product
chrome(chrome_dist
);
933 // Create the Start menu shortcut and pin it to the taskbar.
934 ShellUtil::ShortcutProperties
shortcut_properties(ShellUtil::CURRENT_USER
);
935 chrome
.AddDefaultShortcutProperties(chrome_exe
, &shortcut_properties
);
936 shortcut_properties
.set_dual_mode(true);
937 shortcut_properties
.set_pin_to_taskbar(true);
938 ShellUtil::CreateOrUpdateShortcut(
939 ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR
, chrome_dist
,
940 shortcut_properties
, ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS
);
942 // Register Chrome at user-level and make it default.
943 scoped_ptr
<WorkItemList
> delegate_execute_list(
944 WorkItem::CreateWorkItemList());
945 installer::AddDelegateExecuteWorkItems(
946 installer_state
, chrome_exe
.DirName(), Version(), chrome
,
947 delegate_execute_list
.get());
948 delegate_execute_list
->Do();
949 if (ShellUtil::CanMakeChromeDefaultUnattended()) {
950 ShellUtil::MakeChromeDefault(
951 chrome_dist
, ShellUtil::CURRENT_USER
, chrome_exe
.value(), true);
953 ShellUtil::ShowMakeChromeDefaultSystemUI(chrome_dist
, chrome_exe
.value());
956 LOG(ERROR
) << "Path not found: " << chrome_exe
.value();
957 status
= installer::INSTALL_FAILED
;
962 // This method processes any command line options that make setup.exe do
963 // various tasks other than installation (renaming chrome.exe, showing eula
964 // among others). This function returns true if any such command line option
965 // has been found and processed (so setup.exe should exit at that point).
966 bool HandleNonInstallCmdLineOptions(const InstallationState
& original_state
,
967 const base::FilePath
& setup_exe
,
968 const CommandLine
& cmd_line
,
969 InstallerState
* installer_state
,
971 // TODO(gab): Add a local |status| variable which each block below sets;
972 // only determine the |exit_code| from |status| at the end (this will allow
973 // this method to validate that
974 // (!handled || status != installer::UNKNOWN_STATUS)).
976 // TODO(tommi): Split these checks up into functions and use a data driven
977 // map of switch->function.
978 if (cmd_line
.HasSwitch(installer::switches::kUpdateSetupExe
)) {
979 installer::InstallStatus status
= installer::SETUP_PATCH_FAILED
;
980 // If --update-setup-exe command line option is given, we apply the given
981 // patch to current exe, and store the resulting binary in the path
982 // specified by --new-setup-exe. But we need to first unpack the file
983 // given in --update-setup-exe.
984 base::ScopedTempDir temp_path
;
985 if (!temp_path
.CreateUniqueTempDir()) {
986 PLOG(ERROR
) << "Could not create temporary path.";
988 base::FilePath
compressed_archive(cmd_line
.GetSwitchValuePath(
989 installer::switches::kUpdateSetupExe
));
990 VLOG(1) << "Opening archive " << compressed_archive
.value();
991 if (installer::ArchivePatchHelper::UncompressAndPatch(
995 cmd_line
.GetSwitchValuePath(installer::switches::kNewSetupExe
))) {
996 status
= installer::NEW_VERSION_UPDATED
;
998 if (!temp_path
.Delete()) {
999 // PLOG would be nice, but Delete() doesn't leave a meaningful value in
1000 // the Windows last-error code.
1001 LOG(WARNING
) << "Scheduling temporary path " << temp_path
.path().value()
1002 << " for deletion at reboot.";
1003 ScheduleDirectoryForDeletion(temp_path
.path());
1007 *exit_code
= InstallUtil::GetInstallReturnCode(status
);
1009 LOG(WARNING
) << "setup.exe patching failed.";
1010 installer_state
->WriteInstallerResult(
1011 status
, IDS_SETUP_PATCH_FAILED_BASE
, NULL
);
1013 // We will be exiting normally, so clear the stage indicator.
1014 installer_state
->UpdateStage(installer::NO_STAGE
);
1015 } else if (cmd_line
.HasSwitch(installer::switches::kShowEula
)) {
1016 // Check if we need to show the EULA. If it is passed as a command line
1017 // then the dialog is shown and regardless of the outcome setup exits here.
1018 base::string16 inner_frame
=
1019 cmd_line
.GetSwitchValueNative(installer::switches::kShowEula
);
1020 *exit_code
= ShowEULADialog(inner_frame
);
1022 if (installer::EULA_REJECTED
!= *exit_code
) {
1023 if (GoogleUpdateSettings::SetEULAConsent(
1024 original_state
, BrowserDistribution::GetDistribution(), true)) {
1025 CreateEULASentinel(BrowserDistribution::GetDistribution());
1027 // For a metro-originated launch, we now need to launch back into metro.
1028 if (cmd_line
.HasSwitch(installer::switches::kShowEulaForMetro
))
1029 ActivateMetroChrome();
1031 } else if (cmd_line
.HasSwitch(installer::switches::kConfigureUserSettings
)) {
1032 // NOTE: Should the work done here, on kConfigureUserSettings, change:
1033 // kActiveSetupVersion in install_worker.cc needs to be increased for Active
1034 // Setup to invoke this again for all users of this install.
1035 const Product
* chrome_install
=
1036 installer_state
->FindProduct(BrowserDistribution::CHROME_BROWSER
);
1037 installer::InstallStatus status
= installer::INVALID_STATE_FOR_OPTION
;
1038 if (chrome_install
&& installer_state
->system_install()) {
1040 cmd_line
.HasSwitch(installer::switches::kForceConfigureUserSettings
);
1041 installer::HandleActiveSetupForBrowser(installer_state
->target_path(),
1042 *chrome_install
, force
);
1043 status
= installer::INSTALL_REPAIRED
;
1045 LOG(DFATAL
) << "chrome_install:" << chrome_install
1046 << ", system_install:" << installer_state
->system_install();
1048 *exit_code
= InstallUtil::GetInstallReturnCode(status
);
1049 } else if (cmd_line
.HasSwitch(installer::switches::kRegisterDevChrome
)) {
1050 installer::InstallStatus status
= RegisterDevChrome(
1051 original_state
, *installer_state
, setup_exe
, cmd_line
);
1052 *exit_code
= InstallUtil::GetInstallReturnCode(status
);
1053 } else if (cmd_line
.HasSwitch(installer::switches::kRegisterChromeBrowser
)) {
1054 installer::InstallStatus status
= installer::UNKNOWN_STATUS
;
1055 const Product
* chrome_install
=
1056 installer_state
->FindProduct(BrowserDistribution::CHROME_BROWSER
);
1057 if (chrome_install
) {
1058 // If --register-chrome-browser option is specified, register all
1059 // Chrome protocol/file associations, as well as register it as a valid
1060 // browser for Start Menu->Internet shortcut. This switch will also
1061 // register Chrome as a valid handler for a set of URL protocols that
1062 // Chrome may become the default handler for, either by the user marking
1063 // Chrome as the default browser, through the Windows Default Programs
1064 // control panel settings, or through website use of
1065 // registerProtocolHandler. These protocols are found in
1066 // ShellUtil::kPotentialProtocolAssociations.
1067 // The --register-url-protocol will additionally register Chrome as a
1068 // potential handler for the supplied protocol, and is used if a website
1069 // registers a handler for a protocol not found in
1070 // ShellUtil::kPotentialProtocolAssociations.
1071 // These options should only be used when setup.exe is launched with admin
1072 // rights. We do not make any user specific changes with this option.
1073 DCHECK(IsUserAnAdmin());
1074 base::string16
chrome_exe(cmd_line
.GetSwitchValueNative(
1075 installer::switches::kRegisterChromeBrowser
));
1076 base::string16 suffix
;
1077 if (cmd_line
.HasSwitch(
1078 installer::switches::kRegisterChromeBrowserSuffix
)) {
1079 suffix
= cmd_line
.GetSwitchValueNative(
1080 installer::switches::kRegisterChromeBrowserSuffix
);
1082 if (cmd_line
.HasSwitch(installer::switches::kRegisterURLProtocol
)) {
1083 base::string16 protocol
= cmd_line
.GetSwitchValueNative(
1084 installer::switches::kRegisterURLProtocol
);
1085 // ShellUtil::RegisterChromeForProtocol performs all registration
1086 // done by ShellUtil::RegisterChromeBrowser, as well as registering
1087 // with Windows as capable of handling the supplied protocol.
1088 if (ShellUtil::RegisterChromeForProtocol(
1089 chrome_install
->distribution(), chrome_exe
, suffix
, protocol
,
1091 status
= installer::IN_USE_UPDATED
;
1093 if (ShellUtil::RegisterChromeBrowser(chrome_install
->distribution(),
1094 chrome_exe
, suffix
, false))
1095 status
= installer::IN_USE_UPDATED
;
1098 LOG(DFATAL
) << "Can't register browser - Chrome distribution not found";
1100 *exit_code
= InstallUtil::GetInstallReturnCode(status
);
1101 } else if (cmd_line
.HasSwitch(installer::switches::kRenameChromeExe
)) {
1102 // If --rename-chrome-exe is specified, we want to rename the executables
1104 *exit_code
= RenameChromeExecutables(original_state
, installer_state
);
1105 } else if (cmd_line
.HasSwitch(
1106 installer::switches::kRemoveChromeRegistration
)) {
1107 // This is almost reverse of --register-chrome-browser option above.
1108 // Here we delete Chrome browser registration. This option should only
1109 // be used when setup.exe is launched with admin rights. We do not
1110 // make any user specific changes in this option.
1111 base::string16 suffix
;
1112 if (cmd_line
.HasSwitch(
1113 installer::switches::kRegisterChromeBrowserSuffix
)) {
1114 suffix
= cmd_line
.GetSwitchValueNative(
1115 installer::switches::kRegisterChromeBrowserSuffix
);
1117 installer::InstallStatus tmp
= installer::UNKNOWN_STATUS
;
1118 const Product
* chrome_install
=
1119 installer_state
->FindProduct(BrowserDistribution::CHROME_BROWSER
);
1120 DCHECK(chrome_install
);
1121 if (chrome_install
) {
1122 installer::DeleteChromeRegistrationKeys(*installer_state
,
1123 chrome_install
->distribution(), HKEY_LOCAL_MACHINE
, suffix
, &tmp
);
1126 } else if (cmd_line
.HasSwitch(installer::switches::kOnOsUpgrade
)) {
1127 const Product
* chrome_install
=
1128 installer_state
->FindProduct(BrowserDistribution::CHROME_BROWSER
);
1129 installer::InstallStatus status
= installer::INVALID_STATE_FOR_OPTION
;
1130 if (chrome_install
) {
1131 installer::HandleOsUpgradeForBrowser(*installer_state
,
1133 status
= installer::INSTALL_REPAIRED
;
1135 LOG(DFATAL
) << "Chrome product not found.";
1137 *exit_code
= InstallUtil::GetInstallReturnCode(status
);
1138 } else if (cmd_line
.HasSwitch(installer::switches::kQueryEULAAcceptance
)) {
1139 *exit_code
= installer::IsEULAAccepted(installer_state
->system_install());
1140 } else if (cmd_line
.HasSwitch(installer::switches::kInactiveUserToast
)) {
1141 // Launch the inactive user toast experiment.
1143 base::StringToInt(cmd_line
.GetSwitchValueNative(
1144 installer::switches::kInactiveUserToast
), &flavor
);
1145 std::string experiment_group
=
1146 cmd_line
.GetSwitchValueASCII(installer::switches::kExperimentGroup
);
1147 DCHECK_NE(-1, flavor
);
1149 *exit_code
= installer::UNKNOWN_STATUS
;
1151 // This code is called (via setup.exe relaunch) only if a product is known
1152 // to run user experiments, so no check is required.
1153 const Products
& products
= installer_state
->products();
1154 for (Products::const_iterator it
= products
.begin(); it
< products
.end();
1156 const Product
& product
= **it
;
1157 installer::InactiveUserToastExperiment(
1158 flavor
, base::ASCIIToUTF16(experiment_group
), product
,
1159 installer_state
->target_path());
1162 } else if (cmd_line
.HasSwitch(installer::switches::kSystemLevelToast
)) {
1163 const Products
& products
= installer_state
->products();
1164 for (Products::const_iterator it
= products
.begin(); it
< products
.end();
1166 const Product
& product
= **it
;
1167 BrowserDistribution
* browser_dist
= product
.distribution();
1168 // We started as system-level and have been re-launched as user level
1169 // to continue with the toast experiment.
1170 Version installed_version
;
1171 InstallUtil::GetChromeVersion(browser_dist
, true, &installed_version
);
1172 if (!installed_version
.IsValid()) {
1173 LOG(ERROR
) << "No installation of "
1174 << browser_dist
->GetDisplayName()
1175 << " found for system-level toast.";
1177 product
.LaunchUserExperiment(
1178 setup_exe
, installer::REENTRY_SYS_UPDATE
, true);
1181 } else if (cmd_line
.HasSwitch(installer::switches::kPatch
)) {
1182 const std::string
patch_type_str(
1183 cmd_line
.GetSwitchValueASCII(installer::switches::kPatch
));
1184 const base::FilePath
input_file(
1185 cmd_line
.GetSwitchValuePath(installer::switches::kInputFile
));
1186 const base::FilePath
patch_file(
1187 cmd_line
.GetSwitchValuePath(installer::switches::kPatchFile
));
1188 const base::FilePath
output_file(
1189 cmd_line
.GetSwitchValuePath(installer::switches::kOutputFile
));
1191 if (patch_type_str
== installer::kCourgette
) {
1192 *exit_code
= installer::CourgettePatchFiles(input_file
,
1195 } else if (patch_type_str
== installer::kBsdiff
) {
1196 *exit_code
= installer::BsdiffPatchFiles(input_file
,
1200 *exit_code
= installer::PATCH_INVALID_ARGUMENTS
;
1202 } else if (cmd_line
.HasSwitch(installer::switches::kReenableAutoupdates
)) {
1203 // setup.exe has been asked to attempt to reenable updates for Chrome.
1204 // Figure out whether we should do so for the multi binaries or the main
1206 BrowserDistribution::Type dist_type
= BrowserDistribution::CHROME_BROWSER
;
1207 if (installer_state
->is_multi_install())
1208 dist_type
= BrowserDistribution::CHROME_BINARIES
;
1210 BrowserDistribution
* dist
=
1211 BrowserDistribution::GetSpecificDistribution(dist_type
);
1212 bool updates_enabled
=
1213 GoogleUpdateSettings::ReenableAutoupdatesForApp(dist
->GetAppGuid());
1214 *exit_code
= updates_enabled
? installer::REENABLE_UPDATES_SUCCEEDED
:
1215 installer::REENABLE_UPDATES_FAILED
;
1223 bool ShowRebootDialog() {
1224 // Get a token for this process.
1226 if (!OpenProcessToken(GetCurrentProcess(),
1227 TOKEN_ADJUST_PRIVILEGES
| TOKEN_QUERY
,
1229 LOG(ERROR
) << "Failed to open token.";
1233 // Use a ScopedHandle to keep track of and eventually close our handle.
1234 // TODO(robertshield): Add a Receive() method to base's ScopedHandle.
1235 base::win::ScopedHandle
scoped_handle(token
);
1237 // Get the LUID for the shutdown privilege.
1238 TOKEN_PRIVILEGES tkp
= {0};
1239 LookupPrivilegeValue(NULL
, SE_SHUTDOWN_NAME
, &tkp
.Privileges
[0].Luid
);
1240 tkp
.PrivilegeCount
= 1;
1241 tkp
.Privileges
[0].Attributes
= SE_PRIVILEGE_ENABLED
;
1243 // Get the shutdown privilege for this process.
1244 AdjustTokenPrivileges(token
, FALSE
, &tkp
, 0,
1245 reinterpret_cast<PTOKEN_PRIVILEGES
>(NULL
), 0);
1246 if (GetLastError() != ERROR_SUCCESS
) {
1247 LOG(ERROR
) << "Unable to get shutdown privileges.";
1251 // Popup a dialog that will prompt to reboot using the default system message.
1252 // TODO(robertshield): Add a localized, more specific string to the prompt.
1253 RestartDialog(NULL
, NULL
, EWX_REBOOT
| EWX_FORCEIFHUNG
);
1257 // Returns the Custom information for the client identified by the exe path
1258 // passed in. This information is used for crash reporting.
1259 google_breakpad::CustomClientInfo
* GetCustomInfo(const wchar_t* exe_path
) {
1260 base::string16 product
;
1261 base::string16 version
;
1262 scoped_ptr
<FileVersionInfo
> version_info(
1263 FileVersionInfo::CreateFileVersionInfo(base::FilePath(exe_path
)));
1264 if (version_info
.get()) {
1265 version
= version_info
->product_version();
1266 product
= version_info
->product_short_name();
1269 if (version
.empty())
1270 version
= L
"0.1.0.0";
1272 if (product
.empty())
1273 product
= L
"Chrome Installer";
1275 static google_breakpad::CustomInfoEntry
ver_entry(L
"ver", version
.c_str());
1276 static google_breakpad::CustomInfoEntry
prod_entry(L
"prod", product
.c_str());
1277 static google_breakpad::CustomInfoEntry
plat_entry(L
"plat", L
"Win32");
1278 static google_breakpad::CustomInfoEntry
type_entry(L
"ptype",
1279 L
"Chrome Installer");
1280 static google_breakpad::CustomInfoEntry entries
[] = {
1281 ver_entry
, prod_entry
, plat_entry
, type_entry
};
1282 static google_breakpad::CustomClientInfo custom_info
= {
1283 entries
, arraysize(entries
) };
1284 return &custom_info
;
1287 // Initialize crash reporting for this process. This involves connecting to
1289 scoped_ptr
<google_breakpad::ExceptionHandler
> InitializeCrashReporting(
1290 bool system_install
) {
1291 // Only report crashes if the user allows it.
1292 if (!GoogleUpdateSettings::GetCollectStatsConsent())
1293 return scoped_ptr
<google_breakpad::ExceptionHandler
>();
1295 // Get the alternate dump directory. We use the temp path.
1296 base::FilePath temp_directory
;
1297 if (!base::GetTempDir(&temp_directory
) || temp_directory
.empty())
1298 return scoped_ptr
<google_breakpad::ExceptionHandler
>();
1300 wchar_t exe_path
[MAX_PATH
* 2] = {0};
1301 GetModuleFileName(NULL
, exe_path
, arraysize(exe_path
));
1303 // Build the pipe name. It can be either:
1304 // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18"
1305 // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>"
1306 base::string16 user_sid
= kSystemPrincipalSid
;
1308 if (!system_install
) {
1309 if (!base::win::GetUserSidString(&user_sid
)) {
1310 return scoped_ptr
<google_breakpad::ExceptionHandler
>();
1314 base::string16 pipe_name
= kGoogleUpdatePipeName
;
1315 pipe_name
+= user_sid
;
1317 return scoped_ptr
<google_breakpad::ExceptionHandler
>(
1318 new google_breakpad::ExceptionHandler(
1319 temp_directory
.value(), NULL
, NULL
, NULL
,
1320 google_breakpad::ExceptionHandler::HANDLER_ALL
, kLargerDumpType
,
1321 pipe_name
.c_str(), GetCustomInfo(exe_path
)));
1324 // Uninstalls multi-install Chrome Frame if the current operation is a
1325 // multi-install install or update. The operation is performed directly rather
1326 // than delegated to the existing install since there is no facility in older
1327 // versions of setup.exe to uninstall GCF without touching the binaries. The
1328 // binaries will be uninstalled during later processing if they are not in-use
1329 // (see UninstallBinariesIfUnused). |original_state| and |installer_state| are
1330 // updated to reflect the state of the world following the operation.
1331 void UninstallMultiChromeFrameIfPresent(const CommandLine
& cmd_line
,
1332 const MasterPreferences
& prefs
,
1333 InstallationState
* original_state
,
1334 InstallerState
* installer_state
) {
1335 // Early exit if not installing or updating multi-install product(s).
1336 if (installer_state
->operation() != InstallerState::MULTI_INSTALL
&&
1337 installer_state
->operation() != InstallerState::MULTI_UPDATE
) {
1341 // Early exit if Chrome Frame is not present as multi-install.
1342 const ProductState
* chrome_frame_state
=
1343 original_state
->GetProductState(installer_state
->system_install(),
1344 BrowserDistribution::CHROME_FRAME
);
1345 if (!chrome_frame_state
|| !chrome_frame_state
->is_multi_install())
1348 LOG(INFO
) << "Uninstalling multi-install Chrome Frame.";
1349 installer_state
->UpdateStage(installer::UNINSTALLING_CHROME_FRAME
);
1351 // Uninstall Chrome Frame without touching the multi-install binaries.
1352 // Simulate the uninstall as coming from the installed version.
1353 const CommandLine
& uninstall_cmd(chrome_frame_state
->uninstall_command());
1354 MasterPreferences
uninstall_prefs(uninstall_cmd
);
1355 InstallerState uninstall_state
;
1356 uninstall_state
.Initialize(uninstall_cmd
, uninstall_prefs
, *original_state
);
1357 const Product
* chrome_frame_product
= uninstall_state
.FindProduct(
1358 BrowserDistribution::CHROME_FRAME
);
1359 if (chrome_frame_product
) {
1360 // No shared state should be left behind.
1361 const bool remove_all
= true;
1362 // Don't accept no for an answer.
1363 const bool force_uninstall
= true;
1364 installer::InstallStatus uninstall_status
=
1365 installer::UninstallProduct(*original_state
, uninstall_state
,
1366 uninstall_cmd
.GetProgram(),
1367 *chrome_frame_product
, remove_all
,
1368 force_uninstall
, cmd_line
);
1370 VLOG(1) << "Uninstallation of Chrome Frame returned status "
1371 << uninstall_status
;
1373 LOG(ERROR
) << "Chrome Frame not found for uninstall.";
1376 // Refresh state for the continuation of the original install/update.
1377 original_state
->Initialize();
1378 installer_state
->Initialize(cmd_line
, prefs
, *original_state
);
1383 namespace installer
{
1385 InstallStatus
InstallProductsHelper(
1386 const InstallationState
& original_state
,
1387 const base::FilePath
& setup_exe
,
1388 const CommandLine
& cmd_line
,
1389 const MasterPreferences
& prefs
,
1390 const InstallerState
& installer_state
,
1391 base::FilePath
* installer_directory
,
1392 ArchiveType
* archive_type
,
1393 bool* delegated_to_existing
) {
1394 DCHECK(archive_type
);
1395 DCHECK(delegated_to_existing
);
1396 const bool system_install
= installer_state
.system_install();
1397 InstallStatus install_status
= UNKNOWN_STATUS
;
1399 // Drop to background processing mode if the process was started below the
1400 // normal process priority class.
1401 bool entered_background_mode
= AdjustProcessPriority();
1402 VLOG_IF(1, entered_background_mode
) << "Entered background processing mode.";
1404 // Create a temp folder where we will unpack Chrome archive. If it fails,
1405 // then we are doomed, so return immediately and no cleanup is required.
1406 SelfCleaningTempDir temp_path
;
1407 base::FilePath unpack_path
;
1408 if (!CreateTemporaryAndUnpackDirectories(installer_state
, &temp_path
,
1410 installer_state
.WriteInstallerResult(TEMP_DIR_FAILED
,
1411 IDS_INSTALL_TEMP_DIR_FAILED_BASE
,
1413 return TEMP_DIR_FAILED
;
1416 // Uncompress and optionally patch the archive if an uncompressed archive was
1417 // not specified on the command line and a compressed archive is found.
1418 *archive_type
= UNKNOWN_ARCHIVE_TYPE
;
1419 base::FilePath
uncompressed_archive(cmd_line
.GetSwitchValuePath(
1420 switches::kUncompressedArchive
));
1421 if (uncompressed_archive
.empty()) {
1422 scoped_ptr
<ArchivePatchHelper
> archive_helper(
1423 CreateChromeArchiveHelper(setup_exe
, cmd_line
, installer_state
,
1425 if (archive_helper
) {
1426 VLOG(1) << "Installing Chrome from compressed archive "
1427 << archive_helper
->compressed_archive().value();
1428 if (!UncompressAndPatchChromeArchive(original_state
,
1430 archive_helper
.get(),
1433 DCHECK_NE(install_status
, UNKNOWN_STATUS
);
1434 return install_status
;
1436 uncompressed_archive
= archive_helper
->target();
1437 DCHECK(!uncompressed_archive
.empty());
1441 // Check for an uncompressed archive alongside the current executable if one
1442 // was not given or generated.
1443 if (uncompressed_archive
.empty())
1444 uncompressed_archive
= setup_exe
.DirName().Append(kChromeArchive
);
1446 if (*archive_type
== UNKNOWN_ARCHIVE_TYPE
) {
1447 // An archive was not uncompressed or patched above.
1448 if (uncompressed_archive
.empty() ||
1449 !base::PathExists(uncompressed_archive
)) {
1450 LOG(ERROR
) << "Cannot install Chrome without an uncompressed archive.";
1451 installer_state
.WriteInstallerResult(
1452 INVALID_ARCHIVE
, IDS_INSTALL_INVALID_ARCHIVE_BASE
, NULL
);
1453 return INVALID_ARCHIVE
;
1455 *archive_type
= FULL_ARCHIVE_TYPE
;
1458 // Unpack the uncompressed archive.
1459 if (LzmaUtil::UnPackArchive(uncompressed_archive
.value(),
1460 unpack_path
.value(),
1462 installer_state
.WriteInstallerResult(
1463 UNCOMPRESSION_FAILED
,
1464 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE
,
1466 return UNCOMPRESSION_FAILED
;
1469 VLOG(1) << "unpacked to " << unpack_path
.value();
1470 base::FilePath
src_path(
1471 unpack_path
.Append(kInstallSourceChromeDir
));
1473 installer_version(GetMaxVersionFromArchiveDir(src_path
));
1474 if (!installer_version
.get()) {
1475 LOG(ERROR
) << "Did not find any valid version in installer.";
1476 install_status
= INVALID_ARCHIVE
;
1477 installer_state
.WriteInstallerResult(install_status
,
1478 IDS_INSTALL_INVALID_ARCHIVE_BASE
, NULL
);
1480 VLOG(1) << "version to install: " << installer_version
->GetString();
1481 bool proceed_with_installation
= true;
1483 if (installer_state
.operation() == InstallerState::MULTI_INSTALL
) {
1484 // This is a new install of a multi-install product. Rather than give up
1485 // in case a higher version of the binaries (including a single-install
1486 // of Chrome, which can safely be migrated to multi-install by way of
1487 // CheckMultiInstallConditions) is already installed, delegate to the
1488 // installed setup.exe to install the product at hand.
1489 base::FilePath existing_setup_exe
;
1490 if (GetExistingHigherInstaller(original_state
, system_install
,
1491 *installer_version
, &existing_setup_exe
)) {
1492 VLOG(1) << "Deferring to existing installer.";
1493 installer_state
.UpdateStage(DEFERRING_TO_HIGHER_VERSION
);
1494 if (DeferToExistingInstall(existing_setup_exe
, cmd_line
,
1495 installer_state
, temp_path
.path(),
1497 *delegated_to_existing
= true;
1498 return install_status
;
1503 uint32 higher_products
= 0;
1505 sizeof(higher_products
) * 8 > BrowserDistribution::NUM_TYPES
,
1506 too_many_distribution_types_
);
1507 const Products
& products
= installer_state
.products();
1508 for (Products::const_iterator it
= products
.begin(); it
< products
.end();
1510 const Product
& product
= **it
;
1511 const ProductState
* product_state
=
1512 original_state
.GetProductState(system_install
,
1513 product
.distribution()->GetType());
1514 if (product_state
!= NULL
&&
1515 (product_state
->version().CompareTo(*installer_version
) > 0)) {
1516 LOG(ERROR
) << "Higher version of "
1517 << product
.distribution()->GetDisplayName()
1518 << " is already installed.";
1519 higher_products
|= (1 << product
.distribution()->GetType());
1523 if (higher_products
!= 0) {
1524 COMPILE_ASSERT(BrowserDistribution::NUM_TYPES
== 4,
1525 add_support_for_new_products_here_
);
1526 const uint32 kBrowserBit
= 1 << BrowserDistribution::CHROME_BROWSER
;
1527 const uint32 kAppHostBit
= 1 << BrowserDistribution::CHROME_APP_HOST
;
1530 proceed_with_installation
= false;
1531 install_status
= HIGHER_VERSION_EXISTS
;
1532 switch (higher_products
) {
1534 message_id
= IDS_INSTALL_HIGHER_VERSION_BASE
;
1537 message_id
= IDS_INSTALL_HIGHER_VERSION_APP_LAUNCHER_BASE
;
1541 installer_state
.WriteInstallerResult(install_status
, message_id
, NULL
);
1544 proceed_with_installation
=
1545 proceed_with_installation
&&
1546 CheckGroupPolicySettings(original_state
, installer_state
,
1547 *installer_version
, &install_status
);
1549 if (proceed_with_installation
) {
1550 // If Google Update is absent at user-level, install it using the
1551 // Google Update installer from an existing system-level installation.
1552 // This is for quick-enable App Host install from a system-level
1553 // Chrome Binaries installation.
1554 if (!system_install
&& installer_state
.ensure_google_update_present()) {
1555 if (!google_update::EnsureUserLevelGoogleUpdatePresent()) {
1556 LOG(ERROR
) << "Failed to install Google Update";
1557 proceed_with_installation
= false;
1558 install_status
= INSTALL_OF_GOOGLE_UPDATE_FAILED
;
1559 installer_state
.WriteInstallerResult(install_status
, 0, NULL
);
1565 if (proceed_with_installation
) {
1566 base::FilePath
prefs_source_path(cmd_line
.GetSwitchValueNative(
1567 switches::kInstallerData
));
1568 install_status
= InstallOrUpdateProduct(
1569 original_state
, installer_state
, setup_exe
, uncompressed_archive
,
1570 temp_path
.path(), src_path
, prefs_source_path
, prefs
,
1571 *installer_version
);
1573 int install_msg_base
= IDS_INSTALL_FAILED_BASE
;
1574 base::string16 chrome_exe
;
1575 base::string16 quoted_chrome_exe
;
1576 if (install_status
== SAME_VERSION_REPAIR_FAILED
) {
1577 install_msg_base
= IDS_SAME_VERSION_REPAIR_FAILED_BASE
;
1578 } else if (install_status
!= INSTALL_FAILED
) {
1579 if (installer_state
.target_path().empty()) {
1580 // If we failed to construct install path, it means the OS call to
1581 // get %ProgramFiles% or %AppData% failed. Report this as failure.
1582 install_msg_base
= IDS_INSTALL_OS_ERROR_BASE
;
1583 install_status
= OS_ERROR
;
1585 chrome_exe
= installer_state
.target_path().Append(kChromeExe
).value();
1586 quoted_chrome_exe
= L
"\"" + chrome_exe
+ L
"\"";
1587 install_msg_base
= 0;
1591 installer_state
.UpdateStage(FINISHING
);
1593 // Only do Chrome-specific stuff (like launching the browser) if
1594 // Chrome was specifically requested (rather than being upgraded as
1595 // part of a multi-install).
1596 const Product
* chrome_install
= prefs
.install_chrome() ?
1597 installer_state
.FindProduct(BrowserDistribution::CHROME_BROWSER
) :
1600 bool do_not_register_for_update_launch
= false;
1601 if (chrome_install
) {
1602 prefs
.GetBool(master_preferences::kDoNotRegisterForUpdateLaunch
,
1603 &do_not_register_for_update_launch
);
1605 do_not_register_for_update_launch
= true; // Never register.
1608 bool write_chrome_launch_string
=
1609 (!do_not_register_for_update_launch
&&
1610 install_status
!= IN_USE_UPDATED
);
1612 installer_state
.WriteInstallerResult(install_status
, install_msg_base
,
1613 write_chrome_launch_string
? "ed_chrome_exe
: NULL
);
1615 if (install_status
== FIRST_INSTALL_SUCCESS
) {
1616 VLOG(1) << "First install successful.";
1617 if (chrome_install
) {
1618 // We never want to launch Chrome in system level install mode.
1619 bool do_not_launch_chrome
= false;
1620 prefs
.GetBool(master_preferences::kDoNotLaunchChrome
,
1621 &do_not_launch_chrome
);
1622 if (!system_install
&& !do_not_launch_chrome
)
1623 chrome_install
->LaunchChrome(installer_state
.target_path());
1625 } else if ((install_status
== NEW_VERSION_UPDATED
) ||
1626 (install_status
== IN_USE_UPDATED
)) {
1627 const Product
* chrome
= installer_state
.FindProduct(
1628 BrowserDistribution::CHROME_BROWSER
);
1629 if (chrome
!= NULL
) {
1630 DCHECK_NE(chrome_exe
, base::string16());
1631 RemoveChromeLegacyRegistryKeys(chrome
->distribution(), chrome_exe
);
1635 if (prefs
.install_chrome_app_launcher() &&
1636 InstallUtil::GetInstallReturnCode(install_status
) == 0) {
1637 std::string
webstore_item(google_update::GetUntrustedDataValue(
1638 kInstallFromWebstore
));
1639 if (!webstore_item
.empty()) {
1640 bool success
= InstallFromWebstore(webstore_item
);
1641 VLOG_IF(1, !success
) << "Failed to launch app installation.";
1647 // There might be an experiment (for upgrade usually) that needs to happen.
1648 // An experiment's outcome can include chrome's uninstallation. If that is
1649 // the case we would not do that directly at this point but in another
1650 // instance of setup.exe
1652 // There is another way to reach this same function if this is a system
1653 // level install. See HandleNonInstallCmdLineOptions().
1655 // If installation failed, use the path to the currently running setup.
1656 // If installation succeeded, use the path to setup in the installer dir.
1657 base::FilePath
setup_path(setup_exe
);
1658 if (InstallUtil::GetInstallReturnCode(install_status
) == 0) {
1659 setup_path
= installer_state
.GetInstallerDirectory(*installer_version
)
1660 .Append(setup_path
.BaseName());
1662 const Products
& products
= installer_state
.products();
1663 for (Products::const_iterator it
= products
.begin(); it
< products
.end();
1665 const Product
& product
= **it
;
1666 product
.LaunchUserExperiment(setup_path
, install_status
, system_install
);
1670 // If installation completed successfully, return the path to the directory
1671 // containing the newly installed setup.exe and uncompressed archive if the
1672 // caller requested it.
1673 if (installer_directory
&&
1674 InstallUtil::GetInstallReturnCode(install_status
) == 0) {
1675 *installer_directory
=
1676 installer_state
.GetInstallerDirectory(*installer_version
);
1679 // temp_path's dtor will take care of deleting or scheduling itself for
1680 // deletion at reboot when this scope closes.
1681 VLOG(1) << "Deleting temporary directory " << temp_path
.path().value();
1683 return install_status
;
1686 } // namespace installer
1688 int WINAPI
wWinMain(HINSTANCE instance
, HINSTANCE prev_instance
,
1689 wchar_t* command_line
, int show_command
) {
1690 // Check to see if the CPU is supported before doing anything else. There's
1691 // very little than can safely be accomplished if the CPU isn't supported
1692 // since dependent libraries (e.g., base) may use invalid instructions.
1693 if (!installer::IsProcessorSupported())
1694 return installer::CPU_NOT_SUPPORTED
;
1696 // The exit manager is in charge of calling the dtors of singletons.
1697 base::AtExitManager exit_manager
;
1698 CommandLine::Init(0, NULL
);
1700 const MasterPreferences
& prefs
= MasterPreferences::ForCurrentProcess();
1701 installer::InitInstallerLogging(prefs
);
1703 const CommandLine
& cmd_line
= *CommandLine::ForCurrentProcess();
1704 VLOG(1) << "Command Line: " << cmd_line
.GetCommandLineString();
1706 VLOG(1) << "multi install is " << prefs
.is_multi_install();
1707 bool system_install
= false;
1708 prefs
.GetBool(installer::master_preferences::kSystemLevel
, &system_install
);
1709 VLOG(1) << "system install is " << system_install
;
1711 scoped_ptr
<google_breakpad::ExceptionHandler
> breakpad(
1712 InitializeCrashReporting(system_install
));
1714 InstallationState original_state
;
1715 original_state
.Initialize();
1717 InstallerState installer_state
;
1718 installer_state
.Initialize(cmd_line
, prefs
, original_state
);
1719 const bool is_uninstall
= cmd_line
.HasSwitch(installer::switches::kUninstall
);
1721 // Check to make sure current system is WinXP or later. If not, log
1722 // error message and get out.
1723 if (!InstallUtil::IsOSSupported()) {
1724 LOG(ERROR
) << "Chrome only supports Windows XP or later.";
1725 installer_state
.WriteInstallerResult(
1726 installer::OS_NOT_SUPPORTED
, IDS_INSTALL_OS_NOT_SUPPORTED_BASE
, NULL
);
1727 return installer::OS_NOT_SUPPORTED
;
1730 // Initialize COM for use later.
1731 base::win::ScopedCOMInitializer com_initializer
;
1732 if (!com_initializer
.succeeded()) {
1733 installer_state
.WriteInstallerResult(
1734 installer::OS_ERROR
, IDS_INSTALL_OS_ERROR_BASE
, NULL
);
1735 return installer::OS_ERROR
;
1738 // Some command line options don't work with SxS install/uninstall
1739 if (InstallUtil::IsChromeSxSProcess()) {
1740 if (system_install
||
1741 prefs
.is_multi_install() ||
1742 cmd_line
.HasSwitch(installer::switches::kForceUninstall
) ||
1743 cmd_line
.HasSwitch(installer::switches::kMakeChromeDefault
) ||
1744 cmd_line
.HasSwitch(installer::switches::kRegisterChromeBrowser
) ||
1745 cmd_line
.HasSwitch(installer::switches::kRemoveChromeRegistration
) ||
1746 cmd_line
.HasSwitch(installer::switches::kInactiveUserToast
) ||
1747 cmd_line
.HasSwitch(installer::switches::kSystemLevelToast
)) {
1748 return installer::SXS_OPTION_NOT_SUPPORTED
;
1752 // Some command line options are no longer supported and must error out.
1753 if (installer::ContainsUnsupportedSwitch(cmd_line
))
1754 return installer::UNSUPPORTED_OPTION
;
1756 // A variety of installer operations require the path to the current
1757 // executable. Get it once here for use throughout these operations. Note that
1758 // the path service is the authoritative source for this path. One might think
1759 // that CommandLine::GetProgram would suffice, but it won't since
1760 // CreateProcess may have been called with a command line that is somewhat
1761 // ambiguous (e.g., an unquoted path with spaces, or a path lacking the file
1762 // extension), in which case CommandLineToArgv will not yield an argv with the
1763 // true path to the program at position 0.
1764 base::FilePath setup_exe
;
1765 if (!PathService::Get(base::FILE_EXE
, &setup_exe
)) {
1767 return installer::OS_ERROR
;
1771 if (HandleNonInstallCmdLineOptions(
1772 original_state
, setup_exe
, cmd_line
, &installer_state
, &exit_code
)) {
1776 if (system_install
&& !IsUserAnAdmin()) {
1777 if (base::win::GetVersion() >= base::win::VERSION_VISTA
&&
1778 !cmd_line
.HasSwitch(installer::switches::kRunAsAdmin
)) {
1779 CommandLine
new_cmd(CommandLine::NO_PROGRAM
);
1780 new_cmd
.AppendArguments(cmd_line
, true);
1781 // Append --run-as-admin flag to let the new instance of setup.exe know
1782 // that we already tried to launch ourselves as admin.
1783 new_cmd
.AppendSwitch(installer::switches::kRunAsAdmin
);
1784 // If system_install became true due to an environment variable, append
1785 // it to the command line here since env vars may not propagate past the
1787 if (!new_cmd
.HasSwitch(installer::switches::kSystemLevel
))
1788 new_cmd
.AppendSwitch(installer::switches::kSystemLevel
);
1790 DWORD exit_code
= installer::UNKNOWN_STATUS
;
1791 InstallUtil::ExecuteExeAsAdmin(new_cmd
, &exit_code
);
1794 LOG(ERROR
) << "Non admin user can not install system level Chrome.";
1795 installer_state
.WriteInstallerResult(installer::INSUFFICIENT_RIGHTS
,
1796 IDS_INSTALL_INSUFFICIENT_RIGHTS_BASE
, NULL
);
1797 return installer::INSUFFICIENT_RIGHTS
;
1801 UninstallMultiChromeFrameIfPresent(cmd_line
, prefs
,
1802 &original_state
, &installer_state
);
1804 base::FilePath installer_directory
;
1805 installer::InstallStatus install_status
= installer::UNKNOWN_STATUS
;
1806 // If --uninstall option is given, uninstall the identified product(s)
1809 UninstallProducts(original_state
, installer_state
, setup_exe
, cmd_line
);
1811 // If --uninstall option is not specified, we assume it is install case.
1813 InstallProducts(original_state
, setup_exe
, cmd_line
, prefs
,
1814 &installer_state
, &installer_directory
);
1817 // Validate that the machine is now in a good state following the operation.
1818 // TODO(grt): change this to log at DFATAL once we're convinced that the
1819 // validator handles all cases properly.
1820 InstallationValidator::InstallationType installation_type
=
1821 InstallationValidator::NO_PRODUCTS
;
1823 !InstallationValidator::ValidateInstallationType(system_install
,
1824 &installation_type
));
1826 int return_code
= 0;
1827 // MSI demands that custom actions always return 0 (ERROR_SUCCESS) or it will
1828 // rollback the action. If we're uninstalling we want to avoid this, so always
1829 // report success, squashing any more informative return codes.
1830 if (!(installer_state
.is_msi() && is_uninstall
)) {
1831 // Note that we allow the status installer::UNINSTALL_REQUIRES_REBOOT
1832 // to pass through, since this is only returned on uninstall which is
1833 // never invoked directly by Google Update.
1834 return_code
= InstallUtil::GetInstallReturnCode(install_status
);
1837 VLOG(1) << "Installation complete, returning: " << return_code
;