blob: b4c9128933c7dcbb005f544fc790c572b1ff3a9b [file] [log] [blame]
scottmge5a1d492017-05-24 23:41:431// Copyright 2017 The Chromium 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 "base/process/launch.h"
6
Avi Drissman933398e2022-01-22 00:55:427#include <tuple>
8
Wez5c3c6f152018-06-09 18:24:029#include <lib/fdio/limits.h>
10#include <lib/fdio/namespace.h>
11#include <lib/fdio/spawn.h>
Wez82017b0e2018-07-09 17:21:1012#include <lib/zx/job.h>
Kevin Marshall65c26702017-09-25 18:21:4213#include <stdint.h>
Scott Graham3ba02bd2017-05-25 23:16:3914#include <unistd.h>
Scott Grahamfe0e9f462017-09-18 21:25:0415#include <zircon/processargs.h>
scottmge5a1d492017-05-24 23:41:4316
17#include "base/command_line.h"
Sergey Ulanovfea2f072017-10-21 04:34:2618#include "base/files/file_util.h"
Kevin Marshalla6c7a4112017-08-25 23:39:1219#include "base/fuchsia/default_job.h"
Kevin Marshallad910ae22018-06-16 05:40:5320#include "base/fuchsia/file_utils.h"
Wezeebd36b2018-03-28 18:24:0321#include "base/fuchsia/fuchsia_logging.h"
Scott Graham3ba02bd2017-05-25 23:16:3922#include "base/logging.h"
Kevin Marshall2bd04552018-02-01 21:23:4523#include "base/memory/ptr_util.h"
David Benjamin76ee79eb2019-03-15 17:02:0924#include "base/process/environment_internal.h"
Kevin Marshall2bd04552018-02-01 21:23:4525#include "base/scoped_generic.h"
Gabriel Charette6836c0d52021-01-11 17:40:2626#include "base/threading/scoped_blocking_call.h"
27#include "base/trace_event/base_tracing.h"
scottmge5a1d492017-05-24 23:41:4328
29namespace base {
30
Scott Graham3ba02bd2017-05-25 23:16:3931namespace {
32
Wez78d12652017-08-29 23:22:4833bool GetAppOutputInternal(const CommandLine& cmd_line,
Scott Graham3ba02bd2017-05-25 23:16:3934 bool include_stderr,
35 std::string* output,
36 int* exit_code) {
37 DCHECK(exit_code);
Gabriel Charette6836c0d52021-01-11 17:40:2638 TRACE_EVENT0("base", "GetAppOutput");
Scott Graham3ba02bd2017-05-25 23:16:3939
Wez78d12652017-08-29 23:22:4840 LaunchOptions options;
Scott Graham3ba02bd2017-05-25 23:16:3941
Wez78d12652017-08-29 23:22:4842 // LaunchProcess will automatically clone any stdio fd we do not explicitly
43 // map.
44 int pipe_fd[2];
45 if (pipe(pipe_fd) < 0)
Scott Graham3ba02bd2017-05-25 23:16:3946 return false;
Wez78d12652017-08-29 23:22:4847 options.fds_to_remap.emplace_back(pipe_fd[1], STDOUT_FILENO);
Scott Graham3ba02bd2017-05-25 23:16:3948 if (include_stderr)
Wez78d12652017-08-29 23:22:4849 options.fds_to_remap.emplace_back(pipe_fd[1], STDERR_FILENO);
Scott Graham3ba02bd2017-05-25 23:16:3950
Wez78d12652017-08-29 23:22:4851 Process process = LaunchProcess(cmd_line, options);
52 close(pipe_fd[1]);
53 if (!process.IsValid()) {
54 close(pipe_fd[0]);
Scott Graham3ba02bd2017-05-25 23:16:3955 return false;
56 }
57
58 output->clear();
59 for (;;) {
60 char buffer[256];
Wez78d12652017-08-29 23:22:4861 ssize_t bytes_read = read(pipe_fd[0], buffer, sizeof(buffer));
Scott Graham3ba02bd2017-05-25 23:16:3962 if (bytes_read <= 0)
63 break;
64 output->append(buffer, bytes_read);
65 }
Wez78d12652017-08-29 23:22:4866 close(pipe_fd[0]);
Scott Graham3ba02bd2017-05-25 23:16:3967
Gabriel Charette6836c0d52021-01-11 17:40:2668 // 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;
Scott Graham3ba02bd2017-05-25 23:16:3972 return process.WaitForExit(exit_code);
73}
74
Wez0629d402018-06-06 00:26:4375fdio_spawn_action_t FdioSpawnAction(uint32_t action) {
76 fdio_spawn_action_t new_action = {};
77 new_action.action = action;
78 return new_action;
79}
Kevin Marshall2bd04552018-02-01 21:23:4580
Wez0629d402018-06-06 00:26:4381fdio_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}
Kevin Marshall2bd04552018-02-01 21:23:4587
Wez0629d402018-06-06 00:26:4388fdio_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
96fdio_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}
Kevin Marshall2bd04552018-02-01 21:23:45102
Sergey Ulanov53ab7dd2019-08-27 17:53:18103fdio_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
Scott Graham3ba02bd2017-05-25 23:16:39109} // namespace
110
Wez35e50b52018-12-01 01:52:44111// static
112uint32_t LaunchOptions::AddHandleToTransfer(
113 HandlesToTransferVector* handles_to_transfer,
114 zx_handle_t handle) {
Sharon Yang9fadcefd2020-07-07 23:49:19115 CHECK_LE(handles_to_transfer->size(), std::numeric_limits<uint16_t>::max())
116 << "Number of handles to transfer exceeds total allowed";
Wez35e50b52018-12-01 01:52:44117 uint32_t handle_id = PA_HND(PA_USER1, handles_to_transfer->size());
118 handles_to_transfer->push_back({handle_id, handle});
119 return handle_id;
120}
121
scottmge5a1d492017-05-24 23:41:43122Process LaunchProcess(const CommandLine& cmdline,
123 const LaunchOptions& options) {
124 return LaunchProcess(cmdline.argv(), options);
125}
126
127Process LaunchProcess(const std::vector<std::string>& argv,
128 const LaunchOptions& options) {
Wez0629d402018-06-06 00:26:43129 // fdio_spawn_etc() accepts an array of |fdio_spawn_action_t|, describing
130 // namespace entries, descriptors and handles to launch the child process
David Dorwin8a971d32f2022-02-09 16:37:21131 // with. |fdio_spawn_action_t| does not own any values assigned to its
132 // members, so strings assigned to members must be valid through the
133 // fdio_spawn_etc() call.
Wez0629d402018-06-06 00:26:43134 std::vector<fdio_spawn_action_t> spawn_actions;
135
136 // Handles to be transferred to the child are owned by this vector, so that
137 // they they are closed on early-exit, and can be release()d otherwise.
Kevin Marshallad910ae22018-06-16 05:40:53138 std::vector<zx::handle> transferred_handles;
Wez0629d402018-06-06 00:26:43139
140 // Add caller-supplied handles for transfer. We must do this first to ensure
141 // that the handles are consumed even if some later step fails.
142 for (const auto& id_and_handle : options.handles_to_transfer) {
143 spawn_actions.push_back(
144 FdioSpawnActionAddHandle(id_and_handle.id, id_and_handle.handle));
145 transferred_handles.emplace_back(id_and_handle.handle);
146 }
147
148 // Determine the job under which to launch the new process.
Wez82017b0e2018-07-09 17:21:10149 zx::unowned_job job = options.job_handle != ZX_HANDLE_INVALID
150 ? zx::unowned_job(options.job_handle)
151 : GetDefaultJob();
152 DCHECK(job->is_valid());
Wez0629d402018-06-06 00:26:43153
154 // Construct an |argv| array of C-strings from the supplied std::strings.
scottmge5a1d492017-05-24 23:41:43155 std::vector<const char*> argv_cstr;
156 argv_cstr.reserve(argv.size() + 1);
Scott Graham3ba02bd2017-05-25 23:16:39157 for (const auto& arg : argv)
158 argv_cstr.push_back(arg.c_str());
scottmge5a1d492017-05-24 23:41:43159 argv_cstr.push_back(nullptr);
160
David Benjamin76ee79eb2019-03-15 17:02:09161 // Determine the environment to pass to the new process. If
162 // |clear_environment|, |environment| or |current_directory| are set then we
Wez0629d402018-06-06 00:26:43163 // construct a new (possibly empty) environment, otherwise we let fdio_spawn()
164 // clone the caller's environment into the new process.
Adam Lesinski3a725722020-10-01 14:40:36165 uint32_t spawn_flags = FDIO_SPAWN_DEFAULT_LDSVC | FDIO_SPAWN_CLONE_UTC_CLOCK |
166 options.spawn_flags;
Scott Grahamf63059692017-06-21 00:37:51167
David Benjamin76ee79eb2019-03-15 17:02:09168 EnvironmentMap environ_modifications = options.environment;
Scott Grahamf63059692017-06-21 00:37:51169 if (!options.current_directory.empty()) {
170 environ_modifications["PWD"] = options.current_directory.value();
Sergey Ulanovfea2f072017-10-21 04:34:26171 } else {
172 FilePath cwd;
Kevin Marshallc948f0f2018-05-14 03:34:45173 GetCurrentDirectory(&cwd);
Sergey Ulanovfea2f072017-10-21 04:34:26174 environ_modifications["PWD"] = cwd.value();
Scott Grahamf63059692017-06-21 00:37:51175 }
176
Fabrice de Gans-Riberiee44a3b2020-10-10 00:21:13177 std::unique_ptr<char*[]> new_environ;
Wez0629d402018-06-06 00:26:43178 if (!environ_modifications.empty()) {
179 char* const empty_environ = nullptr;
David Benjamin76ee79eb2019-03-15 17:02:09180 char* const* old_environ =
181 options.clear_environment ? &empty_environ : environ;
182 new_environ =
183 internal::AlterEnvironment(old_environ, environ_modifications);
184 } else if (!options.clear_environment) {
Wez0629d402018-06-06 00:26:43185 spawn_flags |= FDIO_SPAWN_CLONE_ENVIRON;
186 }
Scott Grahamf63059692017-06-21 00:37:51187
Wez0629d402018-06-06 00:26:43188 // Add actions to clone handles for any specified paths into the new process'
189 // namespace.
Kevin Marshallad910ae22018-06-16 05:40:53190 if (!options.paths_to_clone.empty() || !options.paths_to_transfer.empty()) {
Sergey Ulanovf1b12ddf2018-11-27 02:56:58191 DCHECK((options.spawn_flags & FDIO_SPAWN_CLONE_NAMESPACE) == 0);
Wez0629d402018-06-06 00:26:43192 transferred_handles.reserve(transferred_handles.size() +
Kevin Marshallad910ae22018-06-16 05:40:53193 options.paths_to_clone.size() +
194 options.paths_to_transfer.size());
Wez0629d402018-06-06 00:26:43195
Kevin Marshallad910ae22018-06-16 05:40:53196 for (const auto& path_to_transfer : options.paths_to_transfer) {
197 zx::handle handle(path_to_transfer.handle);
Wez0629d402018-06-06 00:26:43198 spawn_actions.push_back(FdioSpawnActionAddNamespaceEntry(
Kevin Marshallad910ae22018-06-16 05:40:53199 path_to_transfer.path.value().c_str(), handle.get()));
Kevin Marshallad910ae22018-06-16 05:40:53200 transferred_handles.push_back(std::move(handle));
201 }
202
203 for (const auto& path_to_clone : options.paths_to_clone) {
Sergey Ulanov713b77772019-03-14 22:53:32204 fidl::InterfaceHandle<::fuchsia::io::Directory> directory =
Fabrice de Gans-Riberiee44a3b2020-10-10 00:21:13205 base::OpenDirectoryHandle(path_to_clone);
Sergey Ulanov713b77772019-03-14 22:53:32206 if (!directory) {
Kevin Marshallad910ae22018-06-16 05:40:53207 LOG(WARNING) << "Could not open handle for path: " << path_to_clone;
208 return base::Process();
209 }
210
Sergey Ulanov713b77772019-03-14 22:53:32211 zx::handle handle = directory.TakeChannel();
212
Kevin Marshallad910ae22018-06-16 05:40:53213 spawn_actions.push_back(FdioSpawnActionAddNamespaceEntry(
214 path_to_clone.value().c_str(), handle.get()));
Wez0629d402018-06-06 00:26:43215 transferred_handles.push_back(std::move(handle));
Kevin Marshall2bd04552018-02-01 21:23:45216 }
217 }
218
Wez0629d402018-06-06 00:26:43219 // Add any file-descriptors to be cloned into the new process.
220 // Note that if FDIO_SPAWN_CLONE_STDIO is set, then any stdio entries in
221 // |fds_to_remap| will be used in place of the parent process' descriptors.
Wezec2506f2017-08-03 17:49:18222 for (const auto& src_target : options.fds_to_remap) {
Wez0629d402018-06-06 00:26:43223 spawn_actions.push_back(
224 FdioSpawnActionCloneFd(src_target.first, src_target.second));
Wezec2506f2017-08-03 17:49:18225 }
scottmge5a1d492017-05-24 23:41:43226
Sergey Ulanov53ab7dd2019-08-27 17:53:18227 // If |process_name_suffix| is specified then set process name as
228 // "<file_name><suffix>", otherwise leave the default value.
David Dorwin8a971d32f2022-02-09 16:37:21229 std::string process_name; // Must outlive the fdio_spawn_etc() call.
Sergey Ulanov53ab7dd2019-08-27 17:53:18230 if (!options.process_name_suffix.empty()) {
231 process_name = base::FilePath(argv[0]).BaseName().value() +
232 options.process_name_suffix;
233 spawn_actions.push_back(FdioSpawnActionSetName(process_name.c_str()));
234 }
235
Wez157707d62018-07-10 22:48:47236 zx::process process_handle;
Wez0629d402018-06-06 00:26:43237 // fdio_spawn_etc() will write a null-terminated scring to |error_message| in
238 // case of failure, so we avoid unnecessarily initializing it here.
239 char error_message[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
240 zx_status_t status = fdio_spawn_etc(
Wez82017b0e2018-07-09 17:21:10241 job->get(), spawn_flags, argv_cstr[0], argv_cstr.data(),
242 new_environ.get(), spawn_actions.size(), spawn_actions.data(),
Wez157707d62018-07-10 22:48:47243 process_handle.reset_and_get_address(), error_message);
Wez1603c322017-08-10 05:24:54244
Wez0629d402018-06-06 00:26:43245 // fdio_spawn_etc() will close all handles specified in add-handle actions,
246 // regardless of whether it succeeds or fails, so release our copies.
247 for (auto& transferred_handle : transferred_handles)
Avi Drissman933398e2022-01-22 00:55:42248 std::ignore = transferred_handle.release();
Wez0629d402018-06-06 00:26:43249
250 if (status != ZX_OK) {
251 ZX_LOG(ERROR, status) << "fdio_spawn: " << error_message;
scottmge5a1d492017-05-24 23:41:43252 return Process();
253 }
254
Wez0629d402018-06-06 00:26:43255 // Wrap the handle into a Process, and wait for it to terminate, if requested.
256 Process process(process_handle.release());
Wezc6b685d2018-01-09 17:27:42257 if (options.wait) {
258 status = zx_object_wait_one(process.Handle(), ZX_TASK_TERMINATED,
259 ZX_TIME_INFINITE, nullptr);
Wez0629d402018-06-06 00:26:43260 ZX_DCHECK(status == ZX_OK, status) << "zx_object_wait_one";
Wezc6b685d2018-01-09 17:27:42261 }
262
263 return process;
scottmge5a1d492017-05-24 23:41:43264}
265
Scott Graham3ba02bd2017-05-25 23:16:39266bool GetAppOutput(const CommandLine& cl, std::string* output) {
Scott Graham3ba02bd2017-05-25 23:16:39267 int exit_code;
Wez78d12652017-08-29 23:22:48268 bool result = GetAppOutputInternal(cl, false, output, &exit_code);
Scott Graham3ba02bd2017-05-25 23:16:39269 return result && exit_code == EXIT_SUCCESS;
270}
271
Wez78d12652017-08-29 23:22:48272bool GetAppOutput(const std::vector<std::string>& argv, std::string* output) {
273 return GetAppOutput(CommandLine(argv), output);
274}
275
Scott Graham3ba02bd2017-05-25 23:16:39276bool GetAppOutputAndError(const CommandLine& cl, std::string* output) {
Wez78d12652017-08-29 23:22:48277 int exit_code;
278 bool result = GetAppOutputInternal(cl, true, output, &exit_code);
279 return result && exit_code == EXIT_SUCCESS;
Scott Graham3ba02bd2017-05-25 23:16:39280}
281
282bool GetAppOutputAndError(const std::vector<std::string>& argv,
283 std::string* output) {
Wez78d12652017-08-29 23:22:48284 return GetAppOutputAndError(CommandLine(argv), output);
Scott Graham3ba02bd2017-05-25 23:16:39285}
286
287bool GetAppOutputWithExitCode(const CommandLine& cl,
288 std::string* output,
289 int* exit_code) {
Scott Graham0b0f7d72017-07-26 01:47:11290 // Contrary to GetAppOutput(), |true| return here means that the process was
291 // launched and the exit code was waited upon successfully, but not
292 // necessarily that the exit code was EXIT_SUCCESS.
Wez78d12652017-08-29 23:22:48293 return GetAppOutputInternal(cl, false, output, exit_code);
Scott Graham3ba02bd2017-05-25 23:16:39294}
295
Sergey Ulanova93cf8b2017-12-01 21:52:53296void RaiseProcessToHighPriority() {
297 // Fuchsia doesn't provide an API to change process priority.
298}
299
scottmge5a1d492017-05-24 23:41:43300} // namespace base