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 "android-base/logging.h"
25 #include "android-base/strings.h"
26 #include "gflags/gflags.h"
27 #include "json/json.h"
28
29 #include "common/libs/utils/files.h"
30
31 namespace cuttlefish {
32
33 namespace {
34
35 const char* kFlags = "flags";
36 const char* kCvdFiles = "cvd_files";
37 const char* kCvdFileSource = "source";
38 const char* kCvdFileBuildId = "build_id";
39 const char* kCvdFileBuildTarget = "build_target";
40
SourceStringToEnum(std::string source)41 FileSource SourceStringToEnum(std::string source) {
42 for (auto& c : source) {
43 c = std::tolower(c);
44 }
45 if (source == "default_build") {
46 return FileSource::DEFAULT_BUILD;
47 } else if (source == "system_build") {
48 return FileSource::SYSTEM_BUILD;
49 } else if (source == "kernel_build") {
50 return FileSource::KERNEL_BUILD;
51 } else if (source == "local_file") {
52 return FileSource::LOCAL_FILE;
53 } else if (source == "generated") {
54 return FileSource::GENERATED;
55 } else {
56 return FileSource::UNKNOWN_PURPOSE;
57 }
58 }
59
SourceEnumToString(const FileSource & source)60 std::string SourceEnumToString(const FileSource& source) {
61 if (source == FileSource::DEFAULT_BUILD) {
62 return "default_build";
63 } else if (source == FileSource::SYSTEM_BUILD) {
64 return "system_build";
65 } else if (source == FileSource::KERNEL_BUILD) {
66 return "kernel_build";
67 } else if (source == FileSource::LOCAL_FILE) {
68 return "local_file";
69 } else if (source == FileSource::GENERATED) {
70 return "generated";
71 } else {
72 return "unknown";
73 }
74 }
75
76 } // namespace
77
CvdFile()78 CvdFile::CvdFile() {
79 }
80
CvdFile(const FileSource & source,const std::string & build_id,const std::string & build_target,const std::string & file_path)81 CvdFile::CvdFile(const FileSource& source, const std::string& build_id,
82 const std::string& build_target, const std::string& file_path)
83 : source(source), build_id(build_id), build_target(build_target), file_path(file_path) {
84 }
85
operator <<(std::ostream & os,const CvdFile & cvd_file)86 std::ostream& operator<<(std::ostream& os, const CvdFile& cvd_file) {
87 os << "CvdFile(";
88 os << "source = " << SourceEnumToString(cvd_file.source) << ", ";
89 os << "build_id = " << cvd_file.build_id << ", ";
90 os << "build_target = " << cvd_file.build_target << ", ";
91 os << "file_path = " << cvd_file.file_path << ")";
92 return os;
93 }
94
FetcherConfig()95 FetcherConfig::FetcherConfig() : dictionary_(new Json::Value()) {
96 }
97
98 FetcherConfig::FetcherConfig(FetcherConfig&&) = default;
99
~FetcherConfig()100 FetcherConfig::~FetcherConfig() {
101 }
102
SaveToFile(const std::string & file) const103 bool FetcherConfig::SaveToFile(const std::string& file) const {
104 std::ofstream ofs(file);
105 if (!ofs.is_open()) {
106 LOG(ERROR) << "Unable to write to file " << file;
107 return false;
108 }
109 ofs << *dictionary_;
110 return !ofs.fail();
111 }
112
LoadFromFile(const std::string & file)113 bool FetcherConfig::LoadFromFile(const std::string& file) {
114 auto real_file_path = AbsolutePath(file);
115 if (real_file_path.empty()) {
116 LOG(ERROR) << "Could not get real path for file " << file;
117 return false;
118 }
119 Json::CharReaderBuilder builder;
120 std::ifstream ifs(real_file_path);
121 std::string errorMessage;
122 if (!Json::parseFromStream(builder, ifs, dictionary_.get(), &errorMessage)) {
123 LOG(ERROR) << "Could not read config file " << file << ": " << errorMessage;
124 return false;
125 }
126 return true;
127 }
128
RecordFlags()129 void FetcherConfig::RecordFlags() {
130 std::vector<gflags::CommandLineFlagInfo> all_flags;
131 GetAllFlags(&all_flags);
132 Json::Value flags_json(Json::arrayValue);
133 for (const auto& flag : all_flags) {
134 Json::Value flag_json;
135 flag_json["name"] = flag.name;
136 flag_json["type"] = flag.type;
137 flag_json["description"] = flag.description;
138 flag_json["current_value"] = flag.current_value;
139 flag_json["default_value"] = flag.default_value;
140 flag_json["filename"] = flag.filename;
141 flag_json["has_validator_fn"] = flag.has_validator_fn;
142 flag_json["is_default"] = flag.is_default;
143 flags_json.append(flag_json);
144 }
145 (*dictionary_)[kFlags] = flags_json;
146 }
147
148 namespace {
149
JsonToCvdFile(const std::string & file_path,const Json::Value & json)150 CvdFile JsonToCvdFile(const std::string& file_path, const Json::Value& json) {
151 CvdFile cvd_file;
152 cvd_file.file_path = file_path;
153 if (json.isMember(kCvdFileSource)) {
154 cvd_file.source = SourceStringToEnum(json[kCvdFileSource].asString());
155 } else {
156 cvd_file.source = UNKNOWN_PURPOSE;
157 }
158 if (json.isMember(kCvdFileBuildId)) {
159 cvd_file.build_id = json[kCvdFileBuildId].asString();
160 }
161 if (json.isMember(kCvdFileBuildTarget)) {
162 cvd_file.build_target = json[kCvdFileBuildTarget].asString();
163 }
164 return cvd_file;
165 }
166
CvdFileToJson(const CvdFile & cvd_file)167 Json::Value CvdFileToJson(const CvdFile& cvd_file) {
168 Json::Value json;
169 json[kCvdFileSource] = SourceEnumToString(cvd_file.source);
170 json[kCvdFileBuildId] = cvd_file.build_id;
171 json[kCvdFileBuildTarget] = cvd_file.build_target;
172 return json;
173 }
174
175 } // namespace
176
add_cvd_file(const CvdFile & file,bool override_entry)177 bool FetcherConfig::add_cvd_file(const CvdFile& file, bool override_entry) {
178 if (!dictionary_->isMember(kCvdFiles)) {
179 Json::Value files_json(Json::objectValue);
180 (*dictionary_)[kCvdFiles] = files_json;
181 }
182 if ((*dictionary_)[kCvdFiles].isMember(file.file_path) && !override_entry) {
183 return false;
184 }
185 (*dictionary_)[kCvdFiles][file.file_path] = CvdFileToJson(file);
186 return true;
187 }
188
get_cvd_files() const189 std::map<std::string, CvdFile> FetcherConfig::get_cvd_files() const {
190 if (!dictionary_->isMember(kCvdFiles)) {
191 return {};
192 }
193 std::map<std::string, CvdFile> files;
194 const auto& json_files = (*dictionary_)[kCvdFiles];
195 for (auto it = json_files.begin(); it != json_files.end(); it++) {
196 files[it.key().asString()] = JsonToCvdFile(it.key().asString(), *it);
197 }
198 return files;
199 }
200
FindCvdFileWithSuffix(const std::string & suffix) const201 std::string FetcherConfig::FindCvdFileWithSuffix(const std::string& suffix) const {
202 if (!dictionary_->isMember(kCvdFiles)) {
203 return {};
204 }
205 const auto& json_files = (*dictionary_)[kCvdFiles];
206 for (auto it = json_files.begin(); it != json_files.end(); it++) {
207 const auto& file = it.key().asString();
208 if (android::base::EndsWith(file, suffix)) {
209 return file;
210 }
211 }
212 LOG(DEBUG) << "Could not find file ending in " << suffix;
213 return "";
214 }
215
216 } // namespace cuttlefish
217