1 // Written in the D programming language. 2 3 /** 4 Functions for starting and interacting with other processes, and for 5 working with the current process' execution environment. 6 7 Process_handling: 8 $(UL $(LI 9 $(LREF spawnProcess) spawns a new process, optionally assigning it an 10 arbitrary set of standard input, output, and error streams. 11 The function returns immediately, leaving the child process to execute 12 in parallel with its parent. All other functions in this module that 13 spawn processes are built around `spawnProcess`.) 14 $(LI 15 $(LREF wait) makes the parent process wait for a child process to 16 terminate. In general one should always do this, to avoid 17 child processes becoming "zombies" when the parent process exits. 18 Scope guards are perfect for this – see the $(LREF spawnProcess) 19 documentation for examples. $(LREF tryWait) is similar to `wait`, 20 but does not block if the process has not yet terminated.) 21 $(LI 22 $(LREF pipeProcess) also spawns a child process which runs 23 in parallel with its parent. However, instead of taking 24 arbitrary streams, it automatically creates a set of 25 pipes that allow the parent to communicate with the child 26 through the child's standard input, output, and/or error streams. 27 This function corresponds roughly to C's `popen` function.) 28 $(LI 29 $(LREF execute) starts a new process and waits for it 30 to complete before returning. Additionally, it captures 31 the process' standard output and error streams and returns 32 the output of these as a string.) 33 $(LI 34 $(LREF spawnShell), $(LREF pipeShell) and $(LREF executeShell) work like 35 `spawnProcess`, `pipeProcess` and `execute`, respectively, 36 except that they take a single command string and run it through 37 the current user's default command interpreter. 38 `executeShell` corresponds roughly to C's `system` function.) 39 $(LI 40 $(LREF kill) attempts to terminate a running process.) 41 ) 42 43 The following table compactly summarises the different process creation 44 functions and how they relate to each other: 45 $(BOOKTABLE, 46 $(TR $(TH ) 47 $(TH Runs program directly) 48 $(TH Runs shell command)) 49 $(TR $(TD Low-level process creation) 50 $(TD $(LREF spawnProcess)) 51 $(TD $(LREF spawnShell))) 52 $(TR $(TD Automatic input/output redirection using pipes) 53 $(TD $(LREF pipeProcess)) 54 $(TD $(LREF pipeShell))) 55 $(TR $(TD Execute and wait for completion, collect output) 56 $(TD $(LREF execute)) 57 $(TD $(LREF executeShell))) 58 ) 59 60 Other_functionality: 61 $(UL 62 $(LI 63 $(LREF pipe) is used to create unidirectional pipes.) 64 $(LI 65 $(LREF environment) is an interface through which the current process' 66 environment variables can be read and manipulated.) 67 $(LI 68 $(LREF escapeShellCommand) and $(LREF escapeShellFileName) are useful 69 for constructing shell command lines in a portable way.) 70 ) 71 72 Authors: 73 $(LINK2 https://github.com/kyllingstad, Lars Tandle Kyllingstad), 74 $(LINK2 https://github.com/schveiguy, Steven Schveighoffer), 75 $(HTTP thecybershadow.net, Vladimir Panteleev) 76 Copyright: 77 Copyright (c) 2013, the authors. All rights reserved. 78 License: 79 $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 80 Source: 81 $(PHOBOSSRC std/process.d) 82 Macros: 83 OBJECTREF=$(REF1 $0, object) 84 85 Note: 86 Most of the functionality in this module is not available on iOS, tvOS 87 and watchOS. The only functions available on those platforms are: 88 $(LREF environment), $(LREF thisProcessID) and $(LREF thisThreadID). 89 */ 90 module std.process; 91 92 import core.thread : ThreadID; 93 94 version (Posix) 95 { 96 import core.sys.posix.sys.wait; 97 import core.sys.posix.unistd; 98 } 99 version (Windows) 100 { 101 import core.stdc.stdio; 102 import core.sys.windows.winbase; 103 import core.sys.windows.winnt; 104 import std.utf; 105 import std.windows.syserror; 106 } 107 108 import std.internal.cstring; 109 import std.range; 110 import std.stdio; 111 112 version (OSX) 113 version = Darwin; 114 else version (iOS) 115 { 116 version = Darwin; 117 version = iOSDerived; 118 } 119 else version (TVOS) 120 { 121 version = Darwin; 122 version = iOSDerived; 123 } 124 else version (WatchOS) 125 { 126 version = Darwin; 127 version = iOSDerived; 128 } 129 130 131 // Some of the following should be moved to druntime. 132 private 133 { 134 // Microsoft Visual C Runtime (MSVCRT) declarations. 135 version (CRuntime_Microsoft) 136 { 137 import core.stdc.stdint; 138 enum 139 { 140 STDIN_FILENO = 0, 141 STDOUT_FILENO = 1, 142 STDERR_FILENO = 2, 143 } 144 } 145 146 // POSIX API declarations. 147 version (Posix) 148 { 149 import core.sys.posix.unistd : getEnvironPtr = environ; 150 151 @system unittest 152 { 153 import core.thread : Thread; 154 new Thread({assert(getEnvironPtr !is null);}).start(); 155 } 156 } 157 } // private 158 159 // ============================================================================= 160 // Environment variable manipulation. 161 // ============================================================================= 162 163 /** 164 Manipulates _environment variables using an associative-array-like 165 interface. 166 167 This class contains only static methods, and cannot be instantiated. 168 See below for examples of use. 169 */ 170 abstract final class environment 171 { 172 static import core.sys.posix.stdlib; 173 import core.stdc.errno : errno, EINVAL; 174 175 static: 176 /** 177 Retrieves the value of the environment variable with the given `name`. 178 --- 179 auto path = environment["PATH"]; 180 --- 181 182 Throws: 183 $(OBJECTREF Exception) if the environment variable does not exist, 184 or $(REF UTFException, std,utf) if the variable contains invalid UTF-16 185 characters (Windows only). 186 187 See_also: 188 $(LREF environment.get), which doesn't throw on failure. 189 */ 190 string opIndex(scope const(char)[] name) @safe 191 { 192 import std.exception : enforce; 193 return get(name, null).enforce("Environment variable not found: "~name); 194 } 195 196 /** 197 Retrieves the value of the environment variable with the given `name`, 198 or a default value if the variable doesn't exist. 199 200 Unlike $(LREF environment.opIndex), this function never throws on Posix. 201 --- 202 auto sh = environment.get("SHELL", "/bin/sh"); 203 --- 204 This function is also useful in checking for the existence of an 205 environment variable. 206 --- 207 auto myVar = environment.get("MYVAR"); 208 if (myVar is null) 209 { 210 // Environment variable doesn't exist. 211 // Note that we have to use 'is' for the comparison, since 212 // myVar == null is also true if the variable exists but is 213 // empty. 214 } 215 --- 216 Params: 217 name = name of the environment variable to retrieve 218 defaultValue = default value to return if the environment variable doesn't exist. 219 220 Returns: 221 the value of the environment variable if found, otherwise 222 `null` if the environment doesn't exist. 223 224 Throws: 225 $(REF UTFException, std,utf) if the variable contains invalid UTF-16 226 characters (Windows only). 227 */ 228 string get(scope const(char)[] name, string defaultValue = null) @safe 229 { 230 string value; 231 getImpl(name, (result) { value = result ? cachedToString(result) : defaultValue; }); 232 return value; 233 } 234 235 /** 236 Assigns the given `value` to the environment variable with the given 237 `name`. 238 If `value` is null the variable is removed from environment. 239 240 If the variable does not exist, it will be created. If it already exists, 241 it will be overwritten. 242 --- 243 environment["foo"] = "bar"; 244 --- 245 246 Throws: 247 $(OBJECTREF Exception) if the environment variable could not be added 248 (e.g. if the name is invalid). 249 250 Note: 251 On some platforms, modifying environment variables may not be allowed in 252 multi-threaded programs. See e.g. 253 $(LINK2 https://www.gnu.org/software/libc/manual/html_node/Environment-Access.html#Environment-Access, glibc). 254 */ 255 inout(char)[] opIndexAssign(return scope inout char[] value, scope const(char)[] name) @trusted 256 { 257 version (Posix) 258 { 259 import std.exception : enforce, errnoEnforce; 260 if (value is null) 261 { 262 remove(name); 263 return value; 264 } 265 if (core.sys.posix.stdlib.setenv(name.tempCString(), value.tempCString(), 1) != -1) 266 { 267 return value; 268 } 269 // The default errno error message is very uninformative 270 // in the most common case, so we handle it manually. 271 enforce(errno != EINVAL, 272 "Invalid environment variable name: '"~name~"'"); 273 errnoEnforce(false, 274 "Failed to add environment variable"); 275 assert(0); 276 } 277 else version (Windows) 278 { 279 import std.windows.syserror : wenforce; 280 wenforce( 281 SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW()), 282 ); 283 return value; 284 } 285 else static assert(0); 286 } 287 288 /** 289 Removes the environment variable with the given `name`. 290 291 If the variable isn't in the environment, this function returns 292 successfully without doing anything. 293 294 Note: 295 On some platforms, modifying environment variables may not be allowed in 296 multi-threaded programs. See e.g. 297 $(LINK2 https://www.gnu.org/software/libc/manual/html_node/Environment-Access.html#Environment-Access, glibc). 298 */ 299 void remove(scope const(char)[] name) @trusted nothrow @nogc 300 { 301 version (Windows) SetEnvironmentVariableW(name.tempCStringW(), null); 302 else version (Posix) core.sys.posix.stdlib.unsetenv(name.tempCString()); 303 else static assert(0); 304 } 305 306 /** 307 Identify whether a variable is defined in the environment. 308 309 Because it doesn't return the value, this function is cheaper than `get`. 310 However, if you do need the value as well, you should just check the 311 return of `get` for `null` instead of using this function first. 312 313 Example: 314 ------------- 315 // good usage 316 if ("MY_ENV_FLAG" in environment) 317 doSomething(); 318 319 // bad usage 320 if ("MY_ENV_VAR" in environment) 321 doSomething(environment["MY_ENV_VAR"]); 322 323 // do this instead 324 if (auto var = environment.get("MY_ENV_VAR")) 325 doSomething(var); 326 ------------- 327 */ 328 bool opBinaryRight(string op : "in")(scope const(char)[] name) @trusted 329 { 330 if (name is null) 331 return false; 332 version (Posix) 333 return core.sys.posix.stdlib.getenv(name.tempCString()) !is null; 334 else version (Windows) 335 { 336 SetLastError(NO_ERROR); 337 if (GetEnvironmentVariableW(name.tempCStringW, null, 0) > 0) 338 return true; 339 immutable err = GetLastError(); 340 if (err == NO_ERROR) 341 return true; // zero-length environment variable on Wine / XP 342 if (err == ERROR_ENVVAR_NOT_FOUND) 343 return false; 344 // Some other Windows error, throw. 345 throw new WindowsException(err); 346 } 347 else static assert(0); 348 } 349 350 /** 351 Copies all environment variables into an associative array. 352 353 Windows_specific: 354 While Windows environment variable names are case insensitive, D's 355 built-in associative arrays are not. This function will store all 356 variable names in uppercase (e.g. `PATH`). 357 358 Throws: 359 $(OBJECTREF Exception) if the environment variables could not 360 be retrieved (Windows only). 361 */ 362 string[string] toAA() @trusted 363 { 364 import std.conv : to; 365 string[string] aa; 366 version (Posix) 367 { 368 auto environ = getEnvironPtr; 369 for (int i=0; environ[i] != null; ++i) 370 { 371 import std.string : indexOf; 372 373 immutable varDef = to!string(environ[i]); 374 immutable eq = indexOf(varDef, '='); 375 assert(eq >= 0); 376 377 immutable name = varDef[0 .. eq]; 378 immutable value = varDef[eq+1 .. $]; 379 380 // In POSIX, environment variables may be defined more 381 // than once. This is a security issue, which we avoid 382 // by checking whether the key already exists in the array. 383 // For more info: 384 // http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/environment-variables.html 385 if (name !in aa) aa[name] = value; 386 } 387 } 388 else version (Windows) 389 { 390 import std.exception : enforce; 391 import std.uni : toUpper; 392 auto envBlock = GetEnvironmentStringsW(); 393 enforce(envBlock, "Failed to retrieve environment variables."); 394 scope(exit) FreeEnvironmentStringsW(envBlock); 395 396 for (int i=0; envBlock[i] != '\0'; ++i) 397 { 398 auto start = i; 399 while (envBlock[i] != '=') ++i; 400 immutable name = toUTF8(toUpper(envBlock[start .. i])); 401 402 start = i+1; 403 while (envBlock[i] != '\0') ++i; 404 405 // Ignore variables with empty names. These are used internally 406 // by Windows to keep track of each drive's individual current 407 // directory. 408 if (!name.length) 409 continue; 410 411 // Just like in POSIX systems, environment variables may be 412 // defined more than once in an environment block on Windows, 413 // and it is just as much of a security issue there. Moreso, 414 // in fact, due to the case insensensitivity of variable names, 415 // which is not handled correctly by all programs. 416 auto val = toUTF8(envBlock[start .. i]); 417 if (name !in aa) aa[name] = val is null ? "" : val; 418 } 419 } 420 else static assert(0); 421 return aa; 422 } 423 424 private: 425 version (Windows) alias OSChar = WCHAR; 426 else version (Posix) alias OSChar = char; 427 428 // Retrieves the environment variable. Calls `sink` with a 429 // temporary buffer of OS characters, or `null` if the variable 430 // doesn't exist. 431 void getImpl(scope const(char)[] name, scope void delegate(const(OSChar)[]) @safe sink) @trusted 432 { 433 // fix issue https://issues.dlang.org/show_bug.cgi?id=24549 434 if (name is null) 435 return sink(null); 436 437 version (Windows) 438 { 439 // first we ask windows how long the environment variable is, 440 // then we try to read it in to a buffer of that length. Lots 441 // of error conditions because the windows API is nasty. 442 443 import std.conv : to; 444 const namezTmp = name.tempCStringW(); 445 WCHAR[] buf; 446 447 // clear error because GetEnvironmentVariable only says it sets it 448 // if the environment variable is missing, not on other errors. 449 SetLastError(NO_ERROR); 450 // len includes terminating null 451 immutable len = GetEnvironmentVariableW(namezTmp, null, 0); 452 if (len == 0) 453 { 454 immutable err = GetLastError(); 455 if (err == ERROR_ENVVAR_NOT_FOUND) 456 return sink(null); 457 if (err != NO_ERROR) // Some other Windows error, throw. 458 throw new WindowsException(err); 459 } 460 if (len <= 1) 461 return sink(""); 462 buf.length = len; 463 464 while (true) 465 { 466 // lenRead is either the number of bytes read w/o null - if buf was long enough - or 467 // the number of bytes necessary *including* null if buf wasn't long enough 468 immutable lenRead = GetEnvironmentVariableW(namezTmp, buf.ptr, to!DWORD(buf.length)); 469 if (lenRead == 0) 470 { 471 immutable err = GetLastError(); 472 if (err == NO_ERROR) // sucessfully read a 0-length variable 473 return sink(""); 474 if (err == ERROR_ENVVAR_NOT_FOUND) // variable didn't exist 475 return sink(null); 476 // some other windows error 477 throw new WindowsException(err); 478 } 479 assert(lenRead != buf.length, "impossible according to msft docs"); 480 if (lenRead < buf.length) // the buffer was long enough 481 return sink(buf[0 .. lenRead]); 482 // resize and go around again, because the environment variable grew 483 buf.length = lenRead; 484 } 485 } 486 else version (Posix) 487 { 488 import core.stdc.string : strlen; 489 490 const vz = core.sys.posix.stdlib.getenv(name.tempCString()); 491 if (vz == null) return sink(null); 492 return sink(vz[0 .. strlen(vz)]); 493 } 494 else static assert(0); 495 } 496 497 string cachedToString(C)(scope const(C)[] v) @safe 498 { 499 import std.algorithm.comparison : equal; 500 501 // Cache the last call's result. 502 static string lastResult; 503 if (v.empty) 504 { 505 // Return non-null array for blank result to distinguish from 506 // not-present result. 507 lastResult = ""; 508 } 509 else if (!v.equal(lastResult)) 510 { 511 import std.conv : to; 512 lastResult = v.to!string; 513 } 514 return lastResult; 515 } 516 } 517 518 @safe unittest 519 { 520 import std.exception : assertThrown; 521 // New variable 522 environment["std_process"] = "foo"; 523 assert(environment["std_process"] == "foo"); 524 assert("std_process" in environment); 525 526 // Set variable again (also tests length 1 case) 527 environment["std_process"] = "b"; 528 assert(environment["std_process"] == "b"); 529 assert("std_process" in environment); 530 531 // Remove variable 532 environment.remove("std_process"); 533 assert("std_process" !in environment); 534 535 // Remove again, should succeed 536 environment.remove("std_process"); 537 assert("std_process" !in environment); 538 539 // Throw on not found. 540 assertThrown(environment["std_process"]); 541 542 // get() without default value 543 assert(environment.get("std_process") is null); 544 545 // get() with default value 546 assert(environment.get("std_process", "baz") == "baz"); 547 548 // get() on an empty (but present) value 549 environment["std_process"] = ""; 550 auto res = environment.get("std_process"); 551 assert(res !is null); 552 assert(res == ""); 553 assert("std_process" in environment); 554 555 // Important to do the following round-trip after the previous test 556 // because it tests toAA with an empty var 557 558 // Convert to associative array 559 auto aa = environment.toAA(); 560 assert(aa.length > 0); 561 foreach (n, v; aa) 562 { 563 // Wine has some bugs related to environment variables: 564 // - Wine allows the existence of an env. variable with the name 565 // "\0", but GetEnvironmentVariable refuses to retrieve it. 566 // As of 2.067 we filter these out anyway (see comment in toAA). 567 568 assert(v == environment[n]); 569 } 570 571 // ... and back again. 572 foreach (n, v; aa) 573 environment[n] = v; 574 575 // Complete the roundtrip 576 auto aa2 = environment.toAA(); 577 import std.conv : text; 578 assert(aa == aa2, text(aa, " != ", aa2)); 579 assert("std_process" in environment); 580 581 // Setting null must have the same effect as remove 582 environment["std_process"] = null; 583 assert("std_process" !in environment); 584 } 585 586 // https://issues.dlang.org/show_bug.cgi?id=24549 587 @safe unittest 588 { 589 import std.exception : assertThrown; 590 assert(environment.get(null) is null); 591 assertThrown(environment[null]); 592 assert(!(null in environment)); 593 } 594 595 // ============================================================================= 596 // Functions and classes for process management. 597 // ============================================================================= 598 599 /** 600 * Returns the process ID of the current process, 601 * which is guaranteed to be unique on the system. 602 * 603 * Example: 604 * --- 605 * writefln("Current process ID: %d", thisProcessID); 606 * --- 607 */ 608 @property int thisProcessID() @trusted nothrow @nogc //TODO: @safe 609 { 610 version (Windows) return GetCurrentProcessId(); 611 else version (Posix) return core.sys.posix.unistd.getpid(); 612 } 613 614 615 /** 616 * Returns the process ID of the current thread, 617 * which is guaranteed to be unique within the current process. 618 * 619 * Returns: 620 * A $(REF ThreadID, core,thread) value for the calling thread. 621 * 622 * Example: 623 * --- 624 * writefln("Current thread ID: %s", thisThreadID); 625 * --- 626 */ 627 @property ThreadID thisThreadID() @trusted nothrow @nogc //TODO: @safe 628 { 629 version (Windows) 630 return GetCurrentThreadId(); 631 else 632 version (Posix) 633 { 634 import core.sys.posix.pthread : pthread_self; 635 return pthread_self(); 636 } 637 } 638 639 640 @system unittest 641 { 642 int pidA, pidB; 643 ThreadID tidA, tidB; 644 pidA = thisProcessID; 645 tidA = thisThreadID; 646 647 import core.thread; 648 auto t = new Thread({ 649 pidB = thisProcessID; 650 tidB = thisThreadID; 651 }); 652 t.start(); 653 t.join(); 654 655 assert(pidA == pidB); 656 assert(tidA != tidB); 657 } 658 659 660 package(std) string uniqueTempPath() @safe 661 { 662 import std.file : tempDir; 663 import std.path : buildPath; 664 import std.uuid : randomUUID; 665 // Path should contain spaces to test escaping whitespace 666 return buildPath(tempDir(), "std.process temporary file " ~ 667 randomUUID().toString()); 668 } 669 670 671 version (iOSDerived) {} 672 else: 673 674 /** 675 Spawns a new process, optionally assigning it an arbitrary set of standard 676 input, output, and error streams. 677 678 The function returns immediately, leaving the child process to execute 679 in parallel with its parent. It is recommended to always call $(LREF wait) 680 on the returned $(LREF Pid) unless the process was spawned with 681 `Config.detached` flag, as detailed in the documentation for `wait`. 682 683 Command_line: 684 There are four overloads of this function. The first two take an array 685 of strings, `args`, which should contain the program name as the 686 zeroth element and any command-line arguments in subsequent elements. 687 The third and fourth versions are included for convenience, and may be 688 used when there are no command-line arguments. They take a single string, 689 `program`, which specifies the program name. 690 691 Unless a directory is specified in `args[0]` or `program`, 692 `spawnProcess` will search for the program in a platform-dependent 693 manner. On POSIX systems, it will look for the executable in the 694 directories listed in the PATH environment variable, in the order 695 they are listed. On Windows, it will search for the executable in 696 the following sequence: 697 $(OL 698 $(LI The directory from which the application loaded.) 699 $(LI The current directory for the parent process.) 700 $(LI The 32-bit Windows system directory.) 701 $(LI The 16-bit Windows system directory.) 702 $(LI The Windows directory.) 703 $(LI The directories listed in the PATH environment variable.) 704 ) 705 --- 706 // Run an executable called "prog" located in the current working 707 // directory: 708 auto pid = spawnProcess("./prog"); 709 scope(exit) wait(pid); 710 // We can do something else while the program runs. The scope guard 711 // ensures that the process is waited for at the end of the scope. 712 ... 713 714 // Run DMD on the file "myprog.d", specifying a few compiler switches: 715 auto dmdPid = spawnProcess(["dmd", "-O", "-release", "-inline", "myprog.d" ]); 716 if (wait(dmdPid) != 0) 717 writeln("Compilation failed!"); 718 --- 719 720 Environment_variables: 721 By default, the child process inherits the environment of the parent 722 process, along with any additional variables specified in the `env` 723 parameter. If the same variable exists in both the parent's environment 724 and in `env`, the latter takes precedence. 725 726 If the $(LREF Config.newEnv) flag is set in `config`, the child 727 process will $(I not) inherit the parent's environment. Its entire 728 environment will then be determined by `env`. 729 --- 730 wait(spawnProcess("myapp", ["foo" : "bar"], Config.newEnv)); 731 --- 732 733 Standard_streams: 734 The optional arguments `stdin`, `stdout` and `stderr` may 735 be used to assign arbitrary $(REF File, std,stdio) objects as the standard 736 input, output and error streams, respectively, of the child process. The 737 former must be opened for reading, while the latter two must be opened for 738 writing. The default is for the child process to inherit the standard 739 streams of its parent. 740 --- 741 // Run DMD on the file myprog.d, logging any error messages to a 742 // file named errors.log. 743 auto logFile = File("errors.log", "w"); 744 auto pid = spawnProcess(["dmd", "myprog.d"], 745 std.stdio.stdin, 746 std.stdio.stdout, 747 logFile); 748 if (wait(pid) != 0) 749 writeln("Compilation failed. See errors.log for details."); 750 --- 751 752 Note that if you pass a `File` object that is $(I not) 753 one of the standard input/output/error streams of the parent process, 754 that stream will by default be $(I closed) in the parent process when 755 this function returns. See the $(LREF Config) documentation below for 756 information about how to disable this behaviour. 757 758 Beware of buffering issues when passing `File` objects to 759 `spawnProcess`. The child process will inherit the low-level raw 760 read/write offset associated with the underlying file descriptor, but 761 it will not be aware of any buffered data. In cases where this matters 762 (e.g. when a file should be aligned before being passed on to the 763 child process), it may be a good idea to use unbuffered streams, or at 764 least ensure all relevant buffers are flushed. 765 766 Params: 767 args = An array which contains the program name as the zeroth element 768 and any command-line arguments in the following elements. 769 stdin = The standard input stream of the child process. 770 This can be any $(REF File, std,stdio) that is opened for reading. 771 By default the child process inherits the parent's input 772 stream. 773 stdout = The standard output stream of the child process. 774 This can be any $(REF File, std,stdio) that is opened for writing. 775 By default the child process inherits the parent's output stream. 776 stderr = The standard error stream of the child process. 777 This can be any $(REF File, std,stdio) that is opened for writing. 778 By default the child process inherits the parent's error stream. 779 env = Additional environment variables for the child process. 780 config = Flags that control process creation. See $(LREF Config) 781 for an overview of available flags. 782 workDir = The working directory for the new process. 783 By default the child process inherits the parent's working 784 directory. 785 786 Returns: 787 A $(LREF Pid) object that corresponds to the spawned process. 788 789 Throws: 790 $(LREF ProcessException) on failure to start the process.$(BR) 791 $(REF StdioException, std,stdio) on failure to pass one of the streams 792 to the child process (Windows only).$(BR) 793 $(REF RangeError, core,exception) if `args` is empty. 794 */ 795 Pid spawnProcess(scope const(char[])[] args, 796 File stdin = std.stdio.stdin, 797 File stdout = std.stdio.stdout, 798 File stderr = std.stdio.stderr, 799 const string[string] env = null, 800 Config config = Config.none, 801 scope const char[] workDir = null) 802 @safe 803 { 804 version (Windows) 805 { 806 const commandLine = escapeShellArguments(args); 807 const program = args.length ? args[0] : null; 808 return spawnProcessWin(commandLine, program, stdin, stdout, stderr, env, config, workDir); 809 } 810 else version (Posix) 811 { 812 return spawnProcessPosix(args, stdin, stdout, stderr, env, config, workDir); 813 } 814 else 815 static assert(0); 816 } 817 818 /// ditto 819 Pid spawnProcess(scope const(char[])[] args, 820 const string[string] env, 821 Config config = Config.none, 822 scope const(char)[] workDir = null) 823 @trusted // TODO: Should be @safe 824 { 825 return spawnProcess(args, 826 std.stdio.stdin, 827 std.stdio.stdout, 828 std.stdio.stderr, 829 env, 830 config, 831 workDir); 832 } 833 834 /// ditto 835 Pid spawnProcess(scope const(char)[] program, 836 File stdin = std.stdio.stdin, 837 File stdout = std.stdio.stdout, 838 File stderr = std.stdio.stderr, 839 const string[string] env = null, 840 Config config = Config.none, 841 scope const(char)[] workDir = null) 842 @trusted 843 { 844 return spawnProcess((&program)[0 .. 1], 845 stdin, stdout, stderr, env, config, workDir); 846 } 847 848 /// ditto 849 Pid spawnProcess(scope const(char)[] program, 850 const string[string] env, 851 Config config = Config.none, 852 scope const(char)[] workDir = null) 853 @trusted 854 { 855 return spawnProcess((&program)[0 .. 1], env, config, workDir); 856 } 857 858 version (Posix) private enum InternalError : ubyte 859 { 860 noerror, 861 exec, 862 chdir, 863 getrlimit, 864 doubleFork, 865 malloc, 866 preExec, 867 closefds_dup2, 868 } 869 870 /* 871 Implementation of spawnProcess() for POSIX. 872 873 envz should be a zero-terminated array of zero-terminated strings 874 on the form "var=value". 875 */ 876 version (Posix) 877 private Pid spawnProcessPosix(scope const(char[])[] args, 878 File stdin, 879 File stdout, 880 File stderr, 881 scope const string[string] env, 882 Config config, 883 scope const(char)[] workDir) 884 @trusted // TODO: Should be @safe 885 { 886 import core.exception : RangeError; 887 import std.algorithm.searching : any; 888 import std.conv : text; 889 import std.path : isDirSeparator; 890 import std.string : toStringz; 891 892 if (args.empty) throw new RangeError(); 893 const(char)[] name = args[0]; 894 if (!any!isDirSeparator(name)) 895 { 896 name = searchPathFor(name); 897 if (name is null) 898 throw new ProcessException(text("Executable file not found: ", args[0])); 899 } 900 901 // Convert program name and arguments to C-style strings. 902 auto argz = new const(char)*[args.length+1]; 903 argz[0] = toStringz(name); 904 foreach (i; 1 .. args.length) argz[i] = toStringz(args[i]); 905 argz[$-1] = null; 906 907 // Prepare environment. 908 auto envz = createEnv(env, !(config.flags & Config.Flags.newEnv)); 909 910 // Open the working directory. 911 // We use open in the parent and fchdir in the child 912 // so that most errors (directory doesn't exist, not a directory) 913 // can be propagated as exceptions before forking. 914 int workDirFD = -1; 915 scope(exit) if (workDirFD >= 0) close(workDirFD); 916 if (workDir.length) 917 { 918 import core.sys.posix.fcntl : open, O_RDONLY, stat_t, fstat, S_ISDIR; 919 workDirFD = open(workDir.tempCString(), O_RDONLY); 920 if (workDirFD < 0) 921 throw ProcessException.newFromErrno("Failed to open working directory"); 922 stat_t s; 923 if (fstat(workDirFD, &s) < 0) 924 throw ProcessException.newFromErrno("Failed to stat working directory"); 925 if (!S_ISDIR(s.st_mode)) 926 throw new ProcessException("Not a directory: " ~ cast(string) workDir); 927 } 928 929 static int getFD(ref File f) { return core.stdc.stdio.fileno(f.getFP()); } 930 931 // Get the file descriptors of the streams. 932 // These could potentially be invalid, but that is OK. If so, later calls 933 // to dup2() and close() will just silently fail without causing any harm. 934 auto stdinFD = getFD(stdin); 935 auto stdoutFD = getFD(stdout); 936 auto stderrFD = getFD(stderr); 937 938 // We don't have direct access to the errors that may happen in a child process. 939 // So we use this pipe to deliver them. 940 int[2] forkPipe; 941 if (core.sys.posix.unistd.pipe(forkPipe) == 0) 942 setCLOEXEC(forkPipe[1], true); 943 else 944 throw ProcessException.newFromErrno("Could not create pipe to check startup of child"); 945 scope(exit) close(forkPipe[0]); 946 947 /* 948 To create detached process, we use double fork technique 949 but we don't have a direct access to the second fork pid from the caller side thus use a pipe. 950 We also can't reuse forkPipe for that purpose 951 because we can't predict the order in which pid and possible error will be written 952 since the first and the second forks will run in parallel. 953 */ 954 int[2] pidPipe; 955 if (config.flags & Config.Flags.detached) 956 { 957 if (core.sys.posix.unistd.pipe(pidPipe) != 0) 958 throw ProcessException.newFromErrno("Could not create pipe to get process pid"); 959 setCLOEXEC(pidPipe[1], true); 960 } 961 scope(exit) if (config.flags & Config.Flags.detached) close(pidPipe[0]); 962 963 static void abortOnError(int forkPipeOut, InternalError errorType, int error) nothrow 964 { 965 core.sys.posix.unistd.write(forkPipeOut, &errorType, errorType.sizeof); 966 core.sys.posix.unistd.write(forkPipeOut, &error, error.sizeof); 967 close(forkPipeOut); 968 core.sys.posix.unistd._exit(1); 969 assert(0); 970 } 971 972 void closePipeWriteEnds() 973 { 974 close(forkPipe[1]); 975 if (config.flags & Config.Flags.detached) 976 close(pidPipe[1]); 977 } 978 979 auto id = core.sys.posix.unistd.fork(); 980 if (id < 0) 981 { 982 closePipeWriteEnds(); 983 throw ProcessException.newFromErrno("Failed to spawn new process"); 984 } 985 986 void forkChild() nothrow @nogc 987 { 988 static import core.sys.posix.stdio; 989 990 // Child process 991 992 // no need for the read end of pipe on child side 993 if (config.flags & Config.Flags.detached) 994 close(pidPipe[0]); 995 close(forkPipe[0]); 996 auto forkPipeOut = forkPipe[1]; 997 immutable pidPipeOut = pidPipe[1]; 998 999 // Set the working directory. 1000 if (workDirFD >= 0) 1001 { 1002 if (fchdir(workDirFD) < 0) 1003 { 1004 // Fail. It is dangerous to run a program 1005 // in an unexpected working directory. 1006 abortOnError(forkPipeOut, InternalError.chdir, .errno); 1007 } 1008 close(workDirFD); 1009 } 1010 1011 void execProcess() 1012 { 1013 // Redirect streams and close the old file descriptors. 1014 // In the case that stderr is redirected to stdout, we need 1015 // to backup the file descriptor since stdout may be redirected 1016 // as well. 1017 if (stderrFD == STDOUT_FILENO) stderrFD = dup(stderrFD); 1018 dup2(stdinFD, STDIN_FILENO); 1019 dup2(stdoutFD, STDOUT_FILENO); 1020 dup2(stderrFD, STDERR_FILENO); 1021 1022 // Ensure that the standard streams aren't closed on execute, and 1023 // optionally close all other file descriptors. 1024 setCLOEXEC(STDIN_FILENO, false); 1025 setCLOEXEC(STDOUT_FILENO, false); 1026 setCLOEXEC(STDERR_FILENO, false); 1027 1028 if (!(config.flags & Config.Flags.inheritFDs)) 1029 { 1030 version (FreeBSD) 1031 import core.sys.freebsd.unistd : closefrom; 1032 else version (OpenBSD) 1033 import core.sys.openbsd.unistd : closefrom; 1034 1035 static if (!__traits(compiles, closefrom)) 1036 { 1037 void fallback (int lowfd) 1038 { 1039 import core.sys.posix.dirent : dirent, opendir, readdir, closedir, DIR; 1040 import core.sys.posix.unistd : close; 1041 import core.sys.posix.stdlib : atoi, malloc, free; 1042 import core.sys.posix.sys.resource : rlimit, getrlimit, RLIMIT_NOFILE; 1043 1044 // Get the maximum number of file descriptors that could be open. 1045 rlimit r; 1046 if (getrlimit(RLIMIT_NOFILE, &r) != 0) 1047 abortOnError(forkPipeOut, InternalError.getrlimit, .errno); 1048 1049 immutable maxDescriptors = cast(int) r.rlim_cur; 1050 1051 // Missing druntime declaration 1052 pragma(mangle, "dirfd") 1053 extern(C) nothrow @nogc int dirfd(DIR* dir); 1054 1055 DIR* dir = null; 1056 1057 // We read from /dev/fd or /proc/self/fd only if the limit is high enough 1058 if (maxDescriptors > 128*1024) 1059 { 1060 // Try to open the directory /dev/fd or /proc/self/fd 1061 dir = opendir("/dev/fd"); 1062 if (dir is null) dir = opendir("/proc/self/fd"); 1063 } 1064 1065 // If we have a directory, close all file descriptors except stdin, stdout, and stderr 1066 if (dir) 1067 { 1068 scope(exit) closedir(dir); 1069 1070 int opendirfd = dirfd(dir); 1071 1072 // Iterate over all file descriptors 1073 while (true) 1074 { 1075 dirent* entry = readdir(dir); 1076 1077 if (entry is null) break; 1078 if (entry.d_name[0] == '.') continue; 1079 1080 int fd = atoi(cast(char*) entry.d_name); 1081 1082 // Don't close stdin, stdout, stderr, or the directory file descriptor 1083 if (fd < lowfd || fd == opendirfd) continue; 1084 1085 close(fd); 1086 } 1087 } 1088 else 1089 { 1090 // This is going to allocate 8 bytes for each possible file descriptor from lowfd to r.rlim_cur 1091 if (maxDescriptors <= 128*1024) 1092 { 1093 // NOTE: malloc() and getrlimit() are not on the POSIX async 1094 // signal safe functions list, but practically this should 1095 // not be a problem. Java VM and CPython also use malloc() 1096 // in its own implementation via opendir(). 1097 import core.stdc.stdlib : malloc; 1098 import core.sys.posix.poll : pollfd, poll, POLLNVAL; 1099 1100 immutable maxToClose = maxDescriptors - lowfd; 1101 1102 // Call poll() to see which ones are actually open: 1103 auto pfds = cast(pollfd*) malloc(pollfd.sizeof * maxToClose); 1104 if (pfds is null) 1105 { 1106 abortOnError(forkPipeOut, InternalError.malloc, .errno); 1107 } 1108 1109 foreach (i; 0 .. maxToClose) 1110 { 1111 pfds[i].fd = i + lowfd; 1112 pfds[i].events = 0; 1113 pfds[i].revents = 0; 1114 } 1115 1116 if (poll(pfds, maxToClose, 0) < 0) 1117 // couldn't use poll, use the slow path. 1118 goto LslowClose; 1119 1120 foreach (i; 0 .. maxToClose) 1121 { 1122 // POLLNVAL will be set if the file descriptor is invalid. 1123 if (!(pfds[i].revents & POLLNVAL)) close(pfds[i].fd); 1124 } 1125 } 1126 else 1127 { 1128 LslowClose: 1129 // Fall back to closing everything. 1130 foreach (i; lowfd .. maxDescriptors) 1131 { 1132 close(i); 1133 } 1134 } 1135 } 1136 } 1137 1138 // closefrom may not be available on the version of glibc we build against. 1139 // Until we find a way to perform this check we will try to use dlsym to 1140 // check for the function. See: https://github.com/dlang/phobos/pull/9048 1141 version (CRuntime_Glibc) 1142 { 1143 void closefrom (int lowfd) { 1144 static bool tryGlibcClosefrom (int lowfd) { 1145 import core.sys.posix.dlfcn : dlopen, dlclose, dlsym, dlerror, RTLD_LAZY; 1146 1147 void *handle = dlopen("libc.so.6", RTLD_LAZY); 1148 if (!handle) 1149 return false; 1150 scope(exit) dlclose(handle); 1151 1152 // Clear errors 1153 dlerror(); 1154 alias closefromT = extern(C) void function(int) @nogc @system nothrow; 1155 auto closefrom = cast(closefromT) dlsym(handle, "closefrom"); 1156 if (dlerror()) 1157 return false; 1158 1159 closefrom(lowfd); 1160 return true; 1161 } 1162 1163 if (!tryGlibcClosefrom(lowfd)) 1164 fallback(lowfd); 1165 } 1166 } 1167 else 1168 alias closefrom = fallback; 1169 } 1170 1171 // We need to close all open file descriptors excluding std{in,out,err} 1172 // and forkPipeOut because we still need it. 1173 // Since the various libc's provide us with `closefrom` move forkPipeOut 1174 // to position 3, right after STDERR_FILENO, and close all FDs following that. 1175 if (dup2(forkPipeOut, 3) == -1) 1176 abortOnError(forkPipeOut, InternalError.closefds_dup2, .errno); 1177 forkPipeOut = 3; 1178 // forkPipeOut needs to be closed after we call `exec`. 1179 setCLOEXEC(forkPipeOut, true); 1180 closefrom(forkPipeOut + 1); 1181 } 1182 else // This is already done if we don't inherit descriptors. 1183 { 1184 // Close the old file descriptors, unless they are 1185 // either of the standard streams. 1186 if (stdinFD > STDERR_FILENO) close(stdinFD); 1187 if (stdoutFD > STDERR_FILENO) close(stdoutFD); 1188 if (stderrFD > STDERR_FILENO) close(stderrFD); 1189 } 1190 1191 if (config.preExecFunction !is null) 1192 { 1193 if (config.preExecFunction() != true) 1194 { 1195 abortOnError(forkPipeOut, InternalError.preExec, .errno); 1196 } 1197 } 1198 1199 if (config.preExecDelegate !is null) 1200 { 1201 if (config.preExecDelegate() != true) 1202 { 1203 abortOnError(forkPipeOut, InternalError.preExec, .errno); 1204 } 1205 } 1206 1207 // Execute program. 1208 core.sys.posix.unistd.execve(argz[0], argz.ptr, envz is null ? getEnvironPtr : envz); 1209 1210 // If execution fails, exit as quickly as possible. 1211 abortOnError(forkPipeOut, InternalError.exec, .errno); 1212 } 1213 1214 if (config.flags & Config.Flags.detached) 1215 { 1216 auto secondFork = core.sys.posix.unistd.fork(); 1217 if (secondFork == 0) 1218 { 1219 close(pidPipeOut); 1220 execProcess(); 1221 } 1222 else if (secondFork == -1) 1223 { 1224 auto secondForkErrno = .errno; 1225 close(pidPipeOut); 1226 abortOnError(forkPipeOut, InternalError.doubleFork, secondForkErrno); 1227 } 1228 else 1229 { 1230 core.sys.posix.unistd.write(pidPipeOut, &secondFork, pid_t.sizeof); 1231 close(pidPipeOut); 1232 close(forkPipeOut); 1233 _exit(0); 1234 } 1235 } 1236 else 1237 { 1238 execProcess(); 1239 } 1240 } 1241 1242 if (id == 0) 1243 { 1244 forkChild(); 1245 assert(0); 1246 } 1247 else 1248 { 1249 closePipeWriteEnds(); 1250 1251 T retryInterrupted(T)(scope T delegate() syscall) 1252 { 1253 import core.stdc.errno : errno, EINTR; 1254 T result; 1255 do 1256 result = syscall(); 1257 while (result == -1 && .errno == EINTR); 1258 return result; 1259 } 1260 1261 auto status = InternalError.noerror; 1262 auto readExecResult = retryInterrupted(() => core.sys.posix.unistd.read(forkPipe[0], &status, status.sizeof)); 1263 // Save error number just in case if subsequent "waitpid" fails and overrides errno 1264 immutable lastError = .errno; 1265 1266 if (config.flags & Config.Flags.detached) 1267 { 1268 // Forked child exits right after creating second fork. So it should be safe to wait here. 1269 import core.sys.posix.sys.wait : waitpid; 1270 int waitResult; 1271 retryInterrupted(() => waitpid(id, &waitResult, 0)); 1272 } 1273 1274 if (readExecResult == -1) 1275 throw ProcessException.newFromErrno(lastError, "Could not read from pipe to get child status"); 1276 1277 bool owned = true; 1278 if (status != InternalError.noerror) 1279 { 1280 int error; 1281 readExecResult = retryInterrupted(() => read(forkPipe[0], &error, error.sizeof)); 1282 string errorMsg; 1283 final switch (status) 1284 { 1285 case InternalError.chdir: 1286 errorMsg = "Failed to set working directory"; 1287 break; 1288 case InternalError.getrlimit: 1289 errorMsg = "getrlimit failed"; 1290 break; 1291 case InternalError.exec: 1292 errorMsg = "Failed to execute '" ~ cast(string) name ~ "'"; 1293 break; 1294 case InternalError.doubleFork: 1295 // Can happen only when starting detached process 1296 assert(config.flags & Config.Flags.detached); 1297 errorMsg = "Failed to fork twice"; 1298 break; 1299 case InternalError.malloc: 1300 errorMsg = "Failed to allocate memory"; 1301 break; 1302 case InternalError.preExec: 1303 errorMsg = "Failed to execute preExecFunction or preExecDelegate"; 1304 break; 1305 case InternalError.closefds_dup2: 1306 assert(!(config.flags & Config.Flags.inheritFDs)); 1307 errorMsg = "Failed to close inherited file descriptors"; 1308 break; 1309 case InternalError.noerror: 1310 assert(false); 1311 } 1312 if (readExecResult == error.sizeof) 1313 throw ProcessException.newFromErrno(error, errorMsg); 1314 throw new ProcessException(errorMsg); 1315 } 1316 else if (config.flags & Config.Flags.detached) 1317 { 1318 owned = false; 1319 if (read(pidPipe[0], &id, id.sizeof) != id.sizeof) 1320 throw ProcessException.newFromErrno("Could not read from pipe to get detached process id"); 1321 } 1322 1323 // Parent process: Close streams and return. 1324 if (!(config.flags & Config.Flags.retainStdin ) && stdinFD > STDERR_FILENO 1325 && stdinFD != getFD(std.stdio.stdin )) 1326 stdin.close(); 1327 if (!(config.flags & Config.Flags.retainStdout) && stdoutFD > STDERR_FILENO 1328 && stdoutFD != getFD(std.stdio.stdout)) 1329 stdout.close(); 1330 if (!(config.flags & Config.Flags.retainStderr) && stderrFD > STDERR_FILENO 1331 && stderrFD != getFD(std.stdio.stderr)) 1332 stderr.close(); 1333 return new Pid(id, owned); 1334 } 1335 } 1336 1337 version (Posix) 1338 @system unittest 1339 { 1340 import std.concurrency : ownerTid, receiveTimeout, send, spawn; 1341 import std.datetime : seconds; 1342 1343 sigset_t ss; 1344 sigemptyset(&ss); 1345 sigaddset(&ss, SIGINT); 1346 pthread_sigmask(SIG_BLOCK, &ss, null); 1347 1348 Config config = { 1349 preExecFunction: () @trusted @nogc nothrow { 1350 // Reset signal handlers 1351 sigset_t ss; 1352 if (sigfillset(&ss) != 0) 1353 { 1354 return false; 1355 } 1356 if (sigprocmask(SIG_UNBLOCK, &ss, null) != 0) 1357 { 1358 return false; 1359 } 1360 return true; 1361 }, 1362 }; 1363 1364 auto pid = spawnProcess(["sleep", "10000"], 1365 std.stdio.stdin, 1366 std.stdio.stdout, 1367 std.stdio.stderr, 1368 null, 1369 config, 1370 null); 1371 scope(failure) 1372 { 1373 kill(pid, SIGKILL); 1374 wait(pid); 1375 } 1376 1377 // kill the spawned process with SIGINT 1378 // and send its return code 1379 spawn((shared Pid pid) { 1380 auto p = cast() pid; 1381 kill(p, SIGINT); 1382 auto code = wait(p); 1383 assert(code < 0); 1384 send(ownerTid, code); 1385 }, cast(shared) pid); 1386 1387 auto received = receiveTimeout(3.seconds, (int) {}); 1388 assert(received); 1389 } 1390 1391 version (Posix) 1392 @system unittest 1393 { 1394 __gshared int j; 1395 foreach (i; 0 .. 3) 1396 { 1397 auto config = Config( 1398 preExecFunction: function() @trusted { 1399 j = 1; 1400 return true; 1401 }, 1402 preExecDelegate: delegate() @trusted { 1403 // j should now be 1, as preExecFunction is called before 1404 // preExecDelegate is. 1405 _Exit(i + j); 1406 return true; 1407 }, 1408 ); 1409 auto pid = spawnProcess(["false"], config: config); 1410 assert(wait(pid) == i + 1); 1411 } 1412 } 1413 1414 /* 1415 Implementation of spawnProcess() for Windows. 1416 1417 commandLine must contain the entire command line, properly 1418 quoted/escaped as required by CreateProcessW(). 1419 1420 envz must be a pointer to a block of UTF-16 characters on the form 1421 "var1=value1\0var2=value2\0...varN=valueN\0\0". 1422 */ 1423 version (Windows) 1424 private Pid spawnProcessWin(scope const(char)[] commandLine, 1425 scope const(char)[] program, 1426 File stdin, 1427 File stdout, 1428 File stderr, 1429 scope const string[string] env, 1430 Config config, 1431 scope const(char)[] workDir) 1432 @trusted 1433 { 1434 import core.exception : RangeError; 1435 import std.conv : text; 1436 1437 if (commandLine.empty) throw new RangeError("Command line is empty"); 1438 1439 // Prepare environment. 1440 auto envz = createEnv(env, !(config.flags & Config.Flags.newEnv)); 1441 1442 // Startup info for CreateProcessW(). 1443 STARTUPINFO_W startinfo; 1444 startinfo.cb = startinfo.sizeof; 1445 static int getFD(ref File f) { return f.isOpen ? f.fileno : -1; } 1446 1447 // Extract file descriptors and HANDLEs from the streams and make the 1448 // handles inheritable. 1449 static void prepareStream(ref File file, DWORD stdHandle, string which, 1450 out int fileDescriptor, out HANDLE handle) 1451 { 1452 enum _NO_CONSOLE_FILENO = cast(HANDLE)-2; 1453 fileDescriptor = getFD(file); 1454 handle = null; 1455 if (fileDescriptor >= 0) 1456 handle = file.windowsHandle; 1457 // Windows GUI applications have a fd but not a valid Windows HANDLE. 1458 if (handle is null || handle == INVALID_HANDLE_VALUE || handle == _NO_CONSOLE_FILENO) 1459 handle = GetStdHandle(stdHandle); 1460 1461 DWORD dwFlags; 1462 if (GetHandleInformation(handle, &dwFlags)) 1463 { 1464 if (!(dwFlags & HANDLE_FLAG_INHERIT)) 1465 { 1466 if (!SetHandleInformation(handle, 1467 HANDLE_FLAG_INHERIT, 1468 HANDLE_FLAG_INHERIT)) 1469 { 1470 throw new StdioException( 1471 "Failed to make "~which~" stream inheritable by child process (" 1472 ~generateSysErrorMsg() ~ ')', 1473 0); 1474 } 1475 } 1476 } 1477 } 1478 int stdinFD = -1, stdoutFD = -1, stderrFD = -1; 1479 prepareStream(stdin, STD_INPUT_HANDLE, "stdin" , stdinFD, startinfo.hStdInput ); 1480 prepareStream(stdout, STD_OUTPUT_HANDLE, "stdout", stdoutFD, startinfo.hStdOutput); 1481 prepareStream(stderr, STD_ERROR_HANDLE, "stderr", stderrFD, startinfo.hStdError ); 1482 1483 if ((startinfo.hStdInput != null && startinfo.hStdInput != INVALID_HANDLE_VALUE) 1484 || (startinfo.hStdOutput != null && startinfo.hStdOutput != INVALID_HANDLE_VALUE) 1485 || (startinfo.hStdError != null && startinfo.hStdError != INVALID_HANDLE_VALUE)) 1486 startinfo.dwFlags = STARTF_USESTDHANDLES; 1487 1488 // Create process. 1489 PROCESS_INFORMATION pi; 1490 DWORD dwCreationFlags = 1491 CREATE_UNICODE_ENVIRONMENT | 1492 ((config.flags & Config.Flags.suppressConsole) ? CREATE_NO_WINDOW : 0); 1493 // workaround until https://issues.dlang.org/show_bug.cgi?id=14696 is fixed 1494 auto pworkDir = workDir.tempCStringW(); 1495 if (!CreateProcessW(null, commandLine.tempCStringW().buffPtr, 1496 null, null, true, dwCreationFlags, 1497 envz, workDir.length ? pworkDir : null, &startinfo, &pi)) 1498 throw ProcessException.newFromLastError("Failed to spawn process \"" ~ cast(string) program ~ '"'); 1499 1500 // figure out if we should close any of the streams 1501 if (!(config.flags & Config.Flags.retainStdin ) && stdinFD > STDERR_FILENO 1502 && stdinFD != getFD(std.stdio.stdin )) 1503 stdin.close(); 1504 if (!(config.flags & Config.Flags.retainStdout) && stdoutFD > STDERR_FILENO 1505 && stdoutFD != getFD(std.stdio.stdout)) 1506 stdout.close(); 1507 if (!(config.flags & Config.Flags.retainStderr) && stderrFD > STDERR_FILENO 1508 && stderrFD != getFD(std.stdio.stderr)) 1509 stderr.close(); 1510 1511 // close the thread handle in the process info structure 1512 CloseHandle(pi.hThread); 1513 if (config.flags & Config.Flags.detached) 1514 { 1515 CloseHandle(pi.hProcess); 1516 return new Pid(pi.dwProcessId); 1517 } 1518 return new Pid(pi.dwProcessId, pi.hProcess); 1519 } 1520 1521 // Converts childEnv to a zero-terminated array of zero-terminated strings 1522 // on the form "name=value", optionally adding those of the current process' 1523 // environment strings that are not present in childEnv. If the parent's 1524 // environment should be inherited without modification, this function 1525 // returns null. 1526 version (Posix) 1527 private const(char*)* createEnv(const string[string] childEnv, 1528 bool mergeWithParentEnv) 1529 { 1530 // Determine the number of strings in the parent's environment. 1531 int parentEnvLength = 0; 1532 auto environ = getEnvironPtr; 1533 if (mergeWithParentEnv) 1534 { 1535 if (childEnv.length == 0) return null; 1536 while (environ[parentEnvLength] != null) ++parentEnvLength; 1537 } 1538 1539 // Convert the "new" variables to C-style strings. 1540 auto envz = new const(char)*[parentEnvLength + childEnv.length + 1]; 1541 int pos = 0; 1542 foreach (var, val; childEnv) 1543 envz[pos++] = (var~'='~val~'\0').ptr; 1544 1545 // Add the parent's environment. 1546 foreach (environStr; environ[0 .. parentEnvLength]) 1547 { 1548 int eqPos = 0; 1549 while (environStr[eqPos] != '=' && environStr[eqPos] != '\0') ++eqPos; 1550 if (environStr[eqPos] != '=') continue; 1551 auto var = environStr[0 .. eqPos]; 1552 if (var in childEnv) continue; 1553 envz[pos++] = environStr; 1554 } 1555 envz[pos] = null; 1556 return envz.ptr; 1557 } 1558 1559 version (Posix) @system unittest 1560 { 1561 auto e1 = createEnv(null, false); 1562 assert(e1 != null && *e1 == null); 1563 1564 auto e2 = createEnv(null, true); 1565 assert(e2 == null); 1566 1567 auto e3 = createEnv(["foo" : "bar", "hello" : "world"], false); 1568 assert(e3 != null && e3[0] != null && e3[1] != null && e3[2] == null); 1569 assert((e3[0][0 .. 8] == "foo=bar\0" && e3[1][0 .. 12] == "hello=world\0") 1570 || (e3[0][0 .. 12] == "hello=world\0" && e3[1][0 .. 8] == "foo=bar\0")); 1571 } 1572 1573 1574 // Converts childEnv to a Windows environment block, which is on the form 1575 // "name1=value1\0name2=value2\0...nameN=valueN\0\0", optionally adding 1576 // those of the current process' environment strings that are not present 1577 // in childEnv. Returns null if the parent's environment should be 1578 // inherited without modification, as this is what is expected by 1579 // CreateProcess(). 1580 version (Windows) 1581 private LPVOID createEnv(const string[string] childEnv, 1582 bool mergeWithParentEnv) 1583 { 1584 if (mergeWithParentEnv && childEnv.length == 0) return null; 1585 import std.array : appender; 1586 import std.uni : toUpper; 1587 auto envz = appender!(wchar[])(); 1588 void put(string var, string val) 1589 { 1590 envz.put(var); 1591 envz.put('='); 1592 envz.put(val); 1593 envz.put(cast(wchar) '\0'); 1594 } 1595 1596 // Add the variables in childEnv, removing them from parentEnv 1597 // if they exist there too. 1598 auto parentEnv = mergeWithParentEnv ? environment.toAA() : null; 1599 foreach (k, v; childEnv) 1600 { 1601 auto uk = toUpper(k); 1602 put(uk, v); 1603 if (uk in parentEnv) parentEnv.remove(uk); 1604 } 1605 1606 // Add remaining parent environment variables. 1607 foreach (k, v; parentEnv) put(k, v); 1608 1609 // Two final zeros are needed in case there aren't any environment vars, 1610 // and the last one does no harm when there are. 1611 envz.put("\0\0"w); 1612 return envz.data.ptr; 1613 } 1614 1615 version (Windows) @system unittest 1616 { 1617 assert(createEnv(null, true) == null); 1618 assert((cast(wchar*) createEnv(null, false))[0 .. 2] == "\0\0"w); 1619 auto e1 = (cast(wchar*) createEnv(["foo":"bar", "ab":"c"], false))[0 .. 14]; 1620 assert(e1 == "FOO=bar\0AB=c\0\0"w || e1 == "AB=c\0FOO=bar\0\0"w); 1621 } 1622 1623 // Searches the PATH variable for the given executable file, 1624 // (checking that it is in fact executable). 1625 version (Posix) 1626 package(std) string searchPathFor(scope const(char)[] executable) 1627 @safe 1628 { 1629 import std.algorithm.iteration : splitter; 1630 import std.conv : to; 1631 import std.path : chainPath; 1632 1633 typeof(return) result; 1634 1635 environment.getImpl("PATH", 1636 (scope const(char)[] path) 1637 { 1638 if (!path) 1639 return; 1640 1641 foreach (dir; splitter(path, ":")) 1642 { 1643 auto execPath = chainPath(dir, executable); 1644 if (isExecutable(execPath)) 1645 { 1646 result = execPath.to!(typeof(result)); 1647 return; 1648 } 1649 } 1650 }); 1651 1652 return result; 1653 } 1654 1655 // Checks whether the file exists and can be executed by the 1656 // current user. 1657 version (Posix) 1658 private bool isExecutable(R)(R path) @trusted nothrow @nogc 1659 if (isSomeFiniteCharInputRange!R) 1660 { 1661 return (access(path.tempCString(), X_OK) == 0); 1662 } 1663 1664 version (Posix) @safe unittest 1665 { 1666 import std.algorithm; 1667 auto lsPath = searchPathFor("ls"); 1668 assert(!lsPath.empty); 1669 assert(lsPath[0] == '/'); 1670 assert(lsPath.endsWith("ls")); 1671 auto unlikely = searchPathFor("lkmqwpoialhggyaofijadsohufoiqezm"); 1672 assert(unlikely is null, "Are you kidding me?"); 1673 } 1674 1675 // Sets or unsets the FD_CLOEXEC flag on the given file descriptor. 1676 version (Posix) 1677 private void setCLOEXEC(int fd, bool on) nothrow @nogc 1678 { 1679 import core.sys.posix.fcntl : fcntl, F_GETFD, FD_CLOEXEC, F_SETFD; 1680 auto flags = fcntl(fd, F_GETFD); 1681 if (flags >= 0) 1682 { 1683 if (on) flags |= FD_CLOEXEC; 1684 else flags &= ~(cast(typeof(flags)) FD_CLOEXEC); 1685 flags = fcntl(fd, F_SETFD, flags); 1686 } 1687 assert(flags != -1 || .errno == EBADF); 1688 } 1689 1690 @system unittest // Command line arguments in spawnProcess(). 1691 { 1692 version (Windows) TestScript prog = 1693 "if not [%~1]==[foo] ( exit 1 ) 1694 if not [%~2]==[bar] ( exit 2 ) 1695 exit 0"; 1696 else version (Posix) TestScript prog = 1697 `if test "$1" != "foo"; then exit 1; fi 1698 if test "$2" != "bar"; then exit 2; fi 1699 exit 0`; 1700 assert(wait(spawnProcess(prog.path)) == 1); 1701 assert(wait(spawnProcess([prog.path])) == 1); 1702 assert(wait(spawnProcess([prog.path, "foo"])) == 2); 1703 assert(wait(spawnProcess([prog.path, "foo", "baz"])) == 2); 1704 assert(wait(spawnProcess([prog.path, "foo", "bar"])) == 0); 1705 } 1706 1707 // test that file descriptors are correctly closed / left open. 1708 // ideally this would be done by the child process making libc 1709 // calls, but we make do... 1710 version (Posix) @system unittest 1711 { 1712 import core.stdc.errno : errno; 1713 import core.sys.posix.fcntl : open, O_RDONLY; 1714 import core.sys.posix.unistd : close; 1715 import std.algorithm.searching : canFind, findSplitBefore; 1716 import std.array : split; 1717 import std.conv : to; 1718 static import std.file; 1719 import std.functional : reverseArgs; 1720 import std.path : buildPath; 1721 1722 auto directory = uniqueTempPath(); 1723 std.file.mkdir(directory); 1724 scope(exit) std.file.rmdirRecurse(directory); 1725 auto path = buildPath(directory, "tmp"); 1726 std.file.write(path, null); 1727 errno = 0; 1728 auto fd = open(path.tempCString, O_RDONLY); 1729 if (fd == -1) 1730 { 1731 import core.stdc.string : strerror; 1732 import std.stdio : stderr; 1733 import std.string : fromStringz; 1734 1735 // For the CI logs 1736 stderr.writefln("%s: could not open '%s': %s", 1737 __FUNCTION__, path, strerror(errno).fromStringz); 1738 // TODO: should we retry here instead? 1739 return; 1740 } 1741 scope(exit) close(fd); 1742 1743 // command >&2 (or any other number) checks whethether that number 1744 // file descriptor is open. 1745 // Can't use this for arbitrary descriptors as many shells only support 1746 // single digit fds. 1747 TestScript testDefaults = `command >&0 && command >&1 && command >&2`; 1748 assert(execute(testDefaults.path).status == 0); 1749 assert(execute(testDefaults.path, null, Config.inheritFDs).status == 0); 1750 1751 // Try a few different methods to check whether there are any 1752 // incorrectly-open files. 1753 void testFDs() 1754 { 1755 // try /proc/<pid>/fd/ on linux 1756 version (linux) 1757 { 1758 TestScript proc = "ls /proc/$$/fd"; 1759 auto procRes = execute(proc.path, null); 1760 if (procRes.status == 0) 1761 { 1762 auto fdStr = fd.to!string; 1763 assert(!procRes.output.split.canFind(fdStr)); 1764 assert(execute(proc.path, null, Config.inheritFDs) 1765 .output.split.canFind(fdStr)); 1766 return; 1767 } 1768 } 1769 1770 // try fuser (might sometimes need permissions) 1771 TestScript fuser = "echo $$ && fuser -f " ~ path; 1772 auto fuserRes = execute(fuser.path, null); 1773 if (fuserRes.status == 0) 1774 { 1775 assert(!reverseArgs!canFind(fuserRes 1776 .output.findSplitBefore("\n").expand)); 1777 assert(reverseArgs!canFind(execute(fuser.path, null, Config.inheritFDs) 1778 .output.findSplitBefore("\n").expand)); 1779 return; 1780 } 1781 1782 // last resort, try lsof (not available on all Posix) 1783 TestScript lsof = "lsof -p$$"; 1784 auto lsofRes = execute(lsof.path, null); 1785 if (lsofRes.status == 0) 1786 { 1787 assert(!lsofRes.output.canFind(path)); 1788 auto lsofOut = execute(lsof.path, null, Config.inheritFDs).output; 1789 if (!lsofOut.canFind(path)) 1790 { 1791 std.stdio.stderr.writeln(__FILE__, ':', __LINE__, 1792 ": Warning: unexpected lsof output:", lsofOut); 1793 } 1794 return; 1795 } 1796 1797 std.stdio.stderr.writeln(__FILE__, ':', __LINE__, 1798 ": Warning: Couldn't find any way to check open files"); 1799 } 1800 testFDs(); 1801 } 1802 1803 @system unittest // Environment variables in spawnProcess(). 1804 { 1805 // We really should use set /a on Windows, but Wine doesn't support it. 1806 version (Windows) TestScript envProg = 1807 `if [%STD_PROCESS_UNITTEST1%] == [1] ( 1808 if [%STD_PROCESS_UNITTEST2%] == [2] (exit 3) 1809 exit 1 1810 ) 1811 if [%STD_PROCESS_UNITTEST1%] == [4] ( 1812 if [%STD_PROCESS_UNITTEST2%] == [2] (exit 6) 1813 exit 4 1814 ) 1815 if [%STD_PROCESS_UNITTEST2%] == [2] (exit 2) 1816 exit 0`; 1817 version (Posix) TestScript envProg = 1818 `if test "$std_process_unittest1" = ""; then 1819 std_process_unittest1=0 1820 fi 1821 if test "$std_process_unittest2" = ""; then 1822 std_process_unittest2=0 1823 fi 1824 exit $(($std_process_unittest1+$std_process_unittest2))`; 1825 1826 environment.remove("std_process_unittest1"); // Just in case. 1827 environment.remove("std_process_unittest2"); 1828 assert(wait(spawnProcess(envProg.path)) == 0); 1829 assert(wait(spawnProcess(envProg.path, null, Config.newEnv)) == 0); 1830 1831 environment["std_process_unittest1"] = "1"; 1832 assert(wait(spawnProcess(envProg.path)) == 1); 1833 assert(wait(spawnProcess(envProg.path, null, Config.newEnv)) == 0); 1834 1835 auto env = ["std_process_unittest2" : "2"]; 1836 assert(wait(spawnProcess(envProg.path, env)) == 3); 1837 assert(wait(spawnProcess(envProg.path, env, Config.newEnv)) == 2); 1838 1839 env["std_process_unittest1"] = "4"; 1840 assert(wait(spawnProcess(envProg.path, env)) == 6); 1841 assert(wait(spawnProcess(envProg.path, env, Config.newEnv)) == 6); 1842 1843 environment.remove("std_process_unittest1"); 1844 assert(wait(spawnProcess(envProg.path, env)) == 6); 1845 assert(wait(spawnProcess(envProg.path, env, Config.newEnv)) == 6); 1846 } 1847 1848 @system unittest // Stream redirection in spawnProcess(). 1849 { 1850 import std.path : buildPath; 1851 import std.string; 1852 version (Windows) TestScript prog = 1853 "set /p INPUT= 1854 echo %INPUT% output %~1 1855 echo %INPUT% error %~2 1>&2 1856 echo done > %3"; 1857 else version (Posix) TestScript prog = 1858 "read INPUT 1859 echo $INPUT output $1 1860 echo $INPUT error $2 >&2 1861 echo done > \"$3\""; 1862 1863 // Pipes 1864 void testPipes(Config config) 1865 { 1866 import std.file : tempDir, exists, remove; 1867 import std.uuid : randomUUID; 1868 import std.exception : collectException; 1869 auto pipei = pipe(); 1870 auto pipeo = pipe(); 1871 auto pipee = pipe(); 1872 auto done = buildPath(tempDir(), randomUUID().toString()); 1873 auto pid = spawnProcess([prog.path, "foo", "bar", done], 1874 pipei.readEnd, pipeo.writeEnd, pipee.writeEnd, null, config); 1875 pipei.writeEnd.writeln("input"); 1876 pipei.writeEnd.flush(); 1877 assert(pipeo.readEnd.readln().chomp() == "input output foo"); 1878 assert(pipee.readEnd.readln().chomp().stripRight() == "input error bar"); 1879 if (config.flags & Config.Flags.detached) 1880 while (!done.exists) Thread.sleep(10.msecs); 1881 else 1882 wait(pid); 1883 while (remove(done).collectException) Thread.sleep(10.msecs); 1884 } 1885 1886 // Files 1887 void testFiles(Config config) 1888 { 1889 import std.ascii : newline; 1890 import std.file : tempDir, exists, remove, readText, write; 1891 import std.uuid : randomUUID; 1892 import std.exception : collectException; 1893 auto pathi = buildPath(tempDir(), randomUUID().toString()); 1894 auto patho = buildPath(tempDir(), randomUUID().toString()); 1895 auto pathe = buildPath(tempDir(), randomUUID().toString()); 1896 write(pathi, "INPUT" ~ newline); 1897 auto filei = File(pathi, "r"); 1898 auto fileo = File(patho, "w"); 1899 auto filee = File(pathe, "w"); 1900 auto done = buildPath(tempDir(), randomUUID().toString()); 1901 auto pid = spawnProcess([prog.path, "bar", "baz", done], filei, fileo, filee, null, config); 1902 if (config.flags & Config.Flags.detached) 1903 while (!done.exists) Thread.sleep(10.msecs); 1904 else 1905 wait(pid); 1906 assert(readText(patho).chomp() == "INPUT output bar"); 1907 assert(readText(pathe).chomp().stripRight() == "INPUT error baz"); 1908 while (remove(pathi).collectException) Thread.sleep(10.msecs); 1909 while (remove(patho).collectException) Thread.sleep(10.msecs); 1910 while (remove(pathe).collectException) Thread.sleep(10.msecs); 1911 while (remove(done).collectException) Thread.sleep(10.msecs); 1912 } 1913 1914 testPipes(Config.none); 1915 testFiles(Config.none); 1916 testPipes(Config.detached); 1917 testFiles(Config.detached); 1918 } 1919 1920 @system unittest // Error handling in spawnProcess() 1921 { 1922 import std.algorithm.searching : canFind; 1923 import std.exception : assertThrown, collectExceptionMsg; 1924 1925 static void testNotFoundException(string program) 1926 { 1927 assert(collectExceptionMsg!ProcessException(spawnProcess(program)).canFind(program)); 1928 assert(collectExceptionMsg!ProcessException(spawnProcess(program, null, Config.detached)).canFind(program)); 1929 } 1930 testNotFoundException("ewrgiuhrifuheiohnmnvqweoijwf"); 1931 testNotFoundException("./rgiuhrifuheiohnmnvqweoijwf"); 1932 1933 // can't execute malformed file with executable permissions 1934 version (Posix) 1935 { 1936 import std.path : buildPath; 1937 import std.file : remove, write, setAttributes, tempDir; 1938 import core.sys.posix.sys.stat : S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP, S_IROTH, S_IXOTH; 1939 import std.conv : to; 1940 string deleteme = buildPath(tempDir(), "deleteme.std.process.unittest.pid") ~ to!string(thisProcessID); 1941 write(deleteme, ""); 1942 scope(exit) remove(deleteme); 1943 setAttributes(deleteme, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); 1944 assertThrown!ProcessException(spawnProcess(deleteme)); 1945 assertThrown!ProcessException(spawnProcess(deleteme, null, Config.detached)); 1946 } 1947 } 1948 1949 @system unittest // Specifying a working directory. 1950 { 1951 import std.path; 1952 import std.file; 1953 TestScript prog = "echo foo>bar"; 1954 1955 auto directory = uniqueTempPath(); 1956 mkdir(directory); 1957 scope(exit) rmdirRecurse(directory); 1958 1959 auto pid = spawnProcess([prog.path], null, Config.none, directory); 1960 wait(pid); 1961 assert(exists(buildPath(directory, "bar"))); 1962 } 1963 1964 @system unittest // Specifying a bad working directory. 1965 { 1966 import std.exception : assertThrown; 1967 import std.file; 1968 TestScript prog = "echo"; 1969 1970 auto directory = uniqueTempPath(); 1971 assertThrown!ProcessException(spawnProcess([prog.path], null, Config.none, directory)); 1972 assertThrown!ProcessException(spawnProcess([prog.path], null, Config.detached, directory)); 1973 1974 std.file.write(directory, "foo"); 1975 scope(exit) remove(directory); 1976 assertThrown!ProcessException(spawnProcess([prog.path], null, Config.none, directory)); 1977 assertThrown!ProcessException(spawnProcess([prog.path], null, Config.detached, directory)); 1978 1979 // can't run in directory if user does not have search permission on this directory 1980 version (Posix) 1981 { 1982 if (core.sys.posix.unistd.getuid() != 0) 1983 { 1984 import core.sys.posix.sys.stat : S_IRUSR; 1985 auto directoryNoSearch = uniqueTempPath(); 1986 mkdir(directoryNoSearch); 1987 scope(exit) rmdirRecurse(directoryNoSearch); 1988 setAttributes(directoryNoSearch, S_IRUSR); 1989 assertThrown!ProcessException(spawnProcess(prog.path, null, Config.none, directoryNoSearch)); 1990 assertThrown!ProcessException(spawnProcess(prog.path, null, Config.detached, directoryNoSearch)); 1991 } 1992 } 1993 } 1994 1995 @system unittest // Specifying empty working directory. 1996 { 1997 TestScript prog = ""; 1998 1999 string directory = ""; 2000 assert(directory.ptr && !directory.length); 2001 spawnProcess([prog.path], null, Config.none, directory).wait(); 2002 } 2003 2004 // Reopening the standard streams (https://issues.dlang.org/show_bug.cgi?id=13258) 2005 @system unittest 2006 { 2007 import std.string; 2008 import std.file; 2009 void fun() 2010 { 2011 spawnShell("echo foo").wait(); 2012 spawnShell("echo bar").wait(); 2013 } 2014 2015 auto tmpFile = uniqueTempPath(); 2016 scope(exit) if (exists(tmpFile)) remove(tmpFile); 2017 2018 { 2019 auto oldOut = std.stdio.stdout; 2020 scope(exit) std.stdio.stdout = oldOut; 2021 2022 std.stdio.stdout = File(tmpFile, "w"); 2023 fun(); 2024 std.stdio.stdout.close(); 2025 } 2026 2027 auto lines = readText(tmpFile).splitLines(); 2028 assert(lines == ["foo", "bar"]); 2029 } 2030 2031 // MSVCRT workaround (https://issues.dlang.org/show_bug.cgi?id=14422) 2032 version (Windows) 2033 @system unittest 2034 { 2035 auto fn = uniqueTempPath(); 2036 scope(exit) if (exists(fn)) remove(fn); 2037 std.file.write(fn, "AAAAAAAAAA"); 2038 2039 auto f = File(fn, "a"); 2040 spawnProcess(["cmd", "/c", "echo BBBBB"], std.stdio.stdin, f).wait(); 2041 2042 auto data = readText(fn); 2043 assert(data == "AAAAAAAAAABBBBB\r\n", data); 2044 } 2045 2046 // https://issues.dlang.org/show_bug.cgi?id=20765 2047 // Test that running processes with relative path works in conjunction 2048 // with indicating a workDir. 2049 version (Posix) @system unittest 2050 { 2051 import std.file : mkdir, write, setAttributes, rmdirRecurse; 2052 import std.conv : octal; 2053 2054 auto dir = uniqueTempPath(); 2055 mkdir(dir); 2056 scope(exit) rmdirRecurse(dir); 2057 write(dir ~ "/program", "#!/bin/sh\necho Hello"); 2058 setAttributes(dir ~ "/program", octal!700); 2059 2060 assert(execute(["./program"], null, Config.none, size_t.max, dir).output == "Hello\n"); 2061 } 2062 2063 /** 2064 A variation on $(LREF spawnProcess) that runs the given _command through 2065 the current user's preferred _command interpreter (aka. shell). 2066 2067 The string `command` is passed verbatim to the shell, and is therefore 2068 subject to its rules about _command structure, argument/filename quoting 2069 and escaping of special characters. 2070 The path to the shell executable defaults to $(LREF nativeShell). 2071 2072 In all other respects this function works just like `spawnProcess`. 2073 Please refer to the $(LREF spawnProcess) documentation for descriptions 2074 of the other function parameters, the return value and any exceptions 2075 that may be thrown. 2076 --- 2077 // Run the command/program "foo" on the file named "my file.txt", and 2078 // redirect its output into foo.log. 2079 auto pid = spawnShell(`foo "my file.txt" > foo.log`); 2080 wait(pid); 2081 --- 2082 2083 See_also: 2084 $(LREF escapeShellCommand), which may be helpful in constructing a 2085 properly quoted and escaped shell _command line for the current platform. 2086 */ 2087 Pid spawnShell(scope const(char)[] command, 2088 File stdin = std.stdio.stdin, 2089 File stdout = std.stdio.stdout, 2090 File stderr = std.stdio.stderr, 2091 scope const string[string] env = null, 2092 Config config = Config.none, 2093 scope const(char)[] workDir = null, 2094 scope string shellPath = nativeShell) 2095 @trusted // See reason below 2096 { 2097 version (Windows) 2098 { 2099 // CMD does not parse its arguments like other programs. 2100 // It does not use CommandLineToArgvW. 2101 // Instead, it treats the first and last quote specially. 2102 // See CMD.EXE /? for details. 2103 const commandLine = escapeShellFileName(shellPath) 2104 ~ ` ` ~ shellSwitch ~ ` "` ~ command ~ `"`; 2105 return spawnProcessWin(commandLine, shellPath, stdin, stdout, stderr, env, config, workDir); 2106 } 2107 else version (Posix) 2108 { 2109 const(char)[][3] args; 2110 args[0] = shellPath; 2111 args[1] = shellSwitch; 2112 args[2] = command; 2113 /* The passing of args converts the static array, which is initialized with `scope` pointers, 2114 * to a dynamic array, which is also a scope parameter. So, it is a scope pointer to a 2115 * scope pointer, which although is safely used here, D doesn't allow transitive scope. 2116 * See https://github.com/dlang/dmd/pull/10951 2117 */ 2118 return spawnProcessPosix(args, stdin, stdout, stderr, env, config, workDir); 2119 } 2120 else 2121 static assert(0); 2122 } 2123 2124 /// ditto 2125 Pid spawnShell(scope const(char)[] command, 2126 scope const string[string] env, 2127 Config config = Config.none, 2128 scope const(char)[] workDir = null, 2129 scope string shellPath = nativeShell) 2130 @trusted // TODO: Should be @safe 2131 { 2132 return spawnShell(command, 2133 std.stdio.stdin, 2134 std.stdio.stdout, 2135 std.stdio.stderr, 2136 env, 2137 config, 2138 workDir, 2139 shellPath); 2140 } 2141 2142 @system unittest 2143 { 2144 version (Windows) 2145 auto cmd = "echo %FOO%"; 2146 else version (Posix) 2147 auto cmd = "echo $foo"; 2148 import std.file; 2149 auto tmpFile = uniqueTempPath(); 2150 scope(exit) if (exists(tmpFile)) remove(tmpFile); 2151 auto redir = "> \""~tmpFile~'"'; 2152 auto env = ["foo" : "bar"]; 2153 assert(wait(spawnShell(cmd~redir, env)) == 0); 2154 auto f = File(tmpFile, "a"); 2155 version (CRuntime_Microsoft) f.seek(0, SEEK_END); // MSVCRT probably seeks to the end when writing, not before 2156 assert(wait(spawnShell(cmd, std.stdio.stdin, f, std.stdio.stderr, env)) == 0); 2157 f.close(); 2158 auto output = std.file.readText(tmpFile); 2159 assert(output == "bar\nbar\n" || output == "bar\r\nbar\r\n"); 2160 } 2161 2162 version (Windows) 2163 @system unittest 2164 { 2165 import std.string; 2166 import std.conv : text; 2167 TestScript prog = "echo %0 %*"; 2168 auto outputFn = uniqueTempPath(); 2169 scope(exit) if (exists(outputFn)) remove(outputFn); 2170 auto args = [`a b c`, `a\b\c\`, `a"b"c"`]; 2171 auto result = executeShell( 2172 escapeShellCommand([prog.path] ~ args) 2173 ~ " > " ~ 2174 escapeShellFileName(outputFn)); 2175 assert(result.status == 0); 2176 auto args2 = outputFn.readText().strip().parseCommandLine()[1..$]; 2177 assert(args == args2, text(args2)); 2178 } 2179 2180 2181 /** 2182 Options that control the behaviour of process creation functions in this 2183 module. Most options only apply to $(LREF spawnProcess) and 2184 $(LREF spawnShell). 2185 2186 Example: 2187 --- 2188 auto logFile = File("myapp_error.log", "w"); 2189 2190 // Start program, suppressing the console window (Windows only), 2191 // redirect its error stream to logFile, and leave logFile open 2192 // in the parent process as well. 2193 auto pid = spawnProcess("myapp", stdin, stdout, logFile, 2194 Config.retainStderr | Config.suppressConsole); 2195 scope(exit) 2196 { 2197 auto exitCode = wait(pid); 2198 logFile.writeln("myapp exited with code ", exitCode); 2199 logFile.close(); 2200 } 2201 --- 2202 */ 2203 struct Config 2204 { 2205 /** 2206 Flag options. 2207 Use bitwise OR to combine flags. 2208 **/ 2209 enum Flags 2210 { 2211 none = 0, 2212 2213 /** 2214 By default, the child process inherits the parent's environment, 2215 and any environment variables passed to $(LREF spawnProcess) will 2216 be added to it. If this flag is set, the only variables in the 2217 child process' environment will be those given to spawnProcess. 2218 */ 2219 newEnv = 1, 2220 2221 /** 2222 Unless the child process inherits the standard input/output/error 2223 streams of its parent, one almost always wants the streams closed 2224 in the parent when $(LREF spawnProcess) returns. Therefore, by 2225 default, this is done. If this is not desirable, pass any of these 2226 options to spawnProcess. 2227 */ 2228 retainStdin = 2, 2229 retainStdout = 4, /// ditto 2230 retainStderr = 8, /// ditto 2231 2232 /** 2233 On Windows, if the child process is a console application, this 2234 flag will prevent the creation of a console window. Otherwise, 2235 it will be ignored. On POSIX, `suppressConsole` has no effect. 2236 */ 2237 suppressConsole = 16, 2238 2239 /** 2240 On POSIX, open $(LINK2 http://en.wikipedia.org/wiki/File_descriptor,file descriptors) 2241 are by default inherited by the child process. As this may lead 2242 to subtle bugs when pipes or multiple threads are involved, 2243 $(LREF spawnProcess) ensures that all file descriptors except the 2244 ones that correspond to standard input/output/error are closed 2245 in the child process when it starts. Use `inheritFDs` to prevent 2246 this. 2247 2248 On Windows, this option has no effect, and any handles which have been 2249 explicitly marked as inheritable will always be inherited by the child 2250 process. 2251 */ 2252 inheritFDs = 32, 2253 2254 /** 2255 Spawn process in detached state. This removes the need in calling 2256 $(LREF wait) to clean up the process resources. 2257 2258 Note: 2259 Calling $(LREF wait) or $(LREF kill) with the resulting `Pid` is invalid. 2260 */ 2261 detached = 64, 2262 2263 /** 2264 By default, the $(LREF execute) and $(LREF executeShell) functions 2265 will capture child processes' both stdout and stderr. This can be 2266 undesirable if the standard output is to be processed or otherwise 2267 used by the invoking program, as `execute`'s result would then 2268 contain a mix of output and warning/error messages. 2269 2270 Specify this flag when calling `execute` or `executeShell` to 2271 cause invoked processes' stderr stream to be sent to $(REF stderr, 2272 std,stdio), and only capture and return standard output. 2273 2274 This flag has no effect on $(LREF spawnProcess) or $(LREF spawnShell). 2275 */ 2276 stderrPassThrough = 128, 2277 } 2278 Flags flags; /// ditto 2279 2280 /** 2281 For backwards compatibility, and cases when only flags need to 2282 be specified in the `Config`, these allow building `Config` 2283 instances using flag names only. 2284 */ 2285 enum Config none = Config.init; 2286 enum Config newEnv = Config(Flags.newEnv); /// ditto 2287 enum Config retainStdin = Config(Flags.retainStdin); /// ditto 2288 enum Config retainStdout = Config(Flags.retainStdout); /// ditto 2289 enum Config retainStderr = Config(Flags.retainStderr); /// ditto 2290 enum Config suppressConsole = Config(Flags.suppressConsole); /// ditto 2291 enum Config inheritFDs = Config(Flags.inheritFDs); /// ditto 2292 enum Config detached = Config(Flags.detached); /// ditto 2293 enum Config stderrPassThrough = Config(Flags.stderrPassThrough); /// ditto 2294 Config opUnary(string op)() 2295 if (is(typeof(mixin(op ~ q{flags})))) 2296 { 2297 return Config(mixin(op ~ q{flags})); 2298 } /// ditto 2299 Config opBinary(string op)(Config other) 2300 if (is(typeof(mixin(q{flags} ~ op ~ q{other.flags})))) 2301 { 2302 return Config(mixin(q{flags} ~ op ~ q{other.flags})); 2303 } /// ditto 2304 Config opOpAssign(string op)(Config other) 2305 if (is(typeof(mixin(q{flags} ~ op ~ q{=other.flags})))) 2306 { 2307 return Config(mixin(q{flags} ~ op ~ q{=other.flags})); 2308 } /// ditto 2309 2310 version (StdDdoc) 2311 { 2312 /** 2313 A function that is called before `exec` in $(LREF spawnProcess). 2314 It returns `true` if succeeded and otherwise returns `false`. 2315 2316 $(RED Warning: 2317 Please note that the code in this function must only use 2318 async-signal-safe functions.) 2319 2320 If $(LREF preExecDelegate) is also set, it is called last. 2321 2322 On Windows, this member is not available. 2323 */ 2324 bool function() nothrow @nogc @safe preExecFunction; 2325 2326 /** 2327 A delegate that is called before `exec` in $(LREF spawnProcess). 2328 It returns `true` if succeeded and otherwise returns `false`. 2329 2330 $(RED Warning: 2331 Please note that the code in this function must only use 2332 async-signal-safe functions.) 2333 2334 If $(LREF preExecFunction) is also set, it is called first. 2335 2336 On Windows, this member is not available. 2337 */ 2338 bool delegate() nothrow @nogc @safe preExecDelegate; 2339 } 2340 else version (Posix) 2341 { 2342 bool function() nothrow @nogc @safe preExecFunction; 2343 bool delegate() nothrow @nogc @safe preExecDelegate; 2344 } 2345 } 2346 2347 // https://issues.dlang.org/show_bug.cgi?id=22125 2348 @safe unittest 2349 { 2350 Config c = Config.retainStdin; 2351 c |= Config.retainStdout; 2352 c |= Config.retainStderr; 2353 c &= ~Config.retainStderr; 2354 assert(c == (Config.retainStdin | Config.retainStdout)); 2355 } 2356 2357 /// A handle that corresponds to a spawned process. 2358 final class Pid 2359 { 2360 /** 2361 The process ID number. 2362 2363 This is a number that uniquely identifies the process on the operating 2364 system, for at least as long as the process is running. Once $(LREF wait) 2365 has been called on the $(LREF Pid), this method will return an 2366 invalid (negative) process ID. 2367 */ 2368 @property int processID() const @safe pure nothrow 2369 { 2370 return _processID; 2371 } 2372 2373 /** 2374 An operating system handle to the process. 2375 2376 This handle is used to specify the process in OS-specific APIs. 2377 On POSIX, this function returns a `core.sys.posix.sys.types.pid_t` 2378 with the same value as $(LREF Pid.processID), while on Windows it returns 2379 a `core.sys.windows.windows.HANDLE`. 2380 2381 Once $(LREF wait) has been called on the $(LREF Pid), this method 2382 will return an invalid handle. 2383 */ 2384 // Note: Since HANDLE is a reference, this function cannot be const. 2385 version (Windows) 2386 @property HANDLE osHandle() @nogc @safe pure nothrow 2387 { 2388 return _handle; 2389 } 2390 else version (Posix) 2391 @property pid_t osHandle() @nogc @safe pure nothrow 2392 { 2393 return _processID; 2394 } 2395 2396 private: 2397 /* 2398 Pid.performWait() does the dirty work for wait() and nonBlockingWait(). 2399 2400 If block == true, this function blocks until the process terminates, 2401 sets _processID to terminated, and returns the exit code or terminating 2402 signal as described in the wait() documentation. 2403 2404 If block == false, this function returns immediately, regardless 2405 of the status of the process. If the process has terminated, the 2406 function has the exact same effect as the blocking version. If not, 2407 it returns 0 and does not modify _processID. 2408 */ 2409 version (Posix) 2410 int performWait(bool block) @trusted 2411 { 2412 import std.exception : enforce; 2413 enforce!ProcessException(owned, "Can't wait on a detached process"); 2414 if (_processID == terminated) return _exitCode; 2415 int exitCode; 2416 while (true) 2417 { 2418 int status; 2419 auto check = waitpid(_processID, &status, block ? 0 : WNOHANG); 2420 if (check == -1) 2421 { 2422 if (errno == ECHILD) 2423 { 2424 throw new ProcessException( 2425 "Process does not exist or is not a child process."); 2426 } 2427 else 2428 { 2429 // waitpid() was interrupted by a signal. We simply 2430 // restart it. 2431 assert(errno == EINTR); 2432 continue; 2433 } 2434 } 2435 if (!block && check == 0) return 0; 2436 if (WIFEXITED(status)) 2437 { 2438 exitCode = WEXITSTATUS(status); 2439 break; 2440 } 2441 else if (WIFSIGNALED(status)) 2442 { 2443 exitCode = -WTERMSIG(status); 2444 break; 2445 } 2446 // We check again whether the call should be blocking, 2447 // since we don't care about other status changes besides 2448 // "exited" and "terminated by signal". 2449 if (!block) return 0; 2450 2451 // Process has stopped, but not terminated, so we continue waiting. 2452 } 2453 // Mark Pid as terminated, and cache and return exit code. 2454 _processID = terminated; 2455 _exitCode = exitCode; 2456 return exitCode; 2457 } 2458 else version (Windows) 2459 { 2460 int performWait(const bool block, const DWORD timeout = INFINITE) @trusted 2461 { 2462 import std.exception : enforce; 2463 enforce!ProcessException(owned, "Can't wait on a detached process"); 2464 if (_processID == terminated) return _exitCode; 2465 assert(_handle != INVALID_HANDLE_VALUE); 2466 if (block) 2467 { 2468 auto result = WaitForSingleObject(_handle, timeout); 2469 if (result != WAIT_OBJECT_0) 2470 { 2471 // Wait time exceeded `timeout` milliseconds? 2472 if (result == WAIT_TIMEOUT && timeout != INFINITE) 2473 return 0; 2474 2475 throw ProcessException.newFromLastError("Wait failed."); 2476 } 2477 } 2478 if (!GetExitCodeProcess(_handle, cast(LPDWORD)&_exitCode)) 2479 throw ProcessException.newFromLastError(); 2480 if (!block && _exitCode == STILL_ACTIVE) return 0; 2481 CloseHandle(_handle); 2482 _handle = INVALID_HANDLE_VALUE; 2483 _processID = terminated; 2484 return _exitCode; 2485 } 2486 2487 int performWait(Duration timeout) @safe 2488 { 2489 import std.exception : enforce; 2490 const msecs = timeout.total!"msecs"; 2491 2492 // Limit this implementation the maximum wait time offered by 2493 // WaitForSingleObject. One could theoretically break up larger 2494 // durations into multiple waits but (DWORD.max - 1).msecs 2495 // (> 7 weeks, 17 hours) should be enough for the usual case. 2496 // DWORD.max is reserved for INFINITE 2497 enforce!ProcessException(msecs < DWORD.max, "Timeout exceeds maximum wait time!"); 2498 return performWait(true, cast(DWORD) msecs); 2499 } 2500 2501 ~this() 2502 { 2503 if (_handle != INVALID_HANDLE_VALUE) 2504 { 2505 CloseHandle(_handle); 2506 _handle = INVALID_HANDLE_VALUE; 2507 } 2508 } 2509 } 2510 2511 // Special values for _processID. 2512 enum invalid = -1, terminated = -2; 2513 2514 // OS process ID number. Only nonnegative IDs correspond to 2515 // running processes. 2516 int _processID = invalid; 2517 2518 // Exit code cached by wait(). This is only expected to hold a 2519 // sensible value if _processID == terminated. 2520 int _exitCode; 2521 2522 // Whether the process can be waited for by wait() for or killed by kill(). 2523 // False if process was started as detached. True otherwise. 2524 bool owned; 2525 2526 // Pids are only meant to be constructed inside this module, so 2527 // we make the constructor private. 2528 version (Windows) 2529 { 2530 HANDLE _handle = INVALID_HANDLE_VALUE; 2531 this(int pid, HANDLE handle) @safe pure nothrow 2532 { 2533 _processID = pid; 2534 _handle = handle; 2535 this.owned = true; 2536 } 2537 this(int pid) @safe pure nothrow 2538 { 2539 _processID = pid; 2540 this.owned = false; 2541 } 2542 } 2543 else 2544 { 2545 this(int id, bool owned) @safe pure nothrow 2546 { 2547 _processID = id; 2548 this.owned = owned; 2549 } 2550 } 2551 } 2552 2553 2554 /** 2555 Waits for the process associated with `pid` to terminate, and returns 2556 its exit status. 2557 2558 In general one should always _wait for child processes to terminate 2559 before exiting the parent process unless the process was spawned as detached 2560 (that was spawned with `Config.detached` flag). 2561 Otherwise, they may become "$(HTTP en.wikipedia.org/wiki/Zombie_process,zombies)" 2562 – processes that are defunct, yet still occupy a slot in the OS process table. 2563 You should not and must not wait for detached processes, since you don't own them. 2564 2565 If the process has already terminated, this function returns directly. 2566 The exit code is cached, so that if wait() is called multiple times on 2567 the same $(LREF Pid) it will always return the same value. 2568 2569 POSIX_specific: 2570 If the process is terminated by a signal, this function returns a 2571 negative number whose absolute value is the signal number. 2572 Since POSIX restricts normal exit codes to the range 0-255, a 2573 negative return value will always indicate termination by signal. 2574 Signal codes are defined in the `core.sys.posix.signal` module 2575 (which corresponds to the `signal.h` POSIX header). 2576 2577 Throws: 2578 $(LREF ProcessException) on failure or on attempt to wait for detached process. 2579 2580 Example: 2581 See the $(LREF spawnProcess) documentation. 2582 2583 See_also: 2584 $(LREF tryWait), for a non-blocking function. 2585 */ 2586 int wait(Pid pid) @safe 2587 { 2588 assert(pid !is null, "Called wait on a null Pid."); 2589 return pid.performWait(true); 2590 } 2591 2592 2593 @system unittest // Pid and wait() 2594 { 2595 version (Windows) TestScript prog = "exit %~1"; 2596 else version (Posix) TestScript prog = "exit $1"; 2597 assert(wait(spawnProcess([prog.path, "0"])) == 0); 2598 assert(wait(spawnProcess([prog.path, "123"])) == 123); 2599 auto pid = spawnProcess([prog.path, "10"]); 2600 assert(pid.processID > 0); 2601 version (Windows) assert(pid.osHandle != INVALID_HANDLE_VALUE); 2602 else version (Posix) assert(pid.osHandle == pid.processID); 2603 assert(wait(pid) == 10); 2604 assert(wait(pid) == 10); // cached exit code 2605 assert(pid.processID < 0); 2606 version (Windows) assert(pid.osHandle == INVALID_HANDLE_VALUE); 2607 else version (Posix) assert(pid.osHandle < 0); 2608 } 2609 2610 private import std.typecons : Tuple; 2611 2612 /** 2613 Waits until either the process associated with `pid` terminates or the 2614 elapsed time exceeds the given timeout. 2615 2616 If the process terminates within the given duration it behaves exactly like 2617 `wait`, except that it returns a tuple `(true, exit code)`. 2618 2619 If the process does not terminate within the given duration it will stop 2620 waiting and return `(false, 0).` 2621 2622 The timeout may not exceed `(uint.max - 1).msecs` (~ 7 weeks, 17 hours). 2623 2624 $(BLUE This function is Windows-Only.) 2625 2626 Returns: 2627 An $(D std.typecons.Tuple!(bool, "terminated", int, "status")). 2628 2629 Throws: 2630 $(LREF ProcessException) on failure or on attempt to wait for detached process. 2631 2632 Example: 2633 See the $(LREF spawnProcess) documentation. 2634 2635 See_also: 2636 $(LREF wait), for a blocking function without timeout. 2637 $(LREF tryWait), for a non-blocking function without timeout. 2638 */ 2639 version (StdDdoc) 2640 Tuple!(bool, "terminated", int, "status") waitTimeout(Pid pid, Duration timeout) @safe; 2641 2642 else version (Windows) 2643 Tuple!(bool, "terminated", int, "status") waitTimeout(Pid pid, Duration timeout) @safe 2644 { 2645 assert(pid !is null, "Called wait on a null Pid."); 2646 auto code = pid.performWait(timeout); 2647 return typeof(return)(pid._processID == Pid.terminated, code); 2648 } 2649 2650 version (Windows) 2651 @system unittest // Pid and waitTimeout() 2652 { 2653 import std.exception : collectException; 2654 import std.typecons : tuple; 2655 2656 TestScript prog = ":Loop\r\n" ~ "goto Loop"; 2657 auto pid = spawnProcess(prog.path); 2658 2659 // Doesn't block longer than one second 2660 assert(waitTimeout(pid, 1.seconds) == tuple(false, 0)); 2661 2662 kill(pid); 2663 assert(waitTimeout(pid, 1.seconds) == tuple(true, 1)); // exit 1 because the process is killed 2664 2665 // Rejects timeouts exceeding the Windows API capabilities 2666 const dur = DWORD.max.msecs; 2667 const ex = collectException!ProcessException(waitTimeout(pid, dur)); 2668 assert(ex); 2669 assert(ex.msg == "Timeout exceeds maximum wait time!"); 2670 } 2671 2672 /** 2673 A non-blocking version of $(LREF wait). 2674 2675 If the process associated with `pid` has already terminated, 2676 `tryWait` has the exact same effect as `wait`. 2677 In this case, it returns a tuple where the `terminated` field 2678 is set to `true` and the `status` field has the same 2679 interpretation as the return value of `wait`. 2680 2681 If the process has $(I not) yet terminated, this function differs 2682 from `wait` in that does not wait for this to happen, but instead 2683 returns immediately. The `terminated` field of the returned 2684 tuple will then be set to `false`, while the `status` field 2685 will always be 0 (zero). `wait` or `tryWait` should then be 2686 called again on the same `Pid` at some later time; not only to 2687 get the exit code, but also to avoid the process becoming a "zombie" 2688 when it finally terminates. (See $(LREF wait) for details). 2689 2690 Returns: 2691 An $(D std.typecons.Tuple!(bool, "terminated", int, "status")). 2692 2693 Throws: 2694 $(LREF ProcessException) on failure or on attempt to wait for detached process. 2695 2696 Example: 2697 --- 2698 auto pid = spawnProcess("dmd myapp.d"); 2699 scope(exit) wait(pid); 2700 ... 2701 auto dmd = tryWait(pid); 2702 if (dmd.terminated) 2703 { 2704 if (dmd.status == 0) writeln("Compilation succeeded!"); 2705 else writeln("Compilation failed"); 2706 } 2707 else writeln("Still compiling..."); 2708 ... 2709 --- 2710 Note that in this example, the first `wait` call will have no 2711 effect if the process has already terminated by the time `tryWait` 2712 is called. In the opposite case, however, the `scope` statement 2713 ensures that we always wait for the process if it hasn't terminated 2714 by the time we reach the end of the scope. 2715 */ 2716 auto tryWait(Pid pid) @safe 2717 { 2718 import std.typecons : Tuple; 2719 assert(pid !is null, "Called tryWait on a null Pid."); 2720 auto code = pid.performWait(false); 2721 return Tuple!(bool, "terminated", int, "status")(pid._processID == Pid.terminated, code); 2722 } 2723 // unittest: This function is tested together with kill() below. 2724 2725 2726 /** 2727 Attempts to terminate the process associated with `pid`. 2728 2729 The effect of this function, as well as the meaning of `codeOrSignal`, 2730 is highly platform dependent. Details are given below. Common to all 2731 platforms is that this function only $(I initiates) termination of the process, 2732 and returns immediately. It does not wait for the process to end, 2733 nor does it guarantee that the process does in fact get terminated. 2734 2735 Always call $(LREF wait) to wait for a process to complete, even if `kill` 2736 has been called on it. 2737 2738 Windows_specific: 2739 The process will be 2740 $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms686714%28v=vs.100%29.aspx, 2741 forcefully and abruptly terminated). If `codeOrSignal` is specified, it 2742 must be a nonnegative number which will be used as the exit code of the process. 2743 If not, the process wil exit with code 1. Do not use $(D codeOrSignal = 259), 2744 as this is a special value (aka. $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms683189.aspx,STILL_ACTIVE)) 2745 used by Windows to signal that a process has in fact $(I not) terminated yet. 2746 --- 2747 auto pid = spawnProcess("some_app"); 2748 kill(pid, 10); 2749 assert(wait(pid) == 10); 2750 --- 2751 2752 POSIX_specific: 2753 A $(LINK2 http://en.wikipedia.org/wiki/Unix_signal,signal) will be sent to 2754 the process, whose value is given by `codeOrSignal`. Depending on the 2755 signal sent, this may or may not terminate the process. Symbolic constants 2756 for various $(LINK2 http://en.wikipedia.org/wiki/Unix_signal#POSIX_signals, 2757 POSIX signals) are defined in `core.sys.posix.signal`, which corresponds to the 2758 $(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html, 2759 `signal.h` POSIX header). If `codeOrSignal` is omitted, the 2760 `SIGTERM` signal will be sent. (This matches the behaviour of the 2761 $(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/utilities/kill.html, 2762 `_kill`) shell command.) 2763 --- 2764 import core.sys.posix.signal : SIGKILL; 2765 auto pid = spawnProcess("some_app"); 2766 kill(pid, SIGKILL); 2767 assert(wait(pid) == -SIGKILL); // Negative return value on POSIX! 2768 --- 2769 2770 Throws: 2771 $(LREF ProcessException) on error (e.g. if codeOrSignal is invalid). 2772 or on attempt to kill detached process. 2773 Note that failure to terminate the process is considered a "normal" 2774 outcome, not an error.$(BR) 2775 */ 2776 void kill(Pid pid) 2777 { 2778 version (Windows) kill(pid, 1); 2779 else version (Posix) 2780 { 2781 import core.sys.posix.signal : SIGTERM; 2782 kill(pid, SIGTERM); 2783 } 2784 } 2785 2786 /// ditto 2787 void kill(Pid pid, int codeOrSignal) 2788 { 2789 import std.exception : enforce; 2790 enforce!ProcessException(pid.owned, "Can't kill detached process"); 2791 version (Windows) 2792 { 2793 if (codeOrSignal < 0) throw new ProcessException("Invalid exit code"); 2794 // On Windows, TerminateProcess() appears to terminate the 2795 // *current* process if it is passed an invalid handle... 2796 if (pid.osHandle == INVALID_HANDLE_VALUE) 2797 throw new ProcessException("Invalid process handle"); 2798 if (!TerminateProcess(pid.osHandle, codeOrSignal)) 2799 throw ProcessException.newFromLastError(); 2800 } 2801 else version (Posix) 2802 { 2803 import core.sys.posix.signal : kill; 2804 if (pid.osHandle == Pid.invalid) 2805 throw new ProcessException("Pid is invalid"); 2806 if (pid.osHandle == Pid.terminated) 2807 throw new ProcessException("Pid is already terminated"); 2808 if (kill(pid.osHandle, codeOrSignal) == -1) 2809 throw ProcessException.newFromErrno(); 2810 } 2811 } 2812 2813 @system unittest // tryWait() and kill() 2814 { 2815 import core.thread; 2816 import std.exception : assertThrown; 2817 // The test script goes into an infinite loop. 2818 version (Windows) 2819 { 2820 TestScript prog = ":loop 2821 goto loop"; 2822 } 2823 else version (Posix) 2824 { 2825 import core.sys.posix.signal : SIGTERM, SIGKILL; 2826 TestScript prog = "while true; do sleep 1; done"; 2827 } 2828 auto pid = spawnProcess(prog.path); 2829 // Android appears to automatically kill sleeping processes very quickly, 2830 // so shorten the wait before killing here. 2831 version (Android) 2832 Thread.sleep(dur!"msecs"(5)); 2833 else 2834 Thread.sleep(dur!"msecs"(500)); 2835 kill(pid); 2836 version (Windows) assert(wait(pid) == 1); 2837 else version (Posix) assert(wait(pid) == -SIGTERM); 2838 2839 pid = spawnProcess(prog.path); 2840 Thread.sleep(dur!"msecs"(500)); 2841 auto s = tryWait(pid); 2842 assert(!s.terminated && s.status == 0); 2843 assertThrown!ProcessException(kill(pid, -123)); // Negative code not allowed. 2844 version (Windows) kill(pid, 123); 2845 else version (Posix) kill(pid, SIGKILL); 2846 do { s = tryWait(pid); } while (!s.terminated); 2847 version (Windows) assert(s.status == 123); 2848 else version (Posix) assert(s.status == -SIGKILL); 2849 assertThrown!ProcessException(kill(pid)); // Already terminated 2850 } 2851 2852 @system unittest // wait() and kill() detached process 2853 { 2854 import core.thread; 2855 import std.exception : assertThrown; 2856 TestScript prog = "exit 0"; 2857 auto pid = spawnProcess([prog.path], null, Config.detached); 2858 /* 2859 This sleep is needed because we can't wait() for detached process to end 2860 and therefore TestScript destructor may run at the same time as /bin/sh tries to start the script. 2861 This leads to the annoying message like "/bin/sh: 0: Can't open /tmp/std.process temporary file" to appear when running tests. 2862 It does not happen in unittests with non-detached processes because we always wait() for them to finish. 2863 */ 2864 Thread.sleep(500.msecs); 2865 assert(!pid.owned); 2866 version (Windows) assert(pid.osHandle == INVALID_HANDLE_VALUE); 2867 assertThrown!ProcessException(wait(pid)); 2868 assertThrown!ProcessException(kill(pid)); 2869 } 2870 2871 2872 /** 2873 Creates a unidirectional _pipe. 2874 2875 Data is written to one end of the _pipe and read from the other. 2876 --- 2877 auto p = pipe(); 2878 p.writeEnd.writeln("Hello World"); 2879 p.writeEnd.flush(); 2880 assert(p.readEnd.readln().chomp() == "Hello World"); 2881 --- 2882 Pipes can, for example, be used for interprocess communication 2883 by spawning a new process and passing one end of the _pipe to 2884 the child, while the parent uses the other end. 2885 (See also $(LREF pipeProcess) and $(LREF pipeShell) for an easier 2886 way of doing this.) 2887 --- 2888 // Use cURL to download the dlang.org front page, pipe its 2889 // output to grep to extract a list of links to ZIP files, 2890 // and write the list to the file "D downloads.txt": 2891 auto p = pipe(); 2892 auto outFile = File("D downloads.txt", "w"); 2893 auto cpid = spawnProcess(["curl", "http://dlang.org/download.html"], 2894 std.stdio.stdin, p.writeEnd); 2895 scope(exit) wait(cpid); 2896 auto gpid = spawnProcess(["grep", "-o", `http://\S*\.zip`], 2897 p.readEnd, outFile); 2898 scope(exit) wait(gpid); 2899 --- 2900 2901 Returns: 2902 A $(LREF Pipe) object that corresponds to the created _pipe. 2903 2904 Throws: 2905 $(REF StdioException, std,stdio) on failure. 2906 */ 2907 version (Posix) 2908 Pipe pipe() @trusted //TODO: @safe 2909 { 2910 import core.sys.posix.stdio : fdopen; 2911 int[2] fds; 2912 if (core.sys.posix.unistd.pipe(fds) != 0) 2913 throw new StdioException("Unable to create pipe"); 2914 Pipe p; 2915 auto readFP = fdopen(fds[0], "r"); 2916 if (readFP == null) 2917 throw new StdioException("Cannot open read end of pipe"); 2918 p._read = File(readFP, null); 2919 auto writeFP = fdopen(fds[1], "w"); 2920 if (writeFP == null) 2921 throw new StdioException("Cannot open write end of pipe"); 2922 p._write = File(writeFP, null); 2923 return p; 2924 } 2925 else version (Windows) 2926 Pipe pipe() @trusted //TODO: @safe 2927 { 2928 // use CreatePipe to create an anonymous pipe 2929 HANDLE readHandle; 2930 HANDLE writeHandle; 2931 if (!CreatePipe(&readHandle, &writeHandle, null, 0)) 2932 { 2933 throw new StdioException( 2934 "Error creating pipe (" ~ generateSysErrorMsg() ~ ')', 2935 0); 2936 } 2937 2938 scope(failure) 2939 { 2940 CloseHandle(readHandle); 2941 CloseHandle(writeHandle); 2942 } 2943 2944 try 2945 { 2946 Pipe p; 2947 p._read .windowsHandleOpen(readHandle , "r"); 2948 p._write.windowsHandleOpen(writeHandle, "a"); 2949 return p; 2950 } 2951 catch (Exception e) 2952 { 2953 throw new StdioException("Error attaching pipe (" ~ e.msg ~ ")", 2954 0); 2955 } 2956 } 2957 2958 2959 /// An interface to a pipe created by the $(LREF pipe) function. 2960 struct Pipe 2961 { 2962 /// The read end of the pipe. 2963 @property File readEnd() @safe nothrow { return _read; } 2964 2965 2966 /// The write end of the pipe. 2967 @property File writeEnd() @safe nothrow { return _write; } 2968 2969 2970 /** 2971 Closes both ends of the pipe. 2972 2973 Normally it is not necessary to do this manually, as $(REF File, std,stdio) 2974 objects are automatically closed when there are no more references 2975 to them. 2976 2977 Note that if either end of the pipe has been passed to a child process, 2978 it will only be closed in the parent process. (What happens in the 2979 child process is platform dependent.) 2980 2981 Throws: 2982 $(REF ErrnoException, std,exception) if an error occurs. 2983 */ 2984 void close() @safe 2985 { 2986 _read.close(); 2987 _write.close(); 2988 } 2989 2990 private: 2991 File _read, _write; 2992 } 2993 2994 @system unittest 2995 { 2996 import std.string; 2997 auto p = pipe(); 2998 p.writeEnd.writeln("Hello World"); 2999 p.writeEnd.flush(); 3000 assert(p.readEnd.readln().chomp() == "Hello World"); 3001 p.close(); 3002 assert(!p.readEnd.isOpen); 3003 assert(!p.writeEnd.isOpen); 3004 } 3005 3006 3007 /** 3008 Starts a new process, creating pipes to redirect its standard 3009 input, output and/or error streams. 3010 3011 `pipeProcess` and `pipeShell` are convenient wrappers around 3012 $(LREF spawnProcess) and $(LREF spawnShell), respectively, and 3013 automate the task of redirecting one or more of the child process' 3014 standard streams through pipes. Like the functions they wrap, 3015 these functions return immediately, leaving the child process to 3016 execute in parallel with the invoking process. It is recommended 3017 to always call $(LREF wait) on the returned $(LREF ProcessPipes.pid), 3018 as detailed in the documentation for `wait`. 3019 3020 The `args`/`program`/`command`, `env` and `config` 3021 parameters are forwarded straight to the underlying spawn functions, 3022 and we refer to their documentation for details. 3023 3024 Params: 3025 args = An array which contains the program name as the zeroth element 3026 and any command-line arguments in the following elements. 3027 (See $(LREF spawnProcess) for details.) 3028 program = The program name, $(I without) command-line arguments. 3029 (See $(LREF spawnProcess) for details.) 3030 command = A shell command which is passed verbatim to the command 3031 interpreter. (See $(LREF spawnShell) for details.) 3032 redirect = Flags that determine which streams are redirected, and 3033 how. See $(LREF Redirect) for an overview of available 3034 flags. 3035 env = Additional environment variables for the child process. 3036 (See $(LREF spawnProcess) for details.) 3037 config = Flags that control process creation. See $(LREF Config) 3038 for an overview of available flags, and note that the 3039 `retainStd...` flags have no effect in this function. 3040 workDir = The working directory for the new process. 3041 By default the child process inherits the parent's working 3042 directory. 3043 shellPath = The path to the shell to use to run the specified program. 3044 By default this is $(LREF nativeShell). 3045 3046 Returns: 3047 A $(LREF ProcessPipes) object which contains $(REF File, std,stdio) 3048 handles that communicate with the redirected streams of the child 3049 process, along with a $(LREF Pid) object that corresponds to the 3050 spawned process. 3051 3052 Throws: 3053 $(LREF ProcessException) on failure to start the process.$(BR) 3054 $(REF StdioException, std,stdio) on failure to redirect any of the streams.$(BR) 3055 3056 Example: 3057 --- 3058 // my_application writes to stdout and might write to stderr 3059 auto pipes = pipeProcess("my_application", Redirect.stdout | Redirect.stderr); 3060 scope(exit) wait(pipes.pid); 3061 3062 // Store lines of output. 3063 string[] output; 3064 foreach (line; pipes.stdout.byLine) output ~= line.idup; 3065 3066 // Store lines of errors. 3067 string[] errors; 3068 foreach (line; pipes.stderr.byLine) errors ~= line.idup; 3069 3070 3071 // sendmail expects to read from stdin 3072 pipes = pipeProcess(["/usr/bin/sendmail", "-t"], Redirect.stdin); 3073 pipes.stdin.writeln("To: you"); 3074 pipes.stdin.writeln("From: me"); 3075 pipes.stdin.writeln("Subject: dlang"); 3076 pipes.stdin.writeln(""); 3077 pipes.stdin.writeln(message); 3078 3079 // a single period tells sendmail we are finished 3080 pipes.stdin.writeln("."); 3081 3082 // but at this point sendmail might not see it, we need to flush 3083 pipes.stdin.flush(); 3084 3085 // sendmail happens to exit on ".", but some you have to close the file: 3086 pipes.stdin.close(); 3087 3088 // otherwise this wait will wait forever 3089 wait(pipes.pid); 3090 3091 --- 3092 */ 3093 ProcessPipes pipeProcess(scope const(char[])[] args, 3094 Redirect redirect = Redirect.all, 3095 const string[string] env = null, 3096 Config config = Config.none, 3097 scope const(char)[] workDir = null) 3098 @safe 3099 { 3100 return pipeProcessImpl!spawnProcess(args, redirect, env, config, workDir); 3101 } 3102 3103 /// ditto 3104 ProcessPipes pipeProcess(scope const(char)[] program, 3105 Redirect redirect = Redirect.all, 3106 const string[string] env = null, 3107 Config config = Config.none, 3108 scope const(char)[] workDir = null) 3109 @safe 3110 { 3111 return pipeProcessImpl!spawnProcess(program, redirect, env, config, workDir); 3112 } 3113 3114 /// ditto 3115 ProcessPipes pipeShell(scope const(char)[] command, 3116 Redirect redirect = Redirect.all, 3117 const string[string] env = null, 3118 Config config = Config.none, 3119 scope const(char)[] workDir = null, 3120 string shellPath = nativeShell) 3121 @safe 3122 { 3123 return pipeProcessImpl!spawnShell(command, 3124 redirect, 3125 env, 3126 config, 3127 workDir, 3128 shellPath); 3129 } 3130 3131 // Implementation of the pipeProcess() family of functions. 3132 private ProcessPipes pipeProcessImpl(alias spawnFunc, Cmd, ExtraSpawnFuncArgs...) 3133 (scope Cmd command, 3134 Redirect redirectFlags, 3135 const string[string] env = null, 3136 Config config = Config.none, 3137 scope const(char)[] workDir = null, 3138 ExtraSpawnFuncArgs extraArgs = ExtraSpawnFuncArgs.init) 3139 @trusted //TODO: @safe 3140 { 3141 File childStdin, childStdout, childStderr; 3142 ProcessPipes pipes; 3143 pipes._redirectFlags = redirectFlags; 3144 3145 if (redirectFlags & Redirect.stdin) 3146 { 3147 auto p = pipe(); 3148 childStdin = p.readEnd; 3149 pipes._stdin = p.writeEnd; 3150 } 3151 else 3152 { 3153 childStdin = std.stdio.stdin; 3154 } 3155 3156 if (redirectFlags & Redirect.stdout) 3157 { 3158 if ((redirectFlags & Redirect.stdoutToStderr) != 0) 3159 throw new StdioException("Cannot create pipe for stdout AND " 3160 ~"redirect it to stderr", 0); 3161 auto p = pipe(); 3162 childStdout = p.writeEnd; 3163 pipes._stdout = p.readEnd; 3164 } 3165 else 3166 { 3167 childStdout = std.stdio.stdout; 3168 } 3169 3170 if (redirectFlags & Redirect.stderr) 3171 { 3172 if ((redirectFlags & Redirect.stderrToStdout) != 0) 3173 throw new StdioException("Cannot create pipe for stderr AND " 3174 ~"redirect it to stdout", 0); 3175 auto p = pipe(); 3176 childStderr = p.writeEnd; 3177 pipes._stderr = p.readEnd; 3178 } 3179 else 3180 { 3181 childStderr = std.stdio.stderr; 3182 } 3183 3184 if (redirectFlags & Redirect.stdoutToStderr) 3185 { 3186 if (redirectFlags & Redirect.stderrToStdout) 3187 { 3188 // We know that neither of the other options have been 3189 // set, so we assign the std.stdio.std* streams directly. 3190 childStdout = std.stdio.stderr; 3191 childStderr = std.stdio.stdout; 3192 } 3193 else 3194 { 3195 childStdout = childStderr; 3196 } 3197 } 3198 else if (redirectFlags & Redirect.stderrToStdout) 3199 { 3200 childStderr = childStdout; 3201 } 3202 3203 config.flags &= ~(Config.Flags.retainStdin | Config.Flags.retainStdout | Config.Flags.retainStderr); 3204 pipes._pid = spawnFunc(command, childStdin, childStdout, childStderr, 3205 env, config, workDir, extraArgs); 3206 return pipes; 3207 } 3208 3209 3210 /** 3211 Flags that can be passed to $(LREF pipeProcess) and $(LREF pipeShell) 3212 to specify which of the child process' standard streams are redirected. 3213 Use bitwise OR to combine flags. 3214 */ 3215 enum Redirect 3216 { 3217 /// Redirect the standard input, output or error streams, respectively. 3218 stdin = 1, 3219 stdout = 2, /// ditto 3220 stderr = 4, /// ditto 3221 3222 /** 3223 Redirect _all three streams. This is equivalent to 3224 $(D Redirect.stdin | Redirect.stdout | Redirect.stderr). 3225 */ 3226 all = stdin | stdout | stderr, 3227 3228 /** 3229 Redirect the standard error stream into the standard output stream. 3230 This can not be combined with `Redirect.stderr`. 3231 */ 3232 stderrToStdout = 8, 3233 3234 /** 3235 Redirect the standard output stream into the standard error stream. 3236 This can not be combined with `Redirect.stdout`. 3237 */ 3238 stdoutToStderr = 16, 3239 } 3240 3241 @system unittest 3242 { 3243 import std.string; 3244 version (Windows) TestScript prog = 3245 "call :sub %~1 %~2 0 3246 call :sub %~1 %~2 1 3247 call :sub %~1 %~2 2 3248 call :sub %~1 %~2 3 3249 exit 3 3250 3251 :sub 3252 set /p INPUT= 3253 if -%INPUT%-==-stop- ( exit %~3 ) 3254 echo %INPUT% %~1 3255 echo %INPUT% %~2 1>&2"; 3256 else version (Posix) TestScript prog = 3257 `for EXITCODE in 0 1 2 3; do 3258 read INPUT 3259 if test "$INPUT" = stop; then break; fi 3260 echo "$INPUT $1" 3261 echo "$INPUT $2" >&2 3262 done 3263 exit $EXITCODE`; 3264 auto pp = pipeProcess([prog.path, "bar", "baz"]); 3265 pp.stdin.writeln("foo"); 3266 pp.stdin.flush(); 3267 assert(pp.stdout.readln().chomp() == "foo bar"); 3268 assert(pp.stderr.readln().chomp().stripRight() == "foo baz"); 3269 pp.stdin.writeln("1234567890"); 3270 pp.stdin.flush(); 3271 assert(pp.stdout.readln().chomp() == "1234567890 bar"); 3272 assert(pp.stderr.readln().chomp().stripRight() == "1234567890 baz"); 3273 pp.stdin.writeln("stop"); 3274 pp.stdin.flush(); 3275 assert(wait(pp.pid) == 2); 3276 3277 pp = pipeProcess([prog.path, "12345", "67890"], 3278 Redirect.stdin | Redirect.stdout | Redirect.stderrToStdout); 3279 pp.stdin.writeln("xyz"); 3280 pp.stdin.flush(); 3281 assert(pp.stdout.readln().chomp() == "xyz 12345"); 3282 assert(pp.stdout.readln().chomp().stripRight() == "xyz 67890"); 3283 pp.stdin.writeln("stop"); 3284 pp.stdin.flush(); 3285 assert(wait(pp.pid) == 1); 3286 3287 pp = pipeShell(escapeShellCommand(prog.path, "AAAAA", "BBB"), 3288 Redirect.stdin | Redirect.stdoutToStderr | Redirect.stderr); 3289 pp.stdin.writeln("ab"); 3290 pp.stdin.flush(); 3291 assert(pp.stderr.readln().chomp() == "ab AAAAA"); 3292 assert(pp.stderr.readln().chomp().stripRight() == "ab BBB"); 3293 pp.stdin.writeln("stop"); 3294 pp.stdin.flush(); 3295 assert(wait(pp.pid) == 1); 3296 } 3297 3298 @system unittest 3299 { 3300 import std.exception : assertThrown; 3301 TestScript prog = "exit 0"; 3302 assertThrown!StdioException(pipeProcess( 3303 prog.path, 3304 Redirect.stdout | Redirect.stdoutToStderr)); 3305 assertThrown!StdioException(pipeProcess( 3306 prog.path, 3307 Redirect.stderr | Redirect.stderrToStdout)); 3308 auto p = pipeProcess(prog.path, Redirect.stdin); 3309 assertThrown!Error(p.stdout); 3310 assertThrown!Error(p.stderr); 3311 wait(p.pid); 3312 p = pipeProcess(prog.path, Redirect.stderr); 3313 assertThrown!Error(p.stdin); 3314 assertThrown!Error(p.stdout); 3315 wait(p.pid); 3316 } 3317 3318 /** 3319 Object which contains $(REF File, std,stdio) handles that allow communication 3320 with a child process through its standard streams. 3321 */ 3322 struct ProcessPipes 3323 { 3324 /// The $(LREF Pid) of the child process. 3325 @property Pid pid() @safe nothrow 3326 { 3327 return _pid; 3328 } 3329 3330 /** 3331 An $(REF File, std,stdio) that allows writing to the child process' 3332 standard input stream. 3333 3334 Throws: 3335 $(OBJECTREF Error) if the child process' standard input stream hasn't 3336 been redirected. 3337 */ 3338 @property File stdin() @safe nothrow 3339 { 3340 if ((_redirectFlags & Redirect.stdin) == 0) 3341 throw new Error("Child process' standard input stream hasn't " 3342 ~"been redirected."); 3343 return _stdin; 3344 } 3345 3346 /** 3347 An $(REF File, std,stdio) that allows reading from the child process' 3348 standard output stream. 3349 3350 Throws: 3351 $(OBJECTREF Error) if the child process' standard output stream hasn't 3352 been redirected. 3353 */ 3354 @property File stdout() @safe nothrow 3355 { 3356 if ((_redirectFlags & Redirect.stdout) == 0) 3357 throw new Error("Child process' standard output stream hasn't " 3358 ~"been redirected."); 3359 return _stdout; 3360 } 3361 3362 /** 3363 An $(REF File, std,stdio) that allows reading from the child process' 3364 standard error stream. 3365 3366 Throws: 3367 $(OBJECTREF Error) if the child process' standard error stream hasn't 3368 been redirected. 3369 */ 3370 @property File stderr() @safe nothrow 3371 { 3372 if ((_redirectFlags & Redirect.stderr) == 0) 3373 throw new Error("Child process' standard error stream hasn't " 3374 ~"been redirected."); 3375 return _stderr; 3376 } 3377 3378 private: 3379 Redirect _redirectFlags; 3380 Pid _pid; 3381 File _stdin, _stdout, _stderr; 3382 } 3383 3384 3385 3386 /** 3387 Executes the given program or shell command and returns its exit 3388 code and output. 3389 3390 `execute` and `executeShell` start a new process using 3391 $(LREF spawnProcess) and $(LREF spawnShell), respectively, and wait 3392 for the process to complete before returning. The functions capture 3393 what the child process prints to both its standard output and 3394 standard error streams, and return this together with its exit code. 3395 --- 3396 auto dmd = execute(["dmd", "myapp.d"]); 3397 if (dmd.status != 0) writeln("Compilation failed:\n", dmd.output); 3398 3399 auto ls = executeShell("ls -l"); 3400 if (ls.status != 0) writeln("Failed to retrieve file listing"); 3401 else writeln(ls.output); 3402 --- 3403 3404 The `args`/`program`/`command`, `env` and `config` 3405 parameters are forwarded straight to the underlying spawn functions, 3406 and we refer to their documentation for details. 3407 3408 Params: 3409 args = An array which contains the program name as the zeroth element 3410 and any command-line arguments in the following elements. 3411 (See $(LREF spawnProcess) for details.) 3412 program = The program name, $(I without) command-line arguments. 3413 (See $(LREF spawnProcess) for details.) 3414 command = A shell command which is passed verbatim to the command 3415 interpreter. (See $(LREF spawnShell) for details.) 3416 env = Additional environment variables for the child process. 3417 (See $(LREF spawnProcess) for details.) 3418 config = Flags that control process creation. See $(LREF Config) 3419 for an overview of available flags, and note that the 3420 `retainStd...` flags have no effect in this function. 3421 maxOutput = The maximum number of bytes of output that should be 3422 captured. 3423 workDir = The working directory for the new process. 3424 By default the child process inherits the parent's working 3425 directory. 3426 shellPath = The path to the shell to use to run the specified program. 3427 By default this is $(LREF nativeShell). 3428 3429 3430 Returns: 3431 An $(D std.typecons.Tuple!(int, "status", string, "output")). 3432 3433 POSIX_specific: 3434 If the process is terminated by a signal, the `status` field of 3435 the return value will contain a negative number whose absolute 3436 value is the signal number. (See $(LREF wait) for details.) 3437 3438 Throws: 3439 $(LREF ProcessException) on failure to start the process.$(BR) 3440 $(REF StdioException, std,stdio) on failure to capture output. 3441 */ 3442 auto execute(scope const(char[])[] args, 3443 const string[string] env = null, 3444 Config config = Config.none, 3445 size_t maxOutput = size_t.max, 3446 scope const(char)[] workDir = null) 3447 @safe 3448 { 3449 return executeImpl!pipeProcess(args, env, config, maxOutput, workDir); 3450 } 3451 3452 /// ditto 3453 auto execute(scope const(char)[] program, 3454 const string[string] env = null, 3455 Config config = Config.none, 3456 size_t maxOutput = size_t.max, 3457 scope const(char)[] workDir = null) 3458 @safe 3459 { 3460 return executeImpl!pipeProcess(program, env, config, maxOutput, workDir); 3461 } 3462 3463 /// ditto 3464 auto executeShell(scope const(char)[] command, 3465 const string[string] env = null, 3466 Config config = Config.none, 3467 size_t maxOutput = size_t.max, 3468 scope const(char)[] workDir = null, 3469 string shellPath = nativeShell) 3470 @safe 3471 { 3472 return executeImpl!pipeShell(command, 3473 env, 3474 config, 3475 maxOutput, 3476 workDir, 3477 shellPath); 3478 } 3479 3480 // Does the actual work for execute() and executeShell(). 3481 private auto executeImpl(alias pipeFunc, Cmd, ExtraPipeFuncArgs...)( 3482 Cmd commandLine, 3483 const string[string] env = null, 3484 Config config = Config.none, 3485 size_t maxOutput = size_t.max, 3486 scope const(char)[] workDir = null, 3487 ExtraPipeFuncArgs extraArgs = ExtraPipeFuncArgs.init) 3488 @trusted //TODO: @safe 3489 { 3490 import std.algorithm.comparison : min; 3491 import std.array : appender; 3492 import std.typecons : Tuple; 3493 3494 auto redirect = (config.flags & Config.Flags.stderrPassThrough) 3495 ? Redirect.stdout 3496 : Redirect.stdout | Redirect.stderrToStdout; 3497 3498 auto p = pipeFunc(commandLine, redirect, 3499 env, config, workDir, extraArgs); 3500 3501 auto a = appender!string; 3502 enum size_t defaultChunkSize = 4096; 3503 immutable chunkSize = min(maxOutput, defaultChunkSize); 3504 3505 // Store up to maxOutput bytes in a. 3506 foreach (ubyte[] chunk; p.stdout.byChunk(chunkSize)) 3507 { 3508 immutable size_t remain = maxOutput - a.data.length; 3509 3510 if (chunk.length < remain) a.put(chunk); 3511 else 3512 { 3513 a.put(chunk[0 .. remain]); 3514 break; 3515 } 3516 } 3517 // Exhaust the stream, if necessary. 3518 foreach (ubyte[] chunk; p.stdout.byChunk(defaultChunkSize)) { } 3519 3520 return Tuple!(int, "status", string, "output")(wait(p.pid), a.data); 3521 } 3522 3523 @system unittest 3524 { 3525 import std.string; 3526 // To avoid printing the newline characters, we use the echo|set trick on 3527 // Windows, and printf on POSIX (neither echo -n nor echo \c are portable). 3528 version (Windows) TestScript prog = 3529 "echo|set /p=%~1 3530 echo|set /p=%~2 1>&2 3531 exit 123"; 3532 else version (Android) TestScript prog = 3533 `echo -n $1 3534 echo -n $2 >&2 3535 exit 123`; 3536 else version (Posix) TestScript prog = 3537 `printf '%s' $1 3538 printf '%s' $2 >&2 3539 exit 123`; 3540 auto r = execute([prog.path, "foo", "bar"]); 3541 assert(r.status == 123); 3542 assert(r.output.stripRight() == "foobar"); 3543 auto s = execute([prog.path, "Hello", "World"]); 3544 assert(s.status == 123); 3545 assert(s.output.stripRight() == "HelloWorld"); 3546 } 3547 3548 @safe unittest 3549 { 3550 import std.string; 3551 auto r1 = executeShell("echo foo"); 3552 assert(r1.status == 0); 3553 assert(r1.output.chomp() == "foo"); 3554 auto r2 = executeShell("echo bar 1>&2"); 3555 assert(r2.status == 0); 3556 assert(r2.output.chomp().stripRight() == "bar"); 3557 auto r3 = executeShell("exit 123"); 3558 assert(r3.status == 123); 3559 assert(r3.output.empty); 3560 } 3561 3562 @system unittest 3563 { 3564 // Temporarily disable output to stderr so as to not spam the build log. 3565 import std.stdio : stderr; 3566 import std.typecons : Tuple; 3567 import std.file : readText, exists, remove; 3568 import std.traits : ReturnType; 3569 3570 ReturnType!executeShell r; 3571 auto tmpname = uniqueTempPath; 3572 scope(exit) if (exists(tmpname)) remove(tmpname); 3573 auto t = stderr; 3574 // Open a new scope to minimize code ran with stderr redirected. 3575 { 3576 stderr.open(tmpname, "w"); 3577 scope(exit) stderr = t; 3578 r = executeShell("echo D rox>&2", null, Config.stderrPassThrough); 3579 } 3580 assert(r.status == 0); 3581 assert(r.output.empty); 3582 auto witness = readText(tmpname); 3583 import std.ascii : newline; 3584 assert(witness == "D rox" ~ newline, "'" ~ witness ~ "'"); 3585 } 3586 3587 @safe unittest 3588 { 3589 import std.typecons : Tuple; 3590 void foo() //Just test the compilation 3591 { 3592 auto ret1 = execute(["dummy", "arg"]); 3593 auto ret2 = executeShell("dummy arg"); 3594 static assert(is(typeof(ret1) == typeof(ret2))); 3595 3596 Tuple!(int, string) ret3 = execute(["dummy", "arg"]); 3597 } 3598 } 3599 3600 /// An exception that signals a problem with starting or waiting for a process. 3601 class ProcessException : Exception 3602 { 3603 import std.exception : basicExceptionCtors; 3604 mixin basicExceptionCtors; 3605 3606 // Creates a new ProcessException based on errno. 3607 static ProcessException newFromErrno(string customMsg = null, 3608 string file = __FILE__, 3609 size_t line = __LINE__) 3610 { 3611 import core.stdc.errno : errno; 3612 return newFromErrno(errno, customMsg, file, line); 3613 } 3614 3615 // ditto, but error number is provided by caller 3616 static ProcessException newFromErrno(int error, 3617 string customMsg = null, 3618 string file = __FILE__, 3619 size_t line = __LINE__) 3620 { 3621 import std.exception : errnoString; 3622 auto errnoMsg = errnoString(error); 3623 auto msg = customMsg.empty ? errnoMsg 3624 : customMsg ~ " (" ~ errnoMsg ~ ')'; 3625 return new ProcessException(msg, file, line); 3626 } 3627 3628 // Creates a new ProcessException based on GetLastError() (Windows only). 3629 version (Windows) 3630 static ProcessException newFromLastError(string customMsg = null, 3631 string file = __FILE__, 3632 size_t line = __LINE__) 3633 { 3634 auto lastMsg = generateSysErrorMsg(); 3635 auto msg = customMsg.empty ? lastMsg 3636 : customMsg ~ " (" ~ lastMsg ~ ')'; 3637 return new ProcessException(msg, file, line); 3638 } 3639 } 3640 3641 3642 /** 3643 Determines the path to the current user's preferred command interpreter. 3644 3645 On Windows, this function returns the contents of the COMSPEC environment 3646 variable, if it exists. Otherwise, it returns the result of $(LREF nativeShell). 3647 3648 On POSIX, `userShell` returns the contents of the SHELL environment 3649 variable, if it exists and is non-empty. Otherwise, it returns the result of 3650 $(LREF nativeShell). 3651 */ 3652 @property string userShell() @safe 3653 { 3654 version (Windows) return environment.get("COMSPEC", nativeShell); 3655 else version (Posix) return environment.get("SHELL", nativeShell); 3656 } 3657 3658 /** 3659 The platform-specific native shell path. 3660 3661 This function returns `"cmd.exe"` on Windows, `"/bin/sh"` on POSIX, and 3662 `"/system/bin/sh"` on Android. 3663 */ 3664 @property string nativeShell() @safe @nogc pure nothrow 3665 { 3666 version (Windows) return "cmd.exe"; 3667 else version (Android) return "/system/bin/sh"; 3668 else version (Posix) return "/bin/sh"; 3669 } 3670 3671 // A command-line switch that indicates to the shell that it should 3672 // interpret the following argument as a command to be executed. 3673 version (Posix) private immutable string shellSwitch = "-c"; 3674 version (Windows) private immutable string shellSwitch = "/C"; 3675 3676 // Unittest support code: TestScript takes a string that contains a 3677 // shell script for the current platform, and writes it to a temporary 3678 // file. On Windows the file name gets a .cmd extension, while on 3679 // POSIX its executable permission bit is set. The file is 3680 // automatically deleted when the object goes out of scope. 3681 version (StdUnittest) 3682 private struct TestScript 3683 { 3684 this(string code) @system 3685 { 3686 // @system due to chmod 3687 import std.ascii : newline; 3688 import std.file : write; 3689 version (Windows) 3690 { 3691 auto ext = ".cmd"; 3692 auto firstLine = "@echo off"; 3693 } 3694 else version (Posix) 3695 { 3696 auto ext = ""; 3697 auto firstLine = "#!" ~ nativeShell; 3698 } 3699 path = uniqueTempPath()~ext; 3700 write(path, firstLine ~ newline ~ code ~ newline); 3701 version (Posix) 3702 { 3703 import core.sys.posix.sys.stat : chmod; 3704 import std.conv : octal; 3705 chmod(path.tempCString(), octal!777); 3706 } 3707 } 3708 3709 ~this() 3710 { 3711 import std.file : remove, exists; 3712 if (!path.empty && exists(path)) 3713 { 3714 try { remove(path); } 3715 catch (Exception e) 3716 { 3717 debug std.stdio.stderr.writeln(e.msg); 3718 } 3719 } 3720 } 3721 3722 string path; 3723 } 3724 3725 3726 // ============================================================================= 3727 // Functions for shell command quoting/escaping. 3728 // ============================================================================= 3729 3730 3731 /* 3732 Command line arguments exist in three forms: 3733 1) string or char* array, as received by main. 3734 Also used internally on POSIX systems. 3735 2) Command line string, as used in Windows' 3736 CreateProcess and CommandLineToArgvW functions. 3737 A specific quoting and escaping algorithm is used 3738 to distinguish individual arguments. 3739 3) Shell command string, as written at a shell prompt 3740 or passed to cmd /C - this one may contain shell 3741 control characters, e.g. > or | for redirection / 3742 piping - thus, yet another layer of escaping is 3743 used to distinguish them from program arguments. 3744 3745 Except for escapeWindowsArgument, the intermediary 3746 format (2) is hidden away from the user in this module. 3747 */ 3748 3749 /** 3750 Escapes an argv-style argument array to be used with $(LREF spawnShell), 3751 $(LREF pipeShell) or $(LREF executeShell). 3752 --- 3753 string url = "http://dlang.org/"; 3754 executeShell(escapeShellCommand("wget", url, "-O", "dlang-index.html")); 3755 --- 3756 3757 Concatenate multiple `escapeShellCommand` and 3758 $(LREF escapeShellFileName) results to use shell redirection or 3759 piping operators. 3760 --- 3761 executeShell( 3762 escapeShellCommand("curl", "http://dlang.org/download.html") ~ 3763 "|" ~ 3764 escapeShellCommand("grep", "-o", `http://\S*\.zip`) ~ 3765 ">" ~ 3766 escapeShellFileName("D download links.txt")); 3767 --- 3768 3769 Throws: 3770 $(OBJECTREF Exception) if any part of the command line contains unescapable 3771 characters (NUL on all platforms, as well as CR and LF on Windows). 3772 */ 3773 string escapeShellCommand(scope const(char[])[] args...) @safe pure 3774 { 3775 if (args.empty) 3776 return null; 3777 version (Windows) 3778 { 3779 // Do not ^-escape the first argument (the program path), 3780 // as the shell parses it differently from parameters. 3781 // ^-escaping a program path that contains spaces will fail. 3782 string result = escapeShellFileName(args[0]); 3783 if (args.length > 1) 3784 { 3785 result ~= " " ~ escapeShellCommandString( 3786 escapeShellArguments(args[1..$])); 3787 } 3788 return result; 3789 } 3790 version (Posix) 3791 { 3792 return escapeShellCommandString(escapeShellArguments(args)); 3793 } 3794 } 3795 3796 @safe unittest 3797 { 3798 // This is a simple unit test without any special requirements, 3799 // in addition to the unittest_burnin one below which requires 3800 // special preparation. 3801 3802 struct TestVector { string[] args; string windows, posix; } 3803 TestVector[] tests = 3804 [ 3805 { 3806 args : ["foo bar"], 3807 windows : `"foo bar"`, 3808 posix : `'foo bar'` 3809 }, 3810 { 3811 args : ["foo bar", "hello"], 3812 windows : `"foo bar" hello`, 3813 posix : `'foo bar' hello` 3814 }, 3815 { 3816 args : ["foo bar", "hello world"], 3817 windows : `"foo bar" ^"hello world^"`, 3818 posix : `'foo bar' 'hello world'` 3819 }, 3820 { 3821 args : ["foo bar", "hello", "world"], 3822 windows : `"foo bar" hello world`, 3823 posix : `'foo bar' hello world` 3824 }, 3825 { 3826 args : ["foo bar", `'"^\`], 3827 windows : `"foo bar" ^"'\^"^^\\^"`, 3828 posix : `'foo bar' ''\''"^\'` 3829 }, 3830 { 3831 args : ["foo bar", ""], 3832 windows : `"foo bar" ^"^"`, 3833 posix : `'foo bar' ''` 3834 }, 3835 { 3836 args : ["foo bar", "2"], 3837 windows : `"foo bar" ^"2^"`, 3838 posix : `'foo bar' '2'` 3839 }, 3840 ]; 3841 3842 foreach (test; tests) 3843 { 3844 auto actual = escapeShellCommand(test.args); 3845 version (Windows) 3846 string expected = test.windows; 3847 else 3848 string expected = test.posix; 3849 assert(actual == expected, "\nExpected: " ~ expected ~ "\nGot: " ~ actual); 3850 } 3851 } 3852 3853 private string escapeShellCommandString(return scope string command) @safe pure 3854 { 3855 version (Windows) 3856 return escapeWindowsShellCommand(command); 3857 else 3858 return command; 3859 } 3860 3861 private string escapeWindowsShellCommand(scope const(char)[] command) @safe pure 3862 { 3863 import std.array : appender; 3864 auto result = appender!string(); 3865 result.reserve(command.length); 3866 3867 foreach (c; command) 3868 switch (c) 3869 { 3870 case '\0': 3871 throw new Exception("Cannot put NUL in command line"); 3872 case '\r': 3873 case '\n': 3874 throw new Exception("CR/LF are not escapable"); 3875 case '\x01': .. case '\x09': 3876 case '\x0B': .. case '\x0C': 3877 case '\x0E': .. case '\x1F': 3878 case '"': 3879 case '^': 3880 case '&': 3881 case '<': 3882 case '>': 3883 case '|': 3884 result.put('^'); 3885 goto default; 3886 default: 3887 result.put(c); 3888 } 3889 return result.data; 3890 } 3891 3892 private string escapeShellArguments(scope const(char[])[] args...) 3893 @trusted pure nothrow 3894 { 3895 import std.exception : assumeUnique; 3896 char[] buf; 3897 3898 @safe nothrow 3899 char[] allocator(size_t size) 3900 { 3901 if (buf.length == 0) 3902 return buf = new char[size]; 3903 else 3904 { 3905 auto p = buf.length; 3906 buf.length = buf.length + 1 + size; 3907 buf[p++] = ' '; 3908 return buf[p .. p+size]; 3909 } 3910 } 3911 3912 foreach (arg; args) 3913 escapeShellArgument!allocator(arg); 3914 return assumeUnique(buf); 3915 } 3916 3917 private auto escapeShellArgument(alias allocator)(scope const(char)[] arg) @safe nothrow 3918 { 3919 // The unittest for this function requires special 3920 // preparation - see below. 3921 3922 version (Windows) 3923 return escapeWindowsArgumentImpl!allocator(arg); 3924 else 3925 return escapePosixArgumentImpl!allocator(arg); 3926 } 3927 3928 /** 3929 Quotes a command-line argument in a manner conforming to the behavior of 3930 $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx, 3931 CommandLineToArgvW). 3932 */ 3933 string escapeWindowsArgument(scope const(char)[] arg) @trusted pure nothrow 3934 { 3935 // Rationale for leaving this function as public: 3936 // this algorithm of escaping paths is also used in other software, 3937 // e.g. DMD's response files. 3938 import std.exception : assumeUnique; 3939 auto buf = escapeWindowsArgumentImpl!charAllocator(arg); 3940 return assumeUnique(buf); 3941 } 3942 3943 3944 private char[] charAllocator(size_t size) @safe pure nothrow 3945 { 3946 return new char[size]; 3947 } 3948 3949 3950 private char[] escapeWindowsArgumentImpl(alias allocator)(scope const(char)[] arg) 3951 @safe nothrow 3952 if (is(typeof(allocator(size_t.init)[0] = char.init))) 3953 { 3954 // References: 3955 // * http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx 3956 // * http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx 3957 3958 // Check if the string needs to be escaped, 3959 // and calculate the total string size. 3960 3961 // Trailing backslashes must be escaped 3962 bool escaping = true; 3963 bool needEscape = false; 3964 // Result size = input size + 2 for surrounding quotes + 1 for the 3965 // backslash for each escaped character. 3966 size_t size = 1 + arg.length + 1; 3967 3968 foreach_reverse (char c; arg) 3969 { 3970 if (c == '"') 3971 { 3972 needEscape = true; 3973 escaping = true; 3974 size++; 3975 } 3976 else 3977 if (c == '\\') 3978 { 3979 if (escaping) 3980 size++; 3981 } 3982 else 3983 { 3984 if (c == ' ' || c == '\t') 3985 needEscape = true; 3986 escaping = false; 3987 } 3988 } 3989 3990 import std.ascii : isDigit; 3991 // Empty arguments need to be specified as "" 3992 if (!arg.length) 3993 needEscape = true; 3994 else 3995 // Arguments ending with digits need to be escaped, 3996 // to disambiguate with 1>file redirection syntax 3997 if (isDigit(arg[$-1])) 3998 needEscape = true; 3999 4000 if (!needEscape) 4001 return allocator(arg.length)[] = arg; 4002 4003 // Construct result string. 4004 4005 auto buf = allocator(size); 4006 size_t p = size; 4007 buf[--p] = '"'; 4008 escaping = true; 4009 foreach_reverse (char c; arg) 4010 { 4011 if (c == '"') 4012 escaping = true; 4013 else 4014 if (c != '\\') 4015 escaping = false; 4016 4017 buf[--p] = c; 4018 if (escaping) 4019 buf[--p] = '\\'; 4020 } 4021 buf[--p] = '"'; 4022 assert(p == 0); 4023 4024 return buf; 4025 } 4026 4027 version (Windows) version (StdUnittest) 4028 { 4029 private: 4030 import core.stdc.stddef; 4031 import core.stdc.wchar_ : wcslen; 4032 import core.sys.windows.shellapi : CommandLineToArgvW; 4033 import core.sys.windows.winbase; 4034 import core.sys.windows.winnt; 4035 import std.array; 4036 4037 string[] parseCommandLine(string line) 4038 { 4039 import std.algorithm.iteration : map; 4040 import std.array : array; 4041 import std.conv : to; 4042 auto lpCommandLine = (to!(WCHAR[])(line) ~ '\0').ptr; 4043 int numArgs; 4044 auto args = CommandLineToArgvW(lpCommandLine, &numArgs); 4045 scope(exit) LocalFree(args); 4046 return args[0 .. numArgs] 4047 .map!(arg => to!string(arg[0 .. wcslen(arg)])) 4048 .array(); 4049 } 4050 4051 @system unittest 4052 { 4053 import std.conv : text; 4054 string[] testStrings = [ 4055 `Hello`, 4056 `Hello, world`, 4057 `Hello, "world"`, 4058 `C:\`, 4059 `C:\dmd`, 4060 `C:\Program Files\`, 4061 ]; 4062 4063 enum CHARS = `_x\" *&^` ~ "\t"; // _ is placeholder for nothing 4064 foreach (c1; CHARS) 4065 foreach (c2; CHARS) 4066 foreach (c3; CHARS) 4067 foreach (c4; CHARS) 4068 testStrings ~= [c1, c2, c3, c4].replace("_", ""); 4069 4070 foreach (s; testStrings) 4071 { 4072 auto q = escapeWindowsArgument(s); 4073 auto args = parseCommandLine("Dummy.exe " ~ q); 4074 assert(args.length == 2, s ~ " => " ~ q ~ " #" ~ text(args.length-1)); 4075 assert(args[1] == s, s ~ " => " ~ q ~ " => " ~ args[1]); 4076 } 4077 } 4078 } 4079 4080 private string escapePosixArgument(scope const(char)[] arg) @trusted pure nothrow 4081 { 4082 import std.exception : assumeUnique; 4083 auto buf = escapePosixArgumentImpl!charAllocator(arg); 4084 return assumeUnique(buf); 4085 } 4086 4087 private char[] escapePosixArgumentImpl(alias allocator)(scope const(char)[] arg) 4088 @safe nothrow 4089 if (is(typeof(allocator(size_t.init)[0] = char.init))) 4090 { 4091 bool needQuoting = { 4092 import std.ascii : isAlphaNum, isDigit; 4093 import std.algorithm.comparison : among; 4094 4095 // Empty arguments need to be specified as '' 4096 if (arg.length == 0) 4097 return true; 4098 // Arguments ending with digits need to be escaped, 4099 // to disambiguate with 1>file redirection syntax 4100 if (isDigit(arg[$-1])) 4101 return true; 4102 4103 // Obtained using: 4104 // for n in $(seq 1 255) ; do 4105 // c=$(printf \\$(printf "%o" $n)) 4106 // q=$(/bin/printf '%q' "$c") 4107 // if [[ "$q" == "$c" ]] ; then printf "%s, " "'$c'" ; fi 4108 // done 4109 // printf '\n' 4110 foreach (char c; arg) 4111 if (!isAlphaNum(c) && !c.among('%', '+', ',', '-', '.', '/', ':', '@', ']', '_')) 4112 return true; 4113 return false; 4114 }(); 4115 if (!needQuoting) 4116 { 4117 auto buf = allocator(arg.length); 4118 buf[] = arg; 4119 return buf; 4120 } 4121 4122 // '\'' means: close quoted part of argument, append an escaped 4123 // single quote, and reopen quotes 4124 4125 // Below code is equivalent to: 4126 // return `'` ~ std.array.replace(arg, `'`, `'\''`) ~ `'`; 4127 4128 size_t size = 1 + arg.length + 1; 4129 foreach (char c; arg) 4130 if (c == '\'') 4131 size += 3; 4132 4133 auto buf = allocator(size); 4134 size_t p = 0; 4135 buf[p++] = '\''; 4136 foreach (char c; arg) 4137 if (c == '\'') 4138 { 4139 buf[p .. p+4] = `'\''`; 4140 p += 4; 4141 } 4142 else 4143 buf[p++] = c; 4144 buf[p++] = '\''; 4145 assert(p == size); 4146 4147 return buf; 4148 } 4149 4150 /** 4151 Escapes a filename to be used for shell redirection with $(LREF spawnShell), 4152 $(LREF pipeShell) or $(LREF executeShell). 4153 */ 4154 string escapeShellFileName(scope const(char)[] fileName) @trusted pure nothrow 4155 { 4156 // The unittest for this function requires special 4157 // preparation - see below. 4158 4159 version (Windows) 4160 { 4161 // If a file starts with &, it can cause cmd.exe to misinterpret 4162 // the file name as the stream redirection syntax: 4163 // command > "&foo.txt" 4164 // gets interpreted as 4165 // command >&foo.txt 4166 // Prepend .\ to disambiguate. 4167 4168 if (fileName.length && fileName[0] == '&') 4169 return cast(string)(`".\` ~ fileName ~ '"'); 4170 4171 return cast(string)('"' ~ fileName ~ '"'); 4172 } 4173 else 4174 return escapePosixArgument(fileName); 4175 } 4176 4177 // Loop generating strings with random characters 4178 //version = unittest_burnin; 4179 4180 version (unittest_burnin) 4181 @system unittest 4182 { 4183 // There are no readily-available commands on all platforms suitable 4184 // for properly testing command escaping. The behavior of CMD's "echo" 4185 // built-in differs from the POSIX program, and Windows ports of POSIX 4186 // environments (Cygwin, msys, gnuwin32) may interfere with their own 4187 // "echo" ports. 4188 4189 // To run this unit test, create std_process_unittest_helper.d with the 4190 // following content and compile it: 4191 // import std.stdio, std.array; void main(string[] args) { write(args.join("\0")); } 4192 // Then, test this module with: 4193 // rdmd --main -unittest -version=unittest_burnin process.d 4194 4195 import std.file : readText, remove; 4196 import std.format : format; 4197 import std.path : absolutePath; 4198 import std.random : uniform; 4199 4200 auto helper = absolutePath("std_process_unittest_helper"); 4201 assert(executeShell(helper ~ " hello").output.split("\0")[1..$] == ["hello"], "Helper malfunction"); 4202 4203 void test(string[] s, string fn) 4204 { 4205 string e; 4206 string[] g; 4207 4208 e = escapeShellCommand(helper ~ s); 4209 { 4210 scope(failure) writefln("executeShell() failed.\nExpected:\t%s\nEncoded:\t%s", s, [e]); 4211 auto result = executeShell(e); 4212 assert(result.status == 0, "std_process_unittest_helper failed"); 4213 g = result.output.split("\0")[1..$]; 4214 } 4215 assert(s == g, format("executeShell() test failed.\nExpected:\t%s\nGot:\t\t%s\nEncoded:\t%s", s, g, [e])); 4216 4217 e = escapeShellCommand(helper ~ s) ~ ">" ~ escapeShellFileName(fn); 4218 { 4219 scope(failure) writefln( 4220 "executeShell() with redirect failed.\nExpected:\t%s\nFilename:\t%s\nEncoded:\t%s", s, [fn], [e]); 4221 auto result = executeShell(e); 4222 assert(result.status == 0, "std_process_unittest_helper failed"); 4223 assert(!result.output.length, "No output expected, got:\n" ~ result.output); 4224 g = readText(fn).split("\0")[1..$]; 4225 } 4226 remove(fn); 4227 assert(s == g, 4228 format("executeShell() with redirect test failed.\nExpected:\t%s\nGot:\t\t%s\nEncoded:\t%s", s, g, [e])); 4229 } 4230 4231 while (true) 4232 { 4233 string[] args; 4234 foreach (n; 0 .. uniform(1, 4)) 4235 { 4236 string arg; 4237 foreach (l; 0 .. uniform(0, 10)) 4238 { 4239 dchar c; 4240 while (true) 4241 { 4242 version (Windows) 4243 { 4244 // As long as DMD's system() uses CreateProcessA, 4245 // we can't reliably pass Unicode 4246 c = uniform(0, 128); 4247 } 4248 else 4249 c = uniform!ubyte(); 4250 4251 if (c == 0) 4252 continue; // argv-strings are zero-terminated 4253 version (Windows) 4254 if (c == '\r' || c == '\n') 4255 continue; // newlines are unescapable on Windows 4256 break; 4257 } 4258 arg ~= c; 4259 } 4260 args ~= arg; 4261 } 4262 4263 // generate filename 4264 string fn; 4265 foreach (l; 0 .. uniform(1, 10)) 4266 { 4267 dchar c; 4268 while (true) 4269 { 4270 version (Windows) 4271 c = uniform(0, 128); // as above 4272 else 4273 c = uniform!ubyte(); 4274 4275 if (c == 0 || c == '/') 4276 continue; // NUL and / are the only characters 4277 // forbidden in POSIX filenames 4278 version (Windows) 4279 if (c < '\x20' || c == '<' || c == '>' || c == ':' || 4280 c == '"' || c == '\\' || c == '|' || c == '?' || c == '*') 4281 continue; // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx 4282 break; 4283 } 4284 4285 fn ~= c; 4286 } 4287 fn = fn[0..$/2] ~ "_testfile_" ~ fn[$/2..$]; 4288 4289 test(args, fn); 4290 } 4291 } 4292 4293 // ============================================================================= 4294 // Everything below this line was part of the old std.process, and most of 4295 // it will be deprecated and removed. 4296 // ============================================================================= 4297 4298 4299 /* 4300 Copyright: Copyright The D Language Foundation 2007 - 2009. 4301 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 4302 Authors: $(HTTP digitalmars.com, Walter Bright), 4303 $(HTTP erdani.org, Andrei Alexandrescu), 4304 $(HTTP thecybershadow.net, Vladimir Panteleev) 4305 Source: $(PHOBOSSRC std/_process.d) 4306 */ 4307 /* 4308 Copyright The D Language Foundation 2007 - 2009. 4309 Distributed under the Boost Software License, Version 1.0. 4310 (See accompanying file LICENSE_1_0.txt or copy at 4311 http://www.boost.org/LICENSE_1_0.txt) 4312 */ 4313 4314 4315 import core.stdc.errno; 4316 import core.stdc.stdlib; 4317 import core.stdc.string; 4318 import core.thread; 4319 4320 version (Windows) 4321 { 4322 import std.file, std.format, std.random; 4323 } 4324 version (Posix) 4325 { 4326 import core.sys.posix.stdlib; 4327 } 4328 4329 private const(char)** toAStringz(in string[] a) 4330 { 4331 import std.string : toStringz; 4332 auto p = (new const(char)*[1 + a.length]).ptr; 4333 foreach (i, string s; a) 4334 p[i] = toStringz(s); 4335 p[a.length] = null; 4336 return p; 4337 } 4338 4339 4340 /* ========================================================== */ 4341 4342 //version (Windows) 4343 //{ 4344 // int spawnvp(int mode, string pathname, string[] argv) 4345 // { 4346 // char** argv_ = cast(char**) core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length)); 4347 // scope(exit) core.stdc.stdlib.free(argv_); 4348 // 4349 // toAStringz(argv, argv_); 4350 // 4351 // return spawnvp(mode, pathname.tempCString(), argv_); 4352 // } 4353 //} 4354 4355 // Incorporating idea (for spawnvp() on Posix) from Dave Fladebo 4356 4357 enum { _P_WAIT, _P_NOWAIT, _P_OVERLAY } 4358 version (Windows) extern(C) int spawnvp(int, scope const(char) *, scope const(char*)*); 4359 alias P_WAIT = _P_WAIT; 4360 alias P_NOWAIT = _P_NOWAIT; 4361 4362 /* ========================================================== */ 4363 4364 version (StdDdoc) 4365 { 4366 /** 4367 Replaces the current process by executing a command, `pathname`, with 4368 the arguments in `argv`. 4369 4370 $(BLUE This function is Posix-Only.) 4371 4372 Typically, the first element of `argv` is 4373 the command being executed, i.e. $(D argv[0] == pathname). The 'p' 4374 versions of `exec` search the PATH environment variable for $(D 4375 pathname). The 'e' versions additionally take the new process' 4376 environment variables as an array of strings of the form key=value. 4377 4378 Does not return on success (the current process will have been 4379 replaced). Returns -1 on failure with no indication of the 4380 underlying error. 4381 4382 Windows_specific: 4383 These functions are only supported on POSIX platforms, as the Windows 4384 operating systems do not provide the ability to overwrite the current 4385 process image with another. In single-threaded programs it is possible 4386 to approximate the effect of `execv*` by using $(LREF spawnProcess) 4387 and terminating the current process once the child process has returned. 4388 For example: 4389 --- 4390 auto commandLine = [ "program", "arg1", "arg2" ]; 4391 version (Posix) 4392 { 4393 execv(commandLine[0], commandLine); 4394 throw new Exception("Failed to execute program"); 4395 } 4396 else version (Windows) 4397 { 4398 import core.stdc.stdlib : _Exit; 4399 _Exit(wait(spawnProcess(commandLine))); 4400 } 4401 --- 4402 This is, however, NOT equivalent to POSIX' `execv*`. For one thing, the 4403 executed program is started as a separate process, with all this entails. 4404 Secondly, in a multithreaded program, other threads will continue to do 4405 work while the current thread is waiting for the child process to complete. 4406 4407 A better option may sometimes be to terminate the current program immediately 4408 after spawning the child process. This is the behaviour exhibited by the 4409 $(LINK2 http://msdn.microsoft.com/en-us/library/431x4c1w.aspx,`__exec`) 4410 functions in Microsoft's C runtime library, and it is how D's now-deprecated 4411 Windows `execv*` functions work. Example: 4412 --- 4413 auto commandLine = [ "program", "arg1", "arg2" ]; 4414 version (Posix) 4415 { 4416 execv(commandLine[0], commandLine); 4417 throw new Exception("Failed to execute program"); 4418 } 4419 else version (Windows) 4420 { 4421 spawnProcess(commandLine); 4422 import core.stdc.stdlib : _exit; 4423 _exit(0); 4424 } 4425 --- 4426 */ 4427 int execv(in string pathname, in string[] argv); 4428 ///ditto 4429 int execve(in string pathname, in string[] argv, in string[] envp); 4430 /// ditto 4431 int execvp(in string pathname, in string[] argv); 4432 /// ditto 4433 int execvpe(in string pathname, in string[] argv, in string[] envp); 4434 } 4435 else version (Posix) 4436 { 4437 int execv(in string pathname, in string[] argv) 4438 { 4439 return execv_(pathname, argv); 4440 } 4441 int execve(in string pathname, in string[] argv, in string[] envp) 4442 { 4443 return execve_(pathname, argv, envp); 4444 } 4445 int execvp(in string pathname, in string[] argv) 4446 { 4447 return execvp_(pathname, argv); 4448 } 4449 int execvpe(in string pathname, in string[] argv, in string[] envp) 4450 { 4451 return execvpe_(pathname, argv, envp); 4452 } 4453 } 4454 4455 // Move these C declarations to druntime if we decide to keep the D wrappers 4456 extern(C) 4457 { 4458 int execv(scope const(char) *, scope const(char *)*); 4459 int execve(scope const(char)*, scope const(char*)*, scope const(char*)*); 4460 int execvp(scope const(char)*, scope const(char*)*); 4461 version (Windows) int execvpe(scope const(char)*, scope const(char*)*, scope const(char*)*); 4462 } 4463 4464 private int execv_(in string pathname, in string[] argv) 4465 { 4466 return execv(pathname.tempCString(), toAStringz(argv)); 4467 } 4468 4469 private int execve_(in string pathname, in string[] argv, in string[] envp) 4470 { 4471 return execve(pathname.tempCString(), toAStringz(argv), toAStringz(envp)); 4472 } 4473 4474 private int execvp_(in string pathname, in string[] argv) 4475 { 4476 return execvp(pathname.tempCString(), toAStringz(argv)); 4477 } 4478 4479 private int execvpe_(in string pathname, in string[] argv, in string[] envp) 4480 { 4481 version (Posix) 4482 { 4483 import std.array : split; 4484 import std.conv : to; 4485 // Is pathname rooted? 4486 if (pathname[0] == '/') 4487 { 4488 // Yes, so just call execve() 4489 return execve(pathname, argv, envp); 4490 } 4491 else 4492 { 4493 // No, so must traverse PATHs, looking for first match 4494 string[] envPaths = split( 4495 to!string(core.stdc.stdlib.getenv("PATH")), ":"); 4496 int iRet = 0; 4497 4498 // Note: if any call to execve() succeeds, this process will cease 4499 // execution, so there's no need to check the execve() result through 4500 // the loop. 4501 4502 foreach (string pathDir; envPaths) 4503 { 4504 string composite = cast(string) (pathDir ~ "/" ~ pathname); 4505 4506 iRet = execve(composite, argv, envp); 4507 } 4508 if (0 != iRet) 4509 { 4510 iRet = execve(pathname, argv, envp); 4511 } 4512 4513 return iRet; 4514 } 4515 } 4516 else version (Windows) 4517 { 4518 return execvpe(pathname.tempCString(), toAStringz(argv), toAStringz(envp)); 4519 } 4520 else 4521 { 4522 static assert(0); 4523 } // version 4524 } 4525 4526 version (StdDdoc) 4527 { 4528 /**************************************** 4529 * Start up the browser and set it to viewing the page at url. 4530 */ 4531 void browse(scope const(char)[] url); 4532 } 4533 else 4534 version (Windows) 4535 { 4536 import core.sys.windows.shellapi, core.sys.windows.winuser; 4537 4538 pragma(lib,"shell32.lib"); 4539 4540 void browse(scope const(char)[] url) nothrow @nogc @trusted 4541 { 4542 ShellExecuteW(null, "open", url.tempCStringW(), null, null, SW_SHOWNORMAL); 4543 } 4544 } 4545 else version (Posix) 4546 { 4547 import core.stdc.stdio; 4548 import core.stdc.string; 4549 import core.sys.posix.unistd; 4550 4551 void browse(scope const(char)[] url) nothrow @nogc @safe 4552 { 4553 const buffer = url.tempCString(); // Retain buffer until end of scope 4554 const(char)*[3] args; 4555 4556 // Trusted because it's called with a zero-terminated literal 4557 const(char)* browser = (() @trusted => core.stdc.stdlib.getenv("BROWSER"))(); 4558 if (browser) 4559 { 4560 // String already zero-terminated 4561 browser = (() @trusted => strdup(browser))(); 4562 args[0] = browser; 4563 } 4564 else 4565 { 4566 version (OSX) 4567 { 4568 args[0] = "open"; 4569 } 4570 else 4571 { 4572 //args[0] = "x-www-browser"; // doesn't work on some systems 4573 args[0] = "xdg-open"; 4574 } 4575 } 4576 4577 args[1] = buffer; 4578 args[2] = null; 4579 4580 auto childpid = core.sys.posix.unistd.fork(); 4581 if (childpid == 0) 4582 { 4583 // Trusted because args and all entries are always zero-terminated 4584 (() @trusted { 4585 core.sys.posix.unistd.execvp(args[0], &args[0]); 4586 perror(args[0]); 4587 core.sys.posix.unistd._exit(1); 4588 })(); 4589 assert(0, "Child failed to exec"); 4590 } 4591 if (browser) 4592 // Trusted because it's allocated via strdup above 4593 (() @trusted => free(cast(void*) browser))(); 4594 4595 version (StdUnittest) 4596 { 4597 // Verify that the test script actually suceeds 4598 int status; 4599 const check = (() @trusted => waitpid(childpid, &status, 0))(); 4600 assert(check != -1); 4601 assert(status == 0); 4602 } 4603 } 4604 } 4605 else 4606 static assert(0, "os not supported"); 4607 4608 // Verify attributes are consistent between all implementations 4609 @safe @nogc nothrow unittest 4610 { 4611 if (false) 4612 browse(""); 4613 } 4614 4615 version (Windows) { /* Doesn't use BROWSER */ } 4616 else 4617 @system unittest 4618 { 4619 import std.conv : text; 4620 import std.range : repeat; 4621 immutable string url = text("http://", repeat('x', 249)); 4622 4623 TestScript prog = `if [ "$1" != "` ~ url ~ `" ]; then exit 1; fi`; 4624 environment["BROWSER"] = prog.path; 4625 browse(url); 4626 }