Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / chrome / installer / setup / setup_main.cc
blob4cd22563852759592418e1141ce5a5e38833cfc7
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/installer/setup/setup_main.h"
7 #include <windows.h>
8 #include <msi.h>
9 #include <shellapi.h>
10 #include <shlobj.h>
12 #include <string>
14 #include "base/at_exit.h"
15 #include "base/basictypes.h"
16 #include "base/command_line.h"
17 #include "base/file_version_info.h"
18 #include "base/files/file_path.h"
19 #include "base/files/file_util.h"
20 #include "base/files/scoped_temp_dir.h"
21 #include "base/memory/scoped_ptr.h"
22 #include "base/path_service.h"
23 #include "base/process/launch.h"
24 #include "base/strings/string16.h"
25 #include "base/strings/string_number_conversions.h"
26 #include "base/strings/string_util.h"
27 #include "base/strings/stringprintf.h"
28 #include "base/strings/utf_string_conversions.h"
29 #include "base/values.h"
30 #include "base/win/registry.h"
31 #include "base/win/scoped_com_initializer.h"
32 #include "base/win/scoped_comptr.h"
33 #include "base/win/scoped_handle.h"
34 #include "base/win/win_util.h"
35 #include "base/win/windows_version.h"
36 #include "breakpad/src/client/windows/handler/exception_handler.h"
37 #include "chrome/common/chrome_constants.h"
38 #include "chrome/common/chrome_paths.h"
39 #include "chrome/common/chrome_switches.h"
40 #include "chrome/installer/setup/archive_patch_helper.h"
41 #include "chrome/installer/setup/install.h"
42 #include "chrome/installer/setup/install_worker.h"
43 #include "chrome/installer/setup/setup_constants.h"
44 #include "chrome/installer/setup/setup_util.h"
45 #include "chrome/installer/setup/uninstall.h"
46 #include "chrome/installer/util/browser_distribution.h"
47 #include "chrome/installer/util/channel_info.h"
48 #include "chrome/installer/util/delete_after_reboot_helper.h"
49 #include "chrome/installer/util/delete_tree_work_item.h"
50 #include "chrome/installer/util/eula_util.h"
51 #include "chrome/installer/util/google_update_constants.h"
52 #include "chrome/installer/util/google_update_settings.h"
53 #include "chrome/installer/util/google_update_util.h"
54 #include "chrome/installer/util/helper.h"
55 #include "chrome/installer/util/html_dialog.h"
56 #include "chrome/installer/util/install_util.h"
57 #include "chrome/installer/util/installation_state.h"
58 #include "chrome/installer/util/installation_validator.h"
59 #include "chrome/installer/util/installer_state.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 #include "installer_util_strings.h" // NOLINT
71 using installer::InstallerState;
72 using installer::InstallationState;
73 using installer::InstallationValidator;
74 using installer::MasterPreferences;
75 using installer::Product;
76 using installer::ProductState;
77 using installer::Products;
79 const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices";
80 const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\";
81 const wchar_t kSystemPrincipalSid[] = L"S-1-5-18";
83 const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
84 MiniDumpWithProcessThreadData | // Get PEB and TEB.
85 MiniDumpWithUnloadedModules | // Get unloaded modules when available.
86 MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack.
88 namespace {
90 // Returns NULL if no compressed archive is available for processing, otherwise
91 // returns a patch helper configured to uncompress and patch.
92 scoped_ptr<installer::ArchivePatchHelper> CreateChromeArchiveHelper(
93 const base::FilePath& setup_exe,
94 const CommandLine& command_line,
95 const installer::InstallerState& installer_state,
96 const base::FilePath& working_directory) {
97 // A compressed archive is ordinarily given on the command line by the mini
98 // installer. If one was not given, look for chrome.packed.7z next to the
99 // running program.
100 base::FilePath compressed_archive(
101 command_line.GetSwitchValuePath(installer::switches::kInstallArchive));
102 bool compressed_archive_specified = !compressed_archive.empty();
103 if (!compressed_archive_specified) {
104 compressed_archive = setup_exe.DirName().Append(
105 installer::kChromeCompressedArchive);
108 // Fail if no compressed archive is found.
109 if (!base::PathExists(compressed_archive)) {
110 if (compressed_archive_specified) {
111 LOG(ERROR) << installer::switches::kInstallArchive << "="
112 << compressed_archive.value() << " not found.";
114 return scoped_ptr<installer::ArchivePatchHelper>();
117 // chrome.7z is either extracted directly from the compressed archive into the
118 // working dir or is the target of patching in the working dir.
119 base::FilePath target(working_directory.Append(installer::kChromeArchive));
120 DCHECK(!base::PathExists(target));
122 // Specify an empty path for the patch source since it isn't yet known that
123 // one is needed. It will be supplied in UncompressAndPatchChromeArchive if it
124 // is.
125 return scoped_ptr<installer::ArchivePatchHelper>(
126 new installer::ArchivePatchHelper(working_directory,
127 compressed_archive,
128 base::FilePath(),
129 target));
132 // Workhorse for producing an uncompressed archive (chrome.7z) given a
133 // chrome.packed.7z containing either a patch file based on the version of
134 // chrome being updated or the full uncompressed archive. Returns true on
135 // success, in which case |archive_type| is populated based on what was found.
136 // Returns false on failure, in which case |install_status| contains the error
137 // code and the result is written to the registry (via WriteInstallerResult).
138 bool UncompressAndPatchChromeArchive(
139 const installer::InstallationState& original_state,
140 const installer::InstallerState& installer_state,
141 installer::ArchivePatchHelper* archive_helper,
142 installer::ArchiveType* archive_type,
143 installer::InstallStatus* install_status) {
144 installer_state.UpdateStage(installer::UNCOMPRESSING);
145 if (!archive_helper->Uncompress(NULL)) {
146 *install_status = installer::UNCOMPRESSION_FAILED;
147 installer_state.WriteInstallerResult(*install_status,
148 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
149 NULL);
150 return false;
153 // Short-circuit if uncompression produced the uncompressed archive rather
154 // than a patch file.
155 if (base::PathExists(archive_helper->target())) {
156 *archive_type = installer::FULL_ARCHIVE_TYPE;
157 return true;
160 // Find the installed version's archive to serve as the source for patching.
161 base::FilePath patch_source(installer::FindArchiveToPatch(original_state,
162 installer_state));
163 if (patch_source.empty()) {
164 LOG(ERROR) << "Failed to find archive to patch.";
165 *install_status = installer::DIFF_PATCH_SOURCE_MISSING;
166 installer_state.WriteInstallerResult(*install_status,
167 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
168 NULL);
169 return false;
171 archive_helper->set_patch_source(patch_source);
173 // Try courgette first. Failing that, try bspatch.
174 if ((installer_state.UpdateStage(installer::ENSEMBLE_PATCHING),
175 !archive_helper->EnsemblePatch()) &&
176 (installer_state.UpdateStage(installer::BINARY_PATCHING),
177 !archive_helper->BinaryPatch())) {
178 *install_status = installer::APPLY_DIFF_PATCH_FAILED;
179 installer_state.WriteInstallerResult(*install_status,
180 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
181 NULL);
182 return false;
185 *archive_type = installer::INCREMENTAL_ARCHIVE_TYPE;
186 return true;
189 // In multi-install, adds all products to |installer_state| that are
190 // multi-installed and must be updated along with the products already present
191 // in |installer_state|.
192 void AddExistingMultiInstalls(const InstallationState& original_state,
193 InstallerState* installer_state) {
194 if (installer_state->is_multi_install()) {
195 for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
196 BrowserDistribution::Type type =
197 static_cast<BrowserDistribution::Type>(i);
199 if (!installer_state->FindProduct(type)) {
200 const ProductState* state =
201 original_state.GetProductState(installer_state->system_install(),
202 type);
203 if ((state != NULL) && state->is_multi_install()) {
204 installer_state->AddProductFromState(type, *state);
205 VLOG(1) << "Product already installed and must be included: "
206 << BrowserDistribution::GetSpecificDistribution(type)->
207 GetDisplayName();
214 // This function is called when --rename-chrome-exe option is specified on
215 // setup.exe command line. This function assumes an in-use update has happened
216 // for Chrome so there should be a file called new_chrome.exe on the file
217 // system and a key called 'opv' in the registry. This function will move
218 // new_chrome.exe to chrome.exe and delete 'opv' key in one atomic operation.
219 // This function also deletes elevation policies associated with the old version
220 // if they exist.
221 installer::InstallStatus RenameChromeExecutables(
222 const InstallationState& original_state,
223 InstallerState* installer_state) {
224 // See what products are already installed in multi mode. When we do the
225 // rename for multi installs, we must update all installations since they
226 // share the binaries.
227 AddExistingMultiInstalls(original_state, installer_state);
228 const base::FilePath &target_path = installer_state->target_path();
229 base::FilePath chrome_exe(target_path.Append(installer::kChromeExe));
230 base::FilePath chrome_new_exe(target_path.Append(installer::kChromeNewExe));
231 base::FilePath chrome_old_exe(target_path.Append(installer::kChromeOldExe));
233 // Create a temporary backup directory on the same volume as chrome.exe so
234 // that moving in-use files doesn't lead to trouble.
235 installer::SelfCleaningTempDir temp_path;
236 if (!temp_path.Initialize(target_path.DirName(),
237 installer::kInstallTempDir)) {
238 PLOG(ERROR) << "Failed to create Temp directory "
239 << target_path.DirName()
240 .Append(installer::kInstallTempDir).value();
241 return installer::RENAME_FAILED;
243 scoped_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList());
244 // Move chrome.exe to old_chrome.exe, then move new_chrome.exe to chrome.exe.
245 install_list->AddMoveTreeWorkItem(chrome_exe.value(),
246 chrome_old_exe.value(),
247 temp_path.path().value(),
248 WorkItem::ALWAYS_MOVE);
249 install_list->AddMoveTreeWorkItem(chrome_new_exe.value(),
250 chrome_exe.value(),
251 temp_path.path().value(),
252 WorkItem::ALWAYS_MOVE);
253 install_list->AddDeleteTreeWorkItem(chrome_new_exe, temp_path.path());
254 // old_chrome.exe is still in use in most cases, so ignore failures here.
255 install_list->AddDeleteTreeWorkItem(chrome_old_exe, temp_path.path())->
256 set_ignore_failure(true);
258 // Add work items to delete the "opv", "cpv", and "cmd" values from all
259 // products we're operating on (which including the multi-install binaries).
260 const Products& products = installer_state->products();
261 HKEY reg_root = installer_state->root_key();
262 base::string16 version_key;
263 for (Products::const_iterator it = products.begin(); it < products.end();
264 ++it) {
265 version_key = (*it)->distribution()->GetVersionKey();
266 install_list->AddDeleteRegValueWorkItem(reg_root,
267 version_key,
268 KEY_WOW64_32KEY,
269 google_update::kRegOldVersionField);
270 install_list->AddDeleteRegValueWorkItem(
271 reg_root,
272 version_key,
273 KEY_WOW64_32KEY,
274 google_update::kRegCriticalVersionField);
275 install_list->AddDeleteRegValueWorkItem(reg_root,
276 version_key,
277 KEY_WOW64_32KEY,
278 google_update::kRegRenameCmdField);
280 installer::InstallStatus ret = installer::RENAME_SUCCESSFUL;
281 if (!install_list->Do()) {
282 LOG(ERROR) << "Renaming of executables failed. Rolling back any changes.";
283 install_list->Rollback();
284 ret = installer::RENAME_FAILED;
286 // temp_path's dtor will take care of deleting or scheduling itself for
287 // deletion at reboot when this scope closes.
288 VLOG(1) << "Deleting temporary directory " << temp_path.path().value();
290 return ret;
293 // For each product that is being updated (i.e., already installed at an earlier
294 // version), see if that product has an update policy override that differs from
295 // that for the binaries. If any are found, fail with an error indicating that
296 // the Group Policy settings are in an inconsistent state. Do not do this test
297 // for same-version installs, since it would be unkind to block attempts to
298 // repair a corrupt installation. This function returns false when installation
299 // should be halted, in which case |status| contains the relevant exit code and
300 // the proper installer result has been written to the registry.
301 bool CheckGroupPolicySettings(const InstallationState& original_state,
302 const InstallerState& installer_state,
303 const Version& new_version,
304 installer::InstallStatus* status) {
305 #if !defined(GOOGLE_CHROME_BUILD)
306 // Chromium builds are not updated via Google Update, so there are no
307 // Group Policy settings to consult.
308 return true;
309 #else
310 DCHECK(status);
312 // Single installs are always in good shape.
313 if (!installer_state.is_multi_install())
314 return true;
316 bool settings_are_valid = true;
317 const bool is_system_install = installer_state.system_install();
318 BrowserDistribution* const binaries_dist =
319 installer_state.multi_package_binaries_distribution();
321 // Get the update policy for the binaries.
322 const GoogleUpdateSettings::UpdatePolicy binaries_policy =
323 GoogleUpdateSettings::GetAppUpdatePolicy(binaries_dist->GetAppGuid(),
324 NULL);
326 // Check for differing update policies for all of the products being updated.
327 const Products& products = installer_state.products();
328 Products::const_iterator scan = products.begin();
329 for (Products::const_iterator end = products.end(); scan != end; ++scan) {
330 BrowserDistribution* dist = (*scan)->distribution();
331 const ProductState* product_state =
332 original_state.GetProductState(is_system_install, dist->GetType());
333 // Is an earlier version of this product already installed?
334 if (product_state != NULL &&
335 product_state->version().CompareTo(new_version) < 0) {
336 bool is_overridden = false;
337 GoogleUpdateSettings::UpdatePolicy app_policy =
338 GoogleUpdateSettings::GetAppUpdatePolicy(dist->GetAppGuid(),
339 &is_overridden);
340 if (is_overridden && app_policy != binaries_policy) {
341 LOG(ERROR) << "Found legacy Group Policy setting for "
342 << dist->GetDisplayName() << " (value: " << app_policy
343 << ") that does not match the setting for "
344 << binaries_dist->GetDisplayName()
345 << " (value: " << binaries_policy << ").";
346 settings_are_valid = false;
351 if (!settings_are_valid) {
352 // TODO(grt): add " See http://goo.gl/+++ for details." to the end of this
353 // log message and to the IDS_INSTALL_INCONSISTENT_UPDATE_POLICY string once
354 // we have a help center article that explains why this error is being
355 // reported and how to resolve it.
356 LOG(ERROR) << "Cannot apply update on account of inconsistent "
357 "Google Update Group Policy settings. Use the Group Policy "
358 "Editor to set the update policy override for the "
359 << binaries_dist->GetDisplayName()
360 << " application and try again.";
361 *status = installer::INCONSISTENT_UPDATE_POLICY;
362 installer_state.WriteInstallerResult(
363 *status, IDS_INSTALL_INCONSISTENT_UPDATE_POLICY_BASE, NULL);
366 return settings_are_valid;
367 #endif // defined(GOOGLE_CHROME_BUILD)
370 // If only the binaries are being updated, fail.
371 // If any product is being installed in single-mode that already exists in
372 // multi-mode, fail.
373 bool CheckMultiInstallConditions(const InstallationState& original_state,
374 InstallerState* installer_state,
375 installer::InstallStatus* status) {
376 const Products& products = installer_state->products();
377 DCHECK(products.size());
379 const bool system_level = installer_state->system_install();
381 if (installer_state->is_multi_install()) {
382 const Product* chrome =
383 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
384 const Product* app_host =
385 installer_state->FindProduct(BrowserDistribution::CHROME_APP_HOST);
386 const Product* binaries =
387 installer_state->FindProduct(BrowserDistribution::CHROME_BINARIES);
388 const ProductState* chrome_state =
389 original_state.GetProductState(system_level,
390 BrowserDistribution::CHROME_BROWSER);
392 if (binaries) {
393 if (products.size() == 1) {
394 // There are no products aside from the binaries, so there is no update
395 // to be applied. This can happen after multi-install Chrome Frame is
396 // migrated to single-install. This is treated as an update failure
397 // unless the binaries are not in-use, in which case they will be
398 // uninstalled and success will be reported (see handling in wWinMain).
399 VLOG(1) << "No products to be updated.";
400 *status = installer::UNUSED_BINARIES;
401 installer_state->WriteInstallerResult(*status, 0, NULL);
402 return false;
404 } else {
405 // This will only be hit if --multi-install is given with no products, or
406 // if the app host is being installed and doesn't need the binaries at
407 // user-level.
408 // The former case might be due to a request by an orphaned Application
409 // Host to re-install the binaries. Thus we add them to the installation.
410 // The latter case is fine and we let it be.
411 // If this is not an app host install and the binaries are not already
412 // present, the installation will fail later due to a lack of products to
413 // install.
414 if (app_host && !chrome && !chrome_state) {
415 DCHECK(!system_level);
416 // App Host may use Chrome/Chrome binaries at system-level.
417 if (original_state.GetProductState(
418 true, // system
419 BrowserDistribution::CHROME_BROWSER) ||
420 original_state.GetProductState(
421 true, // system
422 BrowserDistribution::CHROME_BINARIES)) {
423 VLOG(1) << "Installing/updating App Launcher without binaries.";
424 } else {
425 // Somehow the binaries were present when the quick-enable app host
426 // command was run, but now they appear to be missing.
427 // Force binaries to be installed/updated.
428 scoped_ptr<Product> binaries_to_add(new Product(
429 BrowserDistribution::GetSpecificDistribution(
430 BrowserDistribution::CHROME_BINARIES)));
431 binaries_to_add->SetOption(installer::kOptionMultiInstall, true);
432 binaries = installer_state->AddProduct(&binaries_to_add);
433 VLOG(1) <<
434 "Adding binaries for pre-existing App Launcher installation.";
438 return true;
441 if (!chrome && chrome_state) {
442 // A product other than Chrome is being installed in multi-install mode,
443 // and Chrome is already present. Add Chrome to the set of products
444 // (making it multi-install in the process) so that it is updated, too.
445 scoped_ptr<Product> multi_chrome(new Product(
446 BrowserDistribution::GetSpecificDistribution(
447 BrowserDistribution::CHROME_BROWSER)));
448 multi_chrome->SetOption(installer::kOptionMultiInstall, true);
449 chrome = installer_state->AddProduct(&multi_chrome);
450 VLOG(1) << "Upgrading existing Chrome browser in multi-install mode.";
452 } else {
453 // This is a non-multi installation.
455 // Check for an existing installation of the product.
456 const ProductState* product_state = original_state.GetProductState(
457 system_level, products[0]->distribution()->GetType());
458 if (product_state != NULL) {
459 // Block downgrades from multi-install to single-install.
460 if (product_state->is_multi_install()) {
461 LOG(ERROR) << "Multi-install "
462 << products[0]->distribution()->GetDisplayName()
463 << " exists; aborting single install.";
464 *status = installer::MULTI_INSTALLATION_EXISTS;
465 installer_state->WriteInstallerResult(*status,
466 IDS_INSTALL_MULTI_INSTALLATION_EXISTS_BASE, NULL);
467 return false;
472 return true;
475 // Checks app host pre-install conditions, specifically that this is a
476 // user-level multi-install. When the pre-install conditions are not
477 // satisfied, the result is written to the registry (via WriteInstallerResult),
478 // |status| is set appropriately, and false is returned.
479 bool CheckAppHostPreconditions(const InstallationState& original_state,
480 InstallerState* installer_state,
481 installer::InstallStatus* status) {
482 if (installer_state->FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
483 if (!installer_state->is_multi_install()) {
484 LOG(DFATAL) << "App Launcher requires multi install";
485 *status = installer::APP_HOST_REQUIRES_MULTI_INSTALL;
486 // No message string since there is nothing a user can do.
487 installer_state->WriteInstallerResult(*status, 0, NULL);
488 return false;
491 if (installer_state->system_install()) {
492 LOG(DFATAL) << "App Launcher may only be installed at user-level.";
493 *status = installer::APP_HOST_REQUIRES_USER_LEVEL;
494 // No message string since there is nothing a user can do.
495 installer_state->WriteInstallerResult(*status, 0, NULL);
496 return false;
500 return true;
503 // Checks for compatibility between the current state of the system and the
504 // desired operation. Also applies policy that mutates the desired operation;
505 // specifically, the |installer_state| object.
506 // Also blocks simultaneous user-level and system-level installs. In the case
507 // of trying to install user-level Chrome when system-level exists, the
508 // existing system-level Chrome is launched.
509 // When the pre-install conditions are not satisfied, the result is written to
510 // the registry (via WriteInstallerResult), |status| is set appropriately, and
511 // false is returned.
512 bool CheckPreInstallConditions(const InstallationState& original_state,
513 InstallerState* installer_state,
514 installer::InstallStatus* status) {
515 if (!CheckAppHostPreconditions(original_state, installer_state, status)) {
516 DCHECK_NE(*status, installer::UNKNOWN_STATUS);
517 return false;
520 // See what products are already installed in multi mode. When we do multi
521 // installs, we must upgrade all installations since they share the binaries.
522 AddExistingMultiInstalls(original_state, installer_state);
524 if (!CheckMultiInstallConditions(original_state, installer_state, status)) {
525 DCHECK_NE(*status, installer::UNKNOWN_STATUS);
526 return false;
529 const Products& products = installer_state->products();
530 if (products.empty()) {
531 // We haven't been given any products on which to operate.
532 LOG(ERROR)
533 << "Not given any products to install and no products found to update.";
534 *status = installer::CHROME_NOT_INSTALLED;
535 installer_state->WriteInstallerResult(*status,
536 IDS_INSTALL_NO_PRODUCTS_TO_UPDATE_BASE, NULL);
537 return false;
540 if (!installer_state->system_install()) {
541 // This is a user-level installation. Make sure that we are not installing
542 // on top of an existing system-level installation.
543 for (Products::const_iterator it = products.begin(); it < products.end();
544 ++it) {
545 const Product& product = **it;
546 BrowserDistribution* browser_dist = product.distribution();
548 // Skip over the binaries, as it's okay for them to be at both levels
549 // for different products.
550 if (browser_dist->GetType() == BrowserDistribution::CHROME_BINARIES)
551 continue;
553 const ProductState* user_level_product_state =
554 original_state.GetProductState(false, browser_dist->GetType());
555 const ProductState* system_level_product_state =
556 original_state.GetProductState(true, browser_dist->GetType());
558 // Allow upgrades to proceed so that out-of-date versions are not left
559 // around.
560 if (user_level_product_state)
561 continue;
563 // This is a new user-level install...
565 if (system_level_product_state) {
566 // ... and the product already exists at system-level.
567 LOG(ERROR) << "Already installed version "
568 << system_level_product_state->version().GetString()
569 << " at system-level conflicts with this one at user-level.";
570 if (product.is_chrome()) {
571 // Instruct Google Update to launch the existing system-level Chrome.
572 // There should be no error dialog.
573 base::FilePath install_path(installer::GetChromeInstallPath(
574 true, // system
575 browser_dist));
576 if (install_path.empty()) {
577 // Give up if we failed to construct the install path.
578 *status = installer::OS_ERROR;
579 installer_state->WriteInstallerResult(*status,
580 IDS_INSTALL_OS_ERROR_BASE,
581 NULL);
582 } else {
583 *status = installer::EXISTING_VERSION_LAUNCHED;
584 base::FilePath chrome_exe =
585 install_path.Append(installer::kChromeExe);
586 CommandLine cmd(chrome_exe);
587 cmd.AppendSwitch(switches::kForceFirstRun);
588 installer_state->WriteInstallerResult(*status, 0, NULL);
589 VLOG(1) << "Launching existing system-level chrome instead.";
590 base::LaunchProcess(cmd, base::LaunchOptions(), NULL);
592 } else {
593 // It's no longer possible for |product| to be anything other than
594 // Chrome.
595 NOTREACHED();
597 return false;
601 } else { // System-level install.
602 // --ensure-google-update-present is supported for user-level only.
603 // The flag is generic, but its primary use case involves App Host.
604 if (installer_state->ensure_google_update_present()) {
605 LOG(DFATAL) << "--" << installer::switches::kEnsureGoogleUpdatePresent
606 << " is supported for user-level only.";
607 *status = installer::APP_HOST_REQUIRES_USER_LEVEL;
608 // No message string since there is nothing a user can do.
609 installer_state->WriteInstallerResult(*status, 0, NULL);
610 return false;
614 return true;
617 // Initializes |temp_path| to "Temp" within the target directory, and
618 // |unpack_path| to a random directory beginning with "source" within
619 // |temp_path|. Returns false on error.
620 bool CreateTemporaryAndUnpackDirectories(
621 const InstallerState& installer_state,
622 installer::SelfCleaningTempDir* temp_path,
623 base::FilePath* unpack_path) {
624 DCHECK(temp_path && unpack_path);
626 if (!temp_path->Initialize(installer_state.target_path().DirName(),
627 installer::kInstallTempDir)) {
628 PLOG(ERROR) << "Could not create temporary path.";
629 return false;
631 VLOG(1) << "Created path " << temp_path->path().value();
633 if (!base::CreateTemporaryDirInDir(temp_path->path(),
634 installer::kInstallSourceDir,
635 unpack_path)) {
636 PLOG(ERROR) << "Could not create temporary path for unpacked archive.";
637 return false;
640 return true;
643 installer::InstallStatus UninstallProduct(
644 const InstallationState& original_state,
645 const InstallerState& installer_state,
646 const base::FilePath& setup_exe,
647 const CommandLine& cmd_line,
648 bool remove_all,
649 bool force_uninstall,
650 const Product& product) {
651 const ProductState* product_state =
652 original_state.GetProductState(installer_state.system_install(),
653 product.distribution()->GetType());
654 if (product_state != NULL) {
655 VLOG(1) << "version on the system: "
656 << product_state->version().GetString();
657 } else if (!force_uninstall) {
658 LOG(ERROR) << product.distribution()->GetDisplayName()
659 << " not found for uninstall.";
660 return installer::CHROME_NOT_INSTALLED;
663 return installer::UninstallProduct(
664 original_state, installer_state, setup_exe, product, remove_all,
665 force_uninstall, cmd_line);
668 installer::InstallStatus UninstallProducts(
669 const InstallationState& original_state,
670 const InstallerState& installer_state,
671 const base::FilePath& setup_exe,
672 const CommandLine& cmd_line) {
673 const Products& products = installer_state.products();
675 // System-level Chrome will be launched via this command if its program gets
676 // set below.
677 CommandLine system_level_cmd(CommandLine::NO_PROGRAM);
679 const Product* chrome =
680 installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER);
681 if (chrome) {
682 // InstallerState::Initialize always puts Chrome first, and we rely on that
683 // here for this reason: if Chrome is in-use, the user will be prompted to
684 // confirm uninstallation. Upon cancel, we should not continue with the
685 // other products.
686 DCHECK(products[0]->is_chrome());
688 if (cmd_line.HasSwitch(installer::switches::kSelfDestruct) &&
689 !installer_state.system_install()) {
690 BrowserDistribution* dist = chrome->distribution();
691 const base::FilePath system_exe_path(
692 installer::GetChromeInstallPath(true, dist)
693 .Append(installer::kChromeExe));
694 system_level_cmd.SetProgram(system_exe_path);
697 if (installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)) {
698 // Chrome Binaries should be last; if something else is cancelled, they
699 // should stay.
700 DCHECK(products[products.size() - 1]->is_chrome_binaries());
703 installer::InstallStatus install_status = installer::UNINSTALL_SUCCESSFUL;
704 installer::InstallStatus prod_status = installer::UNKNOWN_STATUS;
705 const bool force = cmd_line.HasSwitch(installer::switches::kForceUninstall);
706 const bool remove_all = !cmd_line.HasSwitch(
707 installer::switches::kDoNotRemoveSharedItems);
709 for (Products::const_iterator it = products.begin();
710 install_status != installer::UNINSTALL_CANCELLED && it < products.end();
711 ++it) {
712 prod_status = UninstallProduct(original_state, installer_state, setup_exe,
713 cmd_line, remove_all, force, **it);
714 if (prod_status != installer::UNINSTALL_SUCCESSFUL)
715 install_status = prod_status;
718 installer::CleanUpInstallationDirectoryAfterUninstall(
719 original_state, installer_state, setup_exe, &install_status);
721 // The app and vendor dirs may now be empty. Make a last-ditch attempt to
722 // delete them.
723 installer::DeleteChromeDirectoriesIfEmpty(installer_state.target_path());
725 // Trigger Active Setup if it was requested for the chrome product. This needs
726 // to be done after the UninstallProduct calls as some of them might
727 // otherwise terminate the process launched by TriggerActiveSetupCommand().
728 if (chrome && cmd_line.HasSwitch(installer::switches::kTriggerActiveSetup))
729 InstallUtil::TriggerActiveSetupCommand();
731 if (!system_level_cmd.GetProgram().empty())
732 base::LaunchProcess(system_level_cmd, base::LaunchOptions(), NULL);
734 // Tell Google Update that an uninstall has taken place.
735 // Ignore the return value: success or failure of Google Update
736 // has no bearing on the success or failure of Chrome's uninstallation.
737 google_update::UninstallGoogleUpdate(installer_state.system_install());
739 return install_status;
742 // Uninstall the binaries if they are the only product present and they're not
743 // in-use.
744 void UninstallBinariesIfUnused(
745 const InstallationState& original_state,
746 const InstallerState& installer_state,
747 installer::InstallStatus* install_status) {
748 // Early exit if the binaries are still in use.
749 if (*install_status != installer::UNUSED_BINARIES ||
750 installer_state.AreBinariesInUse(original_state)) {
751 return;
754 LOG(INFO) << "Uninstalling unused binaries";
755 installer_state.UpdateStage(installer::UNINSTALLING_BINARIES);
757 // Simulate the uninstall as coming from the installed version.
758 const ProductState* binaries_state =
759 original_state.GetProductState(installer_state.system_install(),
760 BrowserDistribution::CHROME_BINARIES);
761 const CommandLine& uninstall_cmd(binaries_state->uninstall_command());
762 MasterPreferences uninstall_prefs(uninstall_cmd);
763 InstallerState uninstall_state;
764 uninstall_state.Initialize(uninstall_cmd, uninstall_prefs, original_state);
766 *install_status = UninstallProducts(original_state, uninstall_state,
767 uninstall_cmd.GetProgram(),
768 uninstall_cmd);
770 // Report that the binaries were uninstalled if they were. This translates
771 // into a successful install return code.
772 if (IsUninstallSuccess(*install_status)) {
773 *install_status = installer::UNUSED_BINARIES_UNINSTALLED;
774 installer_state.WriteInstallerResult(*install_status, 0, NULL);
778 installer::InstallStatus InstallProducts(
779 const InstallationState& original_state,
780 const base::FilePath& setup_exe,
781 const CommandLine& cmd_line,
782 const MasterPreferences& prefs,
783 InstallerState* installer_state,
784 base::FilePath* installer_directory) {
785 DCHECK(installer_state);
786 const bool system_install = installer_state->system_install();
787 installer::InstallStatus install_status = installer::UNKNOWN_STATUS;
788 installer::ArchiveType archive_type = installer::UNKNOWN_ARCHIVE_TYPE;
789 bool delegated_to_existing = false;
790 installer_state->UpdateStage(installer::PRECONDITIONS);
791 // The stage provides more fine-grained information than -multifail, so remove
792 // the -multifail suffix from the Google Update "ap" value.
793 BrowserDistribution::GetSpecificDistribution(installer_state->state_type())->
794 UpdateInstallStatus(system_install, archive_type, install_status);
795 if (CheckPreInstallConditions(original_state, installer_state,
796 &install_status)) {
797 VLOG(1) << "Installing to " << installer_state->target_path().value();
798 install_status = InstallProductsHelper(
799 original_state, setup_exe, cmd_line, prefs, *installer_state,
800 installer_directory, &archive_type, &delegated_to_existing);
801 } else {
802 // CheckPreInstallConditions must set the status on failure.
803 DCHECK_NE(install_status, installer::UNKNOWN_STATUS);
806 // Delete the master preferences file if present. Note that we do not care
807 // about rollback here and we schedule for deletion on reboot if the delete
808 // fails. As such, we do not use DeleteTreeWorkItem.
809 if (cmd_line.HasSwitch(installer::switches::kInstallerData)) {
810 base::FilePath prefs_path(cmd_line.GetSwitchValuePath(
811 installer::switches::kInstallerData));
812 if (!base::DeleteFile(prefs_path, false)) {
813 LOG(ERROR) << "Failed deleting master preferences file "
814 << prefs_path.value()
815 << ", scheduling for deletion after reboot.";
816 ScheduleFileSystemEntityForDeletion(prefs_path);
820 // Early exit if this setup.exe delegated to another, since that one would
821 // have taken care of UpdateInstallStatus and UpdateStage.
822 if (delegated_to_existing)
823 return install_status;
825 const Products& products = installer_state->products();
826 for (Products::const_iterator it = products.begin(); it < products.end();
827 ++it) {
828 (*it)->distribution()->UpdateInstallStatus(
829 system_install, archive_type, install_status);
832 UninstallBinariesIfUnused(original_state, *installer_state, &install_status);
834 installer_state->UpdateStage(installer::NO_STAGE);
835 return install_status;
838 installer::InstallStatus ShowEULADialog(const base::string16& inner_frame) {
839 VLOG(1) << "About to show EULA";
840 base::string16 eula_path = installer::GetLocalizedEulaResource();
841 if (eula_path.empty()) {
842 LOG(ERROR) << "No EULA path available";
843 return installer::EULA_REJECTED;
845 // Newer versions of the caller pass an inner frame parameter that must
846 // be given to the html page being launched.
847 installer::EulaHTMLDialog dlg(eula_path, inner_frame);
848 installer::EulaHTMLDialog::Outcome outcome = dlg.ShowModal();
849 if (installer::EulaHTMLDialog::REJECTED == outcome) {
850 LOG(ERROR) << "EULA rejected or EULA failure";
851 return installer::EULA_REJECTED;
853 if (installer::EulaHTMLDialog::ACCEPTED_OPT_IN == outcome) {
854 VLOG(1) << "EULA accepted (opt-in)";
855 return installer::EULA_ACCEPTED_OPT_IN;
857 VLOG(1) << "EULA accepted (no opt-in)";
858 return installer::EULA_ACCEPTED;
861 // Creates the sentinel indicating that the EULA was required and has been
862 // accepted.
863 bool CreateEULASentinel(BrowserDistribution* dist) {
864 base::FilePath eula_sentinel;
865 if (!InstallUtil::GetEULASentinelFilePath(&eula_sentinel))
866 return false;
868 return (base::CreateDirectory(eula_sentinel.DirName()) &&
869 base::WriteFile(eula_sentinel, "", 0) != -1);
872 void ActivateMetroChrome() {
873 // Check to see if we're per-user or not. Need to do this since we may
874 // not have been invoked with --system-level even for a machine install.
875 wchar_t exe_path[MAX_PATH * 2] = {};
876 GetModuleFileName(NULL, exe_path, arraysize(exe_path));
877 bool is_per_user_install = InstallUtil::IsPerUserInstall(exe_path);
879 base::string16 app_model_id = ShellUtil::GetBrowserModelId(
880 BrowserDistribution::GetDistribution(), is_per_user_install);
882 base::win::ScopedComPtr<IApplicationActivationManager> activator;
883 HRESULT hr = activator.CreateInstance(CLSID_ApplicationActivationManager);
884 if (SUCCEEDED(hr)) {
885 DWORD pid = 0;
886 hr = activator->ActivateApplication(
887 app_model_id.c_str(), L"open", AO_NONE, &pid);
890 LOG_IF(ERROR, FAILED(hr)) << "Tried and failed to launch Metro Chrome. "
891 << "hr=" << std::hex << hr;
894 installer::InstallStatus RegisterDevChrome(
895 const InstallationState& original_state,
896 const InstallerState& installer_state,
897 const base::FilePath& setup_exe,
898 const CommandLine& cmd_line) {
899 BrowserDistribution* chrome_dist =
900 BrowserDistribution::GetSpecificDistribution(
901 BrowserDistribution::CHROME_BROWSER);
903 // Only proceed with registering a dev chrome if no real Chrome installation
904 // of the same distribution are present on this system.
905 const ProductState* existing_chrome =
906 original_state.GetProductState(false,
907 BrowserDistribution::CHROME_BROWSER);
908 if (!existing_chrome) {
909 existing_chrome =
910 original_state.GetProductState(true, BrowserDistribution::CHROME_BROWSER);
912 if (existing_chrome) {
913 static const wchar_t kPleaseUninstallYourChromeMessage[] =
914 L"You already have a full-installation (non-dev) of %1ls, please "
915 L"uninstall it first using Add/Remove Programs in the control panel.";
916 base::string16 name(chrome_dist->GetDisplayName());
917 base::string16 message(
918 base::StringPrintf(kPleaseUninstallYourChromeMessage, name.c_str()));
920 LOG(ERROR) << "Aborting operation: another installation of " << name
921 << " was found, as a last resort (if the product is not present "
922 "in Add/Remove Programs), try executing: "
923 << existing_chrome->uninstall_command().GetCommandLineString();
924 MessageBox(NULL, message.c_str(), NULL, MB_ICONERROR);
925 return installer::INSTALL_FAILED;
928 base::FilePath chrome_exe(
929 cmd_line.GetSwitchValuePath(installer::switches::kRegisterDevChrome));
930 if (chrome_exe.empty())
931 chrome_exe = setup_exe.DirName().Append(installer::kChromeExe);
932 if (!chrome_exe.IsAbsolute())
933 chrome_exe = base::MakeAbsoluteFilePath(chrome_exe);
935 installer::InstallStatus status = installer::FIRST_INSTALL_SUCCESS;
936 if (base::PathExists(chrome_exe)) {
937 Product chrome(chrome_dist);
939 // Create the Start menu shortcut and pin it to the taskbar.
940 ShellUtil::ShortcutProperties shortcut_properties(ShellUtil::CURRENT_USER);
941 chrome.AddDefaultShortcutProperties(chrome_exe, &shortcut_properties);
942 shortcut_properties.set_dual_mode(true);
943 shortcut_properties.set_pin_to_taskbar(true);
944 ShellUtil::CreateOrUpdateShortcut(
945 ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR, chrome_dist,
946 shortcut_properties, ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS);
948 // Register Chrome at user-level and make it default.
949 scoped_ptr<WorkItemList> delegate_execute_list(
950 WorkItem::CreateWorkItemList());
951 installer::AddDelegateExecuteWorkItems(
952 installer_state, chrome_exe.DirName(), Version(), chrome,
953 delegate_execute_list.get());
954 delegate_execute_list->Do();
955 if (ShellUtil::CanMakeChromeDefaultUnattended()) {
956 ShellUtil::MakeChromeDefault(
957 chrome_dist, ShellUtil::CURRENT_USER, chrome_exe.value(), true);
958 } else {
959 ShellUtil::ShowMakeChromeDefaultSystemUI(chrome_dist, chrome_exe.value());
961 } else {
962 LOG(ERROR) << "Path not found: " << chrome_exe.value();
963 status = installer::INSTALL_FAILED;
965 return status;
968 // This method processes any command line options that make setup.exe do
969 // various tasks other than installation (renaming chrome.exe, showing eula
970 // among others). This function returns true if any such command line option
971 // has been found and processed (so setup.exe should exit at that point).
972 bool HandleNonInstallCmdLineOptions(const InstallationState& original_state,
973 const base::FilePath& setup_exe,
974 const CommandLine& cmd_line,
975 InstallerState* installer_state,
976 int* exit_code) {
977 // TODO(gab): Add a local |status| variable which each block below sets;
978 // only determine the |exit_code| from |status| at the end (this will allow
979 // this method to validate that
980 // (!handled || status != installer::UNKNOWN_STATUS)).
981 bool handled = true;
982 // TODO(tommi): Split these checks up into functions and use a data driven
983 // map of switch->function.
984 if (cmd_line.HasSwitch(installer::switches::kUpdateSetupExe)) {
985 installer::InstallStatus status = installer::SETUP_PATCH_FAILED;
986 // If --update-setup-exe command line option is given, we apply the given
987 // patch to current exe, and store the resulting binary in the path
988 // specified by --new-setup-exe. But we need to first unpack the file
989 // given in --update-setup-exe.
990 base::ScopedTempDir temp_path;
991 if (!temp_path.CreateUniqueTempDir()) {
992 PLOG(ERROR) << "Could not create temporary path.";
993 } else {
994 base::FilePath compressed_archive(cmd_line.GetSwitchValuePath(
995 installer::switches::kUpdateSetupExe));
996 VLOG(1) << "Opening archive " << compressed_archive.value();
997 if (installer::ArchivePatchHelper::UncompressAndPatch(
998 temp_path.path(),
999 compressed_archive,
1000 setup_exe,
1001 cmd_line.GetSwitchValuePath(installer::switches::kNewSetupExe))) {
1002 status = installer::NEW_VERSION_UPDATED;
1004 if (!temp_path.Delete()) {
1005 // PLOG would be nice, but Delete() doesn't leave a meaningful value in
1006 // the Windows last-error code.
1007 LOG(WARNING) << "Scheduling temporary path " << temp_path.path().value()
1008 << " for deletion at reboot.";
1009 ScheduleDirectoryForDeletion(temp_path.path());
1013 *exit_code = InstallUtil::GetInstallReturnCode(status);
1014 if (*exit_code) {
1015 LOG(WARNING) << "setup.exe patching failed.";
1016 installer_state->WriteInstallerResult(
1017 status, IDS_SETUP_PATCH_FAILED_BASE, NULL);
1019 // We will be exiting normally, so clear the stage indicator.
1020 installer_state->UpdateStage(installer::NO_STAGE);
1021 } else if (cmd_line.HasSwitch(installer::switches::kShowEula)) {
1022 // Check if we need to show the EULA. If it is passed as a command line
1023 // then the dialog is shown and regardless of the outcome setup exits here.
1024 base::string16 inner_frame =
1025 cmd_line.GetSwitchValueNative(installer::switches::kShowEula);
1026 *exit_code = ShowEULADialog(inner_frame);
1028 if (installer::EULA_REJECTED != *exit_code) {
1029 if (GoogleUpdateSettings::SetEULAConsent(
1030 original_state, BrowserDistribution::GetDistribution(), true)) {
1031 CreateEULASentinel(BrowserDistribution::GetDistribution());
1033 // For a metro-originated launch, we now need to launch back into metro.
1034 if (cmd_line.HasSwitch(installer::switches::kShowEulaForMetro))
1035 ActivateMetroChrome();
1037 } else if (cmd_line.HasSwitch(installer::switches::kConfigureUserSettings)) {
1038 // NOTE: Should the work done here, on kConfigureUserSettings, change:
1039 // kActiveSetupVersion in install_worker.cc needs to be increased for Active
1040 // Setup to invoke this again for all users of this install.
1041 const Product* chrome_install =
1042 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
1043 installer::InstallStatus status = installer::INVALID_STATE_FOR_OPTION;
1044 if (chrome_install && installer_state->system_install()) {
1045 bool force =
1046 cmd_line.HasSwitch(installer::switches::kForceConfigureUserSettings);
1047 installer::HandleActiveSetupForBrowser(installer_state->target_path(),
1048 *chrome_install, force);
1049 status = installer::INSTALL_REPAIRED;
1050 } else {
1051 LOG(DFATAL) << "chrome_install:" << chrome_install
1052 << ", system_install:" << installer_state->system_install();
1054 *exit_code = InstallUtil::GetInstallReturnCode(status);
1055 } else if (cmd_line.HasSwitch(installer::switches::kRegisterDevChrome)) {
1056 installer::InstallStatus status = RegisterDevChrome(
1057 original_state, *installer_state, setup_exe, cmd_line);
1058 *exit_code = InstallUtil::GetInstallReturnCode(status);
1059 } else if (cmd_line.HasSwitch(installer::switches::kRegisterChromeBrowser)) {
1060 installer::InstallStatus status = installer::UNKNOWN_STATUS;
1061 const Product* chrome_install =
1062 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
1063 if (chrome_install) {
1064 // If --register-chrome-browser option is specified, register all
1065 // Chrome protocol/file associations, as well as register it as a valid
1066 // browser for Start Menu->Internet shortcut. This switch will also
1067 // register Chrome as a valid handler for a set of URL protocols that
1068 // Chrome may become the default handler for, either by the user marking
1069 // Chrome as the default browser, through the Windows Default Programs
1070 // control panel settings, or through website use of
1071 // registerProtocolHandler. These protocols are found in
1072 // ShellUtil::kPotentialProtocolAssociations.
1073 // The --register-url-protocol will additionally register Chrome as a
1074 // potential handler for the supplied protocol, and is used if a website
1075 // registers a handler for a protocol not found in
1076 // ShellUtil::kPotentialProtocolAssociations.
1077 // These options should only be used when setup.exe is launched with admin
1078 // rights. We do not make any user specific changes with this option.
1079 DCHECK(IsUserAnAdmin());
1080 base::string16 chrome_exe(cmd_line.GetSwitchValueNative(
1081 installer::switches::kRegisterChromeBrowser));
1082 base::string16 suffix;
1083 if (cmd_line.HasSwitch(
1084 installer::switches::kRegisterChromeBrowserSuffix)) {
1085 suffix = cmd_line.GetSwitchValueNative(
1086 installer::switches::kRegisterChromeBrowserSuffix);
1088 if (cmd_line.HasSwitch(installer::switches::kRegisterURLProtocol)) {
1089 base::string16 protocol = cmd_line.GetSwitchValueNative(
1090 installer::switches::kRegisterURLProtocol);
1091 // ShellUtil::RegisterChromeForProtocol performs all registration
1092 // done by ShellUtil::RegisterChromeBrowser, as well as registering
1093 // with Windows as capable of handling the supplied protocol.
1094 if (ShellUtil::RegisterChromeForProtocol(
1095 chrome_install->distribution(), chrome_exe, suffix, protocol,
1096 false))
1097 status = installer::IN_USE_UPDATED;
1098 } else {
1099 if (ShellUtil::RegisterChromeBrowser(chrome_install->distribution(),
1100 chrome_exe, suffix, false))
1101 status = installer::IN_USE_UPDATED;
1103 } else {
1104 LOG(DFATAL) << "Can't register browser - Chrome distribution not found";
1106 *exit_code = InstallUtil::GetInstallReturnCode(status);
1107 } else if (cmd_line.HasSwitch(installer::switches::kRenameChromeExe)) {
1108 // If --rename-chrome-exe is specified, we want to rename the executables
1109 // and exit.
1110 *exit_code = RenameChromeExecutables(original_state, installer_state);
1111 } else if (cmd_line.HasSwitch(
1112 installer::switches::kRemoveChromeRegistration)) {
1113 // This is almost reverse of --register-chrome-browser option above.
1114 // Here we delete Chrome browser registration. This option should only
1115 // be used when setup.exe is launched with admin rights. We do not
1116 // make any user specific changes in this option.
1117 base::string16 suffix;
1118 if (cmd_line.HasSwitch(
1119 installer::switches::kRegisterChromeBrowserSuffix)) {
1120 suffix = cmd_line.GetSwitchValueNative(
1121 installer::switches::kRegisterChromeBrowserSuffix);
1123 installer::InstallStatus tmp = installer::UNKNOWN_STATUS;
1124 const Product* chrome_install =
1125 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
1126 DCHECK(chrome_install);
1127 if (chrome_install) {
1128 installer::DeleteChromeRegistrationKeys(*installer_state,
1129 chrome_install->distribution(), HKEY_LOCAL_MACHINE, suffix, &tmp);
1131 *exit_code = tmp;
1132 } else if (cmd_line.HasSwitch(installer::switches::kOnOsUpgrade)) {
1133 const Product* chrome_install =
1134 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
1135 installer::InstallStatus status = installer::INVALID_STATE_FOR_OPTION;
1136 if (chrome_install) {
1137 installer::HandleOsUpgradeForBrowser(*installer_state,
1138 *chrome_install);
1139 status = installer::INSTALL_REPAIRED;
1140 } else {
1141 LOG(DFATAL) << "Chrome product not found.";
1143 *exit_code = InstallUtil::GetInstallReturnCode(status);
1144 } else if (cmd_line.HasSwitch(installer::switches::kQueryEULAAcceptance)) {
1145 *exit_code = installer::IsEULAAccepted(installer_state->system_install());
1146 } else if (cmd_line.HasSwitch(installer::switches::kInactiveUserToast)) {
1147 // Launch the inactive user toast experiment.
1148 int flavor = -1;
1149 base::StringToInt(cmd_line.GetSwitchValueNative(
1150 installer::switches::kInactiveUserToast), &flavor);
1151 std::string experiment_group =
1152 cmd_line.GetSwitchValueASCII(installer::switches::kExperimentGroup);
1153 DCHECK_NE(-1, flavor);
1154 if (flavor == -1) {
1155 *exit_code = installer::UNKNOWN_STATUS;
1156 } else {
1157 // This code is called (via setup.exe relaunch) only if a product is known
1158 // to run user experiments, so no check is required.
1159 const Products& products = installer_state->products();
1160 for (Products::const_iterator it = products.begin(); it < products.end();
1161 ++it) {
1162 const Product& product = **it;
1163 installer::InactiveUserToastExperiment(
1164 flavor, base::ASCIIToUTF16(experiment_group), product,
1165 installer_state->target_path());
1168 } else if (cmd_line.HasSwitch(installer::switches::kSystemLevelToast)) {
1169 const Products& products = installer_state->products();
1170 for (Products::const_iterator it = products.begin(); it < products.end();
1171 ++it) {
1172 const Product& product = **it;
1173 BrowserDistribution* browser_dist = product.distribution();
1174 // We started as system-level and have been re-launched as user level
1175 // to continue with the toast experiment.
1176 Version installed_version;
1177 InstallUtil::GetChromeVersion(browser_dist, true, &installed_version);
1178 if (!installed_version.IsValid()) {
1179 LOG(ERROR) << "No installation of "
1180 << browser_dist->GetDisplayName()
1181 << " found for system-level toast.";
1182 } else {
1183 product.LaunchUserExperiment(
1184 setup_exe, installer::REENTRY_SYS_UPDATE, true);
1187 } else if (cmd_line.HasSwitch(installer::switches::kPatch)) {
1188 const std::string patch_type_str(
1189 cmd_line.GetSwitchValueASCII(installer::switches::kPatch));
1190 const base::FilePath input_file(
1191 cmd_line.GetSwitchValuePath(installer::switches::kInputFile));
1192 const base::FilePath patch_file(
1193 cmd_line.GetSwitchValuePath(installer::switches::kPatchFile));
1194 const base::FilePath output_file(
1195 cmd_line.GetSwitchValuePath(installer::switches::kOutputFile));
1197 if (patch_type_str == installer::kCourgette) {
1198 *exit_code = installer::CourgettePatchFiles(input_file,
1199 patch_file,
1200 output_file);
1201 } else if (patch_type_str == installer::kBsdiff) {
1202 *exit_code = installer::BsdiffPatchFiles(input_file,
1203 patch_file,
1204 output_file);
1205 } else {
1206 *exit_code = installer::PATCH_INVALID_ARGUMENTS;
1208 } else if (cmd_line.HasSwitch(installer::switches::kReenableAutoupdates)) {
1209 // setup.exe has been asked to attempt to reenable updates for Chrome.
1210 // Figure out whether we should do so for the multi binaries or the main
1211 // Chrome product.
1212 BrowserDistribution::Type dist_type = BrowserDistribution::CHROME_BROWSER;
1213 if (installer_state->is_multi_install())
1214 dist_type = BrowserDistribution::CHROME_BINARIES;
1216 BrowserDistribution* dist =
1217 BrowserDistribution::GetSpecificDistribution(dist_type);
1218 bool updates_enabled =
1219 GoogleUpdateSettings::ReenableAutoupdatesForApp(dist->GetAppGuid());
1220 *exit_code = updates_enabled ? installer::REENABLE_UPDATES_SUCCEEDED :
1221 installer::REENABLE_UPDATES_FAILED;
1222 } else {
1223 handled = false;
1226 return handled;
1229 bool ShowRebootDialog() {
1230 // Get a token for this process.
1231 HANDLE token;
1232 if (!OpenProcessToken(GetCurrentProcess(),
1233 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
1234 &token)) {
1235 LOG(ERROR) << "Failed to open token.";
1236 return false;
1239 // Use a ScopedHandle to keep track of and eventually close our handle.
1240 // TODO(robertshield): Add a Receive() method to base's ScopedHandle.
1241 base::win::ScopedHandle scoped_handle(token);
1243 // Get the LUID for the shutdown privilege.
1244 TOKEN_PRIVILEGES tkp = {0};
1245 LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
1246 tkp.PrivilegeCount = 1;
1247 tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1249 // Get the shutdown privilege for this process.
1250 AdjustTokenPrivileges(token, FALSE, &tkp, 0,
1251 reinterpret_cast<PTOKEN_PRIVILEGES>(NULL), 0);
1252 if (GetLastError() != ERROR_SUCCESS) {
1253 LOG(ERROR) << "Unable to get shutdown privileges.";
1254 return false;
1257 // Popup a dialog that will prompt to reboot using the default system message.
1258 // TODO(robertshield): Add a localized, more specific string to the prompt.
1259 RestartDialog(NULL, NULL, EWX_REBOOT | EWX_FORCEIFHUNG);
1260 return true;
1263 // Returns the Custom information for the client identified by the exe path
1264 // passed in. This information is used for crash reporting.
1265 google_breakpad::CustomClientInfo* GetCustomInfo(const wchar_t* exe_path) {
1266 base::string16 product;
1267 base::string16 version;
1268 scoped_ptr<FileVersionInfo> version_info(
1269 FileVersionInfo::CreateFileVersionInfo(base::FilePath(exe_path)));
1270 if (version_info.get()) {
1271 version = version_info->product_version();
1272 product = version_info->product_short_name();
1275 if (version.empty())
1276 version = L"0.1.0.0";
1278 if (product.empty())
1279 product = L"Chrome Installer";
1281 static google_breakpad::CustomInfoEntry ver_entry(L"ver", version.c_str());
1282 static google_breakpad::CustomInfoEntry prod_entry(L"prod", product.c_str());
1283 static google_breakpad::CustomInfoEntry plat_entry(L"plat", L"Win32");
1284 static google_breakpad::CustomInfoEntry type_entry(L"ptype",
1285 L"Chrome Installer");
1286 static google_breakpad::CustomInfoEntry entries[] = {
1287 ver_entry, prod_entry, plat_entry, type_entry };
1288 static google_breakpad::CustomClientInfo custom_info = {
1289 entries, arraysize(entries) };
1290 return &custom_info;
1293 // Initialize crash reporting for this process. This involves connecting to
1294 // breakpad, etc.
1295 scoped_ptr<google_breakpad::ExceptionHandler> InitializeCrashReporting(
1296 bool system_install) {
1297 // Only report crashes if the user allows it.
1298 if (!GoogleUpdateSettings::GetCollectStatsConsent())
1299 return scoped_ptr<google_breakpad::ExceptionHandler>();
1301 // Get the alternate dump directory. We use the temp path.
1302 base::FilePath temp_directory;
1303 if (!base::GetTempDir(&temp_directory) || temp_directory.empty())
1304 return scoped_ptr<google_breakpad::ExceptionHandler>();
1306 wchar_t exe_path[MAX_PATH * 2] = {0};
1307 GetModuleFileName(NULL, exe_path, arraysize(exe_path));
1309 // Build the pipe name. It can be either:
1310 // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18"
1311 // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>"
1312 base::string16 user_sid = kSystemPrincipalSid;
1314 if (!system_install) {
1315 if (!base::win::GetUserSidString(&user_sid)) {
1316 return scoped_ptr<google_breakpad::ExceptionHandler>();
1320 base::string16 pipe_name = kGoogleUpdatePipeName;
1321 pipe_name += user_sid;
1323 return scoped_ptr<google_breakpad::ExceptionHandler>(
1324 new google_breakpad::ExceptionHandler(
1325 temp_directory.value(), NULL, NULL, NULL,
1326 google_breakpad::ExceptionHandler::HANDLER_ALL, kLargerDumpType,
1327 pipe_name.c_str(), GetCustomInfo(exe_path)));
1330 // Uninstalls multi-install Chrome Frame if the current operation is a
1331 // multi-install install or update. The operation is performed directly rather
1332 // than delegated to the existing install since there is no facility in older
1333 // versions of setup.exe to uninstall GCF without touching the binaries. The
1334 // binaries will be uninstalled during later processing if they are not in-use
1335 // (see UninstallBinariesIfUnused). |original_state| and |installer_state| are
1336 // updated to reflect the state of the world following the operation.
1337 void UninstallMultiChromeFrameIfPresent(const CommandLine& cmd_line,
1338 const MasterPreferences& prefs,
1339 InstallationState* original_state,
1340 InstallerState* installer_state) {
1341 // Early exit if not installing or updating multi-install product(s).
1342 if (installer_state->operation() != InstallerState::MULTI_INSTALL &&
1343 installer_state->operation() != InstallerState::MULTI_UPDATE) {
1344 return;
1347 // Early exit if Chrome Frame is not present as multi-install.
1348 const ProductState* chrome_frame_state =
1349 original_state->GetProductState(installer_state->system_install(),
1350 BrowserDistribution::CHROME_FRAME);
1351 if (!chrome_frame_state || !chrome_frame_state->is_multi_install())
1352 return;
1354 LOG(INFO) << "Uninstalling multi-install Chrome Frame.";
1355 installer_state->UpdateStage(installer::UNINSTALLING_CHROME_FRAME);
1357 // Uninstall Chrome Frame without touching the multi-install binaries.
1358 // Simulate the uninstall as coming from the installed version.
1359 const CommandLine& uninstall_cmd(chrome_frame_state->uninstall_command());
1360 MasterPreferences uninstall_prefs(uninstall_cmd);
1361 InstallerState uninstall_state;
1362 uninstall_state.Initialize(uninstall_cmd, uninstall_prefs, *original_state);
1363 // Post M32, uninstall_prefs and uninstall_state won't have Chrome Frame in
1364 // them since they've lost the power to do Chrome Frame installs.
1365 const Product* chrome_frame_product = uninstall_state.AddProductFromState(
1366 BrowserDistribution::CHROME_FRAME, *chrome_frame_state);
1367 if (chrome_frame_product) {
1368 // No shared state should be left behind.
1369 const bool remove_all = true;
1370 // Don't accept no for an answer.
1371 const bool force_uninstall = true;
1372 installer::InstallStatus uninstall_status =
1373 installer::UninstallProduct(*original_state, uninstall_state,
1374 uninstall_cmd.GetProgram(),
1375 *chrome_frame_product, remove_all,
1376 force_uninstall, cmd_line);
1378 VLOG(1) << "Uninstallation of Chrome Frame returned status "
1379 << uninstall_status;
1380 } else {
1381 LOG(ERROR) << "Chrome Frame not found for uninstall.";
1384 // Refresh state for the continuation of the original install/update.
1385 original_state->Initialize();
1386 installer_state->Initialize(cmd_line, prefs, *original_state);
1389 } // namespace
1391 namespace installer {
1393 InstallStatus InstallProductsHelper(
1394 const InstallationState& original_state,
1395 const base::FilePath& setup_exe,
1396 const CommandLine& cmd_line,
1397 const MasterPreferences& prefs,
1398 const InstallerState& installer_state,
1399 base::FilePath* installer_directory,
1400 ArchiveType* archive_type,
1401 bool* delegated_to_existing) {
1402 DCHECK(archive_type);
1403 DCHECK(delegated_to_existing);
1404 const bool system_install = installer_state.system_install();
1405 InstallStatus install_status = UNKNOWN_STATUS;
1407 // Drop to background processing mode if the process was started below the
1408 // normal process priority class.
1409 bool entered_background_mode = AdjustProcessPriority();
1410 VLOG_IF(1, entered_background_mode) << "Entered background processing mode.";
1412 // Create a temp folder where we will unpack Chrome archive. If it fails,
1413 // then we are doomed, so return immediately and no cleanup is required.
1414 SelfCleaningTempDir temp_path;
1415 base::FilePath unpack_path;
1416 if (!CreateTemporaryAndUnpackDirectories(installer_state, &temp_path,
1417 &unpack_path)) {
1418 installer_state.WriteInstallerResult(TEMP_DIR_FAILED,
1419 IDS_INSTALL_TEMP_DIR_FAILED_BASE,
1420 NULL);
1421 return TEMP_DIR_FAILED;
1424 // Uncompress and optionally patch the archive if an uncompressed archive was
1425 // not specified on the command line and a compressed archive is found.
1426 *archive_type = UNKNOWN_ARCHIVE_TYPE;
1427 base::FilePath uncompressed_archive(cmd_line.GetSwitchValuePath(
1428 switches::kUncompressedArchive));
1429 if (uncompressed_archive.empty()) {
1430 scoped_ptr<ArchivePatchHelper> archive_helper(
1431 CreateChromeArchiveHelper(setup_exe, cmd_line, installer_state,
1432 unpack_path));
1433 if (archive_helper) {
1434 VLOG(1) << "Installing Chrome from compressed archive "
1435 << archive_helper->compressed_archive().value();
1436 if (!UncompressAndPatchChromeArchive(original_state,
1437 installer_state,
1438 archive_helper.get(),
1439 archive_type,
1440 &install_status)) {
1441 DCHECK_NE(install_status, UNKNOWN_STATUS);
1442 return install_status;
1444 uncompressed_archive = archive_helper->target();
1445 DCHECK(!uncompressed_archive.empty());
1449 // Check for an uncompressed archive alongside the current executable if one
1450 // was not given or generated.
1451 if (uncompressed_archive.empty())
1452 uncompressed_archive = setup_exe.DirName().Append(kChromeArchive);
1454 if (*archive_type == UNKNOWN_ARCHIVE_TYPE) {
1455 // An archive was not uncompressed or patched above.
1456 if (uncompressed_archive.empty() ||
1457 !base::PathExists(uncompressed_archive)) {
1458 LOG(ERROR) << "Cannot install Chrome without an uncompressed archive.";
1459 installer_state.WriteInstallerResult(
1460 INVALID_ARCHIVE, IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL);
1461 return INVALID_ARCHIVE;
1463 *archive_type = FULL_ARCHIVE_TYPE;
1466 // Unpack the uncompressed archive.
1467 if (LzmaUtil::UnPackArchive(uncompressed_archive.value(),
1468 unpack_path.value(),
1469 NULL)) {
1470 installer_state.WriteInstallerResult(
1471 UNPACKING_FAILED,
1472 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
1473 NULL);
1474 return UNPACKING_FAILED;
1477 VLOG(1) << "unpacked to " << unpack_path.value();
1478 base::FilePath src_path(
1479 unpack_path.Append(kInstallSourceChromeDir));
1480 scoped_ptr<Version>
1481 installer_version(GetMaxVersionFromArchiveDir(src_path));
1482 if (!installer_version.get()) {
1483 LOG(ERROR) << "Did not find any valid version in installer.";
1484 install_status = INVALID_ARCHIVE;
1485 installer_state.WriteInstallerResult(install_status,
1486 IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL);
1487 } else {
1488 VLOG(1) << "version to install: " << installer_version->GetString();
1489 bool proceed_with_installation = true;
1491 if (installer_state.operation() == InstallerState::MULTI_INSTALL) {
1492 // This is a new install of a multi-install product. Rather than give up
1493 // in case a higher version of the binaries (including a single-install
1494 // of Chrome, which can safely be migrated to multi-install by way of
1495 // CheckMultiInstallConditions) is already installed, delegate to the
1496 // installed setup.exe to install the product at hand.
1497 base::FilePath existing_setup_exe;
1498 if (GetExistingHigherInstaller(original_state, system_install,
1499 *installer_version, &existing_setup_exe)) {
1500 VLOG(1) << "Deferring to existing installer.";
1501 installer_state.UpdateStage(DEFERRING_TO_HIGHER_VERSION);
1502 if (DeferToExistingInstall(existing_setup_exe, cmd_line,
1503 installer_state, temp_path.path(),
1504 &install_status)) {
1505 *delegated_to_existing = true;
1506 return install_status;
1511 uint32 higher_products = 0;
1512 COMPILE_ASSERT(
1513 sizeof(higher_products) * 8 > BrowserDistribution::NUM_TYPES,
1514 too_many_distribution_types_);
1515 const Products& products = installer_state.products();
1516 for (Products::const_iterator it = products.begin(); it < products.end();
1517 ++it) {
1518 const Product& product = **it;
1519 const ProductState* product_state =
1520 original_state.GetProductState(system_install,
1521 product.distribution()->GetType());
1522 if (product_state != NULL &&
1523 (product_state->version().CompareTo(*installer_version) > 0)) {
1524 LOG(ERROR) << "Higher version of "
1525 << product.distribution()->GetDisplayName()
1526 << " is already installed.";
1527 higher_products |= (1 << product.distribution()->GetType());
1531 if (higher_products != 0) {
1532 COMPILE_ASSERT(BrowserDistribution::NUM_TYPES == 4,
1533 add_support_for_new_products_here_);
1534 const uint32 kBrowserBit = 1 << BrowserDistribution::CHROME_BROWSER;
1535 int message_id = 0;
1537 proceed_with_installation = false;
1538 install_status = HIGHER_VERSION_EXISTS;
1539 switch (higher_products) {
1540 case kBrowserBit:
1541 message_id = IDS_INSTALL_HIGHER_VERSION_BASE;
1542 break;
1543 default:
1544 message_id = IDS_INSTALL_HIGHER_VERSION_APP_LAUNCHER_BASE;
1545 break;
1548 installer_state.WriteInstallerResult(install_status, message_id, NULL);
1551 proceed_with_installation =
1552 proceed_with_installation &&
1553 CheckGroupPolicySettings(original_state, installer_state,
1554 *installer_version, &install_status);
1556 if (proceed_with_installation) {
1557 // If Google Update is absent at user-level, install it using the
1558 // Google Update installer from an existing system-level installation.
1559 // This is for quick-enable App Host install from a system-level
1560 // Chrome Binaries installation.
1561 if (!system_install && installer_state.ensure_google_update_present()) {
1562 if (!google_update::EnsureUserLevelGoogleUpdatePresent()) {
1563 LOG(ERROR) << "Failed to install Google Update";
1564 proceed_with_installation = false;
1565 install_status = INSTALL_OF_GOOGLE_UPDATE_FAILED;
1566 installer_state.WriteInstallerResult(install_status, 0, NULL);
1572 if (proceed_with_installation) {
1573 base::FilePath prefs_source_path(cmd_line.GetSwitchValueNative(
1574 switches::kInstallerData));
1575 install_status = InstallOrUpdateProduct(
1576 original_state, installer_state, setup_exe, uncompressed_archive,
1577 temp_path.path(), src_path, prefs_source_path, prefs,
1578 *installer_version);
1580 int install_msg_base = IDS_INSTALL_FAILED_BASE;
1581 base::string16 chrome_exe;
1582 base::string16 quoted_chrome_exe;
1583 if (install_status == SAME_VERSION_REPAIR_FAILED) {
1584 install_msg_base = IDS_SAME_VERSION_REPAIR_FAILED_BASE;
1585 } else if (install_status != INSTALL_FAILED) {
1586 if (installer_state.target_path().empty()) {
1587 // If we failed to construct install path, it means the OS call to
1588 // get %ProgramFiles% or %AppData% failed. Report this as failure.
1589 install_msg_base = IDS_INSTALL_OS_ERROR_BASE;
1590 install_status = OS_ERROR;
1591 } else {
1592 chrome_exe = installer_state.target_path().Append(kChromeExe).value();
1593 quoted_chrome_exe = L"\"" + chrome_exe + L"\"";
1594 install_msg_base = 0;
1598 installer_state.UpdateStage(FINISHING);
1600 // Only do Chrome-specific stuff (like launching the browser) if
1601 // Chrome was specifically requested (rather than being upgraded as
1602 // part of a multi-install).
1603 const Product* chrome_install = prefs.install_chrome() ?
1604 installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) :
1605 NULL;
1607 bool do_not_register_for_update_launch = false;
1608 if (chrome_install) {
1609 prefs.GetBool(master_preferences::kDoNotRegisterForUpdateLaunch,
1610 &do_not_register_for_update_launch);
1611 } else {
1612 do_not_register_for_update_launch = true; // Never register.
1615 bool write_chrome_launch_string =
1616 (!do_not_register_for_update_launch &&
1617 install_status != IN_USE_UPDATED);
1619 installer_state.WriteInstallerResult(install_status, install_msg_base,
1620 write_chrome_launch_string ? &quoted_chrome_exe : NULL);
1622 if (install_status == FIRST_INSTALL_SUCCESS) {
1623 VLOG(1) << "First install successful.";
1624 if (chrome_install) {
1625 // We never want to launch Chrome in system level install mode.
1626 bool do_not_launch_chrome = false;
1627 prefs.GetBool(master_preferences::kDoNotLaunchChrome,
1628 &do_not_launch_chrome);
1629 if (!system_install && !do_not_launch_chrome)
1630 chrome_install->LaunchChrome(installer_state.target_path());
1632 } else if ((install_status == NEW_VERSION_UPDATED) ||
1633 (install_status == IN_USE_UPDATED)) {
1634 const Product* chrome = installer_state.FindProduct(
1635 BrowserDistribution::CHROME_BROWSER);
1636 if (chrome != NULL) {
1637 DCHECK_NE(chrome_exe, base::string16());
1638 RemoveChromeLegacyRegistryKeys(chrome->distribution(), chrome_exe);
1644 // There might be an experiment (for upgrade usually) that needs to happen.
1645 // An experiment's outcome can include chrome's uninstallation. If that is
1646 // the case we would not do that directly at this point but in another
1647 // instance of setup.exe
1649 // There is another way to reach this same function if this is a system
1650 // level install. See HandleNonInstallCmdLineOptions().
1652 // If installation failed, use the path to the currently running setup.
1653 // If installation succeeded, use the path to setup in the installer dir.
1654 base::FilePath setup_path(setup_exe);
1655 if (InstallUtil::GetInstallReturnCode(install_status) == 0) {
1656 setup_path = installer_state.GetInstallerDirectory(*installer_version)
1657 .Append(setup_path.BaseName());
1659 const Products& products = installer_state.products();
1660 for (Products::const_iterator it = products.begin(); it < products.end();
1661 ++it) {
1662 const Product& product = **it;
1663 product.LaunchUserExperiment(setup_path, install_status, system_install);
1667 // If installation completed successfully, return the path to the directory
1668 // containing the newly installed setup.exe and uncompressed archive if the
1669 // caller requested it.
1670 if (installer_directory &&
1671 InstallUtil::GetInstallReturnCode(install_status) == 0) {
1672 *installer_directory =
1673 installer_state.GetInstallerDirectory(*installer_version);
1676 // temp_path's dtor will take care of deleting or scheduling itself for
1677 // deletion at reboot when this scope closes.
1678 VLOG(1) << "Deleting temporary directory " << temp_path.path().value();
1680 return install_status;
1683 } // namespace installer
1685 int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance,
1686 wchar_t* command_line, int show_command) {
1687 // Check to see if the CPU is supported before doing anything else. There's
1688 // very little than can safely be accomplished if the CPU isn't supported
1689 // since dependent libraries (e.g., base) may use invalid instructions.
1690 if (!installer::IsProcessorSupported())
1691 return installer::CPU_NOT_SUPPORTED;
1693 // The exit manager is in charge of calling the dtors of singletons.
1694 base::AtExitManager exit_manager;
1695 CommandLine::Init(0, NULL);
1697 // install_util uses chrome paths.
1698 chrome::RegisterPathProvider();
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)) {
1766 NOTREACHED();
1767 return installer::OS_ERROR;
1770 int exit_code = 0;
1771 if (HandleNonInstallCmdLineOptions(
1772 original_state, setup_exe, cmd_line, &installer_state, &exit_code)) {
1773 return 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
1786 // elevation.
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);
1792 return exit_code;
1793 } else {
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)
1807 if (is_uninstall) {
1808 install_status =
1809 UninstallProducts(original_state, installer_state, setup_exe, cmd_line);
1810 } else {
1811 // If --uninstall option is not specified, we assume it is install case.
1812 install_status =
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;
1822 LOG_IF(ERROR,
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;
1839 return return_code;