| // 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 |