blob: 020dcf029cfcd63eea24cb2febf8b35014423e60 [file] [log] [blame]
// Copyright 2018 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 <stdint.h>
#include <stdlib.h>
#include <iostream>
#include <limits>
#include <string>
#include <utility>
#include <base/at_exit.h>
#include <base/logging.h>
#include <base/message_loop/message_loop.h>
#include <brillo/flag_helper.h>
#include <brillo/syslog_logging.h>
#include <chromeos/dbus/service_constants.h>
#include <dbus/bus.h>
#include <dbus/message.h>
#include <dbus/object_path.h>
#include <dbus/object_proxy.h>
#include <seneschal/proto_bindings/seneschal_service.pb.h>
#include <vm_concierge/proto_bindings/service.pb.h>
using std::string;
namespace {
int StartServer(dbus::ObjectProxy* proxy, uint64_t port, uint64_t accept_cid) {
if (port == 0) {
LOG(ERROR) << "--port is required";
return EXIT_FAILURE;
}
if (port > static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())) {
LOG(ERROR) << "--port value is too large; maximum value allowed is "
<< std::numeric_limits<uint32_t>::max();
return EXIT_FAILURE;
}
if (accept_cid < 3) {
LOG(ERROR) << "invalid value for --accept_cid: " << accept_cid;
return EXIT_FAILURE;
}
if (accept_cid >
static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())) {
LOG(ERROR) << "--accept_cid value is too large; maximum value allowed is "
<< std::numeric_limits<uint32_t>::max();
return EXIT_FAILURE;
}
LOG(INFO) << "Starting server";
dbus::MethodCall method_call(vm_tools::seneschal::kSeneschalInterface,
vm_tools::seneschal::kStartServerMethod);
dbus::MessageWriter writer(&method_call);
vm_tools::seneschal::StartServerRequest request;
request.mutable_vsock()->set_port(static_cast<uint32_t>(port));
request.mutable_vsock()->set_accept_cid(static_cast<uint32_t>(accept_cid));
if (!writer.AppendProtoAsArrayOfBytes(request)) {
LOG(ERROR) << "Failed to encode StartServerRequest protobuf";
return EXIT_FAILURE;
}
std::unique_ptr<dbus::Response> dbus_response = proxy->CallMethodAndBlock(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
if (!dbus_response) {
LOG(ERROR) << "Failed to send dbus message to seneschal service";
return EXIT_FAILURE;
}
dbus::MessageReader reader(dbus_response.get());
vm_tools::seneschal::StartServerResponse response;
if (!reader.PopArrayOfBytesAsProto(&response)) {
LOG(ERROR) << "Failed to parse response protobuf";
return EXIT_FAILURE;
}
if (!response.success()) {
LOG(ERROR) << "Failed to start server: " << response.failure_reason();
return EXIT_FAILURE;
}
LOG(INFO) << "Started server with handle: " << response.handle();
return EXIT_SUCCESS;
}
int StopServer(dbus::ObjectProxy* proxy, uint64_t handle) {
if (handle == 0) {
LOG(ERROR) << "--handle is required";
return EXIT_FAILURE;
}
if (handle > static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())) {
LOG(ERROR) << "--handle value is too large; maximum value allowed is "
<< std::numeric_limits<uint32_t>::max();
return EXIT_FAILURE;
}
LOG(INFO) << "Stopping server " << handle;
dbus::MethodCall method_call(vm_tools::seneschal::kSeneschalInterface,
vm_tools::seneschal::kStopServerMethod);
dbus::MessageWriter writer(&method_call);
vm_tools::seneschal::StopServerRequest request;
request.set_handle(static_cast<uint32_t>(handle));
if (!writer.AppendProtoAsArrayOfBytes(request)) {
LOG(ERROR) << "Failed to encode StopServerRequest protobuf";
return EXIT_FAILURE;
}
std::unique_ptr<dbus::Response> dbus_response = proxy->CallMethodAndBlock(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
if (!dbus_response) {
LOG(ERROR) << "Failed to send dbus message to seneschal service";
return EXIT_FAILURE;
}
dbus::MessageReader reader(dbus_response.get());
vm_tools::seneschal::StopServerResponse response;
if (!reader.PopArrayOfBytesAsProto(&response)) {
LOG(ERROR) << "Failed to parse response protobuf";
return EXIT_FAILURE;
}
if (!response.success()) {
LOG(ERROR) << "Failed to stop server: " << response.failure_reason();
return EXIT_FAILURE;
}
LOG(INFO) << "Stopped server " << handle;
return EXIT_SUCCESS;
}
int SharePath(dbus::ObjectProxy* proxy,
uint64_t handle,
string owner_id,
string path,
bool writable) {
if (handle == 0) {
LOG(ERROR) << "--handle is required";
return EXIT_FAILURE;
}
if (handle > static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())) {
LOG(ERROR) << "--handle value is too large; maximum value allowed is "
<< std::numeric_limits<uint32_t>::max();
return EXIT_FAILURE;
}
if (path.empty()) {
LOG(ERROR) << "--path is required";
return EXIT_FAILURE;
}
LOG(INFO) << "Sharing " << path << " with server " << handle;
dbus::MethodCall method_call(vm_tools::seneschal::kSeneschalInterface,
vm_tools::seneschal::kSharePathMethod);
dbus::MessageWriter writer(&method_call);
vm_tools::seneschal::SharePathRequest request;
request.set_handle(static_cast<uint32_t>(handle));
request.set_owner_id(std::move(owner_id));
request.set_storage_location(
vm_tools::seneschal::SharePathRequest::DOWNLOADS);
vm_tools::seneschal::SharedPath* shared_path = request.mutable_shared_path();
shared_path->set_path(std::move(path));
shared_path->set_writable(writable);
if (!writer.AppendProtoAsArrayOfBytes(request)) {
LOG(ERROR) << "Failed to encode SharePathRequest protobuf";
return EXIT_FAILURE;
}
std::unique_ptr<dbus::Response> dbus_response = proxy->CallMethodAndBlock(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
if (!dbus_response) {
LOG(ERROR) << "Failed to send dbus message to seneschal service";
return EXIT_FAILURE;
}
dbus::MessageReader reader(dbus_response.get());
vm_tools::seneschal::SharePathResponse response;
if (!reader.PopArrayOfBytesAsProto(&response)) {
LOG(ERROR) << "Failed to parse response protobuf";
return EXIT_FAILURE;
}
if (!response.success()) {
std::cout << "Unable to share path: " << response.failure_reason()
<< std::endl;
return EXIT_FAILURE;
}
std::cout << request.shared_path().path() << " is available at path "
<< "/mnt/shared" << response.path() << std::endl;
return EXIT_SUCCESS;
}
bool GetServerHandle(scoped_refptr<dbus::Bus> bus,
const string& owner_id,
string vm_name,
uint64_t* handle) {
dbus::ObjectProxy* concierge_proxy = bus->GetObjectProxy(
vm_tools::concierge::kVmConciergeServiceName,
dbus::ObjectPath(vm_tools::concierge::kVmConciergeServicePath));
dbus::MethodCall method_call(vm_tools::concierge::kVmConciergeInterface,
vm_tools::concierge::kGetVmInfoMethod);
dbus::MessageWriter writer(&method_call);
vm_tools::concierge::GetVmInfoRequest request;
request.set_owner_id(owner_id);
request.set_name(vm_name);
if (!writer.AppendProtoAsArrayOfBytes(request)) {
LOG(ERROR) << "Failed to encode GetVmInfo protobuf";
return false;
}
std::unique_ptr<dbus::Response> dbus_response =
concierge_proxy->CallMethodAndBlock(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
if (!dbus_response) {
LOG(ERROR) << "Failed to send dbus message to concierge service";
return false;
}
dbus::MessageReader reader(dbus_response.get());
vm_tools::concierge::GetVmInfoResponse response;
if (!reader.PopArrayOfBytesAsProto(&response)) {
LOG(ERROR) << "Failed to parse response protobuf";
return false;
}
if (!response.success()) {
LOG(ERROR) << "Failed to get VM info for " << vm_name;
return false;
}
*handle = response.vm_info().seneschal_server_handle();
return true;
}
} // namespace
int main(int argc, char** argv) {
base::AtExitManager at_exit;
// Operations.
DEFINE_bool(start, false, "Start a new server");
DEFINE_bool(stop, false, "Stop a running server");
DEFINE_bool(share_path, false, "Share a path with a running server");
// Parameters.
DEFINE_string(vm_name, "", "The name for the VM");
DEFINE_string(owner_id, "", "The cryptohome id of the user");
DEFINE_uint64(handle, 0, "The handle for the server");
DEFINE_uint64(port, 0, "Port number on which the server should listen");
DEFINE_uint64(
accept_cid, 0,
"The vsock context id from which the server should accept connections");
DEFINE_string(path, "", "Path to share with a running server");
DEFINE_bool(writable, false, "Whether the shared path should be writable");
brillo::FlagHelper::Init(argc, argv, "seneschal client tool");
brillo::InitLog(brillo::kLogToStderrIfTty);
base::MessageLoopForIO message_loop;
dbus::Bus::Options opts;
opts.bus_type = dbus::Bus::SYSTEM;
scoped_refptr<dbus::Bus> bus(new dbus::Bus(std::move(opts)));
if (!bus->Connect()) {
LOG(ERROR) << "Failed to connect to system bus";
return EXIT_FAILURE;
}
dbus::ObjectProxy* proxy = bus->GetObjectProxy(
vm_tools::seneschal::kSeneschalServiceName,
dbus::ObjectPath(vm_tools::seneschal::kSeneschalServicePath));
if (!proxy) {
LOG(ERROR) << "Unable to get dbus proxy for "
<< vm_tools::seneschal::kSeneschalServiceName;
return EXIT_FAILURE;
}
if (FLAGS_start + FLAGS_stop + FLAGS_share_path != 1) {
LOG(ERROR) << "Exactly one of --start, --stop, or --share_path is required";
return EXIT_FAILURE;
}
if (FLAGS_start) {
return StartServer(proxy, FLAGS_port, FLAGS_accept_cid);
} else if (FLAGS_stop) {
return StopServer(proxy, FLAGS_handle);
} else if (FLAGS_share_path) {
if (FLAGS_handle == 0 && FLAGS_vm_name.empty()) {
LOG(ERROR) << "--handle or --vm_name is required";
return EXIT_FAILURE;
}
if (FLAGS_owner_id.empty()) {
LOG(ERROR) << "--owner_id is required";
return EXIT_FAILURE;
}
uint64_t handle = FLAGS_handle;
if (handle == 0 && !GetServerHandle(bus, FLAGS_owner_id,
std::move(FLAGS_vm_name), &handle)) {
LOG(ERROR) << "Failed to get server handle";
return EXIT_FAILURE;
}
return SharePath(proxy, handle, std::move(FLAGS_owner_id),
std::move(FLAGS_path), FLAGS_writable);
}
NOTREACHED();
return EXIT_FAILURE;
}