1 /******************************************************************************* 2 copyright: Copyright (c) 2006 Juan Jose Comellas. All rights reserved 3 license: BSD style: $(LICENSE) 4 author: Juan Jose Comellas <juanjo@comellas.com.ar> 5 *******************************************************************************/ 6 7 module tango.sys.Process; 8 9 private import tango.io.model.IFile; 10 private import tango.io.Console; 11 private import tango.sys.Common; 12 private import tango.sys.Pipe; 13 private import tango.core.Exception; 14 private import tango.text.Util; 15 private import Integer = tango.text.convert.Integer; 16 17 private import tango.stdc.stdlib; 18 private import tango.stdc.string; 19 private import tango.stdc.stringz; 20 21 version (Posix) 22 { 23 private import tango.stdc.errno; 24 private import tango.stdc.posix.fcntl; 25 private import tango.stdc.posix.unistd; 26 private import tango.stdc.posix.sys.wait; 27 28 version(OSX) 29 { 30 extern (C) char*** _NSGetEnviron(); 31 private __gshared char** environ; 32 33 shared static this () 34 { 35 environ = *_NSGetEnviron(); 36 } 37 } 38 39 else 40 private extern (C) extern __gshared char** environ; 41 } 42 43 version (Windows) 44 { 45 version (Win32SansUnicode) 46 { 47 } 48 else 49 { 50 private import tango.text.convert.Utf : toString16; 51 } 52 } 53 54 debug (Process) 55 { 56 private import tango.io.Stdout; 57 } 58 59 60 /** 61 * Redirect flags for processes. Defined outside process class to cut down on 62 * verbosity. 63 */ 64 enum Redirect 65 { 66 /** 67 * Redirect none of the standard handles 68 */ 69 None = 0, 70 71 /** 72 * Redirect the stdout handle to a pipe. 73 */ 74 Output = 1, 75 76 /** 77 * Redirect the stderr handle to a pipe. 78 */ 79 Error = 2, 80 81 /** 82 * Redirect the stdin handle to a pipe. 83 */ 84 Input = 4, 85 86 /** 87 * Redirect all three handles to pipes (default). 88 */ 89 All = Output | Error | Input, 90 91 /** 92 * Send stderr to stdout's handle. Note that the stderr PipeConduit will 93 * be null. 94 */ 95 ErrorToOutput = 0x10, 96 97 /** 98 * Send stdout to stderr's handle. Note that the stdout PipeConduit will 99 * be null. 100 */ 101 OutputToError = 0x20, 102 } 103 104 /** 105 * The Process class is used to start external programs and communicate with 106 * them via their standard input, output and error streams. 107 * 108 * You can pass either the command line or an array of arguments to execute, 109 * either in the constructor or to the args property. The environment 110 * variables can be set in a similar way using the env property and you can 111 * set the program's working directory via the workDir property. 112 * 113 * To actually start a process you need to use the execute() method. Once the 114 * program is running you will be able to write to its standard input via the 115 * stdin OutputStream and you will be able to read from its standard output and 116 * error through the stdout and stderr InputStream respectively. 117 * 118 * You can check whether the process is running or not with the isRunning() 119 * method and you can get its process ID via the pid property. 120 * 121 * After you are done with the process, or if you just want to wait for it to 122 * end, you need to call the wait() method which will return once the process 123 * is no longer running. 124 * 125 * To stop a running process you must use kill() method. If you do this you 126 * cannot call the wait() method. Once the kill() method returns the process 127 * will be already dead. 128 * 129 * After calling either wait() or kill(), and no more data is expected on the 130 * pipes, you should call close() as this will clean the pipes. Not doing this 131 * may lead to a depletion of the available file descriptors for the main 132 * process if many processes are created. 133 * 134 * Examples: 135 * --- 136 * try 137 * { 138 * auto p = new Process ("ls -al", null); 139 * p.execute; 140 * 141 * Stdout.formatln ("Output from {}:", p.programName); 142 * Stdout.copy (p.stdout).flush; 143 * auto result = p.wait; 144 * 145 * Stdout.formatln ("Process '{}' ({}) exited with reason {}, status {}", 146 * p.programName, p.pid, cast(int) result.reason, result.status); 147 * } 148 * catch (ProcessException e) 149 * Stdout.formatln ("Process execution failed: {}", e); 150 * --- 151 */ 152 class Process 153 { 154 /** 155 * Result returned by wait(). 156 */ 157 public struct Result 158 { 159 /** 160 * Reasons returned by wait() indicating why the process is no 161 * longer running. 162 */ 163 public enum 164 { 165 Exit, 166 Signal, 167 Stop, 168 Continue, 169 Error 170 } 171 172 public int reason; 173 public int status; 174 175 /** 176 * Returns a string with a description of the process execution result. 177 */ 178 public immutable(char)[] toString() 179 { 180 const(char)[] str; 181 182 switch (reason) 183 { 184 case Exit: 185 str = format("Process exited normally with return code ", status); 186 break; 187 188 case Signal: 189 str = format("Process was killed with signal ", status); 190 break; 191 192 case Stop: 193 str = format("Process was stopped with signal ", status); 194 break; 195 196 case Continue: 197 str = format("Process was resumed with signal ", status); 198 break; 199 200 case Error: 201 str = format("Process failed with error code ", reason) ~ 202 " : " ~ SysError.lookup(status); 203 break; 204 205 default: 206 str = format("Unknown process result ", reason); 207 break; 208 } 209 return cast(immutable(char)[])str; 210 } 211 } 212 213 enum uint DefaultStdinBufferSize = 512; 214 enum uint DefaultStdoutBufferSize = 8192; 215 enum uint DefaultStderrBufferSize = 512; 216 enum Redirect DefaultRedirectFlags = Redirect.All; 217 218 private const(char[])[] _args; 219 private const(char)[] _program; 220 private const(char[])[char[]] _env; 221 private const(char)[] _workDir; 222 private PipeConduit _stdin; 223 private PipeConduit _stdout; 224 private PipeConduit _stderr; 225 private bool _running = false; 226 private bool _copyEnv = false; 227 private Redirect _redirect = DefaultRedirectFlags; 228 229 version (Windows) 230 { 231 private PROCESS_INFORMATION *_info = null; 232 private bool _gui = false; 233 } 234 else 235 { 236 private pid_t _pid = cast(pid_t) -1; 237 } 238 239 /** 240 * Constructor (variadic version). Note that by default, the environment 241 * will not be copied. 242 * 243 * Params: 244 * args = array of strings with the process' arguments. If there is 245 * exactly one argument, it is considered to contain the entire 246 * command line including parameters. If you pass only one 247 * argument, spaces that are not intended to separate 248 * parameters should be embedded in quotes. The arguments can 249 * also be empty. 250 * Note: The class will use only slices, .dup when necessary. 251 * 252 * Examples: 253 * --- 254 * auto p = new Process("myprogram", "first argument", "second", "third"); 255 * auto p = new Process("myprogram \"first argument\" second third"); 256 * --- 257 */ 258 public this(const(char[])[] args ...) 259 { 260 if(args.length == 1) 261 _args = splitArgs(args[0]); 262 else 263 _args = args; 264 if(_args.length > 0) 265 { 266 _program = _args[0]; 267 _args = _args[1..$]; 268 } 269 } 270 271 /** 272 * Constructor (variadic version, with environment copy). 273 * 274 * Params: 275 * copyEnv = if true, the environment is copied from the current process. 276 * args = array of strings with the process' arguments. If there is 277 * exactly one argument, it is considered to contain the entire 278 * command line including parameters. If you pass only one 279 * argument, spaces that are not intended to separate 280 * parameters should be embedded in quotes. The arguments can 281 * also be empty. 282 * Note: The class will use only slices, .dup when necessary. 283 * 284 * Examples: 285 * --- 286 * auto p = new Process(true, "myprogram", "first argument", "second", "third"); 287 * auto p = new Process(true, "myprogram \"first argument\" second third"); 288 * --- 289 */ 290 public this(bool copyEnv, const(char[])[] args ...) 291 { 292 _copyEnv = copyEnv; 293 this(args); 294 } 295 296 /** 297 * Constructor. 298 * 299 * Params: 300 * command = string with the process' command line; arguments that have 301 * embedded whitespace must be enclosed in inside double-quotes ("). 302 * Note: The class will use only slices, .dup when necessary. 303 * env = associative array of strings with the process' environment 304 * variables; the variable name must be the key of each entry. 305 * 306 * Examples: 307 * --- 308 * char[] command = "myprogram \"first argument\" second third"; 309 * char[][char[]] env; 310 * 311 * // Environment variables 312 * env["MYVAR1"] = "first"; 313 * env["MYVAR2"] = "second"; 314 * 315 * auto p = new Process(command, env) 316 * --- 317 */ 318 public this(const(char)[] command, const(char[])[char[]] env) 319 in 320 { 321 assert(command.length > 0); 322 } 323 body 324 { 325 _args = splitArgs(command); 326 _env = env; 327 if(_args.length > 0) 328 { 329 _program = _args[0]; 330 _args = _args[1..$]; 331 } 332 } 333 334 /** 335 * Constructor. 336 * 337 * Params: 338 * args = array of strings with the process' arguments; the first 339 * argument must be the process' name; the arguments can be 340 * empty. 341 * Note: The class will use only slices, .dup when necessary. 342 * env = associative array of strings with the process' environment 343 * variables; the variable name must be the key of each entry. 344 * 345 * Examples: 346 * --- 347 * char[][] args; 348 * char[][char[]] env; 349 * 350 * // Process name 351 * args ~= "myprogram"; 352 * // Process arguments 353 * args ~= "first argument"; 354 * args ~= "second"; 355 * args ~= "third"; 356 * 357 * // Environment variables 358 * env["MYVAR1"] = "first"; 359 * env["MYVAR2"] = "second"; 360 * 361 * auto p = new Process(args, env) 362 * --- 363 */ 364 public this(const(char[])[] args, const(char[])[char[]] env) 365 in 366 { 367 assert(args.length > 0); 368 assert(args[0].length > 0); 369 } 370 body 371 { 372 _args = args; 373 _env = env; 374 if(_args.length > 0) 375 { 376 _program = _args[0]; 377 _args = _args[1..$]; 378 } 379 } 380 381 /** 382 * Indicate whether the process is running or not. 383 */ 384 public const bool isRunning() 385 { 386 return _running; 387 } 388 389 /** 390 * Return the running process' ID. 391 * 392 * Returns: an int with the process ID if the process is running; 393 * -1 if not. 394 */ 395 @property 396 public const int pid() 397 { 398 version (Windows) 399 { 400 return (_info !is null ? cast(int) _info.dwProcessId : -1); 401 } 402 else // version (Posix) 403 { 404 return cast(int) _pid; 405 } 406 } 407 408 /** 409 * Return the process' executable filename. 410 */ 411 @property 412 public const const(char)[] programName() 413 { 414 return _program; 415 } 416 417 /** 418 * Set the process' executable filename. 419 */ 420 @property 421 public const(char)[] programName(const(char)[] name) 422 { 423 return _program = name; 424 } 425 426 /** 427 * Set the process' executable filename, return 'this' for chaining 428 */ 429 public Process setProgramName(const(char)[] name) 430 { 431 programName = name; 432 return this; 433 } 434 435 /** 436 * Return an array with the process' arguments. This does not include the actual program name. 437 */ 438 public 439 const(char[])[] args() const 440 { 441 return _args; 442 } 443 444 /** 445 * Set the process' arguments from the arguments received by the method. 446 * 447 * Remarks: 448 * The first element of the array must be the name of the process' 449 * executable. 450 * 451 * Returns: the arguments that were set. This doesn't include the progname. 452 * 453 * Examples: 454 * --- 455 * p.args("myprogram", "first", "second argument", "third"); 456 * --- 457 */ 458 public const(char[])[] args(const(char)[] progname, const(char[])[] args ...) 459 { 460 _program = progname; 461 return _args = args.dup; 462 } 463 464 /** 465 * Set the process' arguments from the arguments received by the method. 466 * 467 * Remarks: 468 * The first element of the array must be the name of the process' 469 * executable. 470 * 471 * Returns: a reference to this for chaining 472 * 473 * Examples: 474 * --- 475 * p.setArgs("myprogram", "first", "second argument", "third").execute(); 476 * --- 477 */ 478 public Process setArgs(const(char)[] progname, const(char[])[] args ...) 479 { 480 this.args(progname, args); 481 return this; 482 } 483 484 /** 485 * If true, the environment from the current process will be copied to the 486 * child process. 487 */ 488 @property 489 public const bool copyEnv() 490 { 491 return _copyEnv; 492 } 493 494 /** 495 * Set the copyEnv flag. If set to true, then the environment will be 496 * copied from the current process. If set to false, then the environment 497 * is set from the env field. 498 */ 499 @property 500 public bool copyEnv(bool b) 501 { 502 return _copyEnv = b; 503 } 504 505 /** 506 * Set the copyEnv flag. If set to true, then the environment will be 507 * copied from the current process. If set to false, then the environment 508 * is set from the env field. 509 * 510 * Returns: 511 * A reference to this for chaining 512 */ 513 public Process setCopyEnv(bool b) 514 { 515 _copyEnv = b; 516 return this; 517 } 518 519 /** 520 * Return an associative array with the process' environment variables. 521 * 522 * Note that if copyEnv is set to true, this value is ignored. 523 */ 524 @property 525 public const(const(char[])[char[]]) env() const 526 { 527 return _env; 528 } 529 530 /** 531 * Set the process' environment variables from the associative array 532 * received by the method. 533 * 534 * This also clears the copyEnv flag. 535 * 536 * Params: 537 * env = associative array of strings containing the environment 538 * variables for the process. The variable name should be the key 539 * used for each entry. 540 * 541 * Returns: the env set. 542 * Examples: 543 * --- 544 * char[][char[]] env; 545 * 546 * env["MYVAR1"] = "first"; 547 * env["MYVAR2"] = "second"; 548 * 549 * p.env = env; 550 * --- 551 */ 552 @property 553 public const(char[])[char[]] env(const(char[])[char[]] env) 554 { 555 _copyEnv = false; 556 return _env = env; 557 } 558 559 /** 560 * Set the process' environment variables from the associative array 561 * received by the method. Returns a 'this' reference for chaining. 562 * 563 * This also clears the copyEnv flag. 564 * 565 * Params: 566 * env = associative array of strings containing the environment 567 * variables for the process. The variable name should be the key 568 * used for each entry. 569 * 570 * Returns: A reference to this process object 571 * Examples: 572 * --- 573 * char[][char[]] env; 574 * 575 * env["MYVAR1"] = "first"; 576 * env["MYVAR2"] = "second"; 577 * 578 * p.setEnv(env).execute(); 579 * --- 580 */ 581 public Process setEnv(const(char[])[char[]] env) 582 { 583 _copyEnv = false; 584 _env = env; 585 return this; 586 } 587 588 /** 589 * Return an UTF-8 string with the process' command line. 590 */ 591 public override 592 string toString() 593 { 594 immutable(char)[] command; 595 596 command ~= _program.substitute("\\", "\\\\").substitute(`"`, `\"`); 597 598 for (size_t i = 0; i < _args.length; ++i) 599 { 600 if (i > 0) 601 { 602 command ~= ' '; 603 } 604 if (contains(_args[i], ' ') || _args[i].length == 0) 605 { 606 command ~= '"'; 607 command ~= _args[i].substitute("\\", "\\\\").substitute(`"`, `\"`); 608 command ~= '"'; 609 } 610 else 611 { 612 command ~= _args[i].substitute("\\", "\\\\").substitute(`"`, `\"`); 613 } 614 } 615 return command; 616 } 617 618 /** 619 * Return the working directory for the process. 620 * 621 * Returns: a string with the working directory; null if the working 622 * directory is the current directory. 623 */ 624 @property 625 public const(char)[] workDir() const 626 { 627 return _workDir; 628 } 629 630 /** 631 * Set the working directory for the process. 632 * 633 * Params: 634 * dir = a string with the working directory; null if the working 635 * directory is the current directory. 636 * 637 * Returns: the directory set. 638 */ 639 @property 640 public const(char)[] workDir(const(char)[] dir) 641 { 642 return _workDir = dir; 643 } 644 645 /** 646 * Set the working directory for the process. Returns a 'this' reference 647 * for chaining 648 * 649 * Params: 650 * dir = a string with the working directory; null if the working 651 * directory is the current directory. 652 * 653 * Returns: a reference to this process. 654 */ 655 public Process setWorkDir(const(char)[] dir) 656 { 657 _workDir = dir; 658 return this; 659 } 660 661 /** 662 * Get the redirect flags for the process. 663 * 664 * The redirect flags are used to determine whether stdout, stderr, or 665 * stdin are redirected. The flags are an or'd combination of which 666 * standard handles to redirect. A redirected handle creates a pipe, 667 * whereas a non-redirected handle simply points to the same handle this 668 * process is pointing to. 669 * 670 * You can also redirect stdout or stderr to each other. The flags to 671 * redirect a handle to a pipe and to redirect it to another handle are 672 * mutually exclusive. In the case both are specified, the redirect to 673 * the other handle takes precedent. It is illegal to specify both 674 * redirection from stdout to stderr and from stderr to stdout. If both 675 * of these are specified, an exception is thrown. 676 * 677 * If redirected to a pipe, once the process is executed successfully, its 678 * input and output can be manipulated through the stdin, stdout and 679 * stderr member PipeConduit's. Note that if you redirect for example 680 * stderr to stdout, and you redirect stdout to a pipe, only stdout will 681 * be non-null. 682 */ 683 @property public const Redirect redirect() 684 { 685 return _redirect; 686 } 687 688 /** 689 * Set the redirect flags for the process. 690 */ 691 @property public Redirect redirect(Redirect flags) 692 { 693 return _redirect = flags; 694 } 695 696 /** 697 * Set the redirect flags for the process. Return a reference to this 698 * process for chaining. 699 */ 700 public Process setRedirect(Redirect flags) 701 { 702 _redirect = flags; 703 return this; 704 } 705 706 /** 707 * Get the GUI flag. 708 * 709 * This flag indicates on Windows systems that the CREATE_NO_WINDOW flag 710 * should be set on CreateProcess. Although this is a specific windows 711 * flag, it is present on posix systems as a noop for compatibility. 712 * 713 * Without this flag, a console window will be allocated if it doesn't 714 * already exist. 715 */ 716 @property 717 public bool gui() const 718 { 719 version(Windows) 720 return _gui; 721 else 722 return false; 723 } 724 725 /** 726 * Set the GUI flag. 727 * 728 * This flag indicates on Windows systems that the CREATE_NO_WINDOW flag 729 * should be set on CreateProcess. Although this is a specific windows 730 * flag, it is present on posix systems as a noop for compatibility. 731 * 732 * Without this flag, a console window will be allocated if it doesn't 733 * already exist. 734 */ 735 @property 736 public bool gui(bool value) 737 { 738 version(Windows) 739 return _gui = value; 740 else 741 return false; 742 } 743 744 /** 745 * Set the GUI flag. Returns a reference to this process for chaining. 746 * 747 * This flag indicates on Windows systems that the CREATE_NO_WINDOW flag 748 * should be set on CreateProcess. Although this is a specific windows 749 * flag, it is present on posix systems as a noop for compatibility. 750 * 751 * Without this flag, a console window will be allocated if it doesn't 752 * already exist. 753 */ 754 public Process setGui(bool value) 755 { 756 version(Windows) 757 { 758 _gui = value; 759 } 760 return this; 761 } 762 763 /** 764 * Return the running process' standard input pipe. 765 * 766 * Returns: a write-only PipeConduit connected to the child 767 * process' stdin. 768 * 769 * Remarks: 770 * The stream will be null if no child process has been executed, or the 771 * standard input stream was not redirected. 772 */ 773 @property public PipeConduit stdin() 774 { 775 return _stdin; 776 } 777 778 /** 779 * Return the running process' standard output pipe. 780 * 781 * Returns: a read-only PipeConduit connected to the child 782 * process' stdout. 783 * 784 * Remarks: 785 * The stream will be null if no child process has been executed, or the 786 * standard output stream was not redirected. 787 */ 788 @property public PipeConduit stdout() 789 { 790 return _stdout; 791 } 792 793 /** 794 * Return the running process' standard error pipe. 795 * 796 * Returns: a read-only PipeConduit connected to the child 797 * process' stderr. 798 * 799 * Remarks: 800 * The stream will be null if no child process has been executed, or the 801 * standard error stream was not redirected. 802 */ 803 @property public PipeConduit stderr() 804 { 805 return _stderr; 806 } 807 808 /** 809 * Execute a process using the arguments as parameters to this method. 810 * 811 * Once the process is executed successfully, its input and output can be 812 * manipulated through the stdin, stdout and 813 * stderr member PipeConduit's. 814 * 815 * Throws: 816 * ProcessCreateException if the process could not be created 817 * successfully; ProcessForkException if the call to the fork() 818 * system call failed (on POSIX-compatible platforms). 819 * 820 * Remarks: 821 * The process must not be running and the provided list of arguments must 822 * not be empty. If there was any argument already present in the args 823 * member, they will be replaced by the arguments supplied to the method. 824 * 825 * Deprecated: Use constructor or properties to set up process for 826 * execution. 827 */ 828 deprecated public void execute(const(char)[] arg1, const(char[])[] args ...) 829 in 830 { 831 assert(!_running); 832 } 833 body 834 { 835 this._program = arg1; 836 this._args = args; 837 execute(); 838 } 839 840 /** 841 * Execute a process using the command line arguments as parameters to 842 * this method. 843 * 844 * Once the process is executed successfully, its input and output can be 845 * manipulated through the stdin, stdout and 846 * stderr member PipeConduit's. 847 * 848 * This also clears the copyEnv flag 849 * 850 * Params: 851 * command = string with the process' command line; arguments that have 852 * embedded whitespace must be enclosed in inside double-quotes ("). 853 * env = associative array of strings with the process' environment 854 * variables; the variable name must be the key of each entry. 855 * 856 * Throws: 857 * ProcessCreateException if the process could not be created 858 * successfully; ProcessForkException if the call to the fork() 859 * system call failed (on POSIX-compatible platforms). 860 * 861 * Remarks: 862 * The process must not be running and the provided list of arguments must 863 * not be empty. If there was any argument already present in the args 864 * member, they will be replaced by the arguments supplied to the method. 865 * 866 * Deprecated: use properties or the constructor to set these parameters 867 * instead. 868 */ 869 deprecated public void execute(const(char)[] command, const(char[])[const(char)[]] env) 870 in 871 { 872 assert(!_running); 873 assert(command.length > 0); 874 } 875 body 876 { 877 _args = splitArgs(command); 878 if (_args.length > 0) 879 { 880 _program = _args[0]; 881 _args = _args[1..$]; 882 } 883 _copyEnv = false; 884 _env = env; 885 execute(); 886 } 887 888 /** 889 * Execute a process using the command line arguments as parameters to 890 * this method. 891 * 892 * Once the process is executed successfully, its input and output can be 893 * manipulated through the stdin, stdout and 894 * stderr member PipeConduit's. 895 * 896 * This also clears the copyEnv flag 897 * 898 * Params: 899 * args = array of strings with the process' arguments; the first 900 * argument must be the process' name; the arguments can be 901 * empty. 902 * env = associative array of strings with the process' environment 903 * variables; the variable name must be the key of each entry. 904 * 905 * Throws: 906 * ProcessCreateException if the process could not be created 907 * successfully; ProcessForkException if the call to the fork() 908 * system call failed (on POSIX-compatible platforms). 909 * 910 * Remarks: 911 * The process must not be running and the provided list of arguments must 912 * not be empty. If there was any argument already present in the args 913 * member, they will be replaced by the arguments supplied to the method. 914 * 915 * Deprecated: 916 * Use properties or the constructor to set these parameters instead. 917 * 918 * Examples: 919 * --- 920 * auto p = new Process(); 921 * char[][] args; 922 * 923 * args ~= "ls"; 924 * args ~= "-l"; 925 * 926 * p.execute(args, null); 927 * --- 928 */ 929 deprecated public void execute(const(char[])[] args, const(char[])[char[]] env) 930 in 931 { 932 assert(!_running); 933 assert(args.length > 0); 934 } 935 body 936 { 937 _args = args; 938 if (_args.length > 0) 939 { 940 _program = _args[0]; 941 _args = _args[1..$]; 942 } 943 _env = env; 944 _copyEnv = false; 945 946 execute(); 947 } 948 949 /** 950 * Execute a process using the arguments that were supplied to the 951 * constructor or to the args property. 952 * 953 * Once the process is executed successfully, its input and output can be 954 * manipulated through the stdin, stdout and 955 * stderr member PipeConduit's. 956 * 957 * Returns: 958 * A reference to this process object for chaining. 959 * 960 * Throws: 961 * ProcessCreateException if the process could not be created 962 * successfully; ProcessForkException if the call to the fork() 963 * system call failed (on POSIX-compatible platforms). 964 * 965 * Remarks: 966 * The process must not be running and the list of arguments must 967 * not be empty before calling this method. 968 */ 969 public Process execute() 970 in 971 { 972 assert(!_running); 973 assert(_program !is null); 974 } 975 body 976 { 977 version (Windows) 978 { 979 SECURITY_ATTRIBUTES sa; 980 STARTUPINFO startup; 981 982 // We close and delete the pipes that could have been left open 983 // from a previous execution. 984 cleanPipes(); 985 986 // Set up the security attributes struct. 987 sa.nLength = SECURITY_ATTRIBUTES.sizeof; 988 sa.lpSecurityDescriptor = null; 989 sa.bInheritHandle = true; 990 991 // Set up members of the STARTUPINFO structure. 992 memset(&startup, '\0', STARTUPINFO.sizeof); 993 startup.cb = STARTUPINFO.sizeof; 994 995 Pipe pin, pout, perr; 996 if(_redirect != Redirect.None) 997 { 998 if((_redirect & (Redirect.OutputToError | Redirect.ErrorToOutput)) == (Redirect.OutputToError | Redirect.ErrorToOutput)) 999 throw new ProcessCreateException(_program, "Illegal redirection flags", __FILE__, __LINE__); 1000 // 1001 // some redirection is specified, set the flag that indicates 1002 startup.dwFlags |= STARTF_USESTDHANDLES; 1003 1004 // Create the pipes used to communicate with the child process. 1005 if(_redirect & Redirect.Input) 1006 { 1007 pin = new Pipe(DefaultStdinBufferSize, &sa); 1008 // Replace stdin with the "read" pipe 1009 _stdin = pin.sink; 1010 startup.hStdInput = cast(HANDLE) pin.source.fileHandle(); 1011 // Ensure the write handle to the pipe for STDIN is not inherited. 1012 SetHandleInformation(cast(HANDLE) pin.sink.fileHandle(), HANDLE_FLAG_INHERIT, 0); 1013 } 1014 else 1015 { 1016 // need to get the local process stdin handle 1017 startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE); 1018 } 1019 1020 if((_redirect & (Redirect.Output | Redirect.OutputToError)) == Redirect.Output) 1021 { 1022 pout = new Pipe(DefaultStdoutBufferSize, &sa); 1023 // Replace stdout with the "write" pipe 1024 _stdout = pout.source; 1025 startup.hStdOutput = cast(HANDLE) pout.sink.fileHandle(); 1026 // Ensure the read handle to the pipe for STDOUT is not inherited. 1027 SetHandleInformation(cast(HANDLE) pout.source.fileHandle(), HANDLE_FLAG_INHERIT, 0); 1028 } 1029 else 1030 { 1031 // need to get the local process stdout handle 1032 startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); 1033 } 1034 1035 if((_redirect & (Redirect.Error | Redirect.ErrorToOutput)) == Redirect.Error) 1036 { 1037 perr = new Pipe(DefaultStderrBufferSize, &sa); 1038 // Replace stderr with the "write" pipe 1039 _stderr = perr.source; 1040 startup.hStdError = cast(HANDLE) perr.sink.fileHandle(); 1041 // Ensure the read handle to the pipe for STDOUT is not inherited. 1042 SetHandleInformation(cast(HANDLE) perr.source.fileHandle(), HANDLE_FLAG_INHERIT, 0); 1043 } 1044 else 1045 { 1046 // need to get the local process stderr handle 1047 startup.hStdError = GetStdHandle(STD_ERROR_HANDLE); 1048 } 1049 1050 // do redirection from one handle to another 1051 if(_redirect & Redirect.ErrorToOutput) 1052 { 1053 startup.hStdError = startup.hStdOutput; 1054 } 1055 1056 if(_redirect & Redirect.OutputToError) 1057 { 1058 startup.hStdOutput = startup.hStdError; 1059 } 1060 } 1061 1062 // close the unused end of the pipes on scope exit 1063 scope(exit) 1064 { 1065 if(pin !is null) 1066 pin.source.close(); 1067 if(pout !is null) 1068 pout.sink.close(); 1069 if(perr !is null) 1070 perr.sink.close(); 1071 } 1072 1073 _info = new PROCESS_INFORMATION; 1074 // Set up members of the PROCESS_INFORMATION structure. 1075 memset(_info, '\0', PROCESS_INFORMATION.sizeof); 1076 1077 /* 1078 * quotes and backslashes in the command line are handled very 1079 * strangely by Windows. Through trial and error, I believe that 1080 * these are the rules: 1081 * 1082 * inside or outside quote mode: 1083 * 1. if 2 or more backslashes are followed by a quote, the first 1084 * 2 backslashes are reduced to 1 backslash which does not 1085 * affect anything after it. 1086 * 2. one backslash followed by a quote is interpreted as a 1087 * literal quote, which cannot be used to close quote mode, and 1088 * does not affect anything after it. 1089 * 1090 * outside quote mode: 1091 * 3. a quote enters quote mode 1092 * 4. whitespace delineates an argument 1093 * 1094 * inside quote mode: 1095 * 5. 2 quotes sequentially are interpreted as a literal quote and 1096 * an exit from quote mode. 1097 * 6. a quote at the end of the string, or one that is followed by 1098 * anything other than a quote exits quote mode, but does not 1099 * affect the character after the quote. 1100 * 7. end of line exits quote mode 1101 * 1102 * In our 'reverse' routine, we will only utilize the first 2 rules 1103 * for escapes. 1104 */ 1105 char[] command; 1106 1107 void append_arg(const(char)[] arg) 1108 { 1109 const(char)[] nextarg = arg.substitute(`"`, `\"`); 1110 // 1111 // find all instances where \\" occurs, and double all the 1112 // backslashes. Otherwise, it will fall under rule 1, and those 1113 // backslashes will be halved. 1114 // 1115 uint pos = 0; 1116 while((pos = nextarg.locatePattern(`\\"`, pos)) < nextarg.length) 1117 { 1118 // 1119 // move back until we have all the backslashes 1120 // 1121 uint afterback = pos+1; 1122 while(pos > 0 && nextarg[pos - 1] == '\\') 1123 pos--; 1124 1125 // 1126 // double the number of backslashes that do not escape the 1127 // quote 1128 // 1129 nextarg = nextarg[0..afterback] ~ nextarg[pos..$]; 1130 pos = afterback + afterback - pos + 2; 1131 } 1132 1133 // 1134 // check to see if we need to surround the arg with quotes. 1135 // 1136 if(nextarg.length == 0) 1137 { 1138 nextarg = `""`; 1139 } 1140 else if(nextarg.contains(' ')) 1141 { 1142 // 1143 // surround with quotes, but if the arg ends in backslashes, 1144 // we must double all the backslashes, or they will fall under 1145 // rule 1 and be halved. 1146 // 1147 1148 if(nextarg[$-1] == '\\') 1149 { 1150 // 1151 // ends in a backslash. count all the \'s at the end of the 1152 // string, and repeat them 1153 // 1154 pos = nextarg.length - 1; 1155 while(pos > 0 && nextarg[pos-1] == '\\') 1156 pos--; 1157 nextarg ~= nextarg[pos..$]; 1158 } 1159 1160 // surround the argument with quotes 1161 nextarg = '"' ~ nextarg ~ '"'; 1162 } 1163 1164 command ~= ' '; 1165 command ~= nextarg; 1166 } 1167 1168 append_arg(_program); 1169 1170 foreach(a; _args) 1171 append_arg(a); 1172 1173 command ~= '\0'; 1174 command = command[1..$]; 1175 1176 // old way 1177 //char[] command = toString(); 1178 //command ~= '\0'; 1179 1180 version(Win32SansUnicode) 1181 { 1182 // 1183 // ASCII version of CreateProcess 1184 // 1185 1186 // Convert the working directory to a null-ended string if 1187 // necessary. 1188 // 1189 // Note, this used to contain DETACHED_PROCESS, but 1190 // this causes problems with redirection if the program being 1191 // started decides to allocate a console (i.e. if you run a batch 1192 // file) 1193 if (CreateProcessA(null, command.ptr, null, null, true, 1194 _gui ? CREATE_NO_WINDOW : 0, 1195 (_copyEnv ? null : toNullEndedBuffer(_env).ptr), 1196 toStringz(_workDir), &startup, _info)) 1197 { 1198 CloseHandle(_info.hThread); 1199 _running = true; 1200 } 1201 else 1202 { 1203 throw new ProcessCreateException(_program, __FILE__, __LINE__); 1204 } 1205 } 1206 else 1207 { 1208 // Convert the working directory to a null-ended string if 1209 // necessary. 1210 // 1211 // Note, this used to contain DETACHED_PROCESS, but 1212 // this causes problems with redirection if the program being 1213 // started decides to allocate a console (i.e. if you run a batch 1214 // file) 1215 if (CreateProcessW(null, toString16(command).ptr, null, null, true, 1216 _gui ? CREATE_NO_WINDOW : 0, 1217 (_copyEnv ? null : toNullEndedBuffer(_env).ptr), 1218 toString16z(toString16(_workDir)), &startup, _info)) 1219 { 1220 CloseHandle(_info.hThread); 1221 _running = true; 1222 } 1223 else 1224 { 1225 throw new ProcessCreateException(_program, __FILE__, __LINE__); 1226 } 1227 } 1228 } 1229 else version (Posix) 1230 { 1231 // We close and delete the pipes that could have been left open 1232 // from a previous execution. 1233 cleanPipes(); 1234 1235 // validate the redirection flags 1236 if((_redirect & (Redirect.OutputToError | Redirect.ErrorToOutput)) == (Redirect.OutputToError | Redirect.ErrorToOutput)) 1237 throw new ProcessCreateException(_program, "Illegal redirection flags", __FILE__, __LINE__); 1238 1239 1240 Pipe pin, pout, perr; 1241 if(_redirect & Redirect.Input) 1242 pin = new Pipe(DefaultStdinBufferSize); 1243 if((_redirect & (Redirect.Output | Redirect.OutputToError)) == Redirect.Output) 1244 pout = new Pipe(DefaultStdoutBufferSize); 1245 1246 if((_redirect & (Redirect.Error | Redirect.ErrorToOutput)) == Redirect.Error) 1247 perr = new Pipe(DefaultStderrBufferSize); 1248 1249 // This pipe is used to propagate the result of the call to 1250 // execv*() from the child process to the parent process. 1251 Pipe pexec = new Pipe(8); 1252 int status = 0; 1253 1254 _pid = fork(); 1255 if (_pid >= 0) 1256 { 1257 if (_pid != 0) 1258 { 1259 // Parent process 1260 if(pin !is null) 1261 { 1262 _stdin = pin.sink; 1263 pin.source.close(); 1264 } 1265 1266 if(pout !is null) 1267 { 1268 _stdout = pout.source; 1269 pout.sink.close(); 1270 } 1271 1272 if(perr !is null) 1273 { 1274 _stderr = perr.source; 1275 perr.sink.close(); 1276 } 1277 1278 pexec.sink.close(); 1279 scope(exit) 1280 pexec.source.close(); 1281 1282 try 1283 { 1284 pexec.source.input.read((cast(byte*) &status)[0 .. status.sizeof]); 1285 } 1286 catch (Exception e) 1287 { 1288 // Everything's OK, the pipe was closed after the call to execv*() 1289 } 1290 1291 if (status == 0) 1292 { 1293 _running = true; 1294 } 1295 else 1296 { 1297 // We set errno to the value that was sent through 1298 // the pipe from the child process 1299 errno = status; 1300 _running = false; 1301 1302 throw new ProcessCreateException(_program, __FILE__, __LINE__); 1303 } 1304 } 1305 else 1306 { 1307 // Child process 1308 int rc; 1309 const(char)*[] argptr; 1310 const(char)*[] envptr; 1311 1312 // Note that for all the pipes, we can close both ends 1313 // because dup2 opens a duplicate file descriptor to the 1314 // same resource. 1315 1316 // Replace stdin with the "read" pipe 1317 if(pin !is null) 1318 { 1319 if (dup2(pin.source.fileHandle(), STDIN_FILENO) < 0) 1320 throw new Exception("dup2 < 0"); 1321 pin.sink().close(); 1322 pin.source.close(); 1323 } 1324 1325 // Replace stdout with the "write" pipe 1326 if(pout !is null) 1327 { 1328 if (dup2(pout.sink.fileHandle(), STDOUT_FILENO) < 0) 1329 throw new Exception("dup2 < 0"); 1330 pout.source.close(); 1331 pout.sink.close(); 1332 } 1333 1334 // Replace stderr with the "write" pipe 1335 if(perr !is null) 1336 { 1337 if (dup2(perr.sink.fileHandle(), STDERR_FILENO) < 0) 1338 throw new Exception("dup2 < 0"); 1339 perr.source.close(); 1340 perr.sink.close(); 1341 } 1342 1343 // Check for redirection from stdout to stderr or vice 1344 // versa 1345 if(_redirect & Redirect.OutputToError) 1346 { 1347 if(dup2(STDERR_FILENO, STDOUT_FILENO) < 0) 1348 throw new Exception("dup2 < 0"); 1349 } 1350 1351 if(_redirect & Redirect.ErrorToOutput) 1352 { 1353 if(dup2(STDOUT_FILENO, STDERR_FILENO) < 0) 1354 throw new Exception("dup2 < 0"); 1355 } 1356 1357 // We close the unneeded part of the execv*() notification pipe 1358 pexec.source.close(); 1359 1360 // Set the "write" pipe so that it closes upon a successful 1361 // call to execv*() 1362 if (fcntl(cast(int) pexec.sink.fileHandle(), F_SETFD, FD_CLOEXEC) == 0) 1363 { 1364 // Convert the arguments and the environment variables to 1365 // the format expected by the execv() family of functions. 1366 argptr = toNullEndedArray(_program, _args); 1367 envptr = (_copyEnv ? null : toNullEndedArray(_env)); 1368 1369 // Switch to the working directory if it has been set. 1370 if (_workDir.length > 0) 1371 { 1372 chdir(toStringz(_workDir)); 1373 } 1374 1375 // Replace the child fork with a new process. We always use the 1376 // system PATH to look for executables that don't specify 1377 // directories in their names. 1378 rc = execvpe(_program, argptr, envptr); 1379 if (rc == -1) 1380 { 1381 Cerr("Failed to exec ")(_program)(": ")(SysError.lastMsg).newline; 1382 1383 try 1384 { 1385 status = errno; 1386 1387 // Propagate the child process' errno value to 1388 // the parent process. 1389 pexec.sink.output.write((cast(byte*) &status)[0 .. status.sizeof]); 1390 } 1391 catch (Exception e) 1392 { 1393 } 1394 exit(errno); 1395 } 1396 exit(errno); 1397 } 1398 else 1399 { 1400 Cerr("Failed to set notification pipe to close-on-exec for ") 1401 (_program)(": ")(SysError.lastMsg).newline; 1402 exit(errno); 1403 } 1404 } 1405 } 1406 else 1407 { 1408 throw new ProcessForkException(_pid, __FILE__, __LINE__); 1409 } 1410 } 1411 else 1412 { 1413 assert(false, "tango.sys.Process: Unsupported platform"); 1414 } 1415 return this; 1416 } 1417 1418 1419 /** 1420 * Unconditionally wait for a process to end and return the reason and 1421 * status code why the process ended. 1422 * 1423 * Returns: 1424 * The return value is a Result struct, which has two members: 1425 * reason and status. The reason can take the 1426 * following values: 1427 * 1428 * Process.Result.Exit: the child process exited normally; 1429 * status has the process' return 1430 * code. 1431 * 1432 * Process.Result.Signal: the child process was killed by a signal; 1433 * status has the signal number 1434 * that killed the process. 1435 * 1436 * Process.Result.Stop: the process was stopped; status 1437 * has the signal number that was used to stop 1438 * the process. 1439 * 1440 * Process.Result.Continue: the process had been previously stopped 1441 * and has now been restarted; 1442 * status has the signal number 1443 * that was used to continue the process. 1444 * 1445 * Process.Result.Error: We could not properly wait on the child 1446 * process; status has the 1447 * errno value if the process was 1448 * running and -1 if not. 1449 * 1450 * Remarks: 1451 * You can only call wait() on a running process once. The Signal, Stop 1452 * and Continue reasons will only be returned on POSIX-compatible 1453 * platforms. 1454 * Calling wait() will not clean the pipes as the parent process may still 1455 * want the remaining output. It is however recommended to call close() 1456 * when no more content is expected, as this will close the pipes. 1457 */ 1458 public Result wait() 1459 { 1460 version (Windows) 1461 { 1462 Result result; 1463 1464 if (_running) 1465 { 1466 DWORD rc; 1467 DWORD exitCode; 1468 1469 assert(_info !is null); 1470 1471 // We clean up the process related data and set the _running 1472 // flag to false once we're done waiting for the process to 1473 // finish. 1474 // 1475 // IMPORTANT: we don't delete the open pipes so that the parent 1476 // process can get whatever the child process left on 1477 // these pipes before dying. 1478 scope(exit) 1479 { 1480 CloseHandle(_info.hProcess); 1481 _running = false; 1482 } 1483 1484 rc = WaitForSingleObject(_info.hProcess, INFINITE); 1485 if (rc == WAIT_OBJECT_0) 1486 { 1487 GetExitCodeProcess(_info.hProcess, &exitCode); 1488 1489 result.reason = Result.Exit; 1490 result.status = cast(typeof(result.status)) exitCode; 1491 1492 debug (Process) 1493 Stdout.formatln("Child process '{0}' ({1}) returned with code {2}\n", 1494 _program, pid, result.status); 1495 } 1496 else if (rc == WAIT_FAILED) 1497 { 1498 result.reason = Result.Error; 1499 result.status = cast(short) GetLastError(); 1500 1501 debug (Process) 1502 Stdout.formatln("Child process '{0}' ({1}) failed " ~ 1503 "with unknown exit status {2}\n", 1504 _program, pid, result.status); 1505 } 1506 } 1507 else 1508 { 1509 result.reason = Result.Error; 1510 result.status = -1; 1511 1512 debug (Process) 1513 Stdout.formatln("Child process '{0}' is not running", _program); 1514 } 1515 return result; 1516 } 1517 else version (Posix) 1518 { 1519 Result result; 1520 1521 if (_running) 1522 { 1523 int rc; 1524 1525 // We clean up the process related data and set the _running 1526 // flag to false once we're done waiting for the process to 1527 // finish. 1528 // 1529 // IMPORTANT: we don't delete the open pipes so that the parent 1530 // process can get whatever the child process left on 1531 // these pipes before dying. 1532 scope(exit) 1533 { 1534 _running = false; 1535 } 1536 1537 // Wait for child process to end. 1538 if (waitpid(_pid, &rc, 0) != -1) 1539 { 1540 if (WIFEXITED(rc)) 1541 { 1542 result.reason = Result.Exit; 1543 result.status = WEXITSTATUS(rc); 1544 if (result.status != 0) 1545 { 1546 debug (Process) 1547 Stdout.formatln("Child process '{0}' ({1}) returned with code {2}\n", 1548 _program, _pid, result.status); 1549 } 1550 } 1551 else 1552 { 1553 if (WIFSIGNALED(rc)) 1554 { 1555 result.reason = Result.Signal; 1556 result.status = WTERMSIG(rc); 1557 1558 debug (Process) 1559 Stdout.formatln("Child process '{0}' ({1}) was killed prematurely " ~ 1560 "with signal {2}", 1561 _program, _pid, result.status); 1562 } 1563 else if (WIFSTOPPED(rc)) 1564 { 1565 result.reason = Result.Stop; 1566 result.status = WSTOPSIG(rc); 1567 1568 debug (Process) 1569 Stdout.formatln("Child process '{0}' ({1}) was stopped " ~ 1570 "with signal {2}", 1571 _program, _pid, result.status); 1572 } 1573 else if (WIFCONTINUED(rc)) 1574 { 1575 result.reason = Result.Stop; 1576 result.status = WSTOPSIG(rc); 1577 1578 debug (Process) 1579 Stdout.formatln("Child process '{0}' ({1}) was continued " ~ 1580 "with signal {2}", 1581 _program, _pid, result.status); 1582 } 1583 else 1584 { 1585 result.reason = Result.Error; 1586 result.status = rc; 1587 1588 debug (Process) 1589 Stdout.formatln("Child process '{0}' ({1}) failed " ~ 1590 "with unknown exit status {2}\n", 1591 _program, _pid, result.status); 1592 } 1593 } 1594 } 1595 else 1596 { 1597 result.reason = Result.Error; 1598 result.status = errno; 1599 1600 debug (Process) 1601 Stdout.formatln("Could not wait on child process '{0}' ({1}): ({2}) {3}", 1602 _program, _pid, result.status, SysError.lastMsg); 1603 } 1604 } 1605 else 1606 { 1607 result.reason = Result.Error; 1608 result.status = -1; 1609 1610 debug (Process) 1611 Stdout.formatln("Child process '{0}' is not running", _program); 1612 } 1613 return result; 1614 } 1615 else 1616 { 1617 assert(false, "tango.sys.Process: Unsupported platform"); 1618 } 1619 } 1620 1621 /** 1622 * Kill a running process. This method will not return until the process 1623 * has been killed. 1624 * 1625 * Throws: 1626 * ProcessKillException if the process could not be killed; 1627 * ProcessWaitException if we could not wait on the process after 1628 * killing it. 1629 * 1630 * Remarks: 1631 * After calling this method you will not be able to call wait() on the 1632 * process. 1633 * Killing the process does not clean the attached pipes as the parent 1634 * process may still want/need the remaining content. However, it is 1635 * recommended to call close() on the process when it is no longer needed 1636 * as this will clean the pipes. 1637 */ 1638 public void kill() 1639 { 1640 version (Windows) 1641 { 1642 if (_running) 1643 { 1644 assert(_info !is null); 1645 1646 if (TerminateProcess(_info.hProcess, cast(UINT) -1)) 1647 { 1648 assert(_info !is null); 1649 1650 // We clean up the process related data and set the _running 1651 // flag to false once we're done waiting for the process to 1652 // finish. 1653 // 1654 // IMPORTANT: we don't delete the open pipes so that the parent 1655 // process can get whatever the child process left on 1656 // these pipes before dying. 1657 scope(exit) 1658 { 1659 CloseHandle(_info.hProcess); 1660 _running = false; 1661 } 1662 1663 // FIXME: We should probably use a timeout here 1664 if (WaitForSingleObject(_info.hProcess, INFINITE) == WAIT_FAILED) 1665 { 1666 throw new ProcessWaitException(cast(int) _info.dwProcessId, 1667 __FILE__, __LINE__); 1668 } 1669 } 1670 else 1671 { 1672 throw new ProcessKillException(cast(int) _info.dwProcessId, 1673 __FILE__, __LINE__); 1674 } 1675 } 1676 else 1677 { 1678 debug (Process) 1679 Stdout.print("Tried to kill an invalid process"); 1680 } 1681 } 1682 else version (Posix) 1683 { 1684 if (_running) 1685 { 1686 int rc; 1687 1688 assert(_pid > 0); 1689 1690 if (.kill(_pid, SIGTERM) != -1) 1691 { 1692 // We clean up the process related data and set the _running 1693 // flag to false once we're done waiting for the process to 1694 // finish. 1695 // 1696 // IMPORTANT: we don't delete the open pipes so that the parent 1697 // process can get whatever the child process left on 1698 // these pipes before dying. 1699 scope(exit) 1700 { 1701 _running = false; 1702 } 1703 1704 // FIXME: is this loop really needed? 1705 for (uint i = 0; i < 100; i++) 1706 { 1707 rc = waitpid(pid, null, WNOHANG | WUNTRACED); 1708 if (rc == _pid) 1709 { 1710 break; 1711 } 1712 else if (rc == -1) 1713 { 1714 throw new ProcessWaitException(cast(int) _pid, __FILE__, __LINE__); 1715 } 1716 usleep(50000); 1717 } 1718 } 1719 else 1720 { 1721 throw new ProcessKillException(_pid, __FILE__, __LINE__); 1722 } 1723 } 1724 else 1725 { 1726 debug (Process) 1727 Stdout.print("Tried to kill an invalid process"); 1728 } 1729 } 1730 else 1731 { 1732 assert(false, "tango.sys.Process: Unsupported platform"); 1733 } 1734 } 1735 1736 /** 1737 * Split a string containing the command line used to invoke a program 1738 * and return and array with the parsed arguments. The double-quotes (") 1739 * character can be used to specify arguments with embedded spaces. 1740 * e.g. first "second param" third 1741 */ 1742 protected static const(char)[][] splitArgs(const(char)[] command, const(char)[] delims = " \t\r\n") 1743 in 1744 { 1745 assert(!contains(delims, '"'), 1746 "The argument delimiter string cannot contain a double quotes ('\"') character"); 1747 } 1748 body 1749 { 1750 enum State 1751 { 1752 Start, 1753 FindDelimiter, 1754 InsideQuotes 1755 } 1756 1757 const(char)[][] args = null; 1758 const(char)[][] chunks = null; 1759 int start = -1; 1760 char c; 1761 int i; 1762 State state = State.Start; 1763 1764 // Append an argument to the 'args' array using the 'chunks' array 1765 // and the current position in the 'command' string as the source. 1766 void appendChunksAsArg() 1767 { 1768 size_t argPos; 1769 1770 if (chunks.length > 0) 1771 { 1772 // Create the array element corresponding to the argument by 1773 // appending the first chunk. 1774 args ~= chunks[0]; 1775 argPos = args.length - 1; 1776 1777 for (uint chunkPos = 1; chunkPos < chunks.length; ++chunkPos) 1778 { 1779 args[argPos] ~= chunks[chunkPos]; 1780 } 1781 1782 if (start != -1) 1783 { 1784 args[argPos] ~= command[start .. i]; 1785 } 1786 chunks.length = 0; 1787 } 1788 else 1789 { 1790 if (start != -1) 1791 { 1792 args ~= command[start .. i]; 1793 } 1794 } 1795 start = -1; 1796 } 1797 1798 for (i = 0; i < command.length; i++) 1799 { 1800 c = command[i]; 1801 1802 switch (state) 1803 { 1804 // Start looking for an argument. 1805 case State.Start: 1806 if (c == '"') 1807 { 1808 state = State.InsideQuotes; 1809 } 1810 else if (!contains(delims, c)) 1811 { 1812 start = i; 1813 state = State.FindDelimiter; 1814 } 1815 else 1816 { 1817 appendChunksAsArg(); 1818 } 1819 break; 1820 1821 // Find the ending delimiter for an argument. 1822 case State.FindDelimiter: 1823 if (c == '"') 1824 { 1825 // If we find a quotes character this means that we've 1826 // found a quoted section of an argument. (e.g. 1827 // abc"def"ghi). The quoted section will be appended 1828 // to the preceding part of the argument. This is also 1829 // what Unix shells do (i.e. a"b"c becomes abc). 1830 if (start != -1) 1831 { 1832 chunks ~= command[start .. i]; 1833 start = -1; 1834 } 1835 state = State.InsideQuotes; 1836 } 1837 else if (contains(delims, c)) 1838 { 1839 appendChunksAsArg(); 1840 state = State.Start; 1841 } 1842 break; 1843 1844 // Inside a quoted argument or section of an argument. 1845 case State.InsideQuotes: 1846 if (start == -1) 1847 { 1848 start = i; 1849 } 1850 1851 if (c == '"') 1852 { 1853 chunks ~= command[start .. i]; 1854 start = -1; 1855 state = State.Start; 1856 } 1857 break; 1858 1859 default: 1860 assert(false, "Invalid state in Process.splitArgs"); 1861 } 1862 } 1863 1864 // Add the last argument (if there is one) 1865 appendChunksAsArg(); 1866 1867 return args; 1868 } 1869 1870 /** 1871 * Close and delete any pipe that may have been left open in a previous 1872 * execution of a child process. 1873 */ 1874 protected void cleanPipes() 1875 { 1876 _stdin.destroy; 1877 _stdout.destroy; 1878 _stderr.destroy; 1879 } 1880 1881 /** 1882 * Explicitly close any resources held by this process object. It is recommended 1883 * to always call this when you are done with the process. 1884 */ 1885 public void close() 1886 { 1887 this.cleanPipes(); 1888 } 1889 1890 version (Windows) 1891 { 1892 /** 1893 * Convert an associative array of strings to a buffer containing a 1894 * concatenation of "<name>=<value>" strings separated by a null 1895 * character and with an additional null character at the end of it. 1896 * This is the format expected by the CreateProcess() Windows API for 1897 * the environment variables. 1898 */ 1899 protected static char[] toNullEndedBuffer(const(char[])[char[]] src) 1900 { 1901 char[] dest; 1902 1903 foreach (key, value; src) 1904 { 1905 dest ~= key ~ '=' ~ value ~ '\0'; 1906 } 1907 1908 dest ~= "\0\0"; 1909 return dest; 1910 } 1911 } 1912 else version (Posix) 1913 { 1914 /** 1915 * Convert an array of strings to an array of pointers to char with 1916 * a terminating null character (C strings). The resulting array 1917 * has a null pointer at the end. This is the format expected by 1918 * the execv*() family of POSIX functions. 1919 */ 1920 protected static const(char)*[] toNullEndedArray(const(char)[] elem0, const(char[])[] src) 1921 { 1922 if (src !is null && elem0 !is null) 1923 { 1924 const(char)*[] dest = new const(char)*[src.length + 2]; 1925 auto i = src.length; 1926 1927 // Add terminating null pointer to the array 1928 dest[i + 1] = null; 1929 1930 while (i > 0) 1931 { 1932 --i; 1933 // Add a terminating null character to each string 1934 dest[i + 1] = toStringz(src[i]); 1935 } 1936 1937 dest[0] = toStringz(elem0); 1938 return dest; 1939 } 1940 else 1941 { 1942 return null; 1943 } 1944 } 1945 1946 /** 1947 * Convert an associative array of strings to an array of pointers to 1948 * char with a terminating null character (C strings). The resulting 1949 * array has a null pointer at the end. This is the format expected by 1950 * the execv*() family of POSIX functions for environment variables. 1951 */ 1952 protected static const(char)*[] toNullEndedArray(const(char[])[char[]] src) 1953 { 1954 const(char)*[] dest; 1955 1956 foreach (key, value; src) 1957 { 1958 dest ~= (key ~ '=' ~ value ~ '\0').ptr; 1959 } 1960 1961 dest ~= null; 1962 return dest; 1963 } 1964 1965 /** 1966 * Execute a process by looking up a file in the system path, passing 1967 * the array of arguments and the the environment variables. This 1968 * method is a combination of the execve() and execvp() POSIX system 1969 * calls. 1970 */ 1971 protected static int execvpe(const(char)[] filename, const(char)*[] argv, const(char)*[] envp) 1972 in 1973 { 1974 assert(filename.length > 0); 1975 } 1976 body 1977 { 1978 int rc = -1; 1979 char* str; 1980 1981 if (!contains(filename, FileConst.PathSeparatorChar) && 1982 (str = getenv("PATH")) !is null) 1983 { 1984 auto pathList = delimit(str[0 .. strlen(str)], ":"); 1985 1986 char[] path_buf; 1987 1988 foreach (path; pathList) 1989 { 1990 if (path[$-1] != FileConst.PathSeparatorChar) 1991 { 1992 path_buf.length = path.length + 1 + filename.length + 1; 1993 path_buf[] = (path ~ FileConst.PathSeparatorChar ~ filename ~ '\0')[]; 1994 } 1995 else 1996 { 1997 path_buf.length = path.length +filename.length + 1; 1998 path_buf[] = (path ~ filename ~ '\0')[]; 1999 } 2000 2001 rc = execve(path_buf.ptr, argv.ptr, (envp.length == 0 ? cast(const(char)**)environ : envp.ptr)); 2002 2003 // If the process execution failed because of an error 2004 // other than ENOENT (No such file or directory) we 2005 // abort the loop. 2006 if (rc == -1 && SysError.lastCode !is ENOENT) 2007 { 2008 break; 2009 } 2010 } 2011 } 2012 else 2013 { 2014 debug (Process) 2015 Stdout.formatln("Calling execve('{0}', argv[{1}], {2})", 2016 (argv[0])[0 .. strlen(argv[0])], 2017 argv.length, (envp.length > 0 ? "envp" : "null")); 2018 2019 rc = execve(argv[0], argv.ptr, (envp.length == 0 ? cast(const(char)**)environ : envp.ptr)); 2020 } 2021 return rc; 2022 } 2023 } 2024 } 2025 2026 2027 /** 2028 * Exception thrown when the process cannot be created. 2029 */ 2030 class ProcessCreateException: ProcessException 2031 { 2032 public this(const(char)[] command, const(char)[] file, uint line) 2033 { 2034 this(command, SysError.lastMsg, file, line); 2035 } 2036 2037 public this(const(char)[] command, const(char)[] message, const(char)[] file, uint line) 2038 { 2039 super("Could not create process for " ~ command.idup ~ " : " ~ message.idup); 2040 } 2041 } 2042 2043 /** 2044 * Exception thrown when the parent process cannot be forked. 2045 * 2046 * This exception will only be thrown on POSIX-compatible platforms. 2047 */ 2048 class ProcessForkException: ProcessException 2049 { 2050 public this(int pid, const(char)[] file, uint line) 2051 { 2052 super(format("Could not fork process ", pid).idup ~ " : " ~ SysError.lastMsg.idup); 2053 } 2054 } 2055 2056 /** 2057 * Exception thrown when the process cannot be killed. 2058 */ 2059 class ProcessKillException: ProcessException 2060 { 2061 public this(int pid, const(char)[] file, uint line) 2062 { 2063 super(format("Could not kill process ", pid).idup ~ " : " ~ SysError.lastMsg.idup); 2064 } 2065 } 2066 2067 /** 2068 * Exception thrown when the parent process tries to wait on the child 2069 * process and fails. 2070 */ 2071 class ProcessWaitException: ProcessException 2072 { 2073 public this(int pid, const(char)[] file, uint line) 2074 { 2075 super(format("Could not wait on process ", pid).idup ~ " : " ~ SysError.lastMsg.idup); 2076 } 2077 } 2078 2079 2080 2081 2082 /** 2083 * append an int argument to a message 2084 */ 2085 private char[] format (const(char)[] msg, int value) 2086 { 2087 char[10] tmp; 2088 2089 return msg ~ Integer.format (tmp, value); 2090 } 2091 2092 extern (C) uint sleep (uint s); 2093 debug (UnitTest) 2094 { 2095 import tango.io.Stdout; 2096 import tango.stdc.stdio : printf, fflush, stdout; 2097 2098 unittest 2099 { 2100 const(char)[] message = "hello world"; 2101 version(Windows) 2102 { 2103 const(char)[] command = "cmd.exe /c echo " ~ message; 2104 } 2105 else 2106 const(char)[] command = "echo " ~ message; 2107 2108 2109 try 2110 { 2111 auto p = new Process(command, null); 2112 Stdout.flush(); 2113 p.execute(); 2114 char[255] buffer; 2115 2116 auto nread = p.stdout.read(buffer); 2117 2118 assert(nread != p.stdout.Eof); 2119 2120 version(Windows) 2121 assert(buffer[0..nread] == message ~ "\r\n"); 2122 else 2123 assert(buffer[0..nread] == message ~ "\n"); 2124 2125 nread = p.stdout.read(buffer); 2126 assert(nread == p.stdout.Eof); 2127 2128 auto result = p.wait(); 2129 2130 assert(result.reason == Process.Result.Exit && result.status == 0); 2131 } 2132 catch (ProcessException e) 2133 { 2134 Cerr("Program execution failed: ")(e.toString()).newline(); 2135 } 2136 } 2137 } 2138