• 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/libs/config/cuttlefish_config.h"
30 
31 // To support other files that use this from gflags.
32 DEFINE_string(system_image_dir, "", "");
33 
34 using gflags::FlagSettingMode::SET_FLAGS_DEFAULT;
35 
36 namespace cuttlefish {
37 
38 namespace {
39 
40 class SystemImageDirFlagImpl : public SystemImageDirFlag {
41  public:
INJECT(SystemImageDirFlagImpl ())42   INJECT(SystemImageDirFlagImpl()) {
43     auto help = "Location of the system partition images.";
44     flag_ = GflagsCompatFlag("system_image_dir", path_).Help(help);
45   }
Path()46   const std::string& Path() override { return path_; }
47 
Name() const48   std::string Name() const override { return "SystemImageDirFlagImpl"; }
Dependencies() const49   std::unordered_set<FlagFeature*> Dependencies() const override { return {}; }
Process(std::vector<std::string> & args)50   bool Process(std::vector<std::string>& args) override {
51     path_ = DefaultGuestImagePath("");
52     if (!flag_.Parse(args)) {
53       return false;
54     }
55     // To support other files that use this from gflags.
56     FLAGS_system_image_dir = path_;
57     gflags::SetCommandLineOptionWithMode("system_image_dir", path_.c_str(),
58                                          SET_FLAGS_DEFAULT);
59     return true;
60   }
WriteGflagsCompatHelpXml(std::ostream &) const61   bool WriteGflagsCompatHelpXml(std::ostream&) const override {
62     // TODO(schuffelen): Write something here when this is removed from gflags
63     return true;
64   }
65 
66  private:
67   std::string path_;
68   Flag flag_;
69 };
70 
71 class ConfigReader : public FlagFeature {
72  public:
73   INJECT(ConfigReader()) = default;
74 
HasConfig(const std::string & name) const75   bool HasConfig(const std::string& name) const {
76     return allowed_config_presets_.count(name) > 0;
77   }
AvailableConfigs() const78   const std::set<std::string>& AvailableConfigs() const {
79     return allowed_config_presets_;
80   }
ReadConfig(const std::string & name) const81   std::optional<Json::Value> ReadConfig(const std::string& name) const {
82     auto path =
83         DefaultHostArtifactsPath("etc/cvd_config/cvd_config_" + name + ".json");
84     Json::Value config;
85     Json::CharReaderBuilder builder;
86     std::ifstream ifs(path);
87     std::string errorMessage;
88     if (!Json::parseFromStream(builder, ifs, &config, &errorMessage)) {
89       LOG(ERROR) << "Could not read config file " << path << ": "
90                  << errorMessage;
91       return {};
92     }
93     return config;
94   }
95 
96   // FlagFeature
Name() const97   std::string Name() const override { return "ConfigReader"; }
Dependencies() const98   std::unordered_set<FlagFeature*> Dependencies() const override { return {}; }
Process(std::vector<std::string> &)99   bool Process(std::vector<std::string>&) override {
100     for (const std::string& file :
101          DirectoryContents(DefaultHostArtifactsPath("etc/cvd_config"))) {
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 true;
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) { return ChooseConfig(m.value); };
129     flag_ = GflagsCompatFlag("config").Help(help).Getter(getter).Setter(setter);
130   }
131 
Name() const132   std::string Name() const override { return "ConfigFlagImpl"; }
Dependencies() const133   std::unordered_set<FlagFeature*> Dependencies() const override {
134     return {
135         static_cast<FlagFeature*>(&config_reader_),
136         static_cast<FlagFeature*>(&system_image_dir_flag_),
137     };
138   }
Process(std::vector<std::string> & args)139   bool Process(std::vector<std::string>& args) override {
140     if (!flag_.Parse(args)) {
141       LOG(ERROR) << "Failed to parse `--config` flag";
142       return false;
143     }
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 = config_reader_.ReadConfig(config_);
150     if (!config_values) {
151       LOG(ERROR) << "Failed to read config for " << config_;
152       return false;
153     }
154     for (const std::string& flag : config_values->getMemberNames()) {
155       std::string value;
156       if (flag == "custom_actions") {
157         Json::StreamWriterBuilder factory;
158         value = Json::writeString(factory, (*config_values)[flag]);
159       } else {
160         value = (*config_values)[flag].asString();
161       }
162       args.insert(args.begin(), "--" + flag + "=" + value);
163       // To avoid the flag forwarder from thinking this song is different from a
164       // default. Should fail silently if the flag doesn't exist.
165       gflags::SetCommandLineOptionWithMode(flag.c_str(), value.c_str(),
166                                            SET_FLAGS_DEFAULT);
167     }
168     return true;
169   }
WriteGflagsCompatHelpXml(std::ostream & out) const170   bool WriteGflagsCompatHelpXml(std::ostream& out) const override {
171     return flag_.WriteGflagsCompatXml(out);
172   }
173 
174  private:
ChooseConfig(const std::string & name)175   bool ChooseConfig(const std::string& name) {
176     if (!config_reader_.HasConfig(name)) {
177       LOG(ERROR) << "Invalid --config option '" << name << "'. Valid options: "
178                  << android::base::Join(config_reader_.AvailableConfigs(), ",");
179       return false;
180     }
181     config_ = name;
182     is_default_ = false;
183     return true;
184   }
FindAndroidInfoConfig() const185   std::optional<std::string> FindAndroidInfoConfig() const {
186     auto info_path = system_image_dir_flag_.Path() + "/android-info.txt";
187     if (!FileExists(info_path)) {
188       return {};
189     }
190     std::ifstream ifs{info_path};
191     if (!ifs.is_open()) {
192       return {};
193     }
194     std::string android_info;
195     ifs >> android_info;
196     std::string_view local_android_info(android_info);
197     if (!android::base::ConsumePrefix(&local_android_info, "config=")) {
198       return {};
199     }
200     if (!config_reader_.HasConfig(std::string{local_android_info})) {
201       LOG(WARNING) << info_path << " contains invalid config preset: '"
202                    << local_android_info << "'.";
203       return {};
204     }
205     return std::string{local_android_info};
206   }
207 
208   ConfigReader& config_reader_;
209   SystemImageDirFlag& system_image_dir_flag_;
210   std::string config_;
211   bool is_default_;
212   Flag flag_;
213 };
214 
215 class ConfigFlagPlaceholderImpl : public ConfigFlag {
216  public:
INJECT(ConfigFlagPlaceholderImpl ())217   INJECT(ConfigFlagPlaceholderImpl()) {}
218 
Name() const219   std::string Name() const override { return "ConfigFlagPlaceholderImpl"; }
Dependencies() const220   std::unordered_set<FlagFeature*> Dependencies() const override { return {}; }
Process(std::vector<std::string> &)221   bool Process(std::vector<std::string>&) override { return true; }
WriteGflagsCompatHelpXml(std::ostream &) const222   bool WriteGflagsCompatHelpXml(std::ostream&) const override { return true; }
223 };
224 
225 }  // namespace
226 
ConfigFlagComponent()227 fruit::Component<SystemImageDirFlag, ConfigFlag> ConfigFlagComponent() {
228   return fruit::createComponent()
229       .addMultibinding<FlagFeature, ConfigReader>()
230       .bind<ConfigFlag, ConfigFlagImpl>()
231       .addMultibinding<FlagFeature, ConfigFlag>()
232       .bind<SystemImageDirFlag, SystemImageDirFlagImpl>()
233       .addMultibinding<FlagFeature, SystemImageDirFlag>();
234 }
235 
ConfigFlagPlaceholder()236 fruit::Component<ConfigFlag> ConfigFlagPlaceholder() {
237   return fruit::createComponent()
238       .addMultibinding<FlagFeature, ConfigFlag>()
239       .bind<ConfigFlag, ConfigFlagPlaceholderImpl>();
240 }
241 
242 }  // namespace cuttlefish
243