• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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