blob: 961ffde243e380448c50c6654a70eb36d51a005d [file] [log] [blame]
// Copyright 2019 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/async/cpp/executor.h>
#include <lib/async/default.h>
#include <lib/fidl/cpp/binding_set.h>
#include <lib/fidl/cpp/event_sender.h>
#include <lib/inspect/cpp/inspect.h>
#include <lib/inspect/service/cpp/service.h>
#include <set>
namespace inspect {
namespace {
class InspectTreeNameListService final : public fuchsia::inspect::TreeNameIterator {
public:
InspectTreeNameListService(std::vector<std::string> names,
fit::function<void(InspectTreeNameListService*)> done_callback)
: names_(std::move(names)), done_callback_(std::move(done_callback)) {}
void GetNext(GetNextCallback callback) override {
std::vector<std::string> ret;
size_t bytes_used = sizeof(fidl_message_header_t) + sizeof(fidl_vector_t);
for (; name_offset_ < names_.size(); name_offset_++) {
bytes_used += sizeof(fidl_string_t); // string overhead
bytes_used += FIDL_ALIGN(names_[name_offset_].length());
if (bytes_used > ZX_CHANNEL_MAX_MSG_BYTES) {
break;
}
ret.emplace_back(std::move(names_[name_offset_]));
}
callback(std::move(ret));
// Close the connection only if the response was empty. We respond with an empty vector to let
// the client know we will be disconnecting.
if (ret.empty() == 0) {
done_callback_(this);
}
}
private:
size_t name_offset_ = 0;
std::vector<std::string> names_;
fit::function<void(InspectTreeNameListService*)> done_callback_;
};
class InspectTreeService final : public fuchsia::inspect::Tree {
public:
InspectTreeService(Inspector inspector, async_dispatcher_t* dispatcher,
TreeHandlerSettings settings, fit::function<void(InspectTreeService*)> closer)
: inspector_(std::move(inspector)),
executor_(dispatcher),
settings_(std::move(settings)),
closer_(std::move(closer)) {}
// Cannot be moved or copied.
InspectTreeService(const InspectTreeService&) = delete;
InspectTreeService(InspectTreeService&&) = delete;
InspectTreeService& operator=(InspectTreeService&&) = delete;
InspectTreeService& operator=(const InspectTreeService&) = delete;
void GetContent(GetContentCallback callback) override {
fuchsia::inspect::TreeContent ret;
fuchsia::mem::Buffer buffer;
const auto primary_behavior = settings_.snapshot_behavior.PrimaryBehavior();
const auto failure_behavior = settings_.snapshot_behavior.FailureBehavior();
using behavior_types = TreeServerSendPreference::Type;
if (primary_behavior == behavior_types::Frozen) {
auto maybe_vmo = inspector_.FrozenVmoCopy();
if (maybe_vmo.has_value()) {
buffer.vmo = std::move(maybe_vmo.value());
} else if (failure_behavior.has_value() && *failure_behavior == behavior_types::Live) {
buffer.vmo = inspector_.DuplicateVmo();
} else {
buffer.vmo = inspector_.CopyVmo();
}
} else if (primary_behavior == behavior_types::Live) {
buffer.vmo = inspector_.DuplicateVmo();
} else {
buffer.vmo = inspector_.CopyVmo();
}
buffer.vmo.get_size(&buffer.size);
ret.set_buffer(std::move(buffer));
callback(std::move(ret));
}
void ListChildNames(fidl::InterfaceRequest<fuchsia::inspect::TreeNameIterator> request) override {
auto names = inspector_.GetChildNames();
auto service = std::make_unique<InspectTreeNameListService>(
std::move(names),
[this](InspectTreeNameListService* ptr) { name_list_bindings_.RemoveBinding(ptr); });
name_list_bindings_.AddBinding(std::move(service), std::move(request), executor_.dispatcher());
}
void OpenChild(std::string child_name,
fidl::InterfaceRequest<fuchsia::inspect::Tree> request) override {
executor_.schedule_task(
inspector_.OpenChild(std::move(child_name))
.and_then([this, request = std::move(request)](Inspector& inspector) mutable {
auto child = std::unique_ptr<InspectTreeService>(new InspectTreeService(
std::move(inspector), executor_.dispatcher(), settings_,
[this](InspectTreeService* ptr) { child_bindings_.RemoveBinding(ptr); }));
child_bindings_.AddBinding(std::move(child), std::move(request),
executor_.dispatcher());
}));
}
private:
Inspector inspector_;
async::Executor executor_;
TreeHandlerSettings settings_;
fidl::BindingSet<fuchsia::inspect::Tree, std::unique_ptr<InspectTreeService>> child_bindings_;
fidl::BindingSet<fuchsia::inspect::TreeNameIterator, std::unique_ptr<InspectTreeNameListService>>
name_list_bindings_;
// Calling this function unbinds the given service and destroys it immediately.
fit::function<void(InspectTreeService*)> closer_;
};
} // namespace
fidl::InterfaceRequestHandler<fuchsia::inspect::Tree> MakeTreeHandler(
const inspect::Inspector* inspector, async_dispatcher_t* dispatcher,
TreeHandlerSettings settings) {
if (dispatcher == nullptr) {
dispatcher = async_get_default_dispatcher();
ZX_ASSERT(dispatcher);
}
auto bindings = std::make_unique<
fidl::BindingSet<fuchsia::inspect::Tree, std::unique_ptr<InspectTreeService>>>();
return [bindings = std::move(bindings), dispatcher, settings = std::move(settings),
inspector](fidl::InterfaceRequest<fuchsia::inspect::Tree> request) mutable {
bindings->AddBinding(std::make_unique<InspectTreeService>(
*inspector, dispatcher, settings,
[binding_ptr = bindings.get()](InspectTreeService* ptr) {
binding_ptr->RemoveBinding(ptr);
}),
std::move(request), dispatcher);
};
}
} // namespace inspect