Chromium Code Reviews| Index: runtime/bin/process_macos.cc |
| diff --git a/runtime/bin/process_macos.cc b/runtime/bin/process_macos.cc |
| index 973452204b6a4b47809d3fd72abde8bf0846b90a..e96d8e0f2166742d1f74951b17e15906a2793d46 100644 |
| --- a/runtime/bin/process_macos.cc |
| +++ b/runtime/bin/process_macos.cc |
| @@ -258,12 +258,37 @@ static void ReportChildError(int exec_control_fd) { |
| } |
| +static void ReportPid(int exec_control_fd, int pid) { |
|
kustermann
2015/01/22 09:33:02
I assume this file is doing the exact same thing a
Søren Gjesse
2015/01/22 11:47:25
It is, I have compared the diffs line-by-line.
|
| + // In the case of starting a detached process the actual pid of that process |
| + // is communicated using the exec control pipe. |
| + int bytes_written = |
| + FDUtils::WriteToBlocking(exec_control_fd, &pid, sizeof(pid)); |
| + ASSERT(bytes_written == sizeof(int)); |
| + USE(bytes_written); |
| +} |
| + |
| + |
| +static void ReadChildError(int exec_control_fd, char** error_message) { |
| + static const int kMaxMessageSize = 256; |
| + char* message = static_cast<char*>(malloc(kMaxMessageSize)); |
| + if (message != NULL) { |
| + FDUtils::ReadFromBlocking(exec_control_fd, message, kMaxMessageSize); |
| + message[kMaxMessageSize - 1] = '\0'; |
| + *error_message = message; |
| + } else { |
| + static const char* no_message = "Cannot get error message, out of memory"; |
| + *error_message = const_cast<char*>(no_message); |
| + } |
| +} |
| + |
| + |
| int Process::Start(const char* path, |
| char* arguments[], |
| intptr_t arguments_length, |
| const char* working_directory, |
| char* environment[], |
| intptr_t environment_length, |
| + bool detach, |
| intptr_t* in, |
| intptr_t* out, |
| intptr_t* err, |
| @@ -271,70 +296,61 @@ int Process::Start(const char* path, |
| intptr_t* exit_event, |
| char** os_error_message) { |
| pid_t pid; |
| - int read_in[2]; // Pipe for stdout to child process. |
| - int read_err[2]; // Pipe for stderr to child process. |
| - int write_out[2]; // Pipe for stdin to child process. |
| - int exec_control[2]; // Pipe to get the result from exec. |
| + int read_in[2] = {-1, -1}; // Pipe for stdout to child process. |
| + int read_err[2] = {-1, -1}; // Pipe for stderr to child process. |
| + int write_out[2] = {-1, -1}; // Pipe for stdin to child process. |
| + int exec_control[2] = {-1, -1}; // Pipe to get the result from exec. |
| int result; |
| - result = TEMP_FAILURE_RETRY(pipe(read_in)); |
| - if (result < 0) { |
| - SetChildOsErrorMessage(os_error_message); |
| - Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message); |
| - return errno; |
| - } |
| - FDUtils::SetCloseOnExec(read_in[0]); |
| - |
| - result = TEMP_FAILURE_RETRY(pipe(read_err)); |
| - if (result < 0) { |
| - SetChildOsErrorMessage(os_error_message); |
| - VOID_TEMP_FAILURE_RETRY(close(read_in[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_in[1])); |
| - Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message); |
| - return errno; |
| - } |
| - FDUtils::SetCloseOnExec(read_err[0]); |
| - |
| - result = TEMP_FAILURE_RETRY(pipe(write_out)); |
| - if (result < 0) { |
| - SetChildOsErrorMessage(os_error_message); |
| - VOID_TEMP_FAILURE_RETRY(close(read_in[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_in[1])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_err[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_err[1])); |
| - Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message); |
| - return errno; |
| - } |
| - FDUtils::SetCloseOnExec(write_out[1]); |
| - |
| result = TEMP_FAILURE_RETRY(pipe(exec_control)); |
| if (result < 0) { |
| SetChildOsErrorMessage(os_error_message); |
| - VOID_TEMP_FAILURE_RETRY(close(read_in[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_in[1])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_err[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_err[1])); |
| - VOID_TEMP_FAILURE_RETRY(close(write_out[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(write_out[1])); |
| Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message); |
| return errno; |
| } |
| FDUtils::SetCloseOnExec(exec_control[0]); |
| FDUtils::SetCloseOnExec(exec_control[1]); |
| + // For a detached process the pipe to connect stdout is still used for |
| + // signaling when to do the first fork. |
| + result = TEMP_FAILURE_RETRY(pipe(read_in)); |
| if (result < 0) { |
| SetChildOsErrorMessage(os_error_message); |
| - VOID_TEMP_FAILURE_RETRY(close(read_in[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_in[1])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_err[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_err[1])); |
| - VOID_TEMP_FAILURE_RETRY(close(write_out[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(write_out[1])); |
| VOID_TEMP_FAILURE_RETRY(close(exec_control[0])); |
| VOID_TEMP_FAILURE_RETRY(close(exec_control[1])); |
| - Log::PrintErr("fcntl failed: %s\n", *os_error_message); |
| + Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message); |
| return errno; |
| } |
| + FDUtils::SetCloseOnExec(read_in[0]); |
| + |
| + // For detached processes the pipe to connect stderr and stdin are not used. |
| + if (!detach) { |
| + result = TEMP_FAILURE_RETRY(pipe(read_err)); |
| + if (result < 0) { |
| + SetChildOsErrorMessage(os_error_message); |
| + VOID_TEMP_FAILURE_RETRY(close(exec_control[0])); |
| + VOID_TEMP_FAILURE_RETRY(close(exec_control[1])); |
| + VOID_TEMP_FAILURE_RETRY(close(read_in[0])); |
| + VOID_TEMP_FAILURE_RETRY(close(read_in[1])); |
| + Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message); |
| + return errno; |
| + } |
| + FDUtils::SetCloseOnExec(read_err[0]); |
| + |
| + result = TEMP_FAILURE_RETRY(pipe(write_out)); |
| + if (result < 0) { |
| + SetChildOsErrorMessage(os_error_message); |
| + VOID_TEMP_FAILURE_RETRY(close(exec_control[0])); |
| + VOID_TEMP_FAILURE_RETRY(close(exec_control[1])); |
| + VOID_TEMP_FAILURE_RETRY(close(read_in[0])); |
| + VOID_TEMP_FAILURE_RETRY(close(read_in[1])); |
| + VOID_TEMP_FAILURE_RETRY(close(read_err[0])); |
| + VOID_TEMP_FAILURE_RETRY(close(read_err[1])); |
| + Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message); |
| + return errno; |
| + } |
| + FDUtils::SetCloseOnExec(write_out[1]); |
| + } |
| char** program_arguments = new char*[arguments_length + 2]; |
| program_arguments[0] = const_cast<char*>(path); |
| @@ -356,14 +372,16 @@ int Process::Start(const char* path, |
| if (pid < 0) { |
| SetChildOsErrorMessage(os_error_message); |
| delete[] program_arguments; |
| - VOID_TEMP_FAILURE_RETRY(close(read_in[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_in[1])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_err[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_err[1])); |
| - VOID_TEMP_FAILURE_RETRY(close(write_out[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(write_out[1])); |
| VOID_TEMP_FAILURE_RETRY(close(exec_control[0])); |
| VOID_TEMP_FAILURE_RETRY(close(exec_control[1])); |
| + VOID_TEMP_FAILURE_RETRY(close(read_in[0])); |
| + VOID_TEMP_FAILURE_RETRY(close(read_in[1])); |
| + if (detach) { |
| + VOID_TEMP_FAILURE_RETRY(close(read_err[0])); |
| + VOID_TEMP_FAILURE_RETRY(close(read_err[1])); |
| + VOID_TEMP_FAILURE_RETRY(close(write_out[0])); |
| + VOID_TEMP_FAILURE_RETRY(close(write_out[1])); |
| + } |
| return errno; |
| } else if (pid == 0) { |
| // Wait for parent process before setting up the child process. |
| @@ -373,40 +391,97 @@ int Process::Start(const char* path, |
| perror("Failed receiving notification message"); |
| exit(1); |
| } |
| + if (detach) { |
| + // For a detached process the pipe to connect stdout is only used for |
| + // signaling when to do the first fork. |
| + VOID_TEMP_FAILURE_RETRY(close(read_in[0])); |
| + VOID_TEMP_FAILURE_RETRY(close(read_in[1])); |
| + // Fork once more to start a new session. |
| + pid = TEMP_FAILURE_RETRY(fork()); |
| + if (pid < 0) { |
| + ReportChildError(exec_control[1]); |
| + } else if (pid == 0) { |
| + // Start a new session. |
| + if (TEMP_FAILURE_RETRY(setsid()) == -1) { |
| + ReportChildError(exec_control[1]); |
| + } else { |
| + // Do a final fork to not be the session leader. |
| + pid = TEMP_FAILURE_RETRY(fork()); |
| + if (pid < 0) { |
| + ReportChildError(exec_control[1]); |
| + } else if (pid == 0) { |
| + // Close all open file descriptors except for exec_control[1]. |
| + int max_fds = sysconf(_SC_OPEN_MAX); |
| + if (max_fds == -1) max_fds = _POSIX_OPEN_MAX; |
| + for (int fd = 0; fd < max_fds; fd++) { |
| + if (fd != exec_control[1]) { |
| + VOID_TEMP_FAILURE_RETRY(close(fd)); |
| + } |
| + } |
| - VOID_TEMP_FAILURE_RETRY(close(write_out[1])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_in[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_err[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(exec_control[0])); |
| + // Re-open stdin, stdout and stderr and connect them to /dev/null. |
| + // The loop above should already have closed all of them, so |
| + // creating new file descriptors should start at STDIN_FILENO. |
| + int fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)); |
| + if (fd != STDIN_FILENO) { |
| + ReportChildError(exec_control[1]); |
| + } |
| + if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDOUT_FILENO)) != |
| + STDOUT_FILENO) { |
| + ReportChildError(exec_control[1]); |
| + } |
| + if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDERR_FILENO)) != |
| + STDERR_FILENO) { |
| + ReportChildError(exec_control[1]); |
| + } |
| - if (TEMP_FAILURE_RETRY(dup2(write_out[0], STDIN_FILENO)) == -1) { |
| - ReportChildError(exec_control[1]); |
| - } |
| - VOID_TEMP_FAILURE_RETRY(close(write_out[0])); |
| + // Report the final PID and do the exec. |
| + ReportPid(exec_control[1], getpid()); // getpid cannot fail. |
| + VOID_TEMP_FAILURE_RETRY( |
| + execvp(path, const_cast<char* const*>(program_arguments))); |
| + ReportChildError(exec_control[1]); |
| + } else { |
| + exit(0); |
| + } |
| + } |
| + } else { |
| + exit(0); |
| + } |
| + } else { |
| + VOID_TEMP_FAILURE_RETRY(close(write_out[1])); |
| + VOID_TEMP_FAILURE_RETRY(close(read_in[0])); |
| + VOID_TEMP_FAILURE_RETRY(close(read_err[0])); |
| + VOID_TEMP_FAILURE_RETRY(close(exec_control[0])); |
| - if (TEMP_FAILURE_RETRY(dup2(read_in[1], STDOUT_FILENO)) == -1) { |
| - ReportChildError(exec_control[1]); |
| - } |
| - VOID_TEMP_FAILURE_RETRY(close(read_in[1])); |
| + if (TEMP_FAILURE_RETRY(dup2(write_out[0], STDIN_FILENO)) == -1) { |
| + ReportChildError(exec_control[1]); |
| + } |
| + VOID_TEMP_FAILURE_RETRY(close(write_out[0])); |
| - if (TEMP_FAILURE_RETRY(dup2(read_err[1], STDERR_FILENO)) == -1) { |
| - ReportChildError(exec_control[1]); |
| - } |
| - VOID_TEMP_FAILURE_RETRY(close(read_err[1])); |
| + if (TEMP_FAILURE_RETRY(dup2(read_in[1], STDOUT_FILENO)) == -1) { |
| + ReportChildError(exec_control[1]); |
| + } |
| + VOID_TEMP_FAILURE_RETRY(close(read_in[1])); |
| - if (working_directory != NULL && |
| - TEMP_FAILURE_RETRY(chdir(working_directory)) == -1) { |
| - ReportChildError(exec_control[1]); |
| - } |
| + if (TEMP_FAILURE_RETRY(dup2(read_err[1], STDERR_FILENO)) == -1) { |
| + ReportChildError(exec_control[1]); |
| + } |
| + VOID_TEMP_FAILURE_RETRY(close(read_err[1])); |
| - if (program_environment != NULL) { |
| - environ = program_environment; |
| - } |
| + if (working_directory != NULL && |
| + TEMP_FAILURE_RETRY(chdir(working_directory)) == -1) { |
| + ReportChildError(exec_control[1]); |
| + } |
| + |
| + if (program_environment != NULL) { |
| + environ = program_environment; |
| + } |
| - VOID_TEMP_FAILURE_RETRY( |
| - execvp(path, const_cast<char* const*>(program_arguments))); |
| + VOID_TEMP_FAILURE_RETRY( |
| + execvp(path, const_cast<char* const*>(program_arguments))); |
| - ReportChildError(exec_control[1]); |
| + ReportChildError(exec_control[1]); |
| + } |
| } |
| // Be sure to listen for exit-codes, now we have a child-process. |
| @@ -417,25 +492,27 @@ int Process::Start(const char* path, |
| delete[] program_arguments; |
| delete[] program_environment; |
| - int event_fds[2]; |
| - result = TEMP_FAILURE_RETRY(pipe(event_fds)); |
| - if (result < 0) { |
| - SetChildOsErrorMessage(os_error_message); |
| - VOID_TEMP_FAILURE_RETRY(close(read_in[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_in[1])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_err[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_err[1])); |
| - VOID_TEMP_FAILURE_RETRY(close(write_out[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(write_out[1])); |
| - Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message); |
| - return errno; |
| - } |
| - FDUtils::SetCloseOnExec(event_fds[0]); |
| - FDUtils::SetCloseOnExec(event_fds[1]); |
| + if (!detach) { |
| + int event_fds[2]; |
| + result = TEMP_FAILURE_RETRY(pipe(event_fds)); |
| + if (result < 0) { |
| + SetChildOsErrorMessage(os_error_message); |
| + VOID_TEMP_FAILURE_RETRY(close(read_in[0])); |
| + VOID_TEMP_FAILURE_RETRY(close(read_in[1])); |
| + VOID_TEMP_FAILURE_RETRY(close(read_err[0])); |
| + VOID_TEMP_FAILURE_RETRY(close(read_err[1])); |
| + VOID_TEMP_FAILURE_RETRY(close(write_out[0])); |
| + VOID_TEMP_FAILURE_RETRY(close(write_out[1])); |
| + Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message); |
| + return errno; |
| + } |
| + FDUtils::SetCloseOnExec(event_fds[0]); |
| + FDUtils::SetCloseOnExec(event_fds[1]); |
| - ProcessInfoList::AddProcess(pid, event_fds[1]); |
| - *exit_event = event_fds[0]; |
| - FDUtils::SetNonBlocking(event_fds[0]); |
| + ProcessInfoList::AddProcess(pid, event_fds[1]); |
| + *exit_event = event_fds[0]; |
| + FDUtils::SetNonBlocking(event_fds[0]); |
| + } |
| // Notify child process to start. |
| char msg = '1'; |
| @@ -448,39 +525,58 @@ int Process::Start(const char* path, |
| // successful and the exec call closed the pipe. Otherwise the errno |
| // is written to the pipe. |
| VOID_TEMP_FAILURE_RETRY(close(exec_control[1])); |
| + bool failed = false; |
| int child_errno; |
| int bytes_read = -1; |
| ASSERT(sizeof(child_errno) == sizeof(errno)); |
| - bytes_read = |
| - FDUtils::ReadFromBlocking( |
| - exec_control[0], &child_errno, sizeof(child_errno)); |
| - if (bytes_read == sizeof(child_errno)) { |
| - static const int kMaxMessageSize = 256; |
| - char* message = static_cast<char*>(malloc(kMaxMessageSize)); |
| - bytes_read = FDUtils::ReadFromBlocking(exec_control[0], |
| - message, |
| - kMaxMessageSize); |
| - message[kMaxMessageSize - 1] = '\0'; |
| - *os_error_message = message; |
| + if (!detach) { |
| + // Read exec result from child. If no data is returned the exec was |
| + // successful and the exec call closed the pipe. Otherwise the errno |
| + // is written to the pipe. |
| + bytes_read = |
| + FDUtils::ReadFromBlocking( |
| + exec_control[0], &child_errno, sizeof(child_errno)); |
| + if (bytes_read == sizeof(child_errno)) { |
| + ReadChildError(exec_control[0], os_error_message); |
| + failed = true; |
| + } |
| + } else { |
| + // Read exec result from child. If only pid data is returned the exec was |
| + // successful and the exec call closed the pipe. Otherwise the errno |
| + // is written to the pipe as well. |
| + int result[2]; |
| + ASSERT(sizeof(int) == sizeof(child_errno)); |
| + bytes_read = |
| + FDUtils::ReadFromBlocking( |
| + exec_control[0], result, sizeof(result)); |
| + if (bytes_read == sizeof(int)) { |
| + pid = result[0]; |
| + } else if (bytes_read == 2 * sizeof(int)) { |
| + pid = result[0]; |
| + child_errno = result[1]; |
| + ReadChildError(exec_control[0], os_error_message); |
| + failed = true; |
| + } |
| } |
| VOID_TEMP_FAILURE_RETRY(close(exec_control[0])); |
| // Return error code if any failures. |
| - if (bytes_read != 0) { |
| - VOID_TEMP_FAILURE_RETRY(close(read_in[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_in[1])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_err[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(read_err[1])); |
| - VOID_TEMP_FAILURE_RETRY(close(write_out[0])); |
| - VOID_TEMP_FAILURE_RETRY(close(write_out[1])); |
| - |
| - // Since exec() failed, we're not interested in the exit code. |
| - // We close the reading side of the exit code pipe here. |
| - // GetProcessExitCodes will get a broken pipe error when it tries to write |
| - // to the writing side of the pipe and it will ignore the error. |
| - VOID_TEMP_FAILURE_RETRY(close(*exit_event)); |
| - *exit_event = -1; |
| - |
| + if (failed) { |
| + if (!detach) { |
| + VOID_TEMP_FAILURE_RETRY(close(read_in[0])); |
| + VOID_TEMP_FAILURE_RETRY(close(read_in[1])); |
| + VOID_TEMP_FAILURE_RETRY(close(read_err[0])); |
| + VOID_TEMP_FAILURE_RETRY(close(read_err[1])); |
| + VOID_TEMP_FAILURE_RETRY(close(write_out[0])); |
| + VOID_TEMP_FAILURE_RETRY(close(write_out[1])); |
| + |
| + // Since exec() failed, we're not interested in the exit code. |
| + // We close the reading side of the exit code pipe here. |
| + // GetProcessExitCodes will get a broken pipe error when it tries to write |
| + // to the writing side of the pipe and it will ignore the error. |
| + VOID_TEMP_FAILURE_RETRY(close(*exit_event)); |
| + *exit_event = -1; |
| + } |
| if (bytes_read == -1) { |
| return errno; // Read failed. |
| } else { |