blob: 06fd0698b50d98b9567cc7031c95d3266f9da3b6 [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 <algorithm>
#include <iostream>
#include <base/at_exit.h>
#include <base/command_line.h>
#include <base/json/json_reader.h>
#include <base/logging.h>
#include <brillo/flag_helper.h>
#include <brillo/syslog_logging.h>
#include "runtime_probe/daemon.h"
#include "runtime_probe/probe_config.h"
#include "runtime_probe/probe_config_loader_impl.h"
#include "runtime_probe/probe_function.h"
#include "runtime_probe/system/context_factory_impl.h"
#include "runtime_probe/system/context_helper_impl.h"
#include "runtime_probe/system/context_instance.h"
#include "runtime_probe/system/context_runtime_impl.h"
#include "runtime_probe/system_property_impl.h"
namespace {
enum ExitStatus {
kSuccess = EXIT_SUCCESS, // 0
kUnknownError = 1,
kFailedToParseProbeStatementFromArg = 2,
kArgumentError = 3,
kFailedToLoadProbeConfig = 11,
kFailToParseProbeArgFromConfig = 12,
};
void SetVerbosityLevel(uint32_t verbosity_level) {
verbosity_level = std::min(verbosity_level, 3u);
// VLOG uses negative log level.
logging::SetMinLogLevel(-(static_cast<int32_t>(verbosity_level)));
}
int RunAsHelper() {
const auto* command_line = base::CommandLine::ForCurrentProcess();
const auto args = command_line->GetArgs();
for (size_t i = 0; i < args.size(); ++i) {
DVLOG(1) << "Got arguments, index " << i << " = " << args[i];
}
if (args.size() != 1) {
LOG(ERROR) << "Helper only consumes a single probe statement";
return kFailedToParseProbeStatementFromArg;
}
auto val = base::JSONReader::Read(args[0]);
if (!val || !val->is_dict()) {
LOG(ERROR) << "Failed to parse the probe statement to JSON";
return kFailedToParseProbeStatementFromArg;
}
runtime_probe::ContextInstance::Init<runtime_probe::ContextHelperImpl>();
auto probe_function = runtime_probe::ProbeFunction::FromValue(*val);
if (probe_function == nullptr) {
LOG(ERROR) << "Failed to convert a probe statement to probe function";
return kFailedToParseProbeStatementFromArg;
}
std::string output;
int ret = probe_function->EvalInHelper(&output);
if (ret)
return ret;
std::cout << output << std::flush;
return ExitStatus::kSuccess;
}
int RunAsDaemon() {
if constexpr (USE_FACTORY_RUNTIME_PROBE) {
LOG(FATAL) << "Unexpected error. Daemon mode should never be reachable "
"in factory_runtime_probe.";
return ExitStatus::kUnknownError;
}
LOG(INFO) << "Starting Runtime Probe. Running in daemon mode";
runtime_probe::ContextInstance::Init<runtime_probe::ContextRuntimeImpl>();
runtime_probe::Daemon daemon;
return daemon.Run();
}
// Invoke as a command line tool. Device can load arbitrary probe config
// iff cros_debug == 1
int RunningInCli(const std::string& config_file_path, bool to_stdout) {
LOG(INFO) << "Starting Runtime Probe. Running in CLI mode";
#if USE_FACTORY_RUNTIME_PROBE
runtime_probe::ContextInstance::Init<runtime_probe::ContextFactoryImpl>();
#else
runtime_probe::ContextInstance::Init<runtime_probe::ContextRuntimeImpl>();
#endif
const auto probe_config_loader =
std::make_unique<runtime_probe::ProbeConfigLoaderImpl>();
base::Optional<runtime_probe::ProbeConfigData> probe_config_data;
if (config_file_path == "") {
probe_config_data = probe_config_loader->LoadDefault();
} else {
probe_config_data =
probe_config_loader->LoadFromFile(base::FilePath{config_file_path});
}
if (!probe_config_data) {
LOG(ERROR) << "Failed to load probe config";
return ExitStatus::kFailedToLoadProbeConfig;
}
LOG(INFO) << "Load probe config from: " << probe_config_data->path
<< " (checksum: " << probe_config_data->sha1_hash << ")";
auto probe_config =
runtime_probe::ProbeConfig::FromValue(probe_config_data->config);
if (!probe_config) {
LOG(ERROR) << "Failed to parse from argument from ProbeConfig";
return ExitStatus::kFailToParseProbeArgFromConfig;
}
const auto probe_result = probe_config->Eval();
if (to_stdout) {
LOG(INFO) << "Dumping probe results to stdout";
std::cout << probe_result;
} else {
LOG(INFO) << probe_result;
}
return ExitStatus::kSuccess;
}
} // namespace
int main(int argc, char* argv[]) {
if constexpr (USE_FACTORY_RUNTIME_PROBE) {
int cros_debug;
if (!runtime_probe::SystemPropertyImpl().GetInt("cros_debug",
&cros_debug) ||
cros_debug != 1) {
LOG(FATAL) << "factory_runtime_probe should never run in normal mode.";
return ExitStatus::kUnknownError;
}
}
brillo::InitLog(brillo::kLogToSyslog | brillo::kLogToStderrIfTty);
// Flags are subject to change
DEFINE_string(config_file_path, "",
"File path to probe config, empty to use default one");
#if !USE_FACTORY_RUNTIME_PROBE
DEFINE_bool(dbus, false, "Run in the mode to respond D-Bus call");
#else
constexpr bool FLAGS_dbus = false; // DBus daemon mode is not available in
// factory_runtime_probe.
#endif
DEFINE_bool(helper, false, "Run in the mode to execute probe function");
DEFINE_bool(to_stdout, false, "Output probe result to stdout");
DEFINE_uint32(verbosity_level, 0,
"Set verbosity level. Allowed value: 0 to 3");
brillo::FlagHelper::Init(argc, argv, "ChromeOS runtime probe tool");
SetVerbosityLevel(FLAGS_verbosity_level);
if (FLAGS_helper && FLAGS_dbus) {
LOG(ERROR) << "--helper conflicts with --dbus";
return ExitStatus::kArgumentError;
}
if ((FLAGS_helper || FLAGS_dbus) &&
(FLAGS_to_stdout || FLAGS_config_file_path != "")) {
LOG(WARNING) << "--to_stdout and --config_file_path are not supported in "
"helper mode and dbus mode.";
}
if (FLAGS_helper)
return RunAsHelper();
if (FLAGS_dbus)
return RunAsDaemon();
// Required by dbus in libchrome.
base::AtExitManager at_exit_manager;
return RunningInCli(FLAGS_config_file_path, FLAGS_to_stdout);
}