1 | // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include "platform/globals.h" |
6 | #if defined(HOST_OS_FUCHSIA) |
7 | |
8 | #include "bin/process.h" |
9 | |
10 | #include <errno.h> |
11 | #include <fcntl.h> |
12 | #include <lib/fdio/io.h> |
13 | #include <lib/fdio/namespace.h> |
14 | #include <lib/fdio/spawn.h> |
15 | #include <poll.h> |
16 | #include <pthread.h> |
17 | #include <stdbool.h> |
18 | #include <stdio.h> |
19 | #include <stdlib.h> |
20 | #include <string.h> |
21 | #include <unistd.h> |
22 | #include <zircon/process.h> |
23 | #include <zircon/processargs.h> |
24 | #include <zircon/status.h> |
25 | #include <zircon/syscalls.h> |
26 | #include <zircon/syscalls/object.h> |
27 | #include <zircon/types.h> |
28 | |
29 | #include "bin/dartutils.h" |
30 | #include "bin/eventhandler.h" |
31 | #include "bin/fdutils.h" |
32 | #include "bin/file.h" |
33 | #include "bin/lockers.h" |
34 | #include "bin/namespace.h" |
35 | #include "bin/namespace_fuchsia.h" |
36 | #include "platform/signal_blocker.h" |
37 | #include "platform/syslog.h" |
38 | #include "platform/utils.h" |
39 | |
40 | // #define PROCESS_LOGGING 1 |
41 | #if defined(PROCESS_LOGGING) |
42 | #define LOG_ERR(msg, ...) Syslog::PrintErr("Dart Process: " msg, ##__VA_ARGS__) |
43 | #define LOG_INFO(msg, ...) Syslog::Print("Dart Process: " msg, ##__VA_ARGS__) |
44 | #else |
45 | #define LOG_ERR(msg, ...) |
46 | #define LOG_INFO(msg, ...) |
47 | #endif // defined(PROCESS_LOGGING) |
48 | |
49 | namespace dart { |
50 | namespace bin { |
51 | |
52 | int Process::global_exit_code_ = 0; |
53 | Mutex* Process::global_exit_code_mutex_ = nullptr; |
54 | Process::ExitHook Process::exit_hook_ = NULL; |
55 | |
56 | // ProcessInfo is used to map a process id to the file descriptor for |
57 | // the pipe used to communicate the exit code of the process to Dart. |
58 | // ProcessInfo objects are kept in the static singly-linked |
59 | // ProcessInfoList. |
60 | class ProcessInfo { |
61 | public: |
62 | ProcessInfo(zx_handle_t process, intptr_t fd) |
63 | : process_(process), exit_pipe_fd_(fd) {} |
64 | ~ProcessInfo() { |
65 | int closed = NO_RETRY_EXPECTED(close(exit_pipe_fd_)); |
66 | if (closed != 0) { |
67 | LOG_ERR("Failed to close process exit code pipe" ); |
68 | } |
69 | zx_handle_close(process_); |
70 | } |
71 | zx_handle_t process() const { return process_; } |
72 | intptr_t exit_pipe_fd() const { return exit_pipe_fd_; } |
73 | ProcessInfo* next() const { return next_; } |
74 | void set_next(ProcessInfo* info) { next_ = info; } |
75 | |
76 | private: |
77 | zx_handle_t process_; |
78 | intptr_t exit_pipe_fd_; |
79 | ProcessInfo* next_; |
80 | |
81 | DISALLOW_COPY_AND_ASSIGN(ProcessInfo); |
82 | }; |
83 | |
84 | // Singly-linked list of ProcessInfo objects for all active processes |
85 | // started from Dart. |
86 | class ProcessInfoList { |
87 | public: |
88 | static void Init(); |
89 | static void Cleanup(); |
90 | |
91 | static void AddProcess(zx_handle_t process, intptr_t fd) { |
92 | MutexLocker locker(mutex_); |
93 | ProcessInfo* info = new ProcessInfo(process, fd); |
94 | info->set_next(active_processes_); |
95 | active_processes_ = info; |
96 | } |
97 | |
98 | static intptr_t LookupProcessExitFd(zx_handle_t process) { |
99 | MutexLocker locker(mutex_); |
100 | ProcessInfo* current = active_processes_; |
101 | while (current != NULL) { |
102 | if (current->process() == process) { |
103 | return current->exit_pipe_fd(); |
104 | } |
105 | current = current->next(); |
106 | } |
107 | return 0; |
108 | } |
109 | |
110 | static bool Exists(zx_handle_t process) { |
111 | return LookupProcessExitFd(process) != 0; |
112 | } |
113 | |
114 | static void RemoveProcess(zx_handle_t process) { |
115 | MutexLocker locker(mutex_); |
116 | ProcessInfo* prev = NULL; |
117 | ProcessInfo* current = active_processes_; |
118 | while (current != NULL) { |
119 | if (current->process() == process) { |
120 | if (prev == NULL) { |
121 | active_processes_ = current->next(); |
122 | } else { |
123 | prev->set_next(current->next()); |
124 | } |
125 | delete current; |
126 | return; |
127 | } |
128 | prev = current; |
129 | current = current->next(); |
130 | } |
131 | } |
132 | |
133 | private: |
134 | // Linked list of ProcessInfo objects for all active processes |
135 | // started from Dart code. |
136 | static ProcessInfo* active_processes_; |
137 | // Mutex protecting all accesses to the linked list of active |
138 | // processes. |
139 | static Mutex* mutex_; |
140 | |
141 | DISALLOW_ALLOCATION(); |
142 | DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessInfoList); |
143 | }; |
144 | |
145 | ProcessInfo* ProcessInfoList::active_processes_ = NULL; |
146 | Mutex* ProcessInfoList::mutex_ = nullptr; |
147 | |
148 | // The exit code handler sets up a separate thread which waits for child |
149 | // processes to terminate. That separate thread can then get the exit code from |
150 | // processes that have exited and communicate it to Dart through the |
151 | // event loop. |
152 | class ExitCodeHandler { |
153 | public: |
154 | static void Init(); |
155 | static void Cleanup(); |
156 | |
157 | // Notify the ExitCodeHandler that another process exists. |
158 | static void Start() { |
159 | // Multiple isolates could be starting processes at the same |
160 | // time. Make sure that only one ExitCodeHandler thread exists. |
161 | MonitorLocker locker(monitor_); |
162 | if (running_) { |
163 | return; |
164 | } |
165 | LOG_INFO("ExitCodeHandler Starting\n" ); |
166 | |
167 | zx_status_t status = zx_port_create(0, &port_); |
168 | if (status != ZX_OK) { |
169 | FATAL1("ExitCodeHandler: zx_port_create failed: %s\n" , |
170 | zx_status_get_string(status)); |
171 | return; |
172 | } |
173 | |
174 | // Start thread that handles process exits when wait returns. |
175 | intptr_t result = |
176 | Thread::Start("dart:io Process.start" , ExitCodeHandlerEntry, 0); |
177 | if (result != 0) { |
178 | FATAL1("Failed to start exit code handler worker thread %ld" , result); |
179 | } |
180 | |
181 | running_ = true; |
182 | } |
183 | |
184 | static zx_status_t Add(zx_handle_t process) { |
185 | MonitorLocker locker(monitor_); |
186 | LOG_INFO("ExitCodeHandler Adding Process: %u\n" , process); |
187 | return zx_object_wait_async(process, port_, static_cast<uint64_t>(process), |
188 | ZX_TASK_TERMINATED, ZX_WAIT_ASYNC_ONCE); |
189 | } |
190 | |
191 | static void Terminate() { |
192 | MonitorLocker locker(monitor_); |
193 | if (!running_) { |
194 | return; |
195 | } |
196 | running_ = false; |
197 | |
198 | LOG_INFO("ExitCodeHandler Terminating\n" ); |
199 | SendShutdownMessage(); |
200 | |
201 | while (!terminate_done_) { |
202 | monitor_->Wait(Monitor::kNoTimeout); |
203 | } |
204 | zx_handle_close(port_); |
205 | LOG_INFO("ExitCodeHandler Terminated\n" ); |
206 | } |
207 | |
208 | private: |
209 | static const uint64_t kShutdownPacketKey = 1; |
210 | |
211 | static void SendShutdownMessage() { |
212 | zx_port_packet_t pkt; |
213 | pkt.key = kShutdownPacketKey; |
214 | zx_status_t status = zx_port_queue(port_, &pkt); |
215 | if (status != ZX_OK) { |
216 | Syslog::PrintErr("ExitCodeHandler: zx_port_queue failed: %s\n" , |
217 | zx_status_get_string(status)); |
218 | } |
219 | } |
220 | |
221 | // Entry point for the separate exit code handler thread started by |
222 | // the ExitCodeHandler. |
223 | static void ExitCodeHandlerEntry(uword param) { |
224 | LOG_INFO("ExitCodeHandler Entering ExitCodeHandler thread\n" ); |
225 | |
226 | zx_port_packet_t pkt; |
227 | while (true) { |
228 | zx_status_t status = zx_port_wait(port_, ZX_TIME_INFINITE, &pkt); |
229 | if (status != ZX_OK) { |
230 | FATAL1("ExitCodeHandler: zx_port_wait failed: %s\n" , |
231 | zx_status_get_string(status)); |
232 | } |
233 | if (pkt.type == ZX_PKT_TYPE_USER) { |
234 | ASSERT(pkt.key == kShutdownPacketKey); |
235 | break; |
236 | } |
237 | zx_handle_t process = static_cast<zx_handle_t>(pkt.key); |
238 | zx_signals_t observed = pkt.signal.observed; |
239 | if ((observed & ZX_TASK_TERMINATED) == ZX_SIGNAL_NONE) { |
240 | LOG_ERR("ExitCodeHandler: Unexpected signals, process %u: %ux\n" , |
241 | process, observed); |
242 | } |
243 | SendProcessStatus(process); |
244 | } |
245 | |
246 | LOG_INFO("ExitCodeHandler thread shutting down\n" ); |
247 | terminate_done_ = true; |
248 | monitor_->Notify(); |
249 | } |
250 | |
251 | static void SendProcessStatus(zx_handle_t process) { |
252 | LOG_INFO("ExitCodeHandler thread getting process status: %u\n" , process); |
253 | int return_code = -1; |
254 | zx_info_process_t proc_info; |
255 | zx_status_t status = zx_object_get_info( |
256 | process, ZX_INFO_PROCESS, &proc_info, sizeof(proc_info), NULL, NULL); |
257 | if (status != ZX_OK) { |
258 | Syslog::PrintErr("ExitCodeHandler: zx_object_get_info failed: %s\n" , |
259 | zx_status_get_string(status)); |
260 | } else { |
261 | return_code = proc_info.return_code; |
262 | } |
263 | zx_handle_close(process); |
264 | LOG_INFO("ExitCodeHandler thread process %u exited with %d\n" , process, |
265 | return_code); |
266 | |
267 | const intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(process); |
268 | LOG_INFO("ExitCodeHandler thread sending %u code %d on fd %ld\n" , process, |
269 | return_code, exit_code_fd); |
270 | if (exit_code_fd != 0) { |
271 | int exit_message[2]; |
272 | exit_message[0] = abs(return_code); |
273 | exit_message[1] = return_code >= 0 ? 0 : 1; |
274 | intptr_t result = FDUtils::WriteToBlocking(exit_code_fd, &exit_message, |
275 | sizeof(exit_message)); |
276 | ASSERT((result == -1) || (result == sizeof(exit_code_fd))); |
277 | if ((result == -1) && (errno != EPIPE)) { |
278 | int err = errno; |
279 | Syslog::PrintErr("Failed to write exit code for process %d: errno=%d\n" , |
280 | process, err); |
281 | } |
282 | LOG_INFO("ExitCodeHandler thread wrote %ld bytes to fd %ld\n" , result, |
283 | exit_code_fd); |
284 | LOG_INFO("ExitCodeHandler thread removing process %u from list\n" , |
285 | process); |
286 | ProcessInfoList::RemoveProcess(process); |
287 | } else { |
288 | LOG_ERR("ExitCodeHandler: Process %u not found\n" , process); |
289 | } |
290 | } |
291 | |
292 | static zx_handle_t port_; |
293 | |
294 | // Protected by monitor_. |
295 | static bool terminate_done_; |
296 | static bool running_; |
297 | static Monitor* monitor_; |
298 | |
299 | DISALLOW_ALLOCATION(); |
300 | DISALLOW_IMPLICIT_CONSTRUCTORS(ExitCodeHandler); |
301 | }; |
302 | |
303 | zx_handle_t ExitCodeHandler::port_ = ZX_HANDLE_INVALID; |
304 | bool ExitCodeHandler::running_ = false; |
305 | bool ExitCodeHandler::terminate_done_ = false; |
306 | Monitor* ExitCodeHandler::monitor_ = nullptr; |
307 | |
308 | void Process::TerminateExitCodeHandler() { |
309 | ExitCodeHandler::Terminate(); |
310 | } |
311 | |
312 | intptr_t Process::CurrentProcessId() { |
313 | return static_cast<intptr_t>(getpid()); |
314 | } |
315 | |
316 | int64_t Process::CurrentRSS() { |
317 | zx_info_task_stats_t task_stats; |
318 | zx_handle_t process = zx_process_self(); |
319 | zx_status_t status = zx_object_get_info( |
320 | process, ZX_INFO_TASK_STATS, &task_stats, sizeof(task_stats), NULL, NULL); |
321 | if (status != ZX_OK) { |
322 | // TODO(zra): Translate this to a Unix errno. |
323 | errno = status; |
324 | return -1; |
325 | } |
326 | return task_stats.mem_private_bytes + task_stats.mem_shared_bytes; |
327 | } |
328 | |
329 | int64_t Process::MaxRSS() { |
330 | // There is currently no way to get the high watermark value on Fuchsia, so |
331 | // just return the current RSS value. |
332 | return CurrentRSS(); |
333 | } |
334 | |
335 | class IOHandleScope { |
336 | public: |
337 | explicit IOHandleScope(IOHandle* io_handle) : io_handle_(io_handle) {} |
338 | ~IOHandleScope() { |
339 | io_handle_->Close(); |
340 | io_handle_->Release(); |
341 | } |
342 | |
343 | private: |
344 | IOHandle* io_handle_; |
345 | |
346 | DISALLOW_ALLOCATION(); |
347 | DISALLOW_COPY_AND_ASSIGN(IOHandleScope); |
348 | }; |
349 | |
350 | bool Process::Wait(intptr_t pid, |
351 | intptr_t in, |
352 | intptr_t out, |
353 | intptr_t err, |
354 | intptr_t exit_event, |
355 | ProcessResult* result) { |
356 | IOHandle* out_iohandle = reinterpret_cast<IOHandle*>(out); |
357 | IOHandle* err_iohandle = reinterpret_cast<IOHandle*>(err); |
358 | IOHandle* exit_iohandle = reinterpret_cast<IOHandle*>(exit_event); |
359 | |
360 | // There is no return from this function using Dart_PropagateError |
361 | // as memory used by the buffer lists is freed through their |
362 | // destructors. |
363 | BufferList out_data; |
364 | BufferList err_data; |
365 | union { |
366 | uint8_t bytes[8]; |
367 | int32_t ints[2]; |
368 | } exit_code_data; |
369 | |
370 | // Create a port, which is like an epoll() fd on Linux. |
371 | zx_handle_t port; |
372 | zx_status_t status = zx_port_create(0, &port); |
373 | if (status != ZX_OK) { |
374 | Syslog::PrintErr("Process::Wait: zx_port_create failed: %s\n" , |
375 | zx_status_get_string(status)); |
376 | return false; |
377 | } |
378 | |
379 | IOHandle* out_tmp = out_iohandle; |
380 | IOHandle* err_tmp = err_iohandle; |
381 | IOHandle* exit_tmp = exit_iohandle; |
382 | const uint64_t out_key = reinterpret_cast<uint64_t>(out_tmp); |
383 | const uint64_t err_key = reinterpret_cast<uint64_t>(err_tmp); |
384 | const uint64_t exit_key = reinterpret_cast<uint64_t>(exit_tmp); |
385 | const uint32_t events = POLLRDHUP | POLLIN; |
386 | if (!out_tmp->AsyncWait(port, events, out_key)) { |
387 | return false; |
388 | } |
389 | if (!err_tmp->AsyncWait(port, events, err_key)) { |
390 | return false; |
391 | } |
392 | if (!exit_tmp->AsyncWait(port, events, exit_key)) { |
393 | return false; |
394 | } |
395 | while ((out_tmp != NULL) || (err_tmp != NULL) || (exit_tmp != NULL)) { |
396 | zx_port_packet_t pkt; |
397 | status = zx_port_wait(port, ZX_TIME_INFINITE, &pkt); |
398 | if (status != ZX_OK) { |
399 | Syslog::PrintErr("Process::Wait: zx_port_wait failed: %s\n" , |
400 | zx_status_get_string(status)); |
401 | return false; |
402 | } |
403 | IOHandle* event_handle = reinterpret_cast<IOHandle*>(pkt.key); |
404 | const intptr_t event_mask = event_handle->WaitEnd(pkt.signal.observed); |
405 | if (event_handle == out_tmp) { |
406 | if ((event_mask & POLLIN) != 0) { |
407 | const intptr_t avail = FDUtils::AvailableBytes(out_tmp->fd()); |
408 | if (!out_data.Read(out_tmp->fd(), avail)) { |
409 | return false; |
410 | } |
411 | } |
412 | if ((event_mask & POLLRDHUP) != 0) { |
413 | out_tmp->CancelWait(port, out_key); |
414 | out_tmp = NULL; |
415 | } |
416 | } else if (event_handle == err_tmp) { |
417 | if ((event_mask & POLLIN) != 0) { |
418 | const intptr_t avail = FDUtils::AvailableBytes(err_tmp->fd()); |
419 | if (!err_data.Read(err_tmp->fd(), avail)) { |
420 | return false; |
421 | } |
422 | } |
423 | if ((event_mask & POLLRDHUP) != 0) { |
424 | err_tmp->CancelWait(port, err_key); |
425 | err_tmp = NULL; |
426 | } |
427 | } else if (event_handle == exit_tmp) { |
428 | if ((event_mask & POLLIN) != 0) { |
429 | const intptr_t avail = FDUtils::AvailableBytes(exit_tmp->fd()); |
430 | if (avail == 8) { |
431 | intptr_t b = |
432 | NO_RETRY_EXPECTED(read(exit_tmp->fd(), exit_code_data.bytes, 8)); |
433 | if (b != 8) { |
434 | return false; |
435 | } |
436 | } |
437 | } |
438 | if ((event_mask & POLLRDHUP) != 0) { |
439 | exit_tmp->CancelWait(port, exit_key); |
440 | exit_tmp = NULL; |
441 | } |
442 | } else { |
443 | Syslog::PrintErr("Process::Wait: Unexpected wait key: %p\n" , |
444 | event_handle); |
445 | } |
446 | if (out_tmp != NULL) { |
447 | if (!out_tmp->AsyncWait(port, events, out_key)) { |
448 | return false; |
449 | } |
450 | } |
451 | if (err_tmp != NULL) { |
452 | if (!err_tmp->AsyncWait(port, events, err_key)) { |
453 | return false; |
454 | } |
455 | } |
456 | if (exit_tmp != NULL) { |
457 | if (!exit_tmp->AsyncWait(port, events, exit_key)) { |
458 | return false; |
459 | } |
460 | } |
461 | } |
462 | |
463 | // All handles closed and all data read. |
464 | result->set_stdout_data(out_data.GetData()); |
465 | result->set_stderr_data(err_data.GetData()); |
466 | DEBUG_ASSERT(out_data.IsEmpty()); |
467 | DEBUG_ASSERT(err_data.IsEmpty()); |
468 | |
469 | // Calculate the exit code. |
470 | intptr_t exit_code = exit_code_data.ints[0]; |
471 | intptr_t negative = exit_code_data.ints[1]; |
472 | if (negative != 0) { |
473 | exit_code = -exit_code; |
474 | } |
475 | result->set_exit_code(exit_code); |
476 | |
477 | // Close the process handle. |
478 | zx_handle_t process = static_cast<zx_handle_t>(pid); |
479 | zx_handle_close(process); |
480 | return true; |
481 | } |
482 | |
483 | bool Process::Kill(intptr_t id, int signal) { |
484 | LOG_INFO("Sending signal %d to process with id %ld\n" , signal, id); |
485 | // zx_task_kill is definitely going to kill the process. |
486 | if ((signal != SIGTERM) && (signal != SIGKILL)) { |
487 | LOG_ERR("Signal %d not supported\n" , signal); |
488 | errno = ENOSYS; |
489 | return false; |
490 | } |
491 | // We can only use zx_task_kill if we know id is a process handle, and we only |
492 | // know that for sure if it's in our list. |
493 | zx_handle_t process = static_cast<zx_handle_t>(id); |
494 | if (!ProcessInfoList::Exists(process)) { |
495 | LOG_ERR("Process %ld wasn't in the ProcessInfoList\n" , id); |
496 | errno = ESRCH; // No such process. |
497 | return false; |
498 | } |
499 | zx_status_t status = zx_task_kill(process); |
500 | if (status != ZX_OK) { |
501 | LOG_ERR("zx_task_kill failed: %s\n" , zx_status_get_string(status)); |
502 | errno = EPERM; // TODO(zra): Figure out what it really should be. |
503 | return false; |
504 | } |
505 | LOG_INFO("Signal %d sent successfully to process %ld\n" , signal, id); |
506 | return true; |
507 | } |
508 | |
509 | class ProcessStarter { |
510 | public: |
511 | ProcessStarter(Namespace* namespc, |
512 | const char* path, |
513 | char* arguments[], |
514 | intptr_t arguments_length, |
515 | const char* working_directory, |
516 | char* environment[], |
517 | intptr_t environment_length, |
518 | ProcessStartMode mode, |
519 | intptr_t* in, |
520 | intptr_t* out, |
521 | intptr_t* err, |
522 | intptr_t* id, |
523 | intptr_t* exit_event, |
524 | char** os_error_message) |
525 | : namespc_(namespc), |
526 | path_(path), |
527 | working_directory_(working_directory), |
528 | mode_(mode), |
529 | in_(in), |
530 | out_(out), |
531 | err_(err), |
532 | id_(id), |
533 | exit_event_(exit_event), |
534 | os_error_message_(os_error_message) { |
535 | LOG_INFO("ProcessStarter: ctor %s with %ld args, mode = %d\n" , path, |
536 | arguments_length, mode); |
537 | |
538 | read_in_ = -1; |
539 | read_err_ = -1; |
540 | write_out_ = -1; |
541 | |
542 | program_arguments_ = reinterpret_cast<char**>(Dart_ScopeAllocate( |
543 | (arguments_length + 2) * sizeof(*program_arguments_))); |
544 | program_arguments_[0] = const_cast<char*>(path_); |
545 | for (int i = 0; i < arguments_length; i++) { |
546 | program_arguments_[i + 1] = arguments[i]; |
547 | } |
548 | program_arguments_[arguments_length + 1] = NULL; |
549 | |
550 | program_environment_ = NULL; |
551 | if (environment != NULL) { |
552 | program_environment_ = reinterpret_cast<char**>(Dart_ScopeAllocate( |
553 | (environment_length + 1) * sizeof(*program_environment_))); |
554 | for (int i = 0; i < environment_length; i++) { |
555 | program_environment_[i] = environment[i]; |
556 | } |
557 | program_environment_[environment_length] = NULL; |
558 | } |
559 | } |
560 | |
561 | ~ProcessStarter() { |
562 | if (read_in_ != -1) { |
563 | close(read_in_); |
564 | } |
565 | if (read_err_ != -1) { |
566 | close(read_err_); |
567 | } |
568 | if (write_out_ != -1) { |
569 | close(write_out_); |
570 | } |
571 | } |
572 | |
573 | int Start() { |
574 | LOG_INFO("ProcessStarter: Start()\n" ); |
575 | int exit_pipe_fds[2]; |
576 | intptr_t result = NO_RETRY_EXPECTED(pipe(exit_pipe_fds)); |
577 | if (result != 0) { |
578 | *os_error_message_ = DartUtils::ScopedCopyCString( |
579 | "Failed to create exit code pipe for process start." ); |
580 | return result; |
581 | } |
582 | LOG_INFO("ProcessStarter: Start() set up exit_pipe_fds (%d, %d)\n" , |
583 | exit_pipe_fds[0], exit_pipe_fds[1]); |
584 | |
585 | NamespaceScope ns(namespc_, path_); |
586 | const int pathfd = |
587 | TEMP_FAILURE_RETRY(openat(ns.fd(), ns.path(), O_RDONLY)); |
588 | zx_handle_t vmo = ZX_HANDLE_INVALID; |
589 | zx_status_t status = fdio_get_vmo_clone(pathfd, &vmo); |
590 | close(pathfd); |
591 | if (status != ZX_OK) { |
592 | close(exit_pipe_fds[0]); |
593 | close(exit_pipe_fds[1]); |
594 | *os_error_message_ = DartUtils::ScopedCopyCString( |
595 | "Failed to load executable for process start." ); |
596 | return status; |
597 | } |
598 | |
599 | // After reading the binary into a VMO, we need to mark it as executable, |
600 | // since the VMO returned by fdio_get_vmo_clone should be read-only. |
601 | status = zx_vmo_replace_as_executable(vmo, ZX_HANDLE_INVALID, &vmo); |
602 | if (status != ZX_OK) { |
603 | close(exit_pipe_fds[0]); |
604 | close(exit_pipe_fds[1]); |
605 | *os_error_message_ = DartUtils::ScopedCopyCString( |
606 | "Failed to mark binary as executable for process start." ); |
607 | return status; |
608 | } |
609 | |
610 | fdio_spawn_action_t* actions; |
611 | const intptr_t actions_count = BuildSpawnActions( |
612 | namespc_->namespc()->fdio_ns(), &actions); |
613 | if (actions_count < 0) { |
614 | zx_handle_close(vmo); |
615 | close(exit_pipe_fds[0]); |
616 | close(exit_pipe_fds[1]); |
617 | *os_error_message_ = DartUtils::ScopedCopyCString( |
618 | "Failed to build spawn actions array." ); |
619 | return ZX_ERR_IO; |
620 | } |
621 | |
622 | // TODO(zra): Use the supplied working directory when fdio_spawn_vmo adds an |
623 | // API to set it. |
624 | |
625 | LOG_INFO("ProcessStarter: Start() Calling fdio_spawn_vmo\n" ); |
626 | zx_handle_t process = ZX_HANDLE_INVALID; |
627 | char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]; |
628 | uint32_t flags = FDIO_SPAWN_CLONE_JOB | FDIO_SPAWN_DEFAULT_LDSVC; |
629 | status = fdio_spawn_vmo(ZX_HANDLE_INVALID, flags, vmo, program_arguments_, |
630 | program_environment_, actions_count, actions, |
631 | &process, err_msg); |
632 | // Handles are consumed by fdio_spawn_vmo even if it fails. |
633 | delete[] actions; |
634 | if (status != ZX_OK) { |
635 | LOG_ERR("ProcessStarter: Start() fdio_spawn_vmo failed\n" ); |
636 | close(exit_pipe_fds[0]); |
637 | close(exit_pipe_fds[1]); |
638 | ReportStartError(err_msg); |
639 | return status; |
640 | } |
641 | |
642 | LOG_INFO("ProcessStarter: Start() adding %u to list with exit_pipe %d\n" , |
643 | process, exit_pipe_fds[1]); |
644 | ProcessInfoList::AddProcess(process, exit_pipe_fds[1]); |
645 | ExitCodeHandler::Start(); |
646 | status = ExitCodeHandler::Add(process); |
647 | if (status != ZX_OK) { |
648 | LOG_ERR("ProcessStarter: ExitCodeHandler: Add failed: %s\n" , |
649 | zx_status_get_string(status)); |
650 | close(exit_pipe_fds[0]); |
651 | close(exit_pipe_fds[1]); |
652 | zx_task_kill(process); |
653 | ProcessInfoList::RemoveProcess(process); |
654 | ReportStartError(zx_status_get_string(status)); |
655 | return status; |
656 | } |
657 | |
658 | // The IOHandles allocated below are returned to Dart code. The Dart code |
659 | // calls into the runtime again to allocate a C++ Socket object, which |
660 | // becomes the native field of a Dart _NativeSocket object. The C++ Socket |
661 | // object and the EventHandler manage the lifetime of these IOHandles. |
662 | *id_ = process; |
663 | FDUtils::SetNonBlocking(read_in_); |
664 | *in_ = reinterpret_cast<intptr_t>(new IOHandle(read_in_)); |
665 | read_in_ = -1; |
666 | FDUtils::SetNonBlocking(read_err_); |
667 | *err_ = reinterpret_cast<intptr_t>(new IOHandle(read_err_)); |
668 | read_err_ = -1; |
669 | FDUtils::SetNonBlocking(write_out_); |
670 | *out_ = reinterpret_cast<intptr_t>(new IOHandle(write_out_)); |
671 | write_out_ = -1; |
672 | FDUtils::SetNonBlocking(exit_pipe_fds[0]); |
673 | *exit_event_ = reinterpret_cast<intptr_t>(new IOHandle(exit_pipe_fds[0])); |
674 | return 0; |
675 | } |
676 | |
677 | private: |
678 | void ReportStartError(const char* errormsg) { |
679 | const intptr_t kMaxMessageSize = 256; |
680 | char* message = DartUtils::ScopedCString(kMaxMessageSize); |
681 | snprintf(message, kMaxMessageSize, "Process start failed: %s\n" , errormsg); |
682 | *os_error_message_ = message; |
683 | } |
684 | |
685 | zx_status_t AddPipe(int target_fd, int* local_fd, |
686 | fdio_spawn_action_t* action) { |
687 | zx_status_t status = fdio_pipe_half(local_fd, &action->h.handle); |
688 | if (status != ZX_OK) return status; |
689 | action->action = FDIO_SPAWN_ACTION_ADD_HANDLE; |
690 | action->h.id = PA_HND(PA_HND_TYPE(PA_FD), target_fd); |
691 | return ZX_OK; |
692 | } |
693 | |
694 | // Fills in 'actions_out' and returns action count. |
695 | intptr_t BuildSpawnActions(fdio_ns_t* ns, fdio_spawn_action_t** actions_out) { |
696 | const intptr_t fixed_actions_cnt = 4; |
697 | intptr_t ns_cnt = 0; |
698 | zx_status_t status; |
699 | |
700 | // First, figure out how many namespace actions are needed. |
701 | fdio_flat_namespace_t* flat_ns = nullptr; |
702 | if (ns != nullptr) { |
703 | status = fdio_ns_export(ns, &flat_ns); |
704 | if (status != ZX_OK) { |
705 | LOG_ERR("ProcessStarter: BuildSpawnActions: fdio_ns_export: %s\n" , |
706 | zx_status_get_string(status)); |
707 | return -1; |
708 | } |
709 | ns_cnt = flat_ns->count; |
710 | } |
711 | |
712 | // Allocate the actions array. |
713 | const intptr_t actions_cnt = ns_cnt + fixed_actions_cnt; |
714 | fdio_spawn_action_t* actions = new fdio_spawn_action_t[actions_cnt]; |
715 | |
716 | // Fill in the entries for passing stdin/out/err handles, and the program |
717 | // name. |
718 | status = AddPipe(0, &write_out_, &actions[0]); |
719 | if (status != ZX_OK) { |
720 | LOG_ERR("ProcessStarter: BuildSpawnActions: stdout AddPipe failed: %s\n" , |
721 | zx_status_get_string(status)); |
722 | if (flat_ns != nullptr) { |
723 | fdio_ns_free_flat_ns(flat_ns); |
724 | } |
725 | return -1; |
726 | } |
727 | status = AddPipe(1, &read_in_, &actions[1]); |
728 | if (status != ZX_OK) { |
729 | LOG_ERR("ProcessStarter: BuildSpawnActions: stdin AddPipe failed: %s\n" , |
730 | zx_status_get_string(status)); |
731 | if (flat_ns != nullptr) { |
732 | fdio_ns_free_flat_ns(flat_ns); |
733 | } |
734 | return -1; |
735 | } |
736 | status = AddPipe(2, &read_err_, &actions[2]); |
737 | if (status != ZX_OK) { |
738 | LOG_ERR("ProcessStarter: BuildSpawnActions: stderr AddPipe failed: %s\n" , |
739 | zx_status_get_string(status)); |
740 | if (flat_ns != nullptr) { |
741 | fdio_ns_free_flat_ns(flat_ns); |
742 | } |
743 | return -1; |
744 | } |
745 | actions[3] = { |
746 | .action = FDIO_SPAWN_ACTION_SET_NAME, |
747 | .name = { |
748 | .data = program_arguments_[0], |
749 | }, |
750 | }; |
751 | |
752 | // Then fill in the namespace actions. |
753 | if (ns != nullptr) { |
754 | for (size_t i = 0; i < flat_ns->count; i++) { |
755 | actions[fixed_actions_cnt + i] = { |
756 | .action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY, |
757 | .ns = { |
758 | .prefix = flat_ns->path[i], |
759 | .handle = flat_ns->handle[i], |
760 | }, |
761 | }; |
762 | } |
763 | free(flat_ns); |
764 | flat_ns = nullptr; |
765 | } |
766 | |
767 | *actions_out = actions; |
768 | return actions_cnt; |
769 | } |
770 | |
771 | int read_in_; // Pipe for stdout to child process. |
772 | int read_err_; // Pipe for stderr to child process. |
773 | int write_out_; // Pipe for stdin to child process. |
774 | |
775 | char** program_arguments_; |
776 | char** program_environment_; |
777 | |
778 | Namespace* namespc_; |
779 | const char* path_; |
780 | const char* working_directory_; |
781 | ProcessStartMode mode_; |
782 | intptr_t* in_; |
783 | intptr_t* out_; |
784 | intptr_t* err_; |
785 | intptr_t* id_; |
786 | intptr_t* exit_event_; |
787 | char** os_error_message_; |
788 | |
789 | DISALLOW_ALLOCATION(); |
790 | DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessStarter); |
791 | }; |
792 | |
793 | int Process::Start(Namespace* namespc, |
794 | const char* path, |
795 | char* arguments[], |
796 | intptr_t arguments_length, |
797 | const char* working_directory, |
798 | char* environment[], |
799 | intptr_t environment_length, |
800 | ProcessStartMode mode, |
801 | intptr_t* in, |
802 | intptr_t* out, |
803 | intptr_t* err, |
804 | intptr_t* id, |
805 | intptr_t* exit_event, |
806 | char** os_error_message) { |
807 | if (mode != kNormal) { |
808 | *os_error_message = DartUtils::ScopedCopyCString( |
809 | "Only ProcessStartMode.NORMAL is supported on this platform" ); |
810 | return -1; |
811 | } |
812 | ProcessStarter starter(namespc, path, arguments, arguments_length, |
813 | working_directory, environment, environment_length, |
814 | mode, in, out, err, id, exit_event, os_error_message); |
815 | return starter.Start(); |
816 | } |
817 | |
818 | intptr_t Process::SetSignalHandler(intptr_t signal) { |
819 | errno = ENOSYS; |
820 | return -1; |
821 | } |
822 | |
823 | void Process::ClearSignalHandler(intptr_t signal, Dart_Port port) {} |
824 | |
825 | void Process::ClearSignalHandlerByFd(intptr_t fd, Dart_Port port) {} |
826 | |
827 | void ProcessInfoList::Init() { |
828 | ASSERT(ProcessInfoList::mutex_ == nullptr); |
829 | ProcessInfoList::mutex_ = new Mutex(); |
830 | } |
831 | |
832 | void ProcessInfoList::Cleanup() { |
833 | ASSERT(ProcessInfoList::mutex_ != nullptr); |
834 | delete ProcessInfoList::mutex_; |
835 | ProcessInfoList::mutex_ = nullptr; |
836 | } |
837 | |
838 | void ExitCodeHandler::Init() { |
839 | ASSERT(ExitCodeHandler::monitor_ == nullptr); |
840 | ExitCodeHandler::monitor_ = new Monitor(); |
841 | } |
842 | |
843 | void ExitCodeHandler::Cleanup() { |
844 | ASSERT(ExitCodeHandler::monitor_ != nullptr); |
845 | delete ExitCodeHandler::monitor_; |
846 | ExitCodeHandler::monitor_ = nullptr; |
847 | } |
848 | |
849 | void Process::Init() { |
850 | ExitCodeHandler::Init(); |
851 | ProcessInfoList::Init(); |
852 | |
853 | ASSERT(Process::global_exit_code_mutex_ == nullptr); |
854 | Process::global_exit_code_mutex_ = new Mutex(); |
855 | } |
856 | |
857 | void Process::Cleanup() { |
858 | ASSERT(Process::global_exit_code_mutex_ != nullptr); |
859 | delete Process::global_exit_code_mutex_; |
860 | Process::global_exit_code_mutex_ = nullptr; |
861 | |
862 | ProcessInfoList::Cleanup(); |
863 | ExitCodeHandler::Cleanup(); |
864 | } |
865 | |
866 | } // namespace bin |
867 | } // namespace dart |
868 | |
869 | #endif // defined(HOST_OS_FUCHSIA) |
870 | |