blob: f20b25228f72782c7b5df69757339b0e62b0493d [file]
// Copyright 2026 The Fuchsia 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 <lib/driver/debug/cpp/debug_server.h>
#include <zircon/status.h>
#include <algorithm>
#include <format>
#include <iterator>
#include <string_view>
namespace fdf_debug {
namespace {
void WriteToSocket(const zx::socket& socket, std::string_view data) {
if (data.empty() || !socket.is_valid()) {
return;
}
const char* ptr = data.data();
size_t remaining = data.size();
while (remaining > 0) {
size_t actual = 0;
zx_status_t status = socket.write(0, ptr, remaining, &actual);
if (status != ZX_OK) {
break;
}
ptr += actual;
remaining -= actual;
}
}
void ReplyWithResult(zx::socket stdout_socket, zx::socket stderr_socket,
const ExecutionResult& result,
DebugServer::ExecuteCompleter::Async completer) {
WriteToSocket(stdout_socket, result.stdout_());
stdout_socket.reset();
WriteToSocket(stderr_socket, result.stderr_());
stderr_socket.reset();
fuchsia_driver_debug::DebugExecuteResponse response;
response.exit_code() = result.exit_code();
completer.Reply(fit::ok(std::move(response)));
}
} // namespace
void DebugServer::RegisterCommand(std::string name, std::string description,
StringCommandHandler handler) {
RegisterCommand(std::move(name), std::move(description), "", std::move(handler));
}
void DebugServer::RegisterCommand(std::string name, std::string description, std::string usage,
StringCommandHandler handler) {
RegisterCommand(
std::move(name), std::move(description), std::move(usage),
AsyncCommandHandler([h = std::move(handler)](cpp20::span<const std::string> args,
fit::callback<void(ExecutionResult)> completer) {
auto res = h(args);
ExecutionResult result;
if (res.is_ok()) {
result.stdout_() = std::move(res.value());
result.stderr_() = "";
result.exit_code() = 0;
} else {
result.stdout_() = "";
result.stderr_() = std::move(res.error_value());
result.exit_code() = 1;
}
completer(std::move(result));
}));
}
void DebugServer::RegisterCommand(std::string name, std::string description,
StatusStringCommandHandler handler) {
RegisterCommand(std::move(name), std::move(description), "", std::move(handler));
}
void DebugServer::RegisterCommand(std::string name, std::string description, std::string usage,
StatusStringCommandHandler handler) {
RegisterCommand(
std::move(name), std::move(description), std::move(usage),
AsyncCommandHandler([h = std::move(handler)](cpp20::span<const std::string> args,
fit::callback<void(ExecutionResult)> completer) {
auto res = h(args);
ExecutionResult result;
if (res.is_ok()) {
result.stdout_() = std::move(res.value());
result.stderr_() = "";
result.exit_code() = 0;
} else {
result.stdout_() = "";
result.stderr_() = std::string(zx_status_get_string(res.error_value())) + "\n";
result.exit_code() = 1;
}
completer(std::move(result));
}));
}
void DebugServer::RegisterCommand(std::string name, std::string description,
SimpleStringCommandHandler handler) {
RegisterCommand(std::move(name), std::move(description), "", std::move(handler));
}
void DebugServer::RegisterCommand(std::string name, std::string description, std::string usage,
SimpleStringCommandHandler handler) {
RegisterCommand(
std::move(name), std::move(description), std::move(usage),
AsyncCommandHandler([h = std::move(handler)](cpp20::span<const std::string> args,
fit::callback<void(ExecutionResult)> completer) {
ExecutionResult result;
result.stdout_() = h(args);
result.stderr_() = "";
result.exit_code() = 0;
completer(std::move(result));
}));
}
void DebugServer::RegisterCommand(std::string name, std::string description,
FullCommandHandler handler) {
RegisterCommand(std::move(name), std::move(description), "", std::move(handler));
}
void DebugServer::RegisterCommand(std::string name, std::string description, std::string usage,
FullCommandHandler handler) {
RegisterCommand(
std::move(name), std::move(description), std::move(usage),
AsyncCommandHandler([h = std::move(handler)](cpp20::span<const std::string> args,
fit::callback<void(ExecutionResult)> completer) {
completer(h(args));
}));
}
void DebugServer::RegisterCommand(std::string name, std::string description,
AsyncCommandHandler handler) {
RegisterCommand(std::move(name), std::move(description), "", std::move(handler));
}
void DebugServer::RegisterCommand(std::string name, std::string description, std::string usage,
AsyncCommandHandler handler) {
commands_.push_back(CommandEntry{
.name = std::move(name),
.description = std::move(description),
.usage = std::move(usage),
.handler = std::move(handler),
});
}
zx::result<> DebugServer::Serve(async_dispatcher_t* dispatcher,
component::OutgoingDirectory& outgoing) {
return outgoing.AddUnmanagedProtocol<fuchsia_driver_debug::Debug>(CreateHandler(dispatcher));
}
zx::result<> DebugServer::Serve(async_dispatcher_t* dispatcher, fdf::OutgoingDirectory& outgoing) {
return outgoing.component().AddUnmanagedProtocol<fuchsia_driver_debug::Debug>(
CreateHandler(dispatcher));
}
zx::result<> DebugServer::Serve(async_dispatcher_t* dispatcher,
const std::shared_ptr<fdf::OutgoingDirectory>& outgoing) {
if (!outgoing) {
return zx::error(ZX_ERR_INVALID_ARGS);
}
return Serve(dispatcher, *outgoing);
}
void DebugServer::Bind(async_dispatcher_t* dispatcher,
fidl::ServerEnd<fuchsia_driver_debug::Debug> server_end) {
bindings_.AddBinding(dispatcher, std::move(server_end), this, fidl::kIgnoreBindingClosure);
}
fidl::ProtocolHandler<fuchsia_driver_debug::Debug> DebugServer::CreateHandler(
async_dispatcher_t* dispatcher) {
return bindings_.CreateHandler(this, dispatcher, fidl::kIgnoreBindingClosure);
}
void DebugServer::Execute(ExecuteRequest& request, ExecuteCompleter::Sync& completer) {
const std::vector<std::string>& args = request.args();
zx::socket stdout_socket = std::move(request.stdout_());
zx::socket stderr_socket = std::move(request.stderr_());
if (args.empty() || args[0] == "--help" || args[0] == "-h" || args[0] == "help") {
ExecutionResult result;
result.stdout_() = GenerateHelp();
result.stderr_() = "";
result.exit_code() = 0;
ReplyWithResult(std::move(stdout_socket), std::move(stderr_socket), result,
completer.ToAsync());
return;
}
const std::string& cmd_name = args[0];
auto it = std::find_if(commands_.begin(), commands_.end(),
[&cmd_name](const CommandEntry& entry) { return entry.name == cmd_name; });
if (it == commands_.end()) {
ExecutionResult result;
result.stdout_() = "";
result.stderr_() = "Unrecognized command: " + cmd_name + "\n";
result.exit_code() = 2;
ReplyWithResult(std::move(stdout_socket), std::move(stderr_socket), result,
completer.ToAsync());
return;
}
if (args.size() > 1 && (args[1] == "--help" || args[1] == "-h")) {
ExecutionResult result;
result.stdout_() = GenerateCommandHelp(*it);
result.stderr_() = "";
result.exit_code() = 0;
ReplyWithResult(std::move(stdout_socket), std::move(stderr_socket), result,
completer.ToAsync());
return;
}
auto async_completer = completer.ToAsync();
cpp20::span<const std::string> sub_args = cpp20::span(args).subspan(1);
it->handler(sub_args, [async_completer = std::move(async_completer),
stdout_socket = std::move(stdout_socket),
stderr_socket = std::move(stderr_socket)](ExecutionResult result) mutable {
ReplyWithResult(std::move(stdout_socket), std::move(stderr_socket), result,
std::move(async_completer));
});
}
void DebugServer::ListCommands(ListCommandsCompleter::Sync& completer) {
std::vector<fuchsia_driver_debug::CommandInfo> command_infos;
command_infos.reserve(commands_.size());
for (const auto& cmd : commands_) {
fuchsia_driver_debug::CommandInfo info;
info.name() = cmd.name;
info.description() = cmd.description;
command_infos.push_back(std::move(info));
}
fuchsia_driver_debug::DebugListCommandsResponse response;
response.commands() = std::move(command_infos);
completer.Reply(fit::ok(std::move(response)));
}
void DebugServer::handle_unknown_method(
fidl::UnknownMethodMetadata<fuchsia_driver_debug::Debug> metadata,
fidl::UnknownMethodCompleter::Sync& completer) {
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
std::string DebugServer::GenerateHelp() const {
std::string help = "Usage: debug <command> [<args>]\n\n";
help += "Driver debug commands.\n\n";
help += "Options:\n";
help += " --help display usage information\n\n";
help += "Commands:\n";
size_t max_name_len = 14;
for (const auto& cmd : commands_) {
max_name_len = std::max(max_name_len, cmd.name.length());
}
for (const auto& cmd : commands_) {
std::format_to(std::back_inserter(help), " {:<{}} {}\n", cmd.name, max_name_len,
cmd.description);
}
return help;
}
std::string DebugServer::GenerateCommandHelp(const CommandEntry& cmd) const {
std::string usage_suffix = cmd.usage.empty() ? "[<args>]" : cmd.usage;
std::string help = "Usage: debug " + cmd.name + " " + usage_suffix + "\n\n";
help += cmd.description + "\n";
return help;
}
} // namespace fdf_debug