| // Copyright 2016 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 "fdio.h" |
| |
| #include <lib/fdio/directory.h> |
| #include <lib/fdio/fd.h> |
| #include <lib/fdio/fdio.h> |
| #include <lib/fdio/io.h> |
| #include <lib/fdio/spawn.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <lib/zircon-internal/paths.h> |
| #include <lib/zx/channel.h> |
| #include <lib/zx/debuglog.h> |
| #include <lib/zx/job.h> |
| #include <lib/zx/process.h> |
| #include <lib/zx/resource.h> |
| #include <lib/zx/vmo.h> |
| #include <limits.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <zircon/processargs.h> |
| #include <zircon/status.h> |
| #include <zircon/syscalls.h> |
| #include <zircon/syscalls/log.h> |
| |
| #include <iterator> |
| #include <utility> |
| |
| #include <fbl/algorithm.h> |
| #include <fbl/array.h> |
| #include <fbl/vector.h> |
| |
| namespace devmgr { |
| |
| #define CHILD_JOB_RIGHTS (ZX_RIGHTS_BASIC | ZX_RIGHT_MANAGE_JOB | ZX_RIGHT_MANAGE_PROCESS) |
| |
| enum class FdioAction { |
| AddNsEntry = 1, |
| CloneDir = 2, |
| }; |
| |
| // clang-format off |
| |
| static struct { |
| const char* mount; |
| const char* name; |
| uint32_t flags; |
| FdioAction action; |
| } FSTAB[] = { |
| { "/svc", "svc", FS_SVC, FdioAction::CloneDir }, |
| { "/data", "data", FS_DATA, FdioAction::AddNsEntry }, |
| { "/blob", "blobexec", FS_BLOB_EXEC, FdioAction::AddNsEntry }, |
| { "/svc_blobfs", "svc_blobfs", FS_SVC_BLOBFS, FdioAction::CloneDir }, |
| }; |
| |
| // clang-format on |
| |
| FsProvider::~FsProvider() {} |
| |
| DevmgrLauncher::DevmgrLauncher(FsProvider* fs_provider) : fs_provider_(fs_provider) {} |
| |
| zx_status_t DevmgrLauncher::LaunchWithLoader(const zx::job& job, const char* name, |
| zx::vmo executable, zx::channel loader, |
| const char* const* argv, const char** initial_envp, |
| int stdiofd, const zx::resource& root_resource, |
| const zx_handle_t* handles, const uint32_t* types, |
| size_t hcount, zx::process* out_proc, uint32_t flags) { |
| zx::job job_copy; |
| zx_status_t status = job.duplicate(CHILD_JOB_RIGHTS, &job_copy); |
| if (status != ZX_OK) { |
| FX_LOGS(ERROR) << "launch failed " << zx_status_get_string(status); |
| return status; |
| } |
| |
| zx::debuglog debuglog; |
| if (stdiofd < 0) { |
| if ((status = zx::debuglog::create(root_resource, 0, &debuglog) != ZX_OK)) { |
| return status; |
| } |
| } |
| |
| uint32_t spawn_flags = FDIO_SPAWN_CLONE_JOB | FDIO_SPAWN_CLONE_UTC_CLOCK; |
| |
| // Set up the environ for the new process |
| fbl::Vector<const char*> env; |
| if (getenv(LDSO_TRACE_CMDLINE)) { |
| env.push_back(LDSO_TRACE_ENV); |
| } |
| env.push_back(ZX_SHELL_ENV_PATH); |
| while (initial_envp && initial_envp[0]) { |
| env.push_back(*initial_envp++); |
| } |
| env.push_back(nullptr); |
| |
| fbl::Vector<fdio_spawn_action_t> actions; |
| actions.reserve(3 + std::size(FSTAB) + hcount); |
| |
| actions.push_back((fdio_spawn_action_t){ |
| .action = FDIO_SPAWN_ACTION_SET_NAME, |
| .name = {.data = name}, |
| }); |
| |
| if (loader.is_valid()) { |
| actions.push_back((fdio_spawn_action_t){ |
| .action = FDIO_SPAWN_ACTION_ADD_HANDLE, |
| .h = {.id = PA_HND(PA_LDSVC_LOADER, 0), .handle = loader.release()}, |
| }); |
| } else { |
| spawn_flags |= FDIO_SPAWN_DEFAULT_LDSVC; |
| } |
| |
| // create namespace based on FS_* flags |
| for (unsigned n = 0; n < std::size(FSTAB); n++) { |
| if (!(FSTAB[n].flags & flags)) { |
| continue; |
| } |
| switch (FSTAB[n].action) { |
| case FdioAction::AddNsEntry: { |
| zx_handle_t h; |
| if ((h = fs_provider_->CloneFs(FSTAB[n].name).release()) != ZX_HANDLE_INVALID) { |
| actions.push_back((fdio_spawn_action_t){ |
| .action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY, |
| .ns = {.prefix = FSTAB[n].mount, .handle = h}, |
| }); |
| } |
| } break; |
| case FdioAction::CloneDir: |
| actions.push_back((fdio_spawn_action_t){ |
| .action = FDIO_SPAWN_ACTION_CLONE_DIR, |
| .dir = {.prefix = FSTAB[n].mount}, |
| }); |
| break; |
| default: |
| __UNREACHABLE; |
| } |
| } |
| |
| if (debuglog.is_valid()) { |
| actions.push_back((fdio_spawn_action_t){ |
| .action = FDIO_SPAWN_ACTION_ADD_HANDLE, |
| .h = {.id = PA_HND(PA_FD, FDIO_FLAG_USE_FOR_STDIO | 0), .handle = debuglog.release()}, |
| }); |
| } else { |
| actions.push_back((fdio_spawn_action_t){ |
| .action = FDIO_SPAWN_ACTION_TRANSFER_FD, |
| .fd = {.local_fd = stdiofd, .target_fd = FDIO_FLAG_USE_FOR_STDIO | 0}, |
| }); |
| } |
| |
| for (size_t i = 0; i < hcount; ++i) { |
| actions.push_back((fdio_spawn_action_t){ |
| .action = FDIO_SPAWN_ACTION_ADD_HANDLE, |
| .h = {.id = types[i], .handle = handles[i]}, |
| }); |
| } |
| |
| zx::process proc; |
| char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]; |
| if (executable.is_valid()) { |
| status = fdio_spawn_vmo(job_copy.get(), spawn_flags, executable.release(), argv, env.data(), |
| actions.size(), actions.data(), proc.reset_and_get_address(), err_msg); |
| } else { |
| status = fdio_spawn_etc(job_copy.get(), spawn_flags, argv[0], argv, env.data(), actions.size(), |
| actions.data(), proc.reset_and_get_address(), err_msg); |
| } |
| if (status != ZX_OK) { |
| FX_LOGS(ERROR) << "spawn " << argv[0] << " (" << name << ") failed: " << err_msg << ": " |
| << status; |
| return status; |
| } |
| FX_LOGS(INFO) << "launch " << argv[0] << " (" << name << ") OK"; |
| if (out_proc != nullptr) { |
| *out_proc = std::move(proc); |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t DevmgrLauncher::Launch(const zx::job& job, const char* name, const char* const* argv, |
| const char** initial_envp, int stdiofd, |
| const zx::resource& root_resource, const zx_handle_t* handles, |
| const uint32_t* types, size_t hcount, zx::process* out_proc, |
| uint32_t flags) { |
| return LaunchWithLoader(job, name, zx::vmo(), zx::channel(), argv, initial_envp, stdiofd, |
| root_resource, handles, types, hcount, out_proc, flags); |
| } |
| |
| ArgumentVector ArgumentVector::FromCmdline(const char* cmdline) { |
| ArgumentVector argv; |
| const size_t cmdline_len = strlen(cmdline) + 1; |
| argv.raw_bytes_.reset(new char[cmdline_len]); |
| memcpy(argv.raw_bytes_.get(), cmdline, cmdline_len); |
| |
| // Get the full commandline by splitting on '+'. |
| size_t argc = 0; |
| char* token; |
| char* rest = argv.raw_bytes_.get(); |
| while (argc < std::size(argv.argv_) && (token = strtok_r(rest, "+", &rest))) { |
| argv.argv_[argc++] = token; |
| } |
| argv.argv_[argc] = nullptr; |
| return argv; |
| } |
| |
| std::ostream& operator<<(std::ostream& stream, const ArgumentVector& arguments) { |
| const char* const* argv = arguments.argv(); |
| const char* prefix = "'"; |
| for (const char* arg = *argv; arg != nullptr; ++argv, arg = *argv) { |
| stream << prefix << *argv << "'"; |
| prefix = " '"; |
| } |
| return stream; |
| } |
| |
| } // namespace devmgr |