Chromium Code Reviews| Index: runtime/bin/process_linux.cc |
| diff --git a/runtime/bin/process_linux.cc b/runtime/bin/process_linux.cc |
| index e7bcd80df74f35d35f6410bad5ee4f7c1001abeb..d0a107119a31bb235dc2054faf88af377ce5abac 100644 |
| --- a/runtime/bin/process_linux.cc |
| +++ b/runtime/bin/process_linux.cc |
| @@ -256,12 +256,24 @@ static void ReportChildError(int exec_control_fd) { |
| } |
| +static void ReportPid(int exec_control_fd, int pid) { |
| + // 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)); |
|
kustermann
2015/01/16 16:11:03
doesn't this fit on the previous line?
Søren Gjesse
2015/01/21 12:32:34
Done.
|
| + ASSERT(bytes_written == sizeof(int)); |
| + USE(bytes_written); |
| +} |
| + |
| + |
| int Process::Start(const char* path, |
| char* arguments[], |
| intptr_t arguments_length, |
| const char* working_directory, |
| char* environment[], |
| intptr_t environment_length, |
| + bool detached, |
| intptr_t* in, |
| intptr_t* out, |
| intptr_t* err, |
| @@ -269,70 +281,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 = 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 = 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 = 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 = 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 = 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 (!detached) { |
| + result = 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 = 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); |
| @@ -354,14 +357,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 (!detached) { |
| + 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. |
| @@ -371,38 +376,91 @@ int Process::Start(const char* path, |
| perror("Failed receiving notification message"); |
| exit(1); |
| } |
| + if (detached) { |
| + // 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. |
| + VOID_TEMP_FAILURE_RETRY(close(STDIN_FILENO)); |
|
kustermann
2015/01/16 16:11:03
That one should be included by the loop above.
Søren Gjesse
2015/01/21 12:32:35
Done, and added comment.
|
| + int fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)); |
| + if (fd != STDIN_FILENO) { |
| + ReportChildError(exec_control[1]); |
| + } |
| + if (dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO) { |
| + ReportChildError(exec_control[1]); |
| + } |
| + if (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. |
| + 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 && 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 && chdir(working_directory) == -1) { |
| + ReportChildError(exec_control[1]); |
| + } |
| - execvp(path, const_cast<char* const*>(program_arguments)); |
| + if (program_environment != NULL) { |
| + environ = program_environment; |
| + } |
| + |
| + 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. |
| @@ -413,25 +471,27 @@ int Process::Start(const char* path, |
| delete[] program_arguments; |
| delete[] program_environment; |
| - int event_fds[2]; |
| - result = 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 (!detached) { |
| + int event_fds[2]; |
| + result = 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'; |
| @@ -440,43 +500,71 @@ int Process::Start(const char* path, |
| perror("Failed sending notification message"); |
| } |
| - // 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. |
| 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 (!detached) { |
| + // 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)) { |
| + static const int kMaxMessageSize = 256; |
| + char* message = static_cast<char*>(malloc(kMaxMessageSize)); |
|
kustermann
2015/01/16 16:11:03
The malloc() can potentially return NULL.
Søren Gjesse
2015/01/21 12:32:35
Added check and return a fixed error if malloc fai
|
| + bytes_read = FDUtils::ReadFromBlocking(exec_control[0], |
| + message, |
| + kMaxMessageSize); |
| + message[kMaxMessageSize - 1] = '\0'; |
| + *os_error_message = 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)); |
|
kustermann
2015/01/16 16:11:03
That is a tautology because child_errno is defined
Søren Gjesse
2015/01/21 12:32:35
Yes, it is mainly there to document the checks for
|
| + 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]; |
| + static const int kMaxMessageSize = 256; |
| + char* message = static_cast<char*>(malloc(kMaxMessageSize)); |
|
kustermann
2015/01/16 16:11:03
ditto.
Søren Gjesse
2015/01/21 12:32:35
ditto.
|
| + bytes_read = FDUtils::ReadFromBlocking(exec_control[0], |
| + message, |
| + kMaxMessageSize); |
| + message[kMaxMessageSize - 1] = '\0'; |
| + *os_error_message = 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 (!detached) { |
| + 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 { |