Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / installer / setup / setup_main.cc
blob571964c0beab98ea45b0c5e66a36fb50c550f109
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/installer/setup/setup_main.h"
7 #include <windows.h>
8 #include <msi.h>
9 #include <shellapi.h>
10 #include <shlobj.h>
12 #include <string>
14 #include "base/at_exit.h"
15 #include "base/basictypes.h"
16 #include "base/command_line.h"
17 #include "base/file_version_info.h"
18 #include "base/files/file_path.h"
19 #include "base/files/file_util.h"
20 #include "base/files/scoped_temp_dir.h"
21 #include "base/memory/scoped_ptr.h"
22 #include "base/path_service.h"
23 #include "base/process/launch.h"
24 #include "base/strings/string16.h"
25 #include "base/strings/string_number_conversions.h"
26 #include "base/strings/string_util.h"
27 #include "base/strings/stringprintf.h"
28 #include "base/strings/utf_string_conversions.h"
29 #include "base/values.h"
30 #include "base/win/registry.h"
31 #include "base/win/scoped_com_initializer.h"
32 #include "base/win/scoped_comptr.h"
33 #include "base/win/scoped_handle.h"
34 #include "base/win/win_util.h"
35 #include "base/win/windows_version.h"
36 #include "breakpad/src/client/windows/handler/exception_handler.h"
37 #include "chrome/common/chrome_constants.h"
38 #include "chrome/common/chrome_paths.h"
39 #include "chrome/common/chrome_switches.h"
40 #include "chrome/installer/setup/archive_patch_helper.h"
41 #include "chrome/installer/setup/install.h"
42 #include "chrome/installer/setup/install_worker.h"
43 #include "chrome/installer/setup/setup_constants.h"
44 #include "chrome/installer/setup/setup_util.h"
45 #include "chrome/installer/setup/uninstall.h"
46 #include "chrome/installer/util/browser_distribution.h"
47 #include "chrome/installer/util/channel_info.h"
48 #include "chrome/installer/util/delete_after_reboot_helper.h"
49 #include "chrome/installer/util/delete_tree_work_item.h"
50 #include "chrome/installer/util/google_update_constants.h"
51 #include "chrome/installer/util/google_update_settings.h"
52 #include "chrome/installer/util/google_update_util.h"
53 #include "chrome/installer/util/helper.h"
54 #include "chrome/installer/util/html_dialog.h"
55 #include "chrome/installer/util/install_util.h"
56 #include "chrome/installer/util/installation_state.h"
57 #include "chrome/installer/util/installation_validator.h"
58 #include "chrome/installer/util/installer_state.h"
59 #include "chrome/installer/util/installer_util_strings.h"
60 #include "chrome/installer/util/l10n_string_util.h"
61 #include "chrome/installer/util/logging_installer.h"
62 #include "chrome/installer/util/lzma_util.h"
63 #include "chrome/installer/util/master_preferences.h"
64 #include "chrome/installer/util/master_preferences_constants.h"
65 #include "chrome/installer/util/self_cleaning_temp_dir.h"
66 #include "chrome/installer/util/shell_util.h"
67 #include "chrome/installer/util/user_experiment.h"
69 #if defined(GOOGLE_CHROME_BUILD)
70 #include "chrome/installer/util/updating_app_registration_data.h"
71 #endif
73 using installer::InstallerState;
74 using installer::InstallationState;
75 using installer::InstallationValidator;
76 using installer::MasterPreferences;
77 using installer::Product;
78 using installer::ProductState;
79 using installer::Products;
81 const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\";
82 const wchar_t kSystemPrincipalSid[] = L"S-1-5-18";
84 const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
85 MiniDumpWithProcessThreadData | // Get PEB and TEB.
86 MiniDumpWithUnloadedModules | // Get unloaded modules when available.
87 MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack.
89 namespace {
91 // Returns NULL if no compressed archive is available for processing, otherwise
92 // returns a patch helper configured to uncompress and patch.
93 scoped_ptr<installer::ArchivePatchHelper> CreateChromeArchiveHelper(
94 const base::FilePath& setup_exe,
95 const base::CommandLine& command_line,
96 const installer::InstallerState& installer_state,
97 const base::FilePath& working_directory) {
98 // A compressed archive is ordinarily given on the command line by the mini
99 // installer. If one was not given, look for chrome.packed.7z next to the
100 // running program.
101 base::FilePath compressed_archive(
102 command_line.GetSwitchValuePath(installer::switches::kInstallArchive));
103 bool compressed_archive_specified = !compressed_archive.empty();
104 if (!compressed_archive_specified) {
105 compressed_archive = setup_exe.DirName().Append(
106 installer::kChromeCompressedArchive);
109 // Fail if no compressed archive is found.
110 if (!base::PathExists(compressed_archive)) {
111 if (compressed_archive_specified) {
112 LOG(ERROR) << installer::switches::kInstallArchive << "="
113 << compressed_archive.value() << " not found.";
115 return scoped_ptr<installer::ArchivePatchHelper>();
118 // chrome.7z is either extracted directly from the compressed archive into the
119 // working dir or is the target of patching in the working dir.
120 base::FilePath target(working_directory.Append(installer::kChromeArchive));
121 DCHECK(!base::PathExists(target));
123 // Specify an empty path for the patch source since it isn't yet known that
124 // one is needed. It will be supplied in UncompressAndPatchChromeArchive if it
125 // is.
126 return scoped_ptr<installer::ArchivePatchHelper>(
127 new installer::ArchivePatchHelper(working_directory,
128 compressed_archive,
129 base::FilePath(),
130 target));
133 // Workhorse for producing an uncompressed archive (chrome.7z) given a
134 // chrome.packed.7z containing either a patch file based on the version of
135 // chrome being updated or the full uncompressed archive. Returns true on
136 // success, in which case |archive_type| is populated based on what was found.
137 // Returns false on failure, in which case |install_status| contains the error
138 // code and the result is written to the registry (via WriteInstallerResult).
139 bool UncompressAndPatchChromeArchive(
140 const installer::InstallationState& original_state,
141 const installer::InstallerState& installer_state,
142 installer::ArchivePatchHelper* archive_helper,
143 installer::ArchiveType* archive_type,
144 installer::InstallStatus* install_status,
145 const base::Version& previous_version) {
146 installer_state.UpdateStage(installer::UNCOMPRESSING);
147 if (!archive_helper->Uncompress(NULL)) {
148 *install_status = installer::UNCOMPRESSION_FAILED;
149 installer_state.WriteInstallerResult(*install_status,
150 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
151 NULL);
152 return false;
155 // Short-circuit if uncompression produced the uncompressed archive rather
156 // than a patch file.
157 if (base::PathExists(archive_helper->target())) {
158 *archive_type = installer::FULL_ARCHIVE_TYPE;
159 return true;
162 // Find the installed version's archive to serve as the source for patching.
163 base::FilePath patch_source(installer::FindArchiveToPatch(original_state,
164 installer_state,
165 previous_version));
166 if (patch_source.empty()) {
167 LOG(ERROR) << "Failed to find archive to patch.";
168 *install_status = installer::DIFF_PATCH_SOURCE_MISSING;
169 installer_state.WriteInstallerResult(*install_status,
170 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
171 NULL);
172 return false;
174 archive_helper->set_patch_source(patch_source);
176 // Try courgette first. Failing that, try bspatch.
177 installer_state.UpdateStage(installer::ENSEMBLE_PATCHING);
178 if (!archive_helper->EnsemblePatch()) {
179 installer_state.UpdateStage(installer::BINARY_PATCHING);
180 if (!archive_helper->BinaryPatch()) {
181 *install_status = installer::APPLY_DIFF_PATCH_FAILED;
182 installer_state.WriteInstallerResult(
183 *install_status, IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, NULL);
184 return false;
188 *archive_type = installer::INCREMENTAL_ARCHIVE_TYPE;
189 return true;
192 // In multi-install, adds all products to |installer_state| that are
193 // multi-installed and must be updated along with the products already present
194 // in |installer_state|.
195 void AddExistingMultiInstalls(const InstallationState& original_state,
196 InstallerState* installer_state) {
197 if (installer_state->is_multi_install()) {
198 for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
199 BrowserDistribution::Type type =
200 static_cast<BrowserDistribution::Type>(i);
202 if (!installer_state->FindProduct(type)) {
203 const ProductState* state =
204 original_state.GetProductState(installer_state->system_install(),
205 type);
206 if ((state != NULL) && state->is_multi_install()) {
207 installer_state->AddProductFromState(type, *state);
208 VLOG(1) << "Product already installed and must be included: "
209 << BrowserDistribution::GetSpecificDistribution(type)->
210 GetDisplayName();
217 // This function is called when --rename-chrome-exe option is specified on
218 // setup.exe command line. This function assumes an in-use update has happened
219 // for Chrome so there should be a file called new_chrome.exe on the file
220 // system and a key called 'opv' in the registry. This function will move
221 // new_chrome.exe to chrome.exe and delete 'opv' key in one atomic operation.
222 // This function also deletes elevation policies associated with the old version
223 // if they exist.
224 installer::InstallStatus RenameChromeExecutables(
225 const InstallationState& original_state,
226 InstallerState* installer_state) {
227 // See what products are already installed in multi mode. When we do the
228 // rename for multi installs, we must update all installations since they
229 // share the binaries.
230 AddExistingMultiInstalls(original_state, installer_state);
231 const base::FilePath &target_path = installer_state->target_path();
232 base::FilePath chrome_exe(target_path.Append(installer::kChromeExe));
233 base::FilePath chrome_new_exe(target_path.Append(installer::kChromeNewExe));
234 base::FilePath chrome_old_exe(target_path.Append(installer::kChromeOldExe));
236 // Create a temporary backup directory on the same volume as chrome.exe so
237 // that moving in-use files doesn't lead to trouble.
238 installer::SelfCleaningTempDir temp_path;
239 if (!temp_path.Initialize(target_path.DirName(),
240 installer::kInstallTempDir)) {
241 PLOG(ERROR) << "Failed to create Temp directory "
242 << target_path.DirName()
243 .Append(installer::kInstallTempDir).value();
244 return installer::RENAME_FAILED;
246 scoped_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList());
247 // Move chrome.exe to old_chrome.exe, then move new_chrome.exe to chrome.exe.
248 install_list->AddMoveTreeWorkItem(chrome_exe.value(),
249 chrome_old_exe.value(),
250 temp_path.path().value(),
251 WorkItem::ALWAYS_MOVE);
252 install_list->AddMoveTreeWorkItem(chrome_new_exe.value(),
253 chrome_exe.value(),
254 temp_path.path().value(),
255 WorkItem::ALWAYS_MOVE);
256 install_list->AddDeleteTreeWorkItem(chrome_new_exe, temp_path.path());
257 // old_chrome.exe is still in use in most cases, so ignore failures here.
258 install_list->AddDeleteTreeWorkItem(chrome_old_exe, temp_path.path())->
259 set_ignore_failure(true);
261 // Add work items to delete the "opv", "cpv", and "cmd" values from all
262 // products we're operating on (which including the multi-install binaries).
263 const Products& products = installer_state->products();
264 HKEY reg_root = installer_state->root_key();
265 base::string16 version_key;
266 for (Products::const_iterator it = products.begin(); it < products.end();
267 ++it) {
268 version_key = (*it)->distribution()->GetVersionKey();
269 install_list->AddDeleteRegValueWorkItem(reg_root,
270 version_key,
271 KEY_WOW64_32KEY,
272 google_update::kRegOldVersionField);
273 install_list->AddDeleteRegValueWorkItem(
274 reg_root,
275 version_key,
276 KEY_WOW64_32KEY,
277 google_update::kRegCriticalVersionField);
278 install_list->AddDeleteRegValueWorkItem(reg_root,
279 version_key,
280 KEY_WOW64_32KEY,
281 google_update::kRegRenameCmdField);
283 installer::InstallStatus ret = installer::RENAME_SUCCESSFUL;
284 if (!install_list->Do()) {
285 LOG(ERROR) << "Renaming of executables failed. Rolling back any changes.";
286 install_list->Rollback();
287 ret = installer::RENAME_FAILED;
289 // temp_path's dtor will take care of deleting or scheduling itself for
290 // deletion at reboot when this scope closes.
291 VLOG(1) << "Deleting temporary directory " << temp_path.path().value();
293 return ret;
296 // If only the binaries are being updated, fail.
297 // If any product is being installed in single-mode that already exists in
298 // multi-mode, fail.
299 bool CheckMultiInstallConditions(const InstallationState& original_state,
300 InstallerState* installer_state,
301 installer::InstallStatus* status) {
302 const Products& products = installer_state->products();
303 DCHECK(products.size());
305 const bool system_level = installer_state->system_install();
307 if (installer_state->is_multi_install()) {
308 const Product* chrome =
309 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
310 const Product* binaries =
311 installer_state->FindProduct(BrowserDistribution::CHROME_BINARIES);
312 const ProductState* chrome_state =
313 original_state.GetProductState(system_level,
314 BrowserDistribution::CHROME_BROWSER);
316 if (binaries) {
317 if (products.size() == 1) {
318 // There are no products aside from the binaries, so there is no update
319 // to be applied. This can happen after multi-install Chrome Frame is
320 // migrated to single-install. This is treated as an update failure
321 // unless the binaries are not in-use, in which case they will be
322 // uninstalled and success will be reported (see handling in wWinMain).
323 VLOG(1) << "No products to be updated.";
324 *status = installer::UNUSED_BINARIES;
325 installer_state->WriteInstallerResult(*status, 0, NULL);
326 return false;
328 } else {
329 // This will only be hit if --multi-install is given with no products.
330 return true;
333 if (!chrome && chrome_state) {
334 // A product other than Chrome is being installed in multi-install mode,
335 // and Chrome is already present. Add Chrome to the set of products
336 // (making it multi-install in the process) so that it is updated, too.
337 scoped_ptr<Product> multi_chrome(new Product(
338 BrowserDistribution::GetSpecificDistribution(
339 BrowserDistribution::CHROME_BROWSER)));
340 multi_chrome->SetOption(installer::kOptionMultiInstall, true);
341 chrome = installer_state->AddProduct(&multi_chrome);
342 VLOG(1) << "Upgrading existing Chrome browser in multi-install mode.";
344 } // else migrate multi-install Chrome to single-install.
346 return true;
349 // Checks for compatibility between the current state of the system and the
350 // desired operation. Also applies policy that mutates the desired operation;
351 // specifically, the |installer_state| object.
352 // Also blocks simultaneous user-level and system-level installs. In the case
353 // of trying to install user-level Chrome when system-level exists, the
354 // existing system-level Chrome is launched.
355 // When the pre-install conditions are not satisfied, the result is written to
356 // the registry (via WriteInstallerResult), |status| is set appropriately, and
357 // false is returned.
358 bool CheckPreInstallConditions(const InstallationState& original_state,
359 InstallerState* installer_state,
360 installer::InstallStatus* status) {
361 // See what products are already installed in multi mode. When we do multi
362 // installs, we must upgrade all installations since they share the binaries.
363 AddExistingMultiInstalls(original_state, installer_state);
365 if (!CheckMultiInstallConditions(original_state, installer_state, status)) {
366 DCHECK_NE(*status, installer::UNKNOWN_STATUS);
367 return false;
370 const Products& products = installer_state->products();
371 if (products.empty()) {
372 // We haven't been given any products on which to operate.
373 LOG(ERROR)
374 << "Not given any products to install and no products found to update.";
375 *status = installer::CHROME_NOT_INSTALLED;
376 installer_state->WriteInstallerResult(*status,
377 IDS_INSTALL_NO_PRODUCTS_TO_UPDATE_BASE, NULL);
378 return false;
381 if (!installer_state->system_install()) {
382 // This is a user-level installation. Make sure that we are not installing
383 // on top of an existing system-level installation.
384 for (Products::const_iterator it = products.begin(); it < products.end();
385 ++it) {
386 const Product& product = **it;
387 BrowserDistribution* browser_dist = product.distribution();
389 // Skip over the binaries, as it's okay for them to be at both levels
390 // for different products.
391 if (browser_dist->GetType() == BrowserDistribution::CHROME_BINARIES)
392 continue;
394 const ProductState* user_level_product_state =
395 original_state.GetProductState(false, browser_dist->GetType());
396 const ProductState* system_level_product_state =
397 original_state.GetProductState(true, browser_dist->GetType());
399 // Allow upgrades to proceed so that out-of-date versions are not left
400 // around.
401 if (user_level_product_state)
402 continue;
404 // This is a new user-level install...
406 if (system_level_product_state) {
407 // ... and the product already exists at system-level.
408 LOG(ERROR) << "Already installed version "
409 << system_level_product_state->version().GetString()
410 << " at system-level conflicts with this one at user-level.";
411 if (product.is_chrome()) {
412 // Instruct Google Update to launch the existing system-level Chrome.
413 // There should be no error dialog.
414 base::FilePath install_path(installer::GetChromeInstallPath(
415 true, // system
416 browser_dist));
417 if (install_path.empty()) {
418 // Give up if we failed to construct the install path.
419 *status = installer::OS_ERROR;
420 installer_state->WriteInstallerResult(*status,
421 IDS_INSTALL_OS_ERROR_BASE,
422 NULL);
423 } else {
424 *status = installer::EXISTING_VERSION_LAUNCHED;
425 base::FilePath chrome_exe =
426 install_path.Append(installer::kChromeExe);
427 base::CommandLine cmd(chrome_exe);
428 cmd.AppendSwitch(switches::kForceFirstRun);
429 installer_state->WriteInstallerResult(*status, 0, NULL);
430 VLOG(1) << "Launching existing system-level chrome instead.";
431 base::LaunchProcess(cmd, base::LaunchOptions());
433 } else {
434 // It's no longer possible for |product| to be anything other than
435 // Chrome.
436 NOTREACHED();
438 return false;
443 return true;
446 // Initializes |temp_path| to "Temp" within the target directory, and
447 // |unpack_path| to a random directory beginning with "source" within
448 // |temp_path|. Returns false on error.
449 bool CreateTemporaryAndUnpackDirectories(
450 const InstallerState& installer_state,
451 installer::SelfCleaningTempDir* temp_path,
452 base::FilePath* unpack_path) {
453 DCHECK(temp_path && unpack_path);
455 if (!temp_path->Initialize(installer_state.target_path().DirName(),
456 installer::kInstallTempDir)) {
457 PLOG(ERROR) << "Could not create temporary path.";
458 return false;
460 VLOG(1) << "Created path " << temp_path->path().value();
462 if (!base::CreateTemporaryDirInDir(temp_path->path(),
463 installer::kInstallSourceDir,
464 unpack_path)) {
465 PLOG(ERROR) << "Could not create temporary path for unpacked archive.";
466 return false;
469 return true;
472 installer::InstallStatus UninstallProduct(
473 const InstallationState& original_state,
474 const InstallerState& installer_state,
475 const base::FilePath& setup_exe,
476 const base::CommandLine& cmd_line,
477 bool remove_all,
478 bool force_uninstall,
479 const Product& product) {
480 const ProductState* product_state =
481 original_state.GetProductState(installer_state.system_install(),
482 product.distribution()->GetType());
483 if (product_state != NULL) {
484 VLOG(1) << "version on the system: "
485 << product_state->version().GetString();
486 } else if (!force_uninstall) {
487 LOG(ERROR) << product.distribution()->GetDisplayName()
488 << " not found for uninstall.";
489 return installer::CHROME_NOT_INSTALLED;
492 return installer::UninstallProduct(
493 original_state, installer_state, setup_exe, product, remove_all,
494 force_uninstall, cmd_line);
497 installer::InstallStatus UninstallProducts(
498 const InstallationState& original_state,
499 const InstallerState& installer_state,
500 const base::FilePath& setup_exe,
501 const base::CommandLine& cmd_line) {
502 const Products& products = installer_state.products();
504 // System-level Chrome will be launched via this command if its program gets
505 // set below.
506 base::CommandLine system_level_cmd(base::CommandLine::NO_PROGRAM);
508 const Product* chrome =
509 installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER);
510 if (chrome) {
511 // InstallerState::Initialize always puts Chrome first, and we rely on that
512 // here for this reason: if Chrome is in-use, the user will be prompted to
513 // confirm uninstallation. Upon cancel, we should not continue with the
514 // other products.
515 DCHECK(products[0]->is_chrome());
517 if (cmd_line.HasSwitch(installer::switches::kSelfDestruct) &&
518 !installer_state.system_install()) {
519 BrowserDistribution* dist = chrome->distribution();
520 const base::FilePath system_exe_path(
521 installer::GetChromeInstallPath(true, dist)
522 .Append(installer::kChromeExe));
523 system_level_cmd.SetProgram(system_exe_path);
526 if (installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)) {
527 // Chrome Binaries should be last; if something else is cancelled, they
528 // should stay.
529 DCHECK(products[products.size() - 1]->is_chrome_binaries());
532 installer::InstallStatus install_status = installer::UNINSTALL_SUCCESSFUL;
533 installer::InstallStatus prod_status = installer::UNKNOWN_STATUS;
534 const bool force = cmd_line.HasSwitch(installer::switches::kForceUninstall);
535 const bool remove_all = !cmd_line.HasSwitch(
536 installer::switches::kDoNotRemoveSharedItems);
538 for (Products::const_iterator it = products.begin();
539 install_status != installer::UNINSTALL_CANCELLED && it < products.end();
540 ++it) {
541 prod_status = UninstallProduct(original_state, installer_state, setup_exe,
542 cmd_line, remove_all, force, **it);
543 if (prod_status != installer::UNINSTALL_SUCCESSFUL)
544 install_status = prod_status;
547 installer::CleanUpInstallationDirectoryAfterUninstall(
548 original_state, installer_state, setup_exe, &install_status);
550 // The app and vendor dirs may now be empty. Make a last-ditch attempt to
551 // delete them.
552 installer::DeleteChromeDirectoriesIfEmpty(installer_state.target_path());
554 // Trigger Active Setup if it was requested for the chrome product. This needs
555 // to be done after the UninstallProduct calls as some of them might
556 // otherwise terminate the process launched by TriggerActiveSetupCommand().
557 if (chrome && cmd_line.HasSwitch(installer::switches::kTriggerActiveSetup))
558 InstallUtil::TriggerActiveSetupCommand();
560 if (!system_level_cmd.GetProgram().empty())
561 base::LaunchProcess(system_level_cmd, base::LaunchOptions());
563 // Tell Google Update that an uninstall has taken place.
564 // Ignore the return value: success or failure of Google Update
565 // has no bearing on the success or failure of Chrome's uninstallation.
566 google_update::UninstallGoogleUpdate(installer_state.system_install());
568 return install_status;
571 // Uninstall the binaries if they are the only product present and they're not
572 // in-use.
573 void UninstallBinariesIfUnused(
574 const InstallationState& original_state,
575 const InstallerState& installer_state,
576 installer::InstallStatus* install_status) {
577 // Early exit if the binaries are still in use.
578 if (*install_status != installer::UNUSED_BINARIES ||
579 installer_state.AreBinariesInUse(original_state)) {
580 return;
583 LOG(INFO) << "Uninstalling unused binaries";
584 installer_state.UpdateStage(installer::UNINSTALLING_BINARIES);
586 // Simulate the uninstall as coming from the installed version.
587 const ProductState* binaries_state =
588 original_state.GetProductState(installer_state.system_install(),
589 BrowserDistribution::CHROME_BINARIES);
590 const base::CommandLine& uninstall_cmd(binaries_state->uninstall_command());
591 MasterPreferences uninstall_prefs(uninstall_cmd);
592 InstallerState uninstall_state;
593 uninstall_state.Initialize(uninstall_cmd, uninstall_prefs, original_state);
595 *install_status = UninstallProducts(original_state, uninstall_state,
596 uninstall_cmd.GetProgram(),
597 uninstall_cmd);
599 // Report that the binaries were uninstalled if they were. This translates
600 // into a successful install return code.
601 if (IsUninstallSuccess(*install_status)) {
602 *install_status = installer::UNUSED_BINARIES_UNINSTALLED;
603 installer_state.WriteInstallerResult(*install_status, 0, NULL);
607 // This function is a short-term repair for the damage documented in
608 // http://crbug.com/456602. Briefly: canaries from 42.0.2293.0 through
609 // 42.0.2302.0 (inclusive) contained a bug that broke normal Chrome installed at
610 // user-level. This function detects the broken state during a canary update and
611 // repairs it by calling on the existing Chrome's installer to fix itself.
612 // TODO(grt): Remove this once the majority of impacted canary clients have
613 // picked it up.
614 void RepairChromeIfBroken(const InstallationState& original_state,
615 const InstallerState& installer_state) {
616 #if !defined(GOOGLE_CHROME_BUILD)
617 // Chromium does not support SxS installation, so there is no work to be done.
618 return;
619 #else // GOOGLE_CHROME_BUILD
620 // Nothing to do if not a per-user SxS install/update.
621 if (!InstallUtil::IsChromeSxSProcess() ||
622 installer_state.system_install() ||
623 installer_state.is_multi_install()) {
624 return;
627 // When running a side-by-side install, BrowserDistribution provides no way
628 // to create or access a GoogleChromeDistribution (by design).
629 static const base::char16 kChromeGuid[] =
630 L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
631 static const base::char16 kChromeBinariesGuid[] =
632 L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}";
634 UpdatingAppRegistrationData chrome_reg_data(kChromeGuid);
635 UpdatingAppRegistrationData binaries_reg_data(kChromeBinariesGuid);
637 // Nothing to do if the binaries are installed.
638 base::win::RegKey key;
639 base::string16 version_str;
640 if (key.Open(HKEY_CURRENT_USER,
641 binaries_reg_data.GetVersionKey().c_str(),
642 KEY_QUERY_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS &&
643 key.ReadValue(google_update::kRegVersionField,
644 &version_str) == ERROR_SUCCESS) {
645 return;
648 // Nothing to do if Chrome is not installed.
649 if (key.Open(HKEY_CURRENT_USER,
650 chrome_reg_data.GetVersionKey().c_str(),
651 KEY_QUERY_VALUE | KEY_WOW64_32KEY) != ERROR_SUCCESS ||
652 key.ReadValue(google_update::kRegVersionField,
653 &version_str) != ERROR_SUCCESS) {
654 return;
657 // Nothing to do if Chrome is not multi-install.
658 base::string16 setup_args;
659 if (key.Open(HKEY_CURRENT_USER,
660 chrome_reg_data.GetStateKey().c_str(),
661 KEY_QUERY_VALUE | KEY_WOW64_32KEY) != ERROR_SUCCESS) {
662 LOG(ERROR) << "RepairChrome: Failed to open Chrome's ClientState key.";
663 return;
665 if (key.ReadValue(installer::kUninstallArgumentsField,
666 &setup_args) != ERROR_SUCCESS) {
667 LOG(ERROR) << "RepairChrome: Failed to read Chrome's UninstallArguments.";
668 return;
670 if (setup_args.find(base::UTF8ToUTF16(installer::switches::kMultiInstall)) ==
671 base::string16::npos) {
672 LOG(INFO) << "RepairChrome: Not repairing single-install Chrome.";
673 return;
676 // Generate a command line to run Chrome's installer.
677 base::string16 setup_path;
678 if (key.ReadValue(installer::kUninstallStringField,
679 &setup_path) != ERROR_SUCCESS) {
680 LOG(ERROR) << "RepairChrome: Failed to read Chrome's UninstallString.";
681 return;
684 // Replace --uninstall with --do-not-launch-chrome to cause chrome to
685 // self-repair.
686 base::ReplaceFirstSubstringAfterOffset(
687 &setup_args, 0, base::UTF8ToUTF16(installer::switches::kUninstall),
688 base::UTF8ToUTF16(installer::switches::kDoNotLaunchChrome));
689 base::CommandLine setup_command(base::CommandLine::NO_PROGRAM);
690 InstallUtil::ComposeCommandLine(setup_path, setup_args, &setup_command);
692 // Run Chrome's installer so that it repairs itself. Break away from any job
693 // in which this operation is running so that Google Update doesn't wait
694 // around for the repair. Retry once without the attempt to break away in case
695 // this process doesn't have JOB_OBJECT_LIMIT_BREAKAWAY_OK.
696 base::LaunchOptions launch_options;
697 launch_options.force_breakaway_from_job_ = true;
698 while (true) {
699 if (base::LaunchProcess(setup_command, launch_options).IsValid()) {
700 LOG(INFO) << "RepairChrome: Launched repair command \""
701 << setup_command.GetCommandLineString() << "\"";
702 break;
703 } else {
704 PLOG(ERROR) << "RepairChrome: Failed launching repair command \""
705 << setup_command.GetCommandLineString() << "\"";
706 if (launch_options.force_breakaway_from_job_) {
707 LOG(ERROR) << "RepairChrome: Will retry without breakaway.";
708 launch_options.force_breakaway_from_job_ = false;
709 } else {
710 break;
714 #endif // GOOGLE_CHROME_BUILD
717 installer::InstallStatus InstallProducts(
718 const InstallationState& original_state,
719 const base::FilePath& setup_exe,
720 const base::CommandLine& cmd_line,
721 const MasterPreferences& prefs,
722 InstallerState* installer_state,
723 base::FilePath* installer_directory) {
724 DCHECK(installer_state);
725 const bool system_install = installer_state->system_install();
726 installer::InstallStatus install_status = installer::UNKNOWN_STATUS;
727 installer::ArchiveType archive_type = installer::UNKNOWN_ARCHIVE_TYPE;
728 bool delegated_to_existing = false;
729 installer_state->UpdateStage(installer::PRECONDITIONS);
730 // The stage provides more fine-grained information than -multifail, so remove
731 // the -multifail suffix from the Google Update "ap" value.
732 BrowserDistribution::GetSpecificDistribution(installer_state->state_type())->
733 UpdateInstallStatus(system_install, archive_type, install_status);
734 if (CheckPreInstallConditions(original_state, installer_state,
735 &install_status)) {
736 VLOG(1) << "Installing to " << installer_state->target_path().value();
737 install_status = InstallProductsHelper(
738 original_state, setup_exe, cmd_line, prefs, *installer_state,
739 installer_directory, &archive_type, &delegated_to_existing);
740 } else {
741 // CheckPreInstallConditions must set the status on failure.
742 DCHECK_NE(install_status, installer::UNKNOWN_STATUS);
745 // Delete the master preferences file if present. Note that we do not care
746 // about rollback here and we schedule for deletion on reboot if the delete
747 // fails. As such, we do not use DeleteTreeWorkItem.
748 if (cmd_line.HasSwitch(installer::switches::kInstallerData)) {
749 base::FilePath prefs_path(cmd_line.GetSwitchValuePath(
750 installer::switches::kInstallerData));
751 if (!base::DeleteFile(prefs_path, false)) {
752 LOG(ERROR) << "Failed deleting master preferences file "
753 << prefs_path.value()
754 << ", scheduling for deletion after reboot.";
755 ScheduleFileSystemEntityForDeletion(prefs_path);
759 // Early exit if this setup.exe delegated to another, since that one would
760 // have taken care of UpdateInstallStatus and UpdateStage.
761 if (delegated_to_existing)
762 return install_status;
764 const Products& products = installer_state->products();
765 for (Products::const_iterator it = products.begin(); it < products.end();
766 ++it) {
767 (*it)->distribution()->UpdateInstallStatus(
768 system_install, archive_type, install_status);
771 UninstallBinariesIfUnused(original_state, *installer_state, &install_status);
773 RepairChromeIfBroken(original_state, *installer_state);
775 installer_state->UpdateStage(installer::NO_STAGE);
776 return install_status;
779 installer::InstallStatus ShowEULADialog(const base::string16& inner_frame) {
780 VLOG(1) << "About to show EULA";
781 base::string16 eula_path = installer::GetLocalizedEulaResource();
782 if (eula_path.empty()) {
783 LOG(ERROR) << "No EULA path available";
784 return installer::EULA_REJECTED;
786 // Newer versions of the caller pass an inner frame parameter that must
787 // be given to the html page being launched.
788 installer::EulaHTMLDialog dlg(eula_path, inner_frame);
789 installer::EulaHTMLDialog::Outcome outcome = dlg.ShowModal();
790 if (installer::EulaHTMLDialog::REJECTED == outcome) {
791 LOG(ERROR) << "EULA rejected or EULA failure";
792 return installer::EULA_REJECTED;
794 if (installer::EulaHTMLDialog::ACCEPTED_OPT_IN == outcome) {
795 VLOG(1) << "EULA accepted (opt-in)";
796 return installer::EULA_ACCEPTED_OPT_IN;
798 VLOG(1) << "EULA accepted (no opt-in)";
799 return installer::EULA_ACCEPTED;
802 // Creates the sentinel indicating that the EULA was required and has been
803 // accepted.
804 bool CreateEULASentinel(BrowserDistribution* dist) {
805 base::FilePath eula_sentinel;
806 if (!InstallUtil::GetEULASentinelFilePath(&eula_sentinel))
807 return false;
809 return (base::CreateDirectory(eula_sentinel.DirName()) &&
810 base::WriteFile(eula_sentinel, "", 0) != -1);
813 void ActivateMetroChrome() {
814 // Check to see if we're per-user or not. Need to do this since we may
815 // not have been invoked with --system-level even for a machine install.
816 base::FilePath exe_path;
817 PathService::Get(base::FILE_EXE, &exe_path);
818 bool is_per_user_install = InstallUtil::IsPerUserInstall(exe_path);
820 base::string16 app_model_id = ShellUtil::GetBrowserModelId(
821 BrowserDistribution::GetDistribution(), is_per_user_install);
823 base::win::ScopedComPtr<IApplicationActivationManager> activator;
824 HRESULT hr = activator.CreateInstance(CLSID_ApplicationActivationManager);
825 if (SUCCEEDED(hr)) {
826 DWORD pid = 0;
827 hr = activator->ActivateApplication(
828 app_model_id.c_str(), L"open", AO_NONE, &pid);
831 LOG_IF(ERROR, FAILED(hr)) << "Tried and failed to launch Metro Chrome. "
832 << "hr=" << std::hex << hr;
835 installer::InstallStatus RegisterDevChrome(
836 const InstallationState& original_state,
837 const InstallerState& installer_state,
838 const base::FilePath& setup_exe,
839 const base::CommandLine& cmd_line) {
840 BrowserDistribution* chrome_dist =
841 BrowserDistribution::GetSpecificDistribution(
842 BrowserDistribution::CHROME_BROWSER);
844 // Only proceed with registering a dev chrome if no real Chrome installation
845 // of the same distribution are present on this system.
846 const ProductState* existing_chrome =
847 original_state.GetProductState(false,
848 BrowserDistribution::CHROME_BROWSER);
849 if (!existing_chrome) {
850 existing_chrome =
851 original_state.GetProductState(true, BrowserDistribution::CHROME_BROWSER);
853 if (existing_chrome) {
854 static const wchar_t kPleaseUninstallYourChromeMessage[] =
855 L"You already have a full-installation (non-dev) of %1ls, please "
856 L"uninstall it first using Add/Remove Programs in the control panel.";
857 base::string16 name(chrome_dist->GetDisplayName());
858 base::string16 message(
859 base::StringPrintf(kPleaseUninstallYourChromeMessage, name.c_str()));
861 LOG(ERROR) << "Aborting operation: another installation of " << name
862 << " was found, as a last resort (if the product is not present "
863 "in Add/Remove Programs), try executing: "
864 << existing_chrome->uninstall_command().GetCommandLineString();
865 MessageBox(NULL, message.c_str(), NULL, MB_ICONERROR);
866 return installer::INSTALL_FAILED;
869 base::FilePath chrome_exe(
870 cmd_line.GetSwitchValuePath(installer::switches::kRegisterDevChrome));
871 if (chrome_exe.empty())
872 chrome_exe = setup_exe.DirName().Append(installer::kChromeExe);
873 if (!chrome_exe.IsAbsolute())
874 chrome_exe = base::MakeAbsoluteFilePath(chrome_exe);
876 installer::InstallStatus status = installer::FIRST_INSTALL_SUCCESS;
877 if (base::PathExists(chrome_exe)) {
878 Product chrome(chrome_dist);
880 // Create the Start menu shortcut and pin it to the Win7+ taskbar and Win10+
881 // start menu.
882 ShellUtil::ShortcutProperties shortcut_properties(ShellUtil::CURRENT_USER);
883 chrome.AddDefaultShortcutProperties(chrome_exe, &shortcut_properties);
884 if (InstallUtil::ShouldInstallMetroProperties())
885 shortcut_properties.set_dual_mode(true);
886 shortcut_properties.set_pin_to_taskbar(true);
887 shortcut_properties.set_pin_to_start(true);
888 ShellUtil::CreateOrUpdateShortcut(
889 ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR, chrome_dist,
890 shortcut_properties, ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS);
892 // Register Chrome at user-level and make it default.
893 scoped_ptr<WorkItemList> delegate_execute_list(
894 WorkItem::CreateWorkItemList());
895 installer::AddDelegateExecuteWorkItems(
896 installer_state, chrome_exe.DirName(), Version(), chrome,
897 delegate_execute_list.get());
898 delegate_execute_list->Do();
899 if (ShellUtil::CanMakeChromeDefaultUnattended()) {
900 ShellUtil::MakeChromeDefault(chrome_dist, ShellUtil::CURRENT_USER,
901 chrome_exe, true);
902 } else {
903 ShellUtil::ShowMakeChromeDefaultSystemUI(chrome_dist, chrome_exe);
905 } else {
906 LOG(ERROR) << "Path not found: " << chrome_exe.value();
907 status = installer::INSTALL_FAILED;
909 return status;
912 // This method processes any command line options that make setup.exe do
913 // various tasks other than installation (renaming chrome.exe, showing eula
914 // among others). This function returns true if any such command line option
915 // has been found and processed (so setup.exe should exit at that point).
916 bool HandleNonInstallCmdLineOptions(const InstallationState& original_state,
917 const base::FilePath& setup_exe,
918 const base::CommandLine& cmd_line,
919 InstallerState* installer_state,
920 int* exit_code) {
921 // TODO(gab): Add a local |status| variable which each block below sets;
922 // only determine the |exit_code| from |status| at the end (this will allow
923 // this method to validate that
924 // (!handled || status != installer::UNKNOWN_STATUS)).
925 bool handled = true;
926 // TODO(tommi): Split these checks up into functions and use a data driven
927 // map of switch->function.
928 if (cmd_line.HasSwitch(installer::switches::kUpdateSetupExe)) {
929 installer::InstallStatus status = installer::SETUP_PATCH_FAILED;
930 // If --update-setup-exe command line option is given, we apply the given
931 // patch to current exe, and store the resulting binary in the path
932 // specified by --new-setup-exe. But we need to first unpack the file
933 // given in --update-setup-exe.
934 base::ScopedTempDir temp_path;
935 if (!temp_path.CreateUniqueTempDir()) {
936 PLOG(ERROR) << "Could not create temporary path.";
937 } else {
938 base::FilePath compressed_archive(cmd_line.GetSwitchValuePath(
939 installer::switches::kUpdateSetupExe));
940 VLOG(1) << "Opening archive " << compressed_archive.value();
941 if (installer::ArchivePatchHelper::UncompressAndPatch(
942 temp_path.path(),
943 compressed_archive,
944 setup_exe,
945 cmd_line.GetSwitchValuePath(installer::switches::kNewSetupExe))) {
946 status = installer::NEW_VERSION_UPDATED;
948 if (!temp_path.Delete()) {
949 // PLOG would be nice, but Delete() doesn't leave a meaningful value in
950 // the Windows last-error code.
951 LOG(WARNING) << "Scheduling temporary path " << temp_path.path().value()
952 << " for deletion at reboot.";
953 ScheduleDirectoryForDeletion(temp_path.path());
957 *exit_code = InstallUtil::GetInstallReturnCode(status);
958 if (*exit_code) {
959 LOG(WARNING) << "setup.exe patching failed.";
960 installer_state->WriteInstallerResult(
961 status, IDS_SETUP_PATCH_FAILED_BASE, NULL);
963 // We will be exiting normally, so clear the stage indicator.
964 installer_state->UpdateStage(installer::NO_STAGE);
965 } else if (cmd_line.HasSwitch(installer::switches::kShowEula)) {
966 // Check if we need to show the EULA. If it is passed as a command line
967 // then the dialog is shown and regardless of the outcome setup exits here.
968 base::string16 inner_frame =
969 cmd_line.GetSwitchValueNative(installer::switches::kShowEula);
970 *exit_code = ShowEULADialog(inner_frame);
972 if (installer::EULA_REJECTED != *exit_code) {
973 if (GoogleUpdateSettings::SetEULAConsent(
974 original_state, BrowserDistribution::GetDistribution(), true)) {
975 CreateEULASentinel(BrowserDistribution::GetDistribution());
977 // For a metro-originated launch, we now need to launch back into metro.
978 if (cmd_line.HasSwitch(installer::switches::kShowEulaForMetro))
979 ActivateMetroChrome();
981 } else if (cmd_line.HasSwitch(installer::switches::kConfigureUserSettings)) {
982 // NOTE: Should the work done here, on kConfigureUserSettings, change:
983 // kActiveSetupVersion in install_worker.cc needs to be increased for Active
984 // Setup to invoke this again for all users of this install.
985 const Product* chrome_install =
986 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
987 installer::InstallStatus status = installer::INVALID_STATE_FOR_OPTION;
988 if (chrome_install && installer_state->system_install()) {
989 bool force =
990 cmd_line.HasSwitch(installer::switches::kForceConfigureUserSettings);
991 installer::HandleActiveSetupForBrowser(installer_state->target_path(),
992 *chrome_install, force);
993 status = installer::INSTALL_REPAIRED;
994 } else {
995 LOG(DFATAL) << "chrome_install:" << chrome_install
996 << ", system_install:" << installer_state->system_install();
998 *exit_code = InstallUtil::GetInstallReturnCode(status);
999 } else if (cmd_line.HasSwitch(installer::switches::kRegisterDevChrome)) {
1000 installer::InstallStatus status = RegisterDevChrome(
1001 original_state, *installer_state, setup_exe, cmd_line);
1002 *exit_code = InstallUtil::GetInstallReturnCode(status);
1003 } else if (cmd_line.HasSwitch(installer::switches::kRegisterChromeBrowser)) {
1004 installer::InstallStatus status = installer::UNKNOWN_STATUS;
1005 const Product* chrome_install =
1006 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
1007 if (chrome_install) {
1008 // If --register-chrome-browser option is specified, register all
1009 // Chrome protocol/file associations, as well as register it as a valid
1010 // browser for Start Menu->Internet shortcut. This switch will also
1011 // register Chrome as a valid handler for a set of URL protocols that
1012 // Chrome may become the default handler for, either by the user marking
1013 // Chrome as the default browser, through the Windows Default Programs
1014 // control panel settings, or through website use of
1015 // registerProtocolHandler. These protocols are found in
1016 // ShellUtil::kPotentialProtocolAssociations.
1017 // The --register-url-protocol will additionally register Chrome as a
1018 // potential handler for the supplied protocol, and is used if a website
1019 // registers a handler for a protocol not found in
1020 // ShellUtil::kPotentialProtocolAssociations.
1021 // These options should only be used when setup.exe is launched with admin
1022 // rights. We do not make any user specific changes with this option.
1023 DCHECK(IsUserAnAdmin());
1024 base::FilePath chrome_exe(cmd_line.GetSwitchValuePath(
1025 installer::switches::kRegisterChromeBrowser));
1026 base::string16 suffix;
1027 if (cmd_line.HasSwitch(
1028 installer::switches::kRegisterChromeBrowserSuffix)) {
1029 suffix = cmd_line.GetSwitchValueNative(
1030 installer::switches::kRegisterChromeBrowserSuffix);
1032 if (cmd_line.HasSwitch(installer::switches::kRegisterURLProtocol)) {
1033 base::string16 protocol = cmd_line.GetSwitchValueNative(
1034 installer::switches::kRegisterURLProtocol);
1035 // ShellUtil::RegisterChromeForProtocol performs all registration
1036 // done by ShellUtil::RegisterChromeBrowser, as well as registering
1037 // with Windows as capable of handling the supplied protocol.
1038 if (ShellUtil::RegisterChromeForProtocol(
1039 chrome_install->distribution(), chrome_exe, suffix, protocol,
1040 false))
1041 status = installer::IN_USE_UPDATED;
1042 } else {
1043 if (ShellUtil::RegisterChromeBrowser(chrome_install->distribution(),
1044 chrome_exe, suffix, false))
1045 status = installer::IN_USE_UPDATED;
1047 } else {
1048 LOG(DFATAL) << "Can't register browser - Chrome distribution not found";
1050 *exit_code = InstallUtil::GetInstallReturnCode(status);
1051 } else if (cmd_line.HasSwitch(installer::switches::kRenameChromeExe)) {
1052 // If --rename-chrome-exe is specified, we want to rename the executables
1053 // and exit.
1054 *exit_code = RenameChromeExecutables(original_state, installer_state);
1055 } else if (cmd_line.HasSwitch(
1056 installer::switches::kRemoveChromeRegistration)) {
1057 // This is almost reverse of --register-chrome-browser option above.
1058 // Here we delete Chrome browser registration. This option should only
1059 // be used when setup.exe is launched with admin rights. We do not
1060 // make any user specific changes in this option.
1061 base::string16 suffix;
1062 if (cmd_line.HasSwitch(
1063 installer::switches::kRegisterChromeBrowserSuffix)) {
1064 suffix = cmd_line.GetSwitchValueNative(
1065 installer::switches::kRegisterChromeBrowserSuffix);
1067 installer::InstallStatus tmp = installer::UNKNOWN_STATUS;
1068 const Product* chrome_install =
1069 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
1070 DCHECK(chrome_install);
1071 if (chrome_install) {
1072 installer::DeleteChromeRegistrationKeys(*installer_state,
1073 chrome_install->distribution(), HKEY_LOCAL_MACHINE, suffix, &tmp);
1075 *exit_code = tmp;
1076 } else if (cmd_line.HasSwitch(installer::switches::kOnOsUpgrade)) {
1077 const Product* chrome_install =
1078 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
1079 installer::InstallStatus status = installer::INVALID_STATE_FOR_OPTION;
1080 if (chrome_install) {
1081 installer::HandleOsUpgradeForBrowser(*installer_state,
1082 *chrome_install);
1083 status = installer::INSTALL_REPAIRED;
1084 } else {
1085 LOG(DFATAL) << "Chrome product not found.";
1087 *exit_code = InstallUtil::GetInstallReturnCode(status);
1088 } else if (cmd_line.HasSwitch(installer::switches::kInactiveUserToast)) {
1089 // Launch the inactive user toast experiment.
1090 int flavor = -1;
1091 base::StringToInt(cmd_line.GetSwitchValueNative(
1092 installer::switches::kInactiveUserToast), &flavor);
1093 std::string experiment_group =
1094 cmd_line.GetSwitchValueASCII(installer::switches::kExperimentGroup);
1095 DCHECK_NE(-1, flavor);
1096 if (flavor == -1) {
1097 *exit_code = installer::UNKNOWN_STATUS;
1098 } else {
1099 // This code is called (via setup.exe relaunch) only if a product is known
1100 // to run user experiments, so no check is required.
1101 const Products& products = installer_state->products();
1102 for (Products::const_iterator it = products.begin(); it < products.end();
1103 ++it) {
1104 const Product& product = **it;
1105 installer::InactiveUserToastExperiment(
1106 flavor, base::ASCIIToUTF16(experiment_group), product,
1107 installer_state->target_path());
1110 } else if (cmd_line.HasSwitch(installer::switches::kSystemLevelToast)) {
1111 const Products& products = installer_state->products();
1112 for (Products::const_iterator it = products.begin(); it < products.end();
1113 ++it) {
1114 const Product& product = **it;
1115 BrowserDistribution* browser_dist = product.distribution();
1116 // We started as system-level and have been re-launched as user level
1117 // to continue with the toast experiment.
1118 Version installed_version;
1119 InstallUtil::GetChromeVersion(browser_dist, true, &installed_version);
1120 if (!installed_version.IsValid()) {
1121 LOG(ERROR) << "No installation of "
1122 << browser_dist->GetDisplayName()
1123 << " found for system-level toast.";
1124 } else {
1125 product.LaunchUserExperiment(
1126 setup_exe, installer::REENTRY_SYS_UPDATE, true);
1129 } else if (cmd_line.HasSwitch(installer::switches::kPatch)) {
1130 const std::string patch_type_str(
1131 cmd_line.GetSwitchValueASCII(installer::switches::kPatch));
1132 const base::FilePath input_file(
1133 cmd_line.GetSwitchValuePath(installer::switches::kInputFile));
1134 const base::FilePath patch_file(
1135 cmd_line.GetSwitchValuePath(installer::switches::kPatchFile));
1136 const base::FilePath output_file(
1137 cmd_line.GetSwitchValuePath(installer::switches::kOutputFile));
1139 if (patch_type_str == installer::kCourgette) {
1140 *exit_code = installer::CourgettePatchFiles(input_file,
1141 patch_file,
1142 output_file);
1143 } else if (patch_type_str == installer::kBsdiff) {
1144 *exit_code = installer::BsdiffPatchFiles(input_file,
1145 patch_file,
1146 output_file);
1147 } else {
1148 *exit_code = installer::PATCH_INVALID_ARGUMENTS;
1150 } else if (cmd_line.HasSwitch(installer::switches::kReenableAutoupdates)) {
1151 // setup.exe has been asked to attempt to reenable updates for Chrome.
1152 bool updates_enabled = GoogleUpdateSettings::ReenableAutoupdates();
1153 *exit_code = updates_enabled ? installer::REENABLE_UPDATES_SUCCEEDED :
1154 installer::REENABLE_UPDATES_FAILED;
1155 } else {
1156 handled = false;
1159 return handled;
1162 bool ShowRebootDialog() {
1163 // Get a token for this process.
1164 HANDLE token;
1165 if (!OpenProcessToken(GetCurrentProcess(),
1166 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
1167 &token)) {
1168 LOG(ERROR) << "Failed to open token.";
1169 return false;
1172 // Use a ScopedHandle to keep track of and eventually close our handle.
1173 // TODO(robertshield): Add a Receive() method to base's ScopedHandle.
1174 base::win::ScopedHandle scoped_handle(token);
1176 // Get the LUID for the shutdown privilege.
1177 TOKEN_PRIVILEGES tkp = {0};
1178 LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
1179 tkp.PrivilegeCount = 1;
1180 tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1182 // Get the shutdown privilege for this process.
1183 AdjustTokenPrivileges(token, FALSE, &tkp, 0,
1184 reinterpret_cast<PTOKEN_PRIVILEGES>(NULL), 0);
1185 if (GetLastError() != ERROR_SUCCESS) {
1186 LOG(ERROR) << "Unable to get shutdown privileges.";
1187 return false;
1190 // Popup a dialog that will prompt to reboot using the default system message.
1191 // TODO(robertshield): Add a localized, more specific string to the prompt.
1192 RestartDialog(NULL, NULL, EWX_REBOOT | EWX_FORCEIFHUNG);
1193 return true;
1196 // Returns the Custom information for the client identified by the exe path
1197 // passed in. This information is used for crash reporting.
1198 google_breakpad::CustomClientInfo* GetCustomInfo(const wchar_t* exe_path) {
1199 base::string16 product;
1200 base::string16 version;
1201 scoped_ptr<FileVersionInfo> version_info(
1202 FileVersionInfo::CreateFileVersionInfo(base::FilePath(exe_path)));
1203 if (version_info.get()) {
1204 version = version_info->product_version();
1205 product = version_info->product_short_name();
1208 if (version.empty())
1209 version = L"0.1.0.0";
1211 if (product.empty())
1212 product = L"Chrome Installer";
1214 static google_breakpad::CustomInfoEntry ver_entry(L"ver", version.c_str());
1215 static google_breakpad::CustomInfoEntry prod_entry(L"prod", product.c_str());
1216 static google_breakpad::CustomInfoEntry plat_entry(L"plat", L"Win32");
1217 static google_breakpad::CustomInfoEntry type_entry(L"ptype",
1218 L"Chrome Installer");
1219 static google_breakpad::CustomInfoEntry entries[] = {
1220 ver_entry, prod_entry, plat_entry, type_entry };
1221 static google_breakpad::CustomClientInfo custom_info = {
1222 entries, arraysize(entries) };
1223 return &custom_info;
1226 // Initialize crash reporting for this process. This involves connecting to
1227 // breakpad, etc.
1228 scoped_ptr<google_breakpad::ExceptionHandler> InitializeCrashReporting(
1229 bool system_install) {
1230 // Only report crashes if the user allows it.
1231 if (!GoogleUpdateSettings::GetCollectStatsConsent())
1232 return scoped_ptr<google_breakpad::ExceptionHandler>();
1234 // Get the alternate dump directory. We use the temp path.
1235 base::FilePath temp_directory;
1236 if (!base::GetTempDir(&temp_directory) || temp_directory.empty())
1237 return scoped_ptr<google_breakpad::ExceptionHandler>();
1239 wchar_t exe_path[MAX_PATH * 2] = {0};
1240 GetModuleFileName(NULL, exe_path, arraysize(exe_path));
1242 // Build the pipe name. It can be either:
1243 // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18"
1244 // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>"
1245 base::string16 user_sid = kSystemPrincipalSid;
1247 if (!system_install) {
1248 if (!base::win::GetUserSidString(&user_sid)) {
1249 return scoped_ptr<google_breakpad::ExceptionHandler>();
1253 base::string16 pipe_name = kGoogleUpdatePipeName;
1254 pipe_name += user_sid;
1256 return scoped_ptr<google_breakpad::ExceptionHandler>(
1257 new google_breakpad::ExceptionHandler(
1258 temp_directory.value(), NULL, NULL, NULL,
1259 google_breakpad::ExceptionHandler::HANDLER_ALL, kLargerDumpType,
1260 pipe_name.c_str(), GetCustomInfo(exe_path)));
1263 // Uninstalls multi-install Chrome Frame if the current operation is a
1264 // multi-install install or update. The operation is performed directly rather
1265 // than delegated to the existing install since there is no facility in older
1266 // versions of setup.exe to uninstall GCF without touching the binaries. The
1267 // binaries will be uninstalled during later processing if they are not in-use
1268 // (see UninstallBinariesIfUnused). |original_state| and |installer_state| are
1269 // updated to reflect the state of the world following the operation.
1270 void UninstallMultiChromeFrameIfPresent(const base::CommandLine& cmd_line,
1271 const MasterPreferences& prefs,
1272 InstallationState* original_state,
1273 InstallerState* installer_state) {
1274 // Early exit if not installing or updating.
1275 if (installer_state->operation() == InstallerState::UNINSTALL)
1276 return;
1278 // Early exit if Chrome Frame is not present as multi-install.
1279 const ProductState* chrome_frame_state =
1280 original_state->GetProductState(installer_state->system_install(),
1281 BrowserDistribution::CHROME_FRAME);
1282 if (!chrome_frame_state || !chrome_frame_state->is_multi_install())
1283 return;
1285 LOG(INFO) << "Uninstalling multi-install Chrome Frame.";
1286 installer_state->UpdateStage(installer::UNINSTALLING_CHROME_FRAME);
1288 // Uninstall Chrome Frame without touching the multi-install binaries.
1289 // Simulate the uninstall as coming from the installed version.
1290 const base::CommandLine& uninstall_cmd(
1291 chrome_frame_state->uninstall_command());
1292 MasterPreferences uninstall_prefs(uninstall_cmd);
1293 InstallerState uninstall_state;
1294 uninstall_state.Initialize(uninstall_cmd, uninstall_prefs, *original_state);
1295 // Post M32, uninstall_prefs and uninstall_state won't have Chrome Frame in
1296 // them since they've lost the power to do Chrome Frame installs.
1297 const Product* chrome_frame_product = uninstall_state.AddProductFromState(
1298 BrowserDistribution::CHROME_FRAME, *chrome_frame_state);
1299 if (chrome_frame_product) {
1300 // No shared state should be left behind.
1301 const bool remove_all = true;
1302 // Don't accept no for an answer.
1303 const bool force_uninstall = true;
1304 installer::InstallStatus uninstall_status =
1305 installer::UninstallProduct(*original_state, uninstall_state,
1306 uninstall_cmd.GetProgram(),
1307 *chrome_frame_product, remove_all,
1308 force_uninstall, cmd_line);
1310 VLOG(1) << "Uninstallation of Chrome Frame returned status "
1311 << uninstall_status;
1312 } else {
1313 LOG(ERROR) << "Chrome Frame not found for uninstall.";
1316 // Refresh state for the continuation of the original install/update.
1317 original_state->Initialize();
1318 installer_state->Initialize(cmd_line, prefs, *original_state);
1321 } // namespace
1323 namespace installer {
1325 InstallStatus InstallProductsHelper(const InstallationState& original_state,
1326 const base::FilePath& setup_exe,
1327 const base::CommandLine& cmd_line,
1328 const MasterPreferences& prefs,
1329 const InstallerState& installer_state,
1330 base::FilePath* installer_directory,
1331 ArchiveType* archive_type,
1332 bool* delegated_to_existing) {
1333 DCHECK(archive_type);
1334 DCHECK(delegated_to_existing);
1335 const bool system_install = installer_state.system_install();
1336 InstallStatus install_status = UNKNOWN_STATUS;
1338 // Drop to background processing mode if the process was started below the
1339 // normal process priority class.
1340 bool entered_background_mode = AdjustProcessPriority();
1341 VLOG_IF(1, entered_background_mode) << "Entered background processing mode.";
1343 // Create a temp folder where we will unpack Chrome archive. If it fails,
1344 // then we are doomed, so return immediately and no cleanup is required.
1345 SelfCleaningTempDir temp_path;
1346 base::FilePath unpack_path;
1347 if (!CreateTemporaryAndUnpackDirectories(installer_state, &temp_path,
1348 &unpack_path)) {
1349 installer_state.WriteInstallerResult(TEMP_DIR_FAILED,
1350 IDS_INSTALL_TEMP_DIR_FAILED_BASE,
1351 NULL);
1352 return TEMP_DIR_FAILED;
1355 // Uncompress and optionally patch the archive if an uncompressed archive was
1356 // not specified on the command line and a compressed archive is found.
1357 *archive_type = UNKNOWN_ARCHIVE_TYPE;
1358 base::FilePath uncompressed_archive(cmd_line.GetSwitchValuePath(
1359 switches::kUncompressedArchive));
1360 if (uncompressed_archive.empty()) {
1361 base::Version previous_version;
1362 if (cmd_line.HasSwitch(installer::switches::kPreviousVersion)) {
1363 previous_version = base::Version(cmd_line.GetSwitchValueASCII(
1364 installer::switches::kPreviousVersion));
1367 scoped_ptr<ArchivePatchHelper> archive_helper(
1368 CreateChromeArchiveHelper(setup_exe, cmd_line, installer_state,
1369 unpack_path));
1370 if (archive_helper) {
1371 VLOG(1) << "Installing Chrome from compressed archive "
1372 << archive_helper->compressed_archive().value();
1373 if (!UncompressAndPatchChromeArchive(original_state,
1374 installer_state,
1375 archive_helper.get(),
1376 archive_type,
1377 &install_status,
1378 previous_version)) {
1379 DCHECK_NE(install_status, UNKNOWN_STATUS);
1380 return install_status;
1382 uncompressed_archive = archive_helper->target();
1383 DCHECK(!uncompressed_archive.empty());
1387 // Check for an uncompressed archive alongside the current executable if one
1388 // was not given or generated.
1389 if (uncompressed_archive.empty())
1390 uncompressed_archive = setup_exe.DirName().Append(kChromeArchive);
1392 if (*archive_type == UNKNOWN_ARCHIVE_TYPE) {
1393 // An archive was not uncompressed or patched above.
1394 if (uncompressed_archive.empty() ||
1395 !base::PathExists(uncompressed_archive)) {
1396 LOG(ERROR) << "Cannot install Chrome without an uncompressed archive.";
1397 installer_state.WriteInstallerResult(
1398 INVALID_ARCHIVE, IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL);
1399 return INVALID_ARCHIVE;
1401 *archive_type = FULL_ARCHIVE_TYPE;
1404 // Unpack the uncompressed archive.
1405 if (LzmaUtil::UnPackArchive(uncompressed_archive.value(),
1406 unpack_path.value(),
1407 NULL)) {
1408 installer_state.WriteInstallerResult(
1409 UNPACKING_FAILED,
1410 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
1411 NULL);
1412 return UNPACKING_FAILED;
1415 VLOG(1) << "unpacked to " << unpack_path.value();
1416 base::FilePath src_path(
1417 unpack_path.Append(kInstallSourceChromeDir));
1418 scoped_ptr<Version>
1419 installer_version(GetMaxVersionFromArchiveDir(src_path));
1420 if (!installer_version.get()) {
1421 LOG(ERROR) << "Did not find any valid version in installer.";
1422 install_status = INVALID_ARCHIVE;
1423 installer_state.WriteInstallerResult(install_status,
1424 IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL);
1425 } else {
1426 VLOG(1) << "version to install: " << installer_version->GetString();
1427 bool proceed_with_installation = true;
1429 if (installer_state.operation() == InstallerState::MULTI_INSTALL) {
1430 // This is a new install of a multi-install product. Rather than give up
1431 // in case a higher version of the binaries (including a single-install
1432 // of Chrome, which can safely be migrated to multi-install by way of
1433 // CheckMultiInstallConditions) is already installed, delegate to the
1434 // installed setup.exe to install the product at hand.
1435 base::FilePath existing_setup_exe;
1436 if (GetExistingHigherInstaller(original_state, system_install,
1437 *installer_version, &existing_setup_exe)) {
1438 VLOG(1) << "Deferring to existing installer.";
1439 installer_state.UpdateStage(DEFERRING_TO_HIGHER_VERSION);
1440 if (DeferToExistingInstall(existing_setup_exe, cmd_line,
1441 installer_state, temp_path.path(),
1442 &install_status)) {
1443 *delegated_to_existing = true;
1444 return install_status;
1449 uint32 higher_products = 0;
1450 COMPILE_ASSERT(
1451 sizeof(higher_products) * 8 > BrowserDistribution::NUM_TYPES,
1452 too_many_distribution_types_);
1453 const Products& products = installer_state.products();
1454 for (Products::const_iterator it = products.begin(); it < products.end();
1455 ++it) {
1456 const Product& product = **it;
1457 const ProductState* product_state =
1458 original_state.GetProductState(system_install,
1459 product.distribution()->GetType());
1460 if (product_state != NULL &&
1461 (product_state->version().CompareTo(*installer_version) > 0)) {
1462 LOG(ERROR) << "Higher version of "
1463 << product.distribution()->GetDisplayName()
1464 << " is already installed.";
1465 higher_products |= (1 << product.distribution()->GetType());
1469 if (higher_products != 0) {
1470 COMPILE_ASSERT(BrowserDistribution::NUM_TYPES == 3,
1471 add_support_for_new_products_here_);
1472 int message_id = IDS_INSTALL_HIGHER_VERSION_BASE;
1473 proceed_with_installation = false;
1474 install_status = HIGHER_VERSION_EXISTS;
1475 installer_state.WriteInstallerResult(install_status, message_id, NULL);
1478 if (proceed_with_installation) {
1479 base::FilePath prefs_source_path(cmd_line.GetSwitchValueNative(
1480 switches::kInstallerData));
1481 install_status = InstallOrUpdateProduct(
1482 original_state, installer_state, setup_exe, uncompressed_archive,
1483 temp_path.path(), src_path, prefs_source_path, prefs,
1484 *installer_version);
1486 int install_msg_base = IDS_INSTALL_FAILED_BASE;
1487 base::FilePath chrome_exe;
1488 base::string16 quoted_chrome_exe;
1489 if (install_status == SAME_VERSION_REPAIR_FAILED) {
1490 install_msg_base = IDS_SAME_VERSION_REPAIR_FAILED_BASE;
1491 } else if (install_status != INSTALL_FAILED) {
1492 if (installer_state.target_path().empty()) {
1493 // If we failed to construct install path, it means the OS call to
1494 // get %ProgramFiles% or %AppData% failed. Report this as failure.
1495 install_msg_base = IDS_INSTALL_OS_ERROR_BASE;
1496 install_status = OS_ERROR;
1497 } else {
1498 chrome_exe = installer_state.target_path().Append(kChromeExe);
1499 quoted_chrome_exe = L"\"" + chrome_exe.value() + L"\"";
1500 install_msg_base = 0;
1504 installer_state.UpdateStage(FINISHING);
1506 // Only do Chrome-specific stuff (like launching the browser) if
1507 // Chrome was specifically requested (rather than being upgraded as
1508 // part of a multi-install).
1509 const Product* chrome_install = prefs.install_chrome() ?
1510 installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) :
1511 NULL;
1513 bool do_not_register_for_update_launch = false;
1514 if (chrome_install) {
1515 prefs.GetBool(master_preferences::kDoNotRegisterForUpdateLaunch,
1516 &do_not_register_for_update_launch);
1517 } else {
1518 do_not_register_for_update_launch = true; // Never register.
1521 bool write_chrome_launch_string =
1522 (!do_not_register_for_update_launch &&
1523 install_status != IN_USE_UPDATED);
1525 installer_state.WriteInstallerResult(install_status, install_msg_base,
1526 write_chrome_launch_string ? &quoted_chrome_exe : NULL);
1528 if (install_status == FIRST_INSTALL_SUCCESS) {
1529 VLOG(1) << "First install successful.";
1530 if (chrome_install) {
1531 // We never want to launch Chrome in system level install mode.
1532 bool do_not_launch_chrome = false;
1533 prefs.GetBool(master_preferences::kDoNotLaunchChrome,
1534 &do_not_launch_chrome);
1535 if (!system_install && !do_not_launch_chrome)
1536 chrome_install->LaunchChrome(installer_state.target_path());
1538 } else if ((install_status == NEW_VERSION_UPDATED) ||
1539 (install_status == IN_USE_UPDATED)) {
1540 const Product* chrome = installer_state.FindProduct(
1541 BrowserDistribution::CHROME_BROWSER);
1542 if (chrome != NULL) {
1543 DCHECK_NE(chrome_exe.value(), base::string16());
1544 RemoveChromeLegacyRegistryKeys(chrome->distribution(), chrome_exe);
1550 // There might be an experiment (for upgrade usually) that needs to happen.
1551 // An experiment's outcome can include chrome's uninstallation. If that is
1552 // the case we would not do that directly at this point but in another
1553 // instance of setup.exe
1555 // There is another way to reach this same function if this is a system
1556 // level install. See HandleNonInstallCmdLineOptions().
1558 // If installation failed, use the path to the currently running setup.
1559 // If installation succeeded, use the path to setup in the installer dir.
1560 base::FilePath setup_path(setup_exe);
1561 if (InstallUtil::GetInstallReturnCode(install_status) == 0) {
1562 setup_path = installer_state.GetInstallerDirectory(*installer_version)
1563 .Append(setup_path.BaseName());
1565 const Products& products = installer_state.products();
1566 for (Products::const_iterator it = products.begin(); it < products.end();
1567 ++it) {
1568 const Product& product = **it;
1569 product.LaunchUserExperiment(setup_path, install_status, system_install);
1573 // If installation completed successfully, return the path to the directory
1574 // containing the newly installed setup.exe and uncompressed archive if the
1575 // caller requested it.
1576 if (installer_directory &&
1577 InstallUtil::GetInstallReturnCode(install_status) == 0) {
1578 *installer_directory =
1579 installer_state.GetInstallerDirectory(*installer_version);
1582 // temp_path's dtor will take care of deleting or scheduling itself for
1583 // deletion at reboot when this scope closes.
1584 VLOG(1) << "Deleting temporary directory " << temp_path.path().value();
1586 return install_status;
1589 } // namespace installer
1591 int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance,
1592 wchar_t* command_line, int show_command) {
1593 // Check to see if the CPU is supported before doing anything else. There's
1594 // very little than can safely be accomplished if the CPU isn't supported
1595 // since dependent libraries (e.g., base) may use invalid instructions.
1596 if (!installer::IsProcessorSupported())
1597 return installer::CPU_NOT_SUPPORTED;
1599 // The exit manager is in charge of calling the dtors of singletons.
1600 base::AtExitManager exit_manager;
1601 base::CommandLine::Init(0, NULL);
1603 // install_util uses chrome paths.
1604 chrome::RegisterPathProvider();
1606 const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess();
1607 installer::InitInstallerLogging(prefs);
1609 const base::CommandLine& cmd_line = *base::CommandLine::ForCurrentProcess();
1610 VLOG(1) << "Command Line: " << cmd_line.GetCommandLineString();
1612 VLOG(1) << "multi install is " << prefs.is_multi_install();
1613 bool system_install = false;
1614 prefs.GetBool(installer::master_preferences::kSystemLevel, &system_install);
1615 VLOG(1) << "system install is " << system_install;
1617 scoped_ptr<google_breakpad::ExceptionHandler> breakpad(
1618 InitializeCrashReporting(system_install));
1620 InstallationState original_state;
1621 original_state.Initialize();
1623 InstallerState installer_state;
1624 installer_state.Initialize(cmd_line, prefs, original_state);
1625 const bool is_uninstall = cmd_line.HasSwitch(installer::switches::kUninstall);
1627 // Check to make sure current system is WinXP or later. If not, log
1628 // error message and get out.
1629 if (!InstallUtil::IsOSSupported()) {
1630 LOG(ERROR) << "Chrome only supports Windows XP or later.";
1631 installer_state.WriteInstallerResult(
1632 installer::OS_NOT_SUPPORTED, IDS_INSTALL_OS_NOT_SUPPORTED_BASE, NULL);
1633 return installer::OS_NOT_SUPPORTED;
1636 // Initialize COM for use later.
1637 base::win::ScopedCOMInitializer com_initializer;
1638 if (!com_initializer.succeeded()) {
1639 installer_state.WriteInstallerResult(
1640 installer::OS_ERROR, IDS_INSTALL_OS_ERROR_BASE, NULL);
1641 return installer::OS_ERROR;
1644 // Some command line options don't work with SxS install/uninstall
1645 if (InstallUtil::IsChromeSxSProcess()) {
1646 if (system_install ||
1647 prefs.is_multi_install() ||
1648 cmd_line.HasSwitch(installer::switches::kSelfDestruct) ||
1649 cmd_line.HasSwitch(installer::switches::kMakeChromeDefault) ||
1650 cmd_line.HasSwitch(installer::switches::kRegisterChromeBrowser) ||
1651 cmd_line.HasSwitch(installer::switches::kRemoveChromeRegistration) ||
1652 cmd_line.HasSwitch(installer::switches::kInactiveUserToast) ||
1653 cmd_line.HasSwitch(installer::switches::kSystemLevelToast)) {
1654 return installer::SXS_OPTION_NOT_SUPPORTED;
1658 // Some command line options are no longer supported and must error out.
1659 if (installer::ContainsUnsupportedSwitch(cmd_line))
1660 return installer::UNSUPPORTED_OPTION;
1662 // A variety of installer operations require the path to the current
1663 // executable. Get it once here for use throughout these operations. Note that
1664 // the path service is the authoritative source for this path. One might think
1665 // that CommandLine::GetProgram would suffice, but it won't since
1666 // CreateProcess may have been called with a command line that is somewhat
1667 // ambiguous (e.g., an unquoted path with spaces, or a path lacking the file
1668 // extension), in which case CommandLineToArgv will not yield an argv with the
1669 // true path to the program at position 0.
1670 base::FilePath setup_exe;
1671 PathService::Get(base::FILE_EXE, &setup_exe);
1673 int exit_code = 0;
1674 if (HandleNonInstallCmdLineOptions(
1675 original_state, setup_exe, cmd_line, &installer_state, &exit_code)) {
1676 return exit_code;
1679 if (system_install && !IsUserAnAdmin()) {
1680 if (base::win::GetVersion() >= base::win::VERSION_VISTA &&
1681 !cmd_line.HasSwitch(installer::switches::kRunAsAdmin)) {
1682 base::CommandLine new_cmd(base::CommandLine::NO_PROGRAM);
1683 new_cmd.AppendArguments(cmd_line, true);
1684 // Append --run-as-admin flag to let the new instance of setup.exe know
1685 // that we already tried to launch ourselves as admin.
1686 new_cmd.AppendSwitch(installer::switches::kRunAsAdmin);
1687 // If system_install became true due to an environment variable, append
1688 // it to the command line here since env vars may not propagate past the
1689 // elevation.
1690 if (!new_cmd.HasSwitch(installer::switches::kSystemLevel))
1691 new_cmd.AppendSwitch(installer::switches::kSystemLevel);
1693 DWORD exit_code = installer::UNKNOWN_STATUS;
1694 InstallUtil::ExecuteExeAsAdmin(new_cmd, &exit_code);
1695 return exit_code;
1696 } else {
1697 LOG(ERROR) << "Non admin user can not install system level Chrome.";
1698 installer_state.WriteInstallerResult(installer::INSUFFICIENT_RIGHTS,
1699 IDS_INSTALL_INSUFFICIENT_RIGHTS_BASE, NULL);
1700 return installer::INSUFFICIENT_RIGHTS;
1704 UninstallMultiChromeFrameIfPresent(cmd_line, prefs,
1705 &original_state, &installer_state);
1707 base::FilePath installer_directory;
1708 installer::InstallStatus install_status = installer::UNKNOWN_STATUS;
1709 // If --uninstall option is given, uninstall the identified product(s)
1710 if (is_uninstall) {
1711 install_status =
1712 UninstallProducts(original_state, installer_state, setup_exe, cmd_line);
1713 } else {
1714 // If --uninstall option is not specified, we assume it is install case.
1715 install_status =
1716 InstallProducts(original_state, setup_exe, cmd_line, prefs,
1717 &installer_state, &installer_directory);
1720 // Validate that the machine is now in a good state following the operation.
1721 // TODO(grt): change this to log at DFATAL once we're convinced that the
1722 // validator handles all cases properly.
1723 InstallationValidator::InstallationType installation_type =
1724 InstallationValidator::NO_PRODUCTS;
1725 LOG_IF(ERROR,
1726 !InstallationValidator::ValidateInstallationType(system_install,
1727 &installation_type));
1729 int return_code = 0;
1730 // MSI demands that custom actions always return 0 (ERROR_SUCCESS) or it will
1731 // rollback the action. If we're uninstalling we want to avoid this, so always
1732 // report success, squashing any more informative return codes.
1733 if (!(installer_state.is_msi() && is_uninstall)) {
1734 // Note that we allow the status installer::UNINSTALL_REQUIRES_REBOOT
1735 // to pass through, since this is only returned on uninstall which is
1736 // never invoked directly by Google Update.
1737 return_code = InstallUtil::GetInstallReturnCode(install_status);
1740 VLOG(1) << "Installation complete, returning: " << return_code;
1742 return return_code;