blob: 6b982c9a95ce28a6dc163d1eaabbf9a063b43af5 [file] [log] [blame]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PATCHPANEL_VM_CONCIERGE_CLIENT_H_
#define PATCHPANEL_VM_CONCIERGE_CLIENT_H_
#include <cstdint>
#include <map>
#include <memory>
#include <ostream>
#include <queue>
#include <string>
#include <base/memory/scoped_refptr.h>
#include <dbus/bus.h>
#include <dbus/object_proxy.h>
#include <dbus/vm_concierge/dbus-constants.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
namespace patchpanel {
// Concierge DBus interface client for controlling guest VM after it started.
//
// The guest VMs are identified by its cid (virtual socket context id). To
// control a VM using VmConciergeClient, the VM has to first be registered using
// RegisterVm BEFORE the VM starts. VmConciergeClient is then immediately ready
// to take requests, which are queued until VmConciergeClient receives
// VmStartedSignal for this VM. The VM will be removed when VmStoppingSignal is
// received for this VM.
class VmConciergeClient {
public:
explicit VmConciergeClient(scoped_refptr<dbus::Bus> bus);
VmConciergeClient(const VmConciergeClient&) = delete;
VmConciergeClient& operator=(const VmConciergeClient&) = delete;
~VmConciergeClient();
// VmId is the identifier concierge dbus uses for a VM after it started.
struct VmId {
std::string owner_id;
std::string vm_name;
};
// Registers VM by cid. Client then anticipates VmStartedSignal for this cid.
//
// Returns true if the cid is not known to VmConciergeClient yet, false if it
// has already been registered.
bool RegisterVm(int64_t vm_cid);
// Callback after response for TapAttachRequest is received.
//
// |bus_num|: PCI bus number of on guest VM on success, nullopt on failure.
using AttachTapCallback =
base::OnceCallback<void(std::optional<uint32_t> bus_num)>;
// Attaches a tap device, handles response by |callback|.
//
// Returns true if the Request cannot be made, for example if the VM is not
// registered, or already shutdown.
bool AttachTapDevice(int64_t vm_cid,
const std::string& tap_name,
AttachTapCallback callback);
// Callback after response for TapDetachRequest is received.
//
// |success|: true on detach success, false on failure.
using DetachTapCallback = base::OnceCallback<void(bool success)>;
// Detaches a tap device, handles response by |callback|.
//
// Returns true if the Request cannot be made, for example if the VM is not
// registered, or already shutdown.
bool DetachTapDevice(int64_t vm_cid,
uint32_t bus_num,
DetachTapCallback callback);
private:
// DeferredRequest is a request deferred until VmId is available.
using DeferredRequest = base::OnceCallback<void(const VmId& vm_id)>;
// callback for VM start and ready for requests.
void OnVmStarted(dbus::Signal*);
// callback for VM stopping, and no longer accepting requests.
void OnVmStopping(dbus::Signal*);
void DoAttachTapDevice(const std::string& tap_name,
AttachTapCallback callback,
const VmId& vm_id);
void DoDetachTapDevice(uint32_t bus_num,
DetachTapCallback callback,
const VmId& vm_id);
FRIEND_TEST(VmConciergeClientTest, ReadyAfterVmStartedSignal);
scoped_refptr<dbus::Bus> bus_;
scoped_refptr<dbus::ObjectProxy> concierge_proxy_;
std::map<int64_t, std::optional<VmId>> cid_vmid_map_;
std::map<int64_t, std::queue<DeferredRequest>> cid_requestq_map_;
};
std::ostream& operator<<(std::ostream& os,
const VmConciergeClient::VmId& vm_id);
} // namespace patchpanel
#endif // PATCHPANEL_VM_CONCIERGE_CLIENT_H_