• 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/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