Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(76)

Side by Side Diff: runtime/bin/process_macos.cc

Issue 890633002: Add an option for starting a detached process with stdio connected (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Rebased Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « runtime/bin/process_linux.cc ('k') | runtime/bin/process_patch.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 <crt_externs.h> // NOLINT 10 #include <crt_externs.h> // NOLINT
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after
230 230
231 231
232 class ProcessStarter { 232 class ProcessStarter {
233 public: 233 public:
234 ProcessStarter(const char* path, 234 ProcessStarter(const char* path,
235 char* arguments[], 235 char* arguments[],
236 intptr_t arguments_length, 236 intptr_t arguments_length,
237 const char* working_directory, 237 const char* working_directory,
238 char* environment[], 238 char* environment[],
239 intptr_t environment_length, 239 intptr_t environment_length,
240 bool detach, 240 ProcessStartMode mode,
241 intptr_t* in, 241 intptr_t* in,
242 intptr_t* out, 242 intptr_t* out,
243 intptr_t* err, 243 intptr_t* err,
244 intptr_t* id, 244 intptr_t* id,
245 intptr_t* exit_event, 245 intptr_t* exit_event,
246 char** os_error_message) 246 char** os_error_message)
247 : path_(path), 247 : path_(path),
248 working_directory_(working_directory), 248 working_directory_(working_directory),
249 detach_(detach), 249 mode_(mode),
250 in_(in), 250 in_(in),
251 out_(out), 251 out_(out),
252 err_(err), 252 err_(err),
253 id_(id), 253 id_(id),
254 exit_event_(exit_event), 254 exit_event_(exit_event),
255 os_error_message_(os_error_message) { 255 os_error_message_(os_error_message) {
256 read_in_[0] = -1; 256 read_in_[0] = -1;
257 read_in_[1] = -1; 257 read_in_[1] = -1;
258 read_err_[0] = -1; 258 read_err_[0] = -1;
259 read_err_[1] = -1; 259 read_err_[1] = -1;
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
300 // This runs in the new process. 300 // This runs in the new process.
301 NewProcess(); 301 NewProcess();
302 } 302 }
303 303
304 // This runs in the original process. 304 // This runs in the original process.
305 305
306 // Be sure to listen for exit-codes, now we have a child-process. 306 // Be sure to listen for exit-codes, now we have a child-process.
307 ExitCodeHandler::ProcessStarted(); 307 ExitCodeHandler::ProcessStarted();
308 308
309 // Register the child process if not detached. 309 // Register the child process if not detached.
310 if (!detach_) { 310 if (mode_ == kNormal) {
311 err = RegisterProcess(pid); 311 err = RegisterProcess(pid);
312 if (err != 0) return err; 312 if (err != 0) return err;
313 } 313 }
314 314
315 // Notify child process to start. This is done to delay the call to exec 315 // Notify child process to start. This is done to delay the call to exec
316 // until the process is registered above, and we are ready to receive the 316 // until the process is registered above, and we are ready to receive the
317 // exit code. 317 // exit code.
318 char msg = '1'; 318 char msg = '1';
319 int bytes_written = 319 int bytes_written =
320 FDUtils::WriteToBlocking(read_in_[1], &msg, sizeof(msg)); 320 FDUtils::WriteToBlocking(read_in_[1], &msg, sizeof(msg));
321 if (bytes_written != sizeof(msg)) { 321 if (bytes_written != sizeof(msg)) {
322 return CleanupAndReturnError(); 322 return CleanupAndReturnError();
323 } 323 }
324 324
325 // Read the result of executing the child process. 325 // Read the result of executing the child process.
326 VOID_TEMP_FAILURE_RETRY(close(exec_control_[1])); 326 VOID_TEMP_FAILURE_RETRY(close(exec_control_[1]));
327 exec_control_[1] = -1; 327 exec_control_[1] = -1;
328 if (!detach_) { 328 if (mode_ == kNormal) {
329 err = ReadExecResult(); 329 err = ReadExecResult();
330 } else { 330 } else {
331 err = ReadDetachedExecResult(&pid); 331 err = ReadDetachedExecResult(&pid);
332 } 332 }
333 VOID_TEMP_FAILURE_RETRY(close(exec_control_[0])); 333 VOID_TEMP_FAILURE_RETRY(close(exec_control_[0]));
334 exec_control_[0] = -1; 334 exec_control_[0] = -1;
335 335
336 // Return error code if any failures. 336 // Return error code if any failures.
337 if (err != 0) { 337 if (err != 0) {
338 if (!detach_) { 338 if (mode_ == kNormal) {
339 // Since exec() failed, we're not interested in the exit code. 339 // Since exec() failed, we're not interested in the exit code.
340 // We close the reading side of the exit code pipe here. 340 // We close the reading side of the exit code pipe here.
341 // GetProcessExitCodes will get a broken pipe error when it 341 // GetProcessExitCodes will get a broken pipe error when it
342 // tries to write to the writing side of the pipe and it will 342 // tries to write to the writing side of the pipe and it will
343 // ignore the error. 343 // ignore the error.
344 VOID_TEMP_FAILURE_RETRY(close(*exit_event_)); 344 VOID_TEMP_FAILURE_RETRY(close(*exit_event_));
345 *exit_event_ = -1; 345 *exit_event_ = -1;
346 } 346 }
347 CloseAllPipes(); 347 CloseAllPipes();
348 return err; 348 return err;
349 } 349 }
350 350
351 if (!detach_) { 351 if (mode_ != kDetached) {
352 // Connect stdio, stdout and stderr. 352 // Connect stdio, stdout and stderr.
353 FDUtils::SetNonBlocking(read_in_[0]); 353 FDUtils::SetNonBlocking(read_in_[0]);
354 *in_ = read_in_[0]; 354 *in_ = read_in_[0];
355 VOID_TEMP_FAILURE_RETRY(close(read_in_[1])); 355 VOID_TEMP_FAILURE_RETRY(close(read_in_[1]));
356 FDUtils::SetNonBlocking(write_out_[1]); 356 FDUtils::SetNonBlocking(write_out_[1]);
357 *out_ = write_out_[1]; 357 *out_ = write_out_[1];
358 VOID_TEMP_FAILURE_RETRY(close(write_out_[0])); 358 VOID_TEMP_FAILURE_RETRY(close(write_out_[0]));
359 FDUtils::SetNonBlocking(read_err_[0]); 359 FDUtils::SetNonBlocking(read_err_[0]);
360 *err_ = read_err_[0]; 360 *err_ = read_err_[0];
361 VOID_TEMP_FAILURE_RETRY(close(read_err_[1])); 361 VOID_TEMP_FAILURE_RETRY(close(read_err_[1]));
(...skipping 25 matching lines...) Expand all
387 387
388 // For a detached process the pipe to connect stdout is still used for 388 // For a detached process the pipe to connect stdout is still used for
389 // signaling when to do the first fork. 389 // signaling when to do the first fork.
390 result = TEMP_FAILURE_RETRY(pipe(read_in_)); 390 result = TEMP_FAILURE_RETRY(pipe(read_in_));
391 if (result < 0) { 391 if (result < 0) {
392 return CleanupAndReturnError(); 392 return CleanupAndReturnError();
393 } 393 }
394 FDUtils::SetCloseOnExec(read_in_[0]); 394 FDUtils::SetCloseOnExec(read_in_[0]);
395 395
396 // For detached processes the pipe to connect stderr and stdin are not used. 396 // For detached processes the pipe to connect stderr and stdin are not used.
397 if (!detach_) { 397 if (mode_ != kDetached) {
398 result = TEMP_FAILURE_RETRY(pipe(read_err_)); 398 result = TEMP_FAILURE_RETRY(pipe(read_err_));
399 if (result < 0) { 399 if (result < 0) {
400 return CleanupAndReturnError(); 400 return CleanupAndReturnError();
401 } 401 }
402 FDUtils::SetCloseOnExec(read_err_[0]); 402 FDUtils::SetCloseOnExec(read_err_[0]);
403 403
404 result = TEMP_FAILURE_RETRY(pipe(write_out_)); 404 result = TEMP_FAILURE_RETRY(pipe(write_out_));
405 if (result < 0) { 405 if (result < 0) {
406 return CleanupAndReturnError(); 406 return CleanupAndReturnError();
407 } 407 }
408 FDUtils::SetCloseOnExec(write_out_[1]); 408 FDUtils::SetCloseOnExec(write_out_[1]);
409 } 409 }
410 410
411 return 0; 411 return 0;
412 } 412 }
413 413
414 414
415 void NewProcess() { 415 void NewProcess() {
416 // Wait for parent process before setting up the child process. 416 // Wait for parent process before setting up the child process.
417 char msg; 417 char msg;
418 int bytes_read = FDUtils::ReadFromBlocking(read_in_[0], &msg, sizeof(msg)); 418 int bytes_read = FDUtils::ReadFromBlocking(read_in_[0], &msg, sizeof(msg));
419 if (bytes_read != sizeof(msg)) { 419 if (bytes_read != sizeof(msg)) {
420 perror("Failed receiving notification message"); 420 perror("Failed receiving notification message");
421 exit(1); 421 exit(1);
422 } 422 }
423 if (detach_) { 423 if (mode_ == kNormal) {
424 ExecProcess();
425 } else {
424 ExecDetachedProcess(); 426 ExecDetachedProcess();
425 } else {
426 ExecProcess();
427 } 427 }
428 } 428 }
429 429
430 430
431 void ExecProcess() { 431 void ExecProcess() {
432 VOID_TEMP_FAILURE_RETRY(close(write_out_[1])); 432 VOID_TEMP_FAILURE_RETRY(close(write_out_[1]));
433 VOID_TEMP_FAILURE_RETRY(close(read_in_[0])); 433 VOID_TEMP_FAILURE_RETRY(close(read_in_[0]));
434 VOID_TEMP_FAILURE_RETRY(close(read_err_[0])); 434 VOID_TEMP_FAILURE_RETRY(close(read_err_[0]));
435 VOID_TEMP_FAILURE_RETRY(close(exec_control_[0])); 435 VOID_TEMP_FAILURE_RETRY(close(exec_control_[0]));
436 436
(...skipping 25 matching lines...) Expand all
462 } 462 }
463 463
464 VOID_TEMP_FAILURE_RETRY( 464 VOID_TEMP_FAILURE_RETRY(
465 execvp(path_, const_cast<char* const*>(program_arguments_))); 465 execvp(path_, const_cast<char* const*>(program_arguments_)));
466 466
467 ReportChildError(); 467 ReportChildError();
468 } 468 }
469 469
470 470
471 void ExecDetachedProcess() { 471 void ExecDetachedProcess() {
472 ASSERT(write_out_[0] == -1); 472 if (mode_ == kDetached) {
473 ASSERT(write_out_[1] == -1); 473 ASSERT(write_out_[0] == -1);
474 ASSERT(read_err_[0] == -1); 474 ASSERT(write_out_[1] == -1);
475 ASSERT(read_err_[1] == -1); 475 ASSERT(read_err_[0] == -1);
476 // For a detached process the pipe to connect stdout is only used for 476 ASSERT(read_err_[1] == -1);
477 // signaling when to do the first fork. 477 // For a detached process the pipe to connect stdout is only used for
478 VOID_TEMP_FAILURE_RETRY(close(read_in_[0])); 478 // signaling when to do the first fork.
479 VOID_TEMP_FAILURE_RETRY(close(read_in_[1])); 479 VOID_TEMP_FAILURE_RETRY(close(read_in_[0]));
480 read_in_[0] = -1;
481 VOID_TEMP_FAILURE_RETRY(close(read_in_[1]));
482 read_in_[1] = -1;
483 } else {
484 // Don't close any fds if keeping stdio open to the detached process.
485 ASSERT(mode_ == kDetachedWithStdio);
486 }
480 // Fork once more to start a new session. 487 // Fork once more to start a new session.
481 pid_t pid = TEMP_FAILURE_RETRY(fork()); 488 pid_t pid = TEMP_FAILURE_RETRY(fork());
482 if (pid < 0) { 489 if (pid < 0) {
483 ReportChildError(); 490 ReportChildError();
484 } else if (pid == 0) { 491 } else if (pid == 0) {
485 // Start a new session. 492 // Start a new session.
486 if (TEMP_FAILURE_RETRY(setsid()) == -1) { 493 if (TEMP_FAILURE_RETRY(setsid()) == -1) {
487 ReportChildError(); 494 ReportChildError();
488 } else { 495 } else {
489 // Do a final fork to not be the session leader. 496 // Do a final fork to not be the session leader.
490 pid = TEMP_FAILURE_RETRY(fork()); 497 pid = TEMP_FAILURE_RETRY(fork());
491 if (pid < 0) { 498 if (pid < 0) {
492 ReportChildError(); 499 ReportChildError();
493 } else if (pid == 0) { 500 } else if (pid == 0) {
494 // Close all open file descriptors except for exec_control_[1]. 501 if (mode_ == kDetached) {
495 int max_fds = sysconf(_SC_OPEN_MAX); 502 SetupDetached();
496 if (max_fds == -1) max_fds = _POSIX_OPEN_MAX; 503 } else {
497 for (int fd = 0; fd < max_fds; fd++) { 504 SetupDetachedWithStdio();
498 if (fd != exec_control_[1]) {
499 VOID_TEMP_FAILURE_RETRY(close(fd));
500 }
501 } 505 }
502 506
503 // Re-open stdin, stdout and stderr and connect them to /dev/null. 507 if (working_directory_ != NULL &&
504 // The loop above should already have closed all of them, so 508 TEMP_FAILURE_RETRY(chdir(working_directory_)) == -1) {
505 // creating new file descriptors should start at STDIN_FILENO.
506 int fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
507 if (fd != STDIN_FILENO) {
508 ReportChildError();
509 }
510 if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDOUT_FILENO)) !=
511 STDOUT_FILENO) {
512 ReportChildError();
513 }
514 if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDERR_FILENO)) !=
515 STDERR_FILENO) {
516 ReportChildError(); 509 ReportChildError();
517 } 510 }
518 511
519 // Report the final PID and do the exec. 512 // Report the final PID and do the exec.
520 ReportPid(getpid()); // getpid cannot fail. 513 ReportPid(getpid()); // getpid cannot fail.
521 VOID_TEMP_FAILURE_RETRY( 514 VOID_TEMP_FAILURE_RETRY(
522 execvp(path_, const_cast<char* const*>(program_arguments_))); 515 execvp(path_, const_cast<char* const*>(program_arguments_)));
523 ReportChildError(); 516 ReportChildError();
524 } else { 517 } else {
525 // Exit the intermeiate process. 518 // Exit the intermeiate process.
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
586 child_errno = result[1]; 579 child_errno = result[1];
587 ReadChildError(); 580 ReadChildError();
588 return child_errno; 581 return child_errno;
589 } else if (bytes_read == -1) { 582 } else if (bytes_read == -1) {
590 return errno; 583 return errno;
591 } 584 }
592 return 0; 585 return 0;
593 } 586 }
594 587
595 588
589 void SetupDetached() {
590 ASSERT(mode_ == kDetached);
591
592 // Close all open file descriptors except for exec_control_[1].
593 int max_fds = sysconf(_SC_OPEN_MAX);
594 if (max_fds == -1) max_fds = _POSIX_OPEN_MAX;
595 for (int fd = 0; fd < max_fds; fd++) {
596 if (fd != exec_control_[1]) {
597 VOID_TEMP_FAILURE_RETRY(close(fd));
598 }
599 }
600
601 // Re-open stdin, stdout and stderr and connect them to /dev/null.
602 // The loop above should already have closed all of them, so
603 // creating new file descriptors should start at STDIN_FILENO.
604 int fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
605 if (fd != STDIN_FILENO) {
606 ReportChildError();
607 }
608 if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDOUT_FILENO)) !=
609 STDOUT_FILENO) {
610 ReportChildError();
611 }
612 if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDERR_FILENO)) !=
613 STDERR_FILENO) {
614 ReportChildError();
615 }
616 }
617
618 void SetupDetachedWithStdio() {
619 // Close all open file descriptors except for
620 // exec_control_[1], write_out_[0], read_in_[1] and
621 // read_err_[1].
622 int max_fds = sysconf(_SC_OPEN_MAX);
623 if (max_fds == -1) max_fds = _POSIX_OPEN_MAX;
624 for (int fd = 0; fd < max_fds; fd++) {
625 if (fd != exec_control_[1] &&
626 fd != write_out_[0] &&
627 fd != read_in_[1] &&
628 fd != read_err_[1]) {
629 VOID_TEMP_FAILURE_RETRY(close(fd));
630 }
631 }
632
633 if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) {
634 ReportChildError();
635 }
636 VOID_TEMP_FAILURE_RETRY(close(write_out_[0]));
637
638 if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) {
639 ReportChildError();
640 }
641 VOID_TEMP_FAILURE_RETRY(close(read_in_[1]));
642
643 if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) {
644 ReportChildError();
645 }
646 VOID_TEMP_FAILURE_RETRY(close(read_err_[1]));
647 }
648
649
596 int CleanupAndReturnError() { 650 int CleanupAndReturnError() {
597 int actual_errno = errno; 651 int actual_errno = errno;
598 // If CleanupAndReturnError is called without an actual errno make 652 // If CleanupAndReturnError is called without an actual errno make
599 // sure to return an error anyway. 653 // sure to return an error anyway.
600 if (actual_errno == 0) actual_errno = EPERM; 654 if (actual_errno == 0) actual_errno = EPERM;
601 SetChildOsErrorMessage(); 655 SetChildOsErrorMessage();
602 CloseAllPipes(); 656 CloseAllPipes();
603 return actual_errno; 657 return actual_errno;
604 } 658 }
605 659
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
676 int read_in_[2]; // Pipe for stdout to child process. 730 int read_in_[2]; // Pipe for stdout to child process.
677 int read_err_[2]; // Pipe for stderr to child process. 731 int read_err_[2]; // Pipe for stderr to child process.
678 int write_out_[2]; // Pipe for stdin to child process. 732 int write_out_[2]; // Pipe for stdin to child process.
679 int exec_control_[2]; // Pipe to get the result from exec. 733 int exec_control_[2]; // Pipe to get the result from exec.
680 734
681 char** program_arguments_; 735 char** program_arguments_;
682 char** program_environment_; 736 char** program_environment_;
683 737
684 const char* path_; 738 const char* path_;
685 const char* working_directory_; 739 const char* working_directory_;
686 bool detach_; 740 ProcessStartMode mode_;
687 intptr_t* in_; 741 intptr_t* in_;
688 intptr_t* out_; 742 intptr_t* out_;
689 intptr_t* err_; 743 intptr_t* err_;
690 intptr_t* id_; 744 intptr_t* id_;
691 intptr_t* exit_event_; 745 intptr_t* exit_event_;
692 char** os_error_message_; 746 char** os_error_message_;
693 }; 747 };
694 748
695 749
696 int Process::Start(const char* path, 750 int Process::Start(const char* path,
697 char* arguments[], 751 char* arguments[],
698 intptr_t arguments_length, 752 intptr_t arguments_length,
699 const char* working_directory, 753 const char* working_directory,
700 char* environment[], 754 char* environment[],
701 intptr_t environment_length, 755 intptr_t environment_length,
702 bool detach, 756 ProcessStartMode mode,
703 intptr_t* in, 757 intptr_t* in,
704 intptr_t* out, 758 intptr_t* out,
705 intptr_t* err, 759 intptr_t* err,
706 intptr_t* id, 760 intptr_t* id,
707 intptr_t* exit_event, 761 intptr_t* exit_event,
708 char** os_error_message) { 762 char** os_error_message) {
709 ProcessStarter starter(path, 763 ProcessStarter starter(path,
710 arguments, 764 arguments,
711 arguments_length, 765 arguments_length,
712 working_directory, 766 working_directory,
713 environment, 767 environment,
714 environment_length, 768 environment_length,
715 detach, 769 mode,
716 in, 770 in,
717 out, 771 out,
718 err, 772 err,
719 id, 773 id,
720 exit_event, 774 exit_event,
721 os_error_message); 775 os_error_message);
722 return starter.Start(); 776 return starter.Start();
723 } 777 }
724 778
725 779
(...skipping 283 matching lines...) Expand 10 before | Expand all | Expand 10 after
1009 bzero(&act, sizeof(act)); 1063 bzero(&act, sizeof(act));
1010 act.sa_handler = SIG_DFL; 1064 act.sa_handler = SIG_DFL;
1011 VOID_NO_RETRY_EXPECTED(sigaction(signal, &act, NULL)); 1065 VOID_NO_RETRY_EXPECTED(sigaction(signal, &act, NULL));
1012 } 1066 }
1013 } 1067 }
1014 1068
1015 } // namespace bin 1069 } // namespace bin
1016 } // namespace dart 1070 } // namespace dart
1017 1071
1018 #endif // defined(TARGET_OS_MACOS) 1072 #endif // defined(TARGET_OS_MACOS)
OLDNEW
« no previous file with comments | « runtime/bin/process_linux.cc ('k') | runtime/bin/process_patch.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698