| 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 "bin/process.h" | 5 #include "bin/process.h" |
| 6 | 6 |
| 7 #include <errno.h> | 7 #include <errno.h> |
| 8 #include <fcntl.h> | 8 #include <fcntl.h> |
| 9 #include <poll.h> |
| 9 #include <signal.h> | 10 #include <signal.h> |
| 10 #include <stdio.h> | 11 #include <stdio.h> |
| 11 #include <stdlib.h> | 12 #include <stdlib.h> |
| 12 #include <string.h> | 13 #include <string.h> |
| 13 #include <sys/wait.h> | 14 #include <sys/wait.h> |
| 14 #include <unistd.h> | 15 #include <unistd.h> |
| 15 | 16 |
| 16 #include "bin/fdutils.h" | 17 #include "bin/fdutils.h" |
| 18 #include "bin/thread.h" |
| 17 | 19 |
| 18 | 20 |
| 21 // ProcessInfo is used to map a process id to the file descriptor for |
| 22 // the pipe used to communicate the exit code of the process to Dart. |
| 23 // ProcessInfo objects are kept in the static singly-linked |
| 24 // ProcessInfoList. |
| 19 class ProcessInfo { | 25 class ProcessInfo { |
| 20 public: | 26 public: |
| 21 ProcessInfo(pid_t pid, intptr_t fd) : pid_(pid), fd_(fd) { } | 27 ProcessInfo(pid_t pid, intptr_t fd) : pid_(pid), fd_(fd) { } |
| 22 | |
| 23 pid_t pid() { return pid_; } | 28 pid_t pid() { return pid_; } |
| 24 intptr_t fd() { return fd_; } | 29 intptr_t fd() { return fd_; } |
| 25 ProcessInfo* next() { return next_; } | 30 ProcessInfo* next() { return next_; } |
| 26 void set_next(ProcessInfo* next) { next_ = next; } | 31 void set_next(ProcessInfo* info) { next_ = info; } |
| 27 | 32 |
| 28 private: | 33 private: |
| 29 pid_t pid_; // Process pid. | 34 pid_t pid_; |
| 30 intptr_t fd_; // File descriptor for pipe to report exit code. | 35 intptr_t fd_; |
| 31 ProcessInfo* next_; | 36 ProcessInfo* next_; |
| 32 }; | 37 }; |
| 33 | 38 |
| 34 | 39 |
| 35 ProcessInfo* active_processes = NULL; | 40 // Singly-linked list of ProcessInfo objects for all active processes |
| 41 // started from Dart. |
| 42 class ProcessInfoList { |
| 43 public: |
| 44 static void AddProcess(pid_t pid, intptr_t fd) { |
| 45 MutexLocker locker(&mutex_); |
| 46 ProcessInfo* info = new ProcessInfo(pid, fd); |
| 47 info->set_next(active_processes_); |
| 48 active_processes_ = info; |
| 49 } |
| 36 | 50 |
| 37 | 51 |
| 38 static void AddProcess(ProcessInfo* process) { | 52 static intptr_t LookupProcessExitFd(pid_t pid) { |
| 39 process->set_next(active_processes); | 53 MutexLocker locker(&mutex_); |
| 40 active_processes = process; | 54 ProcessInfo* current = active_processes_; |
| 41 } | 55 while (current != NULL) { |
| 56 if (current->pid() == pid) { |
| 57 return current->fd(); |
| 58 } |
| 59 current = current->next(); |
| 60 } |
| 61 return 0; |
| 62 } |
| 42 | 63 |
| 43 | 64 |
| 44 static ProcessInfo* LookupProcess(pid_t pid) { | 65 static void RemoveProcess(pid_t pid) { |
| 45 ProcessInfo* current = active_processes; | 66 MutexLocker locker(&mutex_); |
| 46 while (current != NULL) { | 67 ProcessInfo* prev = NULL; |
| 47 if (current->pid() == pid) { | 68 ProcessInfo* current = active_processes_; |
| 48 return current; | 69 while (current != NULL) { |
| 70 if (current->pid() == pid) { |
| 71 if (prev == NULL) { |
| 72 active_processes_ = current->next(); |
| 73 } else { |
| 74 prev->set_next(current->next()); |
| 75 } |
| 76 delete current; |
| 77 return; |
| 78 } |
| 79 prev = current; |
| 80 current = current->next(); |
| 49 } | 81 } |
| 50 current = current->next(); | |
| 51 } | 82 } |
| 52 return NULL; | 83 |
| 53 } | 84 private: |
| 85 // Linked list of ProcessInfo objects for all active processes |
| 86 // started from Dart code. |
| 87 static ProcessInfo* active_processes_; |
| 88 // Mutex protecting all accesses to the linked list of active |
| 89 // processes. |
| 90 static dart::Mutex mutex_; |
| 91 }; |
| 54 | 92 |
| 55 | 93 |
| 56 static void RemoveProcess(pid_t pid) { | 94 ProcessInfo* ProcessInfoList::active_processes_ = NULL; |
| 57 ProcessInfo* prev = NULL; | 95 dart::Mutex ProcessInfoList::mutex_; |
| 58 ProcessInfo* current = active_processes; | 96 |
| 59 while (current != NULL) { | 97 |
| 60 if (current->pid() == pid) { | 98 // The exit code handler sets up a separate thread which is signalled |
| 61 if (prev == NULL) { | 99 // on SIGCHLD. That separate thread can then get the exit code from |
| 62 active_processes = current->next(); | 100 // processes that have exited and communicate it to Dart through the |
| 101 // event loop. |
| 102 class ExitCodeHandler { |
| 103 public: |
| 104 // Ensure that the ExitCodeHandler has been initialized. |
| 105 static bool EnsureInitialized() { |
| 106 // Multiple isolates could be starting processes at the same |
| 107 // time. Make sure that only one of them initializes the |
| 108 // ExitCodeHandler. |
| 109 MutexLocker locker(&mutex_); |
| 110 if (initialized_) { |
| 111 return true; |
| 112 } |
| 113 |
| 114 // Allocate a pipe that the signal handler can write a byte to and |
| 115 // that the exit handler thread can poll. |
| 116 int result = TEMP_FAILURE_RETRY(pipe(sig_chld_fds_)); |
| 117 if (result < 0) { |
| 118 return false; |
| 119 } |
| 120 |
| 121 // Start thread that polls the pipe and handles process exits when |
| 122 // data is received on the pipe. |
| 123 new dart::Thread(ExitCodeHandlerEntry, sig_chld_fds_[0]); |
| 124 |
| 125 // Mark write end non-blocking. |
| 126 FDUtils::SetNonBlocking(sig_chld_fds_[1]); |
| 127 |
| 128 // Thread started and the ExitCodeHandler is initialized. |
| 129 initialized_ = true; |
| 130 return true; |
| 131 } |
| 132 |
| 133 // Get the write end of the pipe. |
| 134 static int WakeUpFd() { |
| 135 ASSERT(initialized_); |
| 136 return sig_chld_fds_[1]; |
| 137 } |
| 138 |
| 139 private: |
| 140 // GetProcessExitCodes is called on a separate thread when a SIGCHLD |
| 141 // signal is received to retrieve the exit codes and post them to |
| 142 // dart. |
| 143 static void GetProcessExitCodes() { |
| 144 pid_t pid = 0; |
| 145 int status = 0; |
| 146 while ((pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG))) > 0) { |
| 147 int exit_code = 0; |
| 148 int negative = 0; |
| 149 if (WIFEXITED(status)) { |
| 150 exit_code = WEXITSTATUS(status); |
| 151 } |
| 152 if (WIFSIGNALED(status)) { |
| 153 exit_code = WTERMSIG(status); |
| 154 negative = 1; |
| 155 } |
| 156 intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(pid); |
| 157 if (exit_code_fd != 0) { |
| 158 int message[3] = { pid, exit_code, negative }; |
| 159 ssize_t result = |
| 160 FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message)); |
| 161 if (result != sizeof(message) && errno != EPIPE) { |
| 162 perror("ExitHandler notification failed"); |
| 163 } |
| 164 TEMP_FAILURE_RETRY(close(exit_code_fd)); |
| 165 } |
| 166 } |
| 167 } |
| 168 |
| 169 |
| 170 // Entry point for the separate exit code handler thread started by |
| 171 // the ExitCodeHandler. |
| 172 static void ExitCodeHandlerEntry(uword param) { |
| 173 struct pollfd pollfds; |
| 174 pollfds.fd = param; |
| 175 pollfds.events |= POLLIN; |
| 176 while (true) { |
| 177 int result = TEMP_FAILURE_RETRY(poll(&pollfds, 1, -1)); |
| 178 if (result == -1) { |
| 179 ASSERT(EAGAIN == EWOULDBLOCK); |
| 180 if (errno != EWOULDBLOCK) { |
| 181 perror("ExitCodeHandler poll failed"); |
| 182 } |
| 63 } else { | 183 } else { |
| 64 prev->set_next(current->next()); | 184 // Read the byte from the wake-up fd. |
| 185 ASSERT(result = 1); |
| 186 intptr_t data = 0; |
| 187 ssize_t read_bytes = FDUtils::ReadFromBlocking(pollfds.fd, &data, 1); |
| 188 if (read_bytes < 1) { |
| 189 perror("Failed to read from wake-up fd in exit-code handler"); |
| 190 } |
| 191 // Get the exit code from all processes that have died. |
| 192 GetProcessExitCodes(); |
| 65 } | 193 } |
| 66 delete current; | |
| 67 return; | |
| 68 } | 194 } |
| 69 prev = current; | |
| 70 current = current->next(); | |
| 71 } | 195 } |
| 72 } | 196 |
| 197 static dart::Mutex mutex_; |
| 198 static bool initialized_; |
| 199 static int sig_chld_fds_[2]; |
| 200 }; |
| 201 |
| 202 |
| 203 dart::Mutex ExitCodeHandler::mutex_; |
| 204 bool ExitCodeHandler::initialized_ = false; |
| 205 int ExitCodeHandler::sig_chld_fds_[2] = { 0, 0 }; |
| 73 | 206 |
| 74 | 207 |
| 75 static char* SafeStrNCpy(char* dest, const char* src, size_t n) { | 208 static char* SafeStrNCpy(char* dest, const char* src, size_t n) { |
| 76 strncpy(dest, src, n); | 209 strncpy(dest, src, n); |
| 77 dest[n - 1] = '\0'; | 210 dest[n - 1] = '\0'; |
| 78 return dest; | 211 return dest; |
| 79 } | 212 } |
| 80 | 213 |
| 81 | 214 |
| 82 static void SetChildOsErrorMessage(char* os_error_message, | 215 static void SetChildOsErrorMessage(char* os_error_message, |
| 83 int os_error_message_len) { | 216 int os_error_message_len) { |
| 84 SafeStrNCpy(os_error_message, strerror(errno), os_error_message_len); | 217 SafeStrNCpy(os_error_message, strerror(errno), os_error_message_len); |
| 85 } | 218 } |
| 86 | 219 |
| 87 | 220 |
| 88 void ExitHandler(int process_signal, siginfo_t* siginfo, void* tmp) { | 221 static void SigChldHandler(int process_signal, siginfo_t* siginfo, void* tmp) { |
| 89 int pid = 0; | |
| 90 int status = 0; | |
| 91 // Save errno so it can be restored at the end. | 222 // Save errno so it can be restored at the end. |
| 92 int entry_errno = errno; | 223 int entry_errno = errno; |
| 93 while ((pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG))) > 0) { | 224 // Signal the exit code handler where the actual processing takes |
| 94 int exit_code = 0; | 225 // place. |
| 95 int negative = 0; | 226 ssize_t result = |
| 96 if (WIFEXITED(status)) { | 227 TEMP_FAILURE_RETRY(write(ExitCodeHandler::WakeUpFd(), "", 1)); |
| 97 exit_code = WEXITSTATUS(status); | 228 if (result < 1) { |
| 98 } | 229 perror("Failed to write to wake-up fd in SIGCHLD handler"); |
| 99 if (WIFSIGNALED(status)) { | |
| 100 exit_code = WTERMSIG(status); | |
| 101 negative = 1; | |
| 102 } | |
| 103 // Lookup the process and extract all needed information from | |
| 104 // it. The WriteToBlocking call below can cause the deletion of | |
| 105 // the process object (because this signal handler can be running | |
| 106 // on an arbitrary thread, not just the main thread) so we cannot | |
| 107 // touch it after that call. | |
| 108 ProcessInfo* process = LookupProcess(pid); | |
| 109 intptr_t exit_code_fd = process->fd(); | |
| 110 if (process != NULL) { | |
| 111 int message[3] = { pid, exit_code, negative }; | |
| 112 intptr_t result = | |
| 113 FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message)); | |
| 114 if (result != sizeof(message) && errno != EPIPE) { | |
| 115 perror("ExitHandler notification failed"); | |
| 116 } | |
| 117 TEMP_FAILURE_RETRY(close(exit_code_fd)); | |
| 118 } | |
| 119 } | 230 } |
| 231 // Restore errno. |
| 120 errno = entry_errno; | 232 errno = entry_errno; |
| 121 } | 233 } |
| 122 | 234 |
| 123 | 235 |
| 124 static void ReportChildError(int exec_control_fd) { | 236 static void ReportChildError(int exec_control_fd) { |
| 125 // In the case of failure in the child process write the errno and | 237 // In the case of failure in the child process write the errno and |
| 126 // the OS error message to the exec control pipe and exit. | 238 // the OS error message to the exec control pipe and exit. |
| 127 int child_errno = errno; | 239 int child_errno = errno; |
| 128 char* os_error_message = strerror(errno); | 240 char* os_error_message = strerror(errno); |
| 129 ASSERT(sizeof(child_errno) == sizeof(errno)); | 241 ASSERT(sizeof(child_errno) == sizeof(errno)); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 150 intptr_t* exit_event, | 262 intptr_t* exit_event, |
| 151 char* os_error_message, | 263 char* os_error_message, |
| 152 int os_error_message_len) { | 264 int os_error_message_len) { |
| 153 pid_t pid; | 265 pid_t pid; |
| 154 int read_in[2]; // Pipe for stdout to child process. | 266 int read_in[2]; // Pipe for stdout to child process. |
| 155 int read_err[2]; // Pipe for stderr to child process. | 267 int read_err[2]; // Pipe for stderr to child process. |
| 156 int write_out[2]; // Pipe for stdin to child process. | 268 int write_out[2]; // Pipe for stdin to child process. |
| 157 int exec_control[2]; // Pipe to get the result from exec. | 269 int exec_control[2]; // Pipe to get the result from exec. |
| 158 int result; | 270 int result; |
| 159 | 271 |
| 272 bool initialized = ExitCodeHandler::EnsureInitialized(); |
| 273 if (!initialized) { |
| 274 SetChildOsErrorMessage(os_error_message, os_error_message_len); |
| 275 fprintf(stderr, |
| 276 "Error initializing exit code handler: %s\n", |
| 277 os_error_message); |
| 278 return errno; |
| 279 } |
| 280 |
| 160 result = TEMP_FAILURE_RETRY(pipe(read_in)); | 281 result = TEMP_FAILURE_RETRY(pipe(read_in)); |
| 161 if (result < 0) { | 282 if (result < 0) { |
| 162 SetChildOsErrorMessage(os_error_message, os_error_message_len); | 283 SetChildOsErrorMessage(os_error_message, os_error_message_len); |
| 163 fprintf(stderr, "Error pipe creation failed: %s\n", os_error_message); | 284 fprintf(stderr, "Error pipe creation failed: %s\n", os_error_message); |
| 164 return errno; | 285 return errno; |
| 165 } | 286 } |
| 166 | 287 |
| 167 result = TEMP_FAILURE_RETRY(pipe(read_err)); | 288 result = TEMP_FAILURE_RETRY(pipe(read_err)); |
| 168 if (result < 0) { | 289 if (result < 0) { |
| 169 SetChildOsErrorMessage(os_error_message, os_error_message_len); | 290 SetChildOsErrorMessage(os_error_message, os_error_message_len); |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 218 | 339 |
| 219 char** program_arguments = new char*[arguments_length + 2]; | 340 char** program_arguments = new char*[arguments_length + 2]; |
| 220 program_arguments[0] = const_cast<char *>(path); | 341 program_arguments[0] = const_cast<char *>(path); |
| 221 for (int i = 0; i < arguments_length; i++) { | 342 for (int i = 0; i < arguments_length; i++) { |
| 222 program_arguments[i + 1] = arguments[i]; | 343 program_arguments[i + 1] = arguments[i]; |
| 223 } | 344 } |
| 224 program_arguments[arguments_length + 1] = NULL; | 345 program_arguments[arguments_length + 1] = NULL; |
| 225 | 346 |
| 226 struct sigaction act; | 347 struct sigaction act; |
| 227 bzero(&act, sizeof(act)); | 348 bzero(&act, sizeof(act)); |
| 228 act.sa_sigaction = ExitHandler; | 349 act.sa_sigaction = SigChldHandler; |
| 229 act.sa_flags = SA_NOCLDSTOP | SA_SIGINFO; | 350 act.sa_flags = SA_NOCLDSTOP | SA_SIGINFO; |
| 230 if (sigaction(SIGCHLD, &act, 0) != 0) { | 351 if (sigaction(SIGCHLD, &act, 0) != 0) { |
| 231 perror("Process start: setting signal handler failed"); | 352 perror("Process start: setting signal handler failed"); |
| 232 } | 353 } |
| 233 pid = TEMP_FAILURE_RETRY(fork()); | 354 pid = TEMP_FAILURE_RETRY(fork()); |
| 234 if (pid < 0) { | 355 if (pid < 0) { |
| 235 SetChildOsErrorMessage(os_error_message, os_error_message_len); | 356 SetChildOsErrorMessage(os_error_message, os_error_message_len); |
| 236 delete[] program_arguments; | 357 delete[] program_arguments; |
| 237 TEMP_FAILURE_RETRY(close(read_in[0])); | 358 TEMP_FAILURE_RETRY(close(read_in[0])); |
| 238 TEMP_FAILURE_RETRY(close(read_in[1])); | 359 TEMP_FAILURE_RETRY(close(read_in[1])); |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 292 TEMP_FAILURE_RETRY(close(read_in[0])); | 413 TEMP_FAILURE_RETRY(close(read_in[0])); |
| 293 TEMP_FAILURE_RETRY(close(read_in[1])); | 414 TEMP_FAILURE_RETRY(close(read_in[1])); |
| 294 TEMP_FAILURE_RETRY(close(read_err[0])); | 415 TEMP_FAILURE_RETRY(close(read_err[0])); |
| 295 TEMP_FAILURE_RETRY(close(read_err[1])); | 416 TEMP_FAILURE_RETRY(close(read_err[1])); |
| 296 TEMP_FAILURE_RETRY(close(write_out[0])); | 417 TEMP_FAILURE_RETRY(close(write_out[0])); |
| 297 TEMP_FAILURE_RETRY(close(write_out[1])); | 418 TEMP_FAILURE_RETRY(close(write_out[1])); |
| 298 fprintf(stderr, "Error pipe creation failed: %s\n", os_error_message); | 419 fprintf(stderr, "Error pipe creation failed: %s\n", os_error_message); |
| 299 return errno; | 420 return errno; |
| 300 } | 421 } |
| 301 | 422 |
| 302 ProcessInfo* process = new ProcessInfo(pid, event_fds[1]); | 423 ProcessInfoList::AddProcess(pid, event_fds[1]); |
| 303 AddProcess(process); | |
| 304 *exit_event = event_fds[0]; | 424 *exit_event = event_fds[0]; |
| 305 FDUtils::SetNonBlocking(event_fds[0]); | 425 FDUtils::SetNonBlocking(event_fds[0]); |
| 306 | 426 |
| 307 // Notify child process to start. | 427 // Notify child process to start. |
| 308 char msg = '1'; | 428 char msg = '1'; |
| 309 result = FDUtils::WriteToBlocking(read_in[1], &msg, sizeof(msg)); | 429 result = FDUtils::WriteToBlocking(read_in[1], &msg, sizeof(msg)); |
| 310 if (result != sizeof(msg)) { | 430 if (result != sizeof(msg)) { |
| 311 perror("Failed sending notification message"); | 431 perror("Failed sending notification message"); |
| 312 } | 432 } |
| 313 | 433 |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 362 bool Process::Kill(intptr_t id) { | 482 bool Process::Kill(intptr_t id) { |
| 363 int result = TEMP_FAILURE_RETRY(kill(id, SIGKILL)); | 483 int result = TEMP_FAILURE_RETRY(kill(id, SIGKILL)); |
| 364 if (result == -1) { | 484 if (result == -1) { |
| 365 return false; | 485 return false; |
| 366 } | 486 } |
| 367 return true; | 487 return true; |
| 368 } | 488 } |
| 369 | 489 |
| 370 | 490 |
| 371 void Process::Exit(intptr_t id) { | 491 void Process::Exit(intptr_t id) { |
| 372 RemoveProcess(id); | 492 ProcessInfoList::RemoveProcess(id); |
| 373 } | 493 } |
| OLD | NEW |