| // Copyright 2017 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 <errno.h> |
| #include <fcntl.h> |
| #include <fuchsia/boot/llcpp/fidl.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/zx/debuglog.h> |
| #include <lib/zx/process.h> |
| #include <stdarg.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <zircon/compiler.h> |
| #include <zircon/processargs.h> |
| #include <zircon/status.h> |
| #include <zircon/syscalls.h> |
| |
| #include <fs-management/mount.h> |
| |
| namespace { |
| |
| namespace fboot = ::llcpp::fuchsia::boot; |
| |
| void InitArgvAndActions(zx_handle_t* handles, uint32_t* types, size_t len, |
| fdio_spawn_action_t* actions_out) { |
| for (size_t i = 0; i < len; ++i) { |
| actions_out[i].action = FDIO_SPAWN_ACTION_ADD_HANDLE; |
| actions_out[i].h.id = types[i]; |
| actions_out[i].h.handle = handles[i]; |
| } |
| } |
| |
| constexpr size_t kMaxStdioActions = 1; |
| |
| enum class StdioType { |
| kLog, |
| kClone, |
| kNone, |
| }; |
| |
| zx_handle_t RetriveWriteOnlyDebuglogHandle() { |
| zx::channel local, remote; |
| zx_status_t status = zx::channel::create(0, &local, &remote); |
| if (status != ZX_OK) { |
| fprintf(stderr, "fs-management: Cannot create channel: %d (%s)\n", status, |
| zx_status_get_string(status)); |
| return ZX_HANDLE_INVALID; |
| } |
| |
| std::string path = std::string("/svc/") + fboot::WriteOnlyLog::Name; |
| status = fdio_service_connect(path.c_str(), remote.release()); |
| if (status != ZX_OK) { |
| fprintf(stderr, "fs-management: Failed to connect to WriteOnlyLog: %d (%s)\n", status, |
| zx_status_get_string(status)); |
| return ZX_HANDLE_INVALID; |
| } |
| |
| auto resp = fboot::WriteOnlyLog::Call::Get(zx::unowned(local)); |
| if (!resp.ok()) { |
| fprintf(stderr, "fs-management: WriteOnlyLogGet failed: %d (%s)\n", resp.status(), |
| zx_status_get_string(resp.status())); |
| return ZX_HANDLE_INVALID; |
| } |
| |
| return resp.value().log.release(); |
| } |
| |
| // Initializes Stdio. |
| // |
| // If necessary, updates the |actions| which will be sent to fdio_spawn. |
| // |action_count| is an in/out parameter which may be increased if an action is |
| // added. |
| // |flags| is an in/out parameter which may be modified to alter the cloning of |
| // STDIO. |
| void InitStdio(StdioType stdio, fdio_spawn_action_t* actions, size_t* action_count, |
| uint32_t* flags) { |
| switch (stdio) { |
| case StdioType::kLog: { |
| zx_handle_t h = RetriveWriteOnlyDebuglogHandle(); |
| if (h != ZX_HANDLE_INVALID) { |
| actions[*action_count].action = FDIO_SPAWN_ACTION_ADD_HANDLE; |
| actions[*action_count].h.id = PA_HND(PA_FD, FDIO_FLAG_USE_FOR_STDIO); |
| actions[*action_count].h.handle = h; |
| *action_count += 1; |
| } |
| *flags &= ~FDIO_SPAWN_CLONE_STDIO; |
| break; |
| } |
| case StdioType::kClone: |
| *flags |= FDIO_SPAWN_CLONE_STDIO; |
| break; |
| case StdioType::kNone: |
| *flags &= ~FDIO_SPAWN_CLONE_STDIO; |
| break; |
| } |
| } |
| |
| enum class ProcessAction { |
| kBlock, |
| kNonBlock, |
| }; |
| |
| // Spawns a process. |
| // |
| // Optionally blocks, waiting for the process to terminate, depending |
| // the value provided in |block|. |
| zx_status_t Spawn(ProcessAction proc_action, uint32_t flags, const char** argv, size_t action_count, |
| const fdio_spawn_action_t* actions) { |
| zx::process proc; |
| char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]; |
| zx_status_t status = fdio_spawn_etc(ZX_HANDLE_INVALID, flags, argv[0], argv, nullptr, |
| action_count, actions, proc.reset_and_get_address(), err_msg); |
| if (status != ZX_OK) { |
| fprintf(stderr, "fs-management: Cannot spawn %s: %d (%s): %s\n", argv[0], status, |
| zx_status_get_string(status), err_msg); |
| return status; |
| } |
| |
| if (proc_action == ProcessAction::kBlock) { |
| status = proc.wait_one(ZX_PROCESS_TERMINATED, zx::time::infinite(), nullptr); |
| if (status != ZX_OK) { |
| fprintf(stderr, "spawn: Error waiting for process to terminate\n"); |
| return status; |
| } |
| |
| zx_info_process_t info; |
| status = proc.get_info(ZX_INFO_PROCESS, &info, sizeof(info), nullptr, nullptr); |
| if (status != ZX_OK) { |
| fprintf(stderr, "spawn: Failed to get process info\n"); |
| return status; |
| } |
| |
| if (!info.exited || info.return_code != 0) { |
| return ZX_ERR_BAD_STATE; |
| } |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t Launch(StdioType stdio, ProcessAction proc_action, int argc, const char** argv, |
| zx_handle_t* handles, uint32_t* types, size_t len) { |
| fdio_spawn_action_t actions[len + kMaxStdioActions]; |
| InitArgvAndActions(handles, types, len, actions); |
| |
| size_t action_count = len; |
| uint32_t flags = FDIO_SPAWN_CLONE_ALL; |
| InitStdio(stdio, actions, &action_count, &flags); |
| |
| return Spawn(proc_action, flags, argv, action_count, actions); |
| } |
| |
| } // namespace |
| |
| __EXPORT |
| zx_status_t launch_silent_sync(int argc, const char** argv, zx_handle_t* handles, uint32_t* types, |
| size_t len) { |
| return Launch(StdioType::kNone, ProcessAction::kBlock, argc, argv, handles, types, len); |
| } |
| |
| __EXPORT |
| zx_status_t launch_silent_async(int argc, const char** argv, zx_handle_t* handles, uint32_t* types, |
| size_t len) { |
| return Launch(StdioType::kNone, ProcessAction::kNonBlock, argc, argv, handles, types, len); |
| } |
| |
| __EXPORT |
| zx_status_t launch_stdio_sync(int argc, const char** argv, zx_handle_t* handles, uint32_t* types, |
| size_t len) { |
| return Launch(StdioType::kClone, ProcessAction::kBlock, argc, argv, handles, types, len); |
| } |
| |
| __EXPORT |
| zx_status_t launch_stdio_async(int argc, const char** argv, zx_handle_t* handles, uint32_t* types, |
| size_t len) { |
| return Launch(StdioType::kClone, ProcessAction::kNonBlock, argc, argv, handles, types, len); |
| } |
| |
| __EXPORT |
| zx_status_t launch_logs_async(int argc, const char** argv, zx_handle_t* handles, uint32_t* types, |
| size_t len) { |
| return Launch(StdioType::kLog, ProcessAction::kNonBlock, argc, argv, handles, types, len); |
| } |