| // 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 == "path_prefix") { | |
| args.path_prefix = 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; | |
| } |