blob: 00ed820ca9e07861b1e151985c4f2eab53d7590f [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef VM_TOOLS_CONCIERGE_SHARED_DATA_H_
#define VM_TOOLS_CONCIERGE_SHARED_DATA_H_
#include <memory>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
#include <base/check.h>
#include <base/logging.h>
#include <base/system/sys_info.h>
#include "vm_tools/concierge/service.h"
namespace vm_tools {
namespace concierge {
// Maximum number of extra disks to be mounted inside the VM.
constexpr int kMaxExtraDisks = 10;
// Cryptohome root base path.
constexpr char kCryptohomeRoot[] = "/run/daemon-store";
// crosvm directory name.
constexpr char kCrosvmDir[] = "crosvm";
// Plugin VM directory name.
constexpr char kPluginVmDir[] = "pvm";
// Path to the runtime directory used by VMs.
constexpr char kRuntimeDir[] = "/run/vm";
// Only allow hex digits in the cryptohome id.
constexpr char kValidCryptoHomeCharacters[] = "abcdefABCDEF0123456789";
// Gets the path to the file given the name, user id, location, and extension.
std::optional<base::FilePath> GetFilePathFromName(
const std::string& cryptohome_id,
const std::string& vm_name,
StorageLocation storage_location,
const std::string& extension,
bool create_parent_dir);
bool GetPluginDirectory(const base::FilePath& prefix,
const std::string& extension,
const std::string& vm_id,
bool create,
base::FilePath* path_out);
bool GetPluginIsoDirectory(const std::string& vm_id,
const std::string& cryptohome_id,
bool create,
base::FilePath* path_out);
void SendDbusResponse(dbus::ExportedObject::ResponseSender response_sender,
dbus::MethodCall* method_call,
const vm_tools::concierge::StartVmResponse& response);
template <class StartXXRequest,
int64_t (Service::*GetVmMemory)(const StartXXRequest&),
StartVmResponse (Service::*StartVm)(
StartXXRequest, std::unique_ptr<dbus::MessageReader>, VmMemoryId)>
void Service::StartVmHelper(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
DCHECK(sequence_checker_.CalledOnValidSequence());
auto reader = std::make_unique<dbus::MessageReader>(method_call);
StartXXRequest request;
StartVmResponse response;
// We change to a success status later if necessary.
response.set_status(VM_STATUS_FAILURE);
if (!reader->PopArrayOfBytesAsProto(&request)) {
LOG(ERROR) << "Unable to parse StartVmRequest from message";
response.set_failure_reason("Unable to parse protobuf");
SendDbusResponse(std::move(response_sender), method_call, response);
return;
}
// Check the CPU count.
if (request.cpus() > base::SysInfo::NumberOfProcessors()) {
LOG(ERROR) << "Invalid number of CPUs: " << request.cpus();
response.set_failure_reason("Invalid CPU count");
SendDbusResponse(std::move(response_sender), method_call, response);
return;
}
// Make sure the VM has a name.
if (request.name().empty()) {
LOG(ERROR) << "Ignoring request with empty name";
response.set_failure_reason("Missing VM name");
SendDbusResponse(std::move(response_sender), method_call, response);
return;
}
auto iter = FindVm(request.owner_id(), request.name());
if (iter != vms_.end()) {
LOG(INFO) << "VM with requested name is already running";
VmInterface::Info vm = iter->second->GetInfo();
VmInfo* vm_info = response.mutable_vm_info();
vm_info->set_ipv4_address(vm.ipv4_address);
vm_info->set_pid(vm.pid);
vm_info->set_cid(vm.cid);
vm_info->set_seneschal_server_handle(vm.seneschal_server_handle);
vm_info->set_vm_type(vm.type);
switch (vm.status) {
case VmInterface::Status::STARTING: {
response.set_status(VM_STATUS_STARTING);
break;
}
case VmInterface::Status::RUNNING: {
response.set_status(VM_STATUS_RUNNING);
break;
}
default: {
response.set_status(VM_STATUS_UNKNOWN);
break;
}
}
response.set_success(true);
SendDbusResponse(std::move(response_sender), method_call, response);
return;
}
VmId vm_id(request.owner_id(), request.name());
auto op_iter = std::find_if(
disk_image_ops_.begin(), disk_image_ops_.end(), [&vm_id](auto& info) {
return info.op->vm_id() == vm_id &&
info.op->status() == DISK_STATUS_IN_PROGRESS;
});
if (op_iter != disk_image_ops_.end()) {
LOG(INFO) << "A disk operation for the VM is in progress";
response.set_status(VM_STATUS_DISK_OP_IN_PROGRESS);
response.set_failure_reason("A disk operation for the VM is in progress");
response.set_success(false);
SendDbusResponse(std::move(response_sender), method_call, response);
return;
}
if (!USE_CROSVM_SIBLINGS) {
response = (this->*StartVm)(std::move(request), std::move(reader),
next_vm_memory_id_++);
SendDbusResponse(std::move(response_sender), method_call, response);
return;
}
if (GetVmMemory == nullptr) {
LOG(ERROR) << "Unable to determine required memory";
response.set_failure_reason("Memory size unspecified");
SendDbusResponse(std::move(response_sender), method_call, response);
return;
}
auto resp_ptr = std::make_unique<StartVmResponse>();
// Setting this to true here allows send_resp to determine if a failure
// originated in do_launch or somewhere in mms_.
resp_ptr->set_success(true);
auto do_launch = [](base::WeakPtr<Service> service, StartXXRequest request,
std::unique_ptr<dbus::MessageReader> reader,
StartVmResponse* response,
VmMemoryId vm_memory_id) -> bool {
if (!service) {
LOG(ERROR) << "Service destroyed";
response->set_failure_reason("Service destroyed");
response->set_success(false);
} else {
*response = (service.get()->*StartVm)(std::move(request),
std::move(reader), vm_memory_id);
}
return response->success();
};
auto do_stop = [](base::WeakPtr<Service> service, VmId vm_id) {
LOG(ERROR) << "Stopping VM";
if (service) {
// This should only happen if the VM in question dies during
// startup, in which case the crash handling should clean it
// up. However, stop it anyway just in case. At worst, this
// should just result in some extra error logs.
service->StopVm(vm_id, VM_EXITED);
}
};
auto send_resp = [](dbus::ExportedObject::ResponseSender response_sender,
dbus::MethodCall* method_call,
std::unique_ptr<StartVmResponse> response, bool success) {
if (!success) {
response->clear_vm_info();
response->set_status(VM_STATUS_FAILURE);
if (response->success()) {
response->set_failure_reason("Manatee memory service failure");
response->set_success(false);
}
}
SendDbusResponse(std::move(response_sender), method_call, *response);
return;
};
// |response_sender| owns the unique_ptr which the raw |method_call| raw ptr
// refers to, so it is safe to pass objects which reference the raw ptr to
// other callbacks, since |send_resp_cb| is always invoked last. Also,
// |launch_cb| is only invoked if |stop_cb| has not yet been invoked, so the
// raw resp_ptr is safe.
auto launch_cb =
base::BindOnce(do_launch, weak_ptr_factory_.GetWeakPtr(),
std::move(request), std::move(reader), resp_ptr.get());
auto stop_cb = base::BindOnce(do_stop, weak_ptr_factory_.GetWeakPtr(), vm_id);
auto send_resp_cb = base::BindOnce(send_resp, std::move(response_sender),
method_call, std::move(resp_ptr));
mms_->LaunchVm((this->*GetVmMemory)(request), std::move(launch_cb),
std::move(stop_cb), std::move(send_resp_cb));
}
} // namespace concierge
} // namespace vm_tools
#endif // VM_TOOLS_CONCIERGE_SHARED_DATA_H_