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 
25 namespace {
26 
InitArgvAndActions(int argc,const char ** argv,zx_handle_t * handles,uint32_t * types,size_t len,const char ** null_terminated_argv_out,fdio_spawn_action_t * actions_out)27 void 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 
43 constexpr size_t kMaxStdioActions = 1;
44 
45 enum 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.
InitStdio(StdioType stdio,fdio_spawn_action_t * actions,size_t * action_count,uint32_t * flags)58 void 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 
81 enum 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|.
Spawn(ProcessAction proc_action,uint32_t flags,const char ** argv,size_t action_count,const fdio_spawn_action_t * actions)90 zx_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 
Launch(StdioType stdio,ProcessAction proc_action,int argc,const char ** argv,zx_handle_t * handles,uint32_t * types,size_t len)124 zx_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 
launch_silent_sync(int argc,const char ** argv,zx_handle_t * handles,uint32_t * types,size_t len)139 zx_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 
launch_silent_async(int argc,const char ** argv,zx_handle_t * handles,uint32_t * types,size_t len)144 zx_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 
launch_stdio_sync(int argc,const char ** argv,zx_handle_t * handles,uint32_t * types,size_t len)149 zx_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 
launch_stdio_async(int argc,const char ** argv,zx_handle_t * handles,uint32_t * types,size_t len)154 zx_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 
launch_logs_async(int argc,const char ** argv,zx_handle_t * handles,uint32_t * types,size_t len)159 zx_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