OLD | NEW |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #if !defined(DART_IO_DISABLED) | 5 #if !defined(DART_IO_DISABLED) |
6 | 6 |
7 #include "platform/globals.h" | 7 #include "platform/globals.h" |
8 #if defined(TARGET_OS_FUCHSIA) | 8 #if defined(TARGET_OS_FUCHSIA) |
9 | 9 |
10 #include "bin/process.h" | 10 #include "bin/process.h" |
11 | 11 |
| 12 #include <errno.h> |
| 13 #include <fcntl.h> |
| 14 #include <launchpad/launchpad.h> |
| 15 #include <launchpad/vmo.h> |
| 16 #include <magenta/status.h> |
| 17 #include <magenta/syscalls.h> |
| 18 #include <magenta/syscalls/object.h> |
| 19 #include <mxio/util.h> |
| 20 #include <pthread.h> |
| 21 #include <stdbool.h> |
| 22 #include <stdio.h> |
| 23 #include <stdlib.h> |
| 24 #include <string.h> |
| 25 #include <unistd.h> |
| 26 |
| 27 #include "bin/dartutils.h" |
| 28 #include "bin/fdutils.h" |
12 #include "bin/lockers.h" | 29 #include "bin/lockers.h" |
13 #include "platform/assert.h" | 30 #include "bin/log.h" |
| 31 #include "platform/signal_blocker.h" |
14 | 32 |
| 33 // #define PROCESS_LOGGING 1 |
| 34 #if defined(PROCESS_LOGGING) |
| 35 #define LOG_ERR(msg, ...) Log::PrintErr("Dart Process: " msg, ##__VA_ARGS__) |
| 36 #define LOG_INFO(msg, ...) Log::Print("Dart Process: " msg, ##__VA_ARGS__) |
| 37 #else |
| 38 #define LOG_ERR(msg, ...) |
| 39 #define LOG_INFO(msg, ...) |
| 40 #endif // defined(PROCESS_LOGGING) |
15 | 41 |
16 namespace dart { | 42 namespace dart { |
17 namespace bin { | 43 namespace bin { |
18 | 44 |
19 int Process::global_exit_code_ = 0; | 45 int Process::global_exit_code_ = 0; |
20 Mutex* Process::global_exit_code_mutex_ = new Mutex(); | 46 Mutex* Process::global_exit_code_mutex_ = new Mutex(); |
21 Process::ExitHook Process::exit_hook_ = NULL; | 47 Process::ExitHook Process::exit_hook_ = NULL; |
22 | 48 |
23 void Process::TerminateExitCodeHandler() {} | 49 // ProcessInfo is used to map a process id to the file descriptor for |
| 50 // the pipe used to communicate the exit code of the process to Dart. |
| 51 // ProcessInfo objects are kept in the static singly-linked |
| 52 // ProcessInfoList. |
| 53 class ProcessInfo { |
| 54 public: |
| 55 ProcessInfo(mx_handle_t process, intptr_t fd) |
| 56 : process_(process), exit_pipe_fd_(fd) {} |
| 57 ~ProcessInfo() { |
| 58 int closed = NO_RETRY_EXPECTED(close(exit_pipe_fd_)); |
| 59 if (closed != 0) { |
| 60 FATAL("Failed to close process exit code pipe"); |
| 61 } |
| 62 } |
| 63 mx_handle_t process() const { return process_; } |
| 64 intptr_t exit_pipe_fd() const { return exit_pipe_fd_; } |
| 65 ProcessInfo* next() const { return next_; } |
| 66 void set_next(ProcessInfo* info) { next_ = info; } |
| 67 |
| 68 private: |
| 69 mx_handle_t process_; |
| 70 intptr_t exit_pipe_fd_; |
| 71 ProcessInfo* next_; |
| 72 |
| 73 DISALLOW_COPY_AND_ASSIGN(ProcessInfo); |
| 74 }; |
| 75 |
| 76 |
| 77 // Singly-linked list of ProcessInfo objects for all active processes |
| 78 // started from Dart. |
| 79 class ProcessInfoList { |
| 80 public: |
| 81 static void AddProcess(mx_handle_t process, intptr_t fd) { |
| 82 MutexLocker locker(mutex_); |
| 83 ProcessInfo* info = new ProcessInfo(process, fd); |
| 84 info->set_next(active_processes_); |
| 85 active_processes_ = info; |
| 86 } |
| 87 |
| 88 |
| 89 static intptr_t LookupProcessExitFd(mx_handle_t process) { |
| 90 MutexLocker locker(mutex_); |
| 91 ProcessInfo* current = active_processes_; |
| 92 while (current != NULL) { |
| 93 if (current->process() == process) { |
| 94 return current->exit_pipe_fd(); |
| 95 } |
| 96 current = current->next(); |
| 97 } |
| 98 return 0; |
| 99 } |
| 100 |
| 101 |
| 102 static void RemoveProcess(mx_handle_t process) { |
| 103 MutexLocker locker(mutex_); |
| 104 ProcessInfo* prev = NULL; |
| 105 ProcessInfo* current = active_processes_; |
| 106 while (current != NULL) { |
| 107 if (current->process() == process) { |
| 108 if (prev == NULL) { |
| 109 active_processes_ = current->next(); |
| 110 } else { |
| 111 prev->set_next(current->next()); |
| 112 } |
| 113 delete current; |
| 114 return; |
| 115 } |
| 116 prev = current; |
| 117 current = current->next(); |
| 118 } |
| 119 } |
| 120 |
| 121 private: |
| 122 // Linked list of ProcessInfo objects for all active processes |
| 123 // started from Dart code. |
| 124 static ProcessInfo* active_processes_; |
| 125 // Mutex protecting all accesses to the linked list of active |
| 126 // processes. |
| 127 static Mutex* mutex_; |
| 128 |
| 129 DISALLOW_ALLOCATION(); |
| 130 DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessInfoList); |
| 131 }; |
| 132 |
| 133 ProcessInfo* ProcessInfoList::active_processes_ = NULL; |
| 134 Mutex* ProcessInfoList::mutex_ = new Mutex(); |
| 135 |
| 136 // The exit code handler sets up a separate thread which waits for child |
| 137 // processes to terminate. That separate thread can then get the exit code from |
| 138 // processes that have exited and communicate it to Dart through the |
| 139 // event loop. |
| 140 class ExitCodeHandler { |
| 141 public: |
| 142 // Notify the ExitCodeHandler that another process exists. |
| 143 static void Start() { |
| 144 // Multiple isolates could be starting processes at the same |
| 145 // time. Make sure that only one ExitCodeHandler thread exists. |
| 146 MonitorLocker locker(monitor_); |
| 147 if (running_) { |
| 148 return; |
| 149 } |
| 150 |
| 151 LOG_INFO("ExitCodeHandler Starting\n"); |
| 152 |
| 153 mx_status_t status = mx_socket_create(0, &interrupt_in_, &interrupt_out_); |
| 154 if (status < 0) { |
| 155 FATAL1("Failed to create exit code handler interrupt socket: %s\n", |
| 156 mx_status_get_string(status)); |
| 157 } |
| 158 |
| 159 // Start thread that handles process exits when wait returns. |
| 160 intptr_t result = |
| 161 Thread::Start(ExitCodeHandlerEntry, static_cast<uword>(interrupt_out_)); |
| 162 if (result != 0) { |
| 163 FATAL1("Failed to start exit code handler worker thread %ld", result); |
| 164 } |
| 165 |
| 166 running_ = true; |
| 167 } |
| 168 |
| 169 static void Add(mx_handle_t process) { |
| 170 MonitorLocker locker(monitor_); |
| 171 LOG_INFO("ExitCodeHandler Adding Process: %ld\n", process); |
| 172 SendMessage(Message::kAdd, process); |
| 173 } |
| 174 |
| 175 static void Terminate() { |
| 176 MonitorLocker locker(monitor_); |
| 177 if (!running_) { |
| 178 return; |
| 179 } |
| 180 running_ = false; |
| 181 |
| 182 LOG_INFO("ExitCodeHandler Terminating\n"); |
| 183 SendMessage(Message::kShutdown, MX_HANDLE_INVALID); |
| 184 |
| 185 while (!terminate_done_) { |
| 186 monitor_->Wait(Monitor::kNoTimeout); |
| 187 } |
| 188 mx_handle_close(interrupt_in_); |
| 189 LOG_INFO("ExitCodeHandler Terminated\n"); |
| 190 } |
| 191 |
| 192 private: |
| 193 class Message { |
| 194 public: |
| 195 enum Command { |
| 196 kAdd, |
| 197 kShutdown, |
| 198 }; |
| 199 Command command; |
| 200 mx_handle_t handle; |
| 201 }; |
| 202 |
| 203 static void SendMessage(Message::Command command, mx_handle_t handle) { |
| 204 Message msg; |
| 205 msg.command = command; |
| 206 msg.handle = handle; |
| 207 size_t actual; |
| 208 mx_status_t status = |
| 209 mx_socket_write(interrupt_in_, 0, &msg, sizeof(msg), &actual); |
| 210 if (status < 0) { |
| 211 FATAL1("Write to exit handler interrupt handle failed: %s\n", |
| 212 mx_status_get_string(status)); |
| 213 } |
| 214 ASSERT(actual == sizeof(msg)); |
| 215 } |
| 216 |
| 217 // Entry point for the separate exit code handler thread started by |
| 218 // the ExitCodeHandler. |
| 219 static void ExitCodeHandlerEntry(uword param) { |
| 220 LOG_INFO("ExitCodeHandler Entering ExitCodeHandler thread\n"); |
| 221 item_capacity_ = 16; |
| 222 items_ = reinterpret_cast<mx_wait_item_t*>( |
| 223 malloc(item_capacity_ * sizeof(*items_))); |
| 224 items_to_remove_ = reinterpret_cast<intptr_t*>( |
| 225 malloc(item_capacity_ * sizeof(*items_to_remove_))); |
| 226 |
| 227 // The interrupt handle is fixed to the first entry. |
| 228 items_[0].handle = interrupt_out_; |
| 229 items_[0].waitfor = MX_SOCKET_READABLE | MX_SOCKET_PEER_CLOSED; |
| 230 items_[0].pending = MX_SIGNAL_NONE; |
| 231 item_count_ = 1; |
| 232 |
| 233 while (!do_shutdown_) { |
| 234 LOG_INFO("ExitCodeHandler Calling mx_handle_wait_many: %ld items\n", |
| 235 item_count_); |
| 236 mx_status_t status = |
| 237 mx_handle_wait_many(items_, item_count_, MX_TIME_INFINITE); |
| 238 if (status < 0) { |
| 239 FATAL1("Exit code handler handle wait failed: %s\n", |
| 240 mx_status_get_string(status)); |
| 241 } |
| 242 LOG_INFO("ExitCodeHandler mx_handle_wait_many returned\n"); |
| 243 |
| 244 bool have_interrupt = false; |
| 245 intptr_t remove_count = 0; |
| 246 for (intptr_t i = 0; i < item_count_; i++) { |
| 247 if (items_[i].pending == MX_SIGNAL_NONE) { |
| 248 continue; |
| 249 } |
| 250 if (i == 0) { |
| 251 LOG_INFO("ExitCodeHandler thread saw interrupt\n"); |
| 252 have_interrupt = true; |
| 253 continue; |
| 254 } |
| 255 ASSERT(items_[i].waitfor == MX_TASK_TERMINATED); |
| 256 ASSERT((items_[i].pending & MX_TASK_TERMINATED) != 0); |
| 257 LOG_INFO("ExitCodeHandler signal for %ld\n", items_[i].handle); |
| 258 SendProcessStatus(items_[i].handle); |
| 259 items_to_remove_[remove_count++] = i; |
| 260 } |
| 261 for (intptr_t i = 0; i < remove_count; i++) { |
| 262 RemoveItem(items_to_remove_[i]); |
| 263 } |
| 264 if (have_interrupt) { |
| 265 HandleInterruptMsg(); |
| 266 } |
| 267 } |
| 268 |
| 269 LOG_INFO("ExitCodeHandler thread shutting down\n"); |
| 270 mx_handle_close(interrupt_out_); |
| 271 free(items_); |
| 272 items_ = NULL; |
| 273 free(items_to_remove_); |
| 274 items_to_remove_ = NULL; |
| 275 item_count_ = 0; |
| 276 item_capacity_ = 0; |
| 277 |
| 278 terminate_done_ = true; |
| 279 monitor_->Notify(); |
| 280 } |
| 281 |
| 282 static void SendProcessStatus(mx_handle_t process) { |
| 283 LOG_INFO("ExitCodeHandler thread getting process status: %ld\n", process); |
| 284 mx_info_process_t proc_info; |
| 285 mx_status_t status = mx_object_get_info( |
| 286 process, MX_INFO_PROCESS, &proc_info, sizeof(proc_info), NULL, NULL); |
| 287 if (status < 0) { |
| 288 FATAL1("mx_object_get_info failed on process handle: %s\n", |
| 289 mx_status_get_string(status)); |
| 290 } |
| 291 |
| 292 const int return_code = proc_info.return_code; |
| 293 status = mx_handle_close(process); |
| 294 if (status < 0) { |
| 295 FATAL1("Failed to close process handle: %s\n", |
| 296 mx_status_get_string(status)); |
| 297 } |
| 298 LOG_INFO("ExitCodeHandler thread process %ld exited with %d\n", process, |
| 299 return_code); |
| 300 |
| 301 const intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(process); |
| 302 LOG_INFO("ExitCodeHandler thread sending %ld code %d on fd %ld\n", process, |
| 303 return_code, exit_code_fd); |
| 304 if (exit_code_fd != 0) { |
| 305 int exit_message[2]; |
| 306 exit_message[0] = return_code; |
| 307 exit_message[1] = 0; // Do not negate return_code. |
| 308 intptr_t result = FDUtils::WriteToBlocking(exit_code_fd, &exit_message, |
| 309 sizeof(exit_message)); |
| 310 ASSERT((result == -1) || (result == sizeof(exit_code_fd))); |
| 311 if ((result == -1) && (errno != EPIPE)) { |
| 312 int err = errno; |
| 313 FATAL1("Failed to write exit code to pipe: %d\n", err); |
| 314 } |
| 315 LOG_INFO("ExitCodeHandler thread wrote %ld bytes to fd %ld\n", result, |
| 316 exit_code_fd); |
| 317 LOG_INFO("ExitCodeHandler thread removing process %ld from list\n", |
| 318 process); |
| 319 ProcessInfoList::RemoveProcess(process); |
| 320 } |
| 321 } |
| 322 |
| 323 static void HandleInterruptMsg() { |
| 324 ASSERT(items_[0].handle == interrupt_out_); |
| 325 ASSERT(items_[0].waitfor == MX_SOCKET_READABLE); |
| 326 ASSERT((items_[0].pending & MX_SOCKET_READABLE) != 0); |
| 327 while (true) { |
| 328 Message msg; |
| 329 size_t actual = 0; |
| 330 LOG_INFO("ExitCodeHandler thread reading interrupt message\n"); |
| 331 mx_status_t status = |
| 332 mx_socket_read(interrupt_out_, 0, &msg, sizeof(msg), &actual); |
| 333 if (status == ERR_SHOULD_WAIT) { |
| 334 LOG_INFO("ExitCodeHandler thread done reading interrupt messages\n"); |
| 335 return; |
| 336 } |
| 337 if (status < 0) { |
| 338 FATAL1("Failed to read exit handler interrupt handle: %s\n", |
| 339 mx_status_get_string(status)); |
| 340 } |
| 341 if (actual < sizeof(msg)) { |
| 342 FATAL1("Short read from exit handler interrupt handle: %ld\n", actual); |
| 343 } |
| 344 switch (msg.command) { |
| 345 case Message::kShutdown: |
| 346 LOG_INFO("ExitCodeHandler thread got shutdown message\n"); |
| 347 do_shutdown_ = true; |
| 348 break; |
| 349 case Message::kAdd: |
| 350 LOG_INFO("ExitCodeHandler thread got add message: %ld\n", msg.handle); |
| 351 AddItem(msg.handle); |
| 352 break; |
| 353 } |
| 354 } |
| 355 } |
| 356 |
| 357 static void AddItem(mx_handle_t h) { |
| 358 if (item_count_ == item_capacity_) { |
| 359 item_capacity_ = item_capacity_ + (item_capacity_ >> 1); |
| 360 items_ = |
| 361 reinterpret_cast<mx_wait_item_t*>(realloc(items_, item_capacity_)); |
| 362 items_to_remove_ = reinterpret_cast<intptr_t*>( |
| 363 realloc(items_to_remove_, item_capacity_)); |
| 364 } |
| 365 LOG_INFO("ExitCodeHandler thread adding item %ld at %ld\n", h, item_count_); |
| 366 items_[item_count_].handle = h; |
| 367 items_[item_count_].waitfor = MX_TASK_TERMINATED; |
| 368 items_[item_count_].pending = MX_SIGNAL_NONE; |
| 369 item_count_++; |
| 370 } |
| 371 |
| 372 static void RemoveItem(intptr_t idx) { |
| 373 LOG_INFO("ExitCodeHandler thread removing item %ld at %ld\n", |
| 374 items_[idx].handle, idx); |
| 375 ASSERT(idx != 0); |
| 376 const intptr_t last = item_count_ - 1; |
| 377 items_[idx].handle = MX_HANDLE_INVALID; |
| 378 items_[idx].waitfor = MX_SIGNAL_NONE; |
| 379 items_[idx].pending = MX_SIGNAL_NONE; |
| 380 if (idx != last) { |
| 381 items_[idx] = items_[last]; |
| 382 } |
| 383 item_count_--; |
| 384 } |
| 385 |
| 386 // Interrupt message pipe. |
| 387 static mx_handle_t interrupt_in_; |
| 388 static mx_handle_t interrupt_out_; |
| 389 |
| 390 // Accessed only by the ExitCodeHandler thread. |
| 391 static mx_wait_item_t* items_; |
| 392 static intptr_t* items_to_remove_; |
| 393 static intptr_t item_count_; |
| 394 static intptr_t item_capacity_; |
| 395 |
| 396 // Protected by monitor_. |
| 397 static bool do_shutdown_; |
| 398 static bool terminate_done_; |
| 399 static bool running_; |
| 400 static Monitor* monitor_; |
| 401 |
| 402 DISALLOW_ALLOCATION(); |
| 403 DISALLOW_IMPLICIT_CONSTRUCTORS(ExitCodeHandler); |
| 404 }; |
| 405 |
| 406 mx_handle_t ExitCodeHandler::interrupt_in_ = MX_HANDLE_INVALID; |
| 407 mx_handle_t ExitCodeHandler::interrupt_out_ = MX_HANDLE_INVALID; |
| 408 mx_wait_item_t* ExitCodeHandler::items_ = NULL; |
| 409 intptr_t* ExitCodeHandler::items_to_remove_ = NULL; |
| 410 intptr_t ExitCodeHandler::item_count_ = 0; |
| 411 intptr_t ExitCodeHandler::item_capacity_ = 0; |
| 412 |
| 413 bool ExitCodeHandler::do_shutdown_ = false; |
| 414 bool ExitCodeHandler::running_ = false; |
| 415 bool ExitCodeHandler::terminate_done_ = false; |
| 416 Monitor* ExitCodeHandler::monitor_ = new Monitor(); |
| 417 |
| 418 void Process::TerminateExitCodeHandler() { |
| 419 ExitCodeHandler::Terminate(); |
| 420 } |
| 421 |
24 | 422 |
25 intptr_t Process::CurrentProcessId() { | 423 intptr_t Process::CurrentProcessId() { |
26 UNIMPLEMENTED(); | 424 return static_cast<intptr_t>(getpid()); |
27 return 0; | |
28 } | 425 } |
29 | 426 |
30 intptr_t Process::SetSignalHandler(intptr_t signal) { | |
31 UNIMPLEMENTED(); | |
32 return -1; | |
33 } | |
34 | |
35 | |
36 void Process::ClearSignalHandler(intptr_t signal) { | |
37 UNIMPLEMENTED(); | |
38 } | |
39 | 427 |
40 bool Process::Wait(intptr_t pid, | 428 bool Process::Wait(intptr_t pid, |
41 intptr_t in, | 429 intptr_t in, |
42 intptr_t out, | 430 intptr_t out, |
43 intptr_t err, | 431 intptr_t err, |
44 intptr_t exit_event, | 432 intptr_t exit_event, |
45 ProcessResult* result) { | 433 ProcessResult* result) { |
46 UNIMPLEMENTED(); | 434 UNIMPLEMENTED(); |
47 return false; | 435 return false; |
48 } | 436 } |
49 | 437 |
| 438 |
50 bool Process::Kill(intptr_t id, int signal) { | 439 bool Process::Kill(intptr_t id, int signal) { |
51 UNIMPLEMENTED(); | 440 UNIMPLEMENTED(); |
52 return false; | 441 return false; |
53 } | 442 } |
54 | 443 |
| 444 |
| 445 class ProcessStarter { |
| 446 public: |
| 447 ProcessStarter(const char* path, |
| 448 char* arguments[], |
| 449 intptr_t arguments_length, |
| 450 const char* working_directory, |
| 451 char* environment[], |
| 452 intptr_t environment_length, |
| 453 ProcessStartMode mode, |
| 454 intptr_t* in, |
| 455 intptr_t* out, |
| 456 intptr_t* err, |
| 457 intptr_t* id, |
| 458 intptr_t* exit_event, |
| 459 char** os_error_message) |
| 460 : path_(path), |
| 461 working_directory_(working_directory), |
| 462 mode_(mode), |
| 463 in_(in), |
| 464 out_(out), |
| 465 err_(err), |
| 466 id_(id), |
| 467 exit_event_(exit_event), |
| 468 os_error_message_(os_error_message) { |
| 469 LOG_INFO("ProcessStarter: ctor %s with %ld args, mode = %d\n", path, |
| 470 arguments_length, mode); |
| 471 |
| 472 read_in_ = -1; |
| 473 read_err_ = -1; |
| 474 write_out_ = -1; |
| 475 |
| 476 program_arguments_ = reinterpret_cast<char**>(Dart_ScopeAllocate( |
| 477 (arguments_length + 2) * sizeof(*program_arguments_))); |
| 478 program_arguments_[0] = const_cast<char*>(path_); |
| 479 for (int i = 0; i < arguments_length; i++) { |
| 480 program_arguments_[i + 1] = arguments[i]; |
| 481 } |
| 482 program_arguments_[arguments_length + 1] = NULL; |
| 483 program_arguments_count_ = arguments_length + 1; |
| 484 |
| 485 program_environment_ = NULL; |
| 486 if (environment != NULL) { |
| 487 program_environment_ = reinterpret_cast<char**>(Dart_ScopeAllocate( |
| 488 (environment_length + 1) * sizeof(*program_environment_))); |
| 489 for (int i = 0; i < environment_length; i++) { |
| 490 program_environment_[i] = environment[i]; |
| 491 } |
| 492 program_environment_[environment_length] = NULL; |
| 493 } |
| 494 |
| 495 binary_vmo_ = MX_HANDLE_INVALID; |
| 496 launchpad_ = NULL; |
| 497 } |
| 498 |
| 499 ~ProcessStarter() { |
| 500 if (binary_vmo_ != MX_HANDLE_INVALID) { |
| 501 mx_handle_close(binary_vmo_); |
| 502 } |
| 503 if (launchpad_ != NULL) { |
| 504 launchpad_destroy(launchpad_); |
| 505 } |
| 506 if (read_in_ != -1) { |
| 507 close(read_in_); |
| 508 } |
| 509 if (read_err_ != -1) { |
| 510 close(read_err_); |
| 511 } |
| 512 if (write_out_ != -1) { |
| 513 close(write_out_); |
| 514 } |
| 515 } |
| 516 |
| 517 int Start() { |
| 518 LOG_INFO("ProcessStarter: Start()\n"); |
| 519 int exit_pipe_fds[2]; |
| 520 intptr_t result = NO_RETRY_EXPECTED(pipe(exit_pipe_fds)); |
| 521 if (result != 0) { |
| 522 *os_error_message_ = DartUtils::ScopedCopyCString( |
| 523 "Failed to create exit code pipe for process start."); |
| 524 return result; |
| 525 } |
| 526 LOG_INFO("ProcessStarter: Start() set up exit_pipe_fds (%d, %d)\n", |
| 527 exit_pipe_fds[0], exit_pipe_fds[1]); |
| 528 |
| 529 mx_status_t status = SetupLaunchpad(); |
| 530 if (status != NO_ERROR) { |
| 531 close(exit_pipe_fds[0]); |
| 532 close(exit_pipe_fds[1]); |
| 533 return status; |
| 534 } |
| 535 |
| 536 LOG_INFO("ProcessStarter: Start() Calling launchpad_start\n"); |
| 537 mx_handle_t process = launchpad_start(launchpad_); |
| 538 launchpad_destroy(launchpad_); |
| 539 launchpad_ = NULL; |
| 540 if (process < 0) { |
| 541 LOG_INFO("ProcessStarter: Start() launchpad_start failed\n"); |
| 542 const intptr_t kMaxMessageSize = 256; |
| 543 close(exit_pipe_fds[0]); |
| 544 close(exit_pipe_fds[1]); |
| 545 char* message = DartUtils::ScopedCString(kMaxMessageSize); |
| 546 snprintf(message, kMaxMessageSize, "%s:%d: launchpad_start failed: %s\n", |
| 547 __FILE__, __LINE__, mx_status_get_string(process)); |
| 548 *os_error_message_ = message; |
| 549 return process; |
| 550 } |
| 551 |
| 552 LOG_INFO("ProcessStarter: Start() adding %ld to list with exit_pipe %d\n", |
| 553 process, exit_pipe_fds[1]); |
| 554 ProcessInfoList::AddProcess(process, exit_pipe_fds[1]); |
| 555 ExitCodeHandler::Start(); |
| 556 ExitCodeHandler::Add(process); |
| 557 |
| 558 *id_ = process; |
| 559 FDUtils::SetNonBlocking(read_in_); |
| 560 *in_ = read_in_; |
| 561 read_in_ = -1; |
| 562 FDUtils::SetNonBlocking(read_err_); |
| 563 *err_ = read_err_; |
| 564 read_err_ = -1; |
| 565 FDUtils::SetNonBlocking(write_out_); |
| 566 *out_ = write_out_; |
| 567 write_out_ = -1; |
| 568 FDUtils::SetNonBlocking(exit_pipe_fds[0]); |
| 569 *exit_event_ = exit_pipe_fds[0]; |
| 570 return 0; |
| 571 } |
| 572 |
| 573 private: |
| 574 #define CHECK_FOR_ERROR(status, msg) \ |
| 575 if (status < 0) { \ |
| 576 const intptr_t kMaxMessageSize = 256; \ |
| 577 char* message = DartUtils::ScopedCString(kMaxMessageSize); \ |
| 578 snprintf(message, kMaxMessageSize, "%s:%d: %s: %s\n", __FILE__, __LINE__, \ |
| 579 msg, mx_status_get_string(status)); \ |
| 580 *os_error_message_ = message; \ |
| 581 return status; \ |
| 582 } |
| 583 |
| 584 mx_status_t SetupLaunchpad() { |
| 585 mx_handle_t binary_vmo = launchpad_vmo_from_file(path_); |
| 586 CHECK_FOR_ERROR(binary_vmo, "launchpad_vmo_from_file"); |
| 587 binary_vmo_ = binary_vmo; |
| 588 |
| 589 launchpad_t* lp; |
| 590 mx_status_t status; |
| 591 |
| 592 status = launchpad_create(0, program_arguments_[0], &lp); |
| 593 CHECK_FOR_ERROR(status, "launchpad_create"); |
| 594 launchpad_ = lp; |
| 595 |
| 596 status = |
| 597 launchpad_arguments(lp, program_arguments_count_, program_arguments_); |
| 598 CHECK_FOR_ERROR(status, "launchpad_arguments"); |
| 599 |
| 600 status = launchpad_environ(lp, program_environment_); |
| 601 CHECK_FOR_ERROR(status, "launchpad_environ"); |
| 602 |
| 603 // TODO(zra): Use the supplied working directory when launchpad adds an |
| 604 // API to set it. |
| 605 |
| 606 status = launchpad_clone_mxio_root(lp); |
| 607 CHECK_FOR_ERROR(status, "launchpad_clone_mxio_root"); |
| 608 |
| 609 status = launchpad_add_pipe(lp, &write_out_, 0); |
| 610 CHECK_FOR_ERROR(status, "launchpad_add_pipe"); |
| 611 |
| 612 status = launchpad_add_pipe(lp, &read_in_, 1); |
| 613 CHECK_FOR_ERROR(status, "launchpad_add_pipe"); |
| 614 |
| 615 status = launchpad_add_pipe(lp, &read_err_, 2); |
| 616 CHECK_FOR_ERROR(status, "launchpad_add_pipe"); |
| 617 |
| 618 status = launchpad_add_vdso_vmo(lp); |
| 619 CHECK_FOR_ERROR(status, "launchpad_add_vdso_vmo"); |
| 620 |
| 621 status = launchpad_elf_load(lp, binary_vmo); |
| 622 CHECK_FOR_ERROR(status, "launchpad_elf_load"); |
| 623 binary_vmo_ = MX_HANDLE_INVALID; // launchpad_elf_load consumes the handle. |
| 624 |
| 625 status = launchpad_load_vdso(lp, MX_HANDLE_INVALID); |
| 626 CHECK_FOR_ERROR(status, "launchpad_load_vdso"); |
| 627 |
| 628 status = launchpad_clone_mxio_cwd(lp); |
| 629 CHECK_FOR_ERROR(status, "launchpad_clone_mxio_cwd"); |
| 630 |
| 631 return NO_ERROR; |
| 632 } |
| 633 |
| 634 #undef CHECK_FOR_ERROR |
| 635 |
| 636 int read_in_; // Pipe for stdout to child process. |
| 637 int read_err_; // Pipe for stderr to child process. |
| 638 int write_out_; // Pipe for stdin to child process. |
| 639 |
| 640 char** program_arguments_; |
| 641 intptr_t program_arguments_count_; |
| 642 char** program_environment_; |
| 643 |
| 644 mx_handle_t binary_vmo_; |
| 645 launchpad_t* launchpad_; |
| 646 |
| 647 const char* path_; |
| 648 const char* working_directory_; |
| 649 ProcessStartMode mode_; |
| 650 intptr_t* in_; |
| 651 intptr_t* out_; |
| 652 intptr_t* err_; |
| 653 intptr_t* id_; |
| 654 intptr_t* exit_event_; |
| 655 char** os_error_message_; |
| 656 |
| 657 DISALLOW_ALLOCATION(); |
| 658 DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessStarter); |
| 659 }; |
| 660 |
| 661 |
55 int Process::Start(const char* path, | 662 int Process::Start(const char* path, |
56 char* arguments[], | 663 char* arguments[], |
57 intptr_t arguments_length, | 664 intptr_t arguments_length, |
58 const char* working_directory, | 665 const char* working_directory, |
59 char* environment[], | 666 char* environment[], |
60 intptr_t environment_length, | 667 intptr_t environment_length, |
61 ProcessStartMode mode, | 668 ProcessStartMode mode, |
62 intptr_t* in, | 669 intptr_t* in, |
63 intptr_t* out, | 670 intptr_t* out, |
64 intptr_t* err, | 671 intptr_t* err, |
65 intptr_t* id, | 672 intptr_t* id, |
66 intptr_t* exit_event, | 673 intptr_t* exit_event, |
67 char** os_error_message) { | 674 char** os_error_message) { |
| 675 if (mode != kNormal) { |
| 676 *os_error_message = DartUtils::ScopedCopyCString( |
| 677 "Only ProcessStartMode.NORMAL is supported on this platform"); |
| 678 return -1; |
| 679 } |
| 680 ProcessStarter starter(path, arguments, arguments_length, working_directory, |
| 681 environment, environment_length, mode, in, out, err, |
| 682 id, exit_event, os_error_message); |
| 683 return starter.Start(); |
| 684 } |
| 685 |
| 686 |
| 687 intptr_t Process::SetSignalHandler(intptr_t signal) { |
68 UNIMPLEMENTED(); | 688 UNIMPLEMENTED(); |
69 return -1; | 689 return -1; |
70 } | 690 } |
71 | 691 |
| 692 |
| 693 void Process::ClearSignalHandler(intptr_t signal) { |
| 694 UNIMPLEMENTED(); |
| 695 } |
| 696 |
72 } // namespace bin | 697 } // namespace bin |
73 } // namespace dart | 698 } // namespace dart |
74 | 699 |
75 #endif // defined(TARGET_OS_FUCHSIA) | 700 #endif // defined(TARGET_OS_FUCHSIA) |
76 | 701 |
77 #endif // !defined(DART_IO_DISABLED) | 702 #endif // !defined(DART_IO_DISABLED) |
OLD | NEW |