| // Copyright 2018 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/svc/dir.h" | 
 |  | 
 | #include <fbl/string.h> | 
 | #include <fs/pseudo-dir.h> | 
 | #include <fs/service.h> | 
 | #include <fs/synchronous-vfs.h> | 
 |  | 
 | struct svc_dir { | 
 |   explicit svc_dir(async_dispatcher_t* dispatcher) : vfs(dispatcher) {} | 
 |  | 
 |   fs::SynchronousVfs vfs; | 
 |   fbl::RefPtr<fs::PseudoDir> root; | 
 | }; | 
 |  | 
 | zx_status_t svc_dir_create(async_dispatcher_t* dispatcher, | 
 |                            zx_handle_t dir_request, svc_dir_t** result) { | 
 |   svc_dir_t* dir = new svc_dir_t(dispatcher); | 
 |   dir->root = fbl::AdoptRef(new fs::PseudoDir()); | 
 |   zx_status_t status = | 
 |       dir->vfs.ServeDirectory(dir->root, zx::channel(dir_request)); | 
 |   if (status != ZX_OK) { | 
 |     delete dir; | 
 |     return status; | 
 |   } | 
 |   *result = dir; | 
 |   return ZX_OK; | 
 | } | 
 |  | 
 | zx_status_t svc_dir_add_service(svc_dir_t* dir, const char* type, | 
 |                                 const char* service_name, void* context, | 
 |                                 svc_connector_t handler) { | 
 |   fbl::RefPtr<fs::Vnode> node = dir->root; | 
 |   if (type != nullptr) { | 
 |     zx_status_t status = dir->root->Lookup(&node, type); | 
 |     if (status == ZX_ERR_NOT_FOUND) { | 
 |       node = fbl::AdoptRef(new fs::PseudoDir()); | 
 |       status = dir->root->AddEntry(type, node); | 
 |     } | 
 |     if (status != ZX_OK) | 
 |       return status; | 
 |   } | 
 |   fs::PseudoDir* node_dir = static_cast<fs::PseudoDir*>(node.get()); | 
 |   return node_dir->AddEntry( | 
 |       service_name, | 
 |       fbl::AdoptRef(new fs::Service([service_name = fbl::String(service_name), | 
 |                                      context, handler](zx::channel channel) { | 
 |         handler(context, service_name.c_str(), channel.release()); | 
 |         return ZX_OK; | 
 |       }))); | 
 | } | 
 |  | 
 | zx_status_t svc_dir_remove_service(svc_dir_t* dir, const char* type, | 
 |                                    const char* service_name) { | 
 |   fbl::RefPtr<fs::Vnode> node = dir->root; | 
 |   if (type != nullptr) { | 
 |     zx_status_t status = dir->root->Lookup(&node, type); | 
 |     if (status != ZX_OK) | 
 |       return status; | 
 |   } | 
 |   fs::PseudoDir* node_dir = static_cast<fs::PseudoDir*>(node.get()); | 
 |   return node_dir->RemoveEntry(service_name); | 
 | } | 
 |  | 
 | zx_status_t svc_dir_destroy(svc_dir_t* dir) { | 
 |   delete dir; | 
 |   return ZX_OK; | 
 | } |