1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <lib/devmgr-launcher/launch.h>
6 
7 #include <stdint.h>
8 #include <utility>
9 
10 #include <fbl/algorithm.h>
11 #include <lib/fdio/namespace.h>
12 #include <lib/fdio/spawn.h>
13 #include <lib/fdio/util.h>
14 #include <lib/zx/channel.h>
15 #include <lib/zx/process.h>
16 #include <zircon/assert.h>
17 #include <zircon/device/vfs.h>
18 #include <zircon/processargs.h>
19 #include <zircon/status.h>
20 
21 #include <lib/devmgr-launcher/processargs.h>
22 
23 namespace {
24 
25 constexpr const char* kDevmgrPath = "/boot/bin/devmgr";
26 
27 } // namespace
28 
29 namespace devmgr_launcher {
30 
Launch(Args args,zx::job * devmgr_job,zx::channel * devfs_root)31 zx_status_t Launch(Args args, zx::job* devmgr_job, zx::channel* devfs_root) {
32     // Create containing job (and copy to send to devmgr)
33     zx::job job, job_copy;
34     zx_status_t status = zx::job::create(*zx::job::default_job(), 0, &job);
35     if (status != ZX_OK) {
36         return status;
37     }
38     status = job.duplicate(ZX_RIGHT_SAME_RIGHTS, &job_copy);
39     if (status != ZX_OK) {
40         return status;
41     }
42 
43     // Create a new client to /boot to give to devmgr
44     zx::channel bootfs_client;
45     {
46         zx::channel bootfs_server;
47         status = zx::channel::create(0, &bootfs_client, &bootfs_server);
48         if (status != ZX_OK) {
49             return status;
50         }
51 
52         fdio_ns_t* ns;
53         status = fdio_ns_get_installed(&ns);
54         if (status != ZX_OK) {
55             return status;
56         }
57         status = fdio_ns_connect(ns, "/boot", ZX_FS_RIGHT_READABLE, bootfs_server.release());
58         if (status != ZX_OK) {
59             return status;
60         }
61     }
62 
63     // Create channel to connect to devfs
64     zx::channel devfs_client, devfs_server;
65     status = zx::channel::create(0, &devfs_client, &devfs_server);
66     if (status != ZX_OK) {
67         return status;
68     }
69 
70     const bool clone_stdio = !args.stdio.is_valid();
71 
72     fbl::Vector<const char*> argv;
73     argv.push_back(kDevmgrPath);
74     for (const char* path : args.driver_search_paths) {
75         argv.push_back("--driver-search-path");
76         argv.push_back(path);
77     }
78     for (const char* path : args.load_drivers) {
79         argv.push_back("--load-driver");
80         argv.push_back(path);
81     }
82     if (args.sys_device_driver != nullptr) {
83         argv.push_back("--sys-device-driver");
84         argv.push_back(args.sys_device_driver);
85     }
86     argv.push_back(nullptr);
87 
88     fbl::Vector<fdio_spawn_action_t> actions;
89     actions.push_back(fdio_spawn_action_t{
90         .action = FDIO_SPAWN_ACTION_SET_NAME,
91         .name = { .data = "test-devmgr" },
92     });
93     actions.push_back(fdio_spawn_action_t{
94         .action = FDIO_SPAWN_ACTION_ADD_HANDLE,
95         .h = { .id = PA_HND(PA_JOB_DEFAULT, 0), .handle = job_copy.release() },
96     });
97     actions.push_back(fdio_spawn_action_t{
98         .action = FDIO_SPAWN_ACTION_ADD_HANDLE,
99         .h = { .id = DEVMGR_LAUNCHER_DEVFS_ROOT_HND, .handle = devfs_server.release() },
100     });
101     actions.push_back(fdio_spawn_action_t{
102         .action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY,
103         .ns = { .prefix = "/boot", .handle = bootfs_client.release() },
104     });
105     if (args.bootdata) {
106         actions.push_back(fdio_spawn_action_t{
107             .action = FDIO_SPAWN_ACTION_ADD_HANDLE,
108             .h = { .id = PA_HND(PA_VMO_BOOTDATA, 0), .handle = args.bootdata.release() },
109         });
110     }
111     if (!clone_stdio) {
112         actions.push_back(fdio_spawn_action_t{
113             .action = FDIO_SPAWN_ACTION_TRANSFER_FD,
114             .fd = { .local_fd = args.stdio.release(), .target_fd = FDIO_FLAG_USE_FOR_STDIO },
115         });
116     }
117 
118     uint32_t flags = FDIO_SPAWN_DEFAULT_LDSVC;
119     if (clone_stdio) {
120         flags |= FDIO_SPAWN_CLONE_STDIO;
121     }
122 
123     zx::process new_process;
124     status = fdio_spawn_etc(job.get(),
125                             flags,
126                             kDevmgrPath,
127                             argv.get(),
128                             nullptr /* environ */,
129                             actions.size(),
130                             actions.get(),
131                             new_process.reset_and_get_address(),
132                             nullptr /* err_msg */);
133     if (status != ZX_OK) {
134         return status;
135     }
136 
137     *devmgr_job = std::move(job);
138     *devfs_root = std::move(devfs_client);
139     return ZX_OK;
140 }
141 
142 } // namespace devmgr_integration_test
143