| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 #include "platform/globals.h" | 5 #include "platform/globals.h" |
| 6 #if defined(TARGET_OS_MACOS) | 6 #if defined(TARGET_OS_MACOS) |
| 7 | 7 |
| 8 #include "bin/process.h" | 8 #include "bin/process.h" |
| 9 | 9 |
| 10 #include <errno.h> // NOLINT | 10 #include <errno.h> // NOLINT |
| (...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 223 static Monitor* monitor_; | 223 static Monitor* monitor_; |
| 224 }; | 224 }; |
| 225 | 225 |
| 226 | 226 |
| 227 bool ExitCodeHandler::running_ = false; | 227 bool ExitCodeHandler::running_ = false; |
| 228 int ExitCodeHandler::process_count_ = 0; | 228 int ExitCodeHandler::process_count_ = 0; |
| 229 bool ExitCodeHandler::terminate_done_ = false; | 229 bool ExitCodeHandler::terminate_done_ = false; |
| 230 Monitor* ExitCodeHandler::monitor_ = new Monitor(); | 230 Monitor* ExitCodeHandler::monitor_ = new Monitor(); |
| 231 | 231 |
| 232 | 232 |
| 233 static void SetChildOsErrorMessage(char** os_error_message) { | 233 class ProcessStarter { |
| 234 const int kBufferSize = 1024; | 234 public: |
| 235 char error_message[kBufferSize]; | 235 ProcessStarter(const char* path, |
| 236 strerror_r(errno, error_message, kBufferSize); | 236 char* arguments[], |
| 237 *os_error_message = strdup(error_message); | 237 intptr_t arguments_length, |
| 238 } | 238 const char* working_directory, |
| 239 | 239 char* environment[], |
| 240 | 240 intptr_t environment_length, |
| 241 static void ReportChildError(int exec_control_fd) { | 241 bool detach, |
| 242 // In the case of failure in the child process write the errno and | 242 intptr_t* in, |
| 243 // the OS error message to the exec control pipe and exit. | 243 intptr_t* out, |
| 244 int child_errno = errno; | 244 intptr_t* err, |
| 245 const int kBufferSize = 1024; | 245 intptr_t* id, |
| 246 char os_error_message[kBufferSize]; | 246 intptr_t* exit_event, |
| 247 strerror_r(errno, os_error_message, kBufferSize); | 247 char** os_error_message) |
| 248 ASSERT(sizeof(child_errno) == sizeof(errno)); | 248 : path_(path), |
| 249 int bytes_written = | 249 working_directory_(working_directory), |
| 250 detach_(detach), |
| 251 in_(in), |
| 252 out_(out), |
| 253 err_(err), |
| 254 id_(id), |
| 255 exit_event_(exit_event), |
| 256 os_error_message_(os_error_message) { |
| 257 read_in_[0] = -1; |
| 258 read_in_[1] = -1; |
| 259 read_err_[0] = -1; |
| 260 read_err_[1] = -1; |
| 261 write_out_[0] = -1; |
| 262 write_out_[1] = -1; |
| 263 exec_control_[0] = -1; |
| 264 exec_control_[1] = -1; |
| 265 |
| 266 program_arguments_ = new char*[arguments_length + 2]; |
| 267 program_arguments_[0] = const_cast<char*>(path_); |
| 268 for (int i = 0; i < arguments_length; i++) { |
| 269 program_arguments_[i + 1] = arguments[i]; |
| 270 } |
| 271 program_arguments_[arguments_length + 1] = NULL; |
| 272 |
| 273 program_environment_ = NULL; |
| 274 if (environment != NULL) { |
| 275 program_environment_ = new char*[environment_length + 1]; |
| 276 for (int i = 0; i < environment_length; i++) { |
| 277 program_environment_[i] = environment[i]; |
| 278 } |
| 279 program_environment_[environment_length] = NULL; |
| 280 } |
| 281 } |
| 282 |
| 283 |
| 284 ~ProcessStarter() { |
| 285 delete[] program_arguments_; |
| 286 delete[] program_environment_; |
| 287 } |
| 288 |
| 289 |
| 290 int Start() { |
| 291 // Create pipes required. |
| 292 int err = CreatePipes(); |
| 293 if (err != 0) return err; |
| 294 |
| 295 // Fork to create the new process. |
| 296 pid_t pid = TEMP_FAILURE_RETRY(fork()); |
| 297 if (pid < 0) { |
| 298 // Failed to fork. |
| 299 return CleanupAndReturnError(); |
| 300 } else if (pid == 0) { |
| 301 // This runs in the new process. |
| 302 NewProcess(); |
| 303 } |
| 304 |
| 305 // This runs in the original process. |
| 306 |
| 307 // Be sure to listen for exit-codes, now we have a child-process. |
| 308 ExitCodeHandler::ProcessStarted(); |
| 309 |
| 310 // Register the child process if not detached. |
| 311 if (!detach_) { |
| 312 err = RegisterProcess(pid); |
| 313 if (err != 0) return err; |
| 314 } |
| 315 |
| 316 // Notify child process to start. This is done to delay the call to exec |
| 317 // until the process is registered above, and we are ready to receive the |
| 318 // exit code. |
| 319 char msg = '1'; |
| 320 int bytes_written = |
| 321 FDUtils::WriteToBlocking(read_in_[1], &msg, sizeof(msg)); |
| 322 if (bytes_written != sizeof(msg)) { |
| 323 return CleanupAndReturnError(); |
| 324 } |
| 325 |
| 326 // Read the result of executing the child process. |
| 327 VOID_TEMP_FAILURE_RETRY(close(exec_control_[1])); |
| 328 exec_control_[1] = -1; |
| 329 if (!detach_) { |
| 330 err = ReadExecResult(); |
| 331 } else { |
| 332 err = ReadDetachedExecResult(&pid); |
| 333 } |
| 334 VOID_TEMP_FAILURE_RETRY(close(exec_control_[0])); |
| 335 exec_control_[0] = -1; |
| 336 |
| 337 // Return error code if any failures. |
| 338 if (err != 0) { |
| 339 if (!detach_) { |
| 340 // Since exec() failed, we're not interested in the exit code. |
| 341 // We close the reading side of the exit code pipe here. |
| 342 // GetProcessExitCodes will get a broken pipe error when it |
| 343 // tries to write to the writing side of the pipe and it will |
| 344 // ignore the error. |
| 345 VOID_TEMP_FAILURE_RETRY(close(*exit_event_)); |
| 346 *exit_event_ = -1; |
| 347 } |
| 348 CloseAllPipes(); |
| 349 return err; |
| 350 } |
| 351 |
| 352 if (!detach_) { |
| 353 // Connect stdio, stdout and stderr. |
| 354 FDUtils::SetNonBlocking(read_in_[0]); |
| 355 *in_ = read_in_[0]; |
| 356 VOID_TEMP_FAILURE_RETRY(close(read_in_[1])); |
| 357 FDUtils::SetNonBlocking(write_out_[1]); |
| 358 *out_ = write_out_[1]; |
| 359 VOID_TEMP_FAILURE_RETRY(close(write_out_[0])); |
| 360 FDUtils::SetNonBlocking(read_err_[0]); |
| 361 *err_ = read_err_[0]; |
| 362 VOID_TEMP_FAILURE_RETRY(close(read_err_[1])); |
| 363 } else { |
| 364 // Close all fds. |
| 365 VOID_TEMP_FAILURE_RETRY(close(read_in_[0])); |
| 366 VOID_TEMP_FAILURE_RETRY(close(read_in_[1])); |
| 367 ASSERT(write_out_[0] == -1); |
| 368 ASSERT(write_out_[1] == -1); |
| 369 ASSERT(read_err_[0] == -1); |
| 370 ASSERT(read_err_[1] == -1); |
| 371 } |
| 372 ASSERT(exec_control_[0] == -1); |
| 373 ASSERT(exec_control_[1] == -1); |
| 374 |
| 375 *id_ = pid; |
| 376 return 0; |
| 377 } |
| 378 |
| 379 private: |
| 380 int CreatePipes() { |
| 381 int result; |
| 382 result = TEMP_FAILURE_RETRY(pipe(exec_control_)); |
| 383 if (result < 0) { |
| 384 return CleanupAndReturnError(); |
| 385 } |
| 386 FDUtils::SetCloseOnExec(exec_control_[0]); |
| 387 FDUtils::SetCloseOnExec(exec_control_[1]); |
| 388 |
| 389 // For a detached process the pipe to connect stdout is still used for |
| 390 // signaling when to do the first fork. |
| 391 result = TEMP_FAILURE_RETRY(pipe(read_in_)); |
| 392 if (result < 0) { |
| 393 return CleanupAndReturnError(); |
| 394 } |
| 395 FDUtils::SetCloseOnExec(read_in_[0]); |
| 396 |
| 397 // For detached processes the pipe to connect stderr and stdin are not used. |
| 398 if (!detach_) { |
| 399 result = TEMP_FAILURE_RETRY(pipe(read_err_)); |
| 400 if (result < 0) { |
| 401 return CleanupAndReturnError(); |
| 402 } |
| 403 FDUtils::SetCloseOnExec(read_err_[0]); |
| 404 |
| 405 result = TEMP_FAILURE_RETRY(pipe(write_out_)); |
| 406 if (result < 0) { |
| 407 return CleanupAndReturnError(); |
| 408 } |
| 409 FDUtils::SetCloseOnExec(write_out_[1]); |
| 410 } |
| 411 |
| 412 return 0; |
| 413 } |
| 414 |
| 415 |
| 416 void NewProcess() { |
| 417 // Wait for parent process before setting up the child process. |
| 418 char msg; |
| 419 int bytes_read = FDUtils::ReadFromBlocking(read_in_[0], &msg, sizeof(msg)); |
| 420 if (bytes_read != sizeof(msg)) { |
| 421 perror("Failed receiving notification message"); |
| 422 exit(1); |
| 423 } |
| 424 if (detach_) { |
| 425 ExecDetachedProcess(); |
| 426 } else { |
| 427 ExecProcess(); |
| 428 } |
| 429 } |
| 430 |
| 431 |
| 432 void ExecProcess() { |
| 433 VOID_TEMP_FAILURE_RETRY(close(write_out_[1])); |
| 434 VOID_TEMP_FAILURE_RETRY(close(read_in_[0])); |
| 435 VOID_TEMP_FAILURE_RETRY(close(read_err_[0])); |
| 436 VOID_TEMP_FAILURE_RETRY(close(exec_control_[0])); |
| 437 |
| 438 if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) { |
| 439 ReportChildError(); |
| 440 } |
| 441 VOID_TEMP_FAILURE_RETRY(close(write_out_[0])); |
| 442 |
| 443 if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) { |
| 444 ReportChildError(); |
| 445 } |
| 446 VOID_TEMP_FAILURE_RETRY(close(read_in_[1])); |
| 447 |
| 448 if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) { |
| 449 ReportChildError(); |
| 450 } |
| 451 VOID_TEMP_FAILURE_RETRY(close(read_err_[1])); |
| 452 |
| 453 if (working_directory_ != NULL && |
| 454 TEMP_FAILURE_RETRY(chdir(working_directory_)) == -1) { |
| 455 ReportChildError(); |
| 456 } |
| 457 |
| 458 if (program_environment_ != NULL) { |
| 459 environ = program_environment_; |
| 460 } |
| 461 |
| 462 VOID_TEMP_FAILURE_RETRY( |
| 463 execvp(path_, const_cast<char* const*>(program_arguments_))); |
| 464 |
| 465 ReportChildError(); |
| 466 } |
| 467 |
| 468 |
| 469 void ExecDetachedProcess() { |
| 470 ASSERT(write_out_[0] == -1); |
| 471 ASSERT(write_out_[1] == -1); |
| 472 ASSERT(read_err_[0] == -1); |
| 473 ASSERT(read_err_[1] == -1); |
| 474 // For a detached process the pipe to connect stdout is only used for |
| 475 // signaling when to do the first fork. |
| 476 VOID_TEMP_FAILURE_RETRY(close(read_in_[0])); |
| 477 VOID_TEMP_FAILURE_RETRY(close(read_in_[1])); |
| 478 // Fork once more to start a new session. |
| 479 pid_t pid = TEMP_FAILURE_RETRY(fork()); |
| 480 if (pid < 0) { |
| 481 ReportChildError(); |
| 482 } else if (pid == 0) { |
| 483 // Start a new session. |
| 484 if (TEMP_FAILURE_RETRY(setsid()) == -1) { |
| 485 ReportChildError(); |
| 486 } else { |
| 487 // Do a final fork to not be the session leader. |
| 488 pid = TEMP_FAILURE_RETRY(fork()); |
| 489 if (pid < 0) { |
| 490 ReportChildError(); |
| 491 } else if (pid == 0) { |
| 492 // Close all open file descriptors except for exec_control_[1]. |
| 493 int max_fds = sysconf(_SC_OPEN_MAX); |
| 494 if (max_fds == -1) max_fds = _POSIX_OPEN_MAX; |
| 495 for (int fd = 0; fd < max_fds; fd++) { |
| 496 if (fd != exec_control_[1]) { |
| 497 VOID_TEMP_FAILURE_RETRY(close(fd)); |
| 498 } |
| 499 } |
| 500 |
| 501 // Re-open stdin, stdout and stderr and connect them to /dev/null. |
| 502 // The loop above should already have closed all of them, so |
| 503 // creating new file descriptors should start at STDIN_FILENO. |
| 504 int fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)); |
| 505 if (fd != STDIN_FILENO) { |
| 506 ReportChildError(); |
| 507 } |
| 508 if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDOUT_FILENO)) != |
| 509 STDOUT_FILENO) { |
| 510 ReportChildError(); |
| 511 } |
| 512 if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDERR_FILENO)) != |
| 513 STDERR_FILENO) { |
| 514 ReportChildError(); |
| 515 } |
| 516 |
| 517 // Report the final PID and do the exec. |
| 518 ReportPid(getpid()); // getpid cannot fail. |
| 519 VOID_TEMP_FAILURE_RETRY( |
| 520 execvp(path_, const_cast<char* const*>(program_arguments_))); |
| 521 ReportChildError(); |
| 522 } else { |
| 523 // Exit the intermeiate process. |
| 524 exit(0); |
| 525 } |
| 526 } |
| 527 } else { |
| 528 // Exit the intermeiate process. |
| 529 exit(0); |
| 530 } |
| 531 } |
| 532 |
| 533 |
| 534 int RegisterProcess(pid_t pid) { |
| 535 int result; |
| 536 int event_fds[2]; |
| 537 result = TEMP_FAILURE_RETRY(pipe(event_fds)); |
| 538 if (result < 0) { |
| 539 return CleanupAndReturnError(); |
| 540 } |
| 541 FDUtils::SetCloseOnExec(event_fds[0]); |
| 542 FDUtils::SetCloseOnExec(event_fds[1]); |
| 543 |
| 544 ProcessInfoList::AddProcess(pid, event_fds[1]); |
| 545 *exit_event_ = event_fds[0]; |
| 546 FDUtils::SetNonBlocking(event_fds[0]); |
| 547 return 0; |
| 548 } |
| 549 |
| 550 |
| 551 int ReadExecResult() { |
| 552 int child_errno; |
| 553 int bytes_read = -1; |
| 554 // Read exec result from child. If no data is returned the exec was |
| 555 // successful and the exec call closed the pipe. Otherwise the errno |
| 556 // is written to the pipe. |
| 557 bytes_read = |
| 558 FDUtils::ReadFromBlocking( |
| 559 exec_control_[0], &child_errno, sizeof(child_errno)); |
| 560 if (bytes_read == sizeof(child_errno)) { |
| 561 ReadChildError(); |
| 562 return child_errno; |
| 563 } else if (bytes_read == -1) { |
| 564 return errno; |
| 565 } |
| 566 return 0; |
| 567 } |
| 568 |
| 569 |
| 570 int ReadDetachedExecResult(pid_t *pid) { |
| 571 int child_errno; |
| 572 int bytes_read = -1; |
| 573 // Read exec result from child. If only pid data is returned the exec was |
| 574 // successful and the exec call closed the pipe. Otherwise the errno |
| 575 // is written to the pipe as well. |
| 576 int result[2]; |
| 577 bytes_read = |
| 578 FDUtils::ReadFromBlocking( |
| 579 exec_control_[0], result, sizeof(result)); |
| 580 if (bytes_read == sizeof(int)) { |
| 581 *pid = result[0]; |
| 582 } else if (bytes_read == 2 * sizeof(int)) { |
| 583 *pid = result[0]; |
| 584 child_errno = result[1]; |
| 585 ReadChildError(); |
| 586 return child_errno; |
| 587 } else if (bytes_read == -1) { |
| 588 return errno; |
| 589 } |
| 590 return 0; |
| 591 } |
| 592 |
| 593 |
| 594 int CleanupAndReturnError() { |
| 595 int actual_errno = errno; |
| 596 // If CleanupAndReturnError is called without an actual errno make |
| 597 // sure to return an error anyway. |
| 598 if (actual_errno == 0) actual_errno = EPERM; |
| 599 SetChildOsErrorMessage(); |
| 600 CloseAllPipes(); |
| 601 return actual_errno; |
| 602 } |
| 603 |
| 604 |
| 605 void SetChildOsErrorMessage() { |
| 606 const int kBufferSize = 1024; |
| 607 char error_message[kBufferSize]; |
| 608 strerror_r(errno, error_message, kBufferSize); |
| 609 *os_error_message_ = strdup(error_message); |
| 610 } |
| 611 |
| 612 |
| 613 void ReportChildError() { |
| 614 // In the case of failure in the child process write the errno and |
| 615 // the OS error message to the exec control pipe and exit. |
| 616 int child_errno = errno; |
| 617 const int kBufferSize = 1024; |
| 618 char os_error_message[kBufferSize]; |
| 619 strerror_r(errno, os_error_message, kBufferSize); |
| 620 int bytes_written = |
| 621 FDUtils::WriteToBlocking( |
| 622 exec_control_[1], &child_errno, sizeof(child_errno)); |
| 623 if (bytes_written == sizeof(child_errno)) { |
| 250 FDUtils::WriteToBlocking( | 624 FDUtils::WriteToBlocking( |
| 251 exec_control_fd, &child_errno, sizeof(child_errno)); | 625 exec_control_[1], os_error_message, strlen(os_error_message) + 1); |
| 252 if (bytes_written == sizeof(child_errno)) { | 626 } |
| 253 FDUtils::WriteToBlocking( | 627 VOID_TEMP_FAILURE_RETRY(close(exec_control_[1])); |
| 254 exec_control_fd, os_error_message, strlen(os_error_message) + 1); | 628 exit(1); |
| 255 } | 629 } |
| 256 VOID_TEMP_FAILURE_RETRY(close(exec_control_fd)); | 630 |
| 257 exit(1); | 631 |
| 258 } | 632 void ReportPid(int pid) { |
| 259 | 633 // In the case of starting a detached process the actual pid of that process |
| 260 | 634 // is communicated using the exec control pipe. |
| 261 static void ReportPid(int exec_control_fd, int pid) { | 635 int bytes_written = |
| 262 // In the case of starting a detached process the actual pid of that process | 636 FDUtils::WriteToBlocking(exec_control_[1], &pid, sizeof(pid)); |
| 263 // is communicated using the exec control pipe. | 637 ASSERT(bytes_written == sizeof(int)); |
| 264 int bytes_written = | 638 USE(bytes_written); |
| 265 FDUtils::WriteToBlocking(exec_control_fd, &pid, sizeof(pid)); | 639 } |
| 266 ASSERT(bytes_written == sizeof(int)); | 640 |
| 267 USE(bytes_written); | 641 |
| 268 } | 642 void ReadChildError() { |
| 269 | 643 const int kMaxMessageSize = 256; |
| 270 | 644 char* message = static_cast<char*>(malloc(kMaxMessageSize)); |
| 271 static void ReadChildError(int exec_control_fd, char** error_message) { | 645 if (message != NULL) { |
| 272 const int kMaxMessageSize = 256; | 646 FDUtils::ReadFromBlocking(exec_control_[0], message, kMaxMessageSize); |
| 273 char* message = static_cast<char*>(malloc(kMaxMessageSize)); | 647 message[kMaxMessageSize - 1] = '\0'; |
| 274 if (message != NULL) { | 648 *os_error_message_ = message; |
| 275 FDUtils::ReadFromBlocking(exec_control_fd, message, kMaxMessageSize); | 649 } else { |
| 276 message[kMaxMessageSize - 1] = '\0'; | 650 // Could not get error message. It will be NULL. |
| 277 *error_message = message; | 651 ASSERT(*os_error_message_ == NULL); |
| 278 } else { | 652 } |
| 279 static const char* no_message = "Cannot get error message, out of memory"; | 653 } |
| 280 *error_message = const_cast<char*>(no_message); | 654 |
| 281 } | 655 |
| 282 } | 656 void ClosePipe(int* fds) { |
| 657 for (int i = 0; i < 2; i++) { |
| 658 if (fds[i] != -1) { |
| 659 VOID_TEMP_FAILURE_RETRY(close(fds[i])); |
| 660 fds[i] = -1; |
| 661 } |
| 662 } |
| 663 } |
| 664 |
| 665 |
| 666 void CloseAllPipes() { |
| 667 ClosePipe(exec_control_); |
| 668 ClosePipe(read_in_); |
| 669 ClosePipe(read_err_); |
| 670 ClosePipe(write_out_); |
| 671 } |
| 672 |
| 673 |
| 674 int read_in_[2]; // Pipe for stdout to child process. |
| 675 int read_err_[2]; // Pipe for stderr to child process. |
| 676 int write_out_[2]; // Pipe for stdin to child process. |
| 677 int exec_control_[2]; // Pipe to get the result from exec. |
| 678 |
| 679 char** program_arguments_; |
| 680 char** program_environment_; |
| 681 |
| 682 const char* path_; |
| 683 const char* working_directory_; |
| 684 bool detach_; |
| 685 intptr_t* in_; |
| 686 intptr_t* out_; |
| 687 intptr_t* err_; |
| 688 intptr_t* id_; |
| 689 intptr_t* exit_event_; |
| 690 char** os_error_message_; |
| 691 }; |
| 283 | 692 |
| 284 | 693 |
| 285 int Process::Start(const char* path, | 694 int Process::Start(const char* path, |
| 286 char* arguments[], | 695 char* arguments[], |
| 287 intptr_t arguments_length, | 696 intptr_t arguments_length, |
| 288 const char* working_directory, | 697 const char* working_directory, |
| 289 char* environment[], | 698 char* environment[], |
| 290 intptr_t environment_length, | 699 intptr_t environment_length, |
| 291 bool detach, | 700 bool detach, |
| 292 intptr_t* in, | 701 intptr_t* in, |
| 293 intptr_t* out, | 702 intptr_t* out, |
| 294 intptr_t* err, | 703 intptr_t* err, |
| 295 intptr_t* id, | 704 intptr_t* id, |
| 296 intptr_t* exit_event, | 705 intptr_t* exit_event, |
| 297 char** os_error_message) { | 706 char** os_error_message) { |
| 298 pid_t pid; | 707 ProcessStarter starter(path, |
| 299 int read_in[2] = {-1, -1}; // Pipe for stdout to child process. | 708 arguments, |
| 300 int read_err[2] = {-1, -1}; // Pipe for stderr to child process. | 709 arguments_length, |
| 301 int write_out[2] = {-1, -1}; // Pipe for stdin to child process. | 710 working_directory, |
| 302 int exec_control[2] = {-1, -1}; // Pipe to get the result from exec. | 711 environment, |
| 303 int result; | 712 environment_length, |
| 304 | 713 detach, |
| 305 result = TEMP_FAILURE_RETRY(pipe(exec_control)); | 714 in, |
| 306 if (result < 0) { | 715 out, |
| 307 SetChildOsErrorMessage(os_error_message); | 716 err, |
| 308 Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message); | 717 id, |
| 309 return errno; | 718 exit_event, |
| 310 } | 719 os_error_message); |
| 311 FDUtils::SetCloseOnExec(exec_control[0]); | 720 return starter.Start(); |
| 312 FDUtils::SetCloseOnExec(exec_control[1]); | |
| 313 | |
| 314 // For a detached process the pipe to connect stdout is still used for | |
| 315 // signaling when to do the first fork. | |
| 316 result = TEMP_FAILURE_RETRY(pipe(read_in)); | |
| 317 if (result < 0) { | |
| 318 SetChildOsErrorMessage(os_error_message); | |
| 319 VOID_TEMP_FAILURE_RETRY(close(exec_control[0])); | |
| 320 VOID_TEMP_FAILURE_RETRY(close(exec_control[1])); | |
| 321 Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message); | |
| 322 return errno; | |
| 323 } | |
| 324 FDUtils::SetCloseOnExec(read_in[0]); | |
| 325 | |
| 326 // For detached processes the pipe to connect stderr and stdin are not used. | |
| 327 if (!detach) { | |
| 328 result = TEMP_FAILURE_RETRY(pipe(read_err)); | |
| 329 if (result < 0) { | |
| 330 SetChildOsErrorMessage(os_error_message); | |
| 331 VOID_TEMP_FAILURE_RETRY(close(exec_control[0])); | |
| 332 VOID_TEMP_FAILURE_RETRY(close(exec_control[1])); | |
| 333 VOID_TEMP_FAILURE_RETRY(close(read_in[0])); | |
| 334 VOID_TEMP_FAILURE_RETRY(close(read_in[1])); | |
| 335 Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message); | |
| 336 return errno; | |
| 337 } | |
| 338 FDUtils::SetCloseOnExec(read_err[0]); | |
| 339 | |
| 340 result = TEMP_FAILURE_RETRY(pipe(write_out)); | |
| 341 if (result < 0) { | |
| 342 SetChildOsErrorMessage(os_error_message); | |
| 343 VOID_TEMP_FAILURE_RETRY(close(exec_control[0])); | |
| 344 VOID_TEMP_FAILURE_RETRY(close(exec_control[1])); | |
| 345 VOID_TEMP_FAILURE_RETRY(close(read_in[0])); | |
| 346 VOID_TEMP_FAILURE_RETRY(close(read_in[1])); | |
| 347 VOID_TEMP_FAILURE_RETRY(close(read_err[0])); | |
| 348 VOID_TEMP_FAILURE_RETRY(close(read_err[1])); | |
| 349 Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message); | |
| 350 return errno; | |
| 351 } | |
| 352 FDUtils::SetCloseOnExec(write_out[1]); | |
| 353 } | |
| 354 | |
| 355 char** program_arguments = new char*[arguments_length + 2]; | |
| 356 program_arguments[0] = const_cast<char*>(path); | |
| 357 for (int i = 0; i < arguments_length; i++) { | |
| 358 program_arguments[i + 1] = arguments[i]; | |
| 359 } | |
| 360 program_arguments[arguments_length + 1] = NULL; | |
| 361 | |
| 362 char** program_environment = NULL; | |
| 363 if (environment != NULL) { | |
| 364 program_environment = new char*[environment_length + 1]; | |
| 365 for (int i = 0; i < environment_length; i++) { | |
| 366 program_environment[i] = environment[i]; | |
| 367 } | |
| 368 program_environment[environment_length] = NULL; | |
| 369 } | |
| 370 | |
| 371 pid = TEMP_FAILURE_RETRY(fork()); | |
| 372 if (pid < 0) { | |
| 373 SetChildOsErrorMessage(os_error_message); | |
| 374 delete[] program_arguments; | |
| 375 VOID_TEMP_FAILURE_RETRY(close(exec_control[0])); | |
| 376 VOID_TEMP_FAILURE_RETRY(close(exec_control[1])); | |
| 377 VOID_TEMP_FAILURE_RETRY(close(read_in[0])); | |
| 378 VOID_TEMP_FAILURE_RETRY(close(read_in[1])); | |
| 379 if (!detach) { | |
| 380 VOID_TEMP_FAILURE_RETRY(close(read_err[0])); | |
| 381 VOID_TEMP_FAILURE_RETRY(close(read_err[1])); | |
| 382 VOID_TEMP_FAILURE_RETRY(close(write_out[0])); | |
| 383 VOID_TEMP_FAILURE_RETRY(close(write_out[1])); | |
| 384 } | |
| 385 return errno; | |
| 386 } else if (pid == 0) { | |
| 387 // Wait for parent process before setting up the child process. | |
| 388 char msg; | |
| 389 int bytes_read = FDUtils::ReadFromBlocking(read_in[0], &msg, sizeof(msg)); | |
| 390 if (bytes_read != sizeof(msg)) { | |
| 391 perror("Failed receiving notification message"); | |
| 392 exit(1); | |
| 393 } | |
| 394 if (detach) { | |
| 395 // For a detached process the pipe to connect stdout is only used for | |
| 396 // signaling when to do the first fork. | |
| 397 VOID_TEMP_FAILURE_RETRY(close(read_in[0])); | |
| 398 VOID_TEMP_FAILURE_RETRY(close(read_in[1])); | |
| 399 // Fork once more to start a new session. | |
| 400 pid = TEMP_FAILURE_RETRY(fork()); | |
| 401 if (pid < 0) { | |
| 402 ReportChildError(exec_control[1]); | |
| 403 } else if (pid == 0) { | |
| 404 // Start a new session. | |
| 405 if (TEMP_FAILURE_RETRY(setsid()) == -1) { | |
| 406 ReportChildError(exec_control[1]); | |
| 407 } else { | |
| 408 // Do a final fork to not be the session leader. | |
| 409 pid = TEMP_FAILURE_RETRY(fork()); | |
| 410 if (pid < 0) { | |
| 411 ReportChildError(exec_control[1]); | |
| 412 } else if (pid == 0) { | |
| 413 // Close all open file descriptors except for exec_control[1]. | |
| 414 int max_fds = sysconf(_SC_OPEN_MAX); | |
| 415 if (max_fds == -1) max_fds = _POSIX_OPEN_MAX; | |
| 416 for (int fd = 0; fd < max_fds; fd++) { | |
| 417 if (fd != exec_control[1]) { | |
| 418 VOID_TEMP_FAILURE_RETRY(close(fd)); | |
| 419 } | |
| 420 } | |
| 421 | |
| 422 // Re-open stdin, stdout and stderr and connect them to /dev/null. | |
| 423 // The loop above should already have closed all of them, so | |
| 424 // creating new file descriptors should start at STDIN_FILENO. | |
| 425 int fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)); | |
| 426 if (fd != STDIN_FILENO) { | |
| 427 ReportChildError(exec_control[1]); | |
| 428 } | |
| 429 if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDOUT_FILENO)) != | |
| 430 STDOUT_FILENO) { | |
| 431 ReportChildError(exec_control[1]); | |
| 432 } | |
| 433 if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDERR_FILENO)) != | |
| 434 STDERR_FILENO) { | |
| 435 ReportChildError(exec_control[1]); | |
| 436 } | |
| 437 | |
| 438 // Report the final PID and do the exec. | |
| 439 ReportPid(exec_control[1], getpid()); // getpid cannot fail. | |
| 440 VOID_TEMP_FAILURE_RETRY( | |
| 441 execvp(path, const_cast<char* const*>(program_arguments))); | |
| 442 ReportChildError(exec_control[1]); | |
| 443 } else { | |
| 444 exit(0); | |
| 445 } | |
| 446 } | |
| 447 } else { | |
| 448 exit(0); | |
| 449 } | |
| 450 } else { | |
| 451 VOID_TEMP_FAILURE_RETRY(close(write_out[1])); | |
| 452 VOID_TEMP_FAILURE_RETRY(close(read_in[0])); | |
| 453 VOID_TEMP_FAILURE_RETRY(close(read_err[0])); | |
| 454 VOID_TEMP_FAILURE_RETRY(close(exec_control[0])); | |
| 455 | |
| 456 if (TEMP_FAILURE_RETRY(dup2(write_out[0], STDIN_FILENO)) == -1) { | |
| 457 ReportChildError(exec_control[1]); | |
| 458 } | |
| 459 VOID_TEMP_FAILURE_RETRY(close(write_out[0])); | |
| 460 | |
| 461 if (TEMP_FAILURE_RETRY(dup2(read_in[1], STDOUT_FILENO)) == -1) { | |
| 462 ReportChildError(exec_control[1]); | |
| 463 } | |
| 464 VOID_TEMP_FAILURE_RETRY(close(read_in[1])); | |
| 465 | |
| 466 if (TEMP_FAILURE_RETRY(dup2(read_err[1], STDERR_FILENO)) == -1) { | |
| 467 ReportChildError(exec_control[1]); | |
| 468 } | |
| 469 VOID_TEMP_FAILURE_RETRY(close(read_err[1])); | |
| 470 | |
| 471 if (working_directory != NULL && | |
| 472 TEMP_FAILURE_RETRY(chdir(working_directory)) == -1) { | |
| 473 ReportChildError(exec_control[1]); | |
| 474 } | |
| 475 | |
| 476 if (program_environment != NULL) { | |
| 477 environ = program_environment; | |
| 478 } | |
| 479 | |
| 480 VOID_TEMP_FAILURE_RETRY( | |
| 481 execvp(path, const_cast<char* const*>(program_arguments))); | |
| 482 | |
| 483 ReportChildError(exec_control[1]); | |
| 484 } | |
| 485 } | |
| 486 | |
| 487 // Be sure to listen for exit-codes, now we have a child-process. | |
| 488 ExitCodeHandler::ProcessStarted(); | |
| 489 | |
| 490 // The arguments and environment for the spawned process are not needed | |
| 491 // any longer. | |
| 492 delete[] program_arguments; | |
| 493 delete[] program_environment; | |
| 494 | |
| 495 if (!detach) { | |
| 496 int event_fds[2]; | |
| 497 result = TEMP_FAILURE_RETRY(pipe(event_fds)); | |
| 498 if (result < 0) { | |
| 499 SetChildOsErrorMessage(os_error_message); | |
| 500 VOID_TEMP_FAILURE_RETRY(close(read_in[0])); | |
| 501 VOID_TEMP_FAILURE_RETRY(close(read_in[1])); | |
| 502 VOID_TEMP_FAILURE_RETRY(close(read_err[0])); | |
| 503 VOID_TEMP_FAILURE_RETRY(close(read_err[1])); | |
| 504 VOID_TEMP_FAILURE_RETRY(close(write_out[0])); | |
| 505 VOID_TEMP_FAILURE_RETRY(close(write_out[1])); | |
| 506 Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message); | |
| 507 return errno; | |
| 508 } | |
| 509 FDUtils::SetCloseOnExec(event_fds[0]); | |
| 510 FDUtils::SetCloseOnExec(event_fds[1]); | |
| 511 | |
| 512 ProcessInfoList::AddProcess(pid, event_fds[1]); | |
| 513 *exit_event = event_fds[0]; | |
| 514 FDUtils::SetNonBlocking(event_fds[0]); | |
| 515 } | |
| 516 | |
| 517 // Notify child process to start. | |
| 518 char msg = '1'; | |
| 519 result = FDUtils::WriteToBlocking(read_in[1], &msg, sizeof(msg)); | |
| 520 if (result != sizeof(msg)) { | |
| 521 perror("Failed sending notification message"); | |
| 522 } | |
| 523 | |
| 524 VOID_TEMP_FAILURE_RETRY(close(exec_control[1])); | |
| 525 bool failed = false; | |
| 526 int child_errno; | |
| 527 int bytes_read = -1; | |
| 528 ASSERT(sizeof(child_errno) == sizeof(errno)); | |
| 529 if (!detach) { | |
| 530 // Read exec result from child. If no data is returned the exec was | |
| 531 // successful and the exec call closed the pipe. Otherwise the errno | |
| 532 // is written to the pipe. | |
| 533 bytes_read = | |
| 534 FDUtils::ReadFromBlocking( | |
| 535 exec_control[0], &child_errno, sizeof(child_errno)); | |
| 536 if (bytes_read == sizeof(child_errno)) { | |
| 537 ReadChildError(exec_control[0], os_error_message); | |
| 538 failed = true; | |
| 539 } | |
| 540 } else { | |
| 541 // Read exec result from child. If only pid data is returned the exec was | |
| 542 // successful and the exec call closed the pipe. Otherwise the errno | |
| 543 // is written to the pipe as well. | |
| 544 int result[2]; | |
| 545 ASSERT(sizeof(int) == sizeof(child_errno)); | |
| 546 bytes_read = | |
| 547 FDUtils::ReadFromBlocking( | |
| 548 exec_control[0], result, sizeof(result)); | |
| 549 if (bytes_read == sizeof(int)) { | |
| 550 pid = result[0]; | |
| 551 } else if (bytes_read == 2 * sizeof(int)) { | |
| 552 pid = result[0]; | |
| 553 child_errno = result[1]; | |
| 554 ReadChildError(exec_control[0], os_error_message); | |
| 555 failed = true; | |
| 556 } | |
| 557 } | |
| 558 VOID_TEMP_FAILURE_RETRY(close(exec_control[0])); | |
| 559 | |
| 560 // Return error code if any failures. | |
| 561 if (failed) { | |
| 562 if (!detach) { | |
| 563 VOID_TEMP_FAILURE_RETRY(close(read_in[0])); | |
| 564 VOID_TEMP_FAILURE_RETRY(close(read_in[1])); | |
| 565 VOID_TEMP_FAILURE_RETRY(close(read_err[0])); | |
| 566 VOID_TEMP_FAILURE_RETRY(close(read_err[1])); | |
| 567 VOID_TEMP_FAILURE_RETRY(close(write_out[0])); | |
| 568 VOID_TEMP_FAILURE_RETRY(close(write_out[1])); | |
| 569 | |
| 570 // Since exec() failed, we're not interested in the exit code. | |
| 571 // We close the reading side of the exit code pipe here. | |
| 572 // GetProcessExitCodes will get a broken pipe error when it tries to write | |
| 573 // to the writing side of the pipe and it will ignore the error. | |
| 574 VOID_TEMP_FAILURE_RETRY(close(*exit_event)); | |
| 575 *exit_event = -1; | |
| 576 } | |
| 577 if (bytes_read == -1) { | |
| 578 return errno; // Read failed. | |
| 579 } else { | |
| 580 return child_errno; // Exec failed. | |
| 581 } | |
| 582 } | |
| 583 | |
| 584 FDUtils::SetNonBlocking(read_in[0]); | |
| 585 *in = read_in[0]; | |
| 586 VOID_TEMP_FAILURE_RETRY(close(read_in[1])); | |
| 587 FDUtils::SetNonBlocking(write_out[1]); | |
| 588 *out = write_out[1]; | |
| 589 VOID_TEMP_FAILURE_RETRY(close(write_out[0])); | |
| 590 FDUtils::SetNonBlocking(read_err[0]); | |
| 591 *err = read_err[0]; | |
| 592 VOID_TEMP_FAILURE_RETRY(close(read_err[1])); | |
| 593 | |
| 594 *id = pid; | |
| 595 return 0; | |
| 596 } | 721 } |
| 597 | 722 |
| 598 | 723 |
| 599 class BufferList: public BufferListBase { | 724 class BufferList: public BufferListBase { |
| 600 public: | 725 public: |
| 601 bool Read(int fd, intptr_t available) { | 726 bool Read(int fd, intptr_t available) { |
| 602 // Read all available bytes. | 727 // Read all available bytes. |
| 603 while (available > 0) { | 728 while (available > 0) { |
| 604 if (free_size_ == 0) Allocate(); | 729 if (free_size_ == 0) Allocate(); |
| 605 ASSERT(free_size_ > 0); | 730 ASSERT(free_size_ > 0); |
| (...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 882 bzero(&act, sizeof(act)); | 1007 bzero(&act, sizeof(act)); |
| 883 act.sa_handler = SIG_DFL; | 1008 act.sa_handler = SIG_DFL; |
| 884 VOID_NO_RETRY_EXPECTED(sigaction(signal, &act, NULL)); | 1009 VOID_NO_RETRY_EXPECTED(sigaction(signal, &act, NULL)); |
| 885 } | 1010 } |
| 886 } | 1011 } |
| 887 | 1012 |
| 888 } // namespace bin | 1013 } // namespace bin |
| 889 } // namespace dart | 1014 } // namespace dart |
| 890 | 1015 |
| 891 #endif // defined(TARGET_OS_MACOS) | 1016 #endif // defined(TARGET_OS_MACOS) |
| OLD | NEW |