Make IVServer capable of starting and managing Cuttlefish
This change simplifies execution process to a single supervisor binary.
Change-Id: If89571437a31bbc86859a0626cb6fce798461b2b
(cherry picked from commit ca3a711b4add43f88e433f39dbcd18360d4b0fe1)
diff --git a/ivserver/BUILD b/ivserver/BUILD
index d370720..491eeb7 100644
--- a/ivserver/BUILD
+++ b/ivserver/BUILD
@@ -5,6 +5,7 @@
],
copts = [
"-I/usr/include/jsoncpp",
+ "-I/usr/include/libxml2",
],
linkopts = [
# TODO(ender): find a better way to manage dependencies across
@@ -13,9 +14,12 @@
# to avoid problems with missing (obsolete) library versions on newer systems.
"-l:libjsoncpp.a",
"-ludev",
+ "-lxml2",
+ "-lvirt",
],
deps = [
":ivserver_lib",
+ "//host/launcher:guest_lib",
"//host/vadb:vadb_lib",
"@gflags_repo//:gflags",
"@glog_repo//:glog",
diff --git a/ivserver/main.cc b/ivserver/main.cc
index 75ce28e..5da98c4 100644
--- a/ivserver/main.cc
+++ b/ivserver/main.cc
@@ -4,29 +4,40 @@
#include <fstream>
#include <memory>
+#include <sstream>
#include <gflags/gflags.h>
#include <glog/logging.h>
+#include <libvirt/libvirt.h>
#include "common/libs/fs/shared_select.h"
#include "host/ivserver/ivserver.h"
#include "host/ivserver/options.h"
+#include "host/launcher/file_partition.h"
+#include "host/launcher/guest_config.h"
#include "host/vadb/usbip/server.h"
#include "host/vadb/virtual_adb_server.h"
+DEFINE_int32(instance, 1, "Instance number. Must be unique.");
+DEFINE_int32(cpus, 4, "Virtual CPU count.");
+DEFINE_int32(memory_mb, 1024, "Total amount of memory available for guest, MB.");
+
DEFINE_string(layout, "", "Location of the vsoc_mem.json file.");
DEFINE_string(mempath, "/dev/shm/ivshmem",
"Target location for the shmem file.");
DEFINE_int32(shmsize, 4, "Size of the shared memory region in megabytes.");
DEFINE_string(qemusocket, "/tmp/ivshmem_socket_qemu", "QEmu socket path");
DEFINE_string(clientsocket, "/tmp/ivshmem_socket_client", "Client socket path");
+DEFINE_string(system_image_dir, "", "Location of the system partition images.");
+DEFINE_string(initrd, "", "Location of cuttlefish initrd file.");
+DEFINE_string(kernel, "", "Location of cuttlefish kernel file.");
-DEFINE_string(usbsocket, "/tmp/vusb_socket_qemu",
- "Socket to use to talk to USBForwarder.");
DEFINE_string(usbipsocket, "android_usbip", "Name of the USB/IP socket.");
namespace {
-Json::Value LoadLayoutFile(const std::string &file) {
+constexpr char kLibVirtQemuTarget[] = "qemu:///system";
+
+Json::Value LoadLayoutFile(const std::string& file) {
char real_file_path[PATH_MAX];
if (realpath(file.c_str(), real_file_path) == nullptr) {
LOG(FATAL) << "Could not get real path for file " << file << ": "
@@ -43,43 +54,151 @@
return result;
}
-void VirtualUSBThread(vadb::VirtualADBServer* adb, vadb::usbip::Server* usbip) {
- for (;;) {
- avd::SharedFDSet fd_read;
- fd_read.Zero();
+// VirtualUSBManager manages virtual USB device presence for Cuttlefish.
+class VirtualUSBManager {
+ public:
+ VirtualUSBManager(const std::string& usbsocket)
+ : adb_{usbsocket, FLAGS_usbipsocket},
+ usbip_{FLAGS_usbipsocket, adb_.Pool()} {}
- adb->BeforeSelect(&fd_read);
- usbip->BeforeSelect(&fd_read);
+ ~VirtualUSBManager() = default;
- int ret = avd::Select(&fd_read, nullptr, nullptr, nullptr);
- if (ret <= 0) continue;
-
- adb->AfterSelect(fd_read);
- usbip->AfterSelect(fd_read);
+ // Initialize Virtual USB and start USB management thread.
+ void Start() {
+ CHECK(adb_.Init()) << "Could not initialize Virtual ADB server";
+ CHECK(usbip_.Init()) << "Could not start USB/IP server";
+ thread_.reset(new std::thread([this]() { Thread(); }));
}
-}
+
+ private:
+ void Thread() {
+ for (;;) {
+ avd::SharedFDSet fd_read;
+ fd_read.Zero();
+
+ adb_.BeforeSelect(&fd_read);
+ usbip_.BeforeSelect(&fd_read);
+
+ int ret = avd::Select(&fd_read, nullptr, nullptr, nullptr);
+ if (ret <= 0) continue;
+
+ adb_.AfterSelect(fd_read);
+ usbip_.AfterSelect(fd_read);
+ }
+ }
+
+ vadb::VirtualADBServer adb_;
+ vadb::usbip::Server usbip_;
+ std::unique_ptr<std::thread> thread_;
+
+ VirtualUSBManager(const VirtualUSBManager&) = delete;
+ VirtualUSBManager& operator=(const VirtualUSBManager&) = delete;
+};
+
+// IVServerManager takes care of serving shared memory segments between
+// Cuttlefish and host-side daemons.
+class IVServerManager {
+ public:
+ IVServerManager(const Json::Value& json_root)
+ : server_(ivserver::IVServerOptions(FLAGS_layout, FLAGS_mempath,
+ FLAGS_qemusocket, FLAGS_clientsocket,
+ FLAGS_shmsize),
+ json_root) {}
+
+ ~IVServerManager() = default;
+
+ // Start IVServer thread.
+ void Start() {
+ thread_.reset(new std::thread([this]() { server_.Serve(); }));
+ }
+
+ private:
+ ivserver::IVServer server_;
+ std::unique_ptr<std::thread> thread_;
+
+ IVServerManager(const IVServerManager&) = delete;
+ IVServerManager& operator=(const IVServerManager&) = delete;
+};
+
} // anonymous namespace
-int main(int argc, char **argv) {
+int main(int argc, char** argv) {
google::InitGoogleLogging(argv[0]);
google::InstallFailureSignalHandler();
google::ParseCommandLineFlags(&argc, &argv, true);
- vadb::VirtualADBServer adb(FLAGS_usbsocket, FLAGS_usbipsocket);
- CHECK(adb.Init());
- vadb::usbip::Server usbip_server(FLAGS_usbipsocket, adb.Pool());
- CHECK(usbip_server.Init()) << "Could not start usb/ip server";
-
- std::thread usbip_server_thread(
- [&adb, &usbip_server]() { VirtualUSBThread(&adb, &usbip_server); });
-
- std::unique_ptr<ivserver::IVServerOptions> ivserver_options(
- new ivserver::IVServerOptions(FLAGS_layout, FLAGS_mempath,
- FLAGS_qemusocket, FLAGS_clientsocket,
- FLAGS_shmsize));
+ CHECK(virInitialize() == 0) << "Could not initialize libvirt.";
Json::Value json_root = LoadLayoutFile(FLAGS_layout);
- ivserver::IVServer ivserver(*ivserver_options, json_root);
- ivserver.Serve();
- LOG(FATAL) << "ivserver failed in Serve().";
+
+ // Each of these calls is free to fail and terminate launch if file does not
+ // exist or could not be created.
+ auto ramdisk_partition = launcher::FilePartition::ReuseExistingFile(
+ FLAGS_system_image_dir + "/ramdisk.img");
+ auto system_partition = launcher::FilePartition::ReuseExistingFile(
+ FLAGS_system_image_dir + "/system.img");
+ auto data_partition =
+ launcher::FilePartition::CreateTemporaryFile("/tmp/cf-data", 512);
+ auto cache_partition =
+ launcher::FilePartition::CreateTemporaryFile("/tmp/cf-cache", 512);
+ auto kernel_image = launcher::FilePartition::ReuseExistingFile(FLAGS_kernel);
+ auto initrd_image = launcher::FilePartition::ReuseExistingFile(FLAGS_initrd);
+
+ std::stringstream cmdline;
+ for (const auto& value : json_root["guest"]["kernel_command_line"]) {
+ cmdline << value.asString() << ' ';
+ }
+
+ unsigned long libvirt_version;
+ CHECK(virGetVersion(&libvirt_version, nullptr, nullptr) == 0)
+ << "Could not query libvirt.";
+
+ std::string entropy_source = "/dev/urandom";
+ // There seems to be no macro turning major/minor/patch to a number, but
+ // headers explain this as major * 1'000'000 + minor * 1'000 + patch.
+ if (libvirt_version <= 1003003) {
+ entropy_source = "/dev/random";
+ LOG(WARNING) << "Your system supplies old version of libvirt, that is "
+ << "not able to use /dev/urandom as entropy source.";
+ LOG(WARNING) << "This may affect performance of your virtual instance.";
+ }
+
+ launcher::GuestConfig cfg;
+ cfg.SetID(FLAGS_instance)
+ .SetVCPUs(FLAGS_cpus)
+ .SetMemoryMB(FLAGS_memory_mb)
+ .SetKernelName(kernel_image->GetName())
+ .SetInitRDName(initrd_image->GetName())
+ .SetKernelArgs(cmdline.str())
+ .SetIVShMemSocketPath(FLAGS_qemusocket)
+ .SetIVShMemVectorCount(json_root["vsoc_device_regions"].size())
+ .SetRamdiskPartitionPath(ramdisk_partition->GetName())
+ .SetSystemPartitionPath(system_partition->GetName())
+ .SetCachePartitionPath(cache_partition->GetName())
+ .SetDataPartitionPath(data_partition->GetName())
+ .SetMobileBridgeName("abr0")
+ .SetEntropySource(entropy_source)
+ .SetEmulator(json_root["guest"]["vmm_path"].asString());
+
+ std::string xml = cfg.Build();
+ VLOG(1) << "Using XML:\n" << xml;
+
+ auto libvirt_connection = virConnectOpen(kLibVirtQemuTarget);
+ CHECK(libvirt_connection)
+ << "Could not connect to libvirt backend: " << kLibVirtQemuTarget;
+
+ VirtualUSBManager vadb(cfg.GetUSBSocketName());
+ vadb.Start();
+ IVServerManager ivshmem(json_root);
+ ivshmem.Start();
+
+ sleep(1);
+
+ auto domain = virDomainCreateXML(libvirt_connection, xml.c_str(),
+ VIR_DOMAIN_START_PAUSED |
+ VIR_DOMAIN_START_AUTODESTROY);
+ CHECK(domain) << "Could not create libvirt domain.";
+
+ CHECK(virDomainResume(domain) == 0) << "Could not start domain.";
+ pause();
}
diff --git a/vadb/usbip/vhci_instrument.cpp b/vadb/usbip/vhci_instrument.cpp
index a3744eb..05ad950 100644
--- a/vadb/usbip/vhci_instrument.cpp
+++ b/vadb/usbip/vhci_instrument.cpp
@@ -48,6 +48,7 @@
using ControlMsgType = uint8_t;
constexpr ControlMsgType kControlAttach = 'A';
constexpr ControlMsgType kControlDetach = 'D';
+constexpr ControlMsgType kControlExit = 'E';
// Port status values deducted from /sys/devices/platform/vhci_hcd/status
enum {
@@ -63,6 +64,8 @@
name_(name) {}
VHCIInstrument::~VHCIInstrument() {
+ control_write_end_->Write(&kControlExit, sizeof(kControlExit));
+ attach_thread_->join();
if (sys_fd_ > 0) close(sys_fd_);
}