Revert "Revert "Enable f2fs sparse images + qcow changes""
This reverts commit 6efd9aa78b9ffa5ca52ba9227748d6dbe4641d11.
Test: TreeHugger with later changes
Change-Id: I32393d86843da3cb98501fda054188141d27cfc5
diff --git a/host/commands/assemble_cvd/Android.bp b/host/commands/assemble_cvd/Android.bp
index fcfa903..807f18c 100644
--- a/host/commands/assemble_cvd/Android.bp
+++ b/host/commands/assemble_cvd/Android.bp
@@ -53,6 +53,7 @@
"libz",
],
static_libs: [
+ "libsparse",
"libcuttlefish_host_config",
"libcuttlefish_vm_manager",
"libgflags",
diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc
index f9b183d..241912b 100644
--- a/host/commands/assemble_cvd/flags.cc
+++ b/host/commands/assemble_cvd/flags.cc
@@ -1,6 +1,10 @@
#include "host/commands/assemble_cvd/flags.h"
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <sys/statvfs.h>
+#include <unistd.h>
#include <algorithm>
#include <iostream>
@@ -68,10 +72,6 @@
"be vendor_boot.img in the directory specified by -system_image_dir.");
DEFINE_int32(memory_mb, 2048,
"Total amount of memory available for guest, MB.");
-DEFINE_string(mobile_interface, ForCurrentInstance("cvd-mbr-"),
- "Network interface to use for mobile networking");
-DEFINE_string(mobile_tap_name, ForCurrentInstance("cvd-mtap-"),
- "The name of the tap interface to use for mobile");
DEFINE_string(serial_number, ForCurrentInstance("CUTTLEFISHCVD"),
"Serial number to use for the device");
DEFINE_string(assembly_dir,
@@ -93,13 +93,15 @@
DEFINE_string(misc_image, "",
"Location of the misc partition image. If the image does not "
"exist, a blank new misc partition image is created.");
-DEFINE_string(composite_disk, "", "Location of the composite disk image. "
- "If empty, a composite disk is not used.");
+DEFINE_string(composite_disk, "", "Location of the composite disk image. ");
DEFINE_bool(deprecated_boot_completed, false, "Log boot completed message to"
" host kernel. This is only used during transition of our clients."
" Will be deprecated soon.");
-DEFINE_bool(start_vnc_server, false, "Whether to start the vnc server process.");
+DEFINE_bool(start_vnc_server, false, "Whether to start the vnc server process. "
+ "The VNC server runs at port 6443 + i for "
+ "the vsoc-i user or CUTTLEFISH_INSTANCE=i, "
+ "starting from 1.");
DEFINE_bool(start_webrtc, false, "Whether to start the webrtc process.");
@@ -123,8 +125,6 @@
false,
"If enabled, exposes local adb service through a websocket.");
-DEFINE_int32(vnc_server_port, ForCurrentInstance(6444),
- "The port on which the vnc server should listen");
DEFINE_string(adb_mode, "vsock_half_tunnel",
"Mode for ADB connection."
"'vsock_tunnel' for a TCP connection tunneled through vsock, "
@@ -135,11 +135,6 @@
DEFINE_bool(run_adb_connector, true,
"Maintain adb connection by sending 'adb connect' commands to the "
"server. Only relevant with -adb_mode=tunnel or vsock_tunnel");
-DEFINE_string(wifi_tap_name, ForCurrentInstance("cvd-wtap-"),
- "The name of the tap interface to use for wifi");
-DEFINE_int32(vsock_guest_cid,
- vsoc::GetDefaultPerInstanceVsockCid(),
- "Guest identifier for vsock. Disabled if under 3.");
DEFINE_string(uuid, vsoc::ForCurrentInstance(vsoc::kDefaultUuidPrefix),
"UUID to use for the device. Random if not specified");
@@ -169,6 +164,12 @@
"the slot will be chosen based on the misc partition if using a "
"bootloader. It will default to 'a' if empty and not using a "
"bootloader.");
+DEFINE_int32(num_instances, 1, "Number of Android guests to launch");
+DEFINE_bool(resume, true, "Resume using the disk from the last session, if "
+ "possible. i.e., if --noresume is passed, the disk "
+ "will be reset to the state it was initially launched "
+ "in. This flag is ignored if the underlying partition "
+ "images have been updated since the first launch.");
namespace {
@@ -222,19 +223,20 @@
return config.ForDefaultInstance().PerInstancePath("cuttlefish_config.json");
}
-int GetHostPort() {
- constexpr int kFirstHostPort = 6520;
- return vsoc::ForCurrentInstance(kFirstHostPort);
-}
-
int NumStreamers() {
auto start_flags = {FLAGS_start_vnc_server, FLAGS_start_webrtc};
return std::count(start_flags.begin(), start_flags.end(), true);
}
+std::string StrForInstance(const std::string& prefix, int num) {
+ std::ostringstream stream;
+ stream << prefix << std::setfill('0') << std::setw(2) << num;
+ return stream.str();
+}
+
// Initializes the config object and saves it to file. It doesn't return it, all
// further uses of the config should happen through the singleton
-bool InitializeCuttlefishConfiguration(
+vsoc::CuttlefishConfig InitializeCuttlefishConfiguration(
const cvd::BootImageUnpacker& boot_image_unpacker,
const cvd::FetcherConfig& fetcher_config) {
// At most one streamer can be started.
@@ -242,28 +244,20 @@
vsoc::CuttlefishConfig tmp_config_obj;
tmp_config_obj.set_assembly_dir(FLAGS_assembly_dir);
- auto instance = tmp_config_obj.ForDefaultInstance();
- // Set this first so that calls to PerInstancePath below are correct
- instance.set_instance_dir(FLAGS_instance_dir);
if (!vm_manager::VmManager::IsValidName(FLAGS_vm_manager)) {
- LOG(ERROR) << "Invalid vm_manager: " << FLAGS_vm_manager;
- return false;
+ LOG(FATAL) << "Invalid vm_manager: " << FLAGS_vm_manager;
}
if (!vm_manager::VmManager::IsValidName(FLAGS_vm_manager)) {
- LOG(ERROR) << "Invalid vm_manager: " << FLAGS_vm_manager;
- return false;
+ LOG(FATAL) << "Invalid vm_manager: " << FLAGS_vm_manager;
}
tmp_config_obj.set_vm_manager(FLAGS_vm_manager);
tmp_config_obj.set_gpu_mode(FLAGS_gpu_mode);
if (vm_manager::VmManager::ConfigureGpuMode(tmp_config_obj.vm_manager(),
tmp_config_obj.gpu_mode()).empty()) {
- LOG(ERROR) << "Invalid gpu_mode=" << FLAGS_gpu_mode <<
+ LOG(FATAL) << "Invalid gpu_mode=" << FLAGS_gpu_mode <<
" does not work with vm_manager=" << FLAGS_vm_manager;
- return false;
}
- instance.set_serial_number(FLAGS_serial_number);
-
tmp_config_obj.set_cpus(FLAGS_cpus);
tmp_config_obj.set_memory_mb(FLAGS_memory_mb);
@@ -275,10 +269,6 @@
tmp_config_obj.set_gdb_flag(FLAGS_qemu_gdb);
std::vector<std::string> adb = android::base::Split(FLAGS_adb_mode, ",");
tmp_config_obj.set_adb_mode(std::set<std::string>(adb.begin(), adb.end()));
- instance.set_host_port(GetHostPort());
- instance.set_adb_ip_and_port("127.0.0.1:" + std::to_string(GetHostPort()));
-
- instance.set_device_title(FLAGS_device_title);
std::string discovered_kernel = fetcher_config.FindCvdFileWithSuffix(kKernelDefaultPath);
std::string foreign_kernel = FLAGS_kernel_path.size() ? FLAGS_kernel_path : discovered_kernel;
if (foreign_kernel.size()) {
@@ -298,8 +288,7 @@
auto ramdisk_path = tmp_config_obj.AssemblyPath("ramdisk.img");
auto vendor_ramdisk_path = tmp_config_obj.AssemblyPath("vendor_ramdisk.img");
if (!boot_image_unpacker.HasRamdiskImage()) {
- LOG(INFO) << "A ramdisk is required, but the boot image did not have one.";
- return false;
+ LOG(FATAL) << "A ramdisk is required, but the boot image did not have one.";
}
tmp_config_obj.set_boot_image_kernel_cmdline(boot_image_unpacker.kernel_cmdline());
@@ -309,8 +298,6 @@
tmp_config_obj.set_guest_force_normal_boot(FLAGS_guest_force_normal_boot);
tmp_config_obj.set_extra_kernel_cmdline(FLAGS_extra_kernel_cmdline);
- tmp_config_obj.set_virtual_disk_paths({FLAGS_composite_disk});
-
tmp_config_obj.set_ramdisk_image_path(ramdisk_path);
tmp_config_obj.set_vendor_ramdisk_image_path(vendor_ramdisk_path);
@@ -336,15 +323,6 @@
tmp_config_obj.set_config_server_binary(
vsoc::DefaultHostArtifactsPath("bin/config_server"));
- instance.set_mobile_bridge_name(FLAGS_mobile_interface);
- instance.set_mobile_tap_name(FLAGS_mobile_tap_name);
-
- instance.set_wifi_tap_name(FLAGS_wifi_tap_name);
-
- instance.set_vsock_guest_cid(FLAGS_vsock_guest_cid);
-
- instance.set_uuid(FLAGS_uuid);
-
tmp_config_obj.set_qemu_binary(FLAGS_qemu_binary);
tmp_config_obj.set_crosvm_binary(FLAGS_crosvm_binary);
tmp_config_obj.set_console_forwarder_binary(
@@ -355,7 +333,6 @@
tmp_config_obj.set_enable_vnc_server(FLAGS_start_vnc_server);
tmp_config_obj.set_vnc_server_binary(
vsoc::DefaultHostArtifactsPath("bin/vnc_server"));
- instance.set_vnc_server_port(FLAGS_vnc_server_port);
tmp_config_obj.set_enable_webrtc(FLAGS_start_webrtc);
tmp_config_obj.set_webrtc_binary(
@@ -394,6 +371,41 @@
tmp_config_obj.set_cuttlefish_env_path(GetCuttlefishEnvPath());
+ std::vector<int> instance_nums;
+ for (int i = 0; i < FLAGS_num_instances; i++) {
+ instance_nums.push_back(vsoc::GetInstance() + i);
+ }
+
+ for (const auto& num : instance_nums) {
+ auto instance = tmp_config_obj.ForInstance(num);
+ auto const_instance = const_cast<const vsoc::CuttlefishConfig&>(tmp_config_obj)
+ .ForInstance(num);
+ // Set this first so that calls to PerInstancePath below are correct
+ instance.set_instance_dir(FLAGS_instance_dir + "." + std::to_string(num));
+ instance.set_serial_number(FLAGS_serial_number + std::to_string(num));
+
+ instance.set_mobile_bridge_name(StrForInstance("cvd-mbr-", num));
+ instance.set_mobile_tap_name(StrForInstance("cvd-mtap-", num));
+
+ instance.set_wifi_tap_name(StrForInstance("cvd-wtap-", num));
+
+ instance.set_vsock_guest_cid(3 + num - 1);
+
+ instance.set_uuid(FLAGS_uuid);
+
+ instance.set_vnc_server_port(6444 + num - 1);
+ instance.set_host_port(6520 + num - 1);
+ instance.set_adb_ip_and_port("127.0.0.1:" + std::to_string(6520 + num - 1));
+
+ instance.set_device_title(FLAGS_device_title);
+
+ instance.set_virtual_disk_paths({const_instance.PerInstancePath("overlay.img")});
+ }
+
+ return tmp_config_obj;
+}
+
+bool SaveConfig(const vsoc::CuttlefishConfig& tmp_config_obj) {
auto config_file = GetConfigFilePath(tmp_config_obj);
auto config_link = vsoc::GetGlobalConfigFileLink();
// Save the config object before starting any host process
@@ -456,15 +468,77 @@
return ResolveInstanceFiles();
}
-bool CleanPriorFiles() {
- // Everything on the instance directory
- std::string prior_files = FLAGS_instance_dir + "/*";
- // Everything in the assembly directory
- prior_files += " " + FLAGS_assembly_dir + "/*";
- // The environment file
- prior_files += " " + GetCuttlefishEnvPath();
- // The global link to the config file
- prior_files += " " + vsoc::GetGlobalConfigFileLink();
+std::string cpp_basename(const std::string& str) {
+ char* copy = strdup(str.c_str()); // basename may modify its argument
+ std::string ret(basename(copy));
+ free(copy);
+ return ret;
+}
+
+bool CleanPriorFiles(const std::string& path, const std::set<std::string>& preserving) {
+ if (preserving.count(cpp_basename(path))) {
+ LOG(INFO) << "Preserving: " << path;
+ return true;
+ }
+ struct stat statbuf;
+ if (lstat(path.c_str(), &statbuf) < 0) {
+ int error_num = errno;
+ if (error_num == ENOENT) {
+ return true;
+ } else {
+ LOG(ERROR) << "Could not stat \"" << path << "\": " << strerror(error_num);
+ return false;
+ }
+ }
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ LOG(INFO) << "Deleting: " << path;
+ if (unlink(path.c_str()) < 0) {
+ int error_num = errno;
+ LOG(ERROR) << "Could not unlink \"" << path << "\", error was " << strerror(error_num);
+ return false;
+ }
+ return true;
+ }
+ std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(path.c_str()), closedir);
+ if (!dir) {
+ int error_num = errno;
+ LOG(ERROR) << "Could not clean \"" << path << "\": error was " << strerror(error_num);
+ return false;
+ }
+ for (auto entity = readdir(dir.get()); entity != nullptr; entity = readdir(dir.get())) {
+ std::string entity_name(entity->d_name);
+ if (entity_name == "." || entity_name == "..") {
+ continue;
+ }
+ std::string entity_path = path + "/" + entity_name;
+ if (!CleanPriorFiles(entity_path.c_str(), preserving)) {
+ return false;
+ }
+ }
+ if (rmdir(path.c_str()) < 0) {
+ if (!(errno == EEXIST || errno == ENOTEMPTY)) {
+ // If EEXIST or ENOTEMPTY, probably because a file was preserved
+ int error_num = errno;
+ LOG(ERROR) << "Could not rmdir \"" << path << "\", error was " << strerror(error_num);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool CleanPriorFiles(const std::vector<std::string>& paths, const std::set<std::string>& preserving) {
+ std::string prior_files;
+ for (auto path : paths) {
+ struct stat statbuf;
+ if (stat(path.c_str(), &statbuf) < 0 && errno != ENOENT) {
+ // If ENOENT, it doesn't exist yet, so there is no work to do'
+ int error_num = errno;
+ LOG(ERROR) << "Could not stat \"" << path << "\": " << strerror(error_num);
+ return false;
+ }
+ bool is_directory = (statbuf.st_mode & S_IFMT) == S_IFDIR;
+ prior_files += (is_directory ? (path + "/*") : path) + " ";
+ }
LOG(INFO) << "Assuming prior files of " << prior_files;
std::string lsof_cmd = "lsof -t " + prior_files + " >/dev/null 2>&1";
int rval = std::system(lsof_cmd.c_str());
@@ -473,15 +547,31 @@
LOG(ERROR) << "Clean aborted: files are in use";
return false;
}
- std::string clean_command = "rm -rf " + prior_files;
- rval = std::system(clean_command.c_str());
- if (WEXITSTATUS(rval) != 0) {
- LOG(ERROR) << "Remove of files failed";
- return false;
+ for (const auto& path : paths) {
+ if (!CleanPriorFiles(path, preserving)) {
+ LOG(ERROR) << "Remove of file under \"" << path << "\" failed";
+ return false;
+ }
}
return true;
}
+bool CleanPriorFiles(const vsoc::CuttlefishConfig& config, const std::set<std::string>& preserving) {
+ std::vector<std::string> paths = {
+ // Everything in the assembly directory
+ FLAGS_assembly_dir,
+ // The environment file
+ GetCuttlefishEnvPath(),
+ // The global link to the config file
+ vsoc::GetGlobalConfigFileLink(),
+ };
+ for (const auto& instance : config.Instances()) {
+ paths.push_back(instance.instance_dir());
+ }
+ paths.push_back(FLAGS_instance_dir);
+ return CleanPriorFiles(paths, preserving);
+}
+
bool DecompressKernel(const std::string& src, const std::string& dst) {
cvd::Command decomp_cmd(vsoc::DefaultHostArtifactsPath("bin/extract-vmlinux"));
decomp_cmd.AddParameter(src);
@@ -537,23 +627,20 @@
return partitions;
}
-bool ShouldCreateCompositeDisk() {
- if (FLAGS_vm_manager == vm_manager::CrosvmManager::name()) {
- // The crosvm implementation is very fast to rebuild but also more brittle due to being split
- // into multiple files. The QEMU implementation is slow to build, but completely self-contained
- // at that point. Therefore, always rebuild on crosvm but check if it is necessary for QEMU.
- return true;
- }
- auto composite_age = cvd::FileModificationTime(FLAGS_composite_disk);
+std::chrono::system_clock::time_point LastUpdatedInputDisk() {
+ std::chrono::system_clock::time_point ret;
for (auto& partition : disk_config()) {
- auto partition_age = cvd::FileModificationTime(partition.image_file_path);
- if (partition_age >= composite_age) {
- LOG(INFO) << "composite disk age was \"" << std::chrono::system_clock::to_time_t(composite_age) << "\", "
- << "partition age was \"" << std::chrono::system_clock::to_time_t(partition_age) << "\"";
- return true;
+ auto partition_mod_time = cvd::FileModificationTime(partition.image_file_path);
+ if (partition_mod_time > ret) {
+ ret = partition_mod_time;
}
}
- return false;
+ return ret;
+}
+
+bool ShouldCreateCompositeDisk() {
+ auto composite_age = cvd::FileModificationTime(FLAGS_composite_disk);
+ return composite_age < LastUpdatedInputDisk();
}
bool ConcatRamdisks(const std::string& new_ramdisk_path, const std::string& ramdisk_a_path,
@@ -606,7 +693,7 @@
}
std::string header_path = config.AssemblyPath("gpt_header.img");
std::string footer_path = config.AssemblyPath("gpt_footer.img");
- create_composite_disk(disk_config(), header_path, footer_path, FLAGS_composite_disk);
+ CreateCompositeDisk(disk_config(), header_path, footer_path, FLAGS_composite_disk);
} else {
auto existing_size = cvd::FileSize(FLAGS_composite_disk);
auto available_space = AvailableSpaceAtPath(FLAGS_composite_disk);
@@ -616,7 +703,7 @@
LOG(ERROR) << "Got " << available_space;
return false;
}
- aggregate_image(disk_config(), FLAGS_composite_disk);
+ AggregateImage(disk_config(), FLAGS_composite_disk);
}
return true;
}
@@ -630,39 +717,72 @@
exit(AssemblerExitCodes::kArgumentParsingError);
}
- // Clean up prior files before saving the config file (doing it after would
- // delete it)
- if (!CleanPriorFiles()) {
- LOG(ERROR) << "Failed to clean prior files";
- exit(AssemblerExitCodes::kPrioFilesCleanupError);
- }
- // Create assembly directory if it doesn't exist.
- if (!cvd::DirectoryExists(FLAGS_assembly_dir.c_str())) {
- LOG(INFO) << "Setting up " << FLAGS_assembly_dir;
- if (mkdir(FLAGS_assembly_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0) {
- LOG(ERROR) << "Failed to create assembly directory: "
- << FLAGS_assembly_dir << ". Error: " << errno;
- exit(AssemblerExitCodes::kAssemblyDirCreationError);
+ auto boot_img_unpacker =
+ cvd::BootImageUnpacker::FromImages(FLAGS_boot_image,
+ FLAGS_vendor_boot_image);
+ {
+ // The config object is created here, but only exists in memory until the
+ // SaveConfig line below. Don't launch cuttlefish subprocesses between these
+ // two operations, as those will assume they can read the config object from
+ // disk.
+ auto config = InitializeCuttlefishConfiguration(*boot_img_unpacker, fetcher_config);
+ std::set<std::string> preserving;
+ if (FLAGS_resume && ShouldCreateCompositeDisk()) {
+ LOG(WARNING) << "Requested resuming a previous session (the default behavior) "
+ << "but the base images have changed under the overlay, making the "
+ << "overlay incompatible. Wiping the overlay files.";
+ } else if (FLAGS_resume && !ShouldCreateCompositeDisk()) {
+ preserving.insert("overlay.img");
+ preserving.insert("gpt_header.img");
+ preserving.insert("gpt_footer.img");
+ preserving.insert("composite.img");
+ preserving.insert("access-kregistry");
}
- }
- // Create instance directory if it doesn't exist.
- if (!cvd::DirectoryExists(FLAGS_instance_dir.c_str())) {
- LOG(INFO) << "Setting up " << FLAGS_instance_dir;
- if (mkdir(FLAGS_instance_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0) {
- LOG(ERROR) << "Failed to create instance directory: "
- << FLAGS_instance_dir << ". Error: " << errno;
- exit(AssemblerExitCodes::kInstanceDirCreationError);
+ if (!CleanPriorFiles(config, preserving)) {
+ LOG(ERROR) << "Failed to clean prior files";
+ exit(AssemblerExitCodes::kPrioFilesCleanupError);
+ }
+ // Create assembly directory if it doesn't exist.
+ if (!cvd::DirectoryExists(FLAGS_assembly_dir.c_str())) {
+ LOG(INFO) << "Setting up " << FLAGS_assembly_dir;
+ if (mkdir(FLAGS_assembly_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0
+ && errno != EEXIST) {
+ LOG(ERROR) << "Failed to create assembly directory: "
+ << FLAGS_assembly_dir << ". Error: " << errno;
+ exit(AssemblerExitCodes::kAssemblyDirCreationError);
+ }
+ }
+ for (const auto& instance : config.Instances()) {
+ // Create instance directory if it doesn't exist.
+ if (!cvd::DirectoryExists(instance.instance_dir().c_str())) {
+ LOG(INFO) << "Setting up " << FLAGS_instance_dir << ".N";
+ if (mkdir(instance.instance_dir().c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0
+ && errno != EEXIST) {
+ LOG(ERROR) << "Failed to create instance directory: "
+ << FLAGS_instance_dir << ". Error: " << errno;
+ exit(AssemblerExitCodes::kInstanceDirCreationError);
+ }
+ }
+ auto internal_dir = instance.instance_dir() + "/" + vsoc::kInternalDirName;
+ if (!cvd::DirectoryExists(internal_dir)) {
+ if (mkdir(internal_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0
+ && errno != EEXIST) {
+ LOG(ERROR) << "Failed to create internal instance directory: "
+ << internal_dir << ". Error: " << errno;
+ exit(AssemblerExitCodes::kInstanceDirCreationError);
+ }
+ }
+ }
+ if (!SaveConfig(config)) {
+ LOG(ERROR) << "Failed to initialize configuration";
+ exit(AssemblerExitCodes::kCuttlefishConfigurationInitError);
}
}
- auto internal_dir = FLAGS_instance_dir + "/" + vsoc::kInternalDirName;
- if (!cvd::DirectoryExists(internal_dir)) {
- if (mkdir(internal_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) <
- 0) {
- LOG(ERROR) << "Failed to create internal instance directory: "
- << internal_dir << ". Error: " << errno;
- exit(AssemblerExitCodes::kInstanceDirCreationError);
- }
+ std::string first_instance = FLAGS_instance_dir + "." + std::to_string(vsoc::GetInstance());
+ if (symlink(first_instance.c_str(), FLAGS_instance_dir.c_str()) < 0) {
+ LOG(ERROR) << "Could not symlink \"" << first_instance << "\" to \"" << FLAGS_instance_dir << "\"";
+ exit(cvd::kCuttlefishConfigurationInitError);
}
if (!cvd::FileHasContent(FLAGS_boot_image)) {
@@ -675,14 +795,6 @@
exit(cvd::kCuttlefishConfigurationInitError);
}
- auto boot_img_unpacker =
- cvd::BootImageUnpacker::FromImages(FLAGS_boot_image,
- FLAGS_vendor_boot_image);
-
- if (!InitializeCuttlefishConfiguration(*boot_img_unpacker, fetcher_config)) {
- LOG(ERROR) << "Failed to initialize configuration";
- exit(AssemblerExitCodes::kCuttlefishConfigurationInitError);
- }
// Do this early so that the config object is ready for anything that needs it
auto config = vsoc::CuttlefishConfig::Get();
if (!config) {
@@ -751,9 +863,10 @@
CreateBlankImage(FLAGS_metadata_image, FLAGS_blank_metadata_image_mb, "none");
}
- if (!cvd::FileExists(config->ForDefaultInstance().access_kregistry_path())) {
- CreateBlankImage(config->ForDefaultInstance().access_kregistry_path(), 1,
- "none", "64K");
+ for (const auto& instance : config->Instances()) {
+ if (!cvd::FileExists(instance.access_kregistry_path())) {
+ CreateBlankImage(instance.access_kregistry_path(), 1, "none", "64K");
+ }
}
if (SuperImageNeedsRebuilding(fetcher_config, *config)) {
@@ -769,11 +882,26 @@
}
}
- // Check that the files exist
- for (const auto& file : config->virtual_disk_paths()) {
- if (!file.empty() && !cvd::FileHasContent(file.c_str())) {
- LOG(ERROR) << "File not found: " << file;
- exit(cvd::kCuttlefishConfigurationInitError);
+ for (auto instance : config->Instances()) {
+ auto overlay_path = instance.PerInstancePath("overlay.img");
+ if (!cvd::FileExists(overlay_path) || ShouldCreateCompositeDisk() || !FLAGS_resume
+ || cvd::FileModificationTime(overlay_path) < cvd::FileModificationTime(FLAGS_composite_disk)) {
+ if (FLAGS_resume) {
+ LOG(WARNING) << "Requested to continue an existing session, but the overlay was "
+ << "newer than its underlying composite disk. Wiping the overlay.";
+ }
+ CreateQcowOverlay(config->crosvm_binary(), FLAGS_composite_disk, overlay_path);
+ CreateBlankImage(instance.access_kregistry_path(), 1, "none", "64K");
+ }
+ }
+
+ for (auto instance : config->Instances()) {
+ // Check that the files exist
+ for (const auto& file : instance.virtual_disk_paths()) {
+ if (!file.empty() && !cvd::FileHasContent(file.c_str())) {
+ LOG(ERROR) << "File not found: " << file;
+ exit(cvd::kCuttlefishConfigurationInitError);
+ }
}
}
diff --git a/host/commands/assemble_cvd/image_aggregator.cc b/host/commands/assemble_cvd/image_aggregator.cc
index 17fe9db..eac6a2e 100644
--- a/host/commands/assemble_cvd/image_aggregator.cc
+++ b/host/commands/assemble_cvd/image_aggregator.cc
@@ -16,6 +16,11 @@
#include "host/commands/assemble_cvd/image_aggregator.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+
#include <fstream>
#include <string>
#include <vector>
@@ -23,6 +28,7 @@
#include <glog/logging.h>
#include <json/json.h>
#include <google/protobuf/text_format.h>
+#include <sparse/sparse.h>
#include "common/libs/fs/shared_buf.h"
#include "common/libs/fs/shared_fd.h"
@@ -38,15 +44,36 @@
const std::string BPTTOOL_FILE_PATH = "bin/cf_bpttool";
-Json::Value bpttool_input(const std::vector<ImagePartition>& partitions) {
+Json::Value BpttoolInput(const std::vector<ImagePartition>& partitions) {
std::vector<off_t> file_sizes;
off_t total_size = 20 << 20; // 20 MB for padding
for (auto& partition : partitions) {
- off_t partition_file_size = cvd::FileSize(partition.image_file_path);
- if (partition_file_size == 0) {
- LOG(FATAL) << "Expected partition file \"" << partition.image_file_path
- << "\" but it was missing";
+ LOG(INFO) << "Examining " << partition.label;
+ auto file = cvd::SharedFD::Open(partition.image_file_path.c_str(), O_RDONLY);
+ if (!file->IsOpen()) {
+ LOG(FATAL) << "Could not open \"" << partition.image_file_path
+ << "\": " << file->StrError();
+ break;
}
+ int fd = file->UNMANAGED_Dup();
+ auto sparse = sparse_file_import(fd, /* verbose */ false, /* crc */ false);
+ off_t partition_file_size = 0;
+ if (sparse) {
+ partition_file_size = sparse_file_len(sparse, /* sparse */ false,
+ /* crc */ true);
+ sparse_file_destroy(sparse);
+ close(fd);
+ LOG(INFO) << "was sparse";
+ } else {
+ partition_file_size = cvd::FileSize(partition.image_file_path);
+ if (partition_file_size == 0) {
+ LOG(FATAL) << "Could not get file size of \"" << partition.image_file_path
+ << "\"";
+ break;
+ }
+ LOG(INFO) << "was not sparse";
+ }
+ LOG(INFO) << "size was " << partition_file_size;
total_size += partition_file_size;
file_sizes.push_back(partition_file_size);
}
@@ -65,7 +92,7 @@
return bpttool_input_json;
}
-std::string create_file(size_t len) {
+std::string CreateFile(size_t len) {
char file_template[] = "/tmp/diskXXXXXX";
int fd = mkstemp(file_template);
if (fd < 0) {
@@ -97,7 +124,7 @@
for (auto& bpt_partition: bpt_file["partitions"]) {
if (bpt_partition["offset"].asUInt64() != previous_end) {
ComponentDisk* component = disk.add_component_disks();
- component->set_file_path(create_file(bpt_partition["offset"].asUInt64() - previous_end));
+ component->set_file_path(CreateFile(bpt_partition["offset"].asUInt64() - previous_end));
component->set_offset(previous_end);
}
ComponentDisk* component = disk.add_component_disks();
@@ -113,7 +140,7 @@
size_t footer_start = bpt_file["settings"]["disk_size"].asUInt64() - GPT_FOOTER_SIZE;
if (footer_start != previous_end) {
ComponentDisk* component = disk.add_component_disks();
- component->set_file_path(create_file(footer_start - previous_end));
+ component->set_file_path(CreateFile(footer_start - previous_end));
component->set_offset(previous_end);
}
ComponentDisk* footer = disk.add_component_disks();
@@ -123,7 +150,7 @@
return disk;
}
-cvd::SharedFD json_to_fd(const Json::Value& json) {
+cvd::SharedFD JsonToFd(const Json::Value& json) {
Json::FastWriter json_writer;
std::string json_string = json_writer.write(json);
cvd::SharedFD pipe[2];
@@ -137,7 +164,7 @@
return pipe[0];
}
-Json::Value fd_to_json(cvd::SharedFD fd) {
+Json::Value FdToJson(cvd::SharedFD fd) {
std::string contents;
cvd::ReadAll(fd, &contents);
Json::Reader reader;
@@ -148,7 +175,7 @@
return json;
}
-cvd::SharedFD bpttool_make_table(const cvd::SharedFD& input) {
+cvd::SharedFD BpttoolMakeTable(const cvd::SharedFD& input) {
auto bpttool_path = vsoc::DefaultHostArtifactsPath(BPTTOOL_FILE_PATH);
cvd::Command bpttool_cmd(bpttool_path);
bpttool_cmd.AddParameter("make_table");
@@ -165,7 +192,7 @@
return output_pipe[0];
}
-cvd::SharedFD bpttool_make_partition_table(cvd::SharedFD input) {
+cvd::SharedFD BpttoolMakePartitionTable(cvd::SharedFD input) {
auto bpttool_path = vsoc::DefaultHostArtifactsPath(BPTTOOL_FILE_PATH);
cvd::Command bpttool_cmd(bpttool_path);
bpttool_cmd.AddParameter("make_table");
@@ -203,8 +230,8 @@
}
}
-void bpttool_make_disk_image(const std::vector<ImagePartition>& partitions,
- cvd::SharedFD table, const std::string& output) {
+void BptToolMakeDiskImage(const std::vector<ImagePartition>& partitions,
+ cvd::SharedFD table, const std::string& output) {
auto bpttool_path = vsoc::DefaultHostArtifactsPath(BPTTOOL_FILE_PATH);
cvd::Command bpttool_cmd(bpttool_path);
bpttool_cmd.AddParameter("make_disk_image");
@@ -221,28 +248,77 @@
}
}
+void DeAndroidSparse(const std::vector<ImagePartition>& partitions) {
+ for (const auto& partition : partitions) {
+ auto file = cvd::SharedFD::Open(partition.image_file_path.c_str(), O_RDONLY);
+ if (!file->IsOpen()) {
+ LOG(FATAL) << "Could not open \"" << partition.image_file_path
+ << "\": " << file->StrError();
+ break;
+ }
+ int fd = file->UNMANAGED_Dup();
+ auto sparse = sparse_file_import(fd, /* verbose */ false, /* crc */ false);
+ if (!sparse) {
+ close(fd);
+ continue;
+ }
+ LOG(INFO) << "Desparsing " << partition.image_file_path;
+ std::string out_file_name = partition.image_file_path + ".desparse";
+ auto out_file = cvd::SharedFD::Open(out_file_name.c_str(), O_RDWR | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR | S_IRGRP);
+ int write_fd = out_file->UNMANAGED_Dup();
+ int write_status = sparse_file_write(sparse, write_fd, /* gz */ false,
+ /* sparse */ false, /* crc */ false);
+ if (write_status < 0) {
+ LOG(FATAL) << "Failed to desparse \"" << partition.image_file_path << "\": " << write_status;
+ }
+ close(write_fd);
+ if (rename(out_file_name.c_str(), partition.image_file_path.c_str()) < 0) {
+ int error_num = errno;
+ LOG(FATAL) << "Could not move \"" << out_file_name << "\" to \""
+ << partition.image_file_path << "\": " << strerror(error_num);
+ }
+ sparse_file_destroy(sparse);
+ close(fd);
+ }
+}
+
} // namespace
-void aggregate_image(const std::vector<ImagePartition>& partitions,
- const std::string& output_path) {
- auto bpttool_input_json = bpttool_input(partitions);
- auto input_json_fd = json_to_fd(bpttool_input_json);
- auto table_fd = bpttool_make_table(input_json_fd);
- bpttool_make_disk_image(partitions, table_fd, output_path);
+void AggregateImage(const std::vector<ImagePartition>& partitions,
+ const std::string& output_path) {
+ DeAndroidSparse(partitions);
+ auto bpttool_input_json = BpttoolInput(partitions);
+ auto input_json_fd = JsonToFd(bpttool_input_json);
+ auto table_fd = BpttoolMakeTable(input_json_fd);
+ BptToolMakeDiskImage(partitions, table_fd, output_path);
};
-void create_composite_disk(std::vector<ImagePartition> partitions,
- const std::string& header_file,
- const std::string& footer_file,
- const std::string& output_path) {
- auto bpttool_input_json = bpttool_input(partitions);
- auto table_fd = bpttool_make_table(json_to_fd(bpttool_input_json));
- auto table = fd_to_json(table_fd);
- auto partition_table_fd = bpttool_make_partition_table(json_to_fd(bpttool_input_json));
+void CreateCompositeDisk(std::vector<ImagePartition> partitions,
+ const std::string& header_file,
+ const std::string& footer_file,
+ const std::string& output_composite_path) {
+ auto bpttool_input_json = BpttoolInput(partitions);
+ auto table_fd = BpttoolMakeTable(JsonToFd(bpttool_input_json));
+ auto table = FdToJson(table_fd);
+ auto partition_table_fd = BpttoolMakePartitionTable(JsonToFd(bpttool_input_json));
CreateGptFiles(partition_table_fd, header_file, footer_file);
auto composite_proto = MakeCompositeDiskSpec(table, partitions, header_file, footer_file);
- std::ofstream output(output_path.c_str(), std::ios::binary | std::ios::trunc);
- output << "composite_disk\x1d";
- composite_proto.SerializeToOstream(&output);
- output.flush();
+ std::ofstream composite(output_composite_path.c_str(), std::ios::binary | std::ios::trunc);
+ composite << "composite_disk\x1d";
+ composite_proto.SerializeToOstream(&composite);
+ composite.flush();
+}
+
+void CreateQcowOverlay(const std::string& crosvm_path,
+ const std::string& backing_file,
+ const std::string& output_overlay_path) {
+ cvd::Command crosvm_qcow2_cmd(crosvm_path);
+ crosvm_qcow2_cmd.AddParameter("create_qcow2");
+ crosvm_qcow2_cmd.AddParameter("--backing_file=", backing_file);
+ crosvm_qcow2_cmd.AddParameter(output_overlay_path);
+ int success = crosvm_qcow2_cmd.Start().Wait();
+ if (success != 0) {
+ LOG(FATAL) << "Unable to run crosvm create_qcow2. Exited with status " << success;
+ }
}
diff --git a/host/commands/assemble_cvd/image_aggregator.h b/host/commands/assemble_cvd/image_aggregator.h
index a4394e8..d27931b 100644
--- a/host/commands/assemble_cvd/image_aggregator.h
+++ b/host/commands/assemble_cvd/image_aggregator.h
@@ -24,9 +24,12 @@
std::string image_file_path;
};
-void aggregate_image(const std::vector<ImagePartition>& partitions,
- const std::string& output_path);
-void create_composite_disk(std::vector<ImagePartition> partitions,
- const std::string& header_file,
- const std::string& footer_file,
- const std::string& output_path);
+void AggregateImage(const std::vector<ImagePartition>& partitions,
+ const std::string& output_path);
+void CreateCompositeDisk(std::vector<ImagePartition> partitions,
+ const std::string& header_file,
+ const std::string& footer_file,
+ const std::string& output_composite_path);
+void CreateQcowOverlay(const std::string& crosvm_path,
+ const std::string& backing_file,
+ const std::string& output_overlay_path);
diff --git a/host/commands/launch/launch_cvd.cc b/host/commands/launch/launch_cvd.cc
index 9d3b108..6cc7c93 100644
--- a/host/commands/launch/launch_cvd.cc
+++ b/host/commands/launch/launch_cvd.cc
@@ -39,6 +39,7 @@
*/
DEFINE_bool(run_file_discovery, true,
"Whether to run file discovery or get input files from stdin.");
+DEFINE_int32(num_instances, 1, "Number of Android guests to launch");
namespace {
@@ -95,8 +96,8 @@
gflags::HandleCommandLineHelpFlags();
- cvd::SharedFD assembler_stdout, runner_stdin;
- cvd::SharedFD::Pipe(&runner_stdin, &assembler_stdout);
+ cvd::SharedFD assembler_stdout, assembler_stdout_capture;
+ cvd::SharedFD::Pipe(&assembler_stdout_capture, &assembler_stdout);
cvd::SharedFD launcher_report, assembler_stdin;
bool should_generate_report = FLAGS_run_file_discovery;
@@ -109,13 +110,18 @@
auto assemble_proc = StartAssembler(std::move(assembler_stdin),
std::move(assembler_stdout),
forwarder.ArgvForSubprocess(kAssemblerBin));
- auto run_proc = StartRunner(std::move(runner_stdin),
- forwarder.ArgvForSubprocess(kRunnerBin));
if (should_generate_report) {
WriteFiles(AvailableFilesReport(), std::move(launcher_report));
}
+ std::string assembler_output;
+ if (cvd::ReadAll(assembler_stdout_capture, &assembler_output) < 0) {
+ int error_num = errno;
+ LOG(ERROR) << "Read error getting output from assemble_cvd: " << strerror(error_num);
+ return -1;
+ }
+
auto assemble_ret = assemble_proc.Wait();
if (assemble_ret != 0) {
LOG(ERROR) << "assemble_cvd returned " << assemble_ret;
@@ -124,11 +130,32 @@
LOG(INFO) << "assemble_cvd exited successfully.";
}
- auto run_ret = run_proc.Wait();
- if (run_ret != 0) {
- LOG(ERROR) << "run_cvd returned " << run_ret;
- } else {
- LOG(INFO) << "run_cvd exited successfully.";
+ std::vector<cvd::Subprocess> runners;
+ for (int i = 0; i < FLAGS_num_instances; i++) {
+ cvd::SharedFD runner_stdin_in, runner_stdin_out;
+ cvd::SharedFD::Pipe(&runner_stdin_out, &runner_stdin_in);
+ std::string instance_name = std::to_string(i + vsoc::GetInstance());
+ setenv("CUTTLEFISH_INSTANCE", instance_name.c_str(), /* overwrite */ 1);
+
+ auto run_proc = StartRunner(std::move(runner_stdin_out),
+ forwarder.ArgvForSubprocess(kRunnerBin));
+ runners.push_back(std::move(run_proc));
+ if (cvd::WriteAll(runner_stdin_in, assembler_output) < 0) {
+ int error_num = errno;
+ LOG(ERROR) << "Could not write to run_cvd: " << strerror(error_num);
+ return -1;
+ }
}
- return run_ret;
+
+ bool run_cvd_failure = false;
+ for (auto& run_proc : runners) {
+ auto run_ret = run_proc.Wait();
+ if (run_ret != 0) {
+ run_cvd_failure = true;
+ LOG(ERROR) << "run_cvd returned " << run_ret;
+ } else {
+ LOG(INFO) << "run_cvd exited successfully.";
+ }
+ }
+ return run_cvd_failure ? -1 : 0;
}
diff --git a/host/commands/stop_cvd/main.cc b/host/commands/stop_cvd/main.cc
index 7d425a0..65dbeb7 100644
--- a/host/commands/stop_cvd/main.cc
+++ b/host/commands/stop_cvd/main.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <dirent.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
@@ -35,6 +36,7 @@
#include <string>
#include <vector>
+#include <android-base/strings.h>
#include <gflags/gflags.h>
#include <glog/logging.h>
@@ -50,23 +52,57 @@
"command. A value of zero means wait indefinetly");
namespace {
+
+std::set<std::string> FallbackPaths() {
+ std::set<std::string> paths;
+ std::string parent_path = cvd::StringFromEnv("HOME", ".");
+ paths.insert(parent_path + "/cuttlefish_assembly");
+ paths.insert(parent_path + "/cuttlefish_assembly/*");
+
+ std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(parent_path.c_str()), closedir);
+ for (auto entity = readdir(dir.get()); entity != nullptr; entity = readdir(dir.get())) {
+ std::string subdir(entity->d_name);
+ if (!android::base::StartsWith(subdir, "cuttlefish_runtime.")) {
+ continue;
+ }
+ auto instance_dir = parent_path + "/" + subdir;
+ // Add the instance directory
+ paths.insert(instance_dir);
+ // Add files in instance dir
+ paths.insert(instance_dir + "/*");
+ // Add files in the tombstone directory
+ paths.insert(instance_dir + "/tombstones/*");
+ // Add files in the internal directory
+ paths.insert(instance_dir + "/" + std::string(vsoc::kInternalDirName) + "/*");
+ }
+ return paths;
+}
+
+std::set<std::string> PathsForInstance(const vsoc::CuttlefishConfig& config,
+ const vsoc::CuttlefishConfig::InstanceSpecific instance) {
+ return {
+ config.assembly_dir(),
+ config.assembly_dir() + "/*",
+ instance.instance_dir(),
+ instance.PerInstancePath("*"),
+ instance.PerInstancePath("tombstones"),
+ instance.PerInstancePath("tombstones/*"),
+ instance.instance_internal_dir(),
+ instance.PerInstanceInternalPath("*"),
+ };
+}
+
// Gets a set of the possible process groups of a previous launch
-std::set<pid_t> GetCandidateProcessGroups() {
- std::string cmd = "lsof -t 2>/dev/null";
- // Add the instance directory
- auto instance_dir = cvd::StringFromEnv("HOME", ".") + "/cuttlefish_runtime";
- cmd += " " + instance_dir;
- // Add files in instance dir
- cmd += " " + instance_dir + "/*";
- // Add files in the tombstone directory
- cmd += " " + instance_dir + "/tombstones/*";
- // Add files in the internal directory
- cmd += ((" " + instance_dir + "/") + vsoc::kInternalDirName) + "/*";
- // Add the shared memory file
- cmd += " " + vsoc::ForCurrentInstance("/dev/shm/cvd-");
- std::shared_ptr<FILE> cmd_out(popen(cmd.c_str(), "r"), pclose);
+std::set<pid_t> GetCandidateProcessGroups(const std::set<std::string>& paths) {
+ std::stringstream cmd;
+ cmd << "lsof -t 2>/dev/null";
+ for (const auto& path : paths) {
+ cmd << " " << path;
+ }
+ std::string cmd_str = cmd.str();
+ std::shared_ptr<FILE> cmd_out(popen(cmd_str.c_str(), "r"), pclose);
if (!cmd_out) {
- LOG(ERROR) << "Unable to execute '" << cmd << "': " << strerror(errno);
+ LOG(ERROR) << "Unable to execute '" << cmd_str << "': " << strerror(errno);
return {};
}
int64_t pid;
@@ -85,10 +121,10 @@
return ret;
}
-int FallBackStop() {
+int FallBackStop(const std::set<std::string>& paths) {
auto exit_code = 1; // Having to fallback is an error
- auto process_groups = GetCandidateProcessGroups();
+ auto process_groups = GetCandidateProcessGroups(paths);
for (auto pgid: process_groups) {
LOG(INFO) << "Sending SIGKILL to process group " << pgid;
auto retval = killpg(pgid, SIGKILL);
@@ -101,37 +137,26 @@
return exit_code;
}
-} // anonymous namespace
-int main(int argc, char** argv) {
- ::android::base::InitLogging(argv, android::base::StderrLogger);
- google::ParseCommandLineFlags(&argc, &argv, true);
-
- auto config = vsoc::CuttlefishConfig::Get();
- auto instance = config->ForDefaultInstance();
- if (!config) {
- LOG(ERROR) << "Failed to obtain config object";
- return FallBackStop();
- }
-
+bool CleanStopInstance(const vsoc::CuttlefishConfig::InstanceSpecific& instance) {
auto monitor_path = instance.launcher_monitor_socket_path();
if (monitor_path.empty()) {
LOG(ERROR) << "No path to launcher monitor found";
- return FallBackStop();
+ return false;
}
auto monitor_socket = cvd::SharedFD::SocketLocalClient(monitor_path.c_str(),
false, SOCK_STREAM);
if (!monitor_socket->IsOpen()) {
LOG(ERROR) << "Unable to connect to launcher monitor at " << monitor_path
<< ": " << monitor_socket->StrError();
- return FallBackStop();
+ return false;
}
auto request = cvd::LauncherAction::kStop;
auto bytes_sent = monitor_socket->Send(&request, sizeof(request), 0);
if (bytes_sent < 0) {
LOG(ERROR) << "Error sending launcher monitor the stop command: "
<< monitor_socket->StrError();
- return FallBackStop();
+ return false;
}
// Perform a select with a timeout to guard against launcher hanging
cvd::SharedFDSet read_set;
@@ -142,24 +167,53 @@
if (selected < 0){
LOG(ERROR) << "Failed communication with the launcher monitor: "
<< strerror(errno);
- return FallBackStop();
+ return false;
}
if (selected == 0) {
LOG(ERROR) << "Timeout expired waiting for launcher monitor to respond";
- return FallBackStop();
+ return false;
}
cvd::LauncherResponse response;
auto bytes_recv = monitor_socket->Recv(&response, sizeof(response), 0);
if (bytes_recv < 0) {
LOG(ERROR) << "Error receiving response from launcher monitor: "
<< monitor_socket->StrError();
- return FallBackStop();
+ return false;
}
if (response != cvd::LauncherResponse::kSuccess) {
LOG(ERROR) << "Received '" << static_cast<char>(response)
<< "' response from launcher monitor";
- return FallBackStop();
+ return false;
}
- LOG(INFO) << "Successfully stopped device";
+ LOG(INFO) << "Successfully stopped device " << instance.adb_ip_and_port();
+ return true;
+}
+
+int StopInstance(const vsoc::CuttlefishConfig& config,
+ const vsoc::CuttlefishConfig::InstanceSpecific& instance) {
+ bool res = CleanStopInstance(instance);
+ if (!res) {
+ return FallBackStop(PathsForInstance(config, instance));
+ }
return 0;
}
+
+} // anonymous namespace
+
+int main(int argc, char** argv) {
+ ::android::base::InitLogging(argv, android::base::StderrLogger);
+ google::ParseCommandLineFlags(&argc, &argv, true);
+
+ auto config = vsoc::CuttlefishConfig::Get();
+ if (!config) {
+ LOG(ERROR) << "Failed to obtain config object";
+ return FallBackStop(FallbackPaths());
+ }
+
+ int ret = 0;
+ for (const auto& instance : config->Instances()) {
+ ret |= StopInstance(*config, instance);
+ }
+
+ return ret;
+}
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index 50bbe30..c133a62 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -339,21 +339,21 @@
SetPath(kVendorRamdiskImagePath, vendor_ramdisk_image_path);
}
-std::vector<std::string> CuttlefishConfig::virtual_disk_paths() const {
+std::vector<std::string> CuttlefishConfig::InstanceSpecific::virtual_disk_paths() const {
std::vector<std::string> virtual_disks;
- auto virtual_disks_json_obj = (*dictionary_)[kVirtualDiskPaths];
+ auto virtual_disks_json_obj = (*Dictionary())[kVirtualDiskPaths];
for (const auto& disk : virtual_disks_json_obj) {
virtual_disks.push_back(disk.asString());
}
return virtual_disks;
}
-void CuttlefishConfig::set_virtual_disk_paths(
+void CuttlefishConfig::MutableInstanceSpecific::set_virtual_disk_paths(
const std::vector<std::string>& virtual_disk_paths) {
Json::Value virtual_disks_json_obj(Json::arrayValue);
for (const auto& arg : virtual_disk_paths) {
virtual_disks_json_obj.append(arg);
}
- (*dictionary_)[kVirtualDiskPaths] = virtual_disks_json_obj;
+ (*Dictionary())[kVirtualDiskPaths] = virtual_disks_json_obj;
}
std::string CuttlefishConfig::InstanceSpecific::kernel_log_pipe_name() const {
@@ -844,7 +844,10 @@
CuttlefishConfig::CuttlefishConfig() : dictionary_(new Json::Value()) {}
// Can't use '= default' on the header because the compiler complains of
// Json::Value being an incomplete type
-CuttlefishConfig::~CuttlefishConfig() {}
+CuttlefishConfig::~CuttlefishConfig() = default;
+
+CuttlefishConfig::CuttlefishConfig(CuttlefishConfig&&) = default;
+CuttlefishConfig& CuttlefishConfig::operator=(CuttlefishConfig&&) = default;
bool CuttlefishConfig::LoadFromFile(const char* file) {
auto real_file_path = cvd::AbsolutePath(file);
@@ -895,14 +898,27 @@
return ForCurrentInstance("cvd-");
}
-CuttlefishConfig::MutableInstanceSpecific CuttlefishConfig::ForDefaultInstance() {
- return MutableInstanceSpecific(this, std::to_string(GetInstance()));
+CuttlefishConfig::MutableInstanceSpecific CuttlefishConfig::ForInstance(int num) {
+ return MutableInstanceSpecific(this, std::to_string(num));
+}
+
+CuttlefishConfig::InstanceSpecific CuttlefishConfig::ForInstance(int num) const {
+ return InstanceSpecific(this, std::to_string(num));
}
CuttlefishConfig::InstanceSpecific CuttlefishConfig::ForDefaultInstance() const {
return InstanceSpecific(this, std::to_string(GetInstance()));
}
+std::vector<CuttlefishConfig::InstanceSpecific> CuttlefishConfig::Instances() const {
+ const auto& json = (*dictionary_)[kInstances];
+ std::vector<CuttlefishConfig::InstanceSpecific> instances;
+ for (const auto& name : json.getMemberNames()) {
+ instances.push_back(CuttlefishConfig::InstanceSpecific(this, name));
+ }
+ return instances;
+}
+
int GetInstance() {
static int instance_id = InstanceFromEnvironment();
return instance_id;
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 04adc29..f78327d 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -18,6 +18,7 @@
#include <memory>
#include <string>
#include <set>
+#include <vector>
namespace Json {
class Value;
@@ -51,7 +52,9 @@
static const CuttlefishConfig* Get();
CuttlefishConfig();
+ CuttlefishConfig(CuttlefishConfig&&);
~CuttlefishConfig();
+ CuttlefishConfig& operator=(CuttlefishConfig&&);
// Saves the configuration object in a file, it can then be read in other
// processes by passing the --config_file option.
@@ -135,9 +138,6 @@
void set_vendor_ramdisk_image_path(const std::string&
vendor_ramdisk_image_path);
- std::vector<std::string> virtual_disk_paths() const;
- void set_virtual_disk_paths(const std::vector<std::string>& disk_paths);
-
bool deprecated_boot_completed() const;
void set_deprecated_boot_completed(bool deprecated_boot_completed);
@@ -259,21 +259,22 @@
class InstanceSpecific;
class MutableInstanceSpecific;
- MutableInstanceSpecific ForDefaultInstance();
+ MutableInstanceSpecific ForInstance(int instance_num);
+ InstanceSpecific ForInstance(int instance_num) const;
InstanceSpecific ForDefaultInstance() const;
+ std::vector<InstanceSpecific> Instances() const;
+
// A view into an existing CuttlefishConfig object for a particular instance.
class InstanceSpecific {
const CuttlefishConfig* config_;
std::string id_;
+ friend InstanceSpecific CuttlefishConfig::ForInstance(int num) const;
friend InstanceSpecific CuttlefishConfig::ForDefaultInstance() const;
+ friend std::vector<InstanceSpecific> CuttlefishConfig::Instances() const;
InstanceSpecific(const CuttlefishConfig* config, const std::string& id)
: config_(config), id_(id) {}
- InstanceSpecific(const InstanceSpecific&) = delete;
- InstanceSpecific(InstanceSpecific&&) = delete;
- InstanceSpecific& operator=(const InstanceSpecific&) = delete;
- InstanceSpecific& operator=(InstanceSpecific&&) = delete;
Json::Value* Dictionary();
const Json::Value* Dictionary() const;
@@ -290,6 +291,7 @@
int vsock_guest_cid() const;
std::string uuid() const;
std::string instance_name() const;
+ std::vector<std::string> virtual_disk_paths() const;
// Returns the path to a file with the given name in the instance directory..
std::string PerInstancePath(const char* file_name) const;
@@ -322,14 +324,10 @@
class MutableInstanceSpecific {
CuttlefishConfig* config_;
std::string id_;
- friend MutableInstanceSpecific CuttlefishConfig::ForDefaultInstance();
+ friend MutableInstanceSpecific CuttlefishConfig::ForInstance(int num);
MutableInstanceSpecific(CuttlefishConfig* config, const std::string& id)
: config_(config), id_(id) {}
- MutableInstanceSpecific(const MutableInstanceSpecific&) = delete;
- MutableInstanceSpecific(MutableInstanceSpecific&&) = delete;
- MutableInstanceSpecific& operator=(const MutableInstanceSpecific&) = delete;
- MutableInstanceSpecific& operator=(MutableInstanceSpecific&&) = delete;
Json::Value* Dictionary();
public:
@@ -344,14 +342,12 @@
void set_vsock_guest_cid(int vsock_guest_cid);
void set_uuid(const std::string& uuid);
void set_instance_dir(const std::string& instance_dir);
+ void set_virtual_disk_paths(const std::vector<std::string>& disk_paths);
};
private:
std::unique_ptr<Json::Value> dictionary_;
- InstanceSpecific ForInstance(int instance_num);
- const InstanceSpecific ForInstance(int instance_num) const;
-
void SetPath(const std::string& key, const std::string& path);
bool LoadFromFile(const char* file);
static CuttlefishConfig* BuildConfigImpl();
diff --git a/host/libs/vm_manager/cf_qemu.sh b/host/libs/vm_manager/cf_qemu.sh
index 5d88b3f..3eea194 100755
--- a/host/libs/vm_manager/cf_qemu.sh
+++ b/host/libs/vm_manager/cf_qemu.sh
@@ -105,7 +105,7 @@
bootindex=""
fi
args+=(
- -drive "file=${virtual_disk},format=raw,if=none,id=drive-virtio-disk${virtual_disk_index},aio=threads"
+ -drive "file=${virtual_disk},format=qcow2,if=none,id=drive-virtio-disk${virtual_disk_index},aio=threads"
-device "virtio-blk-pci,scsi=off,drive=drive-virtio-disk${virtual_disk_index},id=virtio-disk${virtual_disk_index}${bootindex}"
)
virtual_disk_index=$((virtual_disk_index + 1))
diff --git a/host/libs/vm_manager/crosvm_manager.cpp b/host/libs/vm_manager/crosvm_manager.cpp
index 7cc1f14..63dd3ed 100644
--- a/host/libs/vm_manager/crosvm_manager.cpp
+++ b/host/libs/vm_manager/crosvm_manager.cpp
@@ -135,7 +135,7 @@
crosvm_cmd.AddParameter("--mem=", config_->memory_mb());
crosvm_cmd.AddParameter("--cpus=", config_->cpus());
crosvm_cmd.AddParameter("--params=", kernel_cmdline_);
- for (const auto& disk : config_->virtual_disk_paths()) {
+ for (const auto& disk : instance.virtual_disk_paths()) {
crosvm_cmd.AddParameter("--rwdisk=", disk);
}
crosvm_cmd.AddParameter("--socket=", GetControlSocketPath(config_));
diff --git a/host/libs/vm_manager/qemu_manager.cpp b/host/libs/vm_manager/qemu_manager.cpp
index 643ffc5..af15f0d 100644
--- a/host/libs/vm_manager/qemu_manager.cpp
+++ b/host/libs/vm_manager/qemu_manager.cpp
@@ -140,7 +140,7 @@
LogAndSetEnv("gdb_flag", config_->gdb_flag());
LogAndSetEnv("ramdisk_image_path", config_->final_ramdisk_path());
LogAndSetEnv("kernel_cmdline", kernel_cmdline_);
- LogAndSetEnv("virtual_disk_paths", JoinString(config_->virtual_disk_paths(),
+ LogAndSetEnv("virtual_disk_paths", JoinString(instance.virtual_disk_paths(),
";"));
LogAndSetEnv("wifi_tap_name", instance.wifi_tap_name());
LogAndSetEnv("mobile_tap_name", instance.mobile_tap_name());
diff --git a/shared/BoardConfig.mk b/shared/BoardConfig.mk
index 1bc3718..f2a8bea 100644
--- a/shared/BoardConfig.mk
+++ b/shared/BoardConfig.mk
@@ -52,7 +52,7 @@
BOARD_USES_GENERIC_AUDIO := false
USE_CAMERA_STUB := true
-TARGET_USERIMAGES_SPARSE_EXT_DISABLED := true
+TARGET_USERIMAGES_SPARSE_EXT_DISABLED := false
# Hardware composer configuration
TARGET_USES_HWC2 := true
@@ -62,7 +62,7 @@
# Make the userdata partition 4.25G to accomodate ASAN and CTS
BOARD_USERDATAIMAGE_PARTITION_SIZE := 4563402752
-TARGET_USERIMAGES_SPARSE_F2FS_DISABLED := true
+TARGET_USERIMAGES_SPARSE_F2FS_DISABLED := false
BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE := f2fs
TARGET_USERIMAGES_USE_F2FS := true