OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 patch class _WindowsCodePageDecoder { | 5 patch class _WindowsCodePageDecoder { |
6 /* patch */ static String _decodeBytes(List<int> bytes) | 6 /* patch */ static String _decodeBytes(List<int> bytes) |
7 native "SystemEncodingToString"; | 7 native "SystemEncodingToString"; |
8 } | 8 } |
9 | 9 |
10 | 10 |
11 patch class _WindowsCodePageEncoder { | 11 patch class _WindowsCodePageEncoder { |
12 /* patch */ static List<int> _encodeString(String string) | 12 /* patch */ static List<int> _encodeString(String string) |
13 native "StringToSystemEncoding"; | 13 native "StringToSystemEncoding"; |
14 } | 14 } |
15 | 15 |
16 | 16 |
17 patch class Process { | 17 patch class Process { |
18 /* patch */ static Future<Process> start( | 18 /* patch */ static Future<Process> start( |
19 String executable, | 19 String executable, |
20 List<String> arguments, | 20 List<String> arguments, |
21 {String workingDirectory, | 21 {String workingDirectory, |
22 Map<String, String> environment, | 22 Map<String, String> environment, |
23 bool includeParentEnvironment: true, | 23 bool includeParentEnvironment: true, |
24 bool runInShell: false}) { | 24 bool runInShell: false, |
| 25 bool detach: false}) { |
25 _ProcessImpl process = new _ProcessImpl(executable, | 26 _ProcessImpl process = new _ProcessImpl(executable, |
26 arguments, | 27 arguments, |
27 workingDirectory, | 28 workingDirectory, |
28 environment, | 29 environment, |
29 includeParentEnvironment, | 30 includeParentEnvironment, |
30 runInShell); | 31 runInShell, |
| 32 detach); |
31 return process._start(); | 33 return process._start(); |
32 } | 34 } |
33 | 35 |
34 /* patch */ static Future<ProcessResult> run( | 36 /* patch */ static Future<ProcessResult> run( |
35 String executable, | 37 String executable, |
36 List<String> arguments, | 38 List<String> arguments, |
37 {String workingDirectory, | 39 {String workingDirectory, |
38 Map<String, String> environment, | 40 Map<String, String> environment, |
39 bool includeParentEnvironment: true, | 41 bool includeParentEnvironment: true, |
40 bool runInShell: false, | 42 bool runInShell: false, |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
166 class _ProcessImpl extends _ProcessImplNativeWrapper with _ServiceObject | 168 class _ProcessImpl extends _ProcessImplNativeWrapper with _ServiceObject |
167 implements Process { | 169 implements Process { |
168 // Use default Map so we keep order. | 170 // Use default Map so we keep order. |
169 static Map<int, _ProcessImpl> _processes = new Map<int, _ProcessImpl>(); | 171 static Map<int, _ProcessImpl> _processes = new Map<int, _ProcessImpl>(); |
170 | 172 |
171 _ProcessImpl(String path, | 173 _ProcessImpl(String path, |
172 List<String> arguments, | 174 List<String> arguments, |
173 String this._workingDirectory, | 175 String this._workingDirectory, |
174 Map<String, String> environment, | 176 Map<String, String> environment, |
175 bool includeParentEnvironment, | 177 bool includeParentEnvironment, |
176 bool runInShell) : super() { | 178 bool runInShell, |
| 179 bool detach) : super() { |
177 _processes[_serviceId] = this; | 180 _processes[_serviceId] = this; |
178 if (runInShell) { | 181 if (runInShell) { |
179 arguments = _getShellArguments(path, arguments); | 182 arguments = _getShellArguments(path, arguments); |
180 path = _getShellCommand(); | 183 path = _getShellCommand(); |
181 } | 184 } |
182 | 185 |
183 if (path is !String) { | 186 if (path is !String) { |
184 throw new ArgumentError("Path is not a String: $path"); | 187 throw new ArgumentError("Path is not a String: $path"); |
185 } | 188 } |
186 _path = path; | 189 _path = path; |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
223 Platform.environment.forEach((key, value) { | 226 Platform.environment.forEach((key, value) { |
224 assert(key is String); | 227 assert(key is String); |
225 assert(value is String); | 228 assert(value is String); |
226 // Do not override keys already set as part of environment. | 229 // Do not override keys already set as part of environment. |
227 if (!environment.containsKey(key)) { | 230 if (!environment.containsKey(key)) { |
228 _environment.add('$key=$value'); | 231 _environment.add('$key=$value'); |
229 } | 232 } |
230 }); | 233 }); |
231 } | 234 } |
232 | 235 |
233 // stdin going to process. | 236 if (detach is !bool) { |
234 _stdin = new _StdSink(new _Socket._writePipe()); | 237 throw new ArgumentError("Detach is not a boolean: $detach"); |
235 _stdin._sink._owner = this; | 238 } |
236 // stdout coming from process. | 239 _detach = detach; |
237 _stdout = new _StdStream(new _Socket._readPipe()); | 240 |
238 _stdout._stream._owner = this; | 241 |
239 // stderr coming from process. | 242 if (!detach) { |
240 _stderr = new _StdStream(new _Socket._readPipe()); | 243 // stdin going to process. |
241 _stderr._stream._owner = this; | 244 _stdin = new _StdSink(new _Socket._writePipe()); |
242 _exitHandler = new _Socket._readPipe(); | 245 _stdin._sink._owner = this; |
| 246 // stdout coming from process. |
| 247 _stdout = new _StdStream(new _Socket._readPipe()); |
| 248 _stdout._stream._owner = this; |
| 249 // stderr coming from process. |
| 250 _stderr = new _StdStream(new _Socket._readPipe()); |
| 251 _stderr._stream._owner = this; |
| 252 _exitHandler = new _Socket._readPipe(); |
| 253 } |
243 _ended = false; | 254 _ended = false; |
244 _started = false; | 255 _started = false; |
245 } | 256 } |
246 | 257 |
247 String get _serviceTypePath => 'io/processes'; | 258 String get _serviceTypePath => 'io/processes'; |
248 String get _serviceTypeName => 'Process'; | 259 String get _serviceTypeName => 'Process'; |
249 | 260 |
250 Map _toJSON(bool ref) { | 261 Map _toJSON(bool ref) { |
251 var r = { | 262 var r = { |
252 'id': _servicePath, | 263 'id': _servicePath, |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
356 | 367 |
357 int _intFromBytes(List<int> bytes, int offset) { | 368 int _intFromBytes(List<int> bytes, int offset) { |
358 return (bytes[offset] + | 369 return (bytes[offset] + |
359 (bytes[offset + 1] << 8) + | 370 (bytes[offset + 1] << 8) + |
360 (bytes[offset + 2] << 16) + | 371 (bytes[offset + 2] << 16) + |
361 (bytes[offset + 3] << 24)); | 372 (bytes[offset + 3] << 24)); |
362 } | 373 } |
363 | 374 |
364 Future<Process> _start() { | 375 Future<Process> _start() { |
365 var completer = new Completer(); | 376 var completer = new Completer(); |
| 377 if (!_detach) { |
| 378 _exitCode = new Completer<int>(); |
| 379 } |
366 // TODO(ager): Make the actual process starting really async instead of | 380 // TODO(ager): Make the actual process starting really async instead of |
367 // simulating it with a timer. | 381 // simulating it with a timer. |
368 Timer.run(() { | 382 Timer.run(() { |
369 var status = new _ProcessStartStatus(); | 383 var status = new _ProcessStartStatus(); |
370 bool success = _startNative(_path, | 384 bool success = |
371 _arguments, | 385 _startNative(_path, |
372 _workingDirectory, | 386 _arguments, |
373 _environment, | 387 _workingDirectory, |
374 _stdin._sink._nativeSocket, | 388 _environment, |
375 _stdout._stream._nativeSocket, | 389 _detach, |
376 _stderr._stream._nativeSocket, | 390 _detach ? null : _stdin._sink._nativeSocket, |
377 _exitHandler._nativeSocket, | 391 _detach ? null : _stdout._stream._nativeSocket, |
378 status); | 392 _detach ? null : _stderr._stream._nativeSocket, |
| 393 _detach ? null : _exitHandler._nativeSocket, |
| 394 status); |
379 if (!success) { | 395 if (!success) { |
380 completer.completeError( | 396 completer.completeError( |
381 new ProcessException(_path, | 397 new ProcessException(_path, |
382 _arguments, | 398 _arguments, |
383 status._errorMessage, | 399 status._errorMessage, |
384 status._errorCode)); | 400 status._errorCode)); |
385 return; | 401 return; |
386 } | 402 } |
387 | 403 |
388 _started = true; | 404 _started = true; |
389 | 405 |
390 // Setup an exit handler to handle internal cleanup and possible | 406 // Setup an exit handler to handle internal cleanup and possible |
391 // callback when a process terminates. | 407 // callback when a process terminates. |
392 int exitDataRead = 0; | 408 if (!_detach) { |
393 final int EXIT_DATA_SIZE = 8; | 409 int exitDataRead = 0; |
394 List<int> exitDataBuffer = new List<int>(EXIT_DATA_SIZE); | 410 final int EXIT_DATA_SIZE = 8; |
395 _exitHandler.listen((data) { | 411 List<int> exitDataBuffer = new List<int>(EXIT_DATA_SIZE); |
| 412 _exitHandler.listen((data) { |
396 | 413 |
397 int exitCode(List<int> ints) { | 414 int exitCode(List<int> ints) { |
398 var code = _intFromBytes(ints, 0); | 415 var code = _intFromBytes(ints, 0); |
399 var negative = _intFromBytes(ints, 4); | 416 var negative = _intFromBytes(ints, 4); |
400 assert(negative == 0 || negative == 1); | 417 assert(negative == 0 || negative == 1); |
401 return (negative == 0) ? code : -code; | 418 return (negative == 0) ? code : -code; |
402 } | 419 } |
403 | 420 |
404 void handleExit() { | 421 void handleExit() { |
405 _ended = true; | 422 _ended = true; |
406 _exitCode.complete(exitCode(exitDataBuffer)); | 423 _exitCode.complete(exitCode(exitDataBuffer)); |
407 // Kill stdin, helping hand if the user forgot to do it. | 424 // Kill stdin, helping hand if the user forgot to do it. |
408 _stdin._sink.destroy(); | 425 _stdin._sink.destroy(); |
409 _processes.remove(_serviceId); | 426 _processes.remove(_serviceId); |
410 } | 427 } |
411 | 428 |
412 exitDataBuffer.setRange(exitDataRead, exitDataRead + data.length, data); | 429 exitDataBuffer.setRange( |
413 exitDataRead += data.length; | 430 exitDataRead, exitDataRead + data.length, data); |
414 if (exitDataRead == EXIT_DATA_SIZE) { | 431 exitDataRead += data.length; |
415 handleExit(); | 432 if (exitDataRead == EXIT_DATA_SIZE) { |
416 } | 433 handleExit(); |
417 }); | 434 } |
| 435 }); |
| 436 } |
418 | 437 |
419 completer.complete(this); | 438 completer.complete(this); |
420 }); | 439 }); |
421 return completer.future; | 440 return completer.future; |
422 } | 441 } |
423 | 442 |
424 ProcessResult _runAndWait(Encoding stdoutEncoding, | 443 ProcessResult _runAndWait(Encoding stdoutEncoding, |
425 Encoding stderrEncoding) { | 444 Encoding stderrEncoding) { |
426 var status = new _ProcessStartStatus(); | 445 var status = new _ProcessStartStatus(); |
| 446 _exitCode = new Completer<int>(); |
427 bool success = _startNative(_path, | 447 bool success = _startNative(_path, |
428 _arguments, | 448 _arguments, |
429 _workingDirectory, | 449 _workingDirectory, |
430 _environment, | 450 _environment, |
| 451 false, |
431 _stdin._sink._nativeSocket, | 452 _stdin._sink._nativeSocket, |
432 _stdout._stream._nativeSocket, | 453 _stdout._stream._nativeSocket, |
433 _stderr._stream._nativeSocket, | 454 _stderr._stream._nativeSocket, |
434 _exitHandler._nativeSocket, | 455 _exitHandler._nativeSocket, |
435 status); | 456 status); |
436 if (!success) { | 457 if (!success) { |
437 throw new ProcessException(_path, | 458 throw new ProcessException(_path, |
438 _arguments, | 459 _arguments, |
439 status._errorMessage, | 460 status._errorMessage, |
440 status._errorCode); | 461 status._errorCode); |
(...skipping 16 matching lines...) Expand all Loading... |
457 result[0], | 478 result[0], |
458 result[1], | 479 result[1], |
459 getOutput(result[2], stdoutEncoding), | 480 getOutput(result[2], stdoutEncoding), |
460 getOutput(result[3], stderrEncoding)); | 481 getOutput(result[3], stderrEncoding)); |
461 } | 482 } |
462 | 483 |
463 bool _startNative(String path, | 484 bool _startNative(String path, |
464 List<String> arguments, | 485 List<String> arguments, |
465 String workingDirectory, | 486 String workingDirectory, |
466 List<String> environment, | 487 List<String> environment, |
| 488 bool detach, |
467 _NativeSocket stdin, | 489 _NativeSocket stdin, |
468 _NativeSocket stdout, | 490 _NativeSocket stdout, |
469 _NativeSocket stderr, | 491 _NativeSocket stderr, |
470 _NativeSocket exitHandler, | 492 _NativeSocket exitHandler, |
471 _ProcessStartStatus status) native "Process_Start"; | 493 _ProcessStartStatus status) native "Process_Start"; |
472 | 494 |
473 _wait(_NativeSocket stdin, | 495 _wait(_NativeSocket stdin, |
474 _NativeSocket stdout, | 496 _NativeSocket stdout, |
475 _NativeSocket stderr, | 497 _NativeSocket stderr, |
476 _NativeSocket exitHandler) native "Process_Wait"; | 498 _NativeSocket exitHandler) native "Process_Wait"; |
477 | 499 |
478 Stream<List<int>> get stdout { | 500 Stream<List<int>> get stdout { |
479 return _stdout; | 501 return _stdout; |
480 } | 502 } |
481 | 503 |
482 Stream<List<int>> get stderr { | 504 Stream<List<int>> get stderr { |
483 return _stderr; | 505 return _stderr; |
484 } | 506 } |
485 | 507 |
486 IOSink get stdin { | 508 IOSink get stdin { |
487 return _stdin; | 509 return _stdin; |
488 } | 510 } |
489 | 511 |
490 Future<int> get exitCode => _exitCode.future; | 512 Future<int> get exitCode => _exitCode != null ? _exitCode.future : null; |
491 | 513 |
492 bool kill([ProcessSignal signal = ProcessSignal.SIGTERM]) { | 514 bool kill([ProcessSignal signal = ProcessSignal.SIGTERM]) { |
493 if (signal is! ProcessSignal) { | 515 if (signal is! ProcessSignal) { |
494 throw new ArgumentError( | 516 throw new ArgumentError( |
495 "Argument 'signal' must be a ProcessSignal"); | 517 "Argument 'signal' must be a ProcessSignal"); |
496 } | 518 } |
497 assert(_started); | 519 assert(_started); |
498 if (_ended) return false; | 520 if (_ended) return false; |
499 return _kill(this, signal._signalNumber); | 521 return _kill(this, signal._signalNumber); |
500 } | 522 } |
501 | 523 |
502 bool _kill(Process p, int signal) native "Process_Kill"; | 524 bool _kill(Process p, int signal) native "Process_Kill"; |
503 | 525 |
504 int get pid => _ProcessUtils._pid(this); | 526 int get pid => _ProcessUtils._pid(this); |
505 | 527 |
506 String _path; | 528 String _path; |
507 List<String> _arguments; | 529 List<String> _arguments; |
508 String _workingDirectory; | 530 String _workingDirectory; |
509 List<String> _environment; | 531 List<String> _environment; |
| 532 bool _detach; |
510 // Private methods of Socket are used by _in, _out, and _err. | 533 // Private methods of Socket are used by _in, _out, and _err. |
511 _StdSink _stdin; | 534 _StdSink _stdin; |
512 _StdStream _stdout; | 535 _StdStream _stdout; |
513 _StdStream _stderr; | 536 _StdStream _stderr; |
514 Socket _exitHandler; | 537 Socket _exitHandler; |
515 bool _ended; | 538 bool _ended; |
516 bool _started; | 539 bool _started; |
517 final Completer<int> _exitCode = new Completer<int>(); | 540 Completer<int> _exitCode; |
518 } | 541 } |
519 | 542 |
520 | 543 |
521 // _NonInteractiveProcess is a wrapper around an interactive process | 544 // _NonInteractiveProcess is a wrapper around an interactive process |
522 // that buffers output so it can be delivered when the process exits. | 545 // that buffers output so it can be delivered when the process exits. |
523 // _NonInteractiveProcess is used to implement the Process.run | 546 // _NonInteractiveProcess is used to implement the Process.run |
524 // method. | 547 // method. |
525 Future<ProcessResult> _runNonInteractiveProcess(String path, | 548 Future<ProcessResult> _runNonInteractiveProcess(String path, |
526 List<String> arguments, | 549 List<String> arguments, |
527 String workingDirectory, | 550 String workingDirectory, |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
577 Map<String, String> environment, | 600 Map<String, String> environment, |
578 bool includeParentEnvironment, | 601 bool includeParentEnvironment, |
579 bool runInShell, | 602 bool runInShell, |
580 Encoding stdoutEncoding, | 603 Encoding stdoutEncoding, |
581 Encoding stderrEncoding) { | 604 Encoding stderrEncoding) { |
582 var process = new _ProcessImpl(executable, | 605 var process = new _ProcessImpl(executable, |
583 arguments, | 606 arguments, |
584 workingDirectory, | 607 workingDirectory, |
585 environment, | 608 environment, |
586 includeParentEnvironment, | 609 includeParentEnvironment, |
587 runInShell); | 610 runInShell, |
| 611 false); |
588 return process._runAndWait(stdoutEncoding, stderrEncoding); | 612 return process._runAndWait(stdoutEncoding, stderrEncoding); |
589 } | 613 } |
590 | 614 |
591 | 615 |
592 class _ProcessResult implements ProcessResult { | 616 class _ProcessResult implements ProcessResult { |
593 const _ProcessResult(int this.pid, | 617 const _ProcessResult(int this.pid, |
594 int this.exitCode, | 618 int this.exitCode, |
595 this.stdout, | 619 this.stdout, |
596 this.stderr); | 620 this.stderr); |
597 | 621 |
598 final int pid; | 622 final int pid; |
599 final int exitCode; | 623 final int exitCode; |
600 final stdout; | 624 final stdout; |
601 final stderr; | 625 final stderr; |
602 } | 626 } |
OLD | NEW |