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