• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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/commands/cvd/selector/start_selector_parser.h"
18 
19 #include <unistd.h>
20 
21 #include <iostream>
22 #include <sstream>
23 #include <string_view>
24 
25 #include <android-base/parseint.h>
26 #include <android-base/strings.h>
27 
28 #include "common/libs/utils/contains.h"
29 #include "common/libs/utils/flag_parser.h"
30 #include "common/libs/utils/users.h"
31 #include "host/commands/cvd/selector/instance_database_utils.h"
32 #include "host/commands/cvd/selector/selector_constants.h"
33 #include "host/commands/cvd/selector/selector_option_parser_utils.h"
34 #include "host/commands/cvd/types.h"
35 #include "host/libs/config/cuttlefish_config.h"
36 #include "host/libs/config/instance_nums.h"
37 
38 namespace std {
39 
40 /* For a needed CF_EXPECT_EQ(vector, vector, msg) below
41  *
42  * the result.h included above requires this operator. The declaration must come
43  * before the header file if the operator<< is in cuttlefish namespace.
44  * Otherwise, operator<< should be in std.
45  *
46  * The namespace resolution rule is to search cuttlefish, and then std if
47  * failed.
48  */
operator <<(std::ostream & out,const std::vector<std::string> & v)49 static inline std::ostream& operator<<(std::ostream& out,
50                                        const std::vector<std::string>& v) {
51   if (v.empty()) {
52     out << "{}";
53     return out;
54   }
55   out << "{";
56   if (v.size() > 1) {
57     for (auto itr = v.cbegin(); itr != v.cend() - 1; itr++) {
58       out << *itr << ", ";
59     }
60   }
61   out << v.back() << "}";
62   return out;
63 }
64 
65 }  // namespace std
66 
67 namespace cuttlefish {
68 namespace selector {
69 
Unique(const std::vector<unsigned> & v)70 static bool Unique(const std::vector<unsigned>& v) {
71   std::unordered_set<unsigned> hash_set(v.begin(), v.end());
72   return v.size() == hash_set.size();
73 }
74 
ParseNaturalNumber(const std::string & token)75 static Result<unsigned> ParseNaturalNumber(const std::string& token) {
76   std::int32_t value;
77   CF_EXPECT(android::base::ParseInt(token, &value));
78   CF_EXPECT(value > 0);
79   return static_cast<unsigned>(value);
80 }
81 
ConductSelectFlagsParser(const uid_t uid,const cvd_common::Args & selector_args,const cvd_common::Args & cmd_args,const cvd_common::Envs & envs)82 Result<StartSelectorParser> StartSelectorParser::ConductSelectFlagsParser(
83     const uid_t uid, const cvd_common::Args& selector_args,
84     const cvd_common::Args& cmd_args, const cvd_common::Envs& envs) {
85   const std::string system_wide_home = CF_EXPECT(SystemWideUserHome(uid));
86   cvd_common::Args selector_args_copied{selector_args};
87   StartSelectorParser parser(
88       system_wide_home, selector_args_copied, cmd_args, envs,
89       CF_EXPECT(SelectorCommonParser::Parse(uid, selector_args_copied, envs)));
90   CF_EXPECT(parser.ParseOptions(), "selector option flag parsing failed.");
91   return {std::move(parser)};
92 }
93 
StartSelectorParser(const std::string & system_wide_user_home,const cvd_common::Args & selector_args,const cvd_common::Args & cmd_args,const cvd_common::Envs & envs,SelectorCommonParser && common_parser)94 StartSelectorParser::StartSelectorParser(
95     const std::string& system_wide_user_home,
96     const cvd_common::Args& selector_args, const cvd_common::Args& cmd_args,
97     const cvd_common::Envs& envs, SelectorCommonParser&& common_parser)
98     : client_user_home_{system_wide_user_home},
99       selector_args_(selector_args),
100       cmd_args_(cmd_args),
101       envs_(envs),
102       common_parser_(std::move(common_parser)) {}
103 
GroupName() const104 std::optional<std::string> StartSelectorParser::GroupName() const {
105   return group_name_;
106 }
107 
PerInstanceNames() const108 std::optional<std::vector<std::string>> StartSelectorParser::PerInstanceNames()
109     const {
110   return per_instance_names_;
111 }
112 
113 namespace {
114 
TryFromCuttlefishInstance(const cvd_common::Envs & envs)115 std::optional<unsigned> TryFromCuttlefishInstance(
116     const cvd_common::Envs& envs) {
117   if (!Contains(envs, kCuttlefishInstanceEnvVarName)) {
118     return std::nullopt;
119   }
120   const auto cuttlefish_instance = envs.at(kCuttlefishInstanceEnvVarName);
121   if (cuttlefish_instance.empty()) {
122     return std::nullopt;
123   }
124   auto parsed = ParseNaturalNumber(cuttlefish_instance);
125   return parsed.ok() ? std::optional(*parsed) : std::nullopt;
126 }
127 
TryFromUser(const cvd_common::Envs & envs)128 std::optional<unsigned> TryFromUser(const cvd_common::Envs& envs) {
129   if (!Contains(envs, "USER")) {
130     return std::nullopt;
131   }
132   std::string_view user{envs.at("USER")};
133   if (user.empty() || !android::base::ConsumePrefix(&user, kVsocUserPrefix)) {
134     return std::nullopt;
135   }
136   const auto& vsoc_num = user;
137   auto vsoc_id = ParseNaturalNumber(vsoc_num.data());
138   return vsoc_id.ok() ? std::optional(*vsoc_id) : std::nullopt;
139 }
140 
141 }  // namespace
142 
143 std::optional<std::vector<unsigned>>
InstanceFromEnvironment(const InstanceFromEnvParam & params)144 StartSelectorParser::InstanceFromEnvironment(
145     const InstanceFromEnvParam& params) {
146   const auto& cuttlefish_instance_env = params.cuttlefish_instance_env;
147   const auto& vsoc_suffix = params.vsoc_suffix;
148   const auto& num_instances = params.num_instances;
149 
150   // see the logic in cuttlefish::InstanceFromEnvironment()
151   // defined in host/libs/config/cuttlefish_config.cpp
152   std::vector<unsigned> nums;
153   std::optional<unsigned> base;
154   if (cuttlefish_instance_env) {
155     base = *cuttlefish_instance_env;
156   }
157   if (!base && vsoc_suffix) {
158     base = *vsoc_suffix;
159   }
160   if (!base) {
161     return std::nullopt;
162   }
163   // this is guaranteed by the caller
164   // assert(num_instances != std::nullopt);
165   for (unsigned i = 0; i != *num_instances; i++) {
166     nums.emplace_back(base.value() + i);
167   }
168   return nums;
169 }
170 
VerifyNumOfInstances(const VerifyNumOfInstancesParam & params,const unsigned default_n_instances) const171 Result<unsigned> StartSelectorParser::VerifyNumOfInstances(
172     const VerifyNumOfInstancesParam& params,
173     const unsigned default_n_instances) const {
174   const auto& num_instances_flag = params.num_instances_flag;
175   const auto& instance_names = params.instance_names;
176   const auto& instance_nums_flag = params.instance_nums_flag;
177 
178   std::optional<unsigned> num_instances;
179   if (num_instances_flag) {
180     num_instances = CF_EXPECT(ParseNaturalNumber(*num_instances_flag));
181   }
182   if (instance_names && !instance_names->empty()) {
183     auto implied_n_instances = instance_names->size();
184     if (num_instances) {
185       CF_EXPECT_EQ(*num_instances, static_cast<unsigned>(implied_n_instances),
186                    "The number of instances requested by --num_instances "
187                        << " are not the same as what is implied by "
188                        << " --instance_name.");
189     }
190     num_instances = implied_n_instances;
191   }
192   if (instance_nums_flag) {
193     std::vector<std::string> tokens =
194         android::base::Split(*instance_nums_flag, ",");
195     for (const auto& t : tokens) {
196       CF_EXPECT(ParseNaturalNumber(t), t << " must be a natural number");
197     }
198     if (!num_instances) {
199       num_instances = tokens.size();
200     }
201     CF_EXPECT_EQ(*num_instances, tokens.size(),
202                  "All information for the number of instances must match.");
203   }
204   return num_instances.value_or(default_n_instances);
205 }
206 
ParseInstanceNums(const std::string & instance_nums_flag)207 static Result<std::vector<unsigned>> ParseInstanceNums(
208     const std::string& instance_nums_flag) {
209   std::vector<unsigned> nums;
210   std::vector<std::string> tokens =
211       android::base::Split(instance_nums_flag, ",");
212   for (const auto& t : tokens) {
213     unsigned num =
214         CF_EXPECT(ParseNaturalNumber(t), t << " must be a natural number");
215     nums.emplace_back(num);
216   }
217   CF_EXPECT(Unique(nums), "--instance_nums include duplicated numbers");
218   return nums;
219 }
220 
221 Result<StartSelectorParser::ParsedInstanceIdsOpt>
HandleInstanceIds(const InstanceIdsParams & instance_id_params)222 StartSelectorParser::HandleInstanceIds(
223     const InstanceIdsParams& instance_id_params) {
224   const auto& instance_nums = instance_id_params.instance_nums;
225   const auto& base_instance_num = instance_id_params.base_instance_num;
226   const auto& cuttlefish_instance_env =
227       instance_id_params.cuttlefish_instance_env;
228   const auto& vsoc_suffix = instance_id_params.vsoc_suffix;
229 
230   // calculate and/or verify the number of instances
231   unsigned num_instances =
232       CF_EXPECT(VerifyNumOfInstances(VerifyNumOfInstancesParam{
233           .num_instances_flag = instance_id_params.num_instances,
234           .instance_names = PerInstanceNames(),
235           .instance_nums_flag = instance_nums}));
236 
237   if (!instance_nums && !base_instance_num) {
238     // num_instances is given. if non-std::nullopt is returned,
239     // the base is also figured out. If base can't be figured out,
240     // std::nullopt is returned.
241     auto instance_ids = InstanceFromEnvironment(
242         {.cuttlefish_instance_env = cuttlefish_instance_env,
243          .vsoc_suffix = vsoc_suffix,
244          .num_instances = num_instances});
245     if (instance_ids) {
246       return ParsedInstanceIdsOpt(*instance_ids);
247     }
248     // the return value, n_instances is the "desired/requested" instances
249     // When instance_ids set isn't figured out, n_instances is not meant to
250     // be always zero; it could be any natural number.
251     return ParsedInstanceIdsOpt(num_instances);
252   }
253 
254   InstanceNumsCalculator calculator;
255   calculator.NumInstances(static_cast<std::int32_t>(num_instances));
256   if (instance_nums) {
257     CF_EXPECT(base_instance_num == std::nullopt,
258               "-base_instance_num and -instance_nums are mutually exclusive.");
259     std::vector<unsigned> parsed_nums =
260         CF_EXPECT(ParseInstanceNums(*instance_nums));
261     return ParsedInstanceIdsOpt(parsed_nums);
262   }
263   if (base_instance_num) {
264     unsigned base = CF_EXPECT(ParseNaturalNumber(*base_instance_num));
265     calculator.BaseInstanceNum(static_cast<std::int32_t>(base));
266   }
267   auto instance_ids = std::move(CF_EXPECT(calculator.CalculateFromFlags()));
268   CF_EXPECT(!instance_ids.empty(),
269             "CalculateFromFlags() must be called when --num_instances or "
270                 << "--base_instance_num is given, and must not return an "
271                 << "empty set");
272   auto instance_ids_vector =
273       std::vector<unsigned>{instance_ids.begin(), instance_ids.end()};
274   return ParsedInstanceIdsOpt{instance_ids_vector};
275 }
276 
CalcMayBeDefaultGroup()277 Result<bool> StartSelectorParser::CalcMayBeDefaultGroup() {
278   auto disable_default_group_flag = CF_EXPECT(
279       SelectorFlags::Get().GetFlag(SelectorFlags::kDisableDefaultGroup));
280   if (CF_EXPECT(
281           disable_default_group_flag.CalculateFlag<bool>(selector_args_))) {
282     return false;
283   }
284   /*
285    * --disable_default_group instructs that the default group
286    * should be disabled anyway. If not given, the logic to determine
287    * whether this group is the default one or not is:
288    *  If HOME is not overridden and no selector options, then
289    *   the default group
290    *  Or, not a default group
291    *
292    */
293   if (CF_EXPECT(common_parser_.HomeOverridden())) {
294     return false;
295   }
296   return !common_parser_.HasDeviceSelectOption();
297 }
298 
IsTrue(const std::string & value)299 static bool IsTrue(const std::string& value) {
300   std::unordered_set<std::string> true_strings = {"y", "yes", "true"};
301   std::string value_in_lower_case = value;
302   /*
303    * https://en.cppreference.com/w/cpp/string/byte/tolower
304    *
305    * char should be converted to unsigned char first.
306    */
307   std::transform(value_in_lower_case.begin(), value_in_lower_case.end(),
308                  value_in_lower_case.begin(),
309                  [](unsigned char c) { return std::tolower(c); });
310   return Contains(true_strings, value_in_lower_case);
311 }
312 
IsFalse(const std::string & value)313 static bool IsFalse(const std::string& value) {
314   std::unordered_set<std::string> false_strings = {"n", "no", "false"};
315   std::string value_in_lower_case = value;
316   /*
317    * https://en.cppreference.com/w/cpp/string/byte/tolower
318    *
319    * char should be converted to unsigned char first.
320    */
321   std::transform(value_in_lower_case.begin(), value_in_lower_case.end(),
322                  value_in_lower_case.begin(),
323                  [](unsigned char c) { return std::tolower(c); });
324   return Contains(false_strings, value_in_lower_case);
325 }
326 
GetAcquireFileLockEnvValue(const cvd_common::Envs & envs)327 static std::optional<std::string> GetAcquireFileLockEnvValue(
328     const cvd_common::Envs& envs) {
329   if (!Contains(envs, SelectorFlags::kAcquireFileLockEnv)) {
330     return std::nullopt;
331   }
332   auto env_value = envs.at(SelectorFlags::kAcquireFileLockEnv);
333   if (env_value.empty()) {
334     return std::nullopt;
335   }
336   return env_value;
337 }
338 
CalcAcquireFileLock()339 Result<bool> StartSelectorParser::CalcAcquireFileLock() {
340   // if the flag is set, flag has the highest priority
341   auto must_acquire_file_lock_flag =
342       CF_EXPECT(SelectorFlags::Get().GetFlag(SelectorFlags::kAcquireFileLock));
343   std::optional<bool> value_opt =
344       CF_EXPECT(must_acquire_file_lock_flag.FilterFlag<bool>(selector_args_));
345   if (value_opt) {
346     return *value_opt;
347   }
348   // flag is not set. see if there is the environment variable set
349   auto env_value_opt = GetAcquireFileLockEnvValue(envs_);
350   if (env_value_opt) {
351     auto value_string = *env_value_opt;
352     if (IsTrue(value_string)) {
353       return true;
354     }
355     if (IsFalse(value_string)) {
356       return false;
357     }
358     return CF_ERR("In \"" << SelectorFlags::kAcquireFileLockEnv << "="
359                           << value_string << ",\" \"" << value_string
360                           << "\" is an invalid value. Try true or false.");
361   }
362   // nothing set, falls back to the default value of the flag
363   auto default_value =
364       CF_EXPECT(must_acquire_file_lock_flag.DefaultValue<bool>());
365   return default_value;
366 }
367 
368 Result<StartSelectorParser::WebrtcCalculatedNames>
CalcNamesUsingWebrtcDeviceId()369 StartSelectorParser::CalcNamesUsingWebrtcDeviceId() {
370   std::optional<std::string> webrtc_device_ids_opt;
371   FilterSelectorFlag(cmd_args_, "webrtc_device_id", webrtc_device_ids_opt);
372   if (!webrtc_device_ids_opt) {
373     return WebrtcCalculatedNames{
374         .group_name = common_parser_.GroupName(),
375         .per_instance_names = common_parser_.PerInstanceNames()};
376   }
377   const std::string webrtc_device_ids =
378       std::move(webrtc_device_ids_opt.value());
379   std::vector<std::string> webrtc_device_names =
380       android::base::Tokenize(webrtc_device_ids, ",");
381 
382   std::unordered_set<std::string> group_names;
383   std::vector<std::string> instance_names;
384   instance_names.reserve(webrtc_device_names.size());
385 
386   // check if the supposedly group names exist and common across each
387   // webrtc_device_id
388   for (const auto& webrtc_device_name : webrtc_device_names) {
389     std::vector<std::string> tokens =
390         android::base::Tokenize(webrtc_device_name, "-");
391     CF_EXPECT_GE(tokens.size(), 2,
392                  webrtc_device_name
393                      << " cannot be split into group name and instance name");
394     group_names.insert(tokens.front());
395     CF_EXPECT_EQ(group_names.size(), 1,
396                  "group names in --webrtc_device_id must be the same but are "
397                  "different.");
398     tokens.erase(tokens.begin());
399     instance_names.push_back(android::base::Join(tokens, "-"));
400   }
401 
402   std::string group_name = *(group_names.begin());
403   CF_EXPECT(IsValidGroupName(group_name),
404             group_name << " is not a valid group name");
405 
406   for (const auto& instance_name : instance_names) {
407     CF_EXPECT(IsValidInstanceName(instance_name),
408               instance_name << " is not a valid instance name.");
409   }
410 
411   if (auto flag_group_name_opt = common_parser_.GroupName()) {
412     CF_EXPECT_EQ(flag_group_name_opt.value(), group_name);
413   }
414   if (auto flag_per_instance_names_opt = common_parser_.PerInstanceNames()) {
415     CF_EXPECT_EQ(flag_per_instance_names_opt.value(), instance_names);
416   }
417   return WebrtcCalculatedNames{.group_name = group_name,
418                                .per_instance_names = instance_names};
419 }
420 
ParseOptions()421 Result<void> StartSelectorParser::ParseOptions() {
422   may_be_default_group_ = CF_EXPECT(CalcMayBeDefaultGroup());
423   must_acquire_file_lock_ = CF_EXPECT(CalcAcquireFileLock());
424 
425   // compare webrtc_device_id against instance names
426   auto verified_names =
427       CF_EXPECT(CalcNamesUsingWebrtcDeviceId(),
428                 "--webrtc_device_id must match the list of device names");
429   group_name_ = verified_names.group_name;
430   per_instance_names_ = verified_names.per_instance_names;
431 
432   std::optional<std::string> num_instances;
433   std::optional<std::string> instance_nums;
434   std::optional<std::string> base_instance_num;
435   // set num_instances as std::nullptr or the value of --num_instances
436   FilterSelectorFlag(cmd_args_, "num_instances", num_instances);
437   FilterSelectorFlag(cmd_args_, "instance_nums", instance_nums);
438   FilterSelectorFlag(cmd_args_, "base_instance_num", base_instance_num);
439 
440   InstanceIdsParams instance_nums_param{
441       .num_instances = std::move(num_instances),
442       .instance_nums = std::move(instance_nums),
443       .base_instance_num = std::move(base_instance_num),
444       .cuttlefish_instance_env = TryFromCuttlefishInstance(envs_),
445       .vsoc_suffix = TryFromUser(envs_)};
446   auto parsed_ids = CF_EXPECT(HandleInstanceIds(instance_nums_param));
447   requested_num_instances_ = parsed_ids.GetNumOfInstances();
448   instance_ids_ = std::move(parsed_ids.GetInstanceIds());
449 
450   return {};
451 }
452 
453 }  // namespace selector
454 }  // namespace cuttlefish
455