| // Copyright 2020 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 "src/devices/bin/driver_manager/driver_runner.h" |
| |
| #include <lib/async/cpp/task.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/fidl/llcpp/server.h> |
| #include <lib/service/llcpp/service.h> |
| #include <zircon/status.h> |
| |
| #include <deque> |
| #include <stack> |
| #include <unordered_set> |
| |
| #include "src/devices/lib/driver2/start_args.h" |
| #include "src/devices/lib/log/log.h" |
| #include "src/lib/fxl/strings/join_strings.h" |
| #include "src/lib/storage/vfs/cpp/service.h" |
| |
| namespace fdata = fuchsia_data; |
| namespace fdf = fuchsia_driver_framework; |
| namespace fio = fuchsia_io; |
| namespace frunner = fuchsia_component_runner; |
| namespace fsys = fuchsia_sys2; |
| |
| using InspectStack = std::stack<std::pair<inspect::Node*, Node*>>; |
| |
| namespace { |
| |
| void InspectNode(inspect::Inspector* inspector, InspectStack* stack) { |
| std::vector<inspect::Node> roots; |
| while (!stack->empty()) { |
| // Pop the current root and node to operate on. |
| auto [root, node] = stack->top(); |
| stack->pop(); |
| |
| // Populate root with data from node. |
| if (auto offers = node->offers(); !offers.empty()) { |
| std::vector<std::string_view> strings; |
| for (auto& offer : offers) { |
| strings.push_back(offer.get()); |
| } |
| root->CreateString("offers", fxl::JoinStrings(strings, ", "), inspector); |
| } |
| if (auto symbols = node->symbols(); !symbols.empty()) { |
| std::vector<std::string_view> strings; |
| for (auto& symbol : symbols) { |
| strings.push_back(symbol.name().get()); |
| } |
| root->CreateString("symbols", fxl::JoinStrings(strings, ", "), inspector); |
| } |
| |
| // Push children of this node onto the stack. |
| for (auto& child : node->children()) { |
| auto& root_for_child = roots.emplace_back(root->CreateChild(child.name())); |
| stack->emplace(&root_for_child, &child); |
| } |
| } |
| |
| // Store all of the roots in the inspector. |
| for (auto& root : roots) { |
| inspector->emplace(std::move(root)); |
| } |
| } |
| |
| template <typename T> |
| void UnbindAndReset(std::optional<fidl::ServerBindingRef<T>>& ref) { |
| if (ref.has_value()) { |
| ref->Unbind(); |
| ref.reset(); |
| } |
| } |
| |
| std::string DriverCollection(std::string_view url) { |
| constexpr auto scheme = "fuchsia-boot://"; |
| return url.compare(0, strlen(scheme), scheme) == 0 ? "boot-drivers" : "pkg-drivers"; |
| } |
| |
| } // namespace |
| |
| class EventHandler : public fidl::WireAsyncEventHandler<fdf::DriverHost> { |
| public: |
| EventHandler(DriverHostComponent* component, |
| fbl::DoublyLinkedList<std::unique_ptr<DriverHostComponent>>* driver_hosts) |
| : component_(component), driver_hosts_(driver_hosts) {} |
| |
| void Unbound(fidl::UnbindInfo info) override { driver_hosts_->erase(*component_); } |
| |
| private: |
| DriverHostComponent* const component_; |
| fbl::DoublyLinkedList<std::unique_ptr<DriverHostComponent>>* driver_hosts_; |
| }; |
| |
| DriverComponent::DriverComponent(fidl::ClientEnd<fio::Directory> exposed_dir, |
| fidl::ClientEnd<fdf::Driver> driver) |
| : exposed_dir_(std::move(exposed_dir)), |
| driver_(std::move(driver)), |
| wait_(this, driver_.channel().get(), ZX_CHANNEL_PEER_CLOSED) {} |
| |
| DriverComponent::~DriverComponent() { UnbindAndReset(node_ref_); } |
| |
| void DriverComponent::set_driver_ref( |
| fidl::ServerBindingRef<fuchsia_component_runner::ComponentController> driver_ref) { |
| driver_ref_.emplace(std::move(driver_ref)); |
| } |
| |
| void DriverComponent::set_node_ref(fidl::ServerBindingRef<fdf::Node> node_ref) { |
| node_ref_.emplace(std::move(node_ref)); |
| } |
| |
| zx::status<> DriverComponent::WatchDriver(async_dispatcher_t* dispatcher) { |
| auto status = wait_.Begin(dispatcher); |
| return zx::make_status(status); |
| } |
| |
| void DriverComponent::Stop(DriverComponent::StopCompleter::Sync& completer) { |
| UnbindAndReset(node_ref_); |
| } |
| |
| void DriverComponent::Kill(DriverComponent::KillCompleter::Sync& completer) {} |
| |
| void DriverComponent::OnPeerClosed(async_dispatcher_t* dispatcher, async::WaitBase* wait, |
| zx_status_t status, const zx_packet_signal_t* signal) { |
| if (status != ZX_OK) { |
| LOGF(WARNING, "Failed to watch channel for driver: %s", zx_status_get_string(status)); |
| } else if (signal->observed & ZX_CHANNEL_PEER_CLOSED) { |
| UnbindAndReset(driver_ref_); |
| } |
| } |
| |
| DriverHostComponent::DriverHostComponent( |
| fidl::ClientEnd<fdf::DriverHost> driver_host, async_dispatcher_t* dispatcher, |
| fbl::DoublyLinkedList<std::unique_ptr<DriverHostComponent>>* driver_hosts) |
| : driver_host_(std::move(driver_host), dispatcher, |
| std::make_shared<EventHandler>(this, driver_hosts)) {} |
| |
| zx::status<fidl::ClientEnd<fdf::Driver>> DriverHostComponent::Start( |
| fidl::ClientEnd<fdf::Node> node, fidl::VectorView<fidl::StringView> offers, |
| fidl::VectorView<fdf::wire::NodeSymbol> symbols, fidl::StringView url, |
| fdata::wire::Dictionary program, fidl::VectorView<frunner::wire::ComponentNamespaceEntry> ns, |
| fidl::ServerEnd<fio::Directory> outgoing_dir, fidl::ClientEnd<fio::Directory> exposed_dir) { |
| auto endpoints = fidl::CreateEndpoints<fdf::Driver>(); |
| if (endpoints.is_error()) { |
| return endpoints.take_error(); |
| } |
| fidl::FidlAllocator allocator; |
| fdf::wire::DriverStartArgs args(allocator); |
| args.set_node(allocator, std::move(node)) |
| .set_offers(allocator, std::move(offers)) |
| .set_symbols(allocator, std::move(symbols)) |
| .set_url(allocator, std::move(url)) |
| .set_program(allocator, std::move(program)) |
| .set_ns(allocator, std::move(ns)) |
| .set_outgoing_dir(allocator, std::move(outgoing_dir)) |
| .set_exposed_dir(allocator, std::move(exposed_dir)); |
| auto start = driver_host_->Start(std::move(args), std::move(endpoints->server)); |
| if (!start.ok()) { |
| auto binary = start_args::ProgramValue(program, "binary").value_or(""); |
| LOGF(ERROR, "Failed to start driver '%s' in driver host: %s", binary.data(), start.error()); |
| return zx::error(start.status()); |
| } |
| return zx::ok(std::move(endpoints->client)); |
| } |
| |
| Node::Node(Node* parent, DriverBinder* driver_binder, async_dispatcher_t* dispatcher, |
| std::string_view name) |
| : parent_(parent), driver_binder_(driver_binder), dispatcher_(dispatcher), name_(name) {} |
| |
| Node::~Node() { UnbindAndReset(controller_ref_); } |
| |
| const std::string& Node::name() const { return name_; } |
| |
| fidl::VectorView<fidl::StringView> Node::offers() { |
| return fidl::VectorView<fidl::StringView>::FromExternal(offers_); |
| } |
| |
| fidl::VectorView<fdf::wire::NodeSymbol> Node::symbols() { |
| return fidl::VectorView<fdf::wire::NodeSymbol>::FromExternal(symbols_); |
| } |
| |
| DriverHostComponent* Node::parent_driver_host() const { return parent_->driver_host_; } |
| |
| void Node::set_driver_host(DriverHostComponent* driver_host) { driver_host_ = driver_host; } |
| |
| void Node::set_driver_ref(fidl::ServerBindingRef<frunner::ComponentController> driver_ref) { |
| driver_ref_.emplace(std::move(driver_ref)); |
| } |
| |
| void Node::set_controller_ref(fidl::ServerBindingRef<fdf::NodeController> controller_ref) { |
| controller_ref_.emplace(std::move(controller_ref)); |
| } |
| |
| void Node::set_node_ref(fidl::ServerBindingRef<fdf::Node> node_ref) { |
| node_ref_.emplace(std::move(node_ref)); |
| } |
| |
| fbl::DoublyLinkedList<std::unique_ptr<Node>>& Node::children() { return children_; } |
| |
| std::string Node::TopoName() const { |
| std::deque<std::string_view> names; |
| for (auto node = this; node != nullptr; node = node->parent_) { |
| names.push_front(node->name()); |
| } |
| return fxl::JoinStrings(names, "."); |
| } |
| |
| void Node::Unbind() { |
| UnbindAndReset(driver_ref_); |
| UnbindAndReset(controller_ref_); |
| UnbindAndReset(node_ref_); |
| } |
| |
| void Node::Remove() { |
| // Create list of nodes to unbind. |
| std::stack<Node*> stack{{this}}; |
| std::vector<Node*> nodes; |
| std::unordered_set<Node*> unique_nodes; |
| while (!stack.empty()) { |
| auto node = stack.top(); |
| stack.pop(); |
| auto [_, inserted] = unique_nodes.emplace(node); |
| if (!inserted) { |
| // Only insert unique nodes from the DAG. |
| continue; |
| } |
| nodes.emplace_back(node); |
| for (auto& child : node->children_) { |
| stack.emplace(&child); |
| } |
| } |
| |
| bool is_bound = node_ref_.has_value(); |
| // Traverse list of nodes in reverse order in order to unbind depth-first. |
| // Note: Unbind() both unbinds and resets the optional server ref value. This |
| // means that subsequent calls from children removing their own child nodes |
| // are no-ops. |
| for (auto node = nodes.rbegin(), end = nodes.rend(); node != end; ++node) { |
| (*node)->Unbind(); |
| } |
| if (parent_ != nullptr) { |
| if (is_bound) { |
| // Remove() is only called in response to a FIDL server being unbound. |
| // This can happen implicitly, when the underlying channel is closed, or |
| // explicitly, through a call to Remove() over FIDL (see method below). |
| // |
| // When this occurs, the node that FIDL is operating on contains a valid |
| // server ref, and is therefore the root of a sub-tree being removed. |
| // |
| // To safely remove all of the children of the node, we need to tell all |
| // FIDL servers to unbind themselves. However, this also means we need to |
| // delay erasing of the root node of a sub-tree until all of its children |
| // have erased themselves first, therefore avoiding a use-after-free. |
| // |
| // We can achieve this by delaying the removal of a node by posting it |
| // onto the dispatcher, where all the unbind operations are occuring in |
| // FIFO order. |
| async::PostTask(dispatcher_, [this] { parent_->children_.erase(*this); }); |
| } else { |
| // Otherwise, we are free to erase this node from its parent. |
| parent_->children_.erase(*this); |
| } |
| } |
| } |
| |
| void Node::Remove(RemoveCompleter::Sync& completer) { |
| // When NodeController::Remove() is called, we unbind the Node. This causes |
| // the Node server to then call Node::Remove(). |
| // |
| // We take this approach to avoid a use-after-free, where calling |
| // Node::Remove() directly would then cause the the Node server to do the |
| // same, after the Node has already been freed. |
| UnbindAndReset(node_ref_); |
| } |
| |
| void Node::AddChild(fdf::wire::NodeAddArgs args, fidl::ServerEnd<fdf::NodeController> controller, |
| fidl::ServerEnd<fdf::Node> node, AddChildCompleter::Sync& completer) { |
| if (!args.has_name()) { |
| LOGF(ERROR, "Failed to add Node, a name must be provided"); |
| completer.ReplyError(fdf::wire::NodeError::NAME_MISSING); |
| return; |
| } |
| auto name = args.name().get(); |
| if (name.find('.') != std::string_view::npos) { |
| LOGF(ERROR, "Failed to add Node '%.*s', name must not contain '.'", name.size(), name.data()); |
| completer.ReplyError(fdf::wire::NodeError::NAME_INVALID); |
| return; |
| } |
| for (auto& child : children_) { |
| if (child.name() == name) { |
| LOGF(ERROR, "Failed to add Node '%.*s', name already exists among siblings", name.size(), |
| name.data()); |
| completer.ReplyError(fdf::wire::NodeError::NAME_ALREADY_EXISTS); |
| return; |
| } |
| } |
| auto child = std::make_unique<Node>(this, driver_binder_, dispatcher_, name); |
| |
| if (args.has_offers()) { |
| child->offers_.reserve(args.offers().count()); |
| std::unordered_set<std::string_view> names; |
| for (auto& offer : args.offers()) { |
| auto inserted = names.emplace(offer.data(), offer.size()).second; |
| if (!inserted) { |
| LOGF(ERROR, "Failed to add Node '%.*s', offer '%.*s' already exists", name.size(), |
| name.data(), offer.size(), offer.data()); |
| completer.ReplyError(fdf::wire::NodeError::OFFER_ALREADY_EXISTS); |
| return; |
| } |
| child->offers_.emplace_back(child->allocator_, offer.get()); |
| } |
| } |
| |
| if (args.has_symbols()) { |
| child->symbols_.reserve(args.symbols().count()); |
| std::unordered_set<std::string_view> names; |
| for (auto& symbol : args.symbols()) { |
| if (!symbol.has_name()) { |
| LOGF(ERROR, "Failed to add Node '%.*s', a symbol is missing a name", name.size(), |
| name.data()); |
| completer.ReplyError(fdf::wire::NodeError::SYMBOL_NAME_MISSING); |
| return; |
| } |
| if (!symbol.has_address()) { |
| LOGF(ERROR, "Failed to add Node '%.*s', symbol '%.*s' is missing an address", name.size(), |
| name.data(), symbol.name().size(), symbol.name().data()); |
| completer.ReplyError(fdf::wire::NodeError::SYMBOL_ADDRESS_MISSING); |
| return; |
| } |
| auto inserted = names.emplace(symbol.name().data(), symbol.name().size()).second; |
| if (!inserted) { |
| LOGF(ERROR, "Failed to add Node '%.*s', symbol '%.*s' already exists", name.size(), |
| name.data(), symbol.name().size(), symbol.name().data()); |
| completer.ReplyError(fdf::wire::NodeError::SYMBOL_ALREADY_EXISTS); |
| return; |
| } |
| fdf::wire::NodeSymbol node_symbol(child->allocator_); |
| node_symbol.set_name(child->allocator_, child->allocator_, symbol.name().get()); |
| node_symbol.set_address(child->allocator_, symbol.address()); |
| child->symbols_.emplace_back(std::move(node_symbol)); |
| } |
| } |
| |
| auto bind_controller = fidl::BindServer<fidl::WireInterface<fdf::NodeController>>( |
| dispatcher_, std::move(controller), child.get()); |
| child->set_controller_ref(std::move(bind_controller)); |
| |
| if (node.is_valid()) { |
| auto bind_node = fidl::BindServer<fidl::WireInterface<fdf::Node>>( |
| dispatcher_, std::move(node), child.get(), |
| [](fidl::WireInterface<fdf::Node>* node, auto, auto) { |
| static_cast<Node*>(node)->Remove(); |
| }); |
| child->set_node_ref(std::move(bind_node)); |
| children_.push_back(std::move(child)); |
| completer.ReplySuccess(); |
| } else { |
| auto child_ptr = child.get(); |
| auto callback = [this, child = std::move(child), |
| completer = completer.ToAsync()](zx::status<> result) mutable { |
| if (result.is_error()) { |
| completer.Close(result.status_value()); |
| return; |
| } |
| children_.push_back(std::move(child)); |
| completer.ReplySuccess(); |
| }; |
| driver_binder_->Bind(child_ptr, std::move(args), std::move(callback)); |
| } |
| } |
| |
| DriverRunner::DriverRunner(fidl::ClientEnd<fsys::Realm> realm, |
| fidl::ClientEnd<fuchsia_driver_framework::DriverIndex> driver_index, |
| inspect::Inspector* inspector, async_dispatcher_t* dispatcher) |
| : realm_(std::move(realm), dispatcher), |
| driver_index_(std::move(driver_index), dispatcher), |
| dispatcher_(dispatcher), |
| root_node_(nullptr, this, dispatcher, "root") { |
| inspector->GetRoot().CreateLazyNode( |
| "driver_runner", [this] { return Inspect(); }, inspector); |
| } |
| |
| fit::promise<inspect::Inspector> DriverRunner::Inspect() { |
| inspect::Inspector inspector; |
| auto root = inspector.GetRoot().CreateChild(root_node_.name()); |
| InspectStack stack{{std::make_pair(&root, &root_node_)}}; |
| InspectNode(&inspector, &stack); |
| inspector.emplace(std::move(root)); |
| return fit::make_ok_promise(inspector); |
| } |
| |
| zx::status<> DriverRunner::PublishComponentRunner(const fbl::RefPtr<fs::PseudoDir>& svc_dir) { |
| const auto service = [this](zx::channel request) { |
| fidl::BindServer(dispatcher_, std::move(request), this); |
| return ZX_OK; |
| }; |
| zx_status_t status = svc_dir->AddEntry(fidl::DiscoverableProtocolName<frunner::ComponentRunner>, |
| fbl::MakeRefCounted<fs::Service>(service)); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "Failed to add directory entry '%s': %s", |
| fidl::DiscoverableProtocolName<frunner::ComponentRunner>, zx_status_get_string(status)); |
| } |
| return zx::make_status(status); |
| } |
| |
| zx::status<> DriverRunner::StartRootDriver(std::string_view url) { |
| return StartDriver(&root_node_, url); |
| } |
| |
| zx::status<> DriverRunner::StartDriver(Node* node, std::string_view url) { |
| auto create_result = CreateComponent(node->TopoName(), std::string(url), DriverCollection(url)); |
| if (create_result.is_error()) { |
| return create_result.take_error(); |
| } |
| driver_args_.emplace(url, DriverArgs{std::move(create_result.value()), node}); |
| return zx::ok(); |
| } |
| |
| void DriverRunner::Start(frunner::wire::ComponentStartInfo start_info, |
| fidl::ServerEnd<frunner::ComponentController> controller, |
| StartCompleter::Sync& completer) { |
| std::string url(start_info.resolved_url().get()); |
| auto it = driver_args_.find(url); |
| if (it == driver_args_.end()) { |
| LOGF(ERROR, "Failed to start driver '%.*s', unknown request for driver", url.size(), |
| url.data()); |
| completer.Close(ZX_ERR_UNAVAILABLE); |
| return; |
| } |
| auto driver_args = std::move(it->second); |
| driver_args_.erase(it); |
| auto symbols = driver_args.node->symbols(); |
| |
| // Launch a driver host, or use an existing driver host. |
| DriverHostComponent* driver_host; |
| if (start_args::ProgramValue(start_info.program(), "colocate").value_or("") == "true") { |
| if (driver_args.node == &root_node_) { |
| LOGF(ERROR, "Failed to start driver '%.*s', root driver cannot colocate", url.size(), |
| url.data()); |
| completer.Close(ZX_ERR_INVALID_ARGS); |
| return; |
| } |
| driver_host = driver_args.node->parent_driver_host(); |
| } else { |
| // Do not pass symbols across driver hosts. |
| symbols.set_count(0); |
| |
| auto result = StartDriverHost(); |
| if (result.is_error()) { |
| completer.Close(result.error_value()); |
| return; |
| } |
| driver_host = result.value().get(); |
| driver_hosts_.push_back(std::move(result.value())); |
| } |
| driver_args.node->set_driver_host(driver_host); |
| |
| // Start the driver within the driver host. |
| auto endpoints = fidl::CreateEndpoints<fdf::Node>(); |
| if (endpoints.is_error()) { |
| completer.Close(endpoints.status_value()); |
| return; |
| } |
| auto exposed_dir = service::Clone(driver_args.exposed_dir); |
| if (!exposed_dir.is_ok()) { |
| LOGF(ERROR, "Failed to clone exposed directory for driver '%.*s': %s", url.size(), url.data(), |
| exposed_dir.status_string()); |
| completer.Close(ZX_ERR_INTERNAL); |
| return; |
| } |
| auto start = driver_host->Start(std::move(endpoints->client), driver_args.node->offers(), |
| std::move(symbols), std::move(start_info.resolved_url()), |
| std::move(start_info.program()), std::move(start_info.ns()), |
| std::move(start_info.outgoing_dir()), std::move(*exposed_dir)); |
| if (start.is_error()) { |
| completer.Close(start.error_value()); |
| return; |
| } |
| |
| // Create a DriverComponent to manage the driver. |
| auto driver = std::make_unique<DriverComponent>(std::move(driver_args.exposed_dir), |
| std::move(start.value())); |
| auto bind_driver = fidl::BindServer<DriverComponent>( |
| dispatcher_, std::move(controller), driver.get(), |
| [this, name = driver_args.node->TopoName(), collection = DriverCollection(url)]( |
| DriverComponent* driver, auto, auto) { |
| drivers_.erase(*driver); |
| auto destroy_callback = [name](fidl::WireResponse<fsys::Realm::DestroyChild>* response) { |
| if (response->result.is_err()) { |
| LOGF(ERROR, "Failed to destroy component '%s': %u", name.data(), |
| response->result.err()); |
| } |
| }; |
| auto destroy = realm_->DestroyChild( |
| fsys::wire::ChildRef{.name = fidl::StringView::FromExternal(name), |
| .collection = fidl::StringView::FromExternal(collection)}, |
| std::move(destroy_callback)); |
| if (!destroy.ok()) { |
| LOGF(ERROR, "Failed to destroy component '%s': %s", name.data(), destroy.error()); |
| } |
| }); |
| driver_args.node->set_driver_ref(bind_driver); |
| driver->set_driver_ref(std::move(bind_driver)); |
| auto watch = driver->WatchDriver(dispatcher_); |
| if (watch.is_error()) { |
| LOGF(ERROR, "Failed to watch channel for driver '%.*s': %s", url.size(), url.data(), |
| watch.status_string()); |
| completer.Close(watch.error_value()); |
| return; |
| } |
| |
| // Bind the Node associated with the driver. |
| auto bind_node = fidl::BindServer<fidl::WireInterface<fdf::Node>>( |
| dispatcher_, std::move(endpoints->server), driver_args.node, |
| [](fidl::WireInterface<fdf::Node>* node, auto, auto) { static_cast<Node*>(node)->Remove(); }); |
| driver_args.node->set_node_ref(bind_node); |
| driver->set_node_ref(std::move(bind_node)); |
| drivers_.push_back(std::move(driver)); |
| } |
| |
| void DriverRunner::Bind(Node* node, fdf::wire::NodeAddArgs args, |
| fit::callback<void(zx::status<>)> callback) { |
| auto match_callback = [this, callback = callback.share(), node]( |
| fidl::WireResponse<fdf::DriverIndex::MatchDriver>* response) mutable { |
| if (response->result.is_err()) { |
| LOGF(ERROR, "Failed to match driver %s: %s", node->name().data(), |
| zx_status_get_string(response->result.err())); |
| callback(zx::error(response->result.err())); |
| return; |
| } |
| auto& url = response->result.response().url; |
| auto start_result = StartDriver(node, url.get()); |
| if (start_result.is_error()) { |
| LOGF(ERROR, "Failed to start driver %s: %s", node->name().data(), |
| zx_status_get_string(start_result.error_value())); |
| callback(start_result.take_error()); |
| return; |
| } |
| callback(zx::ok()); |
| }; |
| auto match_result = driver_index_->MatchDriver(std::move(args), std::move(match_callback)); |
| if (!match_result.ok()) { |
| LOGF(ERROR, "Failed to call match driver %s: %s", node->name().data(), match_result.error()); |
| callback(zx::error(match_result.status())); |
| } |
| } |
| |
| zx::status<std::unique_ptr<DriverHostComponent>> DriverRunner::StartDriverHost() { |
| auto name = "driver_host-" + std::to_string(next_driver_host_id_++); |
| auto create = CreateComponent(name, "fuchsia-boot:///#meta/driver_host2.cm", "driver_hosts"); |
| if (create.is_error()) { |
| return create.take_error(); |
| } |
| |
| auto endpoints = fidl::CreateEndpoints<fdf::DriverHost>(); |
| if (endpoints.is_error()) { |
| return endpoints.take_error(); |
| } |
| zx_status_t status = fdio_service_connect_at(create->channel().get(), |
| fidl::DiscoverableProtocolName<fdf::DriverHost>, |
| endpoints->server.TakeChannel().release()); |
| if (status != ZX_OK) { |
| LOGF(ERROR, "Failed to connect to service '%s': %s", |
| fidl::DiscoverableProtocolName<fdf::DriverHost>, zx_status_get_string(status)); |
| return zx::error(status); |
| } |
| |
| auto driver_host = std::make_unique<DriverHostComponent>(std::move(endpoints->client), |
| dispatcher_, &driver_hosts_); |
| return zx::ok(std::move(driver_host)); |
| } |
| |
| zx::status<fidl::ClientEnd<fio::Directory>> DriverRunner::CreateComponent(std::string name, |
| std::string url, |
| std::string collection) { |
| auto endpoints = fidl::CreateEndpoints<fio::Directory>(); |
| if (endpoints.is_error()) { |
| return endpoints.take_error(); |
| } |
| auto bind_callback = [name](fidl::WireResponse<fsys::Realm::BindChild>* response) { |
| if (response->result.is_err()) { |
| LOGF(ERROR, "Failed to bind component '%s': %u", name.data(), response->result.err()); |
| } |
| }; |
| auto create_callback = [this, name, collection, server_end = std::move(endpoints->server), |
| bind_callback = std::move(bind_callback)]( |
| fidl::WireResponse<fsys::Realm::CreateChild>* response) mutable { |
| if (response->result.is_err()) { |
| LOGF(ERROR, "Failed to create component '%s': %u", name.data(), response->result.err()); |
| return; |
| } |
| auto bind = realm_->BindChild( |
| fsys::wire::ChildRef{.name = fidl::StringView::FromExternal(name), |
| .collection = fidl::StringView::FromExternal(collection)}, |
| std::move(server_end), std::move(bind_callback)); |
| if (!bind.ok()) { |
| LOGF(ERROR, "Failed to bind component '%s': %s", name.data(), bind.error()); |
| } |
| }; |
| fidl::FidlAllocator allocator; |
| fsys::wire::ChildDecl child_decl(allocator); |
| child_decl.set_name(allocator, fidl::StringView::FromExternal(name)) |
| .set_url(allocator, fidl::StringView::FromExternal(url)) |
| .set_startup(allocator, fsys::wire::StartupMode::LAZY); |
| auto create = realm_->CreateChild( |
| fsys::wire::CollectionRef{.name = fidl::StringView::FromExternal(collection)}, |
| std::move(child_decl), std::move(create_callback)); |
| if (!create.ok()) { |
| LOGF(ERROR, "Failed to create component '%s': %s", name.data(), create.error()); |
| return zx::error(ZX_ERR_INTERNAL); |
| } |
| return zx::ok(std::move(endpoints->client)); |
| } |