1// Copyright 2017 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 <errno.h> 6#include <fcntl.h> 7#include <stdarg.h> 8#include <stdbool.h> 9#include <stdio.h> 10#include <stdlib.h> 11#include <string.h> 12#include <sys/stat.h> 13#include <unistd.h> 14 15#include <fs-management/mount.h> 16#include <lib/fdio/io.h> 17#include <lib/fdio/spawn.h> 18#include <lib/fdio/util.h> 19#include <lib/zx/process.h> 20#include <zircon/compiler.h> 21#include <zircon/processargs.h> 22#include <zircon/status.h> 23#include <zircon/syscalls.h> 24 25namespace { 26 27void InitArgvAndActions(int argc, const char** argv, zx_handle_t* handles, 28 uint32_t* types, size_t len, 29 const char** null_terminated_argv_out, 30 fdio_spawn_action_t* actions_out) { 31 for (int i = 0; i < argc; ++i) { 32 null_terminated_argv_out[i] = argv[i]; 33 } 34 null_terminated_argv_out[argc] = nullptr; 35 36 for (size_t i = 0; i < len; ++i) { 37 actions_out[i].action = FDIO_SPAWN_ACTION_ADD_HANDLE; 38 actions_out[i].h.id = types[i]; 39 actions_out[i].h.handle = handles[i]; 40 } 41} 42 43constexpr size_t kMaxStdioActions = 1; 44 45enum class StdioType { 46 kLog, 47 kClone, 48 kNone, 49}; 50 51// Initializes Stdio. 52// 53// If necessary, updates the |actions| which will be sent to fdio_spawn. 54// |action_count| is an in/out parameter which may be increased if an action is 55// added. 56// |flags| is an in/out parameter which may be modified to alter the cloning of 57// STDIO. 58void InitStdio(StdioType stdio, fdio_spawn_action_t* actions, 59 size_t* action_count, uint32_t* flags) { 60 switch (stdio) { 61 case StdioType::kLog: 62 zx_handle_t h; 63 zx_debuglog_create(ZX_HANDLE_INVALID, 0, &h); 64 if (h != ZX_HANDLE_INVALID) { 65 actions[*action_count].action = FDIO_SPAWN_ACTION_ADD_HANDLE; 66 actions[*action_count].h.id = PA_HND(PA_FDIO_LOGGER, FDIO_FLAG_USE_FOR_STDIO); 67 actions[*action_count].h.handle = h; 68 *action_count += 1; 69 } 70 *flags &= ~FDIO_SPAWN_CLONE_STDIO; 71 break; 72 case StdioType::kClone: 73 *flags |= FDIO_SPAWN_CLONE_STDIO; 74 break; 75 case StdioType::kNone: 76 *flags &= ~FDIO_SPAWN_CLONE_STDIO; 77 break; 78 } 79} 80 81enum class ProcessAction { 82 kBlock, 83 kNonBlock, 84}; 85 86// Spawns a process. 87// 88// Optionally blocks, waiting for the process to terminate, depending 89// the value provided in |block|. 90zx_status_t Spawn(ProcessAction proc_action, uint32_t flags, const char** argv, 91 size_t action_count, const fdio_spawn_action_t* actions) { 92 zx::process proc; 93 char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]; 94 zx_status_t status = fdio_spawn_etc(ZX_HANDLE_INVALID, flags, argv[0], argv, nullptr, 95 action_count, actions, proc.reset_and_get_address(), 96 err_msg); 97 if (status != ZX_OK) { 98 fprintf(stderr, "fs-management: Cannot spawn %s: %d (%s): %s\n", 99 argv[0], status, zx_status_get_string(status), err_msg); 100 return status; 101 } 102 103 if (proc_action == ProcessAction::kBlock) { 104 status = proc.wait_one(ZX_PROCESS_TERMINATED, zx::time::infinite(), nullptr); 105 if (status != ZX_OK) { 106 fprintf(stderr, "spawn: Error waiting for process to terminate\n"); 107 return status; 108 } 109 110 zx_info_process_t info; 111 status = proc.get_info(ZX_INFO_PROCESS, &info, sizeof(info), nullptr, nullptr); 112 if (status != ZX_OK) { 113 fprintf(stderr, "spawn: Failed to get process info\n"); 114 return status; 115 } 116 117 if (!info.exited || info.return_code != 0) { 118 return ZX_ERR_BAD_STATE; 119 } 120 } 121 return ZX_OK; 122} 123 124zx_status_t Launch(StdioType stdio, ProcessAction proc_action, int argc, 125 const char** argv, zx_handle_t* handles, uint32_t* types, size_t len) { 126 const char* null_terminated_argv[argc + 1]; 127 fdio_spawn_action_t actions[len + kMaxStdioActions]; 128 InitArgvAndActions(argc, argv, handles, types, len, null_terminated_argv, actions); 129 130 size_t action_count = len; 131 uint32_t flags = FDIO_SPAWN_CLONE_ALL; 132 InitStdio(stdio, actions, &action_count, &flags); 133 134 return Spawn(proc_action, flags, null_terminated_argv, action_count, actions); 135} 136 137} // namespace 138 139zx_status_t launch_silent_sync(int argc, const char** argv, zx_handle_t* handles, 140 uint32_t* types, size_t len) { 141 return Launch(StdioType::kNone, ProcessAction::kBlock, argc, argv, handles, types, len); 142} 143 144zx_status_t launch_silent_async(int argc, const char** argv, zx_handle_t* handles, 145 uint32_t* types, size_t len) { 146 return Launch(StdioType::kNone, ProcessAction::kNonBlock, argc, argv, handles, types, len); 147} 148 149zx_status_t launch_stdio_sync(int argc, const char** argv, zx_handle_t* handles, 150 uint32_t* types, size_t len) { 151 return Launch(StdioType::kClone, ProcessAction::kBlock, argc, argv, handles, types, len); 152} 153 154zx_status_t launch_stdio_async(int argc, const char** argv, zx_handle_t* handles, 155 uint32_t* types, size_t len) { 156 return Launch(StdioType::kClone, ProcessAction::kNonBlock, argc, argv, handles, types, len); 157} 158 159zx_status_t launch_logs_async(int argc, const char** argv, zx_handle_t* handles, 160 uint32_t* types, size_t len) { 161 return Launch(StdioType::kLog, ProcessAction::kNonBlock, argc, argv, handles, types, len); 162} 163