1 // Copyright 2017 The Chromium Authors
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 "base/process/launch.h"
6
7 #include <tuple>
8
9 #include <lib/fdio/limits.h>
10 #include <lib/fdio/namespace.h>
11 #include <lib/fdio/spawn.h>
12 #include <lib/zx/job.h>
13 #include <stdint.h>
14 #include <unistd.h>
15 #include <zircon/processargs.h>
16
17 #include "base/command_line.h"
18 #include "base/files/file_util.h"
19 #include "base/fuchsia/default_job.h"
20 #include "base/fuchsia/file_utils.h"
21 #include "base/fuchsia/fuchsia_logging.h"
22 #include "base/logging.h"
23 #include "base/memory/ptr_util.h"
24 #include "base/process/environment_internal.h"
25 #include "base/scoped_generic.h"
26 #include "base/threading/scoped_blocking_call.h"
27 #include "base/trace_event/base_tracing.h"
28
29 namespace base {
30
31 namespace {
32
GetAppOutputInternal(const CommandLine & cmd_line,bool include_stderr,std::string * output,int * exit_code)33 bool GetAppOutputInternal(const CommandLine& cmd_line,
34 bool include_stderr,
35 std::string* output,
36 int* exit_code) {
37 DCHECK(exit_code);
38 TRACE_EVENT0("base", "GetAppOutput");
39
40 LaunchOptions options;
41
42 // LaunchProcess will automatically clone any stdio fd we do not explicitly
43 // map.
44 int pipe_fd[2];
45 if (pipe(pipe_fd) < 0)
46 return false;
47 options.fds_to_remap.emplace_back(pipe_fd[1], STDOUT_FILENO);
48 if (include_stderr)
49 options.fds_to_remap.emplace_back(pipe_fd[1], STDERR_FILENO);
50
51 Process process = LaunchProcess(cmd_line, options);
52 close(pipe_fd[1]);
53 if (!process.IsValid()) {
54 close(pipe_fd[0]);
55 return false;
56 }
57
58 output->clear();
59 for (;;) {
60 char buffer[256];
61 ssize_t bytes_read = read(pipe_fd[0], buffer, sizeof(buffer));
62 if (bytes_read <= 0)
63 break;
64 output->append(buffer, static_cast<size_t>(bytes_read));
65 }
66 close(pipe_fd[0]);
67
68 // It is okay to allow this process to wait on the launched process as a
69 // process launched with GetAppOutput*() shouldn't wait back on the process
70 // that launched it.
71 internal::GetAppOutputScopedAllowBaseSyncPrimitives allow_wait;
72 return process.WaitForExit(exit_code);
73 }
74
FdioSpawnAction(uint32_t action)75 fdio_spawn_action_t FdioSpawnAction(uint32_t action) {
76 fdio_spawn_action_t new_action = {};
77 new_action.action = action;
78 return new_action;
79 }
80
FdioSpawnActionCloneFd(int local_fd,int target_fd)81 fdio_spawn_action_t FdioSpawnActionCloneFd(int local_fd, int target_fd) {
82 fdio_spawn_action_t action = FdioSpawnAction(FDIO_SPAWN_ACTION_CLONE_FD);
83 action.fd.local_fd = local_fd;
84 action.fd.target_fd = target_fd;
85 return action;
86 }
87
FdioSpawnActionAddNamespaceEntry(const char * prefix,zx_handle_t handle)88 fdio_spawn_action_t FdioSpawnActionAddNamespaceEntry(const char* prefix,
89 zx_handle_t handle) {
90 fdio_spawn_action_t action = FdioSpawnAction(FDIO_SPAWN_ACTION_ADD_NS_ENTRY);
91 action.ns.prefix = prefix;
92 action.ns.handle = handle;
93 return action;
94 }
95
FdioSpawnActionAddHandle(uint32_t id,zx_handle_t handle)96 fdio_spawn_action_t FdioSpawnActionAddHandle(uint32_t id, zx_handle_t handle) {
97 fdio_spawn_action_t action = FdioSpawnAction(FDIO_SPAWN_ACTION_ADD_HANDLE);
98 action.h.id = id;
99 action.h.handle = handle;
100 return action;
101 }
102
FdioSpawnActionSetName(const char * name)103 fdio_spawn_action_t FdioSpawnActionSetName(const char* name) {
104 fdio_spawn_action_t action = FdioSpawnAction(FDIO_SPAWN_ACTION_SET_NAME);
105 action.name.data = name;
106 return action;
107 }
108
109 } // namespace
110
111 // static
AddHandleToTransfer(HandlesToTransferVector * handles_to_transfer,zx_handle_t handle)112 uint32_t LaunchOptions::AddHandleToTransfer(
113 HandlesToTransferVector* handles_to_transfer,
114 zx_handle_t handle) {
115 CHECK_LE(handles_to_transfer->size(), std::numeric_limits<uint16_t>::max())
116 << "Number of handles to transfer exceeds total allowed";
117 auto handle_id =
118 static_cast<uint32_t>(PA_HND(PA_USER1, handles_to_transfer->size()));
119 handles_to_transfer->push_back({handle_id, handle});
120 return handle_id;
121 }
122
LaunchProcess(const CommandLine & cmdline,const LaunchOptions & options)123 Process LaunchProcess(const CommandLine& cmdline,
124 const LaunchOptions& options) {
125 return LaunchProcess(cmdline.argv(), options);
126 }
127
LaunchProcess(const std::vector<std::string> & argv,const LaunchOptions & options)128 Process LaunchProcess(const std::vector<std::string>& argv,
129 const LaunchOptions& options) {
130 // fdio_spawn_etc() accepts an array of |fdio_spawn_action_t|, describing
131 // namespace entries, descriptors and handles to launch the child process
132 // with. |fdio_spawn_action_t| does not own any values assigned to its
133 // members, so strings assigned to members must be valid through the
134 // fdio_spawn_etc() call.
135 std::vector<fdio_spawn_action_t> spawn_actions;
136
137 // Handles to be transferred to the child are owned by this vector, so that
138 // they they are closed on early-exit, and can be release()d otherwise.
139 std::vector<zx::handle> transferred_handles;
140
141 // Add caller-supplied handles for transfer. We must do this first to ensure
142 // that the handles are consumed even if some later step fails.
143 for (const auto& id_and_handle : options.handles_to_transfer) {
144 spawn_actions.push_back(
145 FdioSpawnActionAddHandle(id_and_handle.id, id_and_handle.handle));
146 transferred_handles.emplace_back(id_and_handle.handle);
147 }
148
149 // Determine the job under which to launch the new process.
150 zx::unowned_job job = options.job_handle != ZX_HANDLE_INVALID
151 ? zx::unowned_job(options.job_handle)
152 : GetDefaultJob();
153 DCHECK(job->is_valid());
154
155 // Construct an |argv| array of C-strings from the supplied std::strings.
156 std::vector<const char*> argv_cstr;
157 argv_cstr.reserve(argv.size() + 1);
158 for (const auto& arg : argv)
159 argv_cstr.push_back(arg.c_str());
160 argv_cstr.push_back(nullptr);
161
162 // If |environment| is set then it contains values to set/replace to create
163 // the new process' environment.
164 EnvironmentMap environ_modifications = options.environment;
165
166 // "PWD" is set in the new process' environment, to one of:
167 // 1. The value of |current_directory|, if set.
168 // 2. The value specified in |environment|, if any.
169 // 3. The current process' current working directory, if known.
170 if (!options.current_directory.empty()) {
171 environ_modifications["PWD"] = options.current_directory.value();
172 } else if (environ_modifications.find("PWD") == environ_modifications.end()) {
173 FilePath cwd;
174 if (GetCurrentDirectory(&cwd)) {
175 environ_modifications["PWD"] = cwd.value();
176 }
177 }
178
179 // By default the calling process' environment is copied, and the collated
180 // modifications applied, to create the new process' environment. If
181 // |clear_environment| is set then only the collated modifications are used.
182 char* const kEmptyEnviron = nullptr;
183 char* const* old_environ =
184 options.clear_environment ? &kEmptyEnviron : environ;
185 std::unique_ptr<char*[]> new_environ =
186 internal::AlterEnvironment(old_environ, environ_modifications);
187
188 // Always clone the library loader service and UTC clock to new processes,
189 // in addition to any flags specified by the caller.
190 uint32_t spawn_flags = FDIO_SPAWN_DEFAULT_LDSVC | FDIO_SPAWN_CLONE_UTC_CLOCK |
191 options.spawn_flags;
192
193 // Add actions to clone handles for any specified paths into the new process'
194 // namespace.
195 if (!options.paths_to_clone.empty() || !options.paths_to_transfer.empty()) {
196 DCHECK((options.spawn_flags & FDIO_SPAWN_CLONE_NAMESPACE) == 0);
197 transferred_handles.reserve(transferred_handles.size() +
198 options.paths_to_clone.size() +
199 options.paths_to_transfer.size());
200
201 for (const auto& path_to_transfer : options.paths_to_transfer) {
202 zx::handle handle(path_to_transfer.handle);
203 spawn_actions.push_back(FdioSpawnActionAddNamespaceEntry(
204 path_to_transfer.path.value().c_str(), handle.get()));
205 transferred_handles.push_back(std::move(handle));
206 }
207
208 for (const auto& path_to_clone : options.paths_to_clone) {
209 fidl::InterfaceHandle<::fuchsia::io::Directory> directory =
210 base::OpenDirectoryHandle(path_to_clone);
211 if (!directory) {
212 LOG(WARNING) << "Could not open handle for path: " << path_to_clone;
213 return base::Process();
214 }
215
216 zx::handle handle = directory.TakeChannel();
217
218 spawn_actions.push_back(FdioSpawnActionAddNamespaceEntry(
219 path_to_clone.value().c_str(), handle.get()));
220 transferred_handles.push_back(std::move(handle));
221 }
222 }
223
224 // Add any file-descriptors to be cloned into the new process.
225 // Note that if FDIO_SPAWN_CLONE_STDIO is set, then any stdio entries in
226 // |fds_to_remap| will be used in place of the parent process' descriptors.
227 for (const auto& src_target : options.fds_to_remap) {
228 spawn_actions.push_back(
229 FdioSpawnActionCloneFd(src_target.first, src_target.second));
230 }
231
232 // If |process_name_suffix| is specified then set process name as
233 // "<file_name><suffix>", otherwise leave the default value.
234 std::string process_name; // Must outlive the fdio_spawn_etc() call.
235 if (!options.process_name_suffix.empty()) {
236 process_name = base::FilePath(argv[0]).BaseName().value() +
237 options.process_name_suffix;
238 spawn_actions.push_back(FdioSpawnActionSetName(process_name.c_str()));
239 }
240
241 zx::process process_handle;
242 // fdio_spawn_etc() will write a null-terminated scring to |error_message| in
243 // case of failure, so we avoid unnecessarily initializing it here.
244 char error_message[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
245 zx_status_t status = fdio_spawn_etc(
246 job->get(), spawn_flags, argv_cstr[0], argv_cstr.data(),
247 new_environ.get(), spawn_actions.size(), spawn_actions.data(),
248 process_handle.reset_and_get_address(), error_message);
249
250 // fdio_spawn_etc() will close all handles specified in add-handle actions,
251 // regardless of whether it succeeds or fails, so release our copies.
252 for (auto& transferred_handle : transferred_handles)
253 std::ignore = transferred_handle.release();
254
255 if (status != ZX_OK) {
256 ZX_LOG(ERROR, status) << "fdio_spawn: " << error_message;
257 return Process();
258 }
259
260 // Wrap the handle into a Process, and wait for it to terminate, if requested.
261 Process process(process_handle.release());
262 if (options.wait) {
263 status = zx_object_wait_one(process.Handle(), ZX_TASK_TERMINATED,
264 ZX_TIME_INFINITE, nullptr);
265 ZX_DCHECK(status == ZX_OK, status) << "zx_object_wait_one";
266 }
267
268 return process;
269 }
270
GetAppOutput(const CommandLine & cl,std::string * output)271 bool GetAppOutput(const CommandLine& cl, std::string* output) {
272 int exit_code;
273 bool result = GetAppOutputInternal(cl, false, output, &exit_code);
274 return result && exit_code == EXIT_SUCCESS;
275 }
276
GetAppOutput(const std::vector<std::string> & argv,std::string * output)277 bool GetAppOutput(const std::vector<std::string>& argv, std::string* output) {
278 return GetAppOutput(CommandLine(argv), output);
279 }
280
GetAppOutputAndError(const CommandLine & cl,std::string * output)281 bool GetAppOutputAndError(const CommandLine& cl, std::string* output) {
282 int exit_code;
283 bool result = GetAppOutputInternal(cl, true, output, &exit_code);
284 return result && exit_code == EXIT_SUCCESS;
285 }
286
GetAppOutputAndError(const std::vector<std::string> & argv,std::string * output)287 bool GetAppOutputAndError(const std::vector<std::string>& argv,
288 std::string* output) {
289 return GetAppOutputAndError(CommandLine(argv), output);
290 }
291
GetAppOutputWithExitCode(const CommandLine & cl,std::string * output,int * exit_code)292 bool GetAppOutputWithExitCode(const CommandLine& cl,
293 std::string* output,
294 int* exit_code) {
295 // Contrary to GetAppOutput(), |true| return here means that the process was
296 // launched and the exit code was waited upon successfully, but not
297 // necessarily that the exit code was EXIT_SUCCESS.
298 return GetAppOutputInternal(cl, false, output, exit_code);
299 }
300
RaiseProcessToHighPriority()301 void RaiseProcessToHighPriority() {
302 // Fuchsia doesn't provide an API to change process priority.
303 }
304
305 } // namespace base
306