blob: 79ee18b5a08335f8be69bcdfaa52f79ec9ae5c3b [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2017 The Chromium Authors
scottmge5a1d492017-05-24 23:41:432// 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
Wez5c3c6f152018-06-09 18:24:027#include <lib/fdio/limits.h>
8#include <lib/fdio/namespace.h>
9#include <lib/fdio/spawn.h>
Wez82017b0e2018-07-09 17:21:1010#include <lib/zx/job.h>
Kevin Marshall65c26702017-09-25 18:21:4211#include <stdint.h>
Scott Graham3ba02bd2017-05-25 23:16:3912#include <unistd.h>
Scott Grahamfe0e9f462017-09-18 21:25:0413#include <zircon/processargs.h>
scottmge5a1d492017-05-24 23:41:4314
Peter Kasting134ef9af2024-12-28 02:30:0915#include <tuple>
16
scottmge5a1d492017-05-24 23:41:4317#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"
Wez4e861052023-07-12 12:18:5723#include "base/memory/ptr_util.h"
David Benjamin76ee79eb2019-03-15 17:02:0924#include "base/process/environment_internal.h"
Wez4e861052023-07-12 12:18:5725#include "base/scoped_generic.h"
26#include "base/threading/scoped_blocking_call.h"
Etienne Pierre-dorayfc7952f02025-06-06 00:04:3327#include "base/trace_event/trace_event.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];
Peter Kasting134ef9af2024-12-28 02:30:0945 if (pipe(pipe_fd) < 0) {
Scott Graham3ba02bd2017-05-25 23:16:3946 return false;
Peter Kasting134ef9af2024-12-28 02:30:0947 }
Wez78d12652017-08-29 23:22:4848 options.fds_to_remap.emplace_back(pipe_fd[1], STDOUT_FILENO);
Peter Kasting134ef9af2024-12-28 02:30:0949 if (include_stderr) {
Wez78d12652017-08-29 23:22:4850 options.fds_to_remap.emplace_back(pipe_fd[1], STDERR_FILENO);
Peter Kasting134ef9af2024-12-28 02:30:0951 }
Scott Graham3ba02bd2017-05-25 23:16:3952
Wez78d12652017-08-29 23:22:4853 Process process = LaunchProcess(cmd_line, options);
54 close(pipe_fd[1]);
55 if (!process.IsValid()) {
56 close(pipe_fd[0]);
Scott Graham3ba02bd2017-05-25 23:16:3957 return false;
58 }
59
60 output->clear();
61 for (;;) {
62 char buffer[256];
Wez78d12652017-08-29 23:22:4863 ssize_t bytes_read = read(pipe_fd[0], buffer, sizeof(buffer));
Peter Kasting134ef9af2024-12-28 02:30:0964 if (bytes_read <= 0) {
Scott Graham3ba02bd2017-05-25 23:16:3965 break;
Peter Kasting134ef9af2024-12-28 02:30:0966 }
Peter Kasting2f61c8b2022-07-19 23:43:4667 output->append(buffer, static_cast<size_t>(bytes_read));
Scott Graham3ba02bd2017-05-25 23:16:3968 }
Wez78d12652017-08-29 23:22:4869 close(pipe_fd[0]);
Scott Graham3ba02bd2017-05-25 23:16:3970
Gabriel Charette6836c0d52021-01-11 17:40:2671 // It is okay to allow this process to wait on the launched process as a
72 // process launched with GetAppOutput*() shouldn't wait back on the process
73 // that launched it.
74 internal::GetAppOutputScopedAllowBaseSyncPrimitives allow_wait;
Scott Graham3ba02bd2017-05-25 23:16:3975 return process.WaitForExit(exit_code);
76}
77
Wez0629d402018-06-06 00:26:4378fdio_spawn_action_t FdioSpawnAction(uint32_t action) {
79 fdio_spawn_action_t new_action = {};
80 new_action.action = action;
81 return new_action;
82}
Kevin Marshall2bd04552018-02-01 21:23:4583
Wez0629d402018-06-06 00:26:4384fdio_spawn_action_t FdioSpawnActionCloneFd(int local_fd, int target_fd) {
85 fdio_spawn_action_t action = FdioSpawnAction(FDIO_SPAWN_ACTION_CLONE_FD);
86 action.fd.local_fd = local_fd;
87 action.fd.target_fd = target_fd;
88 return action;
89}
Kevin Marshall2bd04552018-02-01 21:23:4590
Wez0629d402018-06-06 00:26:4391fdio_spawn_action_t FdioSpawnActionAddNamespaceEntry(const char* prefix,
92 zx_handle_t handle) {
93 fdio_spawn_action_t action = FdioSpawnAction(FDIO_SPAWN_ACTION_ADD_NS_ENTRY);
94 action.ns.prefix = prefix;
95 action.ns.handle = handle;
96 return action;
97}
98
99fdio_spawn_action_t FdioSpawnActionAddHandle(uint32_t id, zx_handle_t handle) {
100 fdio_spawn_action_t action = FdioSpawnAction(FDIO_SPAWN_ACTION_ADD_HANDLE);
101 action.h.id = id;
102 action.h.handle = handle;
103 return action;
104}
Kevin Marshall2bd04552018-02-01 21:23:45105
Sergey Ulanov53ab7dd2019-08-27 17:53:18106fdio_spawn_action_t FdioSpawnActionSetName(const char* name) {
107 fdio_spawn_action_t action = FdioSpawnAction(FDIO_SPAWN_ACTION_SET_NAME);
108 action.name.data = name;
109 return action;
110}
111
Scott Graham3ba02bd2017-05-25 23:16:39112} // namespace
113
Wez35e50b52018-12-01 01:52:44114// static
115uint32_t LaunchOptions::AddHandleToTransfer(
116 HandlesToTransferVector* handles_to_transfer,
117 zx_handle_t handle) {
Sharon Yang9fadcefd2020-07-07 23:49:19118 CHECK_LE(handles_to_transfer->size(), std::numeric_limits<uint16_t>::max())
119 << "Number of handles to transfer exceeds total allowed";
Peter Kasting2f61c8b2022-07-19 23:43:46120 auto handle_id =
121 static_cast<uint32_t>(PA_HND(PA_USER1, handles_to_transfer->size()));
Wez35e50b52018-12-01 01:52:44122 handles_to_transfer->push_back({handle_id, handle});
123 return handle_id;
124}
125
scottmge5a1d492017-05-24 23:41:43126Process LaunchProcess(const CommandLine& cmdline,
127 const LaunchOptions& options) {
128 return LaunchProcess(cmdline.argv(), options);
129}
130
131Process LaunchProcess(const std::vector<std::string>& argv,
132 const LaunchOptions& options) {
Wez0629d402018-06-06 00:26:43133 // fdio_spawn_etc() accepts an array of |fdio_spawn_action_t|, describing
134 // namespace entries, descriptors and handles to launch the child process
David Dorwin8a971d32f2022-02-09 16:37:21135 // with. |fdio_spawn_action_t| does not own any values assigned to its
136 // members, so strings assigned to members must be valid through the
137 // fdio_spawn_etc() call.
Wez0629d402018-06-06 00:26:43138 std::vector<fdio_spawn_action_t> spawn_actions;
139
140 // Handles to be transferred to the child are owned by this vector, so that
141 // they they are closed on early-exit, and can be release()d otherwise.
Kevin Marshallad910ae22018-06-16 05:40:53142 std::vector<zx::handle> transferred_handles;
Wez0629d402018-06-06 00:26:43143
144 // Add caller-supplied handles for transfer. We must do this first to ensure
145 // that the handles are consumed even if some later step fails.
146 for (const auto& id_and_handle : options.handles_to_transfer) {
147 spawn_actions.push_back(
148 FdioSpawnActionAddHandle(id_and_handle.id, id_and_handle.handle));
149 transferred_handles.emplace_back(id_and_handle.handle);
150 }
151
152 // Determine the job under which to launch the new process.
Wez82017b0e2018-07-09 17:21:10153 zx::unowned_job job = options.job_handle != ZX_HANDLE_INVALID
154 ? zx::unowned_job(options.job_handle)
155 : GetDefaultJob();
156 DCHECK(job->is_valid());
Wez0629d402018-06-06 00:26:43157
158 // Construct an |argv| array of C-strings from the supplied std::strings.
scottmge5a1d492017-05-24 23:41:43159 std::vector<const char*> argv_cstr;
160 argv_cstr.reserve(argv.size() + 1);
Peter Kasting134ef9af2024-12-28 02:30:09161 for (const auto& arg : argv) {
Scott Graham3ba02bd2017-05-25 23:16:39162 argv_cstr.push_back(arg.c_str());
Peter Kasting134ef9af2024-12-28 02:30:09163 }
scottmge5a1d492017-05-24 23:41:43164 argv_cstr.push_back(nullptr);
165
Wez9dd1de382022-04-27 16:20:43166 // If |environment| is set then it contains values to set/replace to create
167 // the new process' environment.
David Benjamin76ee79eb2019-03-15 17:02:09168 EnvironmentMap environ_modifications = options.environment;
Wez9dd1de382022-04-27 16:20:43169
170 // "PWD" is set in the new process' environment, to one of:
171 // 1. The value of |current_directory|, if set.
172 // 2. The value specified in |environment|, if any.
173 // 3. The current process' current working directory, if known.
Scott Grahamf63059692017-06-21 00:37:51174 if (!options.current_directory.empty()) {
175 environ_modifications["PWD"] = options.current_directory.value();
Wez9dd1de382022-04-27 16:20:43176 } else if (environ_modifications.find("PWD") == environ_modifications.end()) {
Sergey Ulanovfea2f072017-10-21 04:34:26177 FilePath cwd;
Wez9dd1de382022-04-27 16:20:43178 if (GetCurrentDirectory(&cwd)) {
179 environ_modifications["PWD"] = cwd.value();
180 }
Scott Grahamf63059692017-06-21 00:37:51181 }
182
Wez9dd1de382022-04-27 16:20:43183 // By default the calling process' environment is copied, and the collated
184 // modifications applied, to create the new process' environment. If
185 // |clear_environment| is set then only the collated modifications are used.
186 char* const kEmptyEnviron = nullptr;
187 char* const* old_environ =
188 options.clear_environment ? &kEmptyEnviron : environ;
Maria Kazinova112e7882024-06-10 16:04:36189 base::HeapArray<char*> new_environ =
Wez9dd1de382022-04-27 16:20:43190 internal::AlterEnvironment(old_environ, environ_modifications);
191
192 // Always clone the library loader service and UTC clock to new processes,
193 // in addition to any flags specified by the caller.
194 uint32_t spawn_flags = FDIO_SPAWN_DEFAULT_LDSVC | FDIO_SPAWN_CLONE_UTC_CLOCK |
195 options.spawn_flags;
Scott Grahamf63059692017-06-21 00:37:51196
Wez0629d402018-06-06 00:26:43197 // Add actions to clone handles for any specified paths into the new process'
198 // namespace.
Kevin Marshallad910ae22018-06-16 05:40:53199 if (!options.paths_to_clone.empty() || !options.paths_to_transfer.empty()) {
Sergey Ulanovf1b12ddf2018-11-27 02:56:58200 DCHECK((options.spawn_flags & FDIO_SPAWN_CLONE_NAMESPACE) == 0);
Wez0629d402018-06-06 00:26:43201 transferred_handles.reserve(transferred_handles.size() +
Kevin Marshallad910ae22018-06-16 05:40:53202 options.paths_to_clone.size() +
203 options.paths_to_transfer.size());
Wez0629d402018-06-06 00:26:43204
Kevin Marshallad910ae22018-06-16 05:40:53205 for (const auto& path_to_transfer : options.paths_to_transfer) {
206 zx::handle handle(path_to_transfer.handle);
Wez0629d402018-06-06 00:26:43207 spawn_actions.push_back(FdioSpawnActionAddNamespaceEntry(
Kevin Marshallad910ae22018-06-16 05:40:53208 path_to_transfer.path.value().c_str(), handle.get()));
Kevin Marshallad910ae22018-06-16 05:40:53209 transferred_handles.push_back(std::move(handle));
210 }
211
212 for (const auto& path_to_clone : options.paths_to_clone) {
Sergey Ulanov713b77772019-03-14 22:53:32213 fidl::InterfaceHandle<::fuchsia::io::Directory> directory =
Wez4e861052023-07-12 12:18:57214 base::OpenDirectoryHandle(path_to_clone);
Sergey Ulanov713b77772019-03-14 22:53:32215 if (!directory) {
Wez4e861052023-07-12 12:18:57216 LOG(WARNING) << "Could not open handle for path: " << path_to_clone;
Kevin Marshallad910ae22018-06-16 05:40:53217 return base::Process();
218 }
219
Sergey Ulanov713b77772019-03-14 22:53:32220 zx::handle handle = directory.TakeChannel();
221
Kevin Marshallad910ae22018-06-16 05:40:53222 spawn_actions.push_back(FdioSpawnActionAddNamespaceEntry(
Wez4e861052023-07-12 12:18:57223 path_to_clone.value().c_str(), handle.get()));
Wez0629d402018-06-06 00:26:43224 transferred_handles.push_back(std::move(handle));
Kevin Marshall2bd04552018-02-01 21:23:45225 }
226 }
227
Wez0629d402018-06-06 00:26:43228 // Add any file-descriptors to be cloned into the new process.
229 // Note that if FDIO_SPAWN_CLONE_STDIO is set, then any stdio entries in
230 // |fds_to_remap| will be used in place of the parent process' descriptors.
Wezec2506f2017-08-03 17:49:18231 for (const auto& src_target : options.fds_to_remap) {
Wez0629d402018-06-06 00:26:43232 spawn_actions.push_back(
233 FdioSpawnActionCloneFd(src_target.first, src_target.second));
Wezec2506f2017-08-03 17:49:18234 }
scottmge5a1d492017-05-24 23:41:43235
Sergey Ulanov53ab7dd2019-08-27 17:53:18236 // If |process_name_suffix| is specified then set process name as
237 // "<file_name><suffix>", otherwise leave the default value.
David Dorwin8a971d32f2022-02-09 16:37:21238 std::string process_name; // Must outlive the fdio_spawn_etc() call.
Sergey Ulanov53ab7dd2019-08-27 17:53:18239 if (!options.process_name_suffix.empty()) {
240 process_name = base::FilePath(argv[0]).BaseName().value() +
241 options.process_name_suffix;
242 spawn_actions.push_back(FdioSpawnActionSetName(process_name.c_str()));
243 }
244
Wez157707d62018-07-10 22:48:47245 zx::process process_handle;
Wez0629d402018-06-06 00:26:43246 // fdio_spawn_etc() will write a null-terminated scring to |error_message| in
247 // case of failure, so we avoid unnecessarily initializing it here.
248 char error_message[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
249 zx_status_t status = fdio_spawn_etc(
Wez82017b0e2018-07-09 17:21:10250 job->get(), spawn_flags, argv_cstr[0], argv_cstr.data(),
Maria Kazinova112e7882024-06-10 16:04:36251 new_environ.data(), spawn_actions.size(), spawn_actions.data(),
Wez157707d62018-07-10 22:48:47252 process_handle.reset_and_get_address(), error_message);
Wez1603c322017-08-10 05:24:54253
Wez0629d402018-06-06 00:26:43254 // fdio_spawn_etc() will close all handles specified in add-handle actions,
255 // regardless of whether it succeeds or fails, so release our copies.
Peter Kasting134ef9af2024-12-28 02:30:09256 for (auto& transferred_handle : transferred_handles) {
Avi Drissman933398e2022-01-22 00:55:42257 std::ignore = transferred_handle.release();
Peter Kasting134ef9af2024-12-28 02:30:09258 }
Wez0629d402018-06-06 00:26:43259
260 if (status != ZX_OK) {
261 ZX_LOG(ERROR, status) << "fdio_spawn: " << error_message;
scottmge5a1d492017-05-24 23:41:43262 return Process();
263 }
264
Wez0629d402018-06-06 00:26:43265 // Wrap the handle into a Process, and wait for it to terminate, if requested.
266 Process process(process_handle.release());
Wezc6b685d2018-01-09 17:27:42267 if (options.wait) {
268 status = zx_object_wait_one(process.Handle(), ZX_TASK_TERMINATED,
269 ZX_TIME_INFINITE, nullptr);
Wez0629d402018-06-06 00:26:43270 ZX_DCHECK(status == ZX_OK, status) << "zx_object_wait_one";
Wezc6b685d2018-01-09 17:27:42271 }
272
273 return process;
scottmge5a1d492017-05-24 23:41:43274}
275
Scott Graham3ba02bd2017-05-25 23:16:39276bool GetAppOutput(const CommandLine& cl, std::string* output) {
Scott Graham3ba02bd2017-05-25 23:16:39277 int exit_code;
Wez78d12652017-08-29 23:22:48278 bool result = GetAppOutputInternal(cl, false, output, &exit_code);
Scott Graham3ba02bd2017-05-25 23:16:39279 return result && exit_code == EXIT_SUCCESS;
280}
281
Wez78d12652017-08-29 23:22:48282bool GetAppOutput(const std::vector<std::string>& argv, std::string* output) {
283 return GetAppOutput(CommandLine(argv), output);
284}
285
Scott Graham3ba02bd2017-05-25 23:16:39286bool GetAppOutputAndError(const CommandLine& cl, std::string* output) {
Wez78d12652017-08-29 23:22:48287 int exit_code;
288 bool result = GetAppOutputInternal(cl, true, output, &exit_code);
289 return result && exit_code == EXIT_SUCCESS;
Scott Graham3ba02bd2017-05-25 23:16:39290}
291
292bool GetAppOutputAndError(const std::vector<std::string>& argv,
293 std::string* output) {
Wez78d12652017-08-29 23:22:48294 return GetAppOutputAndError(CommandLine(argv), output);
Scott Graham3ba02bd2017-05-25 23:16:39295}
296
297bool GetAppOutputWithExitCode(const CommandLine& cl,
298 std::string* output,
299 int* exit_code) {
Scott Graham0b0f7d72017-07-26 01:47:11300 // Contrary to GetAppOutput(), |true| return here means that the process was
301 // launched and the exit code was waited upon successfully, but not
302 // necessarily that the exit code was EXIT_SUCCESS.
Wez78d12652017-08-29 23:22:48303 return GetAppOutputInternal(cl, false, output, exit_code);
Scott Graham3ba02bd2017-05-25 23:16:39304}
305
Sergey Ulanova93cf8b2017-12-01 21:52:53306void RaiseProcessToHighPriority() {
307 // Fuchsia doesn't provide an API to change process priority.
308}
309
scottmge5a1d492017-05-24 23:41:43310} // namespace base