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
3// BSD-style license that can be found in the LICENSE file.
4
5#ifndef RUNTIME_BIN_PROCESS_H_
6#define RUNTIME_BIN_PROCESS_H_
7
8#include <errno.h>
9
10#include "bin/builtin.h"
11#include "bin/io_buffer.h"
12#include "bin/lockers.h"
13#include "bin/namespace.h"
14#include "bin/thread.h"
15#include "platform/globals.h"
16#if !defined(HOST_OS_WINDOWS)
17#include "platform/signal_blocker.h"
18#endif
19#include "platform/utils.h"
20
21namespace dart {
22namespace bin {
23
24class ProcessResult {
25 public:
26 ProcessResult() : exit_code_(0) {}
27
28 void set_stdout_data(Dart_Handle stdout_data) { stdout_data_ = stdout_data; }
29 void set_stderr_data(Dart_Handle stderr_data) { stderr_data_ = stderr_data; }
30
31 void set_exit_code(intptr_t exit_code) { exit_code_ = exit_code; }
32
33 Dart_Handle stdout_data() { return stdout_data_; }
34 Dart_Handle stderr_data() { return stderr_data_; }
35 intptr_t exit_code() { return exit_code_; }
36
37 private:
38 Dart_Handle stdout_data_;
39 Dart_Handle stderr_data_;
40 intptr_t exit_code_;
41
42 DISALLOW_ALLOCATION();
43};
44
45// To be kept in sync with ProcessSignal consts in sdk/lib/io/process.dart
46// Note that this map is as on Linux.
47enum ProcessSignals {
48 kSighup = 1,
49 kSigint = 2,
50 kSigquit = 3,
51 kSigill = 4,
52 kSigtrap = 5,
53 kSigabrt = 6,
54 kSigbus = 7,
55 kSigfpe = 8,
56 kSigkill = 9,
57 kSigusr1 = 10,
58 kSigsegv = 11,
59 kSigusr2 = 12,
60 kSigpipe = 13,
61 kSigalrm = 14,
62 kSigterm = 15,
63 kSigchld = 17,
64 kSigcont = 18,
65 kSigstop = 19,
66 kSigtstp = 20,
67 kSigttin = 21,
68 kSigttou = 22,
69 kSigurg = 23,
70 kSigxcpu = 24,
71 kSigxfsz = 25,
72 kSigvtalrm = 26,
73 kSigprof = 27,
74 kSigwinch = 28,
75 kSigpoll = 29,
76 kSigsys = 31,
77 kLastSignal = kSigsys,
78};
79
80// To be kept in sync with ProcessStartMode consts in sdk/lib/io/process.dart.
81enum ProcessStartMode {
82 kNormal = 0,
83 kInheritStdio = 1,
84 kDetached = 2,
85 kDetachedWithStdio = 3,
86};
87
88class Process {
89 public:
90 static void Init();
91 static void Cleanup();
92
93 // Start a new process providing access to stdin, stdout, stderr and
94 // process exit streams.
95 static int Start(Namespace* namespc,
96 const char* path,
97 char* arguments[],
98 intptr_t arguments_length,
99 const char* working_directory,
100 char* environment[],
101 intptr_t environment_length,
102 ProcessStartMode mode,
103 intptr_t* in,
104 intptr_t* out,
105 intptr_t* err,
106 intptr_t* id,
107 intptr_t* exit_handler,
108 char** os_error_message);
109
110 static bool Wait(intptr_t id,
111 intptr_t in,
112 intptr_t out,
113 intptr_t err,
114 intptr_t exit_handler,
115 ProcessResult* result);
116
117 // Kill a process with a given pid.
118 static bool Kill(intptr_t id, int signal);
119
120 // Terminate the exit code handler thread. Does not return before
121 // the thread has terminated.
122 static void TerminateExitCodeHandler();
123
124 static int GlobalExitCode() {
125 MutexLocker ml(global_exit_code_mutex_);
126 return global_exit_code_;
127 }
128
129 static void SetGlobalExitCode(int exit_code) {
130 MutexLocker ml(global_exit_code_mutex_);
131 global_exit_code_ = exit_code;
132 }
133
134 typedef void (*ExitHook)(int64_t exit_code);
135 static void SetExitHook(ExitHook hook) { exit_hook_ = hook; }
136 static void RunExitHook(int64_t exit_code) {
137 if (exit_hook_ != NULL) {
138 exit_hook_(exit_code);
139 }
140 }
141
142 static intptr_t CurrentProcessId();
143
144 static intptr_t SetSignalHandler(intptr_t signal);
145 // When there is a current Isolate and the 'port' argument is
146 // Dart_GetMainPortId(), this clears the signal handler for the current
147 // isolate. When 'port' is ILLEGAL_PORT, this clears all signal handlers for
148 // 'signal' for all Isolates.
149 static void ClearSignalHandler(intptr_t signal, Dart_Port port);
150 static void ClearSignalHandlerByFd(intptr_t fd, Dart_Port port);
151 static void ClearAllSignalHandlers();
152
153 static Dart_Handle GetProcessIdNativeField(Dart_Handle process,
154 intptr_t* pid);
155 static Dart_Handle SetProcessIdNativeField(Dart_Handle process, intptr_t pid);
156
157 static int64_t CurrentRSS();
158 static int64_t MaxRSS();
159 static void GetRSSInformation(int64_t* max_rss, int64_t* current_rss);
160
161 static bool ModeIsAttached(ProcessStartMode mode);
162 static bool ModeHasStdio(ProcessStartMode mode);
163
164 private:
165 static int global_exit_code_;
166 static Mutex* global_exit_code_mutex_;
167 static ExitHook exit_hook_;
168
169 DISALLOW_ALLOCATION();
170 DISALLOW_IMPLICIT_CONSTRUCTORS(Process);
171};
172
173class SignalInfo {
174 public:
175 SignalInfo(intptr_t fd, intptr_t signal, SignalInfo* next)
176 : fd_(fd),
177 signal_(signal),
178 // SignalInfo is expected to be created when in a isolate.
179 port_(Dart_GetMainPortId()),
180 next_(next),
181 prev_(NULL) {
182 if (next_ != NULL) {
183 next_->prev_ = this;
184 }
185 }
186
187 ~SignalInfo();
188
189 void Unlink() {
190 if (prev_ != NULL) {
191 prev_->next_ = next_;
192 }
193 if (next_ != NULL) {
194 next_->prev_ = prev_;
195 }
196 }
197
198 intptr_t fd() const { return fd_; }
199 intptr_t signal() const { return signal_; }
200 Dart_Port port() const { return port_; }
201 SignalInfo* next() const { return next_; }
202
203 private:
204 intptr_t fd_;
205 intptr_t signal_;
206 // The port_ is used to identify what isolate the signal-info belongs to.
207 Dart_Port port_;
208 SignalInfo* next_;
209 SignalInfo* prev_;
210
211 DISALLOW_COPY_AND_ASSIGN(SignalInfo);
212};
213
214// Utility class for collecting the output when running a process
215// synchronously by using Process::Wait. This class is sub-classed in
216// the platform specific files to implement reading into the buffers
217// allocated.
218class BufferListBase {
219 protected:
220 static const intptr_t kBufferSize = 16 * 1024;
221
222 class BufferListNode {
223 public:
224 explicit BufferListNode(intptr_t size) {
225 data_ = new uint8_t[size];
226 // We check for a failed allocation below in Allocate()
227 next_ = NULL;
228 }
229
230 ~BufferListNode() { delete[] data_; }
231
232 bool Valid() const { return data_ != NULL; }
233
234 uint8_t* data() const { return data_; }
235 BufferListNode* next() const { return next_; }
236 void set_next(BufferListNode* n) { next_ = n; }
237
238 private:
239 uint8_t* data_;
240 BufferListNode* next_;
241
242 DISALLOW_IMPLICIT_CONSTRUCTORS(BufferListNode);
243 };
244
245 public:
246 BufferListBase() : head_(NULL), tail_(NULL), data_size_(0), free_size_(0) {}
247 ~BufferListBase() {
248 Free();
249 DEBUG_ASSERT(IsEmpty());
250 }
251
252 // Returns the collected data as a Uint8List. If an error occours an
253 // error handle is returned.
254 Dart_Handle GetData() {
255 uint8_t* buffer;
256 intptr_t buffer_position = 0;
257 Dart_Handle result = IOBuffer::Allocate(data_size_, &buffer);
258 if (Dart_IsNull(result)) {
259 return DartUtils::NewDartOSError();
260 }
261 if (Dart_IsError(result)) {
262 Free();
263 return result;
264 }
265 for (BufferListNode* current = head_; current != NULL;
266 current = current->next()) {
267 intptr_t to_copy = dart::Utils::Minimum(data_size_, kBufferSize);
268 memmove(buffer + buffer_position, current->data(), to_copy);
269 buffer_position += to_copy;
270 data_size_ -= to_copy;
271 }
272 ASSERT(data_size_ == 0);
273 Free();
274 return result;
275 }
276
277#if defined(DEBUG)
278 bool IsEmpty() const { return (head_ == NULL) && (tail_ == NULL); }
279#endif
280
281 protected:
282 bool Allocate() {
283 ASSERT(free_size_ == 0);
284 BufferListNode* node = new BufferListNode(kBufferSize);
285 if ((node == NULL) || !node->Valid()) {
286 // Failed to allocate a buffer for the node.
287 delete node;
288 return false;
289 }
290 if (head_ == NULL) {
291 head_ = node;
292 tail_ = node;
293 } else {
294 ASSERT(tail_->next() == NULL);
295 tail_->set_next(node);
296 tail_ = node;
297 }
298 free_size_ = kBufferSize;
299 return true;
300 }
301
302 void Free() {
303 BufferListNode* current = head_;
304 while (current != NULL) {
305 BufferListNode* tmp = current;
306 current = current->next();
307 delete tmp;
308 }
309 head_ = NULL;
310 tail_ = NULL;
311 data_size_ = 0;
312 free_size_ = 0;
313 }
314
315 // Returns the address of the first byte in the free space.
316 uint8_t* FreeSpaceAddress() {
317 return tail_->data() + (kBufferSize - free_size_);
318 }
319
320 intptr_t data_size() const { return data_size_; }
321 void set_data_size(intptr_t size) { data_size_ = size; }
322
323 intptr_t free_size() const { return free_size_; }
324 void set_free_size(intptr_t size) { free_size_ = size; }
325
326 BufferListNode* head() const { return head_; }
327 BufferListNode* tail() const { return tail_; }
328
329 private:
330 // Linked list for data collected.
331 BufferListNode* head_;
332 BufferListNode* tail_;
333
334 // Number of bytes of data collected in the linked list.
335 intptr_t data_size_;
336
337 // Number of free bytes in the last node in the list.
338 intptr_t free_size_;
339
340 DISALLOW_COPY_AND_ASSIGN(BufferListBase);
341};
342
343#if defined(HOST_OS_ANDROID) || defined(HOST_OS_FUCHSIA) || \
344 defined(HOST_OS_LINUX) || defined(HOST_OS_MACOS)
345class BufferList : public BufferListBase {
346 public:
347 BufferList() {}
348
349 bool Read(int fd, intptr_t available) {
350 // Read all available bytes.
351 while (available > 0) {
352 if (free_size() == 0) {
353 if (!Allocate()) {
354 errno = ENOMEM;
355 return false;
356 }
357 }
358 ASSERT(free_size() > 0);
359 ASSERT(free_size() <= kBufferSize);
360 intptr_t block_size = dart::Utils::Minimum(free_size(), available);
361#if defined(HOST_OS_FUCHSIA)
362 intptr_t bytes = NO_RETRY_EXPECTED(
363 read(fd, reinterpret_cast<void*>(FreeSpaceAddress()), block_size));
364#else
365 intptr_t bytes = TEMP_FAILURE_RETRY(
366 read(fd, reinterpret_cast<void*>(FreeSpaceAddress()), block_size));
367#endif // defined(HOST_OS_FUCHSIA)
368 if (bytes < 0) {
369 return false;
370 }
371 set_data_size(data_size() + bytes);
372 set_free_size(free_size() - bytes);
373 available -= bytes;
374 }
375 return true;
376 }
377
378 private:
379 DISALLOW_COPY_AND_ASSIGN(BufferList);
380};
381#endif // defined(HOST_OS_ANDROID) ...
382
383} // namespace bin
384} // namespace dart
385
386#endif // RUNTIME_BIN_PROCESS_H_
387