| // 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/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 fshost { |
| |
| #define CHILD_JOB_RIGHTS (ZX_RIGHTS_BASIC | ZX_RIGHT_MANAGE_JOB | ZX_RIGHT_MANAGE_PROCESS) |
| |
| zx_status_t 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) { |
| 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); |
| } |
| while (initial_envp && initial_envp[0]) { |
| env.push_back(*initial_envp++); |
| } |
| env.push_back(nullptr); |
| |
| fbl::Vector<fdio_spawn_action_t> actions; |
| actions.reserve(4 + hcount); |
| |
| actions.push_back((fdio_spawn_action_t){ |
| .action = FDIO_SPAWN_ACTION_SET_NAME, |
| .name = {.data = name}, |
| }); |
| |
| spawn_flags |= FDIO_SPAWN_DEFAULT_LDSVC; |
| |
| actions.push_back((fdio_spawn_action_t){ |
| .action = FDIO_SPAWN_ACTION_CLONE_DIR, |
| .dir = {.prefix = "/svc"}, |
| }); |
| |
| 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]; |
| FX_LOGS(INFO) << "launching " << argv[0] << " (" << name << ")..."; |
| 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; |
| } |
| |
| 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 fshost |