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