blob: e8df235cc2625749546bc92d67e395ac9b9137fe [file] [log] [blame]
// Copyright 2022 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.
// This simple program listens on the fuchsia::debugger::DebugAgent protocol,
// and launch a debug_agent when there's a connect request. The debug_agent
// launched expects a numbered handle at PA_HND(PA_USER0, 0), which should
// point to a zx::channel object, which is the server_end of the
// fuchshia_debugger::DebugAgent protocol. This server_end is bound to the
// DebugAgent's MessageLoop from main.
#include <fidl/fuchsia.component.decl/cpp/fidl.h>
#include <fidl/fuchsia.component/cpp/fidl.h>
#include <fidl/fuchsia.debugger/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/default.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/component/incoming/cpp/service.h>
#include <lib/component/outgoing/cpp/outgoing_directory.h>
#include <lib/fdio/spawn.h>
#include <lib/syslog/cpp/log_settings.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/channel.h>
#include <lib/zx/process.h>
#include <zircon/errors.h>
#include <zircon/processargs.h>
#include <map>
#include "src/developer/debug/debug_agent/launcher.h"
namespace {
zx_status_t LaunchDebugAgent(fidl::ServerEnd<fuchsia_debugger::DebugAgent> server_end) {
const char* path = "/pkg/bin/debug_agent";
const char* argv[] = {path, nullptr};
FX_LOGS(DEBUG) << "Spawning debug_agent...";
// Hand the other end of the channel to DebugAgent.
fdio_spawn_action_t action = {
.action = FDIO_SPAWN_ACTION_ADD_HANDLE,
.h =
{
.id = PA_HND(PA_USER0, 0),
.handle = server_end.TakeHandle().release(),
},
};
char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH] = {};
zx_status_t status = fdio_spawn_etc(zx_job_default(), FDIO_SPAWN_CLONE_ALL, path, argv,
/*environ=*/nullptr, 1, &action, nullptr, err_msg);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to launch debug_agent: " << err_msg;
}
return status;
}
// To support a soft transition to the Launcher protocol, this class implements the |DebugAgent|
// protocol, but is also a client of the actual server in DebugAgentServer. This launcher will keep
// the client end of the channel open as long as the DebugAgent is alive.
// TODO(b/311409740): Complete the soft transition and delete this.
class DebugAgentLegacyLauncher : public fidl::Server<fuchsia_debugger::DebugAgent> {
// Manages a single instance of a DebugAgent protocol client. At construction time, this class
// takes over the client_end of the protocol from the caller, registers itself for client events,
// and calls the |Connect| method. The provided callbacks will be called to handle the response to
// the |Connect| method and another to cleanup when a ZX_CHANNEL_PEER_CLOSED signal is received.
class DebugAgentClient : public fidl::AsyncEventHandler<fuchsia_debugger::DebugAgent> {
public:
DebugAgentClient(fidl::ClientEnd<fuchsia_debugger::DebugAgent> client_end, zx::socket socket,
fit::callback<void(zx_status_t)> on_connect_complete,
fit::callback<void(zx_handle_t)> on_peer_closed)
: client_handle_(client_end.handle()->get()), on_peer_closed_(std::move(on_peer_closed)) {
client_.Bind(std::move(client_end), async_get_default_dispatcher(), this);
client_->Connect(fuchsia_debugger::DebugAgentConnectRequest(std::move(socket)))
.Then([cb = std::move(on_connect_complete)](
fidl::Result<fuchsia_debugger::DebugAgent::Connect>& result) mutable {
if (result.is_ok()) {
return cb(ZX_OK);
} else if (result.error_value().is_framework_error()) {
return cb(result.error_value().framework_error().status());
} else {
return cb(result.error_value().domain_error());
}
});
}
void on_fidl_error(fidl::UnbindInfo info) override {
if (info.is_peer_closed()) {
// When the server closes the channel report ourselves as ready to be destructed.
on_peer_closed_(client_handle_);
}
}
void handle_unknown_event(
fidl::UnknownEventMetadata<fuchsia_debugger::DebugAgent> metadata) override {
FX_LOGS(WARNING) << "Unknown event: " << metadata.event_ordinal;
}
private:
zx_handle_t client_handle_;
fit::callback<void(zx_handle_t)> on_peer_closed_;
fidl::Client<fuchsia_debugger::DebugAgent> client_;
};
void Connect(ConnectRequest& request, ConnectCompleter::Sync& completer) override {
FX_CHECK(request.socket().is_valid());
// Create the client and server ends of the DebugAgent protocol. We will be injecting ourselves
// as the "client" in this case. We must keep the client end open as long as the DebugAgent is
// actively connected to the zx::socket.
auto [client_end, server_end] =
std::move(fidl::CreateEndpoints<fuchsia_debugger::DebugAgent>().value());
zx_status_t status = LaunchDebugAgent(std::move(server_end));
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to launch DebugAgent";
return;
}
// Now we bind ourselves to the client end and send over the zx::socket.
zx_handle_t client_handle = client_end.handle()->get();
auto client = std::make_unique<DebugAgentClient>(
std::move(client_end), std::move(request.socket()),
[completer = completer.ToAsync()](zx_status_t status) mutable {
completer.Reply(zx::make_result(status));
return;
},
// It is safe to capture |this| because |this| owns the client and the
// dispatcher will only dispatch events on this thread.
[this](zx_handle_t handle) { clients_.erase(handle); });
clients_.insert({client_handle, std::move(client)});
}
void GetAttachedProcesses(GetAttachedProcessesRequest& request,
GetAttachedProcessesCompleter::Sync& completer) override {}
void handle_unknown_method(fidl::UnknownMethodMetadata<fuchsia_debugger::DebugAgent> metadata,
fidl::UnknownMethodCompleter::Sync& completer) override {
FX_LOGS(WARNING) << "Unknown method: " << metadata.method_ordinal;
}
private:
// This ensures that every client that is created is kept alive until it
// calls the |on_peer_closed| function that we supply to it. The callback
// takes the handle that was associated with that client and removes it from
// the map. The DebugAgentClient will be responsible for unbinding their side
// of the channel.
std::map<zx_handle_t, std::unique_ptr<DebugAgentClient>> clients_;
};
} // namespace
int main() {
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
fuchsia_logging::SetLogSettings(fuchsia_logging::LogSettings{});
component::OutgoingDirectory outgoing = component::OutgoingDirectory(loop.dispatcher());
zx::result res =
outgoing.AddProtocol<fuchsia_debugger::Launcher>(std::make_unique<DebugAgentLauncher>());
if (res.is_error()) {
FX_LOGS(ERROR) << "Failed to add Launcher protocol: " << res.status_string();
return -1;
}
res = outgoing.AddProtocol<fuchsia_debugger::DebugAgent>(
std::make_unique<DebugAgentLegacyLauncher>());
if (res.is_error()) {
FX_LOGS(ERROR) << "Failed to add DebugAgent protocol: " << res.status_string();
return -1;
}
res = outgoing.ServeFromStartupInfo();
if (res.is_error()) {
FX_LOGS(ERROR) << "Failed to serve outgoing directory: " << res.status_string();
return -1;
}
FX_LOGS(INFO) << "Start listening on FIDL fuchsia::debugger::Launcher.";
return loop.Run();
}