1 /*
2 * Copyright (C) 2021 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/config_flag.h"
18
19 #include <android-base/logging.h>
20 #include <android-base/strings.h>
21 #include <gflags/gflags.h>
22 #include <json/json.h>
23 #include <fstream>
24 #include <set>
25 #include <string>
26
27 #include "common/libs/utils/files.h"
28 #include "common/libs/utils/flag_parser.h"
29 #include "host/commands/assemble_cvd/flags_defaults.h"
30 #include "host/libs/config/cuttlefish_config.h"
31
32 // To support other files that use this from gflags.
33 // TODO: Add a description to this flag
34 DEFINE_string(system_image_dir, CF_DEFAULTS_SYSTEM_IMAGE_DIR, "");
35
36 using gflags::FlagSettingMode::SET_FLAGS_DEFAULT;
37
38 namespace cuttlefish {
39
40 namespace {
41
42 class SystemImageDirFlagImpl : public SystemImageDirFlag {
43 public:
INJECT(SystemImageDirFlagImpl ())44 INJECT(SystemImageDirFlagImpl()) {
45 auto help = "Location of the system partition images.";
46 flag_ = GflagsCompatFlag("system_image_dir", path_).Help(help);
47 }
Path()48 const std::string& Path() override { return path_; }
49
Name() const50 std::string Name() const override { return "SystemImageDirFlagImpl"; }
Dependencies() const51 std::unordered_set<FlagFeature*> Dependencies() const override { return {}; }
Process(std::vector<std::string> & args)52 bool Process(std::vector<std::string>& args) override {
53 path_ = DefaultGuestImagePath("");
54 if (!flag_.Parse(args)) {
55 return false;
56 }
57 // To support other files that use this from gflags.
58 FLAGS_system_image_dir = path_;
59 gflags::SetCommandLineOptionWithMode("system_image_dir", path_.c_str(),
60 SET_FLAGS_DEFAULT);
61 return true;
62 }
WriteGflagsCompatHelpXml(std::ostream &) const63 bool WriteGflagsCompatHelpXml(std::ostream&) const override {
64 // TODO(schuffelen): Write something here when this is removed from gflags
65 return true;
66 }
67
68 private:
69 std::string path_;
70 Flag flag_;
71 };
72
73 class ConfigReader : public FlagFeature {
74 public:
75 INJECT(ConfigReader()) = default;
76
HasConfig(const std::string & name) const77 bool HasConfig(const std::string& name) const {
78 return allowed_config_presets_.count(name) > 0;
79 }
AvailableConfigs() const80 const std::set<std::string>& AvailableConfigs() const {
81 return allowed_config_presets_;
82 }
ReadConfig(const std::string & name) const83 std::optional<Json::Value> ReadConfig(const std::string& name) const {
84 auto path =
85 DefaultHostArtifactsPath("etc/cvd_config/cvd_config_" + name + ".json");
86 Json::Value config;
87 Json::CharReaderBuilder builder;
88 std::ifstream ifs(path);
89 std::string errorMessage;
90 if (!Json::parseFromStream(builder, ifs, &config, &errorMessage)) {
91 LOG(ERROR) << "Could not read config file " << path << ": "
92 << errorMessage;
93 return {};
94 }
95 return config;
96 }
97
98 // FlagFeature
Name() const99 std::string Name() const override { return "ConfigReader"; }
Dependencies() const100 std::unordered_set<FlagFeature*> Dependencies() const override { return {}; }
Process(std::vector<std::string> &)101 bool Process(std::vector<std::string>&) override {
102 auto directory_contents_result =
103 DirectoryContents(DefaultHostArtifactsPath("etc/cvd_config"));
104 CHECK(directory_contents_result.ok())
105 << directory_contents_result.error().Message();
106 for (const std::string& file : *directory_contents_result) {
107 std::string_view local_file(file);
108 if (android::base::ConsumePrefix(&local_file, "cvd_config_") &&
109 android::base::ConsumeSuffix(&local_file, ".json")) {
110 allowed_config_presets_.emplace(local_file);
111 }
112 }
113 return true;
114 }
WriteGflagsCompatHelpXml(std::ostream &) const115 bool WriteGflagsCompatHelpXml(std::ostream&) const override { return true; }
116
117 private:
118 std::set<std::string> allowed_config_presets_;
119 };
120
121 class ConfigFlagImpl : public ConfigFlag {
122 public:
INJECT(ConfigFlagImpl (ConfigReader & cr,SystemImageDirFlag & s))123 INJECT(ConfigFlagImpl(ConfigReader& cr, SystemImageDirFlag& s))
124 : config_reader_(cr), system_image_dir_flag_(s) {
125 is_default_ = true;
126 config_ = "phone"; // default value
127 auto help =
128 "Config preset name. Will automatically set flag fields using the "
129 "values from this file of presets. See "
130 "device/google/cuttlefish/shared/config/config_*.json for possible "
131 "values.";
132 auto getter = [this]() { return config_; };
133 auto setter = [this](const FlagMatch& m) { return ChooseConfig(m.value); };
134 flag_ = GflagsCompatFlag("config").Help(help).Getter(getter).Setter(setter);
135 }
136
Name() const137 std::string Name() const override { return "ConfigFlagImpl"; }
Dependencies() const138 std::unordered_set<FlagFeature*> Dependencies() const override {
139 return {
140 static_cast<FlagFeature*>(&config_reader_),
141 static_cast<FlagFeature*>(&system_image_dir_flag_),
142 };
143 }
Process(std::vector<std::string> & args)144 bool Process(std::vector<std::string>& args) override {
145 if (!flag_.Parse(args)) {
146 LOG(ERROR) << "Failed to parse `--config` flag";
147 return false;
148 }
149
150 if (auto info_cfg = FindAndroidInfoConfig(); is_default_ && info_cfg) {
151 config_ = *info_cfg;
152 }
153 LOG(INFO) << "Launching CVD using --config='" << config_ << "'.";
154 auto config_values = config_reader_.ReadConfig(config_);
155 if (!config_values) {
156 LOG(ERROR) << "Failed to read config for " << config_;
157 return false;
158 }
159 for (const std::string& flag : config_values->getMemberNames()) {
160 std::string value;
161 if (flag == "custom_actions") {
162 Json::StreamWriterBuilder factory;
163 value = Json::writeString(factory, (*config_values)[flag]);
164 } else {
165 value = (*config_values)[flag].asString();
166 }
167 args.insert(args.begin(), "--" + flag + "=" + value);
168 // To avoid the flag forwarder from thinking this song is different from a
169 // default. Should fail silently if the flag doesn't exist.
170 gflags::SetCommandLineOptionWithMode(flag.c_str(), value.c_str(),
171 SET_FLAGS_DEFAULT);
172 }
173 return true;
174 }
WriteGflagsCompatHelpXml(std::ostream & out) const175 bool WriteGflagsCompatHelpXml(std::ostream& out) const override {
176 return flag_.WriteGflagsCompatXml(out);
177 }
178
179 private:
ChooseConfig(const std::string & name)180 bool ChooseConfig(const std::string& name) {
181 if (!config_reader_.HasConfig(name)) {
182 LOG(ERROR) << "Invalid --config option '" << name << "'. Valid options: "
183 << android::base::Join(config_reader_.AvailableConfigs(), ",");
184 return false;
185 }
186 config_ = name;
187 is_default_ = false;
188 return true;
189 }
FindAndroidInfoConfig() const190 std::optional<std::string> FindAndroidInfoConfig() const {
191 auto info_path = system_image_dir_flag_.Path() + "/android-info.txt";
192 if (!FileExists(info_path)) {
193 return {};
194 }
195 std::ifstream ifs{info_path};
196 if (!ifs.is_open()) {
197 return {};
198 }
199 std::string android_info;
200 ifs >> android_info;
201 std::string_view local_android_info(android_info);
202 if (!android::base::ConsumePrefix(&local_android_info, "config=")) {
203 return {};
204 }
205 if (!config_reader_.HasConfig(std::string{local_android_info})) {
206 LOG(WARNING) << info_path << " contains invalid config preset: '"
207 << local_android_info << "'.";
208 return {};
209 }
210 return std::string{local_android_info};
211 }
212
213 ConfigReader& config_reader_;
214 SystemImageDirFlag& system_image_dir_flag_;
215 std::string config_;
216 bool is_default_;
217 Flag flag_;
218 };
219
220 class ConfigFlagPlaceholderImpl : public ConfigFlag {
221 public:
INJECT(ConfigFlagPlaceholderImpl ())222 INJECT(ConfigFlagPlaceholderImpl()) {}
223
Name() const224 std::string Name() const override { return "ConfigFlagPlaceholderImpl"; }
Dependencies() const225 std::unordered_set<FlagFeature*> Dependencies() const override { return {}; }
Process(std::vector<std::string> &)226 bool Process(std::vector<std::string>&) override { return true; }
WriteGflagsCompatHelpXml(std::ostream &) const227 bool WriteGflagsCompatHelpXml(std::ostream&) const override { return true; }
228 };
229
230 } // namespace
231
ConfigFlagComponent()232 fruit::Component<SystemImageDirFlag, ConfigFlag> ConfigFlagComponent() {
233 return fruit::createComponent()
234 .addMultibinding<FlagFeature, ConfigReader>()
235 .bind<ConfigFlag, ConfigFlagImpl>()
236 .addMultibinding<FlagFeature, ConfigFlag>()
237 .bind<SystemImageDirFlag, SystemImageDirFlagImpl>()
238 .addMultibinding<FlagFeature, SystemImageDirFlag>();
239 }
240
ConfigFlagPlaceholder()241 fruit::Component<ConfigFlag> ConfigFlagPlaceholder() {
242 return fruit::createComponent()
243 .addMultibinding<FlagFeature, ConfigFlag>()
244 .bind<ConfigFlag, ConfigFlagPlaceholderImpl>();
245 }
246
247 } // namespace cuttlefish
248