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