Android: Get rid of extra dup()s on launching child processes
[chromium-blink-merge.git] / chrome / installer / util / installation_validator.cc
blob0334ceb1c4c4d2fc2660d095df3e4b5de921d2be
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 // Implementation of the installation validator.
7 #include "chrome/installer/util/installation_validator.h"
9 #include <algorithm>
10 #include <set>
11 #include <string>
13 #include "base/logging.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/version.h"
17 #include "base/win/registry.h"
18 #include "chrome/common/chrome_switches.h"
19 #include "chrome/installer/util/browser_distribution.h"
20 #include "chrome/installer/util/google_update_constants.h"
21 #include "chrome/installer/util/helper.h"
22 #include "chrome/installer/util/install_util.h"
23 #include "chrome/installer/util/installation_state.h"
24 #include "chrome/installer/util/updating_app_registration_data.h"
26 namespace installer {
28 BrowserDistribution::Type
29 InstallationValidator::ChromeRules::distribution_type() const {
30 return BrowserDistribution::CHROME_BROWSER;
33 void InstallationValidator::ChromeRules::AddUninstallSwitchExpectations(
34 const ProductContext& ctx,
35 SwitchExpectations* expectations) const {
36 // --chrome should be present for uninstall iff --multi-install. This wasn't
37 // the case in Chrome 10 (between r68996 and r72497), though, so consider it
38 // optional.
41 void InstallationValidator::ChromeRules::AddRenameSwitchExpectations(
42 const ProductContext& ctx,
43 SwitchExpectations* expectations) const {
44 // --chrome should not be present for rename. It was for a time, so we'll be
45 // lenient so that mini_installer tests pass.
47 // --chrome-frame should never be present.
48 expectations->push_back(
49 std::make_pair(std::string(switches::kChromeFrame), false));
52 bool InstallationValidator::ChromeRules::UsageStatsAllowed(
53 const ProductContext& ctx) const {
54 // Products must not have usagestats consent values when multi-install
55 // (only the multi-install binaries may).
56 return !ctx.state.is_multi_install();
59 BrowserDistribution::Type
60 InstallationValidator::ChromeFrameRules::distribution_type() const {
61 return BrowserDistribution::CHROME_FRAME;
64 void InstallationValidator::ChromeFrameRules::AddUninstallSwitchExpectations(
65 const ProductContext& ctx,
66 SwitchExpectations* expectations) const {
67 // --chrome-frame must be present.
68 expectations->push_back(std::make_pair(std::string(switches::kChromeFrame),
69 true));
70 // --chrome must not be present.
71 expectations->push_back(std::make_pair(std::string(switches::kChrome),
72 false));
75 void InstallationValidator::ChromeFrameRules::AddRenameSwitchExpectations(
76 const ProductContext& ctx,
77 SwitchExpectations* expectations) const {
78 // --chrome-frame must be present for SxS rename.
79 expectations->push_back(std::make_pair(std::string(switches::kChromeFrame),
80 !ctx.state.is_multi_install()));
81 // --chrome must not be present.
82 expectations->push_back(std::make_pair(std::string(switches::kChrome),
83 false));
86 bool InstallationValidator::ChromeFrameRules::UsageStatsAllowed(
87 const ProductContext& ctx) const {
88 // Products must not have usagestats consent values when multi-install
89 // (only the multi-install binaries may).
90 return !ctx.state.is_multi_install();
93 BrowserDistribution::Type
94 InstallationValidator::ChromeBinariesRules::distribution_type() const {
95 return BrowserDistribution::CHROME_BINARIES;
98 void InstallationValidator::ChromeBinariesRules::AddUninstallSwitchExpectations(
99 const ProductContext& ctx,
100 SwitchExpectations* expectations) const {
101 NOTREACHED();
104 void InstallationValidator::ChromeBinariesRules::AddRenameSwitchExpectations(
105 const ProductContext& ctx,
106 SwitchExpectations* expectations) const {
107 NOTREACHED();
110 bool InstallationValidator::ChromeBinariesRules::UsageStatsAllowed(
111 const ProductContext& ctx) const {
112 // UsageStats consent values are always allowed on the binaries.
113 return true;
116 // static
117 const InstallationValidator::InstallationType
118 InstallationValidator::kInstallationTypes[] = {
119 NO_PRODUCTS,
120 CHROME_SINGLE,
121 CHROME_MULTI,
122 CHROME_FRAME_SINGLE,
123 CHROME_FRAME_SINGLE_CHROME_SINGLE,
124 CHROME_FRAME_SINGLE_CHROME_MULTI,
125 CHROME_FRAME_MULTI,
126 CHROME_FRAME_MULTI_CHROME_MULTI,
129 void InstallationValidator::ValidateAppCommandFlags(
130 const ProductContext& ctx,
131 const AppCommand& app_cmd,
132 const std::set<base::string16>& flags_exp,
133 const base::string16& name,
134 bool* is_valid) {
135 const struct {
136 const base::string16 exp_key;
137 bool val;
138 const char* msg;
139 } check_list[] = {
140 {google_update::kRegSendsPingsField,
141 app_cmd.sends_pings(),
142 "be configured to send pings"},
143 {google_update::kRegWebAccessibleField,
144 app_cmd.is_web_accessible(),
145 "be web accessible"},
146 {google_update::kRegAutoRunOnOSUpgradeField,
147 app_cmd.is_auto_run_on_os_upgrade(),
148 "be marked to run on OS upgrade"},
149 {google_update::kRegRunAsUserField,
150 app_cmd.is_run_as_user(),
151 "be marked to run as user"},
153 for (int i = 0; i < arraysize(check_list); ++i) {
154 bool expected = flags_exp.find(check_list[i].exp_key) != flags_exp.end();
155 if (check_list[i].val != expected) {
156 *is_valid = false;
157 LOG(ERROR) << ctx.dist->GetDisplayName() << ": "
158 << name << " command should " << (expected ? "" : "not ")
159 << check_list[i].msg << ".";
164 // Validates the "on-os-upgrade" Google Update internal command.
165 void InstallationValidator::ValidateOnOsUpgradeCommand(
166 const ProductContext& ctx,
167 const AppCommand& app_cmd,
168 bool* is_valid) {
169 DCHECK(is_valid);
171 base::CommandLine cmd_line(
172 base::CommandLine::FromString(app_cmd.command_line()));
173 base::string16 name(kCmdOnOsUpgrade);
175 ValidateSetupPath(ctx, cmd_line.GetProgram(), name, is_valid);
177 SwitchExpectations expected;
178 expected.push_back(std::make_pair(std::string(switches::kOnOsUpgrade), true));
179 expected.push_back(std::make_pair(std::string(switches::kSystemLevel),
180 ctx.system_install));
181 expected.push_back(std::make_pair(std::string(switches::kMultiInstall),
182 ctx.state.is_multi_install()));
183 // Expecting kChrome if and only if kMultiInstall.
184 expected.push_back(std::make_pair(std::string(switches::kChrome),
185 ctx.state.is_multi_install()));
187 ValidateCommandExpectations(ctx, cmd_line, expected, name, is_valid);
189 std::set<base::string16> flags_exp;
190 flags_exp.insert(google_update::kRegAutoRunOnOSUpgradeField);
191 ValidateAppCommandFlags(ctx, app_cmd, flags_exp, name, is_valid);
194 // Validates a product's set of Google Update product commands against a
195 // collection of expectations.
196 void InstallationValidator::ValidateAppCommandExpectations(
197 const ProductContext& ctx,
198 const CommandExpectations& expectations,
199 bool* is_valid) {
200 DCHECK(is_valid);
202 CommandExpectations the_expectations(expectations);
204 AppCommands::CommandMapRange cmd_iterators(
205 ctx.state.commands().GetIterators());
206 CommandExpectations::iterator expectation;
207 for (; cmd_iterators.first != cmd_iterators.second; ++cmd_iterators.first) {
208 const base::string16& cmd_id = cmd_iterators.first->first;
209 // Do we have an expectation for this command?
210 expectation = the_expectations.find(cmd_id);
211 if (expectation != the_expectations.end()) {
212 (expectation->second)(ctx, cmd_iterators.first->second, is_valid);
213 // Remove this command from the set of expectations since we found it.
214 the_expectations.erase(expectation);
215 } else {
216 *is_valid = false;
217 LOG(ERROR) << ctx.dist->GetDisplayName()
218 << " has an unexpected Google Update product command named \""
219 << cmd_id << "\".";
223 // Report on any expected commands that weren't present.
224 CommandExpectations::const_iterator scan(the_expectations.begin());
225 CommandExpectations::const_iterator end(the_expectations.end());
226 for (; scan != end; ++scan) {
227 *is_valid = false;
228 LOG(ERROR) << ctx.dist->GetDisplayName()
229 << " is missing the Google Update product command named \""
230 << scan->first << "\".";
234 // Validates the multi-install binaries at level |system_level|.
235 void InstallationValidator::ValidateBinaries(
236 const InstallationState& machine_state,
237 bool system_install,
238 const ProductState& binaries_state,
239 bool* is_valid) {
240 const ChannelInfo& channel = binaries_state.channel();
242 // ap must have -multi
243 if (!channel.IsMultiInstall()) {
244 *is_valid = false;
245 LOG(ERROR) << "Chrome Binaries are missing \"-multi\" in channel name: \""
246 << channel.value() << "\"";
249 // ap must have -chrome iff Chrome is installed
250 const ProductState* chrome_state = machine_state.GetProductState(
251 system_install, BrowserDistribution::CHROME_BROWSER);
252 if (chrome_state != NULL) {
253 if (!channel.IsChrome()) {
254 *is_valid = false;
255 LOG(ERROR) << "Chrome Binaries are missing \"chrome\" in channel name:"
256 << " \"" << channel.value() << "\"";
258 } else if (channel.IsChrome()) {
259 *is_valid = false;
260 LOG(ERROR) << "Chrome Binaries have \"-chrome\" in channel name, yet Chrome"
261 " is not installed: \"" << channel.value() << "\"";
264 // ap must have -chromeframe iff Chrome Frame is installed multi
265 const ProductState* cf_state = machine_state.GetProductState(
266 system_install, BrowserDistribution::CHROME_FRAME);
267 if (cf_state != NULL && cf_state->is_multi_install()) {
268 if (!channel.IsChromeFrame()) {
269 *is_valid = false;
270 LOG(ERROR) << "Chrome Binaries are missing \"-chromeframe\" in channel"
271 " name: \"" << channel.value() << "\"";
273 } else if (channel.IsChromeFrame()) {
274 *is_valid = false;
275 LOG(ERROR) << "Chrome Binaries have \"-chromeframe\" in channel name, yet "
276 "Chrome Frame is not installed multi: \"" << channel.value()
277 << "\"";
280 // Chrome or Chrome Frame must be present
281 if (chrome_state == NULL && cf_state == NULL) {
282 *is_valid = false;
283 LOG(ERROR) << "Chrome Binaries are present with no other products.";
287 // Chrome must be multi-install if present.
288 if (chrome_state != NULL && !chrome_state->is_multi_install()) {
289 *is_valid = false;
290 LOG(ERROR)
291 << "Chrome Binaries are present yet Chrome is not multi-install.";
294 // Chrome Frame must be multi-install if Chrome is not present.
295 if (cf_state != NULL && chrome_state == NULL &&
296 !cf_state->is_multi_install()) {
297 *is_valid = false;
298 LOG(ERROR) << "Chrome Binaries are present without Chrome, yet Chrome Frame"
299 << " is not multi-install.";
302 ChromeBinariesRules binaries_rules;
303 ProductContext ctx(machine_state, system_install, binaries_state,
304 binaries_rules);
306 ValidateUsageStats(ctx, is_valid);
309 // Validates the path to |setup_exe| for the product described by |ctx|.
310 void InstallationValidator::ValidateSetupPath(const ProductContext& ctx,
311 const base::FilePath& setup_exe,
312 const base::string16& purpose,
313 bool* is_valid) {
314 DCHECK(is_valid);
316 BrowserDistribution* bins_dist = ctx.dist;
317 if (ctx.state.is_multi_install()) {
318 bins_dist = BrowserDistribution::GetSpecificDistribution(
319 BrowserDistribution::CHROME_BINARIES);
322 base::FilePath expected_path = installer::GetChromeInstallPath(
323 ctx.system_install, bins_dist);
324 expected_path = expected_path
325 .AppendASCII(ctx.state.version().GetString())
326 .Append(installer::kInstallerDir)
327 .Append(installer::kSetupExe);
328 if (!base::FilePath::CompareEqualIgnoreCase(expected_path.value(),
329 setup_exe.value())) {
330 *is_valid = false;
331 LOG(ERROR) << ctx.dist->GetDisplayName() << " path to " << purpose
332 << " is not " << expected_path.value() << ": "
333 << setup_exe.value();
337 // Validates that |command| meets the expectations described in |expected|.
338 void InstallationValidator::ValidateCommandExpectations(
339 const ProductContext& ctx,
340 const base::CommandLine& command,
341 const SwitchExpectations& expected,
342 const base::string16& source,
343 bool* is_valid) {
344 for (SwitchExpectations::size_type i = 0, size = expected.size(); i < size;
345 ++i) {
346 const SwitchExpectations::value_type& expectation = expected[i];
347 if (command.HasSwitch(expectation.first) != expectation.second) {
348 *is_valid = false;
349 LOG(ERROR) << ctx.dist->GetDisplayName() << " " << source
350 << (expectation.second ? " is missing" : " has") << " \""
351 << expectation.first << "\""
352 << (expectation.second ? "" : " but shouldn't") << ": "
353 << command.GetCommandLineString();
358 // Validates that |command|, originating from |source|, is formed properly for
359 // the product described by |ctx|
360 void InstallationValidator::ValidateUninstallCommand(
361 const ProductContext& ctx,
362 const base::CommandLine& command,
363 const base::string16& source,
364 bool* is_valid) {
365 DCHECK(is_valid);
367 ValidateSetupPath(ctx, command.GetProgram(),
368 base::ASCIIToUTF16("uninstaller"),
369 is_valid);
371 const bool is_multi_install = ctx.state.is_multi_install();
372 SwitchExpectations expected;
374 expected.push_back(std::make_pair(std::string(switches::kUninstall), true));
375 expected.push_back(std::make_pair(std::string(switches::kSystemLevel),
376 ctx.system_install));
377 expected.push_back(std::make_pair(std::string(switches::kMultiInstall),
378 is_multi_install));
379 ctx.rules.AddUninstallSwitchExpectations(ctx, &expected);
381 ValidateCommandExpectations(ctx, command, expected, source, is_valid);
384 // Validates the rename command for the product described by |ctx|.
385 void InstallationValidator::ValidateRenameCommand(const ProductContext& ctx,
386 bool* is_valid) {
387 DCHECK(is_valid);
388 DCHECK(!ctx.state.rename_cmd().empty());
390 base::CommandLine command =
391 base::CommandLine::FromString(ctx.state.rename_cmd());
392 base::string16 name(base::ASCIIToUTF16("in-use renamer"));
394 ValidateSetupPath(ctx, command.GetProgram(), name, is_valid);
396 SwitchExpectations expected;
398 expected.push_back(std::make_pair(std::string(switches::kRenameChromeExe),
399 true));
400 expected.push_back(std::make_pair(std::string(switches::kSystemLevel),
401 ctx.system_install));
402 expected.push_back(std::make_pair(std::string(switches::kMultiInstall),
403 ctx.state.is_multi_install()));
404 ctx.rules.AddRenameSwitchExpectations(ctx, &expected);
406 ValidateCommandExpectations(ctx, command, expected, name, is_valid);
409 // Validates the "opv" and "cmd" values for the product described in |ctx|.
410 void InstallationValidator::ValidateOldVersionValues(
411 const ProductContext& ctx,
412 bool* is_valid) {
413 DCHECK(is_valid);
415 // opv and cmd must both be present or both absent
416 if (ctx.state.old_version() == NULL) {
417 if (!ctx.state.rename_cmd().empty()) {
418 *is_valid = false;
419 LOG(ERROR) << ctx.dist->GetDisplayName()
420 << " has a rename command but no opv: "
421 << ctx.state.rename_cmd();
423 } else {
424 if (ctx.state.rename_cmd().empty()) {
425 *is_valid = false;
426 LOG(ERROR) << ctx.dist->GetDisplayName()
427 << " has an opv but no rename command: "
428 << ctx.state.old_version()->GetString();
429 } else {
430 ValidateRenameCommand(ctx, is_valid);
435 // Validates the multi-install state of the product described in |ctx|.
436 void InstallationValidator::ValidateMultiInstallProduct(
437 const ProductContext& ctx,
438 bool* is_valid) {
439 DCHECK(is_valid);
441 const ProductState* binaries =
442 ctx.machine_state.GetProductState(ctx.system_install,
443 BrowserDistribution::CHROME_BINARIES);
444 if (!binaries) {
445 *is_valid = false;
446 LOG(ERROR) << ctx.dist->GetDisplayName()
447 << " (" << ctx.state.version().GetString() << ") is installed "
448 << "without Chrome Binaries.";
449 } else {
450 // Version must match that of binaries.
451 if (ctx.state.version().CompareTo(binaries->version()) != 0) {
452 *is_valid = false;
453 LOG(ERROR) << "Version of " << ctx.dist->GetDisplayName()
454 << " (" << ctx.state.version().GetString() << ") does not "
455 "match that of Chrome Binaries ("
456 << binaries->version().GetString() << ").";
459 // Channel value must match that of binaries.
460 if (!ctx.state.channel().Equals(binaries->channel())) {
461 *is_valid = false;
462 LOG(ERROR) << "Channel name of " << ctx.dist->GetDisplayName()
463 << " (" << ctx.state.channel().value()
464 << ") does not match that of Chrome Binaries ("
465 << binaries->channel().value() << ").";
470 // Validates the Google Update commands for the product described in |ctx|.
471 void InstallationValidator::ValidateAppCommands(
472 const ProductContext& ctx,
473 bool* is_valid) {
474 DCHECK(is_valid);
476 CommandExpectations expectations;
478 if (ctx.dist->GetType() == BrowserDistribution::CHROME_BROWSER)
479 expectations[kCmdOnOsUpgrade] = &ValidateOnOsUpgradeCommand;
481 ValidateAppCommandExpectations(ctx, expectations, is_valid);
484 // Validates usagestats for the product or binaries in |ctx|.
485 void InstallationValidator::ValidateUsageStats(const ProductContext& ctx,
486 bool* is_valid) {
487 DWORD usagestats = 0;
488 if (ctx.state.GetUsageStats(&usagestats)) {
489 if (!ctx.rules.UsageStatsAllowed(ctx)) {
490 *is_valid = false;
491 LOG(ERROR) << ctx.dist->GetDisplayName()
492 << " has a usagestats value (" << usagestats
493 << "), yet should not.";
494 } else if (usagestats != 0 && usagestats != 1) {
495 *is_valid = false;
496 LOG(ERROR) << ctx.dist->GetDisplayName()
497 << " has an unsupported usagestats value (" << usagestats
498 << ").";
503 // Validates the product described in |product_state| according to |rules|.
504 void InstallationValidator::ValidateProduct(
505 const InstallationState& machine_state,
506 bool system_install,
507 const ProductState& product_state,
508 const ProductRules& rules,
509 bool* is_valid) {
510 DCHECK(is_valid);
512 ProductContext ctx(machine_state, system_install, product_state, rules);
514 ValidateUninstallCommand(ctx, ctx.state.uninstall_command(),
515 base::ASCIIToUTF16(
516 "Google Update uninstall command"),
517 is_valid);
519 ValidateOldVersionValues(ctx, is_valid);
521 if (ctx.state.is_multi_install())
522 ValidateMultiInstallProduct(ctx, is_valid);
524 ValidateAppCommands(ctx, is_valid);
526 ValidateUsageStats(ctx, is_valid);
529 // static
530 bool InstallationValidator::ValidateInstallationTypeForState(
531 const InstallationState& machine_state,
532 bool system_level,
533 InstallationType* type) {
534 DCHECK(type);
535 bool rock_on = true;
536 *type = NO_PRODUCTS;
538 // Does the system have any multi-installed products?
539 const ProductState* multi_state =
540 machine_state.GetProductState(system_level,
541 BrowserDistribution::CHROME_BINARIES);
542 if (multi_state != NULL)
543 ValidateBinaries(machine_state, system_level, *multi_state, &rock_on);
545 // Is Chrome installed?
546 const ProductState* product_state =
547 machine_state.GetProductState(system_level,
548 BrowserDistribution::CHROME_BROWSER);
549 if (product_state != NULL) {
550 ChromeRules chrome_rules;
551 ValidateProduct(machine_state, system_level, *product_state,
552 chrome_rules, &rock_on);
553 *type = static_cast<InstallationType>(
554 *type | (product_state->is_multi_install() ?
555 ProductBits::CHROME_MULTI :
556 ProductBits::CHROME_SINGLE));
559 #if defined(GOOGLE_CHROME_BUILD)
560 if (!InstallUtil::IsChromeSxSProcess()) {
561 // Usage of the app launcher is tracked alongside Chrome.
562 const HKEY root_key = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
563 base::string16 launcher_key(
564 UpdatingAppRegistrationData(kAppLauncherGuid).GetVersionKey());
565 base::win::RegKey key(root_key, launcher_key.c_str(),
566 KEY_QUERY_VALUE | KEY_WOW64_32KEY);
567 bool have_launcher =
568 key.Valid() && key.HasValue(google_update::kRegVersionField);
570 BrowserDistribution* chrome_dist =
571 BrowserDistribution::GetSpecificDistribution(
572 BrowserDistribution::CHROME_BROWSER);
573 if (!have_launcher) {
574 LOG_IF(ERROR, product_state) << "App launcher is not installed with "
575 << chrome_dist->GetDisplayName();
576 } else {
577 LOG_IF(ERROR, !product_state) << "App launcher is installed without "
578 << chrome_dist->GetDisplayName();
581 #endif // GOOGLE_CHROME_BUILD
583 // Is Chrome Frame installed?
584 product_state =
585 machine_state.GetProductState(system_level,
586 BrowserDistribution::CHROME_FRAME);
587 if (product_state != NULL) {
588 ChromeFrameRules chrome_frame_rules;
589 ValidateProduct(machine_state, system_level, *product_state,
590 chrome_frame_rules, &rock_on);
591 int cf_bit = !product_state->is_multi_install() ?
592 ProductBits::CHROME_FRAME_SINGLE :
593 ProductBits::CHROME_FRAME_MULTI;
594 *type = static_cast<InstallationType>(*type | cf_bit);
597 DCHECK_NE(std::find(&kInstallationTypes[0],
598 &kInstallationTypes[arraysize(kInstallationTypes)],
599 *type),
600 &kInstallationTypes[arraysize(kInstallationTypes)])
601 << "Invalid combination of products found on system (" << *type << ")";
603 return rock_on;
606 // static
607 bool InstallationValidator::ValidateInstallationType(bool system_level,
608 InstallationType* type) {
609 DCHECK(type);
610 InstallationState machine_state;
612 machine_state.Initialize();
614 return ValidateInstallationTypeForState(machine_state, system_level, type);
617 } // namespace installer