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/acloud/converter.h"
18
19 #include <sys/stat.h>
20
21 #include <cstdio>
22 #include <fstream>
23 #include <optional>
24 #include <vector>
25
26 #include <android-base/file.h>
27 #include <android-base/parseint.h>
28 #include <android-base/strings.h>
29 #include <google/protobuf/text_format.h>
30
31 #include "common/libs/fs/shared_fd.h"
32 #include "common/libs/utils/files.h"
33 #include "common/libs/utils/flag_parser.h"
34 #include "common/libs/utils/result.h"
35 #include "common/libs/utils/subprocess.h"
36 #include "common/libs/utils/users.h"
37 #include "cvd_server.pb.h"
38 #include "host/commands/cvd/acloud/config.h"
39 #include "host/commands/cvd/command_sequence.h"
40 #include "host/commands/cvd/common_utils.h"
41 #include "host/commands/cvd/instance_lock.h" // TempDir()
42 #include "host/commands/cvd/selector/instance_database_utils.h"
43 #include "host/commands/cvd/selector/selector_constants.h"
44 #include "host/commands/cvd/server_client.h"
45 #include "host/commands/cvd/server_command/utils.h"
46 #include "host/commands/cvd/types.h"
47 #include "host/libs/config/cuttlefish_config.h"
48
49 namespace cuttlefish {
50 namespace {
51
52 // Image names to search
53 const std::vector<std::string> _KERNEL_IMAGE_NAMES = {"kernel", "bzImage",
54 "Image"};
55 const std::vector<std::string> _INITRAMFS_IMAGE_NAME = {"initramfs.img"};
56 const std::vector<std::string> _BOOT_IMAGE_NAME = {"boot.img"};
57 const std::vector<std::string> _VENDOR_BOOT_IMAGE_NAME = {"vendor_boot.img"};
58
59 /**
60 * Find a image file through the input path and pattern.
61 *
62 * If it finds the file, return the path string.
63 * If it can't find the file, return empty string.
64 */
FindImage(const std::string & search_path,const std::vector<std::string> & pattern)65 std::string FindImage(const std::string& search_path,
66 const std::vector<std::string>& pattern) {
67 const std::string& search_path_extend = search_path + "/";
68 for (const auto& name : pattern) {
69 const std::string image = search_path_extend + name;
70 if (FileExists(image)) {
71 return image;
72 }
73 }
74 return "";
75 }
76
77 /**
78 * Split a string into arguments based on shell tokenization rules.
79 *
80 * This behaves like `shlex.split` from python where arguments are separated
81 * based on whitespace, but quoting and quote escaping is respected. This
82 * function effectively removes one level of quoting from its inputs while
83 * making the split.
84 */
BashTokenize(const std::string & str)85 Result<std::vector<std::string>> BashTokenize(const std::string& str) {
86 Command command("bash");
87 command.AddParameter("-c");
88 command.AddParameter("printf '%s\n' ", str);
89 std::string stdout_str;
90 std::string stderr_str;
91 auto ret = RunWithManagedStdio(std::move(command), nullptr, &stdout_str,
92 &stderr_str);
93 CF_EXPECT(ret == 0,
94 "printf fail \"" << stdout_str << "\", \"" << stderr_str << "\"");
95 return android::base::Split(stdout_str, "\n");
96 }
97
98 } // namespace
99
100 // body of pure virtual destructor required by C++
~ConvertAcloudCreateCommand()101 ConvertAcloudCreateCommand::~ConvertAcloudCreateCommand() {}
102
103 class ConvertAcloudCreateCommandImpl : public ConvertAcloudCreateCommand {
104 public:
INJECT(ConvertAcloudCreateCommandImpl ())105 INJECT(ConvertAcloudCreateCommandImpl()) {}
106 ~ConvertAcloudCreateCommandImpl() override = default;
107
Convert(const RequestWithStdio & request)108 Result<ConvertedAcloudCreateCommand> Convert(
109 const RequestWithStdio& request) {
110 auto arguments = ParseInvocation(request.Message()).arguments;
111 CF_EXPECT(arguments.size() > 0);
112 CF_EXPECT(arguments[0] == "create");
113 arguments.erase(arguments.begin());
114
115 const auto& request_command = request.Message().command_request();
116
117 std::vector<Flag> flags;
118 bool local_instance_set;
119 std::optional<int> local_instance;
120 auto local_instance_flag = Flag();
121 local_instance_flag.Alias(
122 {FlagAliasMode::kFlagConsumesArbitrary, "--local-instance"});
123 local_instance_flag.Setter([&local_instance_set,
124 &local_instance](const FlagMatch& m) {
125 local_instance_set = true;
126 if (m.value != "" && local_instance) {
127 LOG(ERROR) << "Instance number already set, was \"" << *local_instance
128 << "\", now set to \"" << m.value << "\"";
129 return false;
130 } else if (m.value != "" && !local_instance) {
131 local_instance = std::stoi(m.value);
132 }
133 return true;
134 });
135 flags.emplace_back(local_instance_flag);
136
137 std::optional<std::string> flavor;
138 flags.emplace_back(
139 Flag()
140 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--config"})
141 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--flavor"})
142 .Setter([&flavor](const FlagMatch& m) {
143 flavor = m.value;
144 return true;
145 }));
146
147 std::optional<std::string> local_kernel_image;
148 flags.emplace_back(Flag()
149 .Alias({FlagAliasMode::kFlagConsumesFollowing,
150 "--local-kernel-image"})
151 .Alias({FlagAliasMode::kFlagConsumesFollowing,
152 "--local-boot-image"})
153 .Setter([&local_kernel_image](const FlagMatch& m) {
154 local_kernel_image = m.value;
155 return true;
156 }));
157
158 std::optional<std::string> image_download_dir;
159 flags.emplace_back(Flag()
160 .Alias({FlagAliasMode::kFlagConsumesFollowing,
161 "--image-download-dir"})
162 .Setter([&image_download_dir](const FlagMatch& m) {
163 image_download_dir = m.value;
164 return true;
165 }));
166
167 verbose_ = false;
168 flags.emplace_back(Flag()
169 .Alias({FlagAliasMode::kFlagExact, "-v"})
170 .Alias({FlagAliasMode::kFlagExact, "-vv"})
171 .Alias({FlagAliasMode::kFlagExact, "--verbose"})
172 .Setter([this](const FlagMatch&) {
173 verbose_ = true;
174 return true;
175 }));
176
177 std::optional<std::string> branch;
178 flags.emplace_back(
179 Flag()
180 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--branch"})
181 .Setter([&branch](const FlagMatch& m) {
182 branch = m.value;
183 return true;
184 }));
185
186 bool local_image;
187 std::optional<std::string> local_image_path;
188 flags.emplace_back(
189 Flag()
190 .Alias({FlagAliasMode::kFlagConsumesArbitrary, "--local-image"})
191 .Setter([&local_image,
192 &local_image_path](const FlagMatch& m) {
193 local_image = true;
194 if (m.value != "") {
195 local_image_path = m.value;
196 }
197 return true;
198 }));
199
200 std::optional<std::string> build_id;
201 flags.emplace_back(
202 Flag()
203 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--build-id"})
204 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--build_id"})
205 .Setter([&build_id](const FlagMatch& m) {
206 build_id = m.value;
207 return true;
208 }));
209
210 std::optional<std::string> build_target;
211 flags.emplace_back(
212 Flag()
213 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--build-target"})
214 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--build_target"})
215 .Setter([&build_target](const FlagMatch& m) {
216 build_target = m.value;
217 return true;
218 }));
219
220 std::optional<std::string> config_file;
221 flags.emplace_back(
222 Flag()
223 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--config-file"})
224 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--config_file"})
225 .Setter([&config_file](const FlagMatch& m) {
226 config_file = m.value;
227 return true;
228 }));
229
230 std::optional<std::string> bootloader_build_id;
231 flags.emplace_back(Flag()
232 .Alias({FlagAliasMode::kFlagConsumesFollowing,
233 "--bootloader-build-id"})
234 .Alias({FlagAliasMode::kFlagConsumesFollowing,
235 "--bootloader_build_id"})
236 .Setter([&bootloader_build_id](const FlagMatch& m) {
237 bootloader_build_id = m.value;
238 return true;
239 }));
240 std::optional<std::string> bootloader_build_target;
241 flags.emplace_back(
242 Flag()
243 .Alias({FlagAliasMode::kFlagConsumesFollowing,
244 "--bootloader-build-target"})
245 .Alias({FlagAliasMode::kFlagConsumesFollowing,
246 "--bootloader_build_target"})
247 .Setter([&bootloader_build_target](const FlagMatch& m) {
248 bootloader_build_target = m.value;
249 return true;
250 }));
251 std::optional<std::string> bootloader_branch;
252 flags.emplace_back(Flag()
253 .Alias({FlagAliasMode::kFlagConsumesFollowing,
254 "--bootloader-branch"})
255 .Alias({FlagAliasMode::kFlagConsumesFollowing,
256 "--bootloader_branch"})
257 .Setter([&bootloader_branch](const FlagMatch& m) {
258 bootloader_branch = m.value;
259 return true;
260 }));
261
262 std::optional<std::string> boot_build_id;
263 flags.emplace_back(
264 Flag()
265 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--boot-build-id"})
266 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--boot_build_id"})
267 .Setter([&boot_build_id](const FlagMatch& m) {
268 boot_build_id = m.value;
269 return true;
270 }));
271 std::optional<std::string> boot_build_target;
272 flags.emplace_back(Flag()
273 .Alias({FlagAliasMode::kFlagConsumesFollowing,
274 "--boot-build-target"})
275 .Alias({FlagAliasMode::kFlagConsumesFollowing,
276 "--boot_build_target"})
277 .Setter([&boot_build_target](const FlagMatch& m) {
278 boot_build_target = m.value;
279 return true;
280 }));
281 std::optional<std::string> boot_branch;
282 flags.emplace_back(
283 Flag()
284 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--boot-branch"})
285 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--boot_branch"})
286 .Setter([&boot_branch](const FlagMatch& m) {
287 boot_branch = m.value;
288 return true;
289 }));
290 std::optional<std::string> boot_artifact;
291 flags.emplace_back(
292 Flag()
293 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--boot-artifact"})
294 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--boot_artifact"})
295 .Setter([&boot_artifact](const FlagMatch& m) {
296 boot_artifact = m.value;
297 return true;
298 }));
299
300 std::optional<std::string> ota_build_id;
301 flags.emplace_back(
302 Flag()
303 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--ota-build-id"})
304 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--ota_build_id"})
305 .Setter([&ota_build_id](const FlagMatch& m) {
306 ota_build_id = m.value;
307 return true;
308 }));
309 std::optional<std::string> ota_build_target;
310 flags.emplace_back(Flag()
311 .Alias({FlagAliasMode::kFlagConsumesFollowing,
312 "--ota-build-target"})
313 .Alias({FlagAliasMode::kFlagConsumesFollowing,
314 "--ota_build_target"})
315 .Setter([&ota_build_target](const FlagMatch& m) {
316 ota_build_target = m.value;
317 return true;
318 }));
319 std::optional<std::string> ota_branch;
320 flags.emplace_back(
321 Flag()
322 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--ota-branch"})
323 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--ota_branch"})
324 .Setter([&ota_branch](const FlagMatch& m) {
325 ota_branch = m.value;
326 return true;
327 }));
328
329 std::optional<std::string> launch_args;
330 flags.emplace_back(
331 Flag()
332 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--launch-args"})
333 .Setter([&launch_args](const FlagMatch& m) {
334 launch_args = m.value;
335 return true;
336 }));
337
338 std::optional<std::string> system_branch;
339 flags.emplace_back(
340 Flag()
341 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--system-branch"})
342 .Setter([&system_branch](const FlagMatch& m) {
343 system_branch = m.value;
344 return true;
345 }));
346
347 std::optional<std::string> system_build_target;
348 flags.emplace_back(Flag()
349 .Alias({FlagAliasMode::kFlagConsumesFollowing,
350 "--system-build-target"})
351 .Setter([&system_build_target](const FlagMatch& m) {
352 system_build_target = m.value;
353 return true;
354 }));
355
356 std::optional<std::string> system_build_id;
357 flags.emplace_back(
358 Flag()
359 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--system-build-id"})
360 .Setter([&system_build_id](const FlagMatch& m) {
361 system_build_id = m.value;
362 return true;
363 }));
364
365 std::optional<std::string> kernel_branch;
366 flags.emplace_back(
367 Flag()
368 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--kernel-branch"})
369 .Setter([&kernel_branch](const FlagMatch& m) {
370 kernel_branch = m.value;
371 return true;
372 }));
373
374 std::optional<std::string> kernel_build_target;
375 flags.emplace_back(Flag()
376 .Alias({FlagAliasMode::kFlagConsumesFollowing,
377 "--kernel-build-target"})
378 .Setter([&kernel_build_target](const FlagMatch& m) {
379 kernel_build_target = m.value;
380 return true;
381 }));
382
383 std::optional<std::string> kernel_build_id;
384 flags.emplace_back(
385 Flag()
386 .Alias({FlagAliasMode::kFlagConsumesFollowing, "--kernel-build-id"})
387 .Setter([&kernel_build_id](const FlagMatch& m) {
388 kernel_build_id = m.value;
389 return true;
390 }));
391
392 std::optional<std::string> pet_name;
393 Flag pet_name_gflag = GflagsCompatFlag("pet-name");
394 flags.emplace_back(
395 GflagsCompatFlag("pet-name")
396 .Getter([&pet_name]() { return (pet_name ? *pet_name : ""); })
397 .Setter([&pet_name](const FlagMatch& match) {
398 pet_name = match.value;
399 return true;
400 }));
401
402 CF_EXPECT(ParseFlags(flags, arguments));
403 CF_EXPECT(arguments.size() == 0,
404 "Unrecognized arguments:'"
405 << android::base::Join(arguments, "', '") << "'");
406
407 CF_EXPECT(local_instance_set == true,
408 "Only '--local-instance' is supported");
409 auto host_dir = TempDir() + "/acloud_image_artifacts/";
410 if (image_download_dir) {
411 host_dir = image_download_dir.value() + "/acloud_image_artifacts/";
412 }
413
414 auto host_artifacts_path = request_command.env().find(kAndroidHostOut);
415 CF_EXPECT(host_artifacts_path != request_command.env().end(),
416 "Missing " << kAndroidHostOut);
417
418 std::vector<cvd::Request> request_protos;
419 const uid_t uid = request.Credentials()->uid;
420 // default user config path
421 std::string user_config_path = CF_EXPECT(GetDefaultConfigFile(uid));
422
423 if (config_file) {
424 user_config_path = config_file.value();
425 }
426 AcloudConfig acloud_config =
427 CF_EXPECT(LoadAcloudConfig(user_config_path, uid));
428
429 if (local_image) {
430 CF_EXPECT(!(system_branch || system_build_target || system_build_id),
431 "--local-image incompatible with --system-* flags");
432 CF_EXPECT(!(bootloader_branch || bootloader_build_target ||
433 bootloader_build_id),
434 "--local-image incompatible with --bootloader-* flags");
435 CF_EXPECT(
436 !(boot_branch || boot_build_target || boot_build_id || boot_artifact),
437 "--local-image incompatible with --boot-* flags");
438 CF_EXPECT(!(ota_branch || ota_build_target || ota_build_id),
439 "--local-image incompatible with --ota-* flags");
440 } else {
441 if (!DirectoryExists(host_dir)) {
442 // fetch/download directory doesn't exist, create directory
443 cvd::Request& mkdir_request = request_protos.emplace_back();
444 auto& mkdir_command = *mkdir_request.mutable_command_request();
445 mkdir_command.add_args("cvd");
446 mkdir_command.add_args("mkdir");
447 mkdir_command.add_args("-p");
448 mkdir_command.add_args(host_dir);
449 auto& mkdir_env = *mkdir_command.mutable_env();
450 mkdir_env[kAndroidHostOut] = host_artifacts_path->second;
451 }
452 if (branch || build_id || build_target) {
453 auto target = build_target ? *build_target : "";
454 auto build = build_id.value_or(branch.value_or("aosp-master"));
455 host_dir += (build + target);
456 } else {
457 host_dir += "aosp-master";
458 }
459 // TODO(weihsu): if we fetch default ID such as aosp-master,
460 // cvd fetch will fetch the latest release. There is a potential
461 // issue that two different fetch with same default ID may
462 // download different releases.
463 // Eventually, we should match python acloud behavior to translate
464 // default ID (aosp-master) to real ID to solve this issue.
465
466 cvd::Request& fetch_request = request_protos.emplace_back();
467 auto& fetch_command = *fetch_request.mutable_command_request();
468 fetch_command.add_args("cvd");
469 fetch_command.add_args("fetch");
470 fetch_command.add_args("--directory");
471 fetch_command.add_args(host_dir);
472 fetch_command_str_ = "";
473 if (branch || build_id || build_target) {
474 fetch_command.add_args("--default_build");
475 fetch_command_str_ += "--default_build=";
476 auto target = build_target ? "/" + *build_target : "";
477 auto build = build_id.value_or(branch.value_or("aosp-master"));
478 fetch_command.add_args(build + target);
479 fetch_command_str_ += (build + target);
480 }
481 if (system_branch || system_build_id || system_build_target) {
482 fetch_command.add_args("--system_build");
483 fetch_command_str_ += " --system_build=";
484 auto target = system_build_target.value_or(build_target.value_or(""));
485 if (target != "") {
486 target = "/" + target;
487 }
488 auto build =
489 system_build_id.value_or(system_branch.value_or("aosp-master"));
490 fetch_command.add_args(build + target);
491 fetch_command_str_ += (build + target);
492 }
493 if (bootloader_branch || bootloader_build_id || bootloader_build_target) {
494 fetch_command.add_args("--bootloader_build");
495 fetch_command_str_ += " --bootloader_build=";
496 auto target = bootloader_build_target.value_or("");
497 if (target != "") {
498 target = "/" + target;
499 }
500 auto build = bootloader_build_id.value_or(
501 bootloader_branch.value_or("aosp_u-boot-mainline"));
502 fetch_command.add_args(build + target);
503 fetch_command_str_ += (build + target);
504 }
505 if (boot_branch || boot_build_id || boot_build_target) {
506 fetch_command.add_args("--boot_build");
507 fetch_command_str_ += " --boot_build=";
508 auto target = boot_build_target.value_or("");
509 if (target != "") {
510 target = "/" + target;
511 }
512 auto build =
513 boot_build_id.value_or(boot_branch.value_or("aosp-master"));
514 fetch_command.add_args(build + target);
515 fetch_command_str_ += (build + target);
516 }
517 if (boot_artifact) {
518 CF_EXPECT(boot_branch || boot_build_target || boot_build_id,
519 "--boot-artifact must combine with other --boot-* flags");
520 fetch_command.add_args("--boot_artifact");
521 fetch_command_str_ += " --boot_artifact=";
522 auto target = boot_artifact.value_or("");
523 fetch_command.add_args(target);
524 fetch_command_str_ += (target);
525 }
526 if (ota_branch || ota_build_id || ota_build_target) {
527 fetch_command.add_args("--otatools_build");
528 fetch_command_str_ += " --otatools_build=";
529 auto target = ota_build_target.value_or("");
530 if (target != "") {
531 target = "/" + target;
532 }
533 auto build = ota_build_id.value_or(ota_branch.value_or(""));
534 fetch_command.add_args(build + target);
535 fetch_command_str_ += (build + target);
536 }
537 if (kernel_branch || kernel_build_id || kernel_build_target) {
538 fetch_command.add_args("--kernel_build");
539 fetch_command_str_ += " --kernel_build=";
540 auto target = kernel_build_target.value_or("kernel_virt_x86_64");
541 auto build = kernel_build_id.value_or(
542 branch.value_or("aosp_kernel-common-android-mainline"));
543 fetch_command.add_args(build + "/" + target);
544 fetch_command_str_ += (build + "/" + target);
545 }
546 auto& fetch_env = *fetch_command.mutable_env();
547 fetch_env[kAndroidHostOut] = host_artifacts_path->second;
548
549 fetch_cvd_args_file_ = host_dir + "/fetch-cvd-args.txt";
550 if (FileExists(fetch_cvd_args_file_)) {
551 // file exists
552 std::string read_str;
553 using android::base::ReadFileToString;
554 CF_EXPECT(ReadFileToString(fetch_cvd_args_file_.c_str(), &read_str,
555 /* follow_symlinks */ true));
556 if (read_str == fetch_command_str_) {
557 // same fetch cvd command, reuse original dir
558 fetch_command_str_ = "";
559 request_protos.pop_back();
560 }
561 }
562 }
563
564 cvd::Request start_request;
565 auto& start_command = *start_request.mutable_command_request();
566 start_command.add_args("cvd");
567 start_command.add_args("start");
568 start_command.add_args("--daemon");
569 start_command.add_args("--undefok");
570 start_command.add_args("report_anonymous_usage_stats");
571 start_command.add_args("--report_anonymous_usage_stats");
572 start_command.add_args("y");
573 if (flavor) {
574 start_command.add_args("-config");
575 start_command.add_args(flavor.value());
576 }
577
578 if (local_kernel_image) {
579 // kernel image has 1st priority than boot image
580 struct stat statbuf;
581 std::string local_boot_image = "";
582 std::string vendor_boot_image = "";
583 std::string kernel_image = "";
584 std::string initramfs_image = "";
585 if (stat(local_kernel_image.value().c_str(), &statbuf) == 0) {
586 if (statbuf.st_mode & S_IFDIR) {
587 // it's a directory, deal with kernel image case first
588 kernel_image =
589 FindImage(local_kernel_image.value(), _KERNEL_IMAGE_NAMES);
590 initramfs_image =
591 FindImage(local_kernel_image.value(), _INITRAMFS_IMAGE_NAME);
592 // This is the original python acloud behavior, it
593 // expects both kernel and initramfs files, however,
594 // there are some very old kernels that are built without
595 // an initramfs.img file,
596 // e.g. aosp_kernel-common-android-4.14-stable
597 if (kernel_image != "" && initramfs_image != "") {
598 start_command.add_args("-kernel_path");
599 start_command.add_args(kernel_image);
600 start_command.add_args("-initramfs_path");
601 start_command.add_args(initramfs_image);
602 } else {
603 // boot.img case
604 // adding boot.img and vendor_boot.img to the path
605 local_boot_image =
606 FindImage(local_kernel_image.value(), _BOOT_IMAGE_NAME);
607 vendor_boot_image =
608 FindImage(local_kernel_image.value(), _VENDOR_BOOT_IMAGE_NAME);
609 start_command.add_args("-boot_image");
610 start_command.add_args(local_boot_image);
611 // vendor boot image may not exist
612 if (vendor_boot_image != "") {
613 start_command.add_args("-vendor_boot_image");
614 start_command.add_args(vendor_boot_image);
615 }
616 }
617 } else if (statbuf.st_mode & S_IFREG) {
618 // it's a file which directly points to boot.img
619 local_boot_image = local_kernel_image.value();
620 start_command.add_args("-boot_image");
621 start_command.add_args(local_boot_image);
622 }
623 }
624 }
625
626 if (launch_args) {
627 for (const auto& arg : CF_EXPECT(BashTokenize(*launch_args))) {
628 start_command.add_args(arg);
629 }
630 }
631 if (acloud_config.launch_args != "") {
632 for (const auto& arg :
633 CF_EXPECT(BashTokenize(acloud_config.launch_args))) {
634 start_command.add_args(arg);
635 }
636 }
637 start_command.mutable_selector_opts()->add_args(
638 std::string("--") + selector::SelectorFlags::kDisableDefaultGroup +
639 "=true");
640 if (pet_name) {
641 const auto [group_name, instance_name] =
642 CF_EXPECT(selector::BreakDeviceName(*pet_name),
643 *pet_name << " must be a group name followed by - "
644 << "followed by an instance name.");
645 std::string group_name_arg = "--";
646 group_name_arg.append(selector::SelectorFlags::kGroupName)
647 .append("=")
648 .append(group_name);
649 std::string instance_name_arg = "--";
650 instance_name_arg.append(selector::SelectorFlags::kInstanceName)
651 .append("=")
652 .append(instance_name);
653 start_command.mutable_selector_opts()->add_args(group_name_arg);
654 start_command.mutable_selector_opts()->add_args(instance_name_arg);
655 }
656
657 auto& start_env = *start_command.mutable_env();
658 if (local_image) {
659 if (local_image_path) {
660 std::string local_image_path_str = local_image_path.value();
661 // Python acloud source: local_image_local_instance.py;l=81
662 // this acloud flag is equal to launch_cvd flag system_image_dir
663 start_command.add_args("-system_image_dir");
664 start_command.add_args(local_image_path_str);
665 }
666
667 start_env[kAndroidHostOut] = host_artifacts_path->second;
668
669 auto product_out = request_command.env().find(kAndroidProductOut);
670 CF_EXPECT(product_out != request_command.env().end(),
671 "Missing " << kAndroidProductOut);
672 start_env[kAndroidProductOut] = product_out->second;
673 } else {
674 start_env[kAndroidHostOut] = host_dir;
675 start_env[kAndroidProductOut] = host_dir;
676 }
677 if (Contains(start_env, kCuttlefishInstanceEnvVarName)) {
678 // Python acloud does not use this variable.
679 // this variable will confuse cvd start, though
680 start_env.erase(kCuttlefishInstanceEnvVarName);
681 }
682 if (local_instance) {
683 start_env[kCuttlefishInstanceEnvVarName] =
684 std::to_string(*local_instance);
685 }
686 // we don't know which HOME is assigned by cvd start.
687 // cvd server does not rely on the working directory for cvd start
688 *start_command.mutable_working_directory() =
689 request_command.working_directory();
690 std::vector<SharedFD> fds;
691 if (verbose_) {
692 fds = request.FileDescriptors();
693 } else {
694 auto dev_null = SharedFD::Open("/dev/null", O_RDWR);
695 CF_EXPECT(dev_null->IsOpen(), dev_null->StrError());
696 fds = {dev_null, dev_null, dev_null};
697 }
698
699 ConvertedAcloudCreateCommand ret{
700 .start_request = RequestWithStdio(request.Client(), start_request, fds,
701 request.Credentials())};
702 for (auto& request_proto : request_protos) {
703 ret.prep_requests.emplace_back(request.Client(), request_proto, fds,
704 request.Credentials());
705 }
706 return ret;
707 }
708
FetchCvdArgsFile() const709 const std::string& FetchCvdArgsFile() const override {
710 return fetch_cvd_args_file_;
711 }
712
FetchCommandString() const713 const std::string& FetchCommandString() const override {
714 return fetch_command_str_;
715 }
Verbose() const716 bool Verbose() const { return verbose_; }
717
718 private:
719 std::string fetch_cvd_args_file_;
720 std::string fetch_command_str_;
721 bool verbose_;
722 };
723
724 fruit::Component<ConvertAcloudCreateCommand>
AcloudCreateConvertCommandComponent()725 AcloudCreateConvertCommandComponent() {
726 return fruit::createComponent()
727 .bind<ConvertAcloudCreateCommand, ConvertAcloudCreateCommandImpl>();
728 }
729
730 } // namespace cuttlefish
731