• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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