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

Side by Side Diff: runtime/bin/process_linux.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_android.cc ('k') | runtime/bin/process_macos.cc » ('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_LINUX) 6 #if defined(TARGET_OS_LINUX)
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 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 22 matching lines...) Expand all
459 } 459 }
460 460
461 VOID_TEMP_FAILURE_RETRY( 461 VOID_TEMP_FAILURE_RETRY(
462 execvp(path_, const_cast<char* const*>(program_arguments_))); 462 execvp(path_, const_cast<char* const*>(program_arguments_)));
463 463
464 ReportChildError(); 464 ReportChildError();
465 } 465 }
466 466
467 467
468 void ExecDetachedProcess() { 468 void ExecDetachedProcess() {
469 ASSERT(write_out_[0] == -1); 469 if (mode_ == kDetached) {
470 ASSERT(write_out_[1] == -1); 470 ASSERT(write_out_[0] == -1);
471 ASSERT(read_err_[0] == -1); 471 ASSERT(write_out_[1] == -1);
472 ASSERT(read_err_[1] == -1); 472 ASSERT(read_err_[0] == -1);
473 // For a detached process the pipe to connect stdout is only used for 473 ASSERT(read_err_[1] == -1);
474 // signaling when to do the first fork. 474 // For a detached process the pipe to connect stdout is only used for
475 VOID_TEMP_FAILURE_RETRY(close(read_in_[0])); 475 // signaling when to do the first fork.
476 VOID_TEMP_FAILURE_RETRY(close(read_in_[1])); 476 VOID_TEMP_FAILURE_RETRY(close(read_in_[0]));
477 read_in_[0] = -1;
478 VOID_TEMP_FAILURE_RETRY(close(read_in_[1]));
479 read_in_[1] = -1;
480 } else {
481 // Don't close any fds if keeping stdio open to the detached process.
482 ASSERT(mode_ == kDetachedWithStdio);
483 }
477 // Fork once more to start a new session. 484 // Fork once more to start a new session.
478 pid_t pid = TEMP_FAILURE_RETRY(fork()); 485 pid_t pid = TEMP_FAILURE_RETRY(fork());
479 if (pid < 0) { 486 if (pid < 0) {
480 ReportChildError(); 487 ReportChildError();
481 } else if (pid == 0) { 488 } else if (pid == 0) {
482 // Start a new session. 489 // Start a new session.
483 if (TEMP_FAILURE_RETRY(setsid()) == -1) { 490 if (TEMP_FAILURE_RETRY(setsid()) == -1) {
484 ReportChildError(); 491 ReportChildError();
485 } else { 492 } else {
486 // Do a final fork to not be the session leader. 493 // Do a final fork to not be the session leader.
487 pid = TEMP_FAILURE_RETRY(fork()); 494 pid = TEMP_FAILURE_RETRY(fork());
488 if (pid < 0) { 495 if (pid < 0) {
489 ReportChildError(); 496 ReportChildError();
490 } else if (pid == 0) { 497 } else if (pid == 0) {
491 // Close all open file descriptors except for exec_control_[1]. 498 if (mode_ == kDetached) {
492 int max_fds = sysconf(_SC_OPEN_MAX); 499 SetupDetached();
493 if (max_fds == -1) max_fds = _POSIX_OPEN_MAX; 500 } else {
494 for (int fd = 0; fd < max_fds; fd++) { 501 SetupDetachedWithStdio();
495 if (fd != exec_control_[1]) {
496 VOID_TEMP_FAILURE_RETRY(close(fd));
497 }
498 } 502 }
499 503
500 // Re-open stdin, stdout and stderr and connect them to /dev/null. 504 if (working_directory_ != NULL &&
501 // The loop above should already have closed all of them, so 505 TEMP_FAILURE_RETRY(chdir(working_directory_)) == -1) {
502 // creating new file descriptors should start at STDIN_FILENO.
503 int fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
504 if (fd != STDIN_FILENO) {
505 ReportChildError();
506 }
507 if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDOUT_FILENO)) !=
508 STDOUT_FILENO) {
509 ReportChildError();
510 }
511 if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDERR_FILENO)) !=
512 STDERR_FILENO) {
513 ReportChildError(); 506 ReportChildError();
514 } 507 }
515 508
516 // Report the final PID and do the exec. 509 // Report the final PID and do the exec.
517 ReportPid(getpid()); // getpid cannot fail. 510 ReportPid(getpid()); // getpid cannot fail.
518 VOID_TEMP_FAILURE_RETRY( 511 VOID_TEMP_FAILURE_RETRY(
519 execvp(path_, const_cast<char* const*>(program_arguments_))); 512 execvp(path_, const_cast<char* const*>(program_arguments_)));
520 ReportChildError(); 513 ReportChildError();
521 } else { 514 } else {
522 // Exit the intermeiate process. 515 // Exit the intermeiate process.
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
583 child_errno = result[1]; 576 child_errno = result[1];
584 ReadChildError(); 577 ReadChildError();
585 return child_errno; 578 return child_errno;
586 } else if (bytes_read == -1) { 579 } else if (bytes_read == -1) {
587 return errno; 580 return errno;
588 } 581 }
589 return 0; 582 return 0;
590 } 583 }
591 584
592 585
586 void SetupDetached() {
587 ASSERT(mode_ == kDetached);
588
589 // Close all open file descriptors except for exec_control_[1].
590 int max_fds = sysconf(_SC_OPEN_MAX);
591 if (max_fds == -1) max_fds = _POSIX_OPEN_MAX;
592 for (int fd = 0; fd < max_fds; fd++) {
593 if (fd != exec_control_[1]) {
594 VOID_TEMP_FAILURE_RETRY(close(fd));
595 }
596 }
597
598 // Re-open stdin, stdout and stderr and connect them to /dev/null.
599 // The loop above should already have closed all of them, so
600 // creating new file descriptors should start at STDIN_FILENO.
601 int fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
602 if (fd != STDIN_FILENO) {
603 ReportChildError();
604 }
605 if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDOUT_FILENO)) !=
606 STDOUT_FILENO) {
607 ReportChildError();
608 }
609 if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDERR_FILENO)) !=
610 STDERR_FILENO) {
611 ReportChildError();
612 }
613 }
614
615 void SetupDetachedWithStdio() {
616 // Close all open file descriptors except for
617 // exec_control_[1], write_out_[0], read_in_[1] and
618 // read_err_[1].
619 int max_fds = sysconf(_SC_OPEN_MAX);
620 if (max_fds == -1) max_fds = _POSIX_OPEN_MAX;
621 for (int fd = 0; fd < max_fds; fd++) {
622 if (fd != exec_control_[1] &&
623 fd != write_out_[0] &&
624 fd != read_in_[1] &&
625 fd != read_err_[1]) {
626 VOID_TEMP_FAILURE_RETRY(close(fd));
627 }
628 }
629
630 if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) {
631 ReportChildError();
632 }
633 VOID_TEMP_FAILURE_RETRY(close(write_out_[0]));
634
635 if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) {
636 ReportChildError();
637 }
638 VOID_TEMP_FAILURE_RETRY(close(read_in_[1]));
639
640 if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) {
641 ReportChildError();
642 }
643 VOID_TEMP_FAILURE_RETRY(close(read_err_[1]));
644 }
645
646
593 int CleanupAndReturnError() { 647 int CleanupAndReturnError() {
594 int actual_errno = errno; 648 int actual_errno = errno;
595 // If CleanupAndReturnError is called without an actual errno make 649 // If CleanupAndReturnError is called without an actual errno make
596 // sure to return an error anyway. 650 // sure to return an error anyway.
597 if (actual_errno == 0) actual_errno = EPERM; 651 if (actual_errno == 0) actual_errno = EPERM;
598 SetChildOsErrorMessage(); 652 SetChildOsErrorMessage();
599 CloseAllPipes(); 653 CloseAllPipes();
600 return actual_errno; 654 return actual_errno;
601 } 655 }
602 656
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
672 int read_in_[2]; // Pipe for stdout to child process. 726 int read_in_[2]; // Pipe for stdout to child process.
673 int read_err_[2]; // Pipe for stderr to child process. 727 int read_err_[2]; // Pipe for stderr to child process.
674 int write_out_[2]; // Pipe for stdin to child process. 728 int write_out_[2]; // Pipe for stdin to child process.
675 int exec_control_[2]; // Pipe to get the result from exec. 729 int exec_control_[2]; // Pipe to get the result from exec.
676 730
677 char** program_arguments_; 731 char** program_arguments_;
678 char** program_environment_; 732 char** program_environment_;
679 733
680 const char* path_; 734 const char* path_;
681 const char* working_directory_; 735 const char* working_directory_;
682 bool detach_; 736 ProcessStartMode mode_;
683 intptr_t* in_; 737 intptr_t* in_;
684 intptr_t* out_; 738 intptr_t* out_;
685 intptr_t* err_; 739 intptr_t* err_;
686 intptr_t* id_; 740 intptr_t* id_;
687 intptr_t* exit_event_; 741 intptr_t* exit_event_;
688 char** os_error_message_; 742 char** os_error_message_;
689 }; 743 };
690 744
691 745
692 int Process::Start(const char* path, 746 int Process::Start(const char* path,
693 char* arguments[], 747 char* arguments[],
694 intptr_t arguments_length, 748 intptr_t arguments_length,
695 const char* working_directory, 749 const char* working_directory,
696 char* environment[], 750 char* environment[],
697 intptr_t environment_length, 751 intptr_t environment_length,
698 bool detach, 752 ProcessStartMode mode,
699 intptr_t* in, 753 intptr_t* in,
700 intptr_t* out, 754 intptr_t* out,
701 intptr_t* err, 755 intptr_t* err,
702 intptr_t* id, 756 intptr_t* id,
703 intptr_t* exit_event, 757 intptr_t* exit_event,
704 char** os_error_message) { 758 char** os_error_message) {
705 ProcessStarter starter(path, 759 ProcessStarter starter(path,
706 arguments, 760 arguments,
707 arguments_length, 761 arguments_length,
708 working_directory, 762 working_directory,
709 environment, 763 environment,
710 environment_length, 764 environment_length,
711 detach, 765 mode,
712 in, 766 in,
713 out, 767 out,
714 err, 768 err,
715 id, 769 id,
716 exit_event, 770 exit_event,
717 os_error_message); 771 os_error_message);
718 return starter.Start(); 772 return starter.Start();
719 } 773 }
720 774
721 775
(...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after
954 bzero(&act, sizeof(act)); 1008 bzero(&act, sizeof(act));
955 act.sa_handler = SIG_DFL; 1009 act.sa_handler = SIG_DFL;
956 sigaction(signal, &act, NULL); 1010 sigaction(signal, &act, NULL);
957 } 1011 }
958 } 1012 }
959 1013
960 } // namespace bin 1014 } // namespace bin
961 } // namespace dart 1015 } // namespace dart
962 1016
963 #endif // defined(TARGET_OS_LINUX) 1017 #endif // defined(TARGET_OS_LINUX)
OLDNEW
« no previous file with comments | « runtime/bin/process_android.cc ('k') | runtime/bin/process_macos.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698