blob: 2c18db04cff32aaa167d0cf9a8d0cf691239481a [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 <sysexits.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/bind.h>
#include <base/json/json_writer.h>
#include <base/memory/ptr_util.h>
#include <chromeos/dbus/service_constants.h>
#include <google/protobuf/util/json_util.h>
#include "runtime_probe/daemon.h"
#include "runtime_probe/probe_config.h"
#include "runtime_probe/utils/config_utils.h"
namespace runtime_probe {
const char kErrorMsgFailedToPackProtobuf[] = "Failed to serailize the protobuf";
namespace {
void DumpProtocolBuffer(const google::protobuf::Message& protobuf,
std::string message_name) {
VLOG(3) << "---> Protobuf dump of " << message_name;
VLOG(3) << " DebugString():\n\n" << protobuf.DebugString();
std::string json_string;
google::protobuf::util::JsonPrintOptions options;
MessageToJsonString(protobuf, &json_string, options);
VLOG(3) << " JSON output:\n\n" << json_string << "\n";
VLOG(3) << "<--- Finished Protobuf dump\n";
}
} // namespace
Daemon::Daemon() {}
Daemon::~Daemon() {}
int Daemon::OnInit() {
int exit_code = DBusDaemon::OnInit();
if (exit_code != EX_OK)
return exit_code;
InitDBus();
return 0;
}
void Daemon::InitDBus() {
LOG(INFO) << "Init DBus for Runtime Probe";
// Get or create the ExportedObject for the Runtime Probe service.
auto const runtime_probe_exported_object =
bus_->GetExportedObject(dbus::ObjectPath(kRuntimeProbeServicePath));
CHECK(runtime_probe_exported_object);
// Register a handler of the ProbeCategories method.
CHECK(runtime_probe_exported_object->ExportMethodAndBlock(
kRuntimeProbeInterfaceName, kProbeCategoriesMethod,
base::Bind(&Daemon::ProbeCategories, weak_ptr_factory_.GetWeakPtr())));
// Take ownership of the RuntimeProbe service.
CHECK(bus_->RequestOwnershipAndBlock(kRuntimeProbeServiceName,
dbus::Bus::REQUIRE_PRIMARY));
LOG(INFO) << kRuntimeProbeServicePath << " DBus initialized.";
}
void Daemon::PostQuitTask() {
bus_->GetOriginTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&Daemon::QuitDaemonInternal, base::Unretained(this)));
}
void Daemon::QuitDaemonInternal() {
bus_->ShutdownAndBlock();
Quit();
}
void Daemon::SendProbeResult(
const ProbeResult& reply,
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
DumpProtocolBuffer(reply, "ProbeResult");
std::unique_ptr<dbus::Response> message(
dbus::Response::FromMethodCall(method_call));
dbus::MessageWriter writer(message.get());
if (!writer.AppendProtoAsArrayOfBytes(reply)) {
LOG(ERROR) << kErrorMsgFailedToPackProtobuf;
std::move(response_sender)
.Run(dbus::ErrorResponse::FromMethodCall(
method_call, DBUS_ERROR_INVALID_ARGS,
kErrorMsgFailedToPackProtobuf));
} else {
// TODO(itspeter): b/119939408, PII filter before return.
std::move(response_sender).Run(std::move(message));
}
PostQuitTask();
}
void Daemon::ProbeCategories(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
std::unique_ptr<dbus::Response> message(
dbus::Response::FromMethodCall(method_call));
dbus::MessageReader reader(method_call);
dbus::MessageWriter writer(message.get());
ProbeRequest request;
ProbeResult reply;
if (!reader.PopArrayOfBytesAsProto(&request)) {
reply.set_error(RUNTIME_PROBE_ERROR_PROBE_REQUEST_INVALID);
return SendProbeResult(reply, method_call, std::move(response_sender));
}
DumpProtocolBuffer(request, "ProbeRequest");
std::string probe_config_path;
if (!runtime_probe::GetProbeConfigPath("", &probe_config_path)) {
reply.set_error(RUNTIME_PROBE_ERROR_DEFAULT_PROBE_CONFIG_NOT_FOUND);
return SendProbeResult(reply, method_call, std::move(response_sender));
}
const auto probe_config_data =
runtime_probe::ParseProbeConfig(probe_config_path);
if (!probe_config_data) {
reply.set_error(RUNTIME_PROBE_ERROR_PROBE_CONFIG_SYNTAX_ERROR);
return SendProbeResult(reply, method_call, std::move(response_sender));
}
reply.set_probe_config_checksum(probe_config_data.value().sha1_hash);
VLOG(2) << "SHA1 checksum returned with protocol buffer: "
<< reply.probe_config_checksum();
const auto probe_config =
runtime_probe::ProbeConfig::FromValue(probe_config_data.value().config);
if (!probe_config) {
reply.set_error(RUNTIME_PROBE_ERROR_PROBE_CONFIG_INCOMPLETE_PROBE_FUNCTION);
return SendProbeResult(reply, method_call, std::move(response_sender));
}
base::Value probe_result;
if (request.probe_default_category()) {
probe_result = probe_config->Eval();
} else {
// Convert the ProbeReuslt from enum into array of string.
std::vector<std::string> categories_to_probe;
const google::protobuf::EnumDescriptor* descriptor =
ProbeRequest_SupportCategory_descriptor();
for (int j = 0; j < request.categories_size(); j++)
categories_to_probe.push_back(
descriptor->FindValueByNumber(request.categories(j))->name());
probe_result = probe_config->Eval(categories_to_probe);
}
// TODO(itspeter): Report assigned but not in the probe config's category.
std::string output_js;
base::JSONWriter::Write(probe_result, &output_js);
VLOG(3) << "Raw JSON probe result\n" << output_js;
// Convert JSON to Protocol Buffer.
auto options = google::protobuf::util::JsonParseOptions();
options.ignore_unknown_fields = true;
ProbeResult placeholder;
const auto json_parse_status = google::protobuf::util::JsonStringToMessage(
output_js, &placeholder, options);
reply.MergeFrom(placeholder);
VLOG(3) << "serialize JSON to Protobuf status: " << json_parse_status;
if (!json_parse_status.ok()) {
reply.set_error(RUNTIME_PROBE_ERROR_PROBE_RESULT_INVALID);
}
return SendProbeResult(reply, method_call, std::move(response_sender));
}
} // namespace runtime_probe