| /* Copyright 2019 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "hardware_verifier/cli.h" |
| |
| #include <iostream> |
| #include <sstream> |
| #include <string> |
| |
| #include <base/files/file_path.h> |
| #include <base/logging.h> |
| #include <base/optional.h> |
| #include <google/protobuf/io/zero_copy_stream_impl.h> |
| #include <google/protobuf/text_format.h> |
| #include <google/protobuf/util/json_util.h> |
| #include <runtime_probe/proto_bindings/runtime_probe.pb.h> |
| |
| #include "hardware_verifier/hardware_verifier.pb.h" |
| #include "hardware_verifier/hw_verification_spec_getter_impl.h" |
| #include "hardware_verifier/observer.h" |
| #include "hardware_verifier/probe_result_getter_impl.h" |
| #include "hardware_verifier/verifier_impl.h" |
| |
| namespace hardware_verifier { |
| |
| namespace { |
| |
| base::Optional<std::string> OutputInTextFormat( |
| HwVerificationReport hw_verification_report, bool pii) { |
| std::stringstream ss; |
| const auto generic_device_info = hw_verification_report.generic_device_info(); |
| hw_verification_report.clear_generic_device_info(); |
| |
| // Output the AVL qualification status in JSON format. |
| auto json_print_opts = google::protobuf::util::JsonPrintOptions(); |
| json_print_opts.add_whitespace = true; |
| json_print_opts.always_print_primitive_fields = true; |
| std::string json_output_data; |
| const auto convert_status = google::protobuf::util::MessageToJsonString( |
| hw_verification_report, &json_output_data, json_print_opts); |
| if (!convert_status.ok()) { |
| LOG(ERROR) << "Failed to output the qualification report in JSON: " |
| << convert_status.ToString() << "."; |
| return base::nullopt; |
| } |
| ss << "[Component Qualification Status]\n" << json_output_data; |
| |
| if (pii) { |
| // Output the generic device info in prototxt format. |
| ss << "\n[Generic Device Info]\n"; |
| // Enclose google::protobuf::io::OstreamOutputStream in another nested |
| // scope so that its data will be flushed while being destroyed. |
| google::protobuf::io::OstreamOutputStream ostream_output_stream{&ss}; |
| if (!google::protobuf::TextFormat::Print(generic_device_info, |
| &ostream_output_stream)) { |
| LOG(ERROR) |
| << "Failed to output the generic device info in prototxt format."; |
| return base::nullopt; |
| } |
| } |
| return ss.str(); |
| } |
| |
| } // namespace |
| |
| CLI::CLI() |
| : pr_getter_(std::make_unique<ProbeResultGetterImpl>()), |
| vp_getter_(std::make_unique<HwVerificationSpecGetterImpl>()), |
| verifier_(std::make_unique<VerifierImpl>()), |
| output_stream_(&std::cout) {} |
| |
| CLIVerificationResult CLI::Run(const std::string& probe_result_file, |
| const std::string& hw_verification_spec_file, |
| const CLIOutputFormat output_format, |
| bool pii) { |
| LOG(INFO) << "Get the verification payload."; |
| base::Optional<HwVerificationSpec> hw_verification_spec; |
| if (hw_verification_spec_file.empty()) { |
| hw_verification_spec = vp_getter_->GetDefault(); |
| } else { |
| hw_verification_spec = |
| vp_getter_->GetFromFile(base::FilePath(hw_verification_spec_file)); |
| } |
| if (!hw_verification_spec) { |
| return CLIVerificationResult::kInvalidHwVerificationSpecFile; |
| } |
| |
| LOG(INFO) << "Get the probe result."; |
| base::Optional<runtime_probe::ProbeResult> probe_result; |
| auto observer = Observer::GetInstance(); |
| |
| if (probe_result_file.empty()) { |
| observer->StartTimer(hardware_verifier::kMetricTimeToProbe); |
| probe_result = pr_getter_->GetFromRuntimeProbe(); |
| observer->StopTimer(hardware_verifier::kMetricTimeToProbe); |
| |
| if (!probe_result) { |
| return CLIVerificationResult::kProbeFail; |
| } |
| } else { |
| probe_result = pr_getter_->GetFromFile(base::FilePath(probe_result_file)); |
| if (!probe_result) { |
| return CLIVerificationResult::kInvalidProbeResultFile; |
| } |
| } |
| |
| LOG(INFO) << "Verify the probe result by the verification payload."; |
| const auto verifier_result = |
| verifier_->Verify(probe_result.value(), hw_verification_spec.value()); |
| if (!verifier_result) { |
| return CLIVerificationResult::kProbeResultHwVerificationSpecMisalignment; |
| } |
| auto hw_verification_report = verifier_result.value(); |
| |
| if (!pii) { |
| // Remove PII data. |
| for (auto& mutable_component : |
| *(hw_verification_report.mutable_found_component_infos())) { |
| mutable_component.clear_component_uuid(); |
| } |
| hw_verification_report.clear_generic_device_info(); |
| } |
| |
| LOG(INFO) << "Output the report."; |
| switch (output_format) { |
| case CLIOutputFormat::kProtoBin: { |
| std::string s; |
| if (!hw_verification_report.SerializeToString(&s)) { |
| return CLIVerificationResult::kUnknownError; |
| } |
| LOG(INFO) << "Output the report in protobuf binary format, " << s.size() |
| << "bytes."; |
| *output_stream_ << s; |
| break; |
| } |
| case CLIOutputFormat::kText: { |
| auto output_data = OutputInTextFormat(hw_verification_report, pii); |
| if (!output_data.has_value()) { |
| return CLIVerificationResult::kUnknownError; |
| } |
| LOG(INFO) << "Output the report in text format:"; |
| LOG(INFO) << output_data.value(); |
| *output_stream_ << output_data.value(); |
| } |
| } |
| |
| LOG(INFO) << "Send to Observer."; |
| observer->RecordHwVerificationReport(hw_verification_report); |
| |
| return (hw_verification_report.is_compliant() ? CLIVerificationResult::kPass |
| : CLIVerificationResult::kFail); |
| } |
| |
| } // namespace hardware_verifier |