blob: 39e7c968169c7fd673360d5dbda3744d26cbb39a [file] [log] [blame]
// Copyright 2022 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 <numeric>
#include <unordered_set>
#include <vector>
#include <base/logging.h>
#include <base/threading/thread_task_runner_handle.h>
#include <brillo/daemons/daemon.h>
#include <brillo/errors/error.h>
#include <brillo/flag_helper.h>
#include <brillo/syslog_logging.h>
#include <dbus/bus.h>
#include <dbus/minios/dbus-constants.h>
#include <minios/proto_bindings/minios.pb.h>
#include <minios/client/dbus-proxies.h>
using dbus::Bus;
using org::chromium::MiniOsInterfaceProxy;
namespace minios {
namespace client {
// Constant to signal that we need to continue running the daemon after
// initialization.
constexpr int kContinueRunning = -1;
// Constant to signal an error exit code.
constexpr int kExitError = 1;
std::string ErrorPtrStr(const brillo::ErrorPtr& err) {
std::ostringstream err_stream;
err_stream << "Domain=" << err->GetDomain() << " "
<< "Error Code=" << err->GetCode() << " "
<< "Error Message=" << err->GetMessage();
return err_stream.str();
}
class MiniOsClient : public brillo::Daemon {
public:
MiniOsClient(int argc, char** argv)
: argc_(argc),
argv_(argv),
mini_os_proxy_(nullptr),
weak_ptr_factory_(this) {}
~MiniOsClient() override = default;
MiniOsClient(const MiniOsClient&) = delete;
MiniOsClient& operator=(const MiniOsClient&) = delete;
protected:
int OnInit() override {
if (int ret = Daemon::OnInit(); ret != EX_OK)
return ret;
Bus::Options options;
options.bus_type = Bus::SYSTEM;
scoped_refptr<Bus> bus{new Bus{options}};
if (!bus->Connect()) {
LOG(ERROR) << "DBus Service not available.";
return EX_UNAVAILABLE;
}
mini_os_proxy_.reset(new MiniOsInterfaceProxy{bus});
// We can't call QuitWithExitCode from OnInit(), so we delay the execution
// of the ProcessFlags method after the Daemon initialization is done.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&MiniOsClient::ProcessFlagsAndExit,
base::Unretained(this)));
return EX_OK;
}
private:
// Main commands implemented by the client.
int GetState() {
minios::State state;
if (!mini_os_proxy_->GetState(&state, /*error=*/nullptr)) {
LOG(ERROR) << "Failed to get MiniOS State info.";
return EX_UNAVAILABLE;
}
LOG(INFO) << "State is " << State_States_Name(state.state());
return EX_OK;
}
int ListNetworks() {
std::vector<std::string> networks;
if (!mini_os_proxy_->GetNetworks(&networks, /*error=*/nullptr)) {
LOG(ERROR) << "Failed to get networks from MiniOS.";
return EX_UNAVAILABLE;
}
if (networks.empty()) {
LOG(INFO) << "No networks found.";
return EX_OK;
}
LOG(INFO) << "Available networks:";
for (const auto& network : networks) {
printf("\t%s\n", network.c_str());
}
return EX_OK;
}
int NextScreen() {
brillo::ErrorPtr err;
if (!mini_os_proxy_->NextScreen(&err)) {
LOG(ERROR) << "Failed to go to the next screen. Reason: "
<< ErrorPtrStr(err);
return kExitError;
}
return EX_OK;
}
int PrevScreen() {
brillo::ErrorPtr err;
if (!mini_os_proxy_->PrevScreen(&err)) {
LOG(ERROR) << "Failed to go to the previous screen. Reason: "
<< ErrorPtrStr(err);
return kExitError;
}
return EX_OK;
}
int Reset() {
brillo::ErrorPtr err;
if (!mini_os_proxy_->ResetState(&err)) {
LOG(ERROR) << "Failed to reset MiniOs. Reason: " << ErrorPtrStr(err);
return kExitError;
}
return EX_OK;
}
int SetNetworkCredentials(const std::string& network_name,
const std::string& network_password) {
if (!mini_os_proxy_->SetNetworkCredentials(network_name, network_password,
/*error=*/nullptr)) {
LOG(ERROR) << "Failed to set network credentials in MiniOS.";
return EX_UNAVAILABLE;
}
LOG(INFO) << "Seeded MiniOS with credentials for " << network_name;
return EX_OK;
}
int StartRecovery(const std::string& network_name,
const std::string& network_password,
bool wait_till_complete) {
brillo::ErrorPtr err;
if (!mini_os_proxy_->StartRecovery(network_name, network_password, &err)) {
LOG(ERROR) << "Failed to set network credentials in MiniOS."
<< "Reason: " << ErrorPtrStr(err);
return kExitError;
}
if (wait_till_complete) {
RegisterStatusHandlers();
return kContinueRunning;
}
return EX_OK;
}
void OnStateChangedSignal(const State& state) {
switch (state.state()) {
case minios::State::NETWORK_SCANNING:
LOG(INFO) << "Scanning networks -- please wait.";
break;
case minios::State::NETWORK_SELECTION:
LOG(INFO) << "Waiting for network to be selected.";
break;
case minios::State::NETWORK_CREDENTIALS:
LOG(INFO) << "Waiting for network password.";
break;
case minios::State::CONNECTING:
LOG(INFO) << "Connecting -- please wait.";
break;
case minios::State::CONNECTED:
LOG(INFO) << "Connected to a network.";
break;
case minios::State::RECOVERING:
LOG(INFO) << "Attempting recovery -- please wait.";
break;
case minios::State::FINALIZING:
LOG(INFO) << "Finalizing recovery -- please wait.";
break;
case minios::State::COMPLETED:
LOG(INFO) << "Recovery completed -- rebooting.";
exit(EX_OK);
break;
case minios::State::ERROR:
LOG(ERROR) << "Recovery failed.";
exit(kExitError);
break;
default:
LOG(INFO) << "Got state update: State is "
<< State_States_Name(state.state());
break;
}
}
void OnStateChangedSignalConnected(const std::string& interface_name,
const std::string& signal_name,
bool success) {
if (!success) {
LOG(ERROR) << "MiniOs OnStateChangedSignalConnected not successful";
} else {
LOG(INFO) << "MiniOs OnStateChangedSignalConnected successful";
}
}
void RegisterStatusHandlers() {
LOG(INFO) << "Register for status updates";
mini_os_proxy_->RegisterMiniOsStateChangedSignalHandler(
base::BindRepeating(&MiniOsClient::OnStateChangedSignal,
weak_ptr_factory_.GetWeakPtr()),
base::BindRepeating(&MiniOsClient::OnStateChangedSignalConnected,
weak_ptr_factory_.GetWeakPtr()));
}
// Main method that parses and triggers all the actions based on the passed
// flags. Returns the exit code of the program of kContinueRunning if it
// should not exit.
int ProcessFlags() {
DEFINE_bool(get_networks, false, "Show the list of available networks.");
DEFINE_bool(get_state, false, "Show the current state of MiniOs.");
DEFINE_string(network_name, "", "The name of the network to connect to.");
DEFINE_string(network_password, "",
"The password for the network to connect to.");
DEFINE_bool(next_screen, false, "Navigate to the next MiniOs screen.");
DEFINE_bool(prev_screen, false, "Navigate to the previous MiniOs screen.");
DEFINE_bool(reset, false, "Reset MiniOs to its initial state.");
DEFINE_bool(set_credentials, false,
"Seed minios with network credentials.");
DEFINE_bool(start_recovery, false, "Start the MiniOs recovery process.");
DEFINE_bool(watch, false, "Wait and log status updates.");
brillo::FlagHelper::Init(argc_, argv_,
"MiniOS Network Based Recovery Client");
// These options are all mutually exclusive with one another.
std::vector<bool> exclusive_flags{
FLAGS_get_networks, FLAGS_get_state, FLAGS_next_screen,
FLAGS_prev_screen, FLAGS_reset, FLAGS_set_credentials,
FLAGS_start_recovery};
if (std::accumulate(exclusive_flags.begin(), exclusive_flags.end(), 0) >
1) {
LOG(ERROR)
<< "Multiple exclusive options selected. "
<< "Select only one of --get_error, --get_networks, --get_state, "
<< "--next_screen, --prev_screen, --reset, --set_credentials or "
<< "--start_recovery";
return EX_USAGE;
}
if (FLAGS_get_networks) {
return ListNetworks();
}
if (FLAGS_get_state) {
return GetState();
}
if (FLAGS_next_screen) {
return NextScreen();
}
if (FLAGS_prev_screen) {
return PrevScreen();
}
if (FLAGS_reset) {
return Reset();
}
if (FLAGS_set_credentials) {
if (FLAGS_network_name.empty()) {
LOG(ERROR)
<< "--set_credentials requires at least a network name and an "
"optional password.";
return EX_USAGE;
}
return SetNetworkCredentials(FLAGS_network_name, FLAGS_network_password);
}
if (FLAGS_start_recovery) {
if (FLAGS_network_name.empty()) {
LOG(ERROR)
<< "--start_recovery requires at least a network name and an "
"optional password.";
return EX_USAGE;
}
return StartRecovery(FLAGS_network_name, FLAGS_network_password,
FLAGS_watch);
}
if (FLAGS_watch) {
RegisterStatusHandlers();
return kContinueRunning;
}
if (!FLAGS_network_name.empty()) {
LOG(ERROR) << "--network_name should be used with --set_credentials or "
"--start_recovery.";
return EX_USAGE;
}
if (!FLAGS_network_password.empty()) {
LOG(ERROR)
<< "--network_password should be used with --set_credentials and "
"--network_name or --start_recovery and --network_name .";
return EX_USAGE;
}
return EX_OK;
}
void ProcessFlagsAndExit() {
int ret = ProcessFlags();
if (ret != kContinueRunning)
QuitWithExitCode(ret);
}
// Copy of argc and argv passed to main().
int argc_;
char** argv_;
std::unique_ptr<org::chromium::MiniOsInterfaceProxyInterface> mini_os_proxy_;
base::WeakPtrFactory<MiniOsClient> weak_ptr_factory_;
};
} // namespace client
} // namespace minios
int main(int argc, char** argv) {
minios::client::MiniOsClient client(argc, argv);
return client.Run();
}