blob: 468166b9ca2edd327424304e78484a719b960f2c [file] [log] [blame]
// Copyright 2018 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef VM_TOOLS_CICERONE_VIRTUAL_MACHINE_H_
#define VM_TOOLS_CICERONE_VIRTUAL_MACHINE_H_
#include <stdint.h>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include <vm_applications/apps.pb.h>
#include <vm_cicerone/cicerone_service.pb.h>
#include <vm_protos/proto_bindings/container_guest.grpc.pb.h>
#include <vm_protos/proto_bindings/tremplin.grpc.pb.h>
#include <net-base/ipv4_address.h>
#include "vm_tools/cicerone/container.h"
namespace vm_tools::cicerone {
class OsRelease;
// Represents a single instance of a virtual machine.
class VirtualMachine {
public:
// Convenience for the enumeration over vm types defined in vm_applications.
using VmType = vm_tools::apps::VmType;
enum class CreateLxdContainerStatus {
UNKNOWN,
CREATING,
EXISTS,
FAILED,
};
enum class DeleteLxdContainerStatus {
UNKNOWN,
DELETING,
DOES_NOT_EXIST,
FAILED,
};
enum class StartLxdContainerStatus {
UNKNOWN,
STARTING,
STARTED,
REMAPPING,
RUNNING,
FAILED,
};
enum class StopLxdContainerStatus {
UNKNOWN,
STOPPING,
STOPPED,
DOES_NOT_EXIST,
FAILED,
};
enum class GetLxdContainerUsernameStatus {
UNKNOWN,
SUCCESS,
CONTAINER_NOT_FOUND,
CONTAINER_NOT_RUNNING,
USER_NOT_FOUND,
FAILED,
};
enum class SetUpLxdContainerUserStatus {
UNKNOWN,
SUCCESS,
EXISTS,
FAILED,
};
enum class GetLxdContainerInfoStatus {
UNKNOWN,
RUNNING,
STOPPED,
NOT_FOUND,
FAILED,
};
enum class ExportLxdContainerStatus {
UNKNOWN,
EXPORTING,
FAILED,
};
enum class CancelExportLxdContainerStatus {
UNKNOWN,
CANCEL_QUEUED,
OPERATION_NOT_FOUND,
FAILED,
};
enum class ImportLxdContainerStatus {
UNKNOWN,
IMPORTING,
FAILED,
};
enum class CancelImportLxdContainerStatus {
UNKNOWN,
CANCEL_QUEUED,
OPERATION_NOT_FOUND,
FAILED,
};
enum class UpgradeContainerStatus {
UNKNOWN,
STARTED,
ALREADY_RUNNING,
NOT_SUPPORTED,
ALREADY_UPGRADED,
FAILED,
};
enum class CancelUpgradeContainerStatus {
UNKNOWN,
NOT_RUNNING,
CANCELLED,
FAILED,
};
enum class StartLxdStatus {
UNKNOWN,
STARTING,
ALREADY_RUNNING,
FAILED,
};
enum class AttachUsbToContainerStatus {
UNKNOWN,
OK,
NO_SUCH_CONTAINER,
FAILED,
};
enum class DetachUsbFromContainerStatus {
UNKNOWN,
OK,
FAILED,
};
enum class UpdateContainerDevicesStatus {
UNKNOWN,
OK,
NO_SUCH_CONTAINER,
FAILED,
};
// Info about the LXD container.
struct LxdContainerInfo {
// The IPv4 address of the container.
// This field is only valid if the container status is RUNNING.
std::optional<net_base::IPv4Address> ipv4_address;
};
// Results of a set timezone request
struct SetTimezoneResults {
int successes;
std::vector<std::string> failure_reasons;
};
// |cid| is nonzero for termina VMs, |vm_token| is non-empty for plugin VMs.
VirtualMachine(uint32_t cid, pid_t pid, std::string vm_token);
VirtualMachine(const VirtualMachine&) = delete;
VirtualMachine& operator=(const VirtualMachine&) = delete;
~VirtualMachine();
bool is_stopping() const { return is_stopping_; }
void notify_shutdown() { is_stopping_ = true; }
// The VM's cid.
uint32_t cid() const { return vsock_cid_; }
pid_t pid() const { return pid_; }
// The VM's token.
std::string vm_token() const { return vm_token_; }
// The type of VM this is (termina, pluginvm, ...).
VmType GetType() const;
// Returns true if this VM does not run containers.
bool IsContainerless() const;
// Call during unit tests to force this class to use |mock_tremplin_stub|
// instead of creating a real tremplin stub by connecting to the GPRC
// service. Must be called before ConnectTremplin().
void SetTremplinStubForTesting(
std::unique_ptr<vm_tools::tremplin::Tremplin::StubInterface>
mock_tremplin_stub);
// Connect to the tremplin instance in the VM.
bool ConnectTremplin();
// Tries to set the default timezone for all containers in |container_names|
// to |timezone_name|. If that fails, falls back to setting the TZ environment
// variable to |posix_tz_string|.
//
// If setting the timezone fails entirely due to high-level issues
// (e.g. tremplin not connected, rpc failed), this will return false and set
// |out_error|.
//
// Otherwise, the results from individual containers will be stored in
// |out_results|.
bool SetTimezone(const std::string& timezone_name,
const std::string& posix_tz_string,
const std::vector<std::string>& container_names,
SetTimezoneResults* out_results,
std::string* out_error);
// Registers a container with the VM using the |container_ip| address,
// |vsock_garcon_port|, and |container_token|. Returns true if the token is
// valid, false otherwise.
bool RegisterContainer(const std::string& container_token,
const uint32_t vsock_garcon_port,
const std::string& container_ip);
// Unregister a container with |container_token| within this VM. Returns true
// if the token is valid, false otherwise.
bool UnregisterContainer(const std::string& container_token);
// Generates a random token string that should be passed into the container
// which can then be used by the container to identify itself when it
// communicates back with us.
std::string GenerateContainerToken(const std::string& container_name);
// For testing only. Add a container with the indicated security token. This
// is the only way to get a consistent security token for unit tests & fuzz
// tests.
void CreateContainerWithTokenForTesting(const std::string& container_name,
const std::string& container_token);
// Returns the name of the container associated with the passed in
// |container_token|. Returns the empty string if no such mapping exists. This
// will only return a name that has been confirmed after calling
// RegisterContainer.
std::string GetContainerNameForToken(const std::string& container_token);
// Returns a pointer to the container associated with the passed in
// |container_token|. Returns nullptr if the container does not exist.
// This function will only return a container that has been confirmed after
// calling RegisterContainer.
//
// The pointer returned is owned by VirtualMachine and may not be stored.
Container* GetContainerForToken(const std::string& container_token);
// Returns a pointer to the pending container associated with the passed in
// |container_token|. Returns nullptr if the container does not exist.
// This function will only return a container that has NOT been confirmed by
// calling RegisterContainer.
//
// The pointer returned is owned by VirtualMachine and may not be stored.
Container* GetPendingContainerForToken(const std::string& container_token);
// Returns a pointer to the container associated with the passed in
// |container_name|. Returns nullptr if the container does not exist.
// This function will only return a name that has been confirmed after calling
// RegisterContainer.
//
// The pointer returned is owned by VirtualMachine and may not be stored.
Container* GetContainerForName(const std::string& container_name);
// Returns a pointer to the OsRelease proto associated with the passed in
// |container_name|. Returns nullptr if the container does not exist..
//
// The pointer returned is owned by VirtualMachine and may not be stored.
const OsRelease* GetOsReleaseForContainer(
const std::string& container_name) const;
void SetOsReleaseForTesting(const std::string& container_name,
const OsRelease& os_release);
// Creates an LXD container.
CreateLxdContainerStatus CreateLxdContainer(const std::string& container_name,
const std::string& image_server,
const std::string& image_alias,
const std::string& rootfs_path,
const std::string& metadata_path,
std::string* out_error);
// Deletes an LXD container.
DeleteLxdContainerStatus DeleteLxdContainer(const std::string& container_name,
std::string* out_error);
// Starts an LXD container.
StartLxdContainerStatus StartLxdContainer(
const std::string& container_name,
const std::string& token,
tremplin::StartContainerRequest::PrivilegeLevel privilege_level,
bool disable_audio_capture,
std::string* out_error);
// Stop an LXD container.
StopLxdContainerStatus StopLxdContainer(const std::string& container_name,
std::string* out_error);
// Gets the primary user of an LXD container.
GetLxdContainerUsernameStatus GetLxdContainerUsername(
const std::string& container_name,
std::string* username,
std::string* homedir,
std::string* out_error);
// Sets up an LXD container.
SetUpLxdContainerUserStatus SetUpLxdContainerUser(
const std::string& container_name,
const std::string& container_username,
std::string* out_username,
std::string* out_error);
// Gets info about an LXD container.
GetLxdContainerInfoStatus GetLxdContainerInfo(
const std::string& container_name,
LxdContainerInfo* out_info,
std::string* out_error);
// Exports an LXD container.
ExportLxdContainerStatus ExportLxdContainer(const std::string& container_name,
const std::string& export_path,
std::string* out_error);
CancelExportLxdContainerStatus CancelExportLxdContainer(
const std::string& in_progress_container_name, std::string* out_error);
// Imports an LXD container.
ImportLxdContainerStatus ImportLxdContainer(const std::string& container_name,
const std::string& import_path,
std::string* out_error);
CancelImportLxdContainerStatus CancelImportLxdContainer(
const std::string& in_progress_container_name, std::string* out_error);
// Begins a container OS upgrade (e.g. from debian/stretch to debian/buster).
UpgradeContainerStatus UpgradeContainer(
const Container* container,
const UpgradeContainerRequest::Version& target_version,
std::string* out_error);
// Cancels a running container OS upgrade.
CancelUpgradeContainerStatus CancelUpgradeContainer(Container* container,
std::string* out_error);
// Attaches a USB device on a given port to a container.
AttachUsbToContainerStatus AttachUsbToContainer(const Container* container,
uint32_t port_num,
std::string* out_error);
// Detaches a USB device on a given port from a container.
DetachUsbFromContainerStatus DetachUsbFromContainer(uint32_t port_num,
std::string* out_error);
UpdateContainerDevicesStatus UpdateContainerDevices(
Container* container,
const google::protobuf::Map<std::string, VmDeviceAction>& updates,
google::protobuf::Map<std::string,
UpdateContainerDevicesResponse::UpdateResult>*
results,
std::string* out_error);
// Tells Tremplin to start LXD.
StartLxdStatus StartLxd(bool reset_lxd_db, std::string* out_error);
// Informs the VM that the host network has changed.
void HostNetworkChanged();
// Gets a list of all the active container names in this VM.
std::vector<std::string> GetContainerNames();
// Gets a reference to the mapping of tokens to active containers in this VM.
const std::map<std::string, std::unique_ptr<Container>>& GetContainers();
bool GetTremplinDebugInfo(std::string* out);
private:
// Virtual socket context id to be used when communicating with this VM, only
// valid for termina VMs.
uint32_t vsock_cid_;
// The pid of the main VM process.
pid_t pid_;
// Token for identifying this VM. Required for VMs which will have a zero
// value cid (e.g. PluginVM).
std::string vm_token_;
// The type of vm this is.
VmType vm_type_;
// Mapping of tokens to containers. The tokens are used to securely
// identify a container when it connects back to concierge to identify itself.
std::map<std::string, std::unique_ptr<Container>> containers_;
// Pending map of tokens to containers. The tokens are put in here when
// they are generated and removed once we have a connection from the
// container. We do not immediately put them in the containers map because we
// may get redundant requests to start a container that is already running
// and we don't want to invalidate an in-use token.
std::map<std::string, std::unique_ptr<Container>> pending_containers_;
// Mapping of container name to OsRelease proto as reported by tremplin.
// This data can change during a session if a user upgrades their container.
std::map<std::string, OsRelease> container_os_releases_;
// The stub for the tremplin instance in this VM.
std::unique_ptr<vm_tools::tremplin::Tremplin::StubInterface> tremplin_stub_;
// Set if |tremplin_stub_| is actually a mock object set for testing. In this
// case, we don't try to connect to tremplin even if ConnectTremplin is called
// for some reason.
bool using_mock_tremplin_stub_ = false;
// True if the VM is expected to shutdown soon.
bool is_stopping_ = false;
base::WeakPtrFactory<VirtualMachine> weak_ptr_factory_;
};
} // namespace vm_tools::cicerone
#endif // VM_TOOLS_CICERONE_VIRTUAL_MACHINE_H_