1 /******************************************************************************* 2 3 Copyright: 4 Copyright (c) 2006 Juan Jose Comellas. 5 Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH. 6 All rights reserved. 7 8 License: 9 Tango Dual License: 3-Clause BSD License / Academic Free License v3.0. 10 See LICENSE_TANGO.txt for details. 11 12 Authors: Juan Jose Comellas <juanjo@comellas.com.ar> 13 14 *******************************************************************************/ 15 16 module ocean.sys.Process; 17 18 import ocean.meta.types.Qualifiers; 19 import ocean.core.Verify; 20 import ocean.core.Array : copy; 21 import ocean.io.model.IFile; 22 import ocean.io.Console; 23 import ocean.sys.Common; 24 import ocean.sys.Pipe; 25 import ocean.core.ExceptionDefinitions; 26 import ocean.text.convert.Formatter; 27 import ocean.text.Util; 28 29 version (unittest) import ocean.core.Test; 30 31 import core.stdc.stdlib; 32 import core.stdc..string; 33 34 import core.stdc.errno; 35 import ocean.stdc.posix.fcntl; 36 import core.sys.posix.unistd; 37 import core.sys.posix.sys.wait; 38 39 private 40 { 41 __gshared extern (C) extern char** environ; 42 } 43 44 debug (Process) 45 { 46 import ocean.io.Stdout; 47 } 48 49 50 /** 51 * Redirect flags for processes. Defined outside process class to cut down on 52 * verbosity. 53 */ 54 enum Redirect 55 { 56 /** 57 * Redirect none of the standard handles 58 */ 59 None = 0, 60 61 /** 62 * Redirect the stdout handle to a pipe. 63 */ 64 Output = 1, 65 66 /** 67 * Redirect the stderr handle to a pipe. 68 */ 69 Error = 2, 70 71 /** 72 * Redirect the stdin handle to a pipe. 73 */ 74 Input = 4, 75 76 /** 77 * Redirect all three handles to pipes (default). 78 */ 79 All = Output | Error | Input, 80 81 /** 82 * Send stderr to stdout's handle. Note that the stderr PipeConduit will 83 * be null. 84 */ 85 ErrorToOutput = 0x10, 86 87 /** 88 * Send stdout to stderr's handle. Note that the stdout PipeConduit will 89 * be null. 90 */ 91 OutputToError = 0x20, 92 } 93 94 /** 95 * The Process class is used to start external programs and communicate with 96 * them via their standard input, output and error streams. 97 * 98 * You can pass either the command line or an array of arguments to execute, 99 * either in the constructor or to the args property. The environment 100 * variables can be set in a similar way using the env property and you can 101 * set the program's working directory via the workDir property. 102 * 103 * To actually start a process you need to use the execute() method. Once the 104 * program is running you will be able to write to its standard input via the 105 * stdin OutputStream and you will be able to read from its standard output and 106 * error through the stdout and stderr InputStream respectively. 107 * 108 * You can check whether the process is running or not with the isRunning() 109 * method and you can get its process ID via the pid property. 110 * 111 * After you are done with the process, or if you just want to wait for it to 112 * end, you need to call the wait() method which will return once the process 113 * is no longer running. 114 * 115 * To stop a running process you must use kill() method. If you do this you 116 * cannot call the wait() method. Once the kill() method returns the process 117 * will be already dead. 118 * 119 * After calling either wait() or kill(), and no more data is expected on the 120 * pipes, you should call close() as this will clean the pipes. Not doing this 121 * may lead to a depletion of the available file descriptors for the main 122 * process if many processes are created. 123 * 124 * Examples: 125 * --- 126 * try 127 * { 128 * auto p = new Process ("ls -al", null); 129 * p.execute; 130 * 131 * Stdout.formatln ("Output from {}:", p.programName); 132 * Stdout.copy (p.stdout).flush; 133 * auto result = p.wait; 134 * 135 * Stdout.formatln ("Process '{}' ({}) exited with reason {}, status {}", 136 * p.programName, p.pid, cast(int) result.reason, result.status); 137 * } 138 * catch (ProcessException e) 139 * Stdout.formatln ("Process execution failed: {}", e); 140 * --- 141 * 142 * --- 143 * // Example how to pipe two processes together: 144 * auto p1 = new Process("ls"); 145 * auto p2 = new Process("head"); 146 * 147 * p1.execute(); 148 * p2.execute(); 149 * 150 * p2.stdin.copy(p1.stdout); 151 * 152 * p2.wait(); 153 * Stdout.copy(p2.stdout); 154 * 155 * --- 156 */ 157 class Process 158 { 159 /** 160 * Result returned by wait(). 161 */ 162 public struct Result 163 { 164 /** 165 * Reasons returned by wait() indicating why the process is no 166 * longer running. 167 */ 168 public enum 169 { 170 Exit, 171 Signal, 172 Stop, 173 Continue, 174 Error 175 } 176 177 public int reason; 178 public int status; 179 180 /// Formatter-compatible string formatting function 181 public void toString (scope FormatterSink sink) 182 { 183 switch (reason) 184 { 185 case Exit: 186 sformat(sink, 187 "Process exited normally with return code {}", status); 188 break; 189 190 case Signal: 191 sformat(sink, "Process was killed with signal {}", status); 192 break; 193 194 case Stop: 195 sformat(sink, "Process was stopped with signal {}", status); 196 break; 197 198 case Continue: 199 sformat(sink, "Process was resumed with signal {}", status); 200 break; 201 202 case Error: 203 sformat(sink, "Process failed with error code {}: {}", 204 reason, SysError.lookup(status)); 205 break; 206 207 default: 208 sformat(sink, "Unknown process result {}", reason); 209 break; 210 } 211 } 212 213 /// Convenience overload that format this as a string 214 public istring toString () 215 { 216 return format("{}", &this); 217 } 218 } 219 220 static immutable uint DefaultStdinBufferSize = 512; 221 static immutable uint DefaultStdoutBufferSize = 8192; 222 static immutable uint DefaultStderrBufferSize = 512; 223 static immutable Redirect DefaultRedirectFlags = Redirect.All; 224 225 private cstring[] _args; 226 private istring[istring] _env; 227 private cstring _workDir; 228 private PipeConduit _stdin; 229 private PipeConduit _stdout; 230 private PipeConduit _stderr; 231 private bool _running = false; 232 private bool _copyEnv = false; 233 private Redirect _redirect = DefaultRedirectFlags; 234 235 private pid_t _pid = cast(pid_t) -1; 236 237 /** 238 * Constructor (variadic version). Note that by default, the environment 239 * will not be copied. 240 * 241 * Params: 242 * args = array of strings with the process' arguments. If there is 243 * exactly one argument, it is considered to contain the entire 244 * command line including parameters. If you pass only one 245 * argument, spaces that are not intended to separate 246 * parameters should be embedded in quotes. The arguments can 247 * also be empty. 248 * Note: The class will use only slices, .dup when necessary. 249 * 250 */ 251 public this(const(mstring)[] args ...) 252 { 253 if(args.length == 1) 254 _args = splitArgs(args[0]); 255 else 256 _args.copy(args); 257 } 258 259 /// 260 unittest 261 { 262 void example ( ) 263 { 264 auto p1 = new Process("myprogram", "first argument", "second", "third"); 265 auto p2 = new Process("myprogram \"first argument\" second third"); 266 } 267 } 268 269 /** 270 * Constructor (variadic version, with environment copy). 271 * 272 * Params: 273 * copyEnv = if true, the environment is copied from the current process. 274 * args = array of strings with the process' arguments. If there is 275 * exactly one argument, it is considered to contain the entire 276 * command line including parameters. If you pass only one 277 * argument, spaces that are not intended to separate 278 * parameters should be embedded in quotes. The arguments can 279 * also be empty. 280 * Note: The class will use only slices, .dup when necessary. 281 */ 282 public this(bool copyEnv, const(mstring)[] args ...) 283 { 284 _copyEnv = copyEnv; 285 this(args); 286 } 287 288 /// 289 unittest 290 { 291 void example ( ) 292 { 293 auto p1 = new Process(true, "myprogram", "first argument", "second", "third"); 294 auto p2 = new Process(true, "myprogram \"first argument\" second third"); 295 } 296 } 297 298 /** 299 * Constructor. 300 * 301 * Params: 302 * command = string with the process' command line; arguments that have 303 * embedded whitespace must be enclosed in inside double-quotes ("). 304 * Note: The class will use only slices, .dup when necessary. 305 * env = associative array of strings with the process' environment 306 * variables; the variable name must be the key of each entry. 307 */ 308 public this(cstring command, istring[istring] env) 309 { 310 verify(command.length > 0); 311 _args = splitArgs(command); 312 _env = env; 313 } 314 315 /// 316 unittest 317 { 318 void example ( ) 319 { 320 cstring command = "myprogram \"first argument\" second third"; 321 istring[istring] env; 322 323 // Environment variables 324 env["MYVAR1"] = "first"; 325 env["MYVAR2"] = "second"; 326 327 auto p = new Process(command, env); 328 } 329 } 330 331 /** 332 * Constructor. 333 * 334 * Params: 335 * args = array of strings with the process' arguments; the first 336 * argument must be the process' name; the arguments can be 337 * empty. 338 * Note: The class will use only slices, .dup when necessary. 339 * env = associative array of strings with the process' environment 340 * variables; the variable name must be the key of each entry. 341 */ 342 public this(const(mstring)[] args, istring[istring] env) 343 { 344 verify(args.length > 0); 345 verify(args[0].length > 0); 346 _args.copy(args); 347 _env = env; 348 } 349 350 /// 351 unittest 352 { 353 void example ( ) 354 { 355 istring[] args; 356 istring[istring] env; 357 358 // Process name 359 args ~= "myprogram"; 360 // Process arguments 361 args ~= "first argument"; 362 args ~= "second"; 363 args ~= "third"; 364 365 // Environment variables 366 env["MYVAR1"] = "first"; 367 env["MYVAR2"] = "second"; 368 369 auto p = new Process(args, env); 370 } 371 } 372 373 /** 374 * Indicate whether the process is running or not. 375 */ 376 public bool isRunning() 377 { 378 return _running; 379 } 380 381 /** 382 * Return the running process' ID. 383 * 384 * Returns: an int with the process ID if the process is running; 385 * -1 if not. 386 */ 387 public int pid() 388 { 389 return cast(int) _pid; 390 } 391 392 /** 393 * Return the process' executable filename. 394 */ 395 public cstring programName() 396 { 397 return (_args !is null ? _args[0] : null); 398 } 399 400 /** 401 * Set the process' executable filename. 402 */ 403 public cstring programName(cstring name) 404 { 405 if (_args.length == 0) 406 { 407 _args.length = 1; 408 } 409 return _args[0] = name; 410 } 411 412 /** 413 * Set the process' executable filename, return 'this' for chaining 414 */ 415 public Process setProgramName(cstring name) 416 { 417 programName = name; 418 return this; 419 } 420 421 /** 422 * Return an array with the process' arguments. 423 */ 424 public cstring[] args() 425 { 426 return _args; 427 } 428 429 /** 430 * Set the process' arguments from the arguments received by the method. 431 * 432 * Remarks: 433 * The first element of the array must be the name of the process' 434 * executable. 435 * 436 * Returns: the arguments that were set. 437 */ 438 public cstring[] args(cstring progname, const(mstring)[] args ...) 439 { 440 return _args.copy(progname ~ args); 441 } 442 443 /// 444 unittest 445 { 446 void example ( ) 447 { 448 auto p = new Process; 449 p.args("myprogram", "first", "second argument", "third"); 450 } 451 } 452 453 /** 454 * Set the process' command and arguments from an array. 455 * 456 * Remarks: 457 * The first element of the array must be the name of the process' 458 * executable. 459 * 460 * Returns: the arguments that were set. 461 * 462 */ 463 464 public void argsWithCommand(const(mstring)[] args) 465 { 466 _args.copy(args); 467 } 468 469 /// 470 unittest 471 { 472 void example ( ) 473 { 474 auto p = new Process; 475 p.argsWithCommand(["myprogram", "first", "second argument", "third"]); 476 } 477 } 478 479 /** 480 * Set the process' arguments from the arguments received by the method. 481 * 482 * Remarks: 483 * The first element of the array must be the name of the process' 484 * executable. 485 * 486 * Returns: a reference to this for chaining 487 * 488 */ 489 public Process setArgs(cstring progname, const(mstring)[] args ...) 490 { 491 this.args(progname, args); 492 return this; 493 } 494 495 /// 496 unittest 497 { 498 void example ( ) 499 { 500 auto p = new Process; 501 p.setArgs("myprogram", "first", "second argument", "third").execute(); 502 } 503 } 504 505 /** 506 * If true, the environment from the current process will be copied to the 507 * child process. 508 */ 509 public bool copyEnv() 510 { 511 return _copyEnv; 512 } 513 514 /** 515 * Set the copyEnv flag. If set to true, then the environment will be 516 * copied from the current process. If set to false, then the environment 517 * is set from the env field. 518 */ 519 public bool copyEnv(bool b) 520 { 521 return _copyEnv = b; 522 } 523 524 /** 525 * Set the copyEnv flag. If set to true, then the environment will be 526 * copied from the current process. If set to false, then the environment 527 * is set from the env field. 528 * 529 * Returns: 530 * A reference to this for chaining 531 */ 532 public Process setCopyEnv(bool b) 533 { 534 _copyEnv = b; 535 return this; 536 } 537 538 /** 539 * Return an associative array with the process' environment variables. 540 * 541 * Note that if copyEnv is set to true, this value is ignored. 542 */ 543 public istring[istring] env() 544 { 545 return _env; 546 } 547 548 /** 549 * Set the process' environment variables from the associative array 550 * received by the method. 551 * 552 * This also clears the copyEnv flag. 553 * 554 * Params: 555 * env = associative array of strings containing the environment 556 * variables for the process. The variable name should be the key 557 * used for each entry. 558 * 559 * Returns: the env set. 560 * Examples: 561 * --- 562 * istring[istring] env; 563 * 564 * env["MYVAR1"] = "first"; 565 * env["MYVAR2"] = "second"; 566 * 567 * p.env = env; 568 * --- 569 */ 570 public istring[istring] env(istring[istring] env) 571 { 572 _copyEnv = false; 573 return _env = env; 574 } 575 576 /** 577 * Set the process' environment variables from the associative array 578 * received by the method. Returns a 'this' reference for chaining. 579 * 580 * This also clears the copyEnv flag. 581 * 582 * Params: 583 * env = associative array of strings containing the environment 584 * variables for the process. The variable name should be the key 585 * used for each entry. 586 * 587 * Returns: A reference to this process object 588 */ 589 public Process setEnv(istring[istring] env) 590 { 591 _copyEnv = false; 592 _env = env; 593 return this; 594 } 595 596 /// 597 unittest 598 { 599 void example ( ) 600 { 601 auto p = new Process; 602 istring[istring] env; 603 env["MYVAR1"] = "first"; 604 env["MYVAR2"] = "second"; 605 p.setEnv(env).execute(); 606 } 607 } 608 609 /** 610 * Return an UTF-8 string with the process' command line. 611 */ 612 public override istring toString() 613 { 614 istring command; 615 616 for (uint i = 0; i < _args.length; ++i) 617 { 618 if (i > 0) 619 { 620 command ~= ' '; 621 } 622 if (contains(_args[i], ' ') || _args[i].length == 0) 623 { 624 command ~= '"'; 625 command ~= _args[i].substitute("\\", "\\\\").substitute(`"`, `\"`); 626 command ~= '"'; 627 } 628 else 629 { 630 command ~= _args[i].substitute("\\", "\\\\").substitute(`"`, `\"`); 631 } 632 } 633 return command; 634 } 635 636 /** 637 * Return the working directory for the process. 638 * 639 * Returns: a string with the working directory; null if the working 640 * directory is the current directory. 641 */ 642 public cstring workDir() 643 { 644 return _workDir; 645 } 646 647 /** 648 * Set the working directory for the process. 649 * 650 * Params: 651 * dir = a string with the working directory; null if the working 652 * directory is the current directory. 653 * 654 * Returns: the directory set. 655 */ 656 public cstring workDir(cstring dir) 657 { 658 return _workDir = dir; 659 } 660 661 /** 662 * Set the working directory for the process. Returns a 'this' reference 663 * for chaining 664 * 665 * Params: 666 * dir = a string with the working directory; null if the working 667 * directory is the current directory. 668 * 669 * Returns: a reference to this process. 670 */ 671 public Process setWorkDir(cstring dir) 672 { 673 _workDir = dir; 674 return this; 675 } 676 677 /** 678 * Get the redirect flags for the process. 679 * 680 * The redirect flags are used to determine whether stdout, stderr, or 681 * stdin are redirected. The flags are an or'd combination of which 682 * standard handles to redirect. A redirected handle creates a pipe, 683 * whereas a non-redirected handle simply points to the same handle this 684 * process is pointing to. 685 * 686 * You can also redirect stdout or stderr to each other. The flags to 687 * redirect a handle to a pipe and to redirect it to another handle are 688 * mutually exclusive. In the case both are specified, the redirect to 689 * the other handle takes precedent. It is illegal to specify both 690 * redirection from stdout to stderr and from stderr to stdout. If both 691 * of these are specified, an exception is thrown. 692 * 693 * If redirected to a pipe, once the process is executed successfully, its 694 * input and output can be manipulated through the stdin, stdout and 695 * stderr member PipeConduit's. Note that if you redirect for example 696 * stderr to stdout, and you redirect stdout to a pipe, only stdout will 697 * be non-null. 698 */ 699 public Redirect redirect() 700 { 701 return _redirect; 702 } 703 704 /** 705 * Set the redirect flags for the process. 706 */ 707 public Redirect redirect(Redirect flags) 708 { 709 return _redirect = flags; 710 } 711 712 /** 713 * Set the redirect flags for the process. Return a reference to this 714 * process for chaining. 715 */ 716 public Process setRedirect(Redirect flags) 717 { 718 _redirect = flags; 719 return this; 720 } 721 722 /** 723 * Get the GUI flag. 724 * 725 * This flag indicates on Windows systems that the CREATE_NO_WINDOW flag 726 * should be set on CreateProcess. Although this is a specific windows 727 * flag, it is present on posix systems as a noop for compatibility. 728 * 729 * Without this flag, a console window will be allocated if it doesn't 730 * already exist. 731 */ 732 public bool gui() 733 { 734 return false; 735 } 736 737 /** 738 * Set the GUI flag. 739 * 740 * This flag indicates on Windows systems that the CREATE_NO_WINDOW flag 741 * should be set on CreateProcess. Although this is a specific windows 742 * flag, it is present on posix systems as a noop for compatibility. 743 * 744 * Without this flag, a console window will be allocated if it doesn't 745 * already exist. 746 */ 747 public bool gui(bool value) 748 { 749 return false; 750 } 751 752 /** 753 * Set the GUI flag. Returns a reference to this process for chaining. 754 * 755 * This flag indicates on Windows systems that the CREATE_NO_WINDOW flag 756 * should be set on CreateProcess. Although this is a specific windows 757 * flag, it is present on posix systems as a noop for compatibility. 758 * 759 * Without this flag, a console window will be allocated if it doesn't 760 * already exist. 761 */ 762 public Process setGui(bool value) 763 { 764 return this; 765 } 766 767 /** 768 * Return the running process' standard input pipe. 769 * 770 * Returns: a write-only PipeConduit connected to the child 771 * process' stdin. 772 * 773 * Remarks: 774 * The stream will be null if no child process has been executed, or the 775 * standard input stream was not redirected. 776 */ 777 public PipeConduit stdin() 778 { 779 return _stdin; 780 } 781 782 /** 783 * Return the running process' standard output pipe. 784 * 785 * Returns: a read-only PipeConduit connected to the child 786 * process' stdout. 787 * 788 * Remarks: 789 * The stream will be null if no child process has been executed, or the 790 * standard output stream was not redirected. 791 */ 792 public PipeConduit stdout() 793 { 794 return _stdout; 795 } 796 797 /** 798 * Return the running process' standard error pipe. 799 * 800 * Returns: a read-only PipeConduit connected to the child 801 * process' stderr. 802 * 803 * Remarks: 804 * The stream will be null if no child process has been executed, or the 805 * standard error stream was not redirected. 806 */ 807 public PipeConduit stderr() 808 { 809 return _stderr; 810 } 811 812 /** 813 * Pipes used during execute(). They are member variables so that they 814 * can be reused by later calls to execute(). 815 * Note that any file handles created during execute() will remain open 816 * and stored in these pipes, unless they are explicitly closed. 817 */ 818 Pipe pin, pout, perr, pexec; 819 820 /** 821 * Execute a process using the arguments that were supplied to the 822 * constructor or to the args property. 823 * 824 * Once the process is executed successfully, its input and output can be 825 * manipulated through the stdin, stdout and 826 * stderr member PipeConduit's. 827 * 828 * Returns: 829 * A reference to this process object for chaining. 830 * 831 * Throws: 832 * ProcessCreateException if the process could not be created 833 * successfully; ProcessForkException if the call to the fork() 834 * system call failed (on POSIX-compatible platforms). 835 * 836 * Remarks: 837 * The process must not be running and the list of arguments must 838 * not be empty before calling this method. 839 */ 840 public Process execute() 841 { 842 verify(!_running); 843 verify(_args.length > 0 && _args[0] !is null); 844 845 // We close the pipes that could have been left open from a previous 846 // execution. 847 cleanPipes(); 848 849 // validate the redirection flags 850 if((_redirect & (Redirect.OutputToError | Redirect.ErrorToOutput)) == (Redirect.OutputToError | Redirect.ErrorToOutput)) 851 throw new ProcessCreateException(_args[0], "Illegal redirection flags"); 852 853 // Are we redirecting stdout and stderr? 854 bool redirected_output = (_redirect & (Redirect.Output | Redirect.OutputToError)) == Redirect.Output; 855 bool redirected_error = (_redirect & (Redirect.Error | Redirect.ErrorToOutput)) == Redirect.Error; 856 857 if(_redirect & Redirect.Input) 858 { 859 if ( ! pin ) 860 { 861 pin = new Pipe(DefaultStdinBufferSize); 862 } 863 else 864 { 865 pin.recreate(DefaultStdinBufferSize); 866 } 867 } 868 869 if( redirected_output ) 870 { 871 if ( ! pout ) 872 { 873 pout = new Pipe(DefaultStdoutBufferSize); 874 } 875 else 876 { 877 pout.recreate(DefaultStdoutBufferSize); 878 } 879 } 880 881 if( redirected_error ) 882 { 883 if ( ! perr) 884 { 885 perr = new Pipe(DefaultStderrBufferSize); 886 } 887 else 888 { 889 perr.recreate(DefaultStderrBufferSize); 890 } 891 } 892 893 // This pipe is used to propagate the result of the call to 894 // execv*() from the child process to the parent process. 895 if (! pexec) 896 { 897 pexec = new Pipe(8); 898 } 899 else 900 { 901 pexec.recreate(8); 902 } 903 904 int status = 0; 905 906 _pid = fork(); 907 if (_pid >= 0) 908 { 909 if (_pid != 0) 910 { 911 // Parent process 912 if(_redirect & Redirect.Input) 913 { 914 _stdin = pin.sink; 915 pin.source.close(); 916 } 917 918 if( redirected_output ) 919 { 920 _stdout = pout.source; 921 pout.sink.close(); 922 } 923 924 if(redirected_error) 925 { 926 _stderr = perr.source; 927 perr.sink.close(); 928 } 929 930 pexec.sink.close(); 931 932 try 933 { 934 pexec.source.input.read((cast(byte*) &status)[0 .. status.sizeof]); 935 } 936 catch (Exception e) 937 { 938 // Everything's OK, the pipe was closed after the call to execv*() 939 } 940 941 pexec.source.close(); 942 943 if (status == 0) 944 { 945 _running = true; 946 } 947 else 948 { 949 // We set errno to the value that was sent through 950 // the pipe from the child process 951 errno = status; 952 _running = false; 953 954 throw new ProcessCreateException(_args[0]); 955 } 956 } 957 else 958 { 959 // Child process 960 int rc; 961 char*[] argptr; 962 char*[] envptr; 963 964 // Note that for all the pipes, we can close both ends 965 // because dup2 opens a duplicate file descriptor to the 966 // same resource. 967 968 // Replace stdin with the "read" pipe 969 if(_redirect & Redirect.Input) 970 { 971 if (dup2(pin.source.fileHandle(), STDIN_FILENO) < 0) 972 throw new Exception("dup2 < 0"); 973 pin.sink.close(); 974 pin.source.close(); 975 } 976 977 // Replace stdout with the "write" pipe 978 if( redirected_output ) 979 { 980 if (dup2(pout.sink.fileHandle(), STDOUT_FILENO) < 0) 981 throw new Exception("dup2 < 0"); 982 pout.source.close(); 983 pout.sink.close(); 984 } 985 986 // Replace stderr with the "write" pipe 987 if( redirected_error ) 988 { 989 if (dup2(perr.sink.fileHandle(), STDERR_FILENO) < 0) 990 throw new Exception("dup2 < 0"); 991 perr.source.close(); 992 perr.sink.close(); 993 } 994 995 // Check for redirection from stdout to stderr or vice 996 // versa 997 if(_redirect & Redirect.OutputToError) 998 { 999 if(dup2(STDERR_FILENO, STDOUT_FILENO) < 0) 1000 throw new Exception("dup2 < 0"); 1001 } 1002 1003 if(_redirect & Redirect.ErrorToOutput) 1004 { 1005 if(dup2(STDOUT_FILENO, STDERR_FILENO) < 0) 1006 throw new Exception("dup2 < 0"); 1007 } 1008 1009 // We close the unneeded part of the execv*() notification pipe 1010 pexec.source.close(); 1011 1012 // Set the "write" pipe so that it closes upon a successful 1013 // call to execv*() 1014 if (fcntl(cast(int) pexec.sink.fileHandle(), F_SETFD, FD_CLOEXEC) == 0) 1015 { 1016 // Convert the arguments and the environment variables to 1017 // the format expected by the execv() family of functions. 1018 argptr = toNullEndedArray(_args); 1019 envptr = (_copyEnv ? null : toNullEndedArray(_env)); 1020 1021 // Switch to the working directory if it has been set. 1022 if (_workDir.length > 0) 1023 { 1024 chdir((_workDir ~ "\0").ptr); 1025 } 1026 1027 // Replace the child fork with a new process. We always use the 1028 // system PATH to look for executables that don't specify 1029 // directories in their names. 1030 rc = execvpe(_args[0], argptr, envptr); 1031 if (rc == -1) 1032 { 1033 Cerr("Failed to exec ")(_args[0])(": ")(SysError.lastMsg).newline; 1034 1035 try 1036 { 1037 status = errno; 1038 1039 // Propagate the child process' errno value to 1040 // the parent process. 1041 pexec.sink.output.write((cast(byte*) &status)[0 .. status.sizeof]); 1042 } 1043 catch (Exception e) 1044 { 1045 } 1046 exit(errno); 1047 } 1048 exit(errno); 1049 } 1050 else 1051 { 1052 Cerr("Failed to set notification pipe to close-on-exec for ") 1053 (_args[0])(": ")(SysError.lastMsg).newline; 1054 exit(errno); 1055 } 1056 } 1057 } 1058 else 1059 { 1060 throw new ProcessForkException(_pid); 1061 } 1062 return this; 1063 } 1064 1065 1066 /** 1067 * Unconditionally wait for a process to end and return the reason and 1068 * status code why the process ended. 1069 * 1070 * Returns: 1071 * The return value is a Result struct, which has two members: 1072 * reason and status. The reason can take the 1073 * following values: 1074 * 1075 * Process.Result.Exit: the child process exited normally; 1076 * status has the process' return 1077 * code. 1078 * 1079 * Process.Result.Signal: the child process was killed by a signal; 1080 * status has the signal number 1081 * that killed the process. 1082 * 1083 * Process.Result.Stop: the process was stopped; status 1084 * has the signal number that was used to stop 1085 * the process. 1086 * 1087 * Process.Result.Continue: the process had been previously stopped 1088 * and has now been restarted; 1089 * status has the signal number 1090 * that was used to continue the process. 1091 * 1092 * Process.Result.Error: We could not properly wait on the child 1093 * process; status has the 1094 * errno value if the process was 1095 * running and -1 if not. 1096 * 1097 * Remarks: 1098 * You can only call wait() on a running process once. The Signal, Stop 1099 * and Continue reasons will only be returned on POSIX-compatible 1100 * platforms. 1101 * Calling wait() will not clean the pipes as the parent process may still 1102 * want the remaining output. It is however recommended to call close() 1103 * when no more content is expected, as this will close the pipes. 1104 */ 1105 public Result wait() 1106 { 1107 Result result; 1108 1109 if (_running) 1110 { 1111 int rc; 1112 1113 // We clean up the process related data and set the _running 1114 // flag to false once we're done waiting for the process to 1115 // finish. 1116 // 1117 // IMPORTANT: we don't delete the open pipes so that the parent 1118 // process can get whatever the child process left on 1119 // these pipes before dying. 1120 scope(exit) 1121 { 1122 _running = false; 1123 } 1124 1125 // Wait for child process to end. 1126 if (waitpid(_pid, &rc, 0) != -1) 1127 { 1128 if (WIFEXITED(rc)) 1129 { 1130 result.reason = Result.Exit; 1131 result.status = WEXITSTATUS(rc); 1132 if (result.status != 0) 1133 { 1134 debug (Process) 1135 Stdout.formatln("Child process '{0}' ({1}) returned with code {2}\n", 1136 _args[0], _pid, result.status); 1137 } 1138 } 1139 else 1140 { 1141 if (WIFSIGNALED(rc)) 1142 { 1143 result.reason = Result.Signal; 1144 result.status = WTERMSIG(rc); 1145 1146 debug (Process) 1147 Stdout.formatln("Child process '{0}' ({1}) was killed prematurely " 1148 ~ "with signal {2}", 1149 _args[0], _pid, result.status); 1150 } 1151 else if (WIFSTOPPED(rc)) 1152 { 1153 result.reason = Result.Stop; 1154 result.status = WSTOPSIG(rc); 1155 1156 debug (Process) 1157 Stdout.formatln("Child process '{0}' ({1}) was stopped " 1158 ~ "with signal {2}", 1159 _args[0], _pid, result.status); 1160 } 1161 else if (WIFCONTINUED(rc)) 1162 { 1163 result.reason = Result.Stop; 1164 result.status = WSTOPSIG(rc); 1165 1166 debug (Process) 1167 Stdout.formatln("Child process '{0}' ({1}) was continued " 1168 ~ "with signal {2}", 1169 _args[0], _pid, result.status); 1170 } 1171 else 1172 { 1173 result.reason = Result.Error; 1174 result.status = rc; 1175 1176 debug (Process) 1177 Stdout.formatln("Child process '{0}' ({1}) failed " 1178 ~ "with unknown exit status {2}\n", 1179 _args[0], _pid, result.status); 1180 } 1181 } 1182 } 1183 else 1184 { 1185 result.reason = Result.Error; 1186 result.status = errno; 1187 1188 debug (Process) 1189 Stdout.formatln("Could not wait on child process '{0}' ({1}): ({2}) {3}", 1190 _args[0], _pid, result.status, SysError.lastMsg); 1191 } 1192 } 1193 else 1194 { 1195 result.reason = Result.Error; 1196 result.status = -1; 1197 1198 debug (Process) 1199 Stdout.formatln("Child process '{0}' is not running", _args[0]); 1200 } 1201 return result; 1202 } 1203 1204 /** 1205 * Kill a running process. This method will not return until the process 1206 * has been killed. 1207 * 1208 * Throws: 1209 * ProcessKillException if the process could not be killed; 1210 * ProcessWaitException if we could not wait on the process after 1211 * killing it. 1212 * 1213 * Remarks: 1214 * After calling this method you will not be able to call wait() on the 1215 * process. 1216 * Killing the process does not clean the attached pipes as the parent 1217 * process may still want/need the remaining content. However, it is 1218 * recommended to call close() on the process when it is no longer needed 1219 * as this will clean the pipes. 1220 */ 1221 public void kill() 1222 { 1223 if (_running) 1224 { 1225 int rc; 1226 1227 verify(_pid > 0); 1228 1229 if (.kill(_pid, SIGTERM) != -1) 1230 { 1231 // We clean up the process related data and set the _running 1232 // flag to false once we're done waiting for the process to 1233 // finish. 1234 // 1235 // IMPORTANT: we don't delete the open pipes so that the parent 1236 // process can get whatever the child process left on 1237 // these pipes before dying. 1238 scope(exit) 1239 { 1240 _running = false; 1241 } 1242 1243 // FIXME: is this loop really needed? 1244 for (uint i = 0; i < 100; i++) 1245 { 1246 rc = waitpid(pid, null, WNOHANG | WUNTRACED); 1247 if (rc == _pid) 1248 { 1249 break; 1250 } 1251 else if (rc == -1) 1252 { 1253 throw new ProcessWaitException(cast(int) _pid); 1254 } 1255 usleep(50000); 1256 } 1257 } 1258 else 1259 { 1260 throw new ProcessKillException(_pid); 1261 } 1262 } 1263 else 1264 { 1265 debug (Process) 1266 Stdout.print("Tried to kill an invalid process"); 1267 } 1268 } 1269 1270 /** 1271 * Split a string containing the command line used to invoke a program 1272 * and return and array with the parsed arguments. The double-quotes (") 1273 * character can be used to specify arguments with embedded spaces. 1274 * e.g. first "second param" third 1275 */ 1276 protected static cstring[] splitArgs(cstring command, cstring delims = " \t\r\n") 1277 { 1278 verify( 1279 !contains(delims, '"'), 1280 "The argument delimiter string cannot contain a double " 1281 ~ "quotes ('\"') character" 1282 ); 1283 1284 enum State 1285 { 1286 Start, 1287 FindDelimiter, 1288 InsideQuotes 1289 } 1290 1291 cstring[] args = null; 1292 cstring[] chunks = null; 1293 int start = -1; 1294 char c; 1295 int i; 1296 State state = State.Start; 1297 1298 // Append an argument to the 'args' array using the 'chunks' array 1299 // and the current position in the 'command' string as the source. 1300 void appendChunksAsArg() 1301 { 1302 size_t argPos; 1303 1304 if (chunks.length > 0) 1305 { 1306 // Create the array element corresponding to the argument by 1307 // appending the first chunk. 1308 args ~= chunks[0]; 1309 argPos = args.length - 1; 1310 1311 for (uint chunkPos = 1; chunkPos < chunks.length; ++chunkPos) 1312 { 1313 args[argPos] ~= chunks[chunkPos]; 1314 } 1315 1316 if (start != -1) 1317 { 1318 args[argPos] ~= command[start .. i]; 1319 } 1320 chunks.length = 0; 1321 } 1322 else 1323 { 1324 if (start != -1) 1325 { 1326 args ~= command[start .. i]; 1327 } 1328 } 1329 start = -1; 1330 } 1331 1332 for (i = 0; i < command.length; i++) 1333 { 1334 c = command[i]; 1335 1336 switch (state) 1337 { 1338 // Start looking for an argument. 1339 case State.Start: 1340 if (c == '"') 1341 { 1342 state = State.InsideQuotes; 1343 } 1344 else if (!contains(delims, c)) 1345 { 1346 start = i; 1347 state = State.FindDelimiter; 1348 } 1349 else 1350 { 1351 appendChunksAsArg(); 1352 } 1353 break; 1354 1355 // Find the ending delimiter for an argument. 1356 case State.FindDelimiter: 1357 if (c == '"') 1358 { 1359 // If we find a quotes character this means that we've 1360 // found a quoted section of an argument. (e.g. 1361 // abc"def"ghi). The quoted section will be appended 1362 // to the preceding part of the argument. This is also 1363 // what Unix shells do (i.e. a"b"c becomes abc). 1364 if (start != -1) 1365 { 1366 chunks ~= command[start .. i]; 1367 start = -1; 1368 } 1369 state = State.InsideQuotes; 1370 } 1371 else if (contains(delims, c)) 1372 { 1373 appendChunksAsArg(); 1374 state = State.Start; 1375 } 1376 break; 1377 1378 // Inside a quoted argument or section of an argument. 1379 case State.InsideQuotes: 1380 if (start == -1) 1381 { 1382 start = i; 1383 } 1384 1385 if (c == '"') 1386 { 1387 chunks ~= command[start .. i]; 1388 start = -1; 1389 state = State.Start; 1390 } 1391 break; 1392 1393 default: 1394 assert(false, "Invalid state in Process.splitArgs"); 1395 } 1396 } 1397 1398 // Add the last argument (if there is one) 1399 appendChunksAsArg(); 1400 1401 return args; 1402 } 1403 1404 /** 1405 * Close and delete any pipe that may have been left open in a previous 1406 * execution of a child process. 1407 */ 1408 protected void cleanPipes() 1409 { 1410 // We re-uses Pipe objects, so simply close them, if they 1411 // have been used before 1412 if ( pin !is null ) pin.close(); 1413 if ( pout !is null ) pout.close(); 1414 if ( perr !is null ) perr.close(); 1415 if ( pexec !is null ) pexec.close(); 1416 } 1417 1418 /** 1419 * Explicitly close any resources held by this process object. It is recommended 1420 * to always call this when you are done with the process. 1421 */ 1422 public void close() 1423 { 1424 this.cleanPipes; 1425 } 1426 1427 /** 1428 * Convert an array of strings to an array of pointers to char with 1429 * a terminating null character (C strings). The resulting array 1430 * has a null pointer at the end. This is the format expected by 1431 * the execv*() family of POSIX functions. 1432 */ 1433 protected static char*[] toNullEndedArray(cstring[] src) 1434 { 1435 if (src !is null) 1436 { 1437 char*[] dest = new char*[src.length + 1]; 1438 auto i = src.length; 1439 1440 // Add terminating null pointer to the array 1441 dest[i] = null; 1442 1443 while (i > 0) 1444 { 1445 --i; 1446 // Add a terminating null character to each string 1447 auto cstr = src[i]; 1448 if (cstr.length == 0) 1449 dest[i] = "\0".dup.ptr; 1450 else if (cstr[$ - 1] == '\0') 1451 dest[i] = cstr.dup.ptr; 1452 else 1453 dest[i] = (cstr ~ "\0").ptr; 1454 } 1455 return dest; 1456 } 1457 else 1458 { 1459 return null; 1460 } 1461 } 1462 1463 /** 1464 * Convert an associative array of strings to an array of pointers to 1465 * char with a terminating null character (C strings). The resulting 1466 * array has a null pointer at the end. This is the format expected by 1467 * the execv*() family of POSIX functions for environment variables. 1468 */ 1469 protected static char*[] toNullEndedArray(istring[istring] src) 1470 { 1471 char*[] dest; 1472 1473 foreach (key, value; src) 1474 { 1475 dest ~= (key.dup ~ '=' ~ value ~ '\0').ptr; 1476 } 1477 1478 dest ~= null; 1479 return dest; 1480 } 1481 1482 /** 1483 * Execute a process by looking up a file in the system path, passing 1484 * the array of arguments and the the environment variables. This 1485 * method is a combination of the execve() and execvp() POSIX system 1486 * calls. 1487 */ 1488 protected static int execvpe(cstring filename, char*[] argv, char*[] envp) 1489 { 1490 verify(filename.length > 0); 1491 1492 int rc = -1; 1493 char* str; 1494 1495 if (!contains(filename, FileConst.PathSeparatorChar) && 1496 (str = getenv("PATH".ptr)) !is null) 1497 { 1498 auto pathList = delimit(str[0 .. strlen(str)], ":"); 1499 1500 mstring path_buf; 1501 1502 foreach (path; pathList) 1503 { 1504 if (path[$-1] != FileConst.PathSeparatorChar) 1505 { 1506 path_buf.length = path.length + 1 + filename.length + 1; 1507 assumeSafeAppend(path_buf); 1508 1509 path_buf[] = path ~ FileConst.PathSeparatorChar ~ filename ~ '\0'; 1510 } 1511 else 1512 { 1513 path_buf.length = path.length +filename.length + 1; 1514 assumeSafeAppend(path_buf); 1515 1516 path_buf[] = path ~ filename ~ '\0'; 1517 } 1518 1519 rc = execve(path_buf.ptr, argv.ptr, (envp.length == 0 ? environ : envp.ptr)); 1520 1521 // If the process execution failed because of an error 1522 // other than ENOENT (No such file or directory) we 1523 // abort the loop. 1524 if (rc == -1 && SysError.lastCode !is ENOENT) 1525 { 1526 break; 1527 } 1528 } 1529 } 1530 else 1531 { 1532 debug (Process) 1533 Stdout.formatln("Calling execve('{0}', argv[{1}], {2})", 1534 (argv[0])[0 .. strlen(argv[0])], 1535 argv.length, (envp.length > 0 ? "envp" : "null")); 1536 1537 rc = execve(argv[0], argv.ptr, (envp.length == 0 ? environ : envp.ptr)); 1538 } 1539 return rc; 1540 } 1541 } 1542 1543 1544 /** 1545 * Exception thrown when the process cannot be created. 1546 */ 1547 class ProcessCreateException: ProcessException 1548 { 1549 public this (cstring command, istring message = SysError.lastMsg, 1550 istring file = __FILE__, uint line = __LINE__) 1551 { 1552 super( 1553 format("Could not create process for '{}': {}", command, message), 1554 file, line); 1555 } 1556 } 1557 1558 /** 1559 * Exception thrown when the parent process cannot be forked. 1560 * 1561 * This exception will only be thrown on POSIX-compatible platforms. 1562 */ 1563 class ProcessForkException: ProcessException 1564 { 1565 public this (int pid, istring file = __FILE__, uint line = __LINE__) 1566 { 1567 super( 1568 format("Could not fork process {}: {}", pid, SysError.lastMsg), 1569 file, line); 1570 } 1571 } 1572 1573 /** 1574 * Exception thrown when the process cannot be killed. 1575 */ 1576 class ProcessKillException: ProcessException 1577 { 1578 public this (int pid, istring file = __FILE__, uint line = __LINE__) 1579 { 1580 super( 1581 format("Could not kill process {}: {}", pid, SysError.lastMsg), 1582 file, line); 1583 } 1584 } 1585 1586 /** 1587 * Exception thrown when the parent process tries to wait on the child 1588 * process and fails. 1589 */ 1590 class ProcessWaitException: ProcessException 1591 { 1592 public this (int pid, istring file = __FILE__, uint line = __LINE__) 1593 { 1594 super( 1595 format("Could not wait on process {}: {}", pid, SysError.lastMsg), 1596 file, line); 1597 } 1598 } 1599 1600 extern (C) uint sleep (uint s); 1601 1602 unittest 1603 { 1604 istring message = "hello world"; 1605 istring command = "echo " ~ message; 1606 1607 try 1608 { 1609 auto p = new Process(command, null); 1610 p.execute(); 1611 char[255] buffer; 1612 1613 auto nread = p.stdout.read(buffer); 1614 test(nread != p.stdout.Eof); 1615 test(buffer[0..nread] == message ~ "\n"); 1616 1617 nread = p.stdout.read(buffer); 1618 test(nread == p.stdout.Eof); 1619 1620 auto result = p.wait(); 1621 1622 test(result.reason == Process.Result.Exit && result.status == 0); 1623 } 1624 catch (ProcessException e) 1625 { 1626 test(false, e.message()); 1627 } 1628 } 1629 1630 // check differently qualified argument calls 1631 unittest 1632 { 1633 auto p = new Process("aaa", "bbb", "ccc"); 1634 mstring s = "xxxx".dup; 1635 p.argsWithCommand([ s, "aaa", "bbb"]); 1636 p.programName("huh"); 1637 } 1638 1639 // check non-literals arguments 1640 unittest 1641 { 1642 istring[] args = [ "aaa", "bbb" ]; 1643 auto p = new Process(args); 1644 p.argsWithCommand(args); 1645 }