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/file.h>
20 #include <android-base/logging.h>
21 #include <android-base/strings.h>
22 #include <gflags/gflags.h>
23 #include <json/json.h>
24 #include <fstream>
25 #include <set>
26 #include <string>
27
28 #include "common/libs/utils/files.h"
29 #include "common/libs/utils/flag_parser.h"
30 #include "common/libs/utils/json.h"
31 #include "host/commands/assemble_cvd/flags_defaults.h"
32 #include "host/libs/config/cuttlefish_config.h"
33
34 // To support other files that use this from gflags.
35 // TODO: Add a description to this flag
36 DEFINE_string(system_image_dir, CF_DEFAULTS_SYSTEM_IMAGE_DIR, "");
37
38 using gflags::FlagSettingMode::SET_FLAGS_DEFAULT;
39 using android::base::ReadFileToString;
40 using android::base::Split;
41
42 namespace cuttlefish {
43
44 namespace {
45
46 class SystemImageDirFlagImpl : public SystemImageDirFlag {
47 public:
INJECT(SystemImageDirFlagImpl ())48 INJECT(SystemImageDirFlagImpl()) {
49 auto help = "Location of the system partition images.";
50 flag_ = GflagsCompatFlag("system_image_dir", path_).Help(help);
51 }
Path()52 const std::string& Path() override { return path_; }
53
Name() const54 std::string Name() const override { return "SystemImageDirFlagImpl"; }
Dependencies() const55 std::unordered_set<FlagFeature*> Dependencies() const override { return {}; }
Process(std::vector<std::string> & args)56 Result<void> Process(std::vector<std::string>& args) override {
57 path_ = DefaultGuestImagePath("");
58 CF_EXPECT(flag_.Parse(args));
59 // To support other files that use this from gflags.
60 FLAGS_system_image_dir = path_;
61 gflags::SetCommandLineOptionWithMode("system_image_dir", path_.c_str(),
62 SET_FLAGS_DEFAULT);
63 return {};
64 }
WriteGflagsCompatHelpXml(std::ostream &) const65 bool WriteGflagsCompatHelpXml(std::ostream&) const override {
66 // TODO(schuffelen): Write something here when this is removed from gflags
67 return true;
68 }
69
70 private:
71 std::string path_;
72 Flag flag_;
73 };
74
75 class ConfigReader : public FlagFeature {
76 public:
77 INJECT(ConfigReader()) = default;
78
HasConfig(const std::string & name) const79 bool HasConfig(const std::string& name) const {
80 return allowed_config_presets_.count(name) > 0;
81 }
AvailableConfigs() const82 const std::set<std::string>& AvailableConfigs() const {
83 return allowed_config_presets_;
84 }
ReadConfig(const std::string & name) const85 Result<Json::Value> ReadConfig(const std::string& name) const {
86 auto path =
87 DefaultHostArtifactsPath("etc/cvd_config/cvd_config_" + name + ".json");
88 std::string config_contents;
89 CF_EXPECTF(android::base::ReadFileToString(path, &config_contents),
90 "Could not read config file \"{}\"", path);
91 return CF_EXPECTF(ParseJson(config_contents),
92 "Could not parse config file \"{}\"", path);
93 }
94
95 // FlagFeature
Name() const96 std::string Name() const override { return "ConfigReader"; }
Dependencies() const97 std::unordered_set<FlagFeature*> Dependencies() const override { return {}; }
Process(std::vector<std::string> &)98 Result<void> Process(std::vector<std::string>&) override {
99 auto config_path = DefaultHostArtifactsPath("etc/cvd_config");
100 auto dir_contents = CF_EXPECT(DirectoryContents(config_path));
101 for (const std::string& file : dir_contents) {
102 std::string_view local_file(file);
103 if (android::base::ConsumePrefix(&local_file, "cvd_config_") &&
104 android::base::ConsumeSuffix(&local_file, ".json")) {
105 allowed_config_presets_.emplace(local_file);
106 }
107 }
108 return {};
109 }
WriteGflagsCompatHelpXml(std::ostream &) const110 bool WriteGflagsCompatHelpXml(std::ostream&) const override { return true; }
111
112 private:
113 std::set<std::string> allowed_config_presets_;
114 };
115
116 class ConfigFlagImpl : public ConfigFlag {
117 public:
INJECT(ConfigFlagImpl (ConfigReader & cr,SystemImageDirFlag & s))118 INJECT(ConfigFlagImpl(ConfigReader& cr, SystemImageDirFlag& s))
119 : config_reader_(cr), system_image_dir_flag_(s) {
120 is_default_ = true;
121 config_ = "phone"; // default value
122 auto help =
123 "Config preset name. Will automatically set flag fields using the "
124 "values from this file of presets. See "
125 "device/google/cuttlefish/shared/config/config_*.json for possible "
126 "values.";
127 auto getter = [this]() { return config_; };
128 auto setter = [this](const FlagMatch& m) -> Result<void> {
129 CF_EXPECT(ChooseConfig(m.value));
130 return {};
131 };
132 flag_ = GflagsCompatFlag("config").Help(help).Getter(getter).Setter(setter);
133 }
134
Name() const135 std::string Name() const override { return "ConfigFlagImpl"; }
Dependencies() const136 std::unordered_set<FlagFeature*> Dependencies() const override {
137 return {
138 static_cast<FlagFeature*>(&config_reader_),
139 static_cast<FlagFeature*>(&system_image_dir_flag_),
140 };
141 }
Process(std::vector<std::string> & args)142 Result<void> Process(std::vector<std::string>& args) override {
143 CF_EXPECT(flag_.Parse(args), "Failed to parse `--config` flag");
144
145 if (auto info_cfg = FindAndroidInfoConfig(); is_default_ && info_cfg) {
146 config_ = *info_cfg;
147 }
148 LOG(INFO) << "Launching CVD using --config='" << config_ << "'.";
149 auto config_values = CF_EXPECT(config_reader_.ReadConfig(config_));
150 for (const std::string& flag : config_values.getMemberNames()) {
151 std::string value;
152 if (flag == "custom_actions") {
153 Json::StreamWriterBuilder factory;
154 value = Json::writeString(factory, config_values[flag]);
155 } else {
156 value = config_values[flag].asString();
157 }
158 args.insert(args.begin(), "--" + flag + "=" + value);
159 // To avoid the flag forwarder from thinking this song is different from a
160 // default. Should fail silently if the flag doesn't exist.
161 gflags::SetCommandLineOptionWithMode(flag.c_str(), value.c_str(),
162 SET_FLAGS_DEFAULT);
163 }
164 return {};
165 }
WriteGflagsCompatHelpXml(std::ostream & out) const166 bool WriteGflagsCompatHelpXml(std::ostream& out) const override {
167 return flag_.WriteGflagsCompatXml(out);
168 }
169
170 private:
ChooseConfig(const std::string & name)171 Result<void> ChooseConfig(const std::string& name) {
172 CF_EXPECTF(config_reader_.HasConfig(name),
173 "Invalid --config option '{}'. Valid options: [{}]", name,
174 fmt::join(config_reader_.AvailableConfigs(), ","));
175 config_ = name;
176 is_default_ = false;
177 return {};
178 }
FindAndroidInfoConfig() const179 std::optional<std::string> FindAndroidInfoConfig() const {
180 auto info_path =
181 android::base::Split(system_image_dir_flag_.Path(), ",")[0] +
182 "/android-info.txt";
183
184 LOG(INFO) << "Reading --config option from: " << info_path;
185 if (!FileExists(info_path)) {
186 return {};
187 }
188 std::string android_info;
189 if(!ReadFileToString(info_path, &android_info)) {
190 return {};
191 }
192 // grab the last value of config in android-info.txt,
193 // it's the setting that's respected.
194 // TODO (rammuthiah) Replace this logic with ParseMiscInfo
195 // from host/commands/assemble_cvd/misc_info.h
196 // Currently blocked on linking error for misc_info which is part of
197 // assemble_cvd and this bit of code which is in run_cvd.
198 size_t config_idx = android_info.rfind("config=");
199 if (config_idx == std::string::npos) {
200 return {};
201 }
202 std::string config_value = android_info.substr(config_idx);
203 std::string_view local_config_value(config_value);
204 if (!android::base::ConsumePrefix(&local_config_value, "config=")) {
205 return {};
206 }
207 auto split_config = Split(std::string{local_config_value},"\n");
208 if (split_config.empty()) {
209 return {};
210 }
211 config_value = split_config[0];
212 if (!config_reader_.HasConfig(config_value)) {
213 LOG(WARNING) << info_path << " contains invalid config preset: '"
214 << config_value << "'.";
215 return {};
216 }
217 return config_value;
218 }
219
220 ConfigReader& config_reader_;
221 SystemImageDirFlag& system_image_dir_flag_;
222 std::string config_;
223 bool is_default_;
224 Flag flag_;
225 };
226
227 class ConfigFlagPlaceholderImpl : public ConfigFlag {
228 public:
INJECT(ConfigFlagPlaceholderImpl ())229 INJECT(ConfigFlagPlaceholderImpl()) {}
230
Name() const231 std::string Name() const override { return "ConfigFlagPlaceholderImpl"; }
Dependencies() const232 std::unordered_set<FlagFeature*> Dependencies() const override { return {}; }
Process(std::vector<std::string> &)233 Result<void> Process(std::vector<std::string>&) override { return {}; }
WriteGflagsCompatHelpXml(std::ostream &) const234 bool WriteGflagsCompatHelpXml(std::ostream&) const override { return true; }
235 };
236
237 } // namespace
238
ConfigFlagComponent()239 fruit::Component<SystemImageDirFlag, ConfigFlag> ConfigFlagComponent() {
240 return fruit::createComponent()
241 .addMultibinding<FlagFeature, ConfigReader>()
242 .bind<ConfigFlag, ConfigFlagImpl>()
243 .addMultibinding<FlagFeature, ConfigFlag>()
244 .bind<SystemImageDirFlag, SystemImageDirFlagImpl>()
245 .addMultibinding<FlagFeature, SystemImageDirFlag>();
246 }
247
ConfigFlagPlaceholder()248 fruit::Component<ConfigFlag> ConfigFlagPlaceholder() {
249 return fruit::createComponent()
250 .addMultibinding<FlagFeature, ConfigFlag>()
251 .bind<ConfigFlag, ConfigFlagPlaceholderImpl>();
252 }
253
254 } // namespace cuttlefish
255