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