| // Copyright 2019 The Fuchsia 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 "build_info.h" |
| |
| #include <fuchsia/sysinfo/cpp/fidl.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/fidl/cpp/string.h> |
| #include <lib/fidl/cpp/synchronous_interface_ptr.h> |
| #include <zircon/status.h> |
| #include <zircon/types.h> |
| |
| #include "src/lib/files/file.h" |
| #include "src/lib/fxl/strings/trim.h" |
| |
| namespace harvester { |
| |
| BuildInfoValue ManifestFinder::Find() { |
| if (content_.empty()) { |
| return BuildInfoValue(BuildInfoError::kEmptyFile); |
| } |
| // Find the one "project" with @name=name_. |
| if (!MoveToProjectWithName(name_)) { |
| return BuildInfoValue(BuildInfoError::kMissingProject); |
| } |
| // We are pointing at the project now so return the value of attribute_. |
| return GetAttributeForCurrentElement(attribute_); |
| } |
| |
| bool ManifestFinder::MovePast(char c) { |
| if (pos_ >= content_.length()) { |
| return false; |
| } |
| pos_ = content_.find_first_of(c, pos_); |
| if (pos_ == std::string::npos) { |
| return false; |
| } |
| ++pos_; |
| return pos_ < content_.length(); |
| } |
| |
| bool ManifestFinder::MoveTo(const std::string& target) { |
| pos_ = content_.find(target, pos_); |
| return pos_ != std::string::npos; |
| } |
| |
| bool ManifestFinder::MoveToProjectWithName(const std::string& name) { |
| std::string project = "<project "; |
| std::string name_attr = "name="; |
| |
| while (pos_ < content_.length()) { |
| // Move to the next project or return false if finding one fails. |
| if (!MoveTo(project)) { |
| return false; |
| } |
| |
| size_t name_pos = content_.find(name_attr, pos_); |
| if (name_pos == std::string::npos) { |
| return false; |
| } |
| |
| // The name attribute looks like name="xxx", this moves name_pos to point |
| // at the first " after the =. |
| name_pos += name_attr.length(); |
| if (name_pos >= content_.length()) { |
| return false; |
| } |
| |
| std::string target = '"' + name + '"'; |
| |
| // If the name matches exactly then pos_ is pointing at the beginning of |
| // this project element so we can just return true. |
| if (content_.compare(name_pos, target.length(), target) == 0) { |
| return true; |
| } |
| // Otherwise we move pos_ past this project and loop back around to look |
| // at the next one if possible. |
| if (!MovePast('>')) { |
| return false; |
| } |
| } |
| |
| return false; |
| } |
| |
| BuildInfoValue ManifestFinder::GetAttributeForCurrentElement( |
| const std::string& attr) { |
| size_t attr_pos = content_.find(attr + '=', pos_); |
| if (attr_pos == std::string::npos) { |
| return BuildInfoValue(BuildInfoError::kMissingAttribute); |
| } |
| size_t attr_value_begin = attr_pos + attr.length() + 2; |
| if (attr_value_begin >= content_.length()) { |
| return BuildInfoValue(BuildInfoError::kMalformedFile); |
| } |
| |
| size_t attr_value_end = content_.find_first_of('"', attr_value_begin); |
| if (attr_value_end == std::string::npos || |
| attr_value_begin >= attr_value_end) { |
| return BuildInfoValue(BuildInfoError::kMalformedFile); |
| } |
| |
| size_t check_format = content_.find_first_of(">=", attr_value_begin); |
| if (check_format == std::string::npos || check_format < attr_value_end) { |
| return BuildInfoValue(BuildInfoError::kMalformedFile); |
| } |
| |
| return BuildInfoValue( |
| content_.substr(attr_value_begin, attr_value_end - attr_value_begin)); |
| } |
| |
| // Reads /config/build-info/snapshot and returns the value of |
| // manifest/projects/project[@name="fuchsia"]/@revision |
| // Returns an empty string if this fails for any reason. |
| BuildInfoValue GetFuchsiaBuildVersion() { |
| std::string filepath("/config/build-info/snapshot"); |
| std::string content; |
| if (!files::ReadFileToString(filepath, &content)) { |
| return BuildInfoValue(BuildInfoError::kMissingFile); |
| } |
| |
| if (content.empty()) { |
| return BuildInfoValue(BuildInfoError::kEmptyFile); |
| } |
| |
| return ManifestFinder(content, "fuchsia", "revision").Find(); |
| } |
| |
| BuildInfoValue AnnotationsProvider::GetDeviceBoardName() { |
| fuchsia::sysinfo::SysInfoSyncPtr sysinfo; |
| |
| if (const zx_status_t status = |
| fdio_service_connect("/svc/fuchsia.sysinfo.SysInfo", |
| sysinfo.NewRequest().TakeChannel().release()); |
| status != ZX_OK) { |
| return BuildInfoValue(BuildInfoError::kConnectionError); |
| } |
| |
| ::fidl::StringPtr out_board_name; |
| zx_status_t out_status; |
| if (const zx_status_t status = |
| sysinfo->GetBoardName(&out_status, &out_board_name); |
| status != ZX_OK) { |
| return BuildInfoValue(BuildInfoError::kConnectionError); |
| } |
| if (out_status != ZX_OK) { |
| return BuildInfoValue(BuildInfoError::kBadValue); |
| } |
| if (!out_board_name) { |
| return BuildInfoValue(BuildInfoError::kMissingValue); |
| } |
| |
| return BuildInfoValue(out_board_name.value()); |
| } |
| |
| BuildInfoValue AnnotationsProvider::ReadAnnotationFromFilepath( |
| const std::string& filepath) { |
| std::string content; |
| if (!files::ReadFileToString(filepath, &content)) { |
| return BuildInfoValue(BuildInfoError::kMissingFile); |
| } |
| return BuildInfoValue(std::string(fxl::TrimString(content, "\r\n"))); |
| } |
| |
| BuildAnnotations AnnotationsProvider::GetAnnotations() { |
| return BuildAnnotations{ |
| .buildBoard = ReadAnnotationFromFilepath("/config/build-info/board"), |
| .buildProduct = ReadAnnotationFromFilepath("/config/build-info/product"), |
| .deviceBoardName = GetDeviceBoardName(), |
| }; |
| } |
| |
| } // namespace harvester |