| // 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/devmgr-launcher/launch.h> |
| #include <lib/devmgr-launcher/processargs.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/fdio/fd.h> |
| #include <lib/fdio/fdio.h> |
| #include <lib/fdio/io.h> |
| #include <lib/fdio/namespace.h> |
| #include <lib/fdio/spawn-actions.h> |
| #include <lib/fdio/spawn.h> |
| #include <lib/zx/channel.h> |
| #include <lib/zx/process.h> |
| #include <stdint.h> |
| #include <zircon/assert.h> |
| #include <zircon/device/vfs.h> |
| #include <zircon/processargs.h> |
| #include <zircon/status.h> |
| |
| #include <utility> |
| #include <vector> |
| |
| #include <fbl/algorithm.h> |
| |
| namespace { |
| |
| constexpr const char* kDevmgrPath = "/pkg/bin/driver_manager"; |
| constexpr const char* kFshostPath = "/pkg/bin/fshost"; |
| |
| } // namespace |
| |
| namespace devmgr_launcher { |
| |
| // Launches an fshost process in the given job. Fshost will have devfs_client |
| // installed in its namespace as /dev, and svc_client as /svc |
| zx_status_t LaunchFshost(Args args, zx::channel svc_client, zx::channel fshost_outgoing_server, |
| zx::job devmgr_job, zx::channel devfs_client) { |
| zx::job job_copy; |
| zx_status_t status = devmgr_job.duplicate(ZX_RIGHT_SAME_RIGHTS, &job_copy); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| const bool clone_stdio = !args.stdio.is_valid(); |
| |
| std::vector<const char*> argv; |
| argv.push_back(kFshostPath); |
| if (args.disable_block_watcher) { |
| argv.push_back("--disable-block-watcher"); |
| } |
| argv.push_back(nullptr); |
| |
| FdioSpawnActions actions; |
| actions.AddAction(fdio_spawn_action_t{ |
| .action = FDIO_SPAWN_ACTION_SET_NAME, |
| .name = {.data = "test-fshost"}, |
| }); |
| actions.AddActionWithHandle( |
| fdio_spawn_action_t{ |
| .action = FDIO_SPAWN_ACTION_ADD_HANDLE, |
| .h = {.id = PA_HND(PA_JOB_DEFAULT, 0)}, |
| }, |
| std::move(job_copy)); |
| actions.AddActionWithHandle( |
| fdio_spawn_action_t{ |
| .action = FDIO_SPAWN_ACTION_ADD_HANDLE, |
| .h = {.id = PA_HND(PA_DIRECTORY_REQUEST, 0)}, |
| }, |
| std::move(fshost_outgoing_server)); |
| |
| actions.AddActionWithNamespace( |
| fdio_spawn_action_t{ |
| .action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY, |
| .ns = {.prefix = "/dev"}, |
| }, |
| std::move(devfs_client)); |
| |
| zx::channel local, remote; |
| status = zx::channel::create(0, &local, &remote); |
| if (status != ZX_OK) { |
| return status; |
| } |
| status = fdio_open("/pkg", ZX_FS_RIGHT_READABLE | ZX_FS_FLAG_DIRECTORY, remote.release()); |
| if (status != ZX_OK) { |
| return status; |
| } |
| actions.AddActionWithNamespace( |
| fdio_spawn_action_t{ |
| .action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY, |
| .ns = {.prefix = "/boot"}, |
| }, |
| std::move(local)); |
| |
| actions.AddActionWithNamespace( |
| fdio_spawn_action_t{ |
| .action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY, |
| .ns = {.prefix = "/svc"}, |
| }, |
| std::move(svc_client)); |
| |
| if (!clone_stdio) { |
| actions.AddAction(fdio_spawn_action_t{ |
| .action = FDIO_SPAWN_ACTION_TRANSFER_FD, |
| .fd = {.local_fd = args.stdio.release(), .target_fd = FDIO_FLAG_USE_FOR_STDIO}, |
| }); |
| } |
| |
| uint32_t flags = FDIO_SPAWN_DEFAULT_LDSVC | FDIO_SPAWN_CLONE_UTC_CLOCK; |
| if (clone_stdio) { |
| flags |= FDIO_SPAWN_CLONE_STDIO; |
| } |
| |
| zx::process new_process; |
| std::vector<fdio_spawn_action_t> spawn_actions = actions.GetActions(); |
| status = fdio_spawn_etc(devmgr_job.get(), flags, kFshostPath, argv.data(), nullptr /* environ */, |
| spawn_actions.size(), spawn_actions.data(), |
| new_process.reset_and_get_address(), nullptr /* err_msg */); |
| if (status != ZX_OK) { |
| return status; |
| } |
| return ZX_OK; |
| } |
| |
| __EXPORT |
| zx_status_t Launch(Args args, zx::channel svc_client, zx::channel fshost_outgoing_server, |
| zx::channel component_lifecycle_server, zx::job* devmgr_job, |
| zx::channel* devfs_root, zx::channel* outgoing_services_root) { |
| // Create containing job (and copies for devcoordinator and fshost) |
| zx::job job, devmgr_job_copy, fshost_job_copy; |
| zx_status_t status = zx::job::create(*zx::job::default_job(), 0, &job); |
| if (status != ZX_OK) { |
| return status; |
| } |
| status = job.duplicate(ZX_RIGHT_SAME_RIGHTS, &devmgr_job_copy); |
| if (status != ZX_OK) { |
| return status; |
| } |
| status = job.duplicate(ZX_RIGHT_SAME_RIGHTS, &fshost_job_copy); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| // Create channel to connect to devfs |
| zx::channel devfs_client, devfs_server; |
| status = zx::channel::create(0, &devfs_client, &devfs_server); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| // Create devfs client clone for fshost |
| zx::channel devfs_for_fshost(fdio_service_clone(devfs_client.get())); |
| |
| // Create svc client clone for fshost |
| zx::channel svc_client_for_fshost(fdio_service_clone(svc_client.get())); |
| |
| // Create channel to connect to outgoing services |
| zx::channel outgoing_services_client, outgoing_services_server; |
| status = zx::channel::create(0, &outgoing_services_client, &outgoing_services_server); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| const bool clone_stdio = !args.stdio.is_valid(); |
| |
| std::vector<const char*> argv; |
| argv.push_back(kDevmgrPath); |
| for (const char* path : args.driver_search_paths) { |
| argv.push_back("--driver-search-path"); |
| argv.push_back(path); |
| } |
| for (const char* path : args.load_drivers) { |
| argv.push_back("--load-driver"); |
| argv.push_back(path); |
| } |
| if (args.no_exit_after_suspend) { |
| argv.push_back("--no-exit-after-suspend"); |
| } |
| if (args.sys_device_driver != nullptr) { |
| argv.push_back("--sys-device-driver"); |
| argv.push_back(args.sys_device_driver); |
| } |
| if (args.driver_runner_root_driver_url != nullptr) { |
| argv.push_back("--driver-runner-root-driver-url"); |
| argv.push_back(args.driver_runner_root_driver_url); |
| } |
| argv.push_back(nullptr); |
| |
| FdioSpawnActions actions; |
| actions.AddAction(fdio_spawn_action_t{ |
| .action = FDIO_SPAWN_ACTION_SET_NAME, |
| .name = {.data = "test-devmgr"}, |
| }); |
| actions.AddActionWithHandle( |
| fdio_spawn_action_t{ |
| .action = FDIO_SPAWN_ACTION_ADD_HANDLE, |
| .h = {.id = PA_HND(PA_JOB_DEFAULT, 0)}, |
| }, |
| std::move(devmgr_job_copy)); |
| actions.AddActionWithHandle( |
| fdio_spawn_action_t{ |
| .action = FDIO_SPAWN_ACTION_ADD_HANDLE, |
| .h = {.id = DEVMGR_LAUNCHER_DEVFS_ROOT_HND}, |
| }, |
| std::move(devfs_server)); |
| actions.AddActionWithHandle( |
| fdio_spawn_action_t{ |
| .action = FDIO_SPAWN_ACTION_ADD_HANDLE, |
| .h = {.id = DEVMGR_LAUNCHER_OUTGOING_SERVICES_HND}, |
| }, |
| std::move(outgoing_services_server)); |
| actions.AddActionWithHandle( |
| fdio_spawn_action_t{ |
| .action = FDIO_SPAWN_ACTION_ADD_HANDLE, |
| .h = {.id = PA_LIFECYCLE}, |
| }, |
| std::move(component_lifecycle_server)); |
| |
| for (auto& ns : args.flat_namespace) { |
| zx::handle ns_handle_clone(fdio_service_clone(ns.second.get())); |
| actions.AddActionWithNamespace( |
| fdio_spawn_action_t{ |
| .action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY, |
| .ns = {.prefix = ns.first}, |
| }, |
| std::move(ns_handle_clone)); |
| } |
| |
| zx::channel local, remote; |
| status = zx::channel::create(0, &local, &remote); |
| if (status != ZX_OK) { |
| return status; |
| } |
| status = fdio_open("/pkg", ZX_FS_RIGHT_READABLE | ZX_FS_FLAG_DIRECTORY, remote.release()); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| actions.AddActionWithNamespace( |
| fdio_spawn_action_t{ |
| .action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY, |
| .ns = {.prefix = "/boot"}, |
| }, |
| std::move(local)); |
| |
| actions.AddActionWithNamespace( |
| fdio_spawn_action_t{ |
| .action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY, |
| .ns = {.prefix = "/svc"}, |
| }, |
| std::move(svc_client)); |
| |
| if (!clone_stdio) { |
| zx_handle_t stdio_clone; |
| status = fdio_fd_clone(args.stdio.get(), &stdio_clone); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| fdio_t* stdio_clone_fdio_t; |
| status = fdio_create(stdio_clone, &stdio_clone_fdio_t); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| int stdio_clone_fd = fdio_bind_to_fd(stdio_clone_fdio_t, -1, 0); |
| |
| actions.AddAction(fdio_spawn_action_t{ |
| .action = FDIO_SPAWN_ACTION_TRANSFER_FD, |
| .fd = {.local_fd = stdio_clone_fd, .target_fd = FDIO_FLAG_USE_FOR_STDIO}, |
| }); |
| } |
| |
| uint32_t flags = FDIO_SPAWN_DEFAULT_LDSVC | FDIO_SPAWN_CLONE_UTC_CLOCK; |
| if (clone_stdio) { |
| flags |= FDIO_SPAWN_CLONE_STDIO; |
| } |
| |
| zx::process new_process; |
| std::vector<fdio_spawn_action_t> spawn_actions = actions.GetActions(); |
| status = fdio_spawn_etc(job.get(), flags, kDevmgrPath, argv.data(), nullptr /* environ */, |
| spawn_actions.size(), spawn_actions.data(), |
| new_process.reset_and_get_address(), nullptr /* err_msg */); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| status = LaunchFshost(std::move(args), std::move(svc_client_for_fshost), |
| std::move(fshost_outgoing_server), std::move(fshost_job_copy), |
| std::move(devfs_for_fshost)); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| *devmgr_job = std::move(job); |
| *devfs_root = std::move(devfs_client); |
| *outgoing_services_root = std::move(outgoing_services_client); |
| return ZX_OK; |
| } |
| |
| } // namespace devmgr_launcher |