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