blob: b3638d5cf4d4a1e5742d8d6863b04353fcf6cae7 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "host/commands/cvd/acloud/converter.h"
#include <sys/stat.h>
#include <cstdio>
#include <fstream>
#include <optional>
#include <regex>
#include <vector>
#include <android-base/file.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <google/protobuf/text_format.h>
#include "common/libs/fs/shared_fd.h"
#include "common/libs/utils/files.h"
#include "common/libs/utils/flag_parser.h"
#include "common/libs/utils/result.h"
#include "common/libs/utils/subprocess.h"
#include "cvd_server.pb.h"
#include "host/commands/cvd/acloud/config.h"
#include "host/commands/cvd/acloud/create_converter_parser.h"
#include "host/commands/cvd/common_utils.h"
#include "host/commands/cvd/lock_file.h"
#include "host/commands/cvd/selector/instance_database_utils.h"
#include "host/commands/cvd/selector/selector_constants.h"
#include "host/commands/cvd/server_client.h"
#include "host/commands/cvd/server_command/utils.h"
#include "host/commands/cvd/types.h"
#include "host/libs/config/cuttlefish_config.h"
namespace cuttlefish {
namespace {
// Image names to search
const std::vector<std::string> kKernelImageNames = {"kernel", "bzImage",
"Image"};
const std::vector<std::string> kInitRamFsImageName = {"initramfs.img"};
const std::vector<std::string> kBootImageName = {"boot.img"};
const std::vector<std::string> kVendorBootImageName = {"vendor_boot.img"};
const std::string kMixedSuperImageName = "mixed_super.img";
struct BranchBuildTargetInfo {
std::string branch_str;
std::string build_target_str;
};
static Result<BranchBuildTargetInfo> GetDefaultBranchBuildTarget(
const std::string default_branch_str, SubprocessWaiter& waiter,
std::function<Result<void>(void)> callback_unlock,
std::function<Result<void>(void)> callback_lock) {
// get the default build branch and target from repo info and git remote
BranchBuildTargetInfo result_info;
result_info.branch_str = default_branch_str;
Command repo_cmd("repo");
repo_cmd.AddParameter("info");
repo_cmd.AddParameter("platform/tools/acloud");
auto cuttlefish_source = StringFromEnv("ANDROID_BUILD_TOP", "") + "/tools/acloud";
auto fd_top = SharedFD::Open(cuttlefish_source, O_RDONLY | O_PATH | O_DIRECTORY);
if (!fd_top->IsOpen()) {
LOG(ERROR) << "Couldn't open \"" << cuttlefish_source
<< "\": " << fd_top->StrError();
} else {
repo_cmd.SetWorkingDirectory(fd_top);
}
RunWithManagedIoParam param_repo {
.cmd_ = std::move(repo_cmd),
.redirect_stdout_ = true,
.redirect_stderr_ = false,
.stdin_ = nullptr,
.callback_ = callback_unlock
};
RunOutput output_repo = CF_EXPECT(waiter.RunWithManagedStdioInterruptable(param_repo));
Command git_cmd("git");
git_cmd.AddParameter("remote");
if (fd_top->IsOpen()) {
git_cmd.SetWorkingDirectory(fd_top);
}
RunWithManagedIoParam param_git {
.cmd_ = std::move(git_cmd),
.redirect_stdout_ = true,
.redirect_stderr_ = false,
.stdin_ = nullptr,
.callback_ = callback_unlock
};
callback_lock();
RunOutput output_git = CF_EXPECT(waiter.RunWithManagedStdioInterruptable(param_git));
output_git.stdout_.erase(std::remove(
output_git.stdout_.begin(), output_git.stdout_.end(), '\n'), output_git.stdout_.cend());
static const std::regex repo_rgx("^Manifest branch: (.+)");
std::smatch repo_matched;
CHECK(std::regex_search(output_repo.stdout_, repo_matched, repo_rgx))
<< "Manifest branch line is not found from: " << output_repo.stdout_;
// master or ...
std::string repo_matched_str = repo_matched[1].str();
if (output_git.stdout_ == "aosp") {
result_info.branch_str = "aosp-";
result_info.build_target_str = "aosp_";
}
result_info.branch_str += repo_matched_str;
// AVD_TYPES_MAPPING default is cf
// _DEFAULT_BUILD_BITNESS default is x86_64
// flavor default is phone
// _DEFAULT_BUILD_TYPE default is userdebug
result_info.build_target_str += "cf_x86_64_phone-userdebug";
return result_info;
}
/**
* Split a string into arguments based on shell tokenization rules.
*
* This behaves like `shlex.split` from python where arguments are separated
* based on whitespace, but quoting and quote escaping is respected. This
* function effectively removes one level of quoting from its inputs while
* making the split.
*/
Result<std::vector<std::string>> BashTokenize(
const std::string& str, SubprocessWaiter& waiter,
std::function<Result<void>(void)> callback_unlock) {
Command command("bash");
command.AddParameter("-c");
command.AddParameter("printf '%s\n' ", str);
RunWithManagedIoParam param_bash {
.cmd_ = std::move(command),
.redirect_stdout_ = true,
.redirect_stderr_ = true,
.stdin_ = nullptr,
.callback_ = callback_unlock
};
RunOutput output_bash = CF_EXPECT(waiter.RunWithManagedStdioInterruptable(param_bash));
return android::base::Split(output_bash.stdout_, "\n");
}
} // namespace
namespace acloud_impl {
Result<ConvertedAcloudCreateCommand> ConvertAcloudCreate(
const RequestWithStdio& request, SubprocessWaiter& waiter,
std::function<Result<void>(void)> callback_unlock,
std::function<Result<void>(void)> callback_lock) {
auto arguments = ParseInvocation(request.Message()).arguments;
CF_EXPECT(arguments.size() > 0);
CF_EXPECT(arguments[0] == "create");
arguments.erase(arguments.begin());
/*
* TODO(chadreynolds@): Move all the flag parsing eventually to the
* converter_parser.{h,cpp}.
*
* Note that the transfer should be done from the top through the bottom.
* ParseFlags() parses each flag in order.
*/
auto parsed_flags = CF_EXPECT(acloud_impl::ParseAcloudCreateFlags(arguments));
std::vector<Flag> flags;
std::optional<std::string> boot_build_id;
flags.emplace_back(
Flag()
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--boot-build-id"})
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--boot_build_id"})
.Setter([&boot_build_id](const FlagMatch& m) -> Result<void> {
boot_build_id = m.value;
return {};
}));
std::optional<std::string> boot_build_target;
flags.emplace_back(
Flag()
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--boot-build-target"})
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--boot_build_target"})
.Setter([&boot_build_target](const FlagMatch& m) -> Result<void> {
boot_build_target = m.value;
return {};
}));
std::optional<std::string> boot_branch;
flags.emplace_back(
Flag()
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--boot-branch"})
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--boot_branch"})
.Setter([&boot_branch](const FlagMatch& m) -> Result<void> {
boot_branch = m.value;
return {};
}));
std::optional<std::string> boot_artifact;
flags.emplace_back(
Flag()
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--boot-artifact"})
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--boot_artifact"})
.Setter([&boot_artifact](const FlagMatch& m) -> Result<void> {
boot_artifact = m.value;
return {};
}));
std::optional<std::string> ota_build_id;
flags.emplace_back(
Flag()
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--ota-build-id"})
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--ota_build_id"})
.Setter([&ota_build_id](const FlagMatch& m) -> Result<void> {
ota_build_id = m.value;
return {};
}));
std::optional<std::string> ota_build_target;
flags.emplace_back(
Flag()
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--ota-build-target"})
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--ota_build_target"})
.Setter([&ota_build_target](const FlagMatch& m) -> Result<void> {
ota_build_target = m.value;
return {};
}));
std::optional<std::string> ota_branch;
flags.emplace_back(
Flag()
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--ota-branch"})
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--ota_branch"})
.Setter([&ota_branch](const FlagMatch& m) -> Result<void> {
ota_branch = m.value;
return {};
}));
std::optional<std::string> launch_args;
flags.emplace_back(
Flag()
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--launch-args"})
.Setter([&launch_args](const FlagMatch& m) -> Result<void> {
launch_args = m.value;
return {};
}));
std::optional<std::string> system_branch;
flags.emplace_back(
Flag()
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--system-branch"})
.Setter([&system_branch](const FlagMatch& m) -> Result<void> {
system_branch = m.value;
return {};
}));
std::optional<std::string> system_build_target;
flags.emplace_back(
Flag()
.Alias(
{FlagAliasMode::kFlagConsumesFollowing, "--system-build-target"})
.Setter([&system_build_target](const FlagMatch& m) -> Result<void> {
system_build_target = m.value;
return {};
}));
std::optional<std::string> system_build_id;
flags.emplace_back(
Flag()
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--system-build-id"})
.Setter([&system_build_id](const FlagMatch& m) -> Result<void> {
system_build_id = m.value;
return {};
}));
std::optional<std::string> kernel_branch;
flags.emplace_back(
Flag()
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--kernel-branch"})
.Setter([&kernel_branch](const FlagMatch& m) -> Result<void> {
kernel_branch = m.value;
return {};
}));
std::optional<std::string> kernel_build_target;
flags.emplace_back(
Flag()
.Alias(
{FlagAliasMode::kFlagConsumesFollowing, "--kernel-build-target"})
.Setter([&kernel_build_target](const FlagMatch& m) -> Result<void> {
kernel_build_target = m.value;
return {};
}));
std::optional<std::string> kernel_build_id;
flags.emplace_back(
Flag()
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--kernel-build-id"})
.Setter([&kernel_build_id](const FlagMatch& m) -> Result<void> {
kernel_build_id = m.value;
return {};
}));
bool use_16k = false;
flags.emplace_back(Flag()
.Alias({FlagAliasMode::kFlagExact, "--16k"})
.Alias({FlagAliasMode::kFlagExact, "--16K"})
.Alias({FlagAliasMode::kFlagExact, "--use-16k"})
.Alias({FlagAliasMode::kFlagExact, "--use-16K"})
.Setter([&use_16k](const FlagMatch&) -> Result<void> {
use_16k = true;
return {};
}));
std::optional<std::string> pet_name;
Flag pet_name_gflag = GflagsCompatFlag("pet-name");
flags.emplace_back(
GflagsCompatFlag("pet-name")
.Getter([&pet_name]() { return (pet_name ? *pet_name : ""); })
.Setter([&pet_name](const FlagMatch& match) -> Result<void> {
pet_name = match.value;
return {};
}));
CF_EXPECT(ParseFlags(flags, arguments));
CF_EXPECT(arguments.size() == 0, "Unrecognized arguments:'"
<< android::base::Join(arguments, "', '")
<< "'");
CF_EXPECT_EQ(parsed_flags.local_instance.is_set, true,
"Only '--local-instance' is supported");
auto host_dir = TempDir() + "/acloud_image_artifacts/";
if (parsed_flags.image_download_dir) {
host_dir =
parsed_flags.image_download_dir.value() + "/acloud_image_artifacts/";
}
const auto& request_command = request.Message().command_request();
auto host_artifacts_path = request_command.env().find(kAndroidHostOut);
CF_EXPECT(host_artifacts_path != request_command.env().end(),
"Missing " << kAndroidHostOut);
std::vector<cvd::Request> request_protos;
const uid_t uid = request.Credentials()->uid;
const std::string user_config_path =
parsed_flags.config_file.value_or(CF_EXPECT(GetDefaultConfigFile(uid)));
AcloudConfig acloud_config =
CF_EXPECT(LoadAcloudConfig(user_config_path, uid));
std::string fetch_command_str;
std::string fetch_cvd_args_file;
if (parsed_flags.local_image.given) {
CF_EXPECT(!(system_branch || system_build_target || system_build_id),
"--local-image incompatible with --system-* flags");
CF_EXPECT(!(parsed_flags.bootloader.branch ||
parsed_flags.bootloader.build_target ||
parsed_flags.bootloader.build_id),
"--local-image incompatible with --bootloader-* flags");
CF_EXPECT(
!(boot_branch || boot_build_target || boot_build_id || boot_artifact),
"--local-image incompatible with --boot-* flags");
CF_EXPECT(!(ota_branch || ota_build_target || ota_build_id),
"--local-image incompatible with --ota-* flags");
} else {
if (!DirectoryExists(host_dir)) {
// fetch/download directory doesn't exist, create directory
cvd::Request& mkdir_request = request_protos.emplace_back();
auto& mkdir_command = *mkdir_request.mutable_command_request();
mkdir_command.add_args("cvd");
mkdir_command.add_args("mkdir");
mkdir_command.add_args("-p");
mkdir_command.add_args(host_dir);
auto& mkdir_env = *mkdir_command.mutable_env();
mkdir_env[kAndroidHostOut] = host_artifacts_path->second;
}
// used for default branch and target when there is no input
std::optional<BranchBuildTargetInfo> given_branch_target_info;
if (parsed_flags.branch || parsed_flags.build_id ||
parsed_flags.build_target) {
auto target = parsed_flags.build_target ? *parsed_flags.build_target : "";
auto build = parsed_flags.build_id.value_or(
parsed_flags.branch.value_or("aosp-master"));
host_dir += (build + target);
} else {
given_branch_target_info = CF_EXPECT(GetDefaultBranchBuildTarget(
"git_", waiter, callback_unlock, callback_lock));
host_dir += (given_branch_target_info->branch_str +
given_branch_target_info->build_target_str);
}
// TODO(weihsu): The default branch and target value are the
// same as python acloud now. The only TODO item is default ID.
// Python acloud use Android build api to query build info,
// including the latest valid build ID. CVD acloud should follow
// the same method by using Android build api to get build ID,
// but it is not easy in C++.
cvd::Request& fetch_request = request_protos.emplace_back();
auto& fetch_command = *fetch_request.mutable_command_request();
fetch_command.add_args("cvd");
fetch_command.add_args("fetch");
fetch_command.add_args("--directory");
fetch_command.add_args(host_dir);
fetch_command.add_args("--default_build");
fetch_command_str += "--default_build=";
if (given_branch_target_info) {
fetch_command.add_args(given_branch_target_info->branch_str + "/" +
given_branch_target_info->build_target_str);
fetch_command_str += (given_branch_target_info->branch_str + "/" +
given_branch_target_info->build_target_str);
} else {
auto target =
parsed_flags.build_target ? "/" + *parsed_flags.build_target : "";
auto build = parsed_flags.build_id.value_or(
parsed_flags.branch.value_or("aosp-master"));
fetch_command.add_args(build + target);
fetch_command_str += (build + target);
}
if (system_branch || system_build_id || system_build_target) {
fetch_command.add_args("--system_build");
fetch_command_str += " --system_build=";
auto target =
system_build_target.value_or(parsed_flags.build_target.value_or(""));
if (target != "") {
target = "/" + target;
}
auto build =
system_build_id.value_or(system_branch.value_or("aosp-master"));
fetch_command.add_args(build + target);
fetch_command_str += (build + target);
}
if (parsed_flags.bootloader.branch || parsed_flags.bootloader.build_id ||
parsed_flags.bootloader.build_target) {
fetch_command.add_args("--bootloader_build");
fetch_command_str += " --bootloader_build=";
auto target = parsed_flags.bootloader.build_target.value_or("");
if (target != "") {
target = "/" + target;
}
auto build = parsed_flags.bootloader.build_id.value_or(
parsed_flags.bootloader.branch.value_or("aosp_u-boot-mainline"));
fetch_command.add_args(build + target);
fetch_command_str += (build + target);
}
if (boot_branch || boot_build_id || boot_build_target) {
fetch_command.add_args("--boot_build");
fetch_command_str += " --boot_build=";
auto target = boot_build_target.value_or("");
if (target != "") {
target = "/" + target;
}
auto build = boot_build_id.value_or(boot_branch.value_or("aosp-master"));
fetch_command.add_args(build + target);
fetch_command_str += (build + target);
}
if (boot_artifact) {
CF_EXPECT(boot_branch || boot_build_target || boot_build_id,
"--boot-artifact must combine with other --boot-* flags");
fetch_command.add_args("--boot_artifact");
fetch_command_str += " --boot_artifact=";
auto target = boot_artifact.value_or("");
fetch_command.add_args(target);
fetch_command_str += (target);
}
if (ota_branch || ota_build_id || ota_build_target) {
fetch_command.add_args("--otatools_build");
fetch_command_str += " --otatools_build=";
auto target = ota_build_target.value_or("");
if (target != "") {
target = "/" + target;
}
auto build = ota_build_id.value_or(ota_branch.value_or(""));
fetch_command.add_args(build + target);
fetch_command_str += (build + target);
}
if (kernel_branch || kernel_build_id || kernel_build_target) {
fetch_command.add_args("--kernel_build");
fetch_command_str += " --kernel_build=";
auto target = kernel_build_target.value_or("kernel_virt_x86_64");
auto build = kernel_build_id.value_or(
kernel_branch.value_or("aosp_kernel-common-android-mainline"));
fetch_command.add_args(build + "/" + target);
fetch_command_str += (build + "/" + target);
}
auto& fetch_env = *fetch_command.mutable_env();
fetch_env[kAndroidHostOut] = host_artifacts_path->second;
fetch_cvd_args_file = host_dir + "/fetch-cvd-args.txt";
if (FileExists(fetch_cvd_args_file)) {
// file exists
std::string read_str;
using android::base::ReadFileToString;
CF_EXPECT(ReadFileToString(fetch_cvd_args_file.c_str(), &read_str,
/* follow_symlinks */ true));
if (read_str == fetch_command_str) {
// same fetch cvd command, reuse original dir
fetch_command_str = "";
request_protos.pop_back();
}
}
}
std::string super_image_path;
if (parsed_flags.local_system_image) {
// in new cvd server design, at this point,
// we don't know which HOME is assigned by cvd start.
// create a temporary directory to store generated
// mix super image
TemporaryDir my_dir;
std::string required_paths;
my_dir.DoNotRemove();
super_image_path = std::string(my_dir.path) + "/" + kMixedSuperImageName;
// combine super_image path and local_system_image path
required_paths = super_image_path;
required_paths += ("," + parsed_flags.local_system_image.value());
cvd::Request& mixsuperimage_request = request_protos.emplace_back();
auto& mixsuperimage_command =
*mixsuperimage_request.mutable_command_request();
mixsuperimage_command.add_args("cvd");
mixsuperimage_command.add_args("acloud");
mixsuperimage_command.add_args("mix-super-image");
mixsuperimage_command.add_args("--super_image");
auto& mixsuperimage_env = *mixsuperimage_command.mutable_env();
if (parsed_flags.local_image.given) {
// added image_dir to required_paths for MixSuperImage use if there is
required_paths.append(",").append(
parsed_flags.local_image.path.value_or(""));
mixsuperimage_env[kAndroidHostOut] = host_artifacts_path->second;
auto product_out = request_command.env().find(kAndroidProductOut);
CF_EXPECT(product_out != request_command.env().end(),
"Missing " << kAndroidProductOut);
mixsuperimage_env[kAndroidProductOut] = product_out->second;
} else {
mixsuperimage_env[kAndroidHostOut] = host_dir;
mixsuperimage_env[kAndroidProductOut] = host_dir;
}
mixsuperimage_command.add_args(required_paths);
}
cvd::Request start_request;
auto& start_command = *start_request.mutable_command_request();
start_command.add_args("cvd");
start_command.add_args("start");
start_command.add_args("--daemon");
start_command.add_args("--undefok");
start_command.add_args("report_anonymous_usage_stats");
start_command.add_args("--report_anonymous_usage_stats");
start_command.add_args("y");
if (parsed_flags.flavor) {
start_command.add_args("-config");
start_command.add_args(parsed_flags.flavor.value());
}
if (parsed_flags.local_system_image) {
start_command.add_args("-super_image");
start_command.add_args(super_image_path);
}
if (parsed_flags.local_kernel_image) {
// kernel image has 1st priority than boot image
struct stat statbuf {};
std::string local_boot_image;
std::string vendor_boot_image;
std::string kernel_image;
std::string initramfs_image;
if (stat(parsed_flags.local_kernel_image.value().c_str(), &statbuf) == 0) {
if (statbuf.st_mode & S_IFDIR) {
// it's a directory, deal with kernel image case first
kernel_image = FindImage(parsed_flags.local_kernel_image.value(),
kKernelImageNames);
initramfs_image = FindImage(parsed_flags.local_kernel_image.value(),
kInitRamFsImageName);
// This is the original python acloud behavior, it
// expects both kernel and initramfs files, however,
// there are some very old kernels that are built without
// an initramfs.img file,
// e.g. aosp_kernel-common-android-4.14-stable
if (kernel_image != "" && initramfs_image != "") {
start_command.add_args("-kernel_path");
start_command.add_args(kernel_image);
start_command.add_args("-initramfs_path");
start_command.add_args(initramfs_image);
} else {
// boot.img case
// adding boot.img and vendor_boot.img to the path
local_boot_image = FindImage(parsed_flags.local_kernel_image.value(),
kBootImageName);
vendor_boot_image = FindImage(parsed_flags.local_kernel_image.value(),
kVendorBootImageName);
start_command.add_args("-boot_image");
start_command.add_args(local_boot_image);
// vendor boot image may not exist
if (vendor_boot_image != "") {
start_command.add_args("-vendor_boot_image");
start_command.add_args(vendor_boot_image);
}
}
} else if (statbuf.st_mode & S_IFREG) {
// it's a file which directly points to boot.img
local_boot_image = parsed_flags.local_kernel_image.value();
start_command.add_args("-boot_image");
start_command.add_args(local_boot_image);
}
}
} else if (kernel_branch || kernel_build_id || kernel_build_target) {
// fetch remote kernel image files
std::string kernel_image = host_dir + "/kernel";
// even if initramfs doesn't exist, launch_cvd will still handle it
// correctly. We push the initramfs handler to launch_cvd stage.
std::string initramfs_image = host_dir + "/initramfs.img";
start_command.add_args("-kernel_path");
start_command.add_args(kernel_image);
start_command.add_args("-initramfs_path");
start_command.add_args(initramfs_image);
}
if (launch_args) {
callback_lock();
for (const auto& arg : CF_EXPECT(BashTokenize(
*launch_args, waiter, callback_unlock))) {
start_command.add_args(arg);
}
}
if (acloud_config.launch_args != "") {
callback_lock();
for (const auto& arg : CF_EXPECT(BashTokenize(
acloud_config.launch_args, waiter, callback_unlock))) {
start_command.add_args(arg);
}
}
start_command.mutable_selector_opts()->add_args(
std::string("--") + selector::SelectorFlags::kDisableDefaultGroup +
"=true");
if (pet_name) {
const auto [group_name, instance_name] =
CF_EXPECT(selector::BreakDeviceName(*pet_name),
*pet_name << " must be a group name followed by - "
<< "followed by an instance name.");
std::string group_name_arg = "--";
group_name_arg.append(selector::SelectorFlags::kGroupName)
.append("=")
.append(group_name);
std::string instance_name_arg = "--";
instance_name_arg.append(selector::SelectorFlags::kInstanceName)
.append("=")
.append(instance_name);
start_command.mutable_selector_opts()->add_args(group_name_arg);
start_command.mutable_selector_opts()->add_args(instance_name_arg);
}
if (use_16k) {
start_command.add_args("--use_16k");
}
auto& start_env = *start_command.mutable_env();
if (parsed_flags.local_image.given) {
if (parsed_flags.local_image.path) {
std::string local_image_path_str = parsed_flags.local_image.path.value();
// Python acloud source: local_image_local_instance.py;l=81
// this acloud flag is equal to launch_cvd flag system_image_dir
start_command.add_args("-system_image_dir");
start_command.add_args(local_image_path_str);
}
start_env[kAndroidHostOut] = host_artifacts_path->second;
auto product_out = request_command.env().find(kAndroidProductOut);
CF_EXPECT(product_out != request_command.env().end(),
"Missing " << kAndroidProductOut);
start_env[kAndroidProductOut] = product_out->second;
} else {
start_env[kAndroidHostOut] = host_dir;
start_env[kAndroidProductOut] = host_dir;
}
if (Contains(start_env, kCuttlefishInstanceEnvVarName)) {
// Python acloud does not use this variable.
// this variable will confuse cvd start, though
start_env.erase(kCuttlefishInstanceEnvVarName);
}
if (parsed_flags.local_instance.id) {
start_env[kCuttlefishInstanceEnvVarName] =
std::to_string(*parsed_flags.local_instance.id);
}
// we don't know which HOME is assigned by cvd start.
// cvd server does not rely on the working directory for cvd start
*start_command.mutable_working_directory() =
request_command.working_directory();
std::vector<SharedFD> fds;
if (parsed_flags.verbose) {
fds = request.FileDescriptors();
} else {
auto dev_null = SharedFD::Open("/dev/null", O_RDWR);
CF_EXPECT(dev_null->IsOpen(), dev_null->StrError());
fds = {dev_null, dev_null, dev_null};
}
ConvertedAcloudCreateCommand ret{
.start_request = RequestWithStdio(request.Client(), start_request, fds,
request.Credentials()),
.fetch_command_str = fetch_command_str,
.fetch_cvd_args_file = fetch_cvd_args_file,
.verbose = parsed_flags.verbose,
};
for (auto& request_proto : request_protos) {
ret.prep_requests.emplace_back(request.Client(), request_proto, fds,
request.Credentials());
}
return ret;
}
} // namespace acloud_impl
} // namespace cuttlefish