731 lines
29 KiB
C++
731 lines
29 KiB
C++
/*
|
|
* 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 <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 "common/libs/utils/users.h"
|
|
#include "cvd_server.pb.h"
|
|
#include "host/commands/cvd/acloud/config.h"
|
|
#include "host/commands/cvd/command_sequence.h"
|
|
#include "host/commands/cvd/common_utils.h"
|
|
#include "host/commands/cvd/instance_lock.h" // TempDir()
|
|
#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> _KERNEL_IMAGE_NAMES = {"kernel", "bzImage",
|
|
"Image"};
|
|
const std::vector<std::string> _INITRAMFS_IMAGE_NAME = {"initramfs.img"};
|
|
const std::vector<std::string> _BOOT_IMAGE_NAME = {"boot.img"};
|
|
const std::vector<std::string> _VENDOR_BOOT_IMAGE_NAME = {"vendor_boot.img"};
|
|
|
|
/**
|
|
* Find a image file through the input path and pattern.
|
|
*
|
|
* If it finds the file, return the path string.
|
|
* If it can't find the file, return empty string.
|
|
*/
|
|
std::string FindImage(const std::string& search_path,
|
|
const std::vector<std::string>& pattern) {
|
|
const std::string& search_path_extend = search_path + "/";
|
|
for (const auto& name : pattern) {
|
|
const std::string image = search_path_extend + name;
|
|
if (FileExists(image)) {
|
|
return image;
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
Command command("bash");
|
|
command.AddParameter("-c");
|
|
command.AddParameter("printf '%s\n' ", str);
|
|
std::string stdout_str;
|
|
std::string stderr_str;
|
|
auto ret = RunWithManagedStdio(std::move(command), nullptr, &stdout_str,
|
|
&stderr_str);
|
|
CF_EXPECT(ret == 0,
|
|
"printf fail \"" << stdout_str << "\", \"" << stderr_str << "\"");
|
|
return android::base::Split(stdout_str, "\n");
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// body of pure virtual destructor required by C++
|
|
ConvertAcloudCreateCommand::~ConvertAcloudCreateCommand() {}
|
|
|
|
class ConvertAcloudCreateCommandImpl : public ConvertAcloudCreateCommand {
|
|
public:
|
|
INJECT(ConvertAcloudCreateCommandImpl()) {}
|
|
~ConvertAcloudCreateCommandImpl() override = default;
|
|
|
|
Result<ConvertedAcloudCreateCommand> Convert(
|
|
const RequestWithStdio& request) {
|
|
auto arguments = ParseInvocation(request.Message()).arguments;
|
|
CF_EXPECT(arguments.size() > 0);
|
|
CF_EXPECT(arguments[0] == "create");
|
|
arguments.erase(arguments.begin());
|
|
|
|
const auto& request_command = request.Message().command_request();
|
|
|
|
std::vector<Flag> flags;
|
|
bool local_instance_set;
|
|
std::optional<int> local_instance;
|
|
auto local_instance_flag = Flag();
|
|
local_instance_flag.Alias(
|
|
{FlagAliasMode::kFlagConsumesArbitrary, "--local-instance"});
|
|
local_instance_flag.Setter([&local_instance_set,
|
|
&local_instance](const FlagMatch& m) {
|
|
local_instance_set = true;
|
|
if (m.value != "" && local_instance) {
|
|
LOG(ERROR) << "Instance number already set, was \"" << *local_instance
|
|
<< "\", now set to \"" << m.value << "\"";
|
|
return false;
|
|
} else if (m.value != "" && !local_instance) {
|
|
local_instance = std::stoi(m.value);
|
|
}
|
|
return true;
|
|
});
|
|
flags.emplace_back(local_instance_flag);
|
|
|
|
std::optional<std::string> flavor;
|
|
flags.emplace_back(
|
|
Flag()
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--config"})
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--flavor"})
|
|
.Setter([&flavor](const FlagMatch& m) {
|
|
flavor = m.value;
|
|
return true;
|
|
}));
|
|
|
|
std::optional<std::string> local_kernel_image;
|
|
flags.emplace_back(Flag()
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing,
|
|
"--local-kernel-image"})
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing,
|
|
"--local-boot-image"})
|
|
.Setter([&local_kernel_image](const FlagMatch& m) {
|
|
local_kernel_image = m.value;
|
|
return true;
|
|
}));
|
|
|
|
std::optional<std::string> image_download_dir;
|
|
flags.emplace_back(Flag()
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing,
|
|
"--image-download-dir"})
|
|
.Setter([&image_download_dir](const FlagMatch& m) {
|
|
image_download_dir = m.value;
|
|
return true;
|
|
}));
|
|
|
|
verbose_ = false;
|
|
flags.emplace_back(Flag()
|
|
.Alias({FlagAliasMode::kFlagExact, "-v"})
|
|
.Alias({FlagAliasMode::kFlagExact, "-vv"})
|
|
.Alias({FlagAliasMode::kFlagExact, "--verbose"})
|
|
.Setter([this](const FlagMatch&) {
|
|
verbose_ = true;
|
|
return true;
|
|
}));
|
|
|
|
std::optional<std::string> branch;
|
|
flags.emplace_back(
|
|
Flag()
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--branch"})
|
|
.Setter([&branch](const FlagMatch& m) {
|
|
branch = m.value;
|
|
return true;
|
|
}));
|
|
|
|
bool local_image;
|
|
std::optional<std::string> local_image_path;
|
|
flags.emplace_back(
|
|
Flag()
|
|
.Alias({FlagAliasMode::kFlagConsumesArbitrary, "--local-image"})
|
|
.Setter([&local_image,
|
|
&local_image_path](const FlagMatch& m) {
|
|
local_image = true;
|
|
if (m.value != "") {
|
|
local_image_path = m.value;
|
|
}
|
|
return true;
|
|
}));
|
|
|
|
std::optional<std::string> build_id;
|
|
flags.emplace_back(
|
|
Flag()
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--build-id"})
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--build_id"})
|
|
.Setter([&build_id](const FlagMatch& m) {
|
|
build_id = m.value;
|
|
return true;
|
|
}));
|
|
|
|
std::optional<std::string> build_target;
|
|
flags.emplace_back(
|
|
Flag()
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--build-target"})
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--build_target"})
|
|
.Setter([&build_target](const FlagMatch& m) {
|
|
build_target = m.value;
|
|
return true;
|
|
}));
|
|
|
|
std::optional<std::string> config_file;
|
|
flags.emplace_back(
|
|
Flag()
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--config-file"})
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--config_file"})
|
|
.Setter([&config_file](const FlagMatch& m) {
|
|
config_file = m.value;
|
|
return true;
|
|
}));
|
|
|
|
std::optional<std::string> bootloader_build_id;
|
|
flags.emplace_back(Flag()
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing,
|
|
"--bootloader-build-id"})
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing,
|
|
"--bootloader_build_id"})
|
|
.Setter([&bootloader_build_id](const FlagMatch& m) {
|
|
bootloader_build_id = m.value;
|
|
return true;
|
|
}));
|
|
std::optional<std::string> bootloader_build_target;
|
|
flags.emplace_back(
|
|
Flag()
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing,
|
|
"--bootloader-build-target"})
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing,
|
|
"--bootloader_build_target"})
|
|
.Setter([&bootloader_build_target](const FlagMatch& m) {
|
|
bootloader_build_target = m.value;
|
|
return true;
|
|
}));
|
|
std::optional<std::string> bootloader_branch;
|
|
flags.emplace_back(Flag()
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing,
|
|
"--bootloader-branch"})
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing,
|
|
"--bootloader_branch"})
|
|
.Setter([&bootloader_branch](const FlagMatch& m) {
|
|
bootloader_branch = m.value;
|
|
return true;
|
|
}));
|
|
|
|
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) {
|
|
boot_build_id = m.value;
|
|
return true;
|
|
}));
|
|
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) {
|
|
boot_build_target = m.value;
|
|
return true;
|
|
}));
|
|
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) {
|
|
boot_branch = m.value;
|
|
return true;
|
|
}));
|
|
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) {
|
|
boot_artifact = m.value;
|
|
return true;
|
|
}));
|
|
|
|
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) {
|
|
ota_build_id = m.value;
|
|
return true;
|
|
}));
|
|
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) {
|
|
ota_build_target = m.value;
|
|
return true;
|
|
}));
|
|
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) {
|
|
ota_branch = m.value;
|
|
return true;
|
|
}));
|
|
|
|
std::optional<std::string> launch_args;
|
|
flags.emplace_back(
|
|
Flag()
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--launch-args"})
|
|
.Setter([&launch_args](const FlagMatch& m) {
|
|
launch_args = m.value;
|
|
return true;
|
|
}));
|
|
|
|
std::optional<std::string> system_branch;
|
|
flags.emplace_back(
|
|
Flag()
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--system-branch"})
|
|
.Setter([&system_branch](const FlagMatch& m) {
|
|
system_branch = m.value;
|
|
return true;
|
|
}));
|
|
|
|
std::optional<std::string> system_build_target;
|
|
flags.emplace_back(Flag()
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing,
|
|
"--system-build-target"})
|
|
.Setter([&system_build_target](const FlagMatch& m) {
|
|
system_build_target = m.value;
|
|
return true;
|
|
}));
|
|
|
|
std::optional<std::string> system_build_id;
|
|
flags.emplace_back(
|
|
Flag()
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--system-build-id"})
|
|
.Setter([&system_build_id](const FlagMatch& m) {
|
|
system_build_id = m.value;
|
|
return true;
|
|
}));
|
|
|
|
std::optional<std::string> kernel_branch;
|
|
flags.emplace_back(
|
|
Flag()
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--kernel-branch"})
|
|
.Setter([&kernel_branch](const FlagMatch& m) {
|
|
kernel_branch = m.value;
|
|
return true;
|
|
}));
|
|
|
|
std::optional<std::string> kernel_build_target;
|
|
flags.emplace_back(Flag()
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing,
|
|
"--kernel-build-target"})
|
|
.Setter([&kernel_build_target](const FlagMatch& m) {
|
|
kernel_build_target = m.value;
|
|
return true;
|
|
}));
|
|
|
|
std::optional<std::string> kernel_build_id;
|
|
flags.emplace_back(
|
|
Flag()
|
|
.Alias({FlagAliasMode::kFlagConsumesFollowing, "--kernel-build-id"})
|
|
.Setter([&kernel_build_id](const FlagMatch& m) {
|
|
kernel_build_id = m.value;
|
|
return true;
|
|
}));
|
|
|
|
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) {
|
|
pet_name = match.value;
|
|
return true;
|
|
}));
|
|
|
|
CF_EXPECT(ParseFlags(flags, arguments));
|
|
CF_EXPECT(arguments.size() == 0,
|
|
"Unrecognized arguments:'"
|
|
<< android::base::Join(arguments, "', '") << "'");
|
|
|
|
CF_EXPECT(local_instance_set == true,
|
|
"Only '--local-instance' is supported");
|
|
auto host_dir = TempDir() + "/acloud_image_artifacts/";
|
|
if (image_download_dir) {
|
|
host_dir = image_download_dir.value() + "/acloud_image_artifacts/";
|
|
}
|
|
|
|
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;
|
|
// default user config path
|
|
std::string user_config_path = CF_EXPECT(GetDefaultConfigFile(uid));
|
|
|
|
if (config_file) {
|
|
user_config_path = config_file.value();
|
|
}
|
|
AcloudConfig acloud_config =
|
|
CF_EXPECT(LoadAcloudConfig(user_config_path, uid));
|
|
|
|
if (local_image) {
|
|
CF_EXPECT(!(system_branch || system_build_target || system_build_id),
|
|
"--local-image incompatible with --system-* flags");
|
|
CF_EXPECT(!(bootloader_branch || bootloader_build_target ||
|
|
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;
|
|
}
|
|
if (branch || build_id || build_target) {
|
|
auto target = build_target ? *build_target : "";
|
|
auto build = build_id.value_or(branch.value_or("aosp-master"));
|
|
host_dir += (build + target);
|
|
} else {
|
|
host_dir += "aosp-master";
|
|
}
|
|
// TODO(weihsu): if we fetch default ID such as aosp-master,
|
|
// cvd fetch will fetch the latest release. There is a potential
|
|
// issue that two different fetch with same default ID may
|
|
// download different releases.
|
|
// Eventually, we should match python acloud behavior to translate
|
|
// default ID (aosp-master) to real ID to solve this issue.
|
|
|
|
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_str_ = "";
|
|
if (branch || build_id || build_target) {
|
|
fetch_command.add_args("--default_build");
|
|
fetch_command_str_ += "--default_build=";
|
|
auto target = build_target ? "/" + *build_target : "";
|
|
auto build = build_id.value_or(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(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 (bootloader_branch || bootloader_build_id || bootloader_build_target) {
|
|
fetch_command.add_args("--bootloader_build");
|
|
fetch_command_str_ += " --bootloader_build=";
|
|
auto target = bootloader_build_target.value_or("");
|
|
if (target != "") {
|
|
target = "/" + target;
|
|
}
|
|
auto build = bootloader_build_id.value_or(
|
|
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(
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
|
|
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 (flavor) {
|
|
start_command.add_args("-config");
|
|
start_command.add_args(flavor.value());
|
|
}
|
|
|
|
if (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(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(local_kernel_image.value(), _KERNEL_IMAGE_NAMES);
|
|
initramfs_image =
|
|
FindImage(local_kernel_image.value(), _INITRAMFS_IMAGE_NAME);
|
|
// 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(local_kernel_image.value(), _BOOT_IMAGE_NAME);
|
|
vendor_boot_image =
|
|
FindImage(local_kernel_image.value(), _VENDOR_BOOT_IMAGE_NAME);
|
|
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 = local_kernel_image.value();
|
|
start_command.add_args("-boot_image");
|
|
start_command.add_args(local_boot_image);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (launch_args) {
|
|
for (const auto& arg : CF_EXPECT(BashTokenize(*launch_args))) {
|
|
start_command.add_args(arg);
|
|
}
|
|
}
|
|
if (acloud_config.launch_args != "") {
|
|
for (const auto& arg :
|
|
CF_EXPECT(BashTokenize(acloud_config.launch_args))) {
|
|
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);
|
|
}
|
|
|
|
auto& start_env = *start_command.mutable_env();
|
|
if (local_image) {
|
|
if (local_image_path) {
|
|
std::string local_image_path_str = 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 (local_instance) {
|
|
start_env[kCuttlefishInstanceEnvVarName] =
|
|
std::to_string(*local_instance);
|
|
}
|
|
// 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 (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())};
|
|
for (auto& request_proto : request_protos) {
|
|
ret.prep_requests.emplace_back(request.Client(), request_proto, fds,
|
|
request.Credentials());
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
const std::string& FetchCvdArgsFile() const override {
|
|
return fetch_cvd_args_file_;
|
|
}
|
|
|
|
const std::string& FetchCommandString() const override {
|
|
return fetch_command_str_;
|
|
}
|
|
bool Verbose() const { return verbose_; }
|
|
|
|
private:
|
|
std::string fetch_cvd_args_file_;
|
|
std::string fetch_command_str_;
|
|
bool verbose_;
|
|
};
|
|
|
|
fruit::Component<ConvertAcloudCreateCommand>
|
|
AcloudCreateConvertCommandComponent() {
|
|
return fruit::createComponent()
|
|
.bind<ConvertAcloudCreateCommand, ConvertAcloudCreateCommandImpl>();
|
|
}
|
|
|
|
} // namespace cuttlefish
|