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