1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "host/libs/config/fetcher_config.h"
18
19 #include <fstream>
20 #include <map>
21 #include <string>
22 #include <vector>
23
24 #include <gflags/gflags.h>
25 #include <android-base/logging.h>
26 #include <json/json.h>
27
28 #include "common/libs/utils/files.h"
29
30 namespace cuttlefish {
31
32 namespace {
33
34 const char* kFlags = "flags";
35 const char* kCvdFiles = "cvd_files";
36 const char* kCvdFileSource = "source";
37 const char* kCvdFileBuildId = "build_id";
38 const char* kCvdFileBuildTarget = "build_target";
39
SourceStringToEnum(std::string source)40 FileSource SourceStringToEnum(std::string source) {
41 for (auto& c : source) {
42 c = std::tolower(c);
43 }
44 if (source == "default_build") {
45 return FileSource::DEFAULT_BUILD;
46 } else if (source == "system_build") {
47 return FileSource::SYSTEM_BUILD;
48 } else if (source == "kernel_build") {
49 return FileSource::KERNEL_BUILD;
50 } else if (source == "local_file") {
51 return FileSource::LOCAL_FILE;
52 } else if (source == "generated") {
53 return FileSource::GENERATED;
54 } else {
55 return FileSource::UNKNOWN_PURPOSE;
56 }
57 }
58
SourceEnumToString(const FileSource & source)59 std::string SourceEnumToString(const FileSource& source) {
60 if (source == FileSource::DEFAULT_BUILD) {
61 return "default_build";
62 } else if (source == FileSource::SYSTEM_BUILD) {
63 return "system_build";
64 } else if (source == FileSource::KERNEL_BUILD) {
65 return "kernel_build";
66 } else if (source == FileSource::LOCAL_FILE) {
67 return "local_file";
68 } else if (source == FileSource::GENERATED) {
69 return "generated";
70 } else {
71 return "unknown";
72 }
73 }
74
75 } // namespace
76
CvdFile()77 CvdFile::CvdFile() {
78 }
79
CvdFile(const FileSource & source,const std::string & build_id,const std::string & build_target,const std::string & file_path)80 CvdFile::CvdFile(const FileSource& source, const std::string& build_id,
81 const std::string& build_target, const std::string& file_path)
82 : source(source), build_id(build_id), build_target(build_target), file_path(file_path) {
83 }
84
operator <<(std::ostream & os,const CvdFile & cvd_file)85 std::ostream& operator<<(std::ostream& os, const CvdFile& cvd_file) {
86 os << "CvdFile(";
87 os << "source = " << SourceEnumToString(cvd_file.source) << ", ";
88 os << "build_id = " << cvd_file.build_id << ", ";
89 os << "build_target = " << cvd_file.build_target << ", ";
90 os << "file_path = " << cvd_file.file_path << ")";
91 return os;
92 }
93
FetcherConfig()94 FetcherConfig::FetcherConfig() : dictionary_(new Json::Value()) {
95 }
96
97 FetcherConfig::FetcherConfig(FetcherConfig&&) = default;
98
~FetcherConfig()99 FetcherConfig::~FetcherConfig() {
100 }
101
SaveToFile(const std::string & file) const102 bool FetcherConfig::SaveToFile(const std::string& file) const {
103 std::ofstream ofs(file);
104 if (!ofs.is_open()) {
105 LOG(ERROR) << "Unable to write to file " << file;
106 return false;
107 }
108 ofs << *dictionary_;
109 return !ofs.fail();
110 }
111
LoadFromFile(const std::string & file)112 bool FetcherConfig::LoadFromFile(const std::string& file) {
113 auto real_file_path = cuttlefish::AbsolutePath(file);
114 if (real_file_path.empty()) {
115 LOG(ERROR) << "Could not get real path for file " << file;
116 return false;
117 }
118 Json::Reader reader;
119 std::ifstream ifs(real_file_path);
120 if (!reader.parse(ifs, *dictionary_)) {
121 LOG(ERROR) << "Could not read config file " << file << ": "
122 << reader.getFormattedErrorMessages();
123 return false;
124 }
125 return true;
126 }
127
RecordFlags()128 void FetcherConfig::RecordFlags() {
129 std::vector<gflags::CommandLineFlagInfo> all_flags;
130 GetAllFlags(&all_flags);
131 Json::Value flags_json(Json::arrayValue);
132 for (const auto& flag : all_flags) {
133 Json::Value flag_json;
134 flag_json["name"] = flag.name;
135 flag_json["type"] = flag.type;
136 flag_json["description"] = flag.description;
137 flag_json["current_value"] = flag.current_value;
138 flag_json["default_value"] = flag.default_value;
139 flag_json["filename"] = flag.filename;
140 flag_json["has_validator_fn"] = flag.has_validator_fn;
141 flag_json["is_default"] = flag.is_default;
142 flags_json.append(flag_json);
143 }
144 (*dictionary_)[kFlags] = flags_json;
145 }
146
147 namespace {
148
JsonToCvdFile(const std::string & file_path,const Json::Value & json)149 CvdFile JsonToCvdFile(const std::string& file_path, const Json::Value& json) {
150 CvdFile cvd_file;
151 cvd_file.file_path = file_path;
152 if (json.isMember(kCvdFileSource)) {
153 cvd_file.source = SourceStringToEnum(json[kCvdFileSource].asString());
154 } else {
155 cvd_file.source = UNKNOWN_PURPOSE;
156 }
157 if (json.isMember(kCvdFileBuildId)) {
158 cvd_file.build_id = json[kCvdFileBuildId].asString();
159 }
160 if (json.isMember(kCvdFileBuildTarget)) {
161 cvd_file.build_target = json[kCvdFileBuildTarget].asString();
162 }
163 return cvd_file;
164 }
165
CvdFileToJson(const CvdFile & cvd_file)166 Json::Value CvdFileToJson(const CvdFile& cvd_file) {
167 Json::Value json;
168 json[kCvdFileSource] = SourceEnumToString(cvd_file.source);
169 json[kCvdFileBuildId] = cvd_file.build_id;
170 json[kCvdFileBuildTarget] = cvd_file.build_target;
171 return json;
172 }
173
174 } // namespace
175
add_cvd_file(const CvdFile & file,bool override_entry)176 bool FetcherConfig::add_cvd_file(const CvdFile& file, bool override_entry) {
177 if (!dictionary_->isMember(kCvdFiles)) {
178 Json::Value files_json(Json::objectValue);
179 (*dictionary_)[kCvdFiles] = files_json;
180 }
181 if ((*dictionary_)[kCvdFiles].isMember(file.file_path) && !override_entry) {
182 return false;
183 }
184 (*dictionary_)[kCvdFiles][file.file_path] = CvdFileToJson(file);
185 return true;
186 }
187
get_cvd_files() const188 std::map<std::string, CvdFile> FetcherConfig::get_cvd_files() const {
189 if (!dictionary_->isMember(kCvdFiles)) {
190 return {};
191 }
192 std::map<std::string, CvdFile> files;
193 const auto& json_files = (*dictionary_)[kCvdFiles];
194 for (auto it = json_files.begin(); it != json_files.end(); it++) {
195 files[it.key().asString()] = JsonToCvdFile(it.key().asString(), *it);
196 }
197 return files;
198 }
199
FindCvdFileWithSuffix(const std::string & suffix) const200 std::string FetcherConfig::FindCvdFileWithSuffix(const std::string& suffix) const {
201 if (!dictionary_->isMember(kCvdFiles)) {
202 return {};
203 }
204 const auto& json_files = (*dictionary_)[kCvdFiles];
205 for (auto it = json_files.begin(); it != json_files.end(); it++) {
206 auto file = it.key().asString();
207 auto expected_pos = file.size() - suffix.size();
208 if (file.rfind(suffix) == expected_pos) {
209 return file;
210 }
211 }
212 LOG(DEBUG) << "Could not find file ending in " << suffix;
213 return "";
214 }
215
216 } // namespace cuttlefish
217