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