// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. part of dart.io; // TODO(ager): The only reason for this class is that we // cannot patch a top-level at this point. class _ProcessUtils { external static void _exit(int status); external static void _setExitCode(int status); external static int _getExitCode(); external static void _sleep(int millis); external static int _pid(Process process); external static Stream _watchSignal(ProcessSignal signal); } /** * Exit the Dart VM process immediately with the given exit code. * * This does not wait for any asynchronous operations to terminate. Using * [exit] is therefore very likely to lose data. * * The handling of exit codes is platform specific. * * On Linux and Mac OS an exit code for normal termination will always * be in the range [0..255]. If an exit code outside this range is * set the actual exit code will be the lower 8 bits masked off and * treated as an unsigned value. E.g. using an exit code of -1 will * result in an actual exit code of 255 being reported. * * On Windows the exit code can be set to any 32-bit value. However * some of these values are reserved for reporting system errors like * crashes. * * Besides this the Dart executable itself uses an exit code of `254` * for reporting compile time errors and an exit code of `255` for * reporting runtime error (unhandled exception). * * Due to these facts it is recommended to only use exit codes in the * range [0..127] for communicating the result of running a Dart * program to the surrounding environment. This will avoid any * cross-platform issues. */ void exit(int code) { if (code is !int) { throw new ArgumentError("Integer value for exit code expected"); } _ProcessUtils._exit(code); } /** * Set the global exit code for the Dart VM. * * The exit code is global for the Dart VM and the last assignment to * exitCode from any isolate determines the exit code of the Dart VM * on normal termination. * * Default value is `0`. * * See [exit] for more information on how to chose a value for the * exit code. */ void set exitCode(int code) { if (code is !int) { throw new ArgumentError("Integer value for exit code expected"); } _ProcessUtils._setExitCode(code); } /* * Get the global exit code for the Dart VM. * * The exit code is global for the Dart VM and the last assignment to * exitCode from any isolate determines the exit code of the Dart VM * on normal termination. * * See [exit] for more information on how to chose a value for the * exit code. */ int get exitCode => _ProcessUtils._getExitCode(); /** * Sleep for the duration specified in [duration]. * * Use this with care, as no asynchronous operations can be processed * in a isolate while it is blocked in a [sleep] call. */ void sleep(Duration duration) { int milliseconds = duration.inMilliseconds; if (milliseconds < 0) { throw new ArgumentError("sleep: duration cannot be negative"); } _ProcessUtils._sleep(milliseconds); } /** * Returns the PID of the current process. */ int get pid => _ProcessUtils._pid(null); /** * The means to execute a program. * * Use the static [start] and [run] methods to start a new process. * The run method executes the process non-interactively to completion. * In contrast, the start method allows your code to interact with the * running process. * * ## Start a process with the run method * * The following code sample uses the run method to create a process * that runs the UNIX command `ls`, which lists the contents of a directory. * The run method completes with a [ProcessResult] object when the process * terminates. This provides access to the output and exit code from the * process. The run method does not return a Process object; this prevents your * code from interacting with the running process. * * import 'dart:io'; * * main() { * // List all files in the current directory in UNIX-like systems. * Process.run('ls', ['-l']).then((ProcessResult results) { * print(results.stdout); * }); * } * * ## Start a process with the start method * * The following example uses start to create the process. * The start method returns a [Future] for a Process object. * When the future completes the process is started and * your code can interact with the * Process: writing to stdin, listening to stdout, and so on. * * The following sample starts the UNIX `cat` utility, which when given no * command-line arguments, echos its input. * The program writes to the process's standard input stream * and prints data from its standard output stream. * * import 'dart:io'; * import 'dart:convert'; * * main() { * Process.start('cat', []).then((Process process) { * process.stdout * .transform(UTF8.decoder) * .listen((data) { print(data); }); * process.stdin.writeln('Hello, world!'); * process.stdin.writeln('Hello, galaxy!'); * process.stdin.writeln('Hello, universe!'); * }); * } * * ## Standard I/O streams * * As seen in the previous code sample, you can interact with the Process's * standard output stream through the getter [stdout], * and you can interact with the Process's standard input stream through * the getter [stdin]. * In addition, Process provides a getter [stderr] for using the Process's * standard error stream. * * A Process's streams are distinct from the top-level streams * for the current program. * * ## Exit codes * * Call the [exitCode] method to get the exit code of the process. * The exit code indicates whether the program terminated successfully * (usually indicated with an exit code of 0) or with an error. * * If the start method is used, the exitCode is available through a future * on the Process object (as shown in the example below). * If the run method is used, the exitCode is available * through a getter on the ProcessResult instance. * * import 'dart:io'; * * main() { * Process.start('ls', ['-l']).then((process) { * // Get the exit code from the new process. * process.exitCode.then((exitCode) { * print('exit code: $exitCode'); * }); * }); * } * * ## Other resources * * [Dart by Example](https://www.dartlang.org/dart-by-example/#dart-io-and-command-line-apps) * provides additional task-oriented code samples that show how to use * various API from the [dart:io] library. */ abstract class Process { /** * Returns a [:Future:] which completes with the exit code of the process * when the process completes. * * The handling of exit codes is platform specific. * * On Linux and Mac a normal exit code will be a positive value in * the range [0..255]. If the process was terminated due to a signal * the exit code will be a negative value in the range [-255..-1], * where the absolute value of the exit code is the signal * number. For example, if a process crashes due to a segmentation * violation the exit code will be -11, as the signal SIGSEGV has the * number 11. * * On Windows a process can report any 32-bit value as an exit * code. When returning the exit code this exit code is turned into * a signed value. Some special values are used to report * termination due to some system event. E.g. if a process crashes * due to an access violation the 32-bit exit code is `0xc0000005`, * which will be returned as the negative number `-1073741819`. To * get the original 32-bit value use `(0x100000000 + exitCode) & * 0xffffffff`. */ Future exitCode; /** * Starts a process running the [executable] with the specified * [arguments]. Returns a [:Future:] that completes with a * Process instance when the process has been successfully * started. That [Process] object can be used to interact with the * process. If the process cannot be started the returned [Future] * completes with an exception. * * Use [workingDirectory] to set the working directory for the process. Note * that the change of directory occurs before executing the process on some * platforms, which may have impact when using relative paths for the * executable and the arguments. * * Use [environment] to set the environment variables for the process. If not * set the environment of the parent process is inherited. Currently, only * US-ASCII environment variables are supported and errors are likely to occur * if an environment variable with code-points outside the US-ASCII range is * passed in. * * If [includeParentEnvironment] is `true`, the process's environment will * include the parent process's environment, with [environment] taking * precedence. Default is `true`. * * If [runInShell] is true, the process will be spawned through a system * shell. On Linux and Mac OS, [:/bin/sh:] is used, while * [:%WINDIR%\system32\cmd.exe:] is used on Windows. * * Users must read all data coming on the [stdout] and [stderr] * streams of processes started with [:Process.start:]. If the user * does not read all data on the streams the underlying system * resources will not be freed since there is still pending data. * * The following code uses `Process.start` to grep for `main` in the * file `test.dart` on Linux. * * Process.start('grep', ['-i', 'main', 'test.dart']).then((process) { * stdout.addStream(process.stdout); * stderr.addStream(process.stderr); * }); */ external static Future start( String executable, List arguments, {String workingDirectory, Map environment, bool includeParentEnvironment: true, bool runInShell: false}); /** * Starts a process and runs it non-interactively to completion. The * process run is [executable] with the specified [arguments]. * * Use [workingDirectory] to set the working directory for the process. Note * that the change of directory occurs before executing the process on some * platforms, which may have impact when using relative paths for the * executable and the arguments. * * Use [environment] to set the environment variables for the process. If not * set the environment of the parent process is inherited. Currently, only * US-ASCII environment variables are supported and errors are likely to occur * if an environment variable with code-points outside the US-ASCII range is * passed in. * * If [includeParentEnvironment] is `true`, the process's environment will * include the parent process's environment, with [environment] taking * precedence. Default is `true`. * * If [runInShell] is true, the process will be spawned through a system * shell. On Linux and Mac OS, `/bin/sh` is used, while * `%WINDIR%\system32\cmd.exe` is used on Windows. * * The encoding used for decoding `stdout` and `stderr` into text is * controlled through [stdoutEncoding] and [stderrEncoding]. The * default encoding is [SYSTEM_ENCODING]. If `null` is used no * decoding will happen and the [ProcessResult] will hold binary * data. * * Returns a `Future` that completes with the * result of running the process, i.e., exit code, standard out and * standard in. * * The following code uses `Process.run` to grep for `main` in the * file `test.dart` on Linux. * * Process.run('grep', ['-i', 'main', 'test.dart']).then((result) { * stdout.write(result.stdout); * stderr.write(result.stderr); * }); */ external static Future run( String executable, List arguments, {String workingDirectory, Map environment, bool includeParentEnvironment: true, bool runInShell: false, Encoding stdoutEncoding: SYSTEM_ENCODING, Encoding stderrEncoding: SYSTEM_ENCODING}); /** * Starts a process and runs it to completion. This is a synchronous * call and will block until the child process terminates. * * The arguments are the same as for `Process.run`. * * Returns a `ProcessResult` with the result of running the process, * i.e., exit code, standard out and standard in. */ external static ProcessResult runSync( String executable, List arguments, {String workingDirectory, Map environment, bool includeParentEnvironment: true, bool runInShell: false, Encoding stdoutEncoding: SYSTEM_ENCODING, Encoding stderrEncoding: SYSTEM_ENCODING}); /** * Returns the standard output stream of the process as a [:Stream:]. */ Stream> get stdout; /** * Returns the standard error stream of the process as a [:Stream:]. */ Stream> get stderr; /** * Returns the standard input stream of the process as an [IOSink]. */ IOSink get stdin; /** * Returns the process id of the process. */ int get pid; /** * On Linux and Mac OS, [kill] sends [signal] to the process. When the process * terminates as a result of calling [kill], the value for [exitCode] may be a * negative number corresponding to the provided [signal]. * * On Windows, [kill] kills the process, ignoring the [signal] flag. * * Returns [:true:] if the signal is successfully sent and process is killed. * Otherwise the signal could not be sent, usually meaning that the process is * already dead. */ bool kill([ProcessSignal signal = ProcessSignal.SIGTERM]); } /** * [ProcessResult] represents the result of running a non-interactive * process started with [:Process.run:]. */ abstract class ProcessResult { /** * Exit code for the process. * * See [Process.exitCode] for more information in the exit code * value. */ int get exitCode; /** * Standard output from the process. The value used for the * `stdoutEncoding` argument to `Process.run` determines the type. If * `null` was used this value is of type `List otherwise it is * of type `String`. */ get stdout; /** * Standard error from the process. The value used for the * `stderrEncoding` argument to `Process.run` determines the type. If * `null` was used this value is of type `List * otherwise it is of type `String`. */ get stderr; /** * Process id from the process. */ int get pid; } /** * On Posix systems, [ProcessSignal] is used to send a specific signal * to a child process, see [:Process.kill:]. * * Some [ProcessSignal]s can also be watched, as a way to intercept the default * signal handler and implement another. See [ProcessSignal.watch] for more * information. */ class ProcessSignal { static const ProcessSignal SIGHUP = const ProcessSignal._(1, "SIGHUP"); static const ProcessSignal SIGINT = const ProcessSignal._(2, "SIGINT"); static const ProcessSignal SIGQUIT = const ProcessSignal._(3, "SIGQUIT"); static const ProcessSignal SIGILL = const ProcessSignal._(4, "SIGILL"); static const ProcessSignal SIGTRAP = const ProcessSignal._(5, "SIGTRAP"); static const ProcessSignal SIGABRT = const ProcessSignal._(6, "SIGABRT"); static const ProcessSignal SIGBUS = const ProcessSignal._(7, "SIGBUS"); static const ProcessSignal SIGFPE = const ProcessSignal._(8, "SIGFPE"); static const ProcessSignal SIGKILL = const ProcessSignal._(9, "SIGKILL"); static const ProcessSignal SIGUSR1 = const ProcessSignal._(10, "SIGUSR1"); static const ProcessSignal SIGSEGV = const ProcessSignal._(11, "SIGSEGV"); static const ProcessSignal SIGUSR2 = const ProcessSignal._(12, "SIGUSR2"); static const ProcessSignal SIGPIPE = const ProcessSignal._(13, "SIGPIPE"); static const ProcessSignal SIGALRM = const ProcessSignal._(14, "SIGALRM"); static const ProcessSignal SIGTERM = const ProcessSignal._(15, "SIGTERM"); static const ProcessSignal SIGCHLD = const ProcessSignal._(17, "SIGCHLD"); static const ProcessSignal SIGCONT = const ProcessSignal._(18, "SIGCONT"); static const ProcessSignal SIGSTOP = const ProcessSignal._(19, "SIGSTOP"); static const ProcessSignal SIGTSTP = const ProcessSignal._(20, "SIGTSTP"); static const ProcessSignal SIGTTIN = const ProcessSignal._(21, "SIGTTIN"); static const ProcessSignal SIGTTOU = const ProcessSignal._(22, "SIGTTOU"); static const ProcessSignal SIGURG = const ProcessSignal._(23, "SIGURG"); static const ProcessSignal SIGXCPU = const ProcessSignal._(24, "SIGXCPU"); static const ProcessSignal SIGXFSZ = const ProcessSignal._(25, "SIGXFSZ"); static const ProcessSignal SIGVTALRM = const ProcessSignal._(26, "SIGVTALRM"); static const ProcessSignal SIGPROF = const ProcessSignal._(27, "SIGPROF"); static const ProcessSignal SIGWINCH = const ProcessSignal._(28, "SIGWINCH"); static const ProcessSignal SIGPOLL = const ProcessSignal._(29, "SIGPOLL"); static const ProcessSignal SIGSYS = const ProcessSignal._(31, "SIGSYS"); final int _signalNumber; final String _name; const ProcessSignal._(this._signalNumber, this._name); String toString() => _name; /** * Watch for process signals. * * The following [ProcessSignal]s can be listened to: * * * [ProcessSignal.SIGHUP]. * * [ProcessSignal.SIGINT]. Signal sent by e.g. CTRL-C. * * [ProcessSignal.SIGTERM]. Not available on Windows. * * [ProcessSignal.SIGUSR1]. Not available on Windows. * * [ProcessSignal.SIGUSR2]. Not available on Windows. * * [ProcessSignal.SIGWINCH]. Not available on Windows. * * Other signals are disallowed, as they may be used by the VM. * * A signal can be watched multiple times, from multiple isolates, where all * callbacks are invoked when signaled, in no specific order. */ Stream watch() => _ProcessUtils._watchSignal(this); } class SignalException implements IOException { final String message; final osError; const SignalException(this.message, [this.osError = null]); String toString() { var msg = ""; if (osError != null) { msg = ", osError: $osError"; } return "SignalException: $message$msg"; } } class ProcessException implements IOException { /** * Contains the executable provided for the process. */ final String executable; /** * Contains the arguments provided for the process. */ final List arguments; /** * Contains the system message for the process exception if any. */ final String message; /** * Contains the OS error code for the process exception if any. */ final int errorCode; const ProcessException(this.executable, this.arguments, [this.message = "", this.errorCode = 0]); String toString() { var msg = (message == null) ? 'OS error code: $errorCode' : message; var args = arguments.join(' '); return "ProcessException: $msg\n Command: $executable $args"; } }