| // 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 <fcntl.h> | 
 | #include <lib/async-loop/cpp/loop.h> | 
 | #include <lib/async-loop/default.h> | 
 | #include <lib/fdio/directory.h> | 
 | #include <lib/sys/cpp/component_context.h> | 
 | #include <lib/syslog/cpp/macros.h> | 
 | #include <lib/vfs/cpp/remote_dir.h> | 
 | #include <zircon/device/vfs.h> | 
 |  | 
 | #include <iostream> | 
 | #include <string_view> | 
 |  | 
 | #include <fbl/ref_ptr.h> | 
 |  | 
 | #include "isolated_devmgr.h" | 
 | #include "src/lib/fxl/command_line.h" | 
 | #include "src/lib/fxl/strings/split_string.h" | 
 | #include "src/lib/fxl/strings/string_number_conversions.h" | 
 |  | 
 | #define DEVICE_IDS_VID_LOC 0 | 
 | #define DEVICE_IDS_PID_LOC 1 | 
 | #define DEVICE_IDS_DID_LOC 2 | 
 | #define DEVICE_IDS_SIZE 3 | 
 | #define ISO_DEV_MGR_RET_OK 0 | 
 | #define ISO_DEV_MGR_RET_ERR 1 | 
 |  | 
 | using namespace isolated_devmgr; | 
 |  | 
 | void Usage() { | 
 |   std::cerr << | 
 |       R"( | 
 | Usage: | 
 |    isolated_devmgr [options] | 
 |  | 
 | Options: | 
 |    --svc_name=[svc_name]: service name to expose, defaults to fuchsia.io.Directory | 
 |    --load_driver=[driver_path]: loads a driver into isolated manager. May be informed multiple | 
 |                                 times. | 
 |    --search_driver=[search_path]: loads all drivers in provided search path. May be informed | 
 |                                   multiple times. | 
 |    --sys_device=[sys_device_driver]: path to sys device driver, defaults to | 
 |                                      /boot/driver/test/sysdev.so | 
 |    --wait_for=[device]: wait for isolated manager to have |device| exposed before serving any | 
 |                         requests. May be informed multiple times. | 
 |    --add_namespace=[ns]: make the namespace 'ns' from this component available to the devmgr | 
 |                          under the same path. | 
 |    --device_vid_pid_did=[dev_vid:dev_pid:dev_did]: adding a device with hex dev_vid, dev_pid | 
 |                                                    and dev_did. May be informed multiple times. | 
 |    --enable_block_watcher: Enable block watcher. | 
 |    --help: displays this help page. | 
 |  | 
 | Note: isolated_devmgr runs as a component, so all paths must be relative to the component's | 
 | namespace. Since the devmgr libraries and executables are currently under /boot, the components | 
 | sandbox metadata must include the "/boot/bin" and "/boot/lib". Additionally, it's common to load | 
 | drivers out of "/boot/driver" and this directory must also be specificed in the components sandbox | 
 | metadata to make these drivers available to isolated_devmgr. | 
 |  | 
 | Example sandbox metadata: | 
 |  | 
 |     "sandbox": { | 
 |         "boot": [ | 
 |             "bin", | 
 |             "driver", | 
 |             "lib" | 
 |         ] | 
 |     } | 
 | )"; | 
 | } | 
 |  | 
 | static int process_device_ids(const char* str, | 
 |                               fbl::Vector<board_test::DeviceEntry>* dev_entry_list_ptr) { | 
 |   board_test::DeviceEntry dev_entry = {}; | 
 |   uint32_t key; | 
 |   std::string_view sv_str = std::string_view(str); | 
 |   auto params = fxl::SplitString(sv_str, ":", fxl::kKeepWhitespace, fxl::kSplitWantNonEmpty); | 
 |   if ((dev_entry_list_ptr == nullptr) || (params.size() < DEVICE_IDS_SIZE)) { | 
 |     return ISO_DEV_MGR_RET_ERR; | 
 |   } | 
 |   if (!fxl::StringToNumberWithError(params.at(DEVICE_IDS_VID_LOC), &key, fxl::Base::k16)) { | 
 |     return ISO_DEV_MGR_RET_ERR; | 
 |   } | 
 |   dev_entry.vid = key; | 
 |   if (!fxl::StringToNumberWithError(params.at(DEVICE_IDS_PID_LOC), &key, fxl::Base::k16)) { | 
 |     return ISO_DEV_MGR_RET_ERR; | 
 |   } | 
 |   dev_entry.pid = key; | 
 |   if (!fxl::StringToNumberWithError(params.at(DEVICE_IDS_DID_LOC), &key, fxl::Base::k16)) { | 
 |     return ISO_DEV_MGR_RET_ERR; | 
 |   } | 
 |   dev_entry.did = key; | 
 |   dev_entry_list_ptr->push_back(dev_entry); | 
 |   return ISO_DEV_MGR_RET_OK; | 
 | } | 
 |  | 
 | int main(int argc, const char** argv) { | 
 |   async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread); | 
 |   devmgr_launcher::Args args; | 
 |   auto device_list_unique_ptr = std::unique_ptr<fbl::Vector<board_test::DeviceEntry>>( | 
 |       new fbl::Vector<board_test::DeviceEntry>()); | 
 |  | 
 |   // fill up defaults: | 
 |   args.sys_device_driver = "/boot/driver/test/sysdev.so"; | 
 |   args.load_drivers.push_back("/boot/driver/test/sysdev.so"); | 
 |   args.stdio = fbl::unique_fd(open("/dev/null", O_RDWR)); | 
 |   args.disable_block_watcher = true; | 
 |   args.disable_netsvc = true; | 
 |  | 
 |   std::string svc_name = "fuchsia.io.Directory"; | 
 |   std::vector<std::string> wait; | 
 |   std::vector<std::string> namespaces; | 
 |  | 
 |   // load options from command line | 
 |   auto cl = fxl::CommandLineFromArgcArgv(argc, argv); | 
 |   for (const auto& opt : cl.options()) { | 
 |     if (opt.name == "svc_name") { | 
 |       svc_name = opt.value; | 
 |     } else if (opt.name == "load_driver") { | 
 |       args.load_drivers.push_back(opt.value.c_str()); | 
 |     } else if (opt.name == "search_driver") { | 
 |       args.driver_search_paths.push_back(opt.value.c_str()); | 
 |     } else if (opt.name == "sys_device") { | 
 |       args.sys_device_driver = opt.value.c_str(); | 
 |     } else if (opt.name == "wait_for") { | 
 |       wait.push_back(opt.value); | 
 |     } else if (opt.name == "add_namespace") { | 
 |       namespaces.push_back(opt.value); | 
 |     } else if (opt.name == "device_vid_pid_did") { | 
 |       int status = process_device_ids(opt.value.c_str(), device_list_unique_ptr.get()); | 
 |       if (status != 0) { | 
 |         Usage(); | 
 |         return status; | 
 |       } | 
 |     } else if (opt.name == "enable_block_watcher") { | 
 |       args.disable_block_watcher = false; | 
 |     } else if (opt.name == "help") { | 
 |       Usage(); | 
 |       return ISO_DEV_MGR_RET_OK; | 
 |     } else { | 
 |       Usage(); | 
 |       return ISO_DEV_MGR_RET_ERR; | 
 |     } | 
 |   } | 
 |  | 
 |   // Pass-through any additional namespaces that we want to provide to the devmgr. These are | 
 |   // exposed to devmgr under the same local path. Ex: if you share '/pkg', you could provide a | 
 |   // driver as '/pkg/data/my_driver.so'. | 
 |   for (const auto& ns : namespaces) { | 
 |     zx::channel client, server; | 
 |     zx_status_t status = zx::channel::create(0, &client, &server); | 
 |     if (status != ZX_OK) { | 
 |       FX_PLOGS(ERROR, status) << "Failed to create channel"; | 
 |       return ISO_DEV_MGR_RET_ERR; | 
 |     } | 
 |     status = fdio_open(ns.c_str(), ZX_FS_RIGHT_READABLE, server.release()); | 
 |     if (status != ZX_OK) { | 
 |       FX_PLOGS(ERROR, status) << "Failed to open namespace " << ns; | 
 |       return ISO_DEV_MGR_RET_ERR; | 
 |     } | 
 |     args.flat_namespace.push_back({ns.c_str(), std::move(client)}); | 
 |   } | 
 |  | 
 |   auto devmgr = | 
 |       IsolatedDevmgr::Create(std::move(args), std::move(device_list_unique_ptr), loop.dispatcher()); | 
 |   if (!devmgr) { | 
 |     return ISO_DEV_MGR_RET_ERR; | 
 |   } | 
 |  | 
 |   devmgr->SetExceptionCallback([]() { | 
 |     FX_LOGS(ERROR) << "Isolated Devmgr crashed"; | 
 |     zx_process_exit(ISO_DEV_MGR_RET_ERR); | 
 |   }); | 
 |  | 
 |   for (const auto& path : wait) { | 
 |     if (devmgr->WaitForFile(path.c_str()) != ZX_OK) { | 
 |       FX_LOGS(ERROR) << "Isolated Devmgr failed while waiting for path " << path; | 
 |       return ISO_DEV_MGR_RET_ERR; | 
 |     } | 
 |   } | 
 |  | 
 |   auto context = sys::ComponentContext::CreateAndServeOutgoingDirectory(); | 
 |   auto service = | 
 |       std::make_unique<vfs::Service>([&devmgr](zx::channel chan, async_dispatcher_t* dispatcher) { | 
 |         devmgr->Connect(std::move(chan)); | 
 |       }); | 
 |   context->outgoing()->AddPublicService(std::move(service), std::move(svc_name)); | 
 |  | 
 |   // Add devfs to out directory | 
 |   zx::channel client, server; | 
 |   auto status = zx::channel::create(0, &client, &server); | 
 |   if (status != ZX_OK) { | 
 |     return status; | 
 |   } | 
 |   devmgr->Connect(std::move(server)); | 
 |   auto devfs_out = std::make_unique<vfs::RemoteDir>(std::move(client)); | 
 |   context->outgoing()->root_dir()->AddEntry("dev", std::move(devfs_out)); | 
 |  | 
 |   loop.Run(); | 
 |  | 
 |   return ISO_DEV_MGR_RET_OK; | 
 | } |