blob: 693265ab0b75e0e834b63b61bc1f4bda2ad84daa [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.
#include <sys/types.h>
#include <unistd.h>
#include <tuple>
#include <utility>
#include <vector>
#include <base/time/time.h>
#include <chromeos/dbus/service_constants.h>
#include <dbus/object_proxy.h>
#include <vm_concierge/proto_bindings/concierge_service.pb.h>
#include "arc/vm/forward-pstore/service.h"
#include "vm_tools/common/naming.h"
#include "vm_tools/common/pstore.h"
namespace arc {
namespace {
constexpr char kArcVmName[] = "arcvm";
constexpr char kArcVmDir[] = "/run/arcvm";
constexpr char kPstoreSourceName[] = "arcvm.pstore";
constexpr char kCryptohomeRoot[] = "/run/daemon-store/crosvm";
constexpr char kPstoreExtension[] = ".pstore";
constexpr base::TimeDelta kReadDelay = base::TimeDelta::FromSeconds(5);
base::FilePath GetPstoreDest(const std::string& owner_id) {
return base::FilePath(kCryptohomeRoot)
.Append(owner_id)
.Append(vm_tools::GetEncodedName(kArcVmName))
.AddExtension(kPstoreExtension);
}
} // namespace
Service::Service(base::Closure quit_closure)
: quit_closure_(quit_closure), weak_ptr_factory_(this) {}
Service::~Service() = default;
void Service::Start() {
// Connect to dbus.
dbus::Bus::Options opts;
opts.bus_type = dbus::Bus::SYSTEM;
bus_ = new dbus::Bus(std::move(opts));
if (!bus_->Connect())
LOG(FATAL) << "Failed to connect to system bus";
// Subscribe to concierge signals
auto concierge_proxy = bus_->GetObjectProxy(
vm_tools::concierge::kVmConciergeServiceName,
dbus::ObjectPath(vm_tools::concierge::kVmConciergeServicePath));
if (!concierge_proxy)
LOG(FATAL) << "Failed to get Concierge proxy";
concierge_proxy->ConnectToSignal(
vm_tools::concierge::kVmConciergeInterface,
vm_tools::concierge::kVmIdChangedSignal,
base::BindRepeating(&Service::OnVmIdChangedSignal,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&Service::OnSignalConnected,
weak_ptr_factory_.GetWeakPtr()));
concierge_proxy->ConnectToSignal(
vm_tools::concierge::kVmConciergeInterface,
vm_tools::concierge::kVmStoppedSignal,
base::BindRepeating(&Service::OnVmStoppedSignal,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&Service::OnSignalConnected,
weak_ptr_factory_.GetWeakPtr()));
}
void Service::OnSignalConnected(const std::string& interface_name,
const std::string& signal_name,
bool is_connected) {
if (!is_connected)
LOG(FATAL) << "Failed to connect to signal: " << signal_name;
VLOG(1) << "Connected to " << signal_name;
}
void Service::OnVmIdChangedSignal(dbus::Signal* signal) {
DCHECK_EQ(signal->GetInterface(), vm_tools::concierge::kVmConciergeInterface);
DCHECK_EQ(signal->GetMember(), vm_tools::concierge::kVmIdChangedSignal);
vm_tools::concierge::VmIdChangedSignal vm_changed_signal;
dbus::MessageReader reader(signal);
if (!reader.PopArrayOfBytesAsProto(&vm_changed_signal)) {
LOG(ERROR) << "Failed to parse proto from DBus Signal";
return;
}
if (vm_changed_signal.name() != kArcVmName) {
VLOG(1) << "Ignoring VmIdChangedSignal from non-ARC VM: "
<< vm_changed_signal.name();
return;
}
VLOG(1) << "Received VmIdChangedSignal for ARCVM";
ForwardPstore(vm_changed_signal.owner_id());
return;
}
void Service::OnVmStoppedSignal(dbus::Signal* signal) {
DCHECK_EQ(signal->GetInterface(), vm_tools::concierge::kVmConciergeInterface);
DCHECK_EQ(signal->GetMember(), vm_tools::concierge::kVmStoppedSignal);
vm_tools::concierge::VmStoppedSignal vm_stopped_signal;
dbus::MessageReader reader(signal);
if (!reader.PopArrayOfBytesAsProto(&vm_stopped_signal)) {
PLOG(ERROR) << "Failed to parse proto from DBus Signal";
return;
}
if (vm_stopped_signal.name() != kArcVmName) {
LOG(INFO) << "Ignoring VmStoppedSignal from non-ARC VM: "
<< vm_stopped_signal.name();
return;
}
// ForwardContents() one last time to get final dmesg output.
ForwardContents(vm_stopped_signal.owner_id());
// Stop timer and close fds.
timer_.Stop();
pstore_fd_.reset();
root_fd_.reset();
dest_fd_.reset();
}
void Service::ForwardPstore(const std::string& owner_id) {
DCHECK(!pstore_fd_.is_valid());
brillo::SafeFD root_fd;
brillo::SafeFD arcvm_dir_fd;
brillo::SafeFD pstore_fd;
brillo::SafeFD::Error err;
std::tie(root_fd, err) = brillo::SafeFD::Root();
if (brillo::SafeFD::IsError(err) || !root_fd.is_valid()) {
LOG(ERROR) << "Failed to open root fd, error:" << static_cast<int>(err);
return;
}
std::tie(arcvm_dir_fd, err) =
root_fd.OpenExistingDir(base::FilePath(kArcVmDir));
if (brillo::SafeFD::IsError(err) || !arcvm_dir_fd.is_valid()) {
LOG(ERROR) << "Failed to open " << kArcVmDir
<< ", error:" << static_cast<int>(err);
return;
}
std::tie(pstore_fd, err) =
arcvm_dir_fd.OpenExistingFile(base::FilePath(kPstoreSourceName));
if (brillo::SafeFD::IsError(err) || !arcvm_dir_fd.is_valid()) {
// On aarch64 platforms, crosvm does not create a pstore file. Log a warning
// and return.
VLOG(1) << "Failed to open " << kArcVmDir << "/" << kPstoreSourceName
<< ", error:" << static_cast<int>(err);
return;
}
// Unlink pstore from /run/arcvm location.
err = arcvm_dir_fd.Unlink(kPstoreSourceName);
if (brillo::SafeFD::IsError(err))
LOG(ERROR) << "Failed to unlink " << kPstoreSourceName
<< ", error:" << static_cast<int>(err);
// Start forwarding the contents to cryptohome location.
root_fd_ = std::move(root_fd);
pstore_fd_ = std::move(pstore_fd);
timer_.Start(FROM_HERE, kReadDelay,
base::BindRepeating(&Service::ForwardContents,
weak_ptr_factory_.GetWeakPtr(), owner_id));
ForwardContents(owner_id);
}
void Service::ForwardContents(const std::string& owner_id) {
if (!pstore_fd_.is_valid()) {
LOG(ERROR) << "Pstore source fd is invalid";
return;
}
// Seek to beginning of file before reading.
if (lseek(pstore_fd_.get(), 0, SEEK_SET) != 0) {
PLOG(ERROR) << "Cannot seek to beginning of pstore file";
return;
}
// Read pstore.
std::vector<char> content;
brillo::SafeFD::Error err;
std::tie(content, err) = pstore_fd_.ReadContents(vm_tools::kArcVmPstoreSize);
if (brillo::SafeFD::IsError(err)) {
LOG(ERROR) << "Failed to read pstore source fd, error:"
<< static_cast<int>(err);
return;
}
// Write to cryptohome path.
if (!dest_fd_.is_valid()) {
base::FilePath dest = GetPstoreDest(owner_id);
std::tie(dest_fd_, err) = root_fd_.MakeFile(dest, 0700, getuid(), getgid(),
O_WRONLY | O_CLOEXEC | O_TRUNC);
if (brillo::SafeFD::IsError(err) || !dest_fd_.is_valid()) {
LOG(ERROR) << "Failed to open destination fd " << dest
<< ", error:" << static_cast<int>(err);
return;
}
}
// Seek to beginning of file before writing.
if (lseek(dest_fd_.get(), 0, SEEK_SET) != 0) {
PLOG(ERROR) << "Cannot seek to beginning of pstore destination";
return;
}
err = dest_fd_.Write(content.data(), content.size());
if (brillo::SafeFD::IsError(err))
LOG(ERROR) << "Failed to write to pstore destination, error:"
<< static_cast<int>(err);
}
} // namespace arc