OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, 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 part of dart.io; | |
6 | |
7 // TODO(ager): The only reason for this class is that we | |
8 // cannot patch a top-level at this point. | |
9 class _ProcessUtils { | |
10 external static void _exit(int status); | |
11 external static void _setExitCode(int status); | |
12 external static int _getExitCode(); | |
13 external static void _sleep(int millis); | |
14 external static int _pid(Process process); | |
15 external static Stream<ProcessSignal> _watchSignal(ProcessSignal signal); | |
16 } | |
17 | |
18 /** | |
19 * Exit the Dart VM process immediately with the given exit code. | |
20 * | |
21 * This does not wait for any asynchronous operations to terminate. Using | |
22 * [exit] is therefore very likely to lose data. | |
23 * | |
24 * The handling of exit codes is platform specific. | |
25 * | |
26 * On Linux and OS X an exit code for normal termination will always | |
27 * be in the range [0..255]. If an exit code outside this range is | |
28 * set the actual exit code will be the lower 8 bits masked off and | |
29 * treated as an unsigned value. E.g. using an exit code of -1 will | |
30 * result in an actual exit code of 255 being reported. | |
31 * | |
32 * On Windows the exit code can be set to any 32-bit value. However | |
33 * some of these values are reserved for reporting system errors like | |
34 * crashes. | |
35 * | |
36 * Besides this the Dart executable itself uses an exit code of `254` | |
37 * for reporting compile time errors and an exit code of `255` for | |
38 * reporting runtime error (unhandled exception). | |
39 * | |
40 * Due to these facts it is recommended to only use exit codes in the | |
41 * range [0..127] for communicating the result of running a Dart | |
42 * program to the surrounding environment. This will avoid any | |
43 * cross-platform issues. | |
44 */ | |
45 void exit(int code) { | |
46 if (code is !int) { | |
47 throw new ArgumentError("Integer value for exit code expected"); | |
48 } | |
49 _ProcessUtils._exit(code); | |
50 } | |
51 | |
52 /** | |
53 * Set the global exit code for the Dart VM. | |
54 * | |
55 * The exit code is global for the Dart VM and the last assignment to | |
56 * exitCode from any isolate determines the exit code of the Dart VM | |
57 * on normal termination. | |
58 * | |
59 * Default value is `0`. | |
60 * | |
61 * See [exit] for more information on how to chose a value for the | |
62 * exit code. | |
63 */ | |
64 void set exitCode(int code) { | |
65 if (code is !int) { | |
66 throw new ArgumentError("Integer value for exit code expected"); | |
67 } | |
68 _ProcessUtils._setExitCode(code); | |
69 } | |
70 | |
71 /** | |
72 * Get the global exit code for the Dart VM. | |
73 * | |
74 * The exit code is global for the Dart VM and the last assignment to | |
75 * exitCode from any isolate determines the exit code of the Dart VM | |
76 * on normal termination. | |
77 * | |
78 * See [exit] for more information on how to chose a value for the | |
79 * exit code. | |
80 */ | |
81 int get exitCode => _ProcessUtils._getExitCode(); | |
82 | |
83 /** | |
84 * Sleep for the duration specified in [duration]. | |
85 * | |
86 * Use this with care, as no asynchronous operations can be processed | |
87 * in a isolate while it is blocked in a [sleep] call. | |
88 */ | |
89 void sleep(Duration duration) { | |
90 int milliseconds = duration.inMilliseconds; | |
91 if (milliseconds < 0) { | |
92 throw new ArgumentError("sleep: duration cannot be negative"); | |
93 } | |
94 _ProcessUtils._sleep(milliseconds); | |
95 } | |
96 | |
97 /** | |
98 * Returns the PID of the current process. | |
99 */ | |
100 int get pid => _ProcessUtils._pid(null); | |
101 | |
102 /** | |
103 * Modes for running a new process. | |
104 */ | |
105 enum ProcessStartMode { | |
106 /// Normal child process. | |
107 NORMAL, | |
108 /// Detached child process with no open communication channel. | |
109 DETACHED, | |
110 /// Detached child process with stdin, stdout and stderr still open | |
111 /// for communication with the child. | |
112 DETACHED_WITH_STDIO | |
113 } | |
114 | |
115 /** | |
116 * The means to execute a program. | |
117 * | |
118 * Use the static [start] and [run] methods to start a new process. | |
119 * The run method executes the process non-interactively to completion. | |
120 * In contrast, the start method allows your code to interact with the | |
121 * running process. | |
122 * | |
123 * ## Start a process with the run method | |
124 * | |
125 * The following code sample uses the run method to create a process | |
126 * that runs the UNIX command `ls`, which lists the contents of a directory. | |
127 * The run method completes with a [ProcessResult] object when the process | |
128 * terminates. This provides access to the output and exit code from the | |
129 * process. The run method does not return a Process object; this prevents your | |
130 * code from interacting with the running process. | |
131 * | |
132 * import 'dart:io'; | |
133 * | |
134 * main() { | |
135 * // List all files in the current directory in UNIX-like systems. | |
136 * Process.run('ls', ['-l']).then((ProcessResult results) { | |
137 * print(results.stdout); | |
138 * }); | |
139 * } | |
140 * | |
141 * ## Start a process with the start method | |
142 * | |
143 * The following example uses start to create the process. | |
144 * The start method returns a [Future] for a Process object. | |
145 * When the future completes the process is started and | |
146 * your code can interact with the | |
147 * Process: writing to stdin, listening to stdout, and so on. | |
148 * | |
149 * The following sample starts the UNIX `cat` utility, which when given no | |
150 * command-line arguments, echos its input. | |
151 * The program writes to the process's standard input stream | |
152 * and prints data from its standard output stream. | |
153 * | |
154 * import 'dart:io'; | |
155 * import 'dart:convert'; | |
156 * | |
157 * main() { | |
158 * Process.start('cat', []).then((Process process) { | |
159 * process.stdout | |
160 * .transform(UTF8.decoder) | |
161 * .listen((data) { print(data); }); | |
162 * process.stdin.writeln('Hello, world!'); | |
163 * process.stdin.writeln('Hello, galaxy!'); | |
164 * process.stdin.writeln('Hello, universe!'); | |
165 * }); | |
166 * } | |
167 * | |
168 * ## Standard I/O streams | |
169 * | |
170 * As seen in the previous code sample, you can interact with the Process's | |
171 * standard output stream through the getter [stdout], | |
172 * and you can interact with the Process's standard input stream through | |
173 * the getter [stdin]. | |
174 * In addition, Process provides a getter [stderr] for using the Process's | |
175 * standard error stream. | |
176 * | |
177 * A Process's streams are distinct from the top-level streams | |
178 * for the current program. | |
179 * | |
180 * ## Exit codes | |
181 * | |
182 * Call the [exitCode] method to get the exit code of the process. | |
183 * The exit code indicates whether the program terminated successfully | |
184 * (usually indicated with an exit code of 0) or with an error. | |
185 * | |
186 * If the start method is used, the exitCode is available through a future | |
187 * on the Process object (as shown in the example below). | |
188 * If the run method is used, the exitCode is available | |
189 * through a getter on the ProcessResult instance. | |
190 * | |
191 * import 'dart:io'; | |
192 * | |
193 * main() { | |
194 * Process.start('ls', ['-l']).then((process) { | |
195 * // Get the exit code from the new process. | |
196 * process.exitCode.then((exitCode) { | |
197 * print('exit code: $exitCode'); | |
198 * }); | |
199 * }); | |
200 * } | |
201 * | |
202 * ## Other resources | |
203 * | |
204 * [Dart by Example](https://www.dartlang.org/dart-by-example/#dart-io-and-comma
nd-line-apps) | |
205 * provides additional task-oriented code samples that show how to use | |
206 * various API from the [dart:io] library. | |
207 */ | |
208 abstract class Process { | |
209 /** | |
210 * Returns a [:Future:] which completes with the exit code of the process | |
211 * when the process completes. | |
212 * | |
213 * The handling of exit codes is platform specific. | |
214 * | |
215 * On Linux and OS X a normal exit code will be a positive value in | |
216 * the range [0..255]. If the process was terminated due to a signal | |
217 * the exit code will be a negative value in the range [-255..-1], | |
218 * where the absolute value of the exit code is the signal | |
219 * number. For example, if a process crashes due to a segmentation | |
220 * violation the exit code will be -11, as the signal SIGSEGV has the | |
221 * number 11. | |
222 * | |
223 * On Windows a process can report any 32-bit value as an exit | |
224 * code. When returning the exit code this exit code is turned into | |
225 * a signed value. Some special values are used to report | |
226 * termination due to some system event. E.g. if a process crashes | |
227 * due to an access violation the 32-bit exit code is `0xc0000005`, | |
228 * which will be returned as the negative number `-1073741819`. To | |
229 * get the original 32-bit value use `(0x100000000 + exitCode) & | |
230 * 0xffffffff`. | |
231 */ | |
232 Future<int> exitCode; | |
233 | |
234 /** | |
235 * Starts a process running the [executable] with the specified | |
236 * [arguments]. Returns a [:Future<Process>:] that completes with a | |
237 * Process instance when the process has been successfully | |
238 * started. That [Process] object can be used to interact with the | |
239 * process. If the process cannot be started the returned [Future] | |
240 * completes with an exception. | |
241 * | |
242 * Use [workingDirectory] to set the working directory for the process. Note | |
243 * that the change of directory occurs before executing the process on some | |
244 * platforms, which may have impact when using relative paths for the | |
245 * executable and the arguments. | |
246 * | |
247 * Use [environment] to set the environment variables for the process. If not | |
248 * set the environment of the parent process is inherited. Currently, only | |
249 * US-ASCII environment variables are supported and errors are likely to occur | |
250 * if an environment variable with code-points outside the US-ASCII range is | |
251 * passed in. | |
252 * | |
253 * If [includeParentEnvironment] is `true`, the process's environment will | |
254 * include the parent process's environment, with [environment] taking | |
255 * precedence. Default is `true`. | |
256 * | |
257 * If [runInShell] is `true`, the process will be spawned through a system | |
258 * shell. On Linux and OS X, [:/bin/sh:] is used, while | |
259 * [:%WINDIR%\system32\cmd.exe:] is used on Windows. | |
260 * | |
261 * Users must read all data coming on the [stdout] and [stderr] | |
262 * streams of processes started with [:Process.start:]. If the user | |
263 * does not read all data on the streams the underlying system | |
264 * resources will not be released since there is still pending data. | |
265 * | |
266 * The following code uses `Process.start` to grep for `main` in the | |
267 * file `test.dart` on Linux. | |
268 * | |
269 * Process.start('grep', ['-i', 'main', 'test.dart']).then((process) { | |
270 * stdout.addStream(process.stdout); | |
271 * stderr.addStream(process.stderr); | |
272 * }); | |
273 * | |
274 * If [mode] is [ProcessStartMode.NORMAL] (the default) a child | |
275 * process will be started with `stdin`, `stdout` and `stderr` | |
276 * connected. | |
277 * | |
278 * If `mode` is [ProcessStartMode.DETACHED] a detached process will | |
279 * be created. A detached process has no connection to its parent, | |
280 * and can keep running on its own when the parent dies. The only | |
281 * information available from a detached process is its `pid`. There | |
282 * is no connection to its `stdin`, `stdout` or `stderr`, nor will | |
283 * the process' exit code become available when it terminates. | |
284 * | |
285 * If `mode` is [ProcessStartMode.DETACHED_WITH_STDIO] a detached | |
286 * process will be created where the `stdin`, `stdout` and `stderr` | |
287 * are connected. The creator can communicate with the child through | |
288 * these. The detached process will keep running even if these | |
289 * communication channels are closed. The process' exit code will | |
290 * not become available when it terminated. | |
291 * | |
292 * The default value for `mode` is `ProcessStartMode.NORMAL`. | |
293 */ | |
294 external static Future<Process> start( | |
295 String executable, | |
296 List<String> arguments, | |
297 {String workingDirectory, | |
298 Map<String, String> environment, | |
299 bool includeParentEnvironment: true, | |
300 bool runInShell: false, | |
301 ProcessStartMode mode: ProcessStartMode.NORMAL}); | |
302 | |
303 /** | |
304 * Starts a process and runs it non-interactively to completion. The | |
305 * process run is [executable] with the specified [arguments]. | |
306 * | |
307 * Use [workingDirectory] to set the working directory for the process. Note | |
308 * that the change of directory occurs before executing the process on some | |
309 * platforms, which may have impact when using relative paths for the | |
310 * executable and the arguments. | |
311 * | |
312 * Use [environment] to set the environment variables for the process. If not | |
313 * set the environment of the parent process is inherited. Currently, only | |
314 * US-ASCII environment variables are supported and errors are likely to occur | |
315 * if an environment variable with code-points outside the US-ASCII range is | |
316 * passed in. | |
317 * | |
318 * If [includeParentEnvironment] is `true`, the process's environment will | |
319 * include the parent process's environment, with [environment] taking | |
320 * precedence. Default is `true`. | |
321 * | |
322 * If [runInShell] is true, the process will be spawned through a system | |
323 * shell. On Linux and OS X, `/bin/sh` is used, while | |
324 * `%WINDIR%\system32\cmd.exe` is used on Windows. | |
325 * | |
326 * The encoding used for decoding `stdout` and `stderr` into text is | |
327 * controlled through [stdoutEncoding] and [stderrEncoding]. The | |
328 * default encoding is [SYSTEM_ENCODING]. If `null` is used no | |
329 * decoding will happen and the [ProcessResult] will hold binary | |
330 * data. | |
331 * | |
332 * Returns a `Future<ProcessResult>` that completes with the | |
333 * result of running the process, i.e., exit code, standard out and | |
334 * standard in. | |
335 * | |
336 * The following code uses `Process.run` to grep for `main` in the | |
337 * file `test.dart` on Linux. | |
338 * | |
339 * Process.run('grep', ['-i', 'main', 'test.dart']).then((result) { | |
340 * stdout.write(result.stdout); | |
341 * stderr.write(result.stderr); | |
342 * }); | |
343 */ | |
344 external static Future<ProcessResult> run( | |
345 String executable, | |
346 List<String> arguments, | |
347 {String workingDirectory, | |
348 Map<String, String> environment, | |
349 bool includeParentEnvironment: true, | |
350 bool runInShell: false, | |
351 Encoding stdoutEncoding: SYSTEM_ENCODING, | |
352 Encoding stderrEncoding: SYSTEM_ENCODING}); | |
353 | |
354 | |
355 /** | |
356 * Starts a process and runs it to completion. This is a synchronous | |
357 * call and will block until the child process terminates. | |
358 * | |
359 * The arguments are the same as for `Process.run`. | |
360 * | |
361 * Returns a `ProcessResult` with the result of running the process, | |
362 * i.e., exit code, standard out and standard in. | |
363 */ | |
364 external static ProcessResult runSync( | |
365 String executable, | |
366 List<String> arguments, | |
367 {String workingDirectory, | |
368 Map<String, String> environment, | |
369 bool includeParentEnvironment: true, | |
370 bool runInShell: false, | |
371 Encoding stdoutEncoding: SYSTEM_ENCODING, | |
372 Encoding stderrEncoding: SYSTEM_ENCODING}); | |
373 | |
374 /** | |
375 * Kills the process with id [pid]. | |
376 * | |
377 * Where possible, sends the [signal] to the process with id | |
378 * `pid`. This includes Linux and OS X. The default signal is | |
379 * [ProcessSignal.SIGTERM] which will normally terminate the | |
380 * process. | |
381 * | |
382 * On platforms without signal support, including Windows, the call | |
383 * just terminates the process with id `pid` in a platform specific | |
384 * way, and the `signal` parameter is ignored. | |
385 * | |
386 * Returns `true` if the signal is successfully delivered to the | |
387 * process. Otherwise the signal could not be sent, usually meaning | |
388 * that the process is already dead. | |
389 */ | |
390 external static bool killPid( | |
391 int pid, [ProcessSignal signal = ProcessSignal.SIGTERM]); | |
392 | |
393 /** | |
394 * Returns the standard output stream of the process as a [:Stream:]. | |
395 */ | |
396 Stream<List<int>> get stdout; | |
397 | |
398 /** | |
399 * Returns the standard error stream of the process as a [:Stream:]. | |
400 */ | |
401 Stream<List<int>> get stderr; | |
402 | |
403 /** | |
404 * Returns the standard input stream of the process as an [IOSink]. | |
405 */ | |
406 IOSink get stdin; | |
407 | |
408 /** | |
409 * Returns the process id of the process. | |
410 */ | |
411 int get pid; | |
412 | |
413 /** | |
414 * Kills the process. | |
415 * | |
416 * Where possible, sends the [signal] to the process. This includes | |
417 * Linux and OS X. The default signal is [ProcessSignal.SIGTERM] | |
418 * which will normally terminate the process. | |
419 * | |
420 * On platforms without signal support, including Windows, the call | |
421 * just terminates the process in a platform specific way, and the | |
422 * `signal` parameter is ignored. | |
423 * | |
424 * Returns `true` if the signal is successfully delivered to the | |
425 * process. Otherwise the signal could not be sent, usually meaning | |
426 * that the process is already dead. | |
427 */ | |
428 bool kill([ProcessSignal signal = ProcessSignal.SIGTERM]); | |
429 } | |
430 | |
431 | |
432 /** | |
433 * [ProcessResult] represents the result of running a non-interactive | |
434 * process started with [Process.run] or [Process.runSync]. | |
435 */ | |
436 class ProcessResult { | |
437 /** | |
438 * Exit code for the process. | |
439 * | |
440 * See [Process.exitCode] for more information in the exit code | |
441 * value. | |
442 */ | |
443 final int exitCode; | |
444 | |
445 /** | |
446 * Standard output from the process. The value used for the | |
447 * `stdoutEncoding` argument to `Process.run` determines the type. If | |
448 * `null` was used this value is of type `List<int> otherwise it is | |
449 * of type `String`. | |
450 */ | |
451 final stdout; | |
452 | |
453 /** | |
454 * Standard error from the process. The value used for the | |
455 * `stderrEncoding` argument to `Process.run` determines the type. If | |
456 * `null` was used this value is of type `List<int> | |
457 * otherwise it is of type `String`. | |
458 */ | |
459 final stderr; | |
460 | |
461 /** | |
462 * Process id of the process. | |
463 */ | |
464 final int pid; | |
465 | |
466 ProcessResult(this.pid, this.exitCode, this.stdout, this.stderr); | |
467 } | |
468 | |
469 | |
470 /** | |
471 * On Posix systems, [ProcessSignal] is used to send a specific signal | |
472 * to a child process, see [:Process.kill:]. | |
473 * | |
474 * Some [ProcessSignal]s can also be watched, as a way to intercept the default | |
475 * signal handler and implement another. See [ProcessSignal.watch] for more | |
476 * information. | |
477 */ | |
478 class ProcessSignal { | |
479 static const ProcessSignal SIGHUP = const ProcessSignal._(1, "SIGHUP"); | |
480 static const ProcessSignal SIGINT = const ProcessSignal._(2, "SIGINT"); | |
481 static const ProcessSignal SIGQUIT = const ProcessSignal._(3, "SIGQUIT"); | |
482 static const ProcessSignal SIGILL = const ProcessSignal._(4, "SIGILL"); | |
483 static const ProcessSignal SIGTRAP = const ProcessSignal._(5, "SIGTRAP"); | |
484 static const ProcessSignal SIGABRT = const ProcessSignal._(6, "SIGABRT"); | |
485 static const ProcessSignal SIGBUS = const ProcessSignal._(7, "SIGBUS"); | |
486 static const ProcessSignal SIGFPE = const ProcessSignal._(8, "SIGFPE"); | |
487 static const ProcessSignal SIGKILL = const ProcessSignal._(9, "SIGKILL"); | |
488 static const ProcessSignal SIGUSR1 = const ProcessSignal._(10, "SIGUSR1"); | |
489 static const ProcessSignal SIGSEGV = const ProcessSignal._(11, "SIGSEGV"); | |
490 static const ProcessSignal SIGUSR2 = const ProcessSignal._(12, "SIGUSR2"); | |
491 static const ProcessSignal SIGPIPE = const ProcessSignal._(13, "SIGPIPE"); | |
492 static const ProcessSignal SIGALRM = const ProcessSignal._(14, "SIGALRM"); | |
493 static const ProcessSignal SIGTERM = const ProcessSignal._(15, "SIGTERM"); | |
494 static const ProcessSignal SIGCHLD = const ProcessSignal._(17, "SIGCHLD"); | |
495 static const ProcessSignal SIGCONT = const ProcessSignal._(18, "SIGCONT"); | |
496 static const ProcessSignal SIGSTOP = const ProcessSignal._(19, "SIGSTOP"); | |
497 static const ProcessSignal SIGTSTP = const ProcessSignal._(20, "SIGTSTP"); | |
498 static const ProcessSignal SIGTTIN = const ProcessSignal._(21, "SIGTTIN"); | |
499 static const ProcessSignal SIGTTOU = const ProcessSignal._(22, "SIGTTOU"); | |
500 static const ProcessSignal SIGURG = const ProcessSignal._(23, "SIGURG"); | |
501 static const ProcessSignal SIGXCPU = const ProcessSignal._(24, "SIGXCPU"); | |
502 static const ProcessSignal SIGXFSZ = const ProcessSignal._(25, "SIGXFSZ"); | |
503 static const ProcessSignal SIGVTALRM = const ProcessSignal._(26, "SIGVTALRM"); | |
504 static const ProcessSignal SIGPROF = const ProcessSignal._(27, "SIGPROF"); | |
505 static const ProcessSignal SIGWINCH = const ProcessSignal._(28, "SIGWINCH"); | |
506 static const ProcessSignal SIGPOLL = const ProcessSignal._(29, "SIGPOLL"); | |
507 static const ProcessSignal SIGSYS = const ProcessSignal._(31, "SIGSYS"); | |
508 | |
509 final int _signalNumber; | |
510 final String _name; | |
511 | |
512 const ProcessSignal._(this._signalNumber, this._name); | |
513 | |
514 String toString() => _name; | |
515 | |
516 /** | |
517 * Watch for process signals. | |
518 * | |
519 * The following [ProcessSignal]s can be listened to: | |
520 * | |
521 * * [ProcessSignal.SIGHUP]. | |
522 * * [ProcessSignal.SIGINT]. Signal sent by e.g. CTRL-C. | |
523 * * [ProcessSignal.SIGTERM]. Not available on Windows. | |
524 * * [ProcessSignal.SIGUSR1]. Not available on Windows. | |
525 * * [ProcessSignal.SIGUSR2]. Not available on Windows. | |
526 * * [ProcessSignal.SIGWINCH]. Not available on Windows. | |
527 * | |
528 * Other signals are disallowed, as they may be used by the VM. | |
529 * | |
530 * A signal can be watched multiple times, from multiple isolates, where all | |
531 * callbacks are invoked when signaled, in no specific order. | |
532 */ | |
533 Stream<ProcessSignal> watch() => _ProcessUtils._watchSignal(this); | |
534 } | |
535 | |
536 | |
537 class SignalException implements IOException { | |
538 final String message; | |
539 final osError; | |
540 | |
541 const SignalException(this.message, [this.osError = null]); | |
542 | |
543 String toString() { | |
544 var msg = ""; | |
545 if (osError != null) { | |
546 msg = ", osError: $osError"; | |
547 } | |
548 return "SignalException: $message$msg"; | |
549 } | |
550 } | |
551 | |
552 | |
553 class ProcessException implements IOException { | |
554 /** | |
555 * Contains the executable provided for the process. | |
556 */ | |
557 final String executable; | |
558 | |
559 /** | |
560 * Contains the arguments provided for the process. | |
561 */ | |
562 final List<String> arguments; | |
563 | |
564 /** | |
565 * Contains the system message for the process exception if any. | |
566 */ | |
567 final String message; | |
568 | |
569 /** | |
570 * Contains the OS error code for the process exception if any. | |
571 */ | |
572 final int errorCode; | |
573 | |
574 const ProcessException(this.executable, this.arguments, [this.message = "", | |
575 this.errorCode = 0]); | |
576 String toString() { | |
577 var msg = (message == null) ? 'OS error code: $errorCode' : message; | |
578 var args = arguments.join(' '); | |
579 return "ProcessException: $msg\n Command: $executable $args"; | |
580 } | |
581 } | |
OLD | NEW |