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 auto status = InternalError.noerror; 1251 auto readExecResult = core.sys.posix.unistd.read(forkPipe[0], &status, status.sizeof); 1252 // Save error number just in case if subsequent "waitpid" fails and overrides errno 1253 immutable lastError = .errno; 1254 1255 if (config.flags & Config.Flags.detached) 1256 { 1257 // Forked child exits right after creating second fork. So it should be safe to wait here. 1258 import core.sys.posix.sys.wait : waitpid; 1259 int waitResult; 1260 waitpid(id, &waitResult, 0); 1261 } 1262 1263 if (readExecResult == -1) 1264 throw ProcessException.newFromErrno(lastError, "Could not read from pipe to get child status"); 1265 1266 bool owned = true; 1267 if (status != InternalError.noerror) 1268 { 1269 int error; 1270 readExecResult = read(forkPipe[0], &error, error.sizeof); 1271 string errorMsg; 1272 final switch (status) 1273 { 1274 case InternalError.chdir: 1275 errorMsg = "Failed to set working directory"; 1276 break; 1277 case InternalError.getrlimit: 1278 errorMsg = "getrlimit failed"; 1279 break; 1280 case InternalError.exec: 1281 errorMsg = "Failed to execute '" ~ cast(string) name ~ "'"; 1282 break; 1283 case InternalError.doubleFork: 1284 // Can happen only when starting detached process 1285 assert(config.flags & Config.Flags.detached); 1286 errorMsg = "Failed to fork twice"; 1287 break; 1288 case InternalError.malloc: 1289 errorMsg = "Failed to allocate memory"; 1290 break; 1291 case InternalError.preExec: 1292 errorMsg = "Failed to execute preExecFunction or preExecDelegate"; 1293 break; 1294 case InternalError.closefds_dup2: 1295 assert(!(config.flags & Config.Flags.inheritFDs)); 1296 errorMsg = "Failed to close inherited file descriptors"; 1297 break; 1298 case InternalError.noerror: 1299 assert(false); 1300 } 1301 if (readExecResult == error.sizeof) 1302 throw ProcessException.newFromErrno(error, errorMsg); 1303 throw new ProcessException(errorMsg); 1304 } 1305 else if (config.flags & Config.Flags.detached) 1306 { 1307 owned = false; 1308 if (read(pidPipe[0], &id, id.sizeof) != id.sizeof) 1309 throw ProcessException.newFromErrno("Could not read from pipe to get detached process id"); 1310 } 1311 1312 // Parent process: Close streams and return. 1313 if (!(config.flags & Config.Flags.retainStdin ) && stdinFD > STDERR_FILENO 1314 && stdinFD != getFD(std.stdio.stdin )) 1315 stdin.close(); 1316 if (!(config.flags & Config.Flags.retainStdout) && stdoutFD > STDERR_FILENO 1317 && stdoutFD != getFD(std.stdio.stdout)) 1318 stdout.close(); 1319 if (!(config.flags & Config.Flags.retainStderr) && stderrFD > STDERR_FILENO 1320 && stderrFD != getFD(std.stdio.stderr)) 1321 stderr.close(); 1322 return new Pid(id, owned); 1323 } 1324 } 1325 1326 version (Posix) 1327 @system unittest 1328 { 1329 import std.concurrency : ownerTid, receiveTimeout, send, spawn; 1330 import std.datetime : seconds; 1331 1332 sigset_t ss; 1333 sigemptyset(&ss); 1334 sigaddset(&ss, SIGINT); 1335 pthread_sigmask(SIG_BLOCK, &ss, null); 1336 1337 Config config = { 1338 preExecFunction: () @trusted @nogc nothrow { 1339 // Reset signal handlers 1340 sigset_t ss; 1341 if (sigfillset(&ss) != 0) 1342 { 1343 return false; 1344 } 1345 if (sigprocmask(SIG_UNBLOCK, &ss, null) != 0) 1346 { 1347 return false; 1348 } 1349 return true; 1350 }, 1351 }; 1352 1353 auto pid = spawnProcess(["sleep", "10000"], 1354 std.stdio.stdin, 1355 std.stdio.stdout, 1356 std.stdio.stderr, 1357 null, 1358 config, 1359 null); 1360 scope(failure) 1361 { 1362 kill(pid, SIGKILL); 1363 wait(pid); 1364 } 1365 1366 // kill the spawned process with SIGINT 1367 // and send its return code 1368 spawn((shared Pid pid) { 1369 auto p = cast() pid; 1370 kill(p, SIGINT); 1371 auto code = wait(p); 1372 assert(code < 0); 1373 send(ownerTid, code); 1374 }, cast(shared) pid); 1375 1376 auto received = receiveTimeout(3.seconds, (int) {}); 1377 assert(received); 1378 } 1379 1380 version (Posix) 1381 @system unittest 1382 { 1383 __gshared int j; 1384 foreach (i; 0 .. 3) 1385 { 1386 auto config = Config( 1387 preExecFunction: function() @trusted { 1388 j = 1; 1389 return true; 1390 }, 1391 preExecDelegate: delegate() @trusted { 1392 // j should now be 1, as preExecFunction is called before 1393 // preExecDelegate is. 1394 _Exit(i + j); 1395 return true; 1396 }, 1397 ); 1398 auto pid = spawnProcess(["false"], config: config); 1399 assert(wait(pid) == i + 1); 1400 } 1401 } 1402 1403 /* 1404 Implementation of spawnProcess() for Windows. 1405 1406 commandLine must contain the entire command line, properly 1407 quoted/escaped as required by CreateProcessW(). 1408 1409 envz must be a pointer to a block of UTF-16 characters on the form 1410 "var1=value1\0var2=value2\0...varN=valueN\0\0". 1411 */ 1412 version (Windows) 1413 private Pid spawnProcessWin(scope const(char)[] commandLine, 1414 scope const(char)[] program, 1415 File stdin, 1416 File stdout, 1417 File stderr, 1418 scope const string[string] env, 1419 Config config, 1420 scope const(char)[] workDir) 1421 @trusted 1422 { 1423 import core.exception : RangeError; 1424 import std.conv : text; 1425 1426 if (commandLine.empty) throw new RangeError("Command line is empty"); 1427 1428 // Prepare environment. 1429 auto envz = createEnv(env, !(config.flags & Config.Flags.newEnv)); 1430 1431 // Startup info for CreateProcessW(). 1432 STARTUPINFO_W startinfo; 1433 startinfo.cb = startinfo.sizeof; 1434 static int getFD(ref File f) { return f.isOpen ? f.fileno : -1; } 1435 1436 // Extract file descriptors and HANDLEs from the streams and make the 1437 // handles inheritable. 1438 static void prepareStream(ref File file, DWORD stdHandle, string which, 1439 out int fileDescriptor, out HANDLE handle) 1440 { 1441 enum _NO_CONSOLE_FILENO = cast(HANDLE)-2; 1442 fileDescriptor = getFD(file); 1443 handle = null; 1444 if (fileDescriptor >= 0) 1445 handle = file.windowsHandle; 1446 // Windows GUI applications have a fd but not a valid Windows HANDLE. 1447 if (handle is null || handle == INVALID_HANDLE_VALUE || handle == _NO_CONSOLE_FILENO) 1448 handle = GetStdHandle(stdHandle); 1449 1450 DWORD dwFlags; 1451 if (GetHandleInformation(handle, &dwFlags)) 1452 { 1453 if (!(dwFlags & HANDLE_FLAG_INHERIT)) 1454 { 1455 if (!SetHandleInformation(handle, 1456 HANDLE_FLAG_INHERIT, 1457 HANDLE_FLAG_INHERIT)) 1458 { 1459 throw new StdioException( 1460 "Failed to make "~which~" stream inheritable by child process (" 1461 ~generateSysErrorMsg() ~ ')', 1462 0); 1463 } 1464 } 1465 } 1466 } 1467 int stdinFD = -1, stdoutFD = -1, stderrFD = -1; 1468 prepareStream(stdin, STD_INPUT_HANDLE, "stdin" , stdinFD, startinfo.hStdInput ); 1469 prepareStream(stdout, STD_OUTPUT_HANDLE, "stdout", stdoutFD, startinfo.hStdOutput); 1470 prepareStream(stderr, STD_ERROR_HANDLE, "stderr", stderrFD, startinfo.hStdError ); 1471 1472 if ((startinfo.hStdInput != null && startinfo.hStdInput != INVALID_HANDLE_VALUE) 1473 || (startinfo.hStdOutput != null && startinfo.hStdOutput != INVALID_HANDLE_VALUE) 1474 || (startinfo.hStdError != null && startinfo.hStdError != INVALID_HANDLE_VALUE)) 1475 startinfo.dwFlags = STARTF_USESTDHANDLES; 1476 1477 // Create process. 1478 PROCESS_INFORMATION pi; 1479 DWORD dwCreationFlags = 1480 CREATE_UNICODE_ENVIRONMENT | 1481 ((config.flags & Config.Flags.suppressConsole) ? CREATE_NO_WINDOW : 0); 1482 // workaround until https://issues.dlang.org/show_bug.cgi?id=14696 is fixed 1483 auto pworkDir = workDir.tempCStringW(); 1484 if (!CreateProcessW(null, commandLine.tempCStringW().buffPtr, 1485 null, null, true, dwCreationFlags, 1486 envz, workDir.length ? pworkDir : null, &startinfo, &pi)) 1487 throw ProcessException.newFromLastError("Failed to spawn process \"" ~ cast(string) program ~ '"'); 1488 1489 // figure out if we should close any of the streams 1490 if (!(config.flags & Config.Flags.retainStdin ) && stdinFD > STDERR_FILENO 1491 && stdinFD != getFD(std.stdio.stdin )) 1492 stdin.close(); 1493 if (!(config.flags & Config.Flags.retainStdout) && stdoutFD > STDERR_FILENO 1494 && stdoutFD != getFD(std.stdio.stdout)) 1495 stdout.close(); 1496 if (!(config.flags & Config.Flags.retainStderr) && stderrFD > STDERR_FILENO 1497 && stderrFD != getFD(std.stdio.stderr)) 1498 stderr.close(); 1499 1500 // close the thread handle in the process info structure 1501 CloseHandle(pi.hThread); 1502 if (config.flags & Config.Flags.detached) 1503 { 1504 CloseHandle(pi.hProcess); 1505 return new Pid(pi.dwProcessId); 1506 } 1507 return new Pid(pi.dwProcessId, pi.hProcess); 1508 } 1509 1510 // Converts childEnv to a zero-terminated array of zero-terminated strings 1511 // on the form "name=value", optionally adding those of the current process' 1512 // environment strings that are not present in childEnv. If the parent's 1513 // environment should be inherited without modification, this function 1514 // returns null. 1515 version (Posix) 1516 private const(char*)* createEnv(const string[string] childEnv, 1517 bool mergeWithParentEnv) 1518 { 1519 // Determine the number of strings in the parent's environment. 1520 int parentEnvLength = 0; 1521 auto environ = getEnvironPtr; 1522 if (mergeWithParentEnv) 1523 { 1524 if (childEnv.length == 0) return null; 1525 while (environ[parentEnvLength] != null) ++parentEnvLength; 1526 } 1527 1528 // Convert the "new" variables to C-style strings. 1529 auto envz = new const(char)*[parentEnvLength + childEnv.length + 1]; 1530 int pos = 0; 1531 foreach (var, val; childEnv) 1532 envz[pos++] = (var~'='~val~'\0').ptr; 1533 1534 // Add the parent's environment. 1535 foreach (environStr; environ[0 .. parentEnvLength]) 1536 { 1537 int eqPos = 0; 1538 while (environStr[eqPos] != '=' && environStr[eqPos] != '\0') ++eqPos; 1539 if (environStr[eqPos] != '=') continue; 1540 auto var = environStr[0 .. eqPos]; 1541 if (var in childEnv) continue; 1542 envz[pos++] = environStr; 1543 } 1544 envz[pos] = null; 1545 return envz.ptr; 1546 } 1547 1548 version (Posix) @system unittest 1549 { 1550 auto e1 = createEnv(null, false); 1551 assert(e1 != null && *e1 == null); 1552 1553 auto e2 = createEnv(null, true); 1554 assert(e2 == null); 1555 1556 auto e3 = createEnv(["foo" : "bar", "hello" : "world"], false); 1557 assert(e3 != null && e3[0] != null && e3[1] != null && e3[2] == null); 1558 assert((e3[0][0 .. 8] == "foo=bar\0" && e3[1][0 .. 12] == "hello=world\0") 1559 || (e3[0][0 .. 12] == "hello=world\0" && e3[1][0 .. 8] == "foo=bar\0")); 1560 } 1561 1562 1563 // Converts childEnv to a Windows environment block, which is on the form 1564 // "name1=value1\0name2=value2\0...nameN=valueN\0\0", optionally adding 1565 // those of the current process' environment strings that are not present 1566 // in childEnv. Returns null if the parent's environment should be 1567 // inherited without modification, as this is what is expected by 1568 // CreateProcess(). 1569 version (Windows) 1570 private LPVOID createEnv(const string[string] childEnv, 1571 bool mergeWithParentEnv) 1572 { 1573 if (mergeWithParentEnv && childEnv.length == 0) return null; 1574 import std.array : appender; 1575 import std.uni : toUpper; 1576 auto envz = appender!(wchar[])(); 1577 void put(string var, string val) 1578 { 1579 envz.put(var); 1580 envz.put('='); 1581 envz.put(val); 1582 envz.put(cast(wchar) '\0'); 1583 } 1584 1585 // Add the variables in childEnv, removing them from parentEnv 1586 // if they exist there too. 1587 auto parentEnv = mergeWithParentEnv ? environment.toAA() : null; 1588 foreach (k, v; childEnv) 1589 { 1590 auto uk = toUpper(k); 1591 put(uk, v); 1592 if (uk in parentEnv) parentEnv.remove(uk); 1593 } 1594 1595 // Add remaining parent environment variables. 1596 foreach (k, v; parentEnv) put(k, v); 1597 1598 // Two final zeros are needed in case there aren't any environment vars, 1599 // and the last one does no harm when there are. 1600 envz.put("\0\0"w); 1601 return envz.data.ptr; 1602 } 1603 1604 version (Windows) @system unittest 1605 { 1606 assert(createEnv(null, true) == null); 1607 assert((cast(wchar*) createEnv(null, false))[0 .. 2] == "\0\0"w); 1608 auto e1 = (cast(wchar*) createEnv(["foo":"bar", "ab":"c"], false))[0 .. 14]; 1609 assert(e1 == "FOO=bar\0AB=c\0\0"w || e1 == "AB=c\0FOO=bar\0\0"w); 1610 } 1611 1612 // Searches the PATH variable for the given executable file, 1613 // (checking that it is in fact executable). 1614 version (Posix) 1615 package(std) string searchPathFor(scope const(char)[] executable) 1616 @safe 1617 { 1618 import std.algorithm.iteration : splitter; 1619 import std.conv : to; 1620 import std.path : chainPath; 1621 1622 typeof(return) result; 1623 1624 environment.getImpl("PATH", 1625 (scope const(char)[] path) 1626 { 1627 if (!path) 1628 return; 1629 1630 foreach (dir; splitter(path, ":")) 1631 { 1632 auto execPath = chainPath(dir, executable); 1633 if (isExecutable(execPath)) 1634 { 1635 result = execPath.to!(typeof(result)); 1636 return; 1637 } 1638 } 1639 }); 1640 1641 return result; 1642 } 1643 1644 // Checks whether the file exists and can be executed by the 1645 // current user. 1646 version (Posix) 1647 private bool isExecutable(R)(R path) @trusted nothrow @nogc 1648 if (isSomeFiniteCharInputRange!R) 1649 { 1650 return (access(path.tempCString(), X_OK) == 0); 1651 } 1652 1653 version (Posix) @safe unittest 1654 { 1655 import std.algorithm; 1656 auto lsPath = searchPathFor("ls"); 1657 assert(!lsPath.empty); 1658 assert(lsPath[0] == '/'); 1659 assert(lsPath.endsWith("ls")); 1660 auto unlikely = searchPathFor("lkmqwpoialhggyaofijadsohufoiqezm"); 1661 assert(unlikely is null, "Are you kidding me?"); 1662 } 1663 1664 // Sets or unsets the FD_CLOEXEC flag on the given file descriptor. 1665 version (Posix) 1666 private void setCLOEXEC(int fd, bool on) nothrow @nogc 1667 { 1668 import core.sys.posix.fcntl : fcntl, F_GETFD, FD_CLOEXEC, F_SETFD; 1669 auto flags = fcntl(fd, F_GETFD); 1670 if (flags >= 0) 1671 { 1672 if (on) flags |= FD_CLOEXEC; 1673 else flags &= ~(cast(typeof(flags)) FD_CLOEXEC); 1674 flags = fcntl(fd, F_SETFD, flags); 1675 } 1676 assert(flags != -1 || .errno == EBADF); 1677 } 1678 1679 @system unittest // Command line arguments in spawnProcess(). 1680 { 1681 version (Windows) TestScript prog = 1682 "if not [%~1]==[foo] ( exit 1 ) 1683 if not [%~2]==[bar] ( exit 2 ) 1684 exit 0"; 1685 else version (Posix) TestScript prog = 1686 `if test "$1" != "foo"; then exit 1; fi 1687 if test "$2" != "bar"; then exit 2; fi 1688 exit 0`; 1689 assert(wait(spawnProcess(prog.path)) == 1); 1690 assert(wait(spawnProcess([prog.path])) == 1); 1691 assert(wait(spawnProcess([prog.path, "foo"])) == 2); 1692 assert(wait(spawnProcess([prog.path, "foo", "baz"])) == 2); 1693 assert(wait(spawnProcess([prog.path, "foo", "bar"])) == 0); 1694 } 1695 1696 // test that file descriptors are correctly closed / left open. 1697 // ideally this would be done by the child process making libc 1698 // calls, but we make do... 1699 version (Posix) @system unittest 1700 { 1701 import core.stdc.errno : errno; 1702 import core.sys.posix.fcntl : open, O_RDONLY; 1703 import core.sys.posix.unistd : close; 1704 import std.algorithm.searching : canFind, findSplitBefore; 1705 import std.array : split; 1706 import std.conv : to; 1707 static import std.file; 1708 import std.functional : reverseArgs; 1709 import std.path : buildPath; 1710 1711 auto directory = uniqueTempPath(); 1712 std.file.mkdir(directory); 1713 scope(exit) std.file.rmdirRecurse(directory); 1714 auto path = buildPath(directory, "tmp"); 1715 std.file.write(path, null); 1716 errno = 0; 1717 auto fd = open(path.tempCString, O_RDONLY); 1718 if (fd == -1) 1719 { 1720 import core.stdc.string : strerror; 1721 import std.stdio : stderr; 1722 import std.string : fromStringz; 1723 1724 // For the CI logs 1725 stderr.writefln("%s: could not open '%s': %s", 1726 __FUNCTION__, path, strerror(errno).fromStringz); 1727 // TODO: should we retry here instead? 1728 return; 1729 } 1730 scope(exit) close(fd); 1731 1732 // command >&2 (or any other number) checks whethether that number 1733 // file descriptor is open. 1734 // Can't use this for arbitrary descriptors as many shells only support 1735 // single digit fds. 1736 TestScript testDefaults = `command >&0 && command >&1 && command >&2`; 1737 assert(execute(testDefaults.path).status == 0); 1738 assert(execute(testDefaults.path, null, Config.inheritFDs).status == 0); 1739 1740 // Try a few different methods to check whether there are any 1741 // incorrectly-open files. 1742 void testFDs() 1743 { 1744 // try /proc/<pid>/fd/ on linux 1745 version (linux) 1746 { 1747 TestScript proc = "ls /proc/$$/fd"; 1748 auto procRes = execute(proc.path, null); 1749 if (procRes.status == 0) 1750 { 1751 auto fdStr = fd.to!string; 1752 assert(!procRes.output.split.canFind(fdStr)); 1753 assert(execute(proc.path, null, Config.inheritFDs) 1754 .output.split.canFind(fdStr)); 1755 return; 1756 } 1757 } 1758 1759 // try fuser (might sometimes need permissions) 1760 TestScript fuser = "echo $$ && fuser -f " ~ path; 1761 auto fuserRes = execute(fuser.path, null); 1762 if (fuserRes.status == 0) 1763 { 1764 assert(!reverseArgs!canFind(fuserRes 1765 .output.findSplitBefore("\n").expand)); 1766 assert(reverseArgs!canFind(execute(fuser.path, null, Config.inheritFDs) 1767 .output.findSplitBefore("\n").expand)); 1768 return; 1769 } 1770 1771 // last resort, try lsof (not available on all Posix) 1772 TestScript lsof = "lsof -p$$"; 1773 auto lsofRes = execute(lsof.path, null); 1774 if (lsofRes.status == 0) 1775 { 1776 assert(!lsofRes.output.canFind(path)); 1777 auto lsofOut = execute(lsof.path, null, Config.inheritFDs).output; 1778 if (!lsofOut.canFind(path)) 1779 { 1780 std.stdio.stderr.writeln(__FILE__, ':', __LINE__, 1781 ": Warning: unexpected lsof output:", lsofOut); 1782 } 1783 return; 1784 } 1785 1786 std.stdio.stderr.writeln(__FILE__, ':', __LINE__, 1787 ": Warning: Couldn't find any way to check open files"); 1788 } 1789 testFDs(); 1790 } 1791 1792 @system unittest // Environment variables in spawnProcess(). 1793 { 1794 // We really should use set /a on Windows, but Wine doesn't support it. 1795 version (Windows) TestScript envProg = 1796 `if [%STD_PROCESS_UNITTEST1%] == [1] ( 1797 if [%STD_PROCESS_UNITTEST2%] == [2] (exit 3) 1798 exit 1 1799 ) 1800 if [%STD_PROCESS_UNITTEST1%] == [4] ( 1801 if [%STD_PROCESS_UNITTEST2%] == [2] (exit 6) 1802 exit 4 1803 ) 1804 if [%STD_PROCESS_UNITTEST2%] == [2] (exit 2) 1805 exit 0`; 1806 version (Posix) TestScript envProg = 1807 `if test "$std_process_unittest1" = ""; then 1808 std_process_unittest1=0 1809 fi 1810 if test "$std_process_unittest2" = ""; then 1811 std_process_unittest2=0 1812 fi 1813 exit $(($std_process_unittest1+$std_process_unittest2))`; 1814 1815 environment.remove("std_process_unittest1"); // Just in case. 1816 environment.remove("std_process_unittest2"); 1817 assert(wait(spawnProcess(envProg.path)) == 0); 1818 assert(wait(spawnProcess(envProg.path, null, Config.newEnv)) == 0); 1819 1820 environment["std_process_unittest1"] = "1"; 1821 assert(wait(spawnProcess(envProg.path)) == 1); 1822 assert(wait(spawnProcess(envProg.path, null, Config.newEnv)) == 0); 1823 1824 auto env = ["std_process_unittest2" : "2"]; 1825 assert(wait(spawnProcess(envProg.path, env)) == 3); 1826 assert(wait(spawnProcess(envProg.path, env, Config.newEnv)) == 2); 1827 1828 env["std_process_unittest1"] = "4"; 1829 assert(wait(spawnProcess(envProg.path, env)) == 6); 1830 assert(wait(spawnProcess(envProg.path, env, Config.newEnv)) == 6); 1831 1832 environment.remove("std_process_unittest1"); 1833 assert(wait(spawnProcess(envProg.path, env)) == 6); 1834 assert(wait(spawnProcess(envProg.path, env, Config.newEnv)) == 6); 1835 } 1836 1837 @system unittest // Stream redirection in spawnProcess(). 1838 { 1839 import std.path : buildPath; 1840 import std.string; 1841 version (Windows) TestScript prog = 1842 "set /p INPUT= 1843 echo %INPUT% output %~1 1844 echo %INPUT% error %~2 1>&2 1845 echo done > %3"; 1846 else version (Posix) TestScript prog = 1847 "read INPUT 1848 echo $INPUT output $1 1849 echo $INPUT error $2 >&2 1850 echo done > \"$3\""; 1851 1852 // Pipes 1853 void testPipes(Config config) 1854 { 1855 import std.file : tempDir, exists, remove; 1856 import std.uuid : randomUUID; 1857 import std.exception : collectException; 1858 auto pipei = pipe(); 1859 auto pipeo = pipe(); 1860 auto pipee = pipe(); 1861 auto done = buildPath(tempDir(), randomUUID().toString()); 1862 auto pid = spawnProcess([prog.path, "foo", "bar", done], 1863 pipei.readEnd, pipeo.writeEnd, pipee.writeEnd, null, config); 1864 pipei.writeEnd.writeln("input"); 1865 pipei.writeEnd.flush(); 1866 assert(pipeo.readEnd.readln().chomp() == "input output foo"); 1867 assert(pipee.readEnd.readln().chomp().stripRight() == "input error bar"); 1868 if (config.flags & Config.Flags.detached) 1869 while (!done.exists) Thread.sleep(10.msecs); 1870 else 1871 wait(pid); 1872 while (remove(done).collectException) Thread.sleep(10.msecs); 1873 } 1874 1875 // Files 1876 void testFiles(Config config) 1877 { 1878 import std.ascii : newline; 1879 import std.file : tempDir, exists, remove, readText, write; 1880 import std.uuid : randomUUID; 1881 import std.exception : collectException; 1882 auto pathi = buildPath(tempDir(), randomUUID().toString()); 1883 auto patho = buildPath(tempDir(), randomUUID().toString()); 1884 auto pathe = buildPath(tempDir(), randomUUID().toString()); 1885 write(pathi, "INPUT" ~ newline); 1886 auto filei = File(pathi, "r"); 1887 auto fileo = File(patho, "w"); 1888 auto filee = File(pathe, "w"); 1889 auto done = buildPath(tempDir(), randomUUID().toString()); 1890 auto pid = spawnProcess([prog.path, "bar", "baz", done], filei, fileo, filee, null, config); 1891 if (config.flags & Config.Flags.detached) 1892 while (!done.exists) Thread.sleep(10.msecs); 1893 else 1894 wait(pid); 1895 assert(readText(patho).chomp() == "INPUT output bar"); 1896 assert(readText(pathe).chomp().stripRight() == "INPUT error baz"); 1897 while (remove(pathi).collectException) Thread.sleep(10.msecs); 1898 while (remove(patho).collectException) Thread.sleep(10.msecs); 1899 while (remove(pathe).collectException) Thread.sleep(10.msecs); 1900 while (remove(done).collectException) Thread.sleep(10.msecs); 1901 } 1902 1903 testPipes(Config.none); 1904 testFiles(Config.none); 1905 testPipes(Config.detached); 1906 testFiles(Config.detached); 1907 } 1908 1909 @system unittest // Error handling in spawnProcess() 1910 { 1911 import std.algorithm.searching : canFind; 1912 import std.exception : assertThrown, collectExceptionMsg; 1913 1914 static void testNotFoundException(string program) 1915 { 1916 assert(collectExceptionMsg!ProcessException(spawnProcess(program)).canFind(program)); 1917 assert(collectExceptionMsg!ProcessException(spawnProcess(program, null, Config.detached)).canFind(program)); 1918 } 1919 testNotFoundException("ewrgiuhrifuheiohnmnvqweoijwf"); 1920 testNotFoundException("./rgiuhrifuheiohnmnvqweoijwf"); 1921 1922 // can't execute malformed file with executable permissions 1923 version (Posix) 1924 { 1925 import std.path : buildPath; 1926 import std.file : remove, write, setAttributes, tempDir; 1927 import core.sys.posix.sys.stat : S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP, S_IROTH, S_IXOTH; 1928 import std.conv : to; 1929 string deleteme = buildPath(tempDir(), "deleteme.std.process.unittest.pid") ~ to!string(thisProcessID); 1930 write(deleteme, ""); 1931 scope(exit) remove(deleteme); 1932 setAttributes(deleteme, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); 1933 assertThrown!ProcessException(spawnProcess(deleteme)); 1934 assertThrown!ProcessException(spawnProcess(deleteme, null, Config.detached)); 1935 } 1936 } 1937 1938 @system unittest // Specifying a working directory. 1939 { 1940 import std.path; 1941 import std.file; 1942 TestScript prog = "echo foo>bar"; 1943 1944 auto directory = uniqueTempPath(); 1945 mkdir(directory); 1946 scope(exit) rmdirRecurse(directory); 1947 1948 auto pid = spawnProcess([prog.path], null, Config.none, directory); 1949 wait(pid); 1950 assert(exists(buildPath(directory, "bar"))); 1951 } 1952 1953 @system unittest // Specifying a bad working directory. 1954 { 1955 import std.exception : assertThrown; 1956 import std.file; 1957 TestScript prog = "echo"; 1958 1959 auto directory = uniqueTempPath(); 1960 assertThrown!ProcessException(spawnProcess([prog.path], null, Config.none, directory)); 1961 assertThrown!ProcessException(spawnProcess([prog.path], null, Config.detached, directory)); 1962 1963 std.file.write(directory, "foo"); 1964 scope(exit) remove(directory); 1965 assertThrown!ProcessException(spawnProcess([prog.path], null, Config.none, directory)); 1966 assertThrown!ProcessException(spawnProcess([prog.path], null, Config.detached, directory)); 1967 1968 // can't run in directory if user does not have search permission on this directory 1969 version (Posix) 1970 { 1971 if (core.sys.posix.unistd.getuid() != 0) 1972 { 1973 import core.sys.posix.sys.stat : S_IRUSR; 1974 auto directoryNoSearch = uniqueTempPath(); 1975 mkdir(directoryNoSearch); 1976 scope(exit) rmdirRecurse(directoryNoSearch); 1977 setAttributes(directoryNoSearch, S_IRUSR); 1978 assertThrown!ProcessException(spawnProcess(prog.path, null, Config.none, directoryNoSearch)); 1979 assertThrown!ProcessException(spawnProcess(prog.path, null, Config.detached, directoryNoSearch)); 1980 } 1981 } 1982 } 1983 1984 @system unittest // Specifying empty working directory. 1985 { 1986 TestScript prog = ""; 1987 1988 string directory = ""; 1989 assert(directory.ptr && !directory.length); 1990 spawnProcess([prog.path], null, Config.none, directory).wait(); 1991 } 1992 1993 // Reopening the standard streams (https://issues.dlang.org/show_bug.cgi?id=13258) 1994 @system unittest 1995 { 1996 import std.string; 1997 import std.file; 1998 void fun() 1999 { 2000 spawnShell("echo foo").wait(); 2001 spawnShell("echo bar").wait(); 2002 } 2003 2004 auto tmpFile = uniqueTempPath(); 2005 scope(exit) if (exists(tmpFile)) remove(tmpFile); 2006 2007 { 2008 auto oldOut = std.stdio.stdout; 2009 scope(exit) std.stdio.stdout = oldOut; 2010 2011 std.stdio.stdout = File(tmpFile, "w"); 2012 fun(); 2013 std.stdio.stdout.close(); 2014 } 2015 2016 auto lines = readText(tmpFile).splitLines(); 2017 assert(lines == ["foo", "bar"]); 2018 } 2019 2020 // MSVCRT workaround (https://issues.dlang.org/show_bug.cgi?id=14422) 2021 version (Windows) 2022 @system unittest 2023 { 2024 auto fn = uniqueTempPath(); 2025 scope(exit) if (exists(fn)) remove(fn); 2026 std.file.write(fn, "AAAAAAAAAA"); 2027 2028 auto f = File(fn, "a"); 2029 spawnProcess(["cmd", "/c", "echo BBBBB"], std.stdio.stdin, f).wait(); 2030 2031 auto data = readText(fn); 2032 assert(data == "AAAAAAAAAABBBBB\r\n", data); 2033 } 2034 2035 // https://issues.dlang.org/show_bug.cgi?id=20765 2036 // Test that running processes with relative path works in conjunction 2037 // with indicating a workDir. 2038 version (Posix) @system unittest 2039 { 2040 import std.file : mkdir, write, setAttributes, rmdirRecurse; 2041 import std.conv : octal; 2042 2043 auto dir = uniqueTempPath(); 2044 mkdir(dir); 2045 scope(exit) rmdirRecurse(dir); 2046 write(dir ~ "/program", "#!/bin/sh\necho Hello"); 2047 setAttributes(dir ~ "/program", octal!700); 2048 2049 assert(execute(["./program"], null, Config.none, size_t.max, dir).output == "Hello\n"); 2050 } 2051 2052 /** 2053 A variation on $(LREF spawnProcess) that runs the given _command through 2054 the current user's preferred _command interpreter (aka. shell). 2055 2056 The string `command` is passed verbatim to the shell, and is therefore 2057 subject to its rules about _command structure, argument/filename quoting 2058 and escaping of special characters. 2059 The path to the shell executable defaults to $(LREF nativeShell). 2060 2061 In all other respects this function works just like `spawnProcess`. 2062 Please refer to the $(LREF spawnProcess) documentation for descriptions 2063 of the other function parameters, the return value and any exceptions 2064 that may be thrown. 2065 --- 2066 // Run the command/program "foo" on the file named "my file.txt", and 2067 // redirect its output into foo.log. 2068 auto pid = spawnShell(`foo "my file.txt" > foo.log`); 2069 wait(pid); 2070 --- 2071 2072 See_also: 2073 $(LREF escapeShellCommand), which may be helpful in constructing a 2074 properly quoted and escaped shell _command line for the current platform. 2075 */ 2076 Pid spawnShell(scope const(char)[] command, 2077 File stdin = std.stdio.stdin, 2078 File stdout = std.stdio.stdout, 2079 File stderr = std.stdio.stderr, 2080 scope const string[string] env = null, 2081 Config config = Config.none, 2082 scope const(char)[] workDir = null, 2083 scope string shellPath = nativeShell) 2084 @trusted // See reason below 2085 { 2086 version (Windows) 2087 { 2088 // CMD does not parse its arguments like other programs. 2089 // It does not use CommandLineToArgvW. 2090 // Instead, it treats the first and last quote specially. 2091 // See CMD.EXE /? for details. 2092 const commandLine = escapeShellFileName(shellPath) 2093 ~ ` ` ~ shellSwitch ~ ` "` ~ command ~ `"`; 2094 return spawnProcessWin(commandLine, shellPath, stdin, stdout, stderr, env, config, workDir); 2095 } 2096 else version (Posix) 2097 { 2098 const(char)[][3] args; 2099 args[0] = shellPath; 2100 args[1] = shellSwitch; 2101 args[2] = command; 2102 /* The passing of args converts the static array, which is initialized with `scope` pointers, 2103 * to a dynamic array, which is also a scope parameter. So, it is a scope pointer to a 2104 * scope pointer, which although is safely used here, D doesn't allow transitive scope. 2105 * See https://github.com/dlang/dmd/pull/10951 2106 */ 2107 return spawnProcessPosix(args, stdin, stdout, stderr, env, config, workDir); 2108 } 2109 else 2110 static assert(0); 2111 } 2112 2113 /// ditto 2114 Pid spawnShell(scope const(char)[] command, 2115 scope const string[string] env, 2116 Config config = Config.none, 2117 scope const(char)[] workDir = null, 2118 scope string shellPath = nativeShell) 2119 @trusted // TODO: Should be @safe 2120 { 2121 return spawnShell(command, 2122 std.stdio.stdin, 2123 std.stdio.stdout, 2124 std.stdio.stderr, 2125 env, 2126 config, 2127 workDir, 2128 shellPath); 2129 } 2130 2131 @system unittest 2132 { 2133 version (Windows) 2134 auto cmd = "echo %FOO%"; 2135 else version (Posix) 2136 auto cmd = "echo $foo"; 2137 import std.file; 2138 auto tmpFile = uniqueTempPath(); 2139 scope(exit) if (exists(tmpFile)) remove(tmpFile); 2140 auto redir = "> \""~tmpFile~'"'; 2141 auto env = ["foo" : "bar"]; 2142 assert(wait(spawnShell(cmd~redir, env)) == 0); 2143 auto f = File(tmpFile, "a"); 2144 version (CRuntime_Microsoft) f.seek(0, SEEK_END); // MSVCRT probably seeks to the end when writing, not before 2145 assert(wait(spawnShell(cmd, std.stdio.stdin, f, std.stdio.stderr, env)) == 0); 2146 f.close(); 2147 auto output = std.file.readText(tmpFile); 2148 assert(output == "bar\nbar\n" || output == "bar\r\nbar\r\n"); 2149 } 2150 2151 version (Windows) 2152 @system unittest 2153 { 2154 import std.string; 2155 import std.conv : text; 2156 TestScript prog = "echo %0 %*"; 2157 auto outputFn = uniqueTempPath(); 2158 scope(exit) if (exists(outputFn)) remove(outputFn); 2159 auto args = [`a b c`, `a\b\c\`, `a"b"c"`]; 2160 auto result = executeShell( 2161 escapeShellCommand([prog.path] ~ args) 2162 ~ " > " ~ 2163 escapeShellFileName(outputFn)); 2164 assert(result.status == 0); 2165 auto args2 = outputFn.readText().strip().parseCommandLine()[1..$]; 2166 assert(args == args2, text(args2)); 2167 } 2168 2169 2170 /** 2171 Options that control the behaviour of process creation functions in this 2172 module. Most options only apply to $(LREF spawnProcess) and 2173 $(LREF spawnShell). 2174 2175 Example: 2176 --- 2177 auto logFile = File("myapp_error.log", "w"); 2178 2179 // Start program, suppressing the console window (Windows only), 2180 // redirect its error stream to logFile, and leave logFile open 2181 // in the parent process as well. 2182 auto pid = spawnProcess("myapp", stdin, stdout, logFile, 2183 Config.retainStderr | Config.suppressConsole); 2184 scope(exit) 2185 { 2186 auto exitCode = wait(pid); 2187 logFile.writeln("myapp exited with code ", exitCode); 2188 logFile.close(); 2189 } 2190 --- 2191 */ 2192 struct Config 2193 { 2194 /** 2195 Flag options. 2196 Use bitwise OR to combine flags. 2197 **/ 2198 enum Flags 2199 { 2200 none = 0, 2201 2202 /** 2203 By default, the child process inherits the parent's environment, 2204 and any environment variables passed to $(LREF spawnProcess) will 2205 be added to it. If this flag is set, the only variables in the 2206 child process' environment will be those given to spawnProcess. 2207 */ 2208 newEnv = 1, 2209 2210 /** 2211 Unless the child process inherits the standard input/output/error 2212 streams of its parent, one almost always wants the streams closed 2213 in the parent when $(LREF spawnProcess) returns. Therefore, by 2214 default, this is done. If this is not desirable, pass any of these 2215 options to spawnProcess. 2216 */ 2217 retainStdin = 2, 2218 retainStdout = 4, /// ditto 2219 retainStderr = 8, /// ditto 2220 2221 /** 2222 On Windows, if the child process is a console application, this 2223 flag will prevent the creation of a console window. Otherwise, 2224 it will be ignored. On POSIX, `suppressConsole` has no effect. 2225 */ 2226 suppressConsole = 16, 2227 2228 /** 2229 On POSIX, open $(LINK2 http://en.wikipedia.org/wiki/File_descriptor,file descriptors) 2230 are by default inherited by the child process. As this may lead 2231 to subtle bugs when pipes or multiple threads are involved, 2232 $(LREF spawnProcess) ensures that all file descriptors except the 2233 ones that correspond to standard input/output/error are closed 2234 in the child process when it starts. Use `inheritFDs` to prevent 2235 this. 2236 2237 On Windows, this option has no effect, and any handles which have been 2238 explicitly marked as inheritable will always be inherited by the child 2239 process. 2240 */ 2241 inheritFDs = 32, 2242 2243 /** 2244 Spawn process in detached state. This removes the need in calling 2245 $(LREF wait) to clean up the process resources. 2246 2247 Note: 2248 Calling $(LREF wait) or $(LREF kill) with the resulting `Pid` is invalid. 2249 */ 2250 detached = 64, 2251 2252 /** 2253 By default, the $(LREF execute) and $(LREF executeShell) functions 2254 will capture child processes' both stdout and stderr. This can be 2255 undesirable if the standard output is to be processed or otherwise 2256 used by the invoking program, as `execute`'s result would then 2257 contain a mix of output and warning/error messages. 2258 2259 Specify this flag when calling `execute` or `executeShell` to 2260 cause invoked processes' stderr stream to be sent to $(REF stderr, 2261 std,stdio), and only capture and return standard output. 2262 2263 This flag has no effect on $(LREF spawnProcess) or $(LREF spawnShell). 2264 */ 2265 stderrPassThrough = 128, 2266 } 2267 Flags flags; /// ditto 2268 2269 /** 2270 For backwards compatibility, and cases when only flags need to 2271 be specified in the `Config`, these allow building `Config` 2272 instances using flag names only. 2273 */ 2274 enum Config none = Config.init; 2275 enum Config newEnv = Config(Flags.newEnv); /// ditto 2276 enum Config retainStdin = Config(Flags.retainStdin); /// ditto 2277 enum Config retainStdout = Config(Flags.retainStdout); /// ditto 2278 enum Config retainStderr = Config(Flags.retainStderr); /// ditto 2279 enum Config suppressConsole = Config(Flags.suppressConsole); /// ditto 2280 enum Config inheritFDs = Config(Flags.inheritFDs); /// ditto 2281 enum Config detached = Config(Flags.detached); /// ditto 2282 enum Config stderrPassThrough = Config(Flags.stderrPassThrough); /// ditto 2283 Config opUnary(string op)() 2284 if (is(typeof(mixin(op ~ q{flags})))) 2285 { 2286 return Config(mixin(op ~ q{flags})); 2287 } /// ditto 2288 Config opBinary(string op)(Config other) 2289 if (is(typeof(mixin(q{flags} ~ op ~ q{other.flags})))) 2290 { 2291 return Config(mixin(q{flags} ~ op ~ q{other.flags})); 2292 } /// ditto 2293 Config opOpAssign(string op)(Config other) 2294 if (is(typeof(mixin(q{flags} ~ op ~ q{=other.flags})))) 2295 { 2296 return Config(mixin(q{flags} ~ op ~ q{=other.flags})); 2297 } /// ditto 2298 2299 version (StdDdoc) 2300 { 2301 /** 2302 A function that is called before `exec` in $(LREF spawnProcess). 2303 It returns `true` if succeeded and otherwise returns `false`. 2304 2305 $(RED Warning: 2306 Please note that the code in this function must only use 2307 async-signal-safe functions.) 2308 2309 If $(LREF preExecDelegate) is also set, it is called last. 2310 2311 On Windows, this member is not available. 2312 */ 2313 bool function() nothrow @nogc @safe preExecFunction; 2314 2315 /** 2316 A delegate that is called before `exec` in $(LREF spawnProcess). 2317 It returns `true` if succeeded and otherwise returns `false`. 2318 2319 $(RED Warning: 2320 Please note that the code in this function must only use 2321 async-signal-safe functions.) 2322 2323 If $(LREF preExecFunction) is also set, it is called first. 2324 2325 On Windows, this member is not available. 2326 */ 2327 bool delegate() nothrow @nogc @safe preExecDelegate; 2328 } 2329 else version (Posix) 2330 { 2331 bool function() nothrow @nogc @safe preExecFunction; 2332 bool delegate() nothrow @nogc @safe preExecDelegate; 2333 } 2334 } 2335 2336 // https://issues.dlang.org/show_bug.cgi?id=22125 2337 @safe unittest 2338 { 2339 Config c = Config.retainStdin; 2340 c |= Config.retainStdout; 2341 c |= Config.retainStderr; 2342 c &= ~Config.retainStderr; 2343 assert(c == (Config.retainStdin | Config.retainStdout)); 2344 } 2345 2346 /// A handle that corresponds to a spawned process. 2347 final class Pid 2348 { 2349 /** 2350 The process ID number. 2351 2352 This is a number that uniquely identifies the process on the operating 2353 system, for at least as long as the process is running. Once $(LREF wait) 2354 has been called on the $(LREF Pid), this method will return an 2355 invalid (negative) process ID. 2356 */ 2357 @property int processID() const @safe pure nothrow 2358 { 2359 return _processID; 2360 } 2361 2362 /** 2363 An operating system handle to the process. 2364 2365 This handle is used to specify the process in OS-specific APIs. 2366 On POSIX, this function returns a `core.sys.posix.sys.types.pid_t` 2367 with the same value as $(LREF Pid.processID), while on Windows it returns 2368 a `core.sys.windows.windows.HANDLE`. 2369 2370 Once $(LREF wait) has been called on the $(LREF Pid), this method 2371 will return an invalid handle. 2372 */ 2373 // Note: Since HANDLE is a reference, this function cannot be const. 2374 version (Windows) 2375 @property HANDLE osHandle() @nogc @safe pure nothrow 2376 { 2377 return _handle; 2378 } 2379 else version (Posix) 2380 @property pid_t osHandle() @nogc @safe pure nothrow 2381 { 2382 return _processID; 2383 } 2384 2385 private: 2386 /* 2387 Pid.performWait() does the dirty work for wait() and nonBlockingWait(). 2388 2389 If block == true, this function blocks until the process terminates, 2390 sets _processID to terminated, and returns the exit code or terminating 2391 signal as described in the wait() documentation. 2392 2393 If block == false, this function returns immediately, regardless 2394 of the status of the process. If the process has terminated, the 2395 function has the exact same effect as the blocking version. If not, 2396 it returns 0 and does not modify _processID. 2397 */ 2398 version (Posix) 2399 int performWait(bool block) @trusted 2400 { 2401 import std.exception : enforce; 2402 enforce!ProcessException(owned, "Can't wait on a detached process"); 2403 if (_processID == terminated) return _exitCode; 2404 int exitCode; 2405 while (true) 2406 { 2407 int status; 2408 auto check = waitpid(_processID, &status, block ? 0 : WNOHANG); 2409 if (check == -1) 2410 { 2411 if (errno == ECHILD) 2412 { 2413 throw new ProcessException( 2414 "Process does not exist or is not a child process."); 2415 } 2416 else 2417 { 2418 // waitpid() was interrupted by a signal. We simply 2419 // restart it. 2420 assert(errno == EINTR); 2421 continue; 2422 } 2423 } 2424 if (!block && check == 0) return 0; 2425 if (WIFEXITED(status)) 2426 { 2427 exitCode = WEXITSTATUS(status); 2428 break; 2429 } 2430 else if (WIFSIGNALED(status)) 2431 { 2432 exitCode = -WTERMSIG(status); 2433 break; 2434 } 2435 // We check again whether the call should be blocking, 2436 // since we don't care about other status changes besides 2437 // "exited" and "terminated by signal". 2438 if (!block) return 0; 2439 2440 // Process has stopped, but not terminated, so we continue waiting. 2441 } 2442 // Mark Pid as terminated, and cache and return exit code. 2443 _processID = terminated; 2444 _exitCode = exitCode; 2445 return exitCode; 2446 } 2447 else version (Windows) 2448 { 2449 int performWait(const bool block, const DWORD timeout = INFINITE) @trusted 2450 { 2451 import std.exception : enforce; 2452 enforce!ProcessException(owned, "Can't wait on a detached process"); 2453 if (_processID == terminated) return _exitCode; 2454 assert(_handle != INVALID_HANDLE_VALUE); 2455 if (block) 2456 { 2457 auto result = WaitForSingleObject(_handle, timeout); 2458 if (result != WAIT_OBJECT_0) 2459 { 2460 // Wait time exceeded `timeout` milliseconds? 2461 if (result == WAIT_TIMEOUT && timeout != INFINITE) 2462 return 0; 2463 2464 throw ProcessException.newFromLastError("Wait failed."); 2465 } 2466 } 2467 if (!GetExitCodeProcess(_handle, cast(LPDWORD)&_exitCode)) 2468 throw ProcessException.newFromLastError(); 2469 if (!block && _exitCode == STILL_ACTIVE) return 0; 2470 CloseHandle(_handle); 2471 _handle = INVALID_HANDLE_VALUE; 2472 _processID = terminated; 2473 return _exitCode; 2474 } 2475 2476 int performWait(Duration timeout) @safe 2477 { 2478 import std.exception : enforce; 2479 const msecs = timeout.total!"msecs"; 2480 2481 // Limit this implementation the maximum wait time offered by 2482 // WaitForSingleObject. One could theoretically break up larger 2483 // durations into multiple waits but (DWORD.max - 1).msecs 2484 // (> 7 weeks, 17 hours) should be enough for the usual case. 2485 // DWORD.max is reserved for INFINITE 2486 enforce!ProcessException(msecs < DWORD.max, "Timeout exceeds maximum wait time!"); 2487 return performWait(true, cast(DWORD) msecs); 2488 } 2489 2490 ~this() 2491 { 2492 if (_handle != INVALID_HANDLE_VALUE) 2493 { 2494 CloseHandle(_handle); 2495 _handle = INVALID_HANDLE_VALUE; 2496 } 2497 } 2498 } 2499 2500 // Special values for _processID. 2501 enum invalid = -1, terminated = -2; 2502 2503 // OS process ID number. Only nonnegative IDs correspond to 2504 // running processes. 2505 int _processID = invalid; 2506 2507 // Exit code cached by wait(). This is only expected to hold a 2508 // sensible value if _processID == terminated. 2509 int _exitCode; 2510 2511 // Whether the process can be waited for by wait() for or killed by kill(). 2512 // False if process was started as detached. True otherwise. 2513 bool owned; 2514 2515 // Pids are only meant to be constructed inside this module, so 2516 // we make the constructor private. 2517 version (Windows) 2518 { 2519 HANDLE _handle = INVALID_HANDLE_VALUE; 2520 this(int pid, HANDLE handle) @safe pure nothrow 2521 { 2522 _processID = pid; 2523 _handle = handle; 2524 this.owned = true; 2525 } 2526 this(int pid) @safe pure nothrow 2527 { 2528 _processID = pid; 2529 this.owned = false; 2530 } 2531 } 2532 else 2533 { 2534 this(int id, bool owned) @safe pure nothrow 2535 { 2536 _processID = id; 2537 this.owned = owned; 2538 } 2539 } 2540 } 2541 2542 2543 /** 2544 Waits for the process associated with `pid` to terminate, and returns 2545 its exit status. 2546 2547 In general one should always _wait for child processes to terminate 2548 before exiting the parent process unless the process was spawned as detached 2549 (that was spawned with `Config.detached` flag). 2550 Otherwise, they may become "$(HTTP en.wikipedia.org/wiki/Zombie_process,zombies)" 2551 – processes that are defunct, yet still occupy a slot in the OS process table. 2552 You should not and must not wait for detached processes, since you don't own them. 2553 2554 If the process has already terminated, this function returns directly. 2555 The exit code is cached, so that if wait() is called multiple times on 2556 the same $(LREF Pid) it will always return the same value. 2557 2558 POSIX_specific: 2559 If the process is terminated by a signal, this function returns a 2560 negative number whose absolute value is the signal number. 2561 Since POSIX restricts normal exit codes to the range 0-255, a 2562 negative return value will always indicate termination by signal. 2563 Signal codes are defined in the `core.sys.posix.signal` module 2564 (which corresponds to the `signal.h` POSIX header). 2565 2566 Throws: 2567 $(LREF ProcessException) on failure or on attempt to wait for detached process. 2568 2569 Example: 2570 See the $(LREF spawnProcess) documentation. 2571 2572 See_also: 2573 $(LREF tryWait), for a non-blocking function. 2574 */ 2575 int wait(Pid pid) @safe 2576 { 2577 assert(pid !is null, "Called wait on a null Pid."); 2578 return pid.performWait(true); 2579 } 2580 2581 2582 @system unittest // Pid and wait() 2583 { 2584 version (Windows) TestScript prog = "exit %~1"; 2585 else version (Posix) TestScript prog = "exit $1"; 2586 assert(wait(spawnProcess([prog.path, "0"])) == 0); 2587 assert(wait(spawnProcess([prog.path, "123"])) == 123); 2588 auto pid = spawnProcess([prog.path, "10"]); 2589 assert(pid.processID > 0); 2590 version (Windows) assert(pid.osHandle != INVALID_HANDLE_VALUE); 2591 else version (Posix) assert(pid.osHandle == pid.processID); 2592 assert(wait(pid) == 10); 2593 assert(wait(pid) == 10); // cached exit code 2594 assert(pid.processID < 0); 2595 version (Windows) assert(pid.osHandle == INVALID_HANDLE_VALUE); 2596 else version (Posix) assert(pid.osHandle < 0); 2597 } 2598 2599 private import std.typecons : Tuple; 2600 2601 /** 2602 Waits until either the process associated with `pid` terminates or the 2603 elapsed time exceeds the given timeout. 2604 2605 If the process terminates within the given duration it behaves exactly like 2606 `wait`, except that it returns a tuple `(true, exit code)`. 2607 2608 If the process does not terminate within the given duration it will stop 2609 waiting and return `(false, 0).` 2610 2611 The timeout may not exceed `(uint.max - 1).msecs` (~ 7 weeks, 17 hours). 2612 2613 $(BLUE This function is Windows-Only.) 2614 2615 Returns: 2616 An $(D std.typecons.Tuple!(bool, "terminated", int, "status")). 2617 2618 Throws: 2619 $(LREF ProcessException) on failure or on attempt to wait for detached process. 2620 2621 Example: 2622 See the $(LREF spawnProcess) documentation. 2623 2624 See_also: 2625 $(LREF wait), for a blocking function without timeout. 2626 $(LREF tryWait), for a non-blocking function without timeout. 2627 */ 2628 version (StdDdoc) 2629 Tuple!(bool, "terminated", int, "status") waitTimeout(Pid pid, Duration timeout) @safe; 2630 2631 else version (Windows) 2632 Tuple!(bool, "terminated", int, "status") waitTimeout(Pid pid, Duration timeout) @safe 2633 { 2634 assert(pid !is null, "Called wait on a null Pid."); 2635 auto code = pid.performWait(timeout); 2636 return typeof(return)(pid._processID == Pid.terminated, code); 2637 } 2638 2639 version (Windows) 2640 @system unittest // Pid and waitTimeout() 2641 { 2642 import std.exception : collectException; 2643 import std.typecons : tuple; 2644 2645 TestScript prog = ":Loop\r\n" ~ "goto Loop"; 2646 auto pid = spawnProcess(prog.path); 2647 2648 // Doesn't block longer than one second 2649 assert(waitTimeout(pid, 1.seconds) == tuple(false, 0)); 2650 2651 kill(pid); 2652 assert(waitTimeout(pid, 1.seconds) == tuple(true, 1)); // exit 1 because the process is killed 2653 2654 // Rejects timeouts exceeding the Windows API capabilities 2655 const dur = DWORD.max.msecs; 2656 const ex = collectException!ProcessException(waitTimeout(pid, dur)); 2657 assert(ex); 2658 assert(ex.msg == "Timeout exceeds maximum wait time!"); 2659 } 2660 2661 /** 2662 A non-blocking version of $(LREF wait). 2663 2664 If the process associated with `pid` has already terminated, 2665 `tryWait` has the exact same effect as `wait`. 2666 In this case, it returns a tuple where the `terminated` field 2667 is set to `true` and the `status` field has the same 2668 interpretation as the return value of `wait`. 2669 2670 If the process has $(I not) yet terminated, this function differs 2671 from `wait` in that does not wait for this to happen, but instead 2672 returns immediately. The `terminated` field of the returned 2673 tuple will then be set to `false`, while the `status` field 2674 will always be 0 (zero). `wait` or `tryWait` should then be 2675 called again on the same `Pid` at some later time; not only to 2676 get the exit code, but also to avoid the process becoming a "zombie" 2677 when it finally terminates. (See $(LREF wait) for details). 2678 2679 Returns: 2680 An $(D std.typecons.Tuple!(bool, "terminated", int, "status")). 2681 2682 Throws: 2683 $(LREF ProcessException) on failure or on attempt to wait for detached process. 2684 2685 Example: 2686 --- 2687 auto pid = spawnProcess("dmd myapp.d"); 2688 scope(exit) wait(pid); 2689 ... 2690 auto dmd = tryWait(pid); 2691 if (dmd.terminated) 2692 { 2693 if (dmd.status == 0) writeln("Compilation succeeded!"); 2694 else writeln("Compilation failed"); 2695 } 2696 else writeln("Still compiling..."); 2697 ... 2698 --- 2699 Note that in this example, the first `wait` call will have no 2700 effect if the process has already terminated by the time `tryWait` 2701 is called. In the opposite case, however, the `scope` statement 2702 ensures that we always wait for the process if it hasn't terminated 2703 by the time we reach the end of the scope. 2704 */ 2705 auto tryWait(Pid pid) @safe 2706 { 2707 import std.typecons : Tuple; 2708 assert(pid !is null, "Called tryWait on a null Pid."); 2709 auto code = pid.performWait(false); 2710 return Tuple!(bool, "terminated", int, "status")(pid._processID == Pid.terminated, code); 2711 } 2712 // unittest: This function is tested together with kill() below. 2713 2714 2715 /** 2716 Attempts to terminate the process associated with `pid`. 2717 2718 The effect of this function, as well as the meaning of `codeOrSignal`, 2719 is highly platform dependent. Details are given below. Common to all 2720 platforms is that this function only $(I initiates) termination of the process, 2721 and returns immediately. It does not wait for the process to end, 2722 nor does it guarantee that the process does in fact get terminated. 2723 2724 Always call $(LREF wait) to wait for a process to complete, even if `kill` 2725 has been called on it. 2726 2727 Windows_specific: 2728 The process will be 2729 $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms686714%28v=vs.100%29.aspx, 2730 forcefully and abruptly terminated). If `codeOrSignal` is specified, it 2731 must be a nonnegative number which will be used as the exit code of the process. 2732 If not, the process wil exit with code 1. Do not use $(D codeOrSignal = 259), 2733 as this is a special value (aka. $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms683189.aspx,STILL_ACTIVE)) 2734 used by Windows to signal that a process has in fact $(I not) terminated yet. 2735 --- 2736 auto pid = spawnProcess("some_app"); 2737 kill(pid, 10); 2738 assert(wait(pid) == 10); 2739 --- 2740 2741 POSIX_specific: 2742 A $(LINK2 http://en.wikipedia.org/wiki/Unix_signal,signal) will be sent to 2743 the process, whose value is given by `codeOrSignal`. Depending on the 2744 signal sent, this may or may not terminate the process. Symbolic constants 2745 for various $(LINK2 http://en.wikipedia.org/wiki/Unix_signal#POSIX_signals, 2746 POSIX signals) are defined in `core.sys.posix.signal`, which corresponds to the 2747 $(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html, 2748 `signal.h` POSIX header). If `codeOrSignal` is omitted, the 2749 `SIGTERM` signal will be sent. (This matches the behaviour of the 2750 $(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/utilities/kill.html, 2751 `_kill`) shell command.) 2752 --- 2753 import core.sys.posix.signal : SIGKILL; 2754 auto pid = spawnProcess("some_app"); 2755 kill(pid, SIGKILL); 2756 assert(wait(pid) == -SIGKILL); // Negative return value on POSIX! 2757 --- 2758 2759 Throws: 2760 $(LREF ProcessException) on error (e.g. if codeOrSignal is invalid). 2761 or on attempt to kill detached process. 2762 Note that failure to terminate the process is considered a "normal" 2763 outcome, not an error.$(BR) 2764 */ 2765 void kill(Pid pid) 2766 { 2767 version (Windows) kill(pid, 1); 2768 else version (Posix) 2769 { 2770 import core.sys.posix.signal : SIGTERM; 2771 kill(pid, SIGTERM); 2772 } 2773 } 2774 2775 /// ditto 2776 void kill(Pid pid, int codeOrSignal) 2777 { 2778 import std.exception : enforce; 2779 enforce!ProcessException(pid.owned, "Can't kill detached process"); 2780 version (Windows) 2781 { 2782 if (codeOrSignal < 0) throw new ProcessException("Invalid exit code"); 2783 // On Windows, TerminateProcess() appears to terminate the 2784 // *current* process if it is passed an invalid handle... 2785 if (pid.osHandle == INVALID_HANDLE_VALUE) 2786 throw new ProcessException("Invalid process handle"); 2787 if (!TerminateProcess(pid.osHandle, codeOrSignal)) 2788 throw ProcessException.newFromLastError(); 2789 } 2790 else version (Posix) 2791 { 2792 import core.sys.posix.signal : kill; 2793 if (pid.osHandle == Pid.invalid) 2794 throw new ProcessException("Pid is invalid"); 2795 if (pid.osHandle == Pid.terminated) 2796 throw new ProcessException("Pid is already terminated"); 2797 if (kill(pid.osHandle, codeOrSignal) == -1) 2798 throw ProcessException.newFromErrno(); 2799 } 2800 } 2801 2802 @system unittest // tryWait() and kill() 2803 { 2804 import core.thread; 2805 import std.exception : assertThrown; 2806 // The test script goes into an infinite loop. 2807 version (Windows) 2808 { 2809 TestScript prog = ":loop 2810 goto loop"; 2811 } 2812 else version (Posix) 2813 { 2814 import core.sys.posix.signal : SIGTERM, SIGKILL; 2815 TestScript prog = "while true; do sleep 1; done"; 2816 } 2817 auto pid = spawnProcess(prog.path); 2818 // Android appears to automatically kill sleeping processes very quickly, 2819 // so shorten the wait before killing here. 2820 version (Android) 2821 Thread.sleep(dur!"msecs"(5)); 2822 else 2823 Thread.sleep(dur!"msecs"(500)); 2824 kill(pid); 2825 version (Windows) assert(wait(pid) == 1); 2826 else version (Posix) assert(wait(pid) == -SIGTERM); 2827 2828 pid = spawnProcess(prog.path); 2829 Thread.sleep(dur!"msecs"(500)); 2830 auto s = tryWait(pid); 2831 assert(!s.terminated && s.status == 0); 2832 assertThrown!ProcessException(kill(pid, -123)); // Negative code not allowed. 2833 version (Windows) kill(pid, 123); 2834 else version (Posix) kill(pid, SIGKILL); 2835 do { s = tryWait(pid); } while (!s.terminated); 2836 version (Windows) assert(s.status == 123); 2837 else version (Posix) assert(s.status == -SIGKILL); 2838 assertThrown!ProcessException(kill(pid)); // Already terminated 2839 } 2840 2841 @system unittest // wait() and kill() detached process 2842 { 2843 import core.thread; 2844 import std.exception : assertThrown; 2845 TestScript prog = "exit 0"; 2846 auto pid = spawnProcess([prog.path], null, Config.detached); 2847 /* 2848 This sleep is needed because we can't wait() for detached process to end 2849 and therefore TestScript destructor may run at the same time as /bin/sh tries to start the script. 2850 This leads to the annoying message like "/bin/sh: 0: Can't open /tmp/std.process temporary file" to appear when running tests. 2851 It does not happen in unittests with non-detached processes because we always wait() for them to finish. 2852 */ 2853 Thread.sleep(500.msecs); 2854 assert(!pid.owned); 2855 version (Windows) assert(pid.osHandle == INVALID_HANDLE_VALUE); 2856 assertThrown!ProcessException(wait(pid)); 2857 assertThrown!ProcessException(kill(pid)); 2858 } 2859 2860 2861 /** 2862 Creates a unidirectional _pipe. 2863 2864 Data is written to one end of the _pipe and read from the other. 2865 --- 2866 auto p = pipe(); 2867 p.writeEnd.writeln("Hello World"); 2868 p.writeEnd.flush(); 2869 assert(p.readEnd.readln().chomp() == "Hello World"); 2870 --- 2871 Pipes can, for example, be used for interprocess communication 2872 by spawning a new process and passing one end of the _pipe to 2873 the child, while the parent uses the other end. 2874 (See also $(LREF pipeProcess) and $(LREF pipeShell) for an easier 2875 way of doing this.) 2876 --- 2877 // Use cURL to download the dlang.org front page, pipe its 2878 // output to grep to extract a list of links to ZIP files, 2879 // and write the list to the file "D downloads.txt": 2880 auto p = pipe(); 2881 auto outFile = File("D downloads.txt", "w"); 2882 auto cpid = spawnProcess(["curl", "http://dlang.org/download.html"], 2883 std.stdio.stdin, p.writeEnd); 2884 scope(exit) wait(cpid); 2885 auto gpid = spawnProcess(["grep", "-o", `http://\S*\.zip`], 2886 p.readEnd, outFile); 2887 scope(exit) wait(gpid); 2888 --- 2889 2890 Returns: 2891 A $(LREF Pipe) object that corresponds to the created _pipe. 2892 2893 Throws: 2894 $(REF StdioException, std,stdio) on failure. 2895 */ 2896 version (Posix) 2897 Pipe pipe() @trusted //TODO: @safe 2898 { 2899 import core.sys.posix.stdio : fdopen; 2900 int[2] fds; 2901 if (core.sys.posix.unistd.pipe(fds) != 0) 2902 throw new StdioException("Unable to create pipe"); 2903 Pipe p; 2904 auto readFP = fdopen(fds[0], "r"); 2905 if (readFP == null) 2906 throw new StdioException("Cannot open read end of pipe"); 2907 p._read = File(readFP, null); 2908 auto writeFP = fdopen(fds[1], "w"); 2909 if (writeFP == null) 2910 throw new StdioException("Cannot open write end of pipe"); 2911 p._write = File(writeFP, null); 2912 return p; 2913 } 2914 else version (Windows) 2915 Pipe pipe() @trusted //TODO: @safe 2916 { 2917 // use CreatePipe to create an anonymous pipe 2918 HANDLE readHandle; 2919 HANDLE writeHandle; 2920 if (!CreatePipe(&readHandle, &writeHandle, null, 0)) 2921 { 2922 throw new StdioException( 2923 "Error creating pipe (" ~ generateSysErrorMsg() ~ ')', 2924 0); 2925 } 2926 2927 scope(failure) 2928 { 2929 CloseHandle(readHandle); 2930 CloseHandle(writeHandle); 2931 } 2932 2933 try 2934 { 2935 Pipe p; 2936 p._read .windowsHandleOpen(readHandle , "r"); 2937 p._write.windowsHandleOpen(writeHandle, "a"); 2938 return p; 2939 } 2940 catch (Exception e) 2941 { 2942 throw new StdioException("Error attaching pipe (" ~ e.msg ~ ")", 2943 0); 2944 } 2945 } 2946 2947 2948 /// An interface to a pipe created by the $(LREF pipe) function. 2949 struct Pipe 2950 { 2951 /// The read end of the pipe. 2952 @property File readEnd() @safe nothrow { return _read; } 2953 2954 2955 /// The write end of the pipe. 2956 @property File writeEnd() @safe nothrow { return _write; } 2957 2958 2959 /** 2960 Closes both ends of the pipe. 2961 2962 Normally it is not necessary to do this manually, as $(REF File, std,stdio) 2963 objects are automatically closed when there are no more references 2964 to them. 2965 2966 Note that if either end of the pipe has been passed to a child process, 2967 it will only be closed in the parent process. (What happens in the 2968 child process is platform dependent.) 2969 2970 Throws: 2971 $(REF ErrnoException, std,exception) if an error occurs. 2972 */ 2973 void close() @safe 2974 { 2975 _read.close(); 2976 _write.close(); 2977 } 2978 2979 private: 2980 File _read, _write; 2981 } 2982 2983 @system unittest 2984 { 2985 import std.string; 2986 auto p = pipe(); 2987 p.writeEnd.writeln("Hello World"); 2988 p.writeEnd.flush(); 2989 assert(p.readEnd.readln().chomp() == "Hello World"); 2990 p.close(); 2991 assert(!p.readEnd.isOpen); 2992 assert(!p.writeEnd.isOpen); 2993 } 2994 2995 2996 /** 2997 Starts a new process, creating pipes to redirect its standard 2998 input, output and/or error streams. 2999 3000 `pipeProcess` and `pipeShell` are convenient wrappers around 3001 $(LREF spawnProcess) and $(LREF spawnShell), respectively, and 3002 automate the task of redirecting one or more of the child process' 3003 standard streams through pipes. Like the functions they wrap, 3004 these functions return immediately, leaving the child process to 3005 execute in parallel with the invoking process. It is recommended 3006 to always call $(LREF wait) on the returned $(LREF ProcessPipes.pid), 3007 as detailed in the documentation for `wait`. 3008 3009 The `args`/`program`/`command`, `env` and `config` 3010 parameters are forwarded straight to the underlying spawn functions, 3011 and we refer to their documentation for details. 3012 3013 Params: 3014 args = An array which contains the program name as the zeroth element 3015 and any command-line arguments in the following elements. 3016 (See $(LREF spawnProcess) for details.) 3017 program = The program name, $(I without) command-line arguments. 3018 (See $(LREF spawnProcess) for details.) 3019 command = A shell command which is passed verbatim to the command 3020 interpreter. (See $(LREF spawnShell) for details.) 3021 redirect = Flags that determine which streams are redirected, and 3022 how. See $(LREF Redirect) for an overview of available 3023 flags. 3024 env = Additional environment variables for the child process. 3025 (See $(LREF spawnProcess) for details.) 3026 config = Flags that control process creation. See $(LREF Config) 3027 for an overview of available flags, and note that the 3028 `retainStd...` flags have no effect in this function. 3029 workDir = The working directory for the new process. 3030 By default the child process inherits the parent's working 3031 directory. 3032 shellPath = The path to the shell to use to run the specified program. 3033 By default this is $(LREF nativeShell). 3034 3035 Returns: 3036 A $(LREF ProcessPipes) object which contains $(REF File, std,stdio) 3037 handles that communicate with the redirected streams of the child 3038 process, along with a $(LREF Pid) object that corresponds to the 3039 spawned process. 3040 3041 Throws: 3042 $(LREF ProcessException) on failure to start the process.$(BR) 3043 $(REF StdioException, std,stdio) on failure to redirect any of the streams.$(BR) 3044 3045 Example: 3046 --- 3047 // my_application writes to stdout and might write to stderr 3048 auto pipes = pipeProcess("my_application", Redirect.stdout | Redirect.stderr); 3049 scope(exit) wait(pipes.pid); 3050 3051 // Store lines of output. 3052 string[] output; 3053 foreach (line; pipes.stdout.byLine) output ~= line.idup; 3054 3055 // Store lines of errors. 3056 string[] errors; 3057 foreach (line; pipes.stderr.byLine) errors ~= line.idup; 3058 3059 3060 // sendmail expects to read from stdin 3061 pipes = pipeProcess(["/usr/bin/sendmail", "-t"], Redirect.stdin); 3062 pipes.stdin.writeln("To: you"); 3063 pipes.stdin.writeln("From: me"); 3064 pipes.stdin.writeln("Subject: dlang"); 3065 pipes.stdin.writeln(""); 3066 pipes.stdin.writeln(message); 3067 3068 // a single period tells sendmail we are finished 3069 pipes.stdin.writeln("."); 3070 3071 // but at this point sendmail might not see it, we need to flush 3072 pipes.stdin.flush(); 3073 3074 // sendmail happens to exit on ".", but some you have to close the file: 3075 pipes.stdin.close(); 3076 3077 // otherwise this wait will wait forever 3078 wait(pipes.pid); 3079 3080 --- 3081 */ 3082 ProcessPipes pipeProcess(scope const(char[])[] args, 3083 Redirect redirect = Redirect.all, 3084 const string[string] env = null, 3085 Config config = Config.none, 3086 scope const(char)[] workDir = null) 3087 @safe 3088 { 3089 return pipeProcessImpl!spawnProcess(args, redirect, env, config, workDir); 3090 } 3091 3092 /// ditto 3093 ProcessPipes pipeProcess(scope const(char)[] program, 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(program, redirect, env, config, workDir); 3101 } 3102 3103 /// ditto 3104 ProcessPipes pipeShell(scope const(char)[] command, 3105 Redirect redirect = Redirect.all, 3106 const string[string] env = null, 3107 Config config = Config.none, 3108 scope const(char)[] workDir = null, 3109 string shellPath = nativeShell) 3110 @safe 3111 { 3112 return pipeProcessImpl!spawnShell(command, 3113 redirect, 3114 env, 3115 config, 3116 workDir, 3117 shellPath); 3118 } 3119 3120 // Implementation of the pipeProcess() family of functions. 3121 private ProcessPipes pipeProcessImpl(alias spawnFunc, Cmd, ExtraSpawnFuncArgs...) 3122 (scope Cmd command, 3123 Redirect redirectFlags, 3124 const string[string] env = null, 3125 Config config = Config.none, 3126 scope const(char)[] workDir = null, 3127 ExtraSpawnFuncArgs extraArgs = ExtraSpawnFuncArgs.init) 3128 @trusted //TODO: @safe 3129 { 3130 File childStdin, childStdout, childStderr; 3131 ProcessPipes pipes; 3132 pipes._redirectFlags = redirectFlags; 3133 3134 if (redirectFlags & Redirect.stdin) 3135 { 3136 auto p = pipe(); 3137 childStdin = p.readEnd; 3138 pipes._stdin = p.writeEnd; 3139 } 3140 else 3141 { 3142 childStdin = std.stdio.stdin; 3143 } 3144 3145 if (redirectFlags & Redirect.stdout) 3146 { 3147 if ((redirectFlags & Redirect.stdoutToStderr) != 0) 3148 throw new StdioException("Cannot create pipe for stdout AND " 3149 ~"redirect it to stderr", 0); 3150 auto p = pipe(); 3151 childStdout = p.writeEnd; 3152 pipes._stdout = p.readEnd; 3153 } 3154 else 3155 { 3156 childStdout = std.stdio.stdout; 3157 } 3158 3159 if (redirectFlags & Redirect.stderr) 3160 { 3161 if ((redirectFlags & Redirect.stderrToStdout) != 0) 3162 throw new StdioException("Cannot create pipe for stderr AND " 3163 ~"redirect it to stdout", 0); 3164 auto p = pipe(); 3165 childStderr = p.writeEnd; 3166 pipes._stderr = p.readEnd; 3167 } 3168 else 3169 { 3170 childStderr = std.stdio.stderr; 3171 } 3172 3173 if (redirectFlags & Redirect.stdoutToStderr) 3174 { 3175 if (redirectFlags & Redirect.stderrToStdout) 3176 { 3177 // We know that neither of the other options have been 3178 // set, so we assign the std.stdio.std* streams directly. 3179 childStdout = std.stdio.stderr; 3180 childStderr = std.stdio.stdout; 3181 } 3182 else 3183 { 3184 childStdout = childStderr; 3185 } 3186 } 3187 else if (redirectFlags & Redirect.stderrToStdout) 3188 { 3189 childStderr = childStdout; 3190 } 3191 3192 config.flags &= ~(Config.Flags.retainStdin | Config.Flags.retainStdout | Config.Flags.retainStderr); 3193 pipes._pid = spawnFunc(command, childStdin, childStdout, childStderr, 3194 env, config, workDir, extraArgs); 3195 return pipes; 3196 } 3197 3198 3199 /** 3200 Flags that can be passed to $(LREF pipeProcess) and $(LREF pipeShell) 3201 to specify which of the child process' standard streams are redirected. 3202 Use bitwise OR to combine flags. 3203 */ 3204 enum Redirect 3205 { 3206 /// Redirect the standard input, output or error streams, respectively. 3207 stdin = 1, 3208 stdout = 2, /// ditto 3209 stderr = 4, /// ditto 3210 3211 /** 3212 Redirect _all three streams. This is equivalent to 3213 $(D Redirect.stdin | Redirect.stdout | Redirect.stderr). 3214 */ 3215 all = stdin | stdout | stderr, 3216 3217 /** 3218 Redirect the standard error stream into the standard output stream. 3219 This can not be combined with `Redirect.stderr`. 3220 */ 3221 stderrToStdout = 8, 3222 3223 /** 3224 Redirect the standard output stream into the standard error stream. 3225 This can not be combined with `Redirect.stdout`. 3226 */ 3227 stdoutToStderr = 16, 3228 } 3229 3230 @system unittest 3231 { 3232 import std.string; 3233 version (Windows) TestScript prog = 3234 "call :sub %~1 %~2 0 3235 call :sub %~1 %~2 1 3236 call :sub %~1 %~2 2 3237 call :sub %~1 %~2 3 3238 exit 3 3239 3240 :sub 3241 set /p INPUT= 3242 if -%INPUT%-==-stop- ( exit %~3 ) 3243 echo %INPUT% %~1 3244 echo %INPUT% %~2 1>&2"; 3245 else version (Posix) TestScript prog = 3246 `for EXITCODE in 0 1 2 3; do 3247 read INPUT 3248 if test "$INPUT" = stop; then break; fi 3249 echo "$INPUT $1" 3250 echo "$INPUT $2" >&2 3251 done 3252 exit $EXITCODE`; 3253 auto pp = pipeProcess([prog.path, "bar", "baz"]); 3254 pp.stdin.writeln("foo"); 3255 pp.stdin.flush(); 3256 assert(pp.stdout.readln().chomp() == "foo bar"); 3257 assert(pp.stderr.readln().chomp().stripRight() == "foo baz"); 3258 pp.stdin.writeln("1234567890"); 3259 pp.stdin.flush(); 3260 assert(pp.stdout.readln().chomp() == "1234567890 bar"); 3261 assert(pp.stderr.readln().chomp().stripRight() == "1234567890 baz"); 3262 pp.stdin.writeln("stop"); 3263 pp.stdin.flush(); 3264 assert(wait(pp.pid) == 2); 3265 3266 pp = pipeProcess([prog.path, "12345", "67890"], 3267 Redirect.stdin | Redirect.stdout | Redirect.stderrToStdout); 3268 pp.stdin.writeln("xyz"); 3269 pp.stdin.flush(); 3270 assert(pp.stdout.readln().chomp() == "xyz 12345"); 3271 assert(pp.stdout.readln().chomp().stripRight() == "xyz 67890"); 3272 pp.stdin.writeln("stop"); 3273 pp.stdin.flush(); 3274 assert(wait(pp.pid) == 1); 3275 3276 pp = pipeShell(escapeShellCommand(prog.path, "AAAAA", "BBB"), 3277 Redirect.stdin | Redirect.stdoutToStderr | Redirect.stderr); 3278 pp.stdin.writeln("ab"); 3279 pp.stdin.flush(); 3280 assert(pp.stderr.readln().chomp() == "ab AAAAA"); 3281 assert(pp.stderr.readln().chomp().stripRight() == "ab BBB"); 3282 pp.stdin.writeln("stop"); 3283 pp.stdin.flush(); 3284 assert(wait(pp.pid) == 1); 3285 } 3286 3287 @system unittest 3288 { 3289 import std.exception : assertThrown; 3290 TestScript prog = "exit 0"; 3291 assertThrown!StdioException(pipeProcess( 3292 prog.path, 3293 Redirect.stdout | Redirect.stdoutToStderr)); 3294 assertThrown!StdioException(pipeProcess( 3295 prog.path, 3296 Redirect.stderr | Redirect.stderrToStdout)); 3297 auto p = pipeProcess(prog.path, Redirect.stdin); 3298 assertThrown!Error(p.stdout); 3299 assertThrown!Error(p.stderr); 3300 wait(p.pid); 3301 p = pipeProcess(prog.path, Redirect.stderr); 3302 assertThrown!Error(p.stdin); 3303 assertThrown!Error(p.stdout); 3304 wait(p.pid); 3305 } 3306 3307 /** 3308 Object which contains $(REF File, std,stdio) handles that allow communication 3309 with a child process through its standard streams. 3310 */ 3311 struct ProcessPipes 3312 { 3313 /// The $(LREF Pid) of the child process. 3314 @property Pid pid() @safe nothrow 3315 { 3316 return _pid; 3317 } 3318 3319 /** 3320 An $(REF File, std,stdio) that allows writing to the child process' 3321 standard input stream. 3322 3323 Throws: 3324 $(OBJECTREF Error) if the child process' standard input stream hasn't 3325 been redirected. 3326 */ 3327 @property File stdin() @safe nothrow 3328 { 3329 if ((_redirectFlags & Redirect.stdin) == 0) 3330 throw new Error("Child process' standard input stream hasn't " 3331 ~"been redirected."); 3332 return _stdin; 3333 } 3334 3335 /** 3336 An $(REF File, std,stdio) that allows reading from the child process' 3337 standard output stream. 3338 3339 Throws: 3340 $(OBJECTREF Error) if the child process' standard output stream hasn't 3341 been redirected. 3342 */ 3343 @property File stdout() @safe nothrow 3344 { 3345 if ((_redirectFlags & Redirect.stdout) == 0) 3346 throw new Error("Child process' standard output stream hasn't " 3347 ~"been redirected."); 3348 return _stdout; 3349 } 3350 3351 /** 3352 An $(REF File, std,stdio) that allows reading from the child process' 3353 standard error stream. 3354 3355 Throws: 3356 $(OBJECTREF Error) if the child process' standard error stream hasn't 3357 been redirected. 3358 */ 3359 @property File stderr() @safe nothrow 3360 { 3361 if ((_redirectFlags & Redirect.stderr) == 0) 3362 throw new Error("Child process' standard error stream hasn't " 3363 ~"been redirected."); 3364 return _stderr; 3365 } 3366 3367 private: 3368 Redirect _redirectFlags; 3369 Pid _pid; 3370 File _stdin, _stdout, _stderr; 3371 } 3372 3373 3374 3375 /** 3376 Executes the given program or shell command and returns its exit 3377 code and output. 3378 3379 `execute` and `executeShell` start a new process using 3380 $(LREF spawnProcess) and $(LREF spawnShell), respectively, and wait 3381 for the process to complete before returning. The functions capture 3382 what the child process prints to both its standard output and 3383 standard error streams, and return this together with its exit code. 3384 --- 3385 auto dmd = execute(["dmd", "myapp.d"]); 3386 if (dmd.status != 0) writeln("Compilation failed:\n", dmd.output); 3387 3388 auto ls = executeShell("ls -l"); 3389 if (ls.status != 0) writeln("Failed to retrieve file listing"); 3390 else writeln(ls.output); 3391 --- 3392 3393 The `args`/`program`/`command`, `env` and `config` 3394 parameters are forwarded straight to the underlying spawn functions, 3395 and we refer to their documentation for details. 3396 3397 Params: 3398 args = An array which contains the program name as the zeroth element 3399 and any command-line arguments in the following elements. 3400 (See $(LREF spawnProcess) for details.) 3401 program = The program name, $(I without) command-line arguments. 3402 (See $(LREF spawnProcess) for details.) 3403 command = A shell command which is passed verbatim to the command 3404 interpreter. (See $(LREF spawnShell) for details.) 3405 env = Additional environment variables for the child process. 3406 (See $(LREF spawnProcess) for details.) 3407 config = Flags that control process creation. See $(LREF Config) 3408 for an overview of available flags, and note that the 3409 `retainStd...` flags have no effect in this function. 3410 maxOutput = The maximum number of bytes of output that should be 3411 captured. 3412 workDir = The working directory for the new process. 3413 By default the child process inherits the parent's working 3414 directory. 3415 shellPath = The path to the shell to use to run the specified program. 3416 By default this is $(LREF nativeShell). 3417 3418 3419 Returns: 3420 An $(D std.typecons.Tuple!(int, "status", string, "output")). 3421 3422 POSIX_specific: 3423 If the process is terminated by a signal, the `status` field of 3424 the return value will contain a negative number whose absolute 3425 value is the signal number. (See $(LREF wait) for details.) 3426 3427 Throws: 3428 $(LREF ProcessException) on failure to start the process.$(BR) 3429 $(REF StdioException, std,stdio) on failure to capture output. 3430 */ 3431 auto execute(scope const(char[])[] args, 3432 const string[string] env = null, 3433 Config config = Config.none, 3434 size_t maxOutput = size_t.max, 3435 scope const(char)[] workDir = null) 3436 @safe 3437 { 3438 return executeImpl!pipeProcess(args, env, config, maxOutput, workDir); 3439 } 3440 3441 /// ditto 3442 auto execute(scope const(char)[] program, 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(program, env, config, maxOutput, workDir); 3450 } 3451 3452 /// ditto 3453 auto executeShell(scope const(char)[] command, 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 string shellPath = nativeShell) 3459 @safe 3460 { 3461 return executeImpl!pipeShell(command, 3462 env, 3463 config, 3464 maxOutput, 3465 workDir, 3466 shellPath); 3467 } 3468 3469 // Does the actual work for execute() and executeShell(). 3470 private auto executeImpl(alias pipeFunc, Cmd, ExtraPipeFuncArgs...)( 3471 Cmd commandLine, 3472 const string[string] env = null, 3473 Config config = Config.none, 3474 size_t maxOutput = size_t.max, 3475 scope const(char)[] workDir = null, 3476 ExtraPipeFuncArgs extraArgs = ExtraPipeFuncArgs.init) 3477 @trusted //TODO: @safe 3478 { 3479 import std.algorithm.comparison : min; 3480 import std.array : appender; 3481 import std.typecons : Tuple; 3482 3483 auto redirect = (config.flags & Config.Flags.stderrPassThrough) 3484 ? Redirect.stdout 3485 : Redirect.stdout | Redirect.stderrToStdout; 3486 3487 auto p = pipeFunc(commandLine, redirect, 3488 env, config, workDir, extraArgs); 3489 3490 auto a = appender!string; 3491 enum size_t defaultChunkSize = 4096; 3492 immutable chunkSize = min(maxOutput, defaultChunkSize); 3493 3494 // Store up to maxOutput bytes in a. 3495 foreach (ubyte[] chunk; p.stdout.byChunk(chunkSize)) 3496 { 3497 immutable size_t remain = maxOutput - a.data.length; 3498 3499 if (chunk.length < remain) a.put(chunk); 3500 else 3501 { 3502 a.put(chunk[0 .. remain]); 3503 break; 3504 } 3505 } 3506 // Exhaust the stream, if necessary. 3507 foreach (ubyte[] chunk; p.stdout.byChunk(defaultChunkSize)) { } 3508 3509 return Tuple!(int, "status", string, "output")(wait(p.pid), a.data); 3510 } 3511 3512 @system unittest 3513 { 3514 import std.string; 3515 // To avoid printing the newline characters, we use the echo|set trick on 3516 // Windows, and printf on POSIX (neither echo -n nor echo \c are portable). 3517 version (Windows) TestScript prog = 3518 "echo|set /p=%~1 3519 echo|set /p=%~2 1>&2 3520 exit 123"; 3521 else version (Android) TestScript prog = 3522 `echo -n $1 3523 echo -n $2 >&2 3524 exit 123`; 3525 else version (Posix) TestScript prog = 3526 `printf '%s' $1 3527 printf '%s' $2 >&2 3528 exit 123`; 3529 auto r = execute([prog.path, "foo", "bar"]); 3530 assert(r.status == 123); 3531 assert(r.output.stripRight() == "foobar"); 3532 auto s = execute([prog.path, "Hello", "World"]); 3533 assert(s.status == 123); 3534 assert(s.output.stripRight() == "HelloWorld"); 3535 } 3536 3537 @safe unittest 3538 { 3539 import std.string; 3540 auto r1 = executeShell("echo foo"); 3541 assert(r1.status == 0); 3542 assert(r1.output.chomp() == "foo"); 3543 auto r2 = executeShell("echo bar 1>&2"); 3544 assert(r2.status == 0); 3545 assert(r2.output.chomp().stripRight() == "bar"); 3546 auto r3 = executeShell("exit 123"); 3547 assert(r3.status == 123); 3548 assert(r3.output.empty); 3549 } 3550 3551 @system unittest 3552 { 3553 // Temporarily disable output to stderr so as to not spam the build log. 3554 import std.stdio : stderr; 3555 import std.typecons : Tuple; 3556 import std.file : readText, exists, remove; 3557 import std.traits : ReturnType; 3558 3559 ReturnType!executeShell r; 3560 auto tmpname = uniqueTempPath; 3561 scope(exit) if (exists(tmpname)) remove(tmpname); 3562 auto t = stderr; 3563 // Open a new scope to minimize code ran with stderr redirected. 3564 { 3565 stderr.open(tmpname, "w"); 3566 scope(exit) stderr = t; 3567 r = executeShell("echo D rox>&2", null, Config.stderrPassThrough); 3568 } 3569 assert(r.status == 0); 3570 assert(r.output.empty); 3571 auto witness = readText(tmpname); 3572 import std.ascii : newline; 3573 assert(witness == "D rox" ~ newline, "'" ~ witness ~ "'"); 3574 } 3575 3576 @safe unittest 3577 { 3578 import std.typecons : Tuple; 3579 void foo() //Just test the compilation 3580 { 3581 auto ret1 = execute(["dummy", "arg"]); 3582 auto ret2 = executeShell("dummy arg"); 3583 static assert(is(typeof(ret1) == typeof(ret2))); 3584 3585 Tuple!(int, string) ret3 = execute(["dummy", "arg"]); 3586 } 3587 } 3588 3589 /// An exception that signals a problem with starting or waiting for a process. 3590 class ProcessException : Exception 3591 { 3592 import std.exception : basicExceptionCtors; 3593 mixin basicExceptionCtors; 3594 3595 // Creates a new ProcessException based on errno. 3596 static ProcessException newFromErrno(string customMsg = null, 3597 string file = __FILE__, 3598 size_t line = __LINE__) 3599 { 3600 import core.stdc.errno : errno; 3601 return newFromErrno(errno, customMsg, file, line); 3602 } 3603 3604 // ditto, but error number is provided by caller 3605 static ProcessException newFromErrno(int error, 3606 string customMsg = null, 3607 string file = __FILE__, 3608 size_t line = __LINE__) 3609 { 3610 import std.exception : errnoString; 3611 auto errnoMsg = errnoString(error); 3612 auto msg = customMsg.empty ? errnoMsg 3613 : customMsg ~ " (" ~ errnoMsg ~ ')'; 3614 return new ProcessException(msg, file, line); 3615 } 3616 3617 // Creates a new ProcessException based on GetLastError() (Windows only). 3618 version (Windows) 3619 static ProcessException newFromLastError(string customMsg = null, 3620 string file = __FILE__, 3621 size_t line = __LINE__) 3622 { 3623 auto lastMsg = generateSysErrorMsg(); 3624 auto msg = customMsg.empty ? lastMsg 3625 : customMsg ~ " (" ~ lastMsg ~ ')'; 3626 return new ProcessException(msg, file, line); 3627 } 3628 } 3629 3630 3631 /** 3632 Determines the path to the current user's preferred command interpreter. 3633 3634 On Windows, this function returns the contents of the COMSPEC environment 3635 variable, if it exists. Otherwise, it returns the result of $(LREF nativeShell). 3636 3637 On POSIX, `userShell` returns the contents of the SHELL environment 3638 variable, if it exists and is non-empty. Otherwise, it returns the result of 3639 $(LREF nativeShell). 3640 */ 3641 @property string userShell() @safe 3642 { 3643 version (Windows) return environment.get("COMSPEC", nativeShell); 3644 else version (Posix) return environment.get("SHELL", nativeShell); 3645 } 3646 3647 /** 3648 The platform-specific native shell path. 3649 3650 This function returns `"cmd.exe"` on Windows, `"/bin/sh"` on POSIX, and 3651 `"/system/bin/sh"` on Android. 3652 */ 3653 @property string nativeShell() @safe @nogc pure nothrow 3654 { 3655 version (Windows) return "cmd.exe"; 3656 else version (Android) return "/system/bin/sh"; 3657 else version (Posix) return "/bin/sh"; 3658 } 3659 3660 // A command-line switch that indicates to the shell that it should 3661 // interpret the following argument as a command to be executed. 3662 version (Posix) private immutable string shellSwitch = "-c"; 3663 version (Windows) private immutable string shellSwitch = "/C"; 3664 3665 // Unittest support code: TestScript takes a string that contains a 3666 // shell script for the current platform, and writes it to a temporary 3667 // file. On Windows the file name gets a .cmd extension, while on 3668 // POSIX its executable permission bit is set. The file is 3669 // automatically deleted when the object goes out of scope. 3670 version (StdUnittest) 3671 private struct TestScript 3672 { 3673 this(string code) @system 3674 { 3675 // @system due to chmod 3676 import std.ascii : newline; 3677 import std.file : write; 3678 version (Windows) 3679 { 3680 auto ext = ".cmd"; 3681 auto firstLine = "@echo off"; 3682 } 3683 else version (Posix) 3684 { 3685 auto ext = ""; 3686 auto firstLine = "#!" ~ nativeShell; 3687 } 3688 path = uniqueTempPath()~ext; 3689 write(path, firstLine ~ newline ~ code ~ newline); 3690 version (Posix) 3691 { 3692 import core.sys.posix.sys.stat : chmod; 3693 import std.conv : octal; 3694 chmod(path.tempCString(), octal!777); 3695 } 3696 } 3697 3698 ~this() 3699 { 3700 import std.file : remove, exists; 3701 if (!path.empty && exists(path)) 3702 { 3703 try { remove(path); } 3704 catch (Exception e) 3705 { 3706 debug std.stdio.stderr.writeln(e.msg); 3707 } 3708 } 3709 } 3710 3711 string path; 3712 } 3713 3714 3715 // ============================================================================= 3716 // Functions for shell command quoting/escaping. 3717 // ============================================================================= 3718 3719 3720 /* 3721 Command line arguments exist in three forms: 3722 1) string or char* array, as received by main. 3723 Also used internally on POSIX systems. 3724 2) Command line string, as used in Windows' 3725 CreateProcess and CommandLineToArgvW functions. 3726 A specific quoting and escaping algorithm is used 3727 to distinguish individual arguments. 3728 3) Shell command string, as written at a shell prompt 3729 or passed to cmd /C - this one may contain shell 3730 control characters, e.g. > or | for redirection / 3731 piping - thus, yet another layer of escaping is 3732 used to distinguish them from program arguments. 3733 3734 Except for escapeWindowsArgument, the intermediary 3735 format (2) is hidden away from the user in this module. 3736 */ 3737 3738 /** 3739 Escapes an argv-style argument array to be used with $(LREF spawnShell), 3740 $(LREF pipeShell) or $(LREF executeShell). 3741 --- 3742 string url = "http://dlang.org/"; 3743 executeShell(escapeShellCommand("wget", url, "-O", "dlang-index.html")); 3744 --- 3745 3746 Concatenate multiple `escapeShellCommand` and 3747 $(LREF escapeShellFileName) results to use shell redirection or 3748 piping operators. 3749 --- 3750 executeShell( 3751 escapeShellCommand("curl", "http://dlang.org/download.html") ~ 3752 "|" ~ 3753 escapeShellCommand("grep", "-o", `http://\S*\.zip`) ~ 3754 ">" ~ 3755 escapeShellFileName("D download links.txt")); 3756 --- 3757 3758 Throws: 3759 $(OBJECTREF Exception) if any part of the command line contains unescapable 3760 characters (NUL on all platforms, as well as CR and LF on Windows). 3761 */ 3762 string escapeShellCommand(scope const(char[])[] args...) @safe pure 3763 { 3764 if (args.empty) 3765 return null; 3766 version (Windows) 3767 { 3768 // Do not ^-escape the first argument (the program path), 3769 // as the shell parses it differently from parameters. 3770 // ^-escaping a program path that contains spaces will fail. 3771 string result = escapeShellFileName(args[0]); 3772 if (args.length > 1) 3773 { 3774 result ~= " " ~ escapeShellCommandString( 3775 escapeShellArguments(args[1..$])); 3776 } 3777 return result; 3778 } 3779 version (Posix) 3780 { 3781 return escapeShellCommandString(escapeShellArguments(args)); 3782 } 3783 } 3784 3785 @safe unittest 3786 { 3787 // This is a simple unit test without any special requirements, 3788 // in addition to the unittest_burnin one below which requires 3789 // special preparation. 3790 3791 struct TestVector { string[] args; string windows, posix; } 3792 TestVector[] tests = 3793 [ 3794 { 3795 args : ["foo bar"], 3796 windows : `"foo bar"`, 3797 posix : `'foo bar'` 3798 }, 3799 { 3800 args : ["foo bar", "hello"], 3801 windows : `"foo bar" hello`, 3802 posix : `'foo bar' hello` 3803 }, 3804 { 3805 args : ["foo bar", "hello world"], 3806 windows : `"foo bar" ^"hello world^"`, 3807 posix : `'foo bar' 'hello world'` 3808 }, 3809 { 3810 args : ["foo bar", "hello", "world"], 3811 windows : `"foo bar" hello world`, 3812 posix : `'foo bar' hello world` 3813 }, 3814 { 3815 args : ["foo bar", `'"^\`], 3816 windows : `"foo bar" ^"'\^"^^\\^"`, 3817 posix : `'foo bar' ''\''"^\'` 3818 }, 3819 { 3820 args : ["foo bar", ""], 3821 windows : `"foo bar" ^"^"`, 3822 posix : `'foo bar' ''` 3823 }, 3824 { 3825 args : ["foo bar", "2"], 3826 windows : `"foo bar" ^"2^"`, 3827 posix : `'foo bar' '2'` 3828 }, 3829 ]; 3830 3831 foreach (test; tests) 3832 { 3833 auto actual = escapeShellCommand(test.args); 3834 version (Windows) 3835 string expected = test.windows; 3836 else 3837 string expected = test.posix; 3838 assert(actual == expected, "\nExpected: " ~ expected ~ "\nGot: " ~ actual); 3839 } 3840 } 3841 3842 private string escapeShellCommandString(return scope string command) @safe pure 3843 { 3844 version (Windows) 3845 return escapeWindowsShellCommand(command); 3846 else 3847 return command; 3848 } 3849 3850 private string escapeWindowsShellCommand(scope const(char)[] command) @safe pure 3851 { 3852 import std.array : appender; 3853 auto result = appender!string(); 3854 result.reserve(command.length); 3855 3856 foreach (c; command) 3857 switch (c) 3858 { 3859 case '\0': 3860 throw new Exception("Cannot put NUL in command line"); 3861 case '\r': 3862 case '\n': 3863 throw new Exception("CR/LF are not escapable"); 3864 case '\x01': .. case '\x09': 3865 case '\x0B': .. case '\x0C': 3866 case '\x0E': .. case '\x1F': 3867 case '"': 3868 case '^': 3869 case '&': 3870 case '<': 3871 case '>': 3872 case '|': 3873 result.put('^'); 3874 goto default; 3875 default: 3876 result.put(c); 3877 } 3878 return result.data; 3879 } 3880 3881 private string escapeShellArguments(scope const(char[])[] args...) 3882 @trusted pure nothrow 3883 { 3884 import std.exception : assumeUnique; 3885 char[] buf; 3886 3887 @safe nothrow 3888 char[] allocator(size_t size) 3889 { 3890 if (buf.length == 0) 3891 return buf = new char[size]; 3892 else 3893 { 3894 auto p = buf.length; 3895 buf.length = buf.length + 1 + size; 3896 buf[p++] = ' '; 3897 return buf[p .. p+size]; 3898 } 3899 } 3900 3901 foreach (arg; args) 3902 escapeShellArgument!allocator(arg); 3903 return assumeUnique(buf); 3904 } 3905 3906 private auto escapeShellArgument(alias allocator)(scope const(char)[] arg) @safe nothrow 3907 { 3908 // The unittest for this function requires special 3909 // preparation - see below. 3910 3911 version (Windows) 3912 return escapeWindowsArgumentImpl!allocator(arg); 3913 else 3914 return escapePosixArgumentImpl!allocator(arg); 3915 } 3916 3917 /** 3918 Quotes a command-line argument in a manner conforming to the behavior of 3919 $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx, 3920 CommandLineToArgvW). 3921 */ 3922 string escapeWindowsArgument(scope const(char)[] arg) @trusted pure nothrow 3923 { 3924 // Rationale for leaving this function as public: 3925 // this algorithm of escaping paths is also used in other software, 3926 // e.g. DMD's response files. 3927 import std.exception : assumeUnique; 3928 auto buf = escapeWindowsArgumentImpl!charAllocator(arg); 3929 return assumeUnique(buf); 3930 } 3931 3932 3933 private char[] charAllocator(size_t size) @safe pure nothrow 3934 { 3935 return new char[size]; 3936 } 3937 3938 3939 private char[] escapeWindowsArgumentImpl(alias allocator)(scope const(char)[] arg) 3940 @safe nothrow 3941 if (is(typeof(allocator(size_t.init)[0] = char.init))) 3942 { 3943 // References: 3944 // * http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx 3945 // * http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx 3946 3947 // Check if the string needs to be escaped, 3948 // and calculate the total string size. 3949 3950 // Trailing backslashes must be escaped 3951 bool escaping = true; 3952 bool needEscape = false; 3953 // Result size = input size + 2 for surrounding quotes + 1 for the 3954 // backslash for each escaped character. 3955 size_t size = 1 + arg.length + 1; 3956 3957 foreach_reverse (char c; arg) 3958 { 3959 if (c == '"') 3960 { 3961 needEscape = true; 3962 escaping = true; 3963 size++; 3964 } 3965 else 3966 if (c == '\\') 3967 { 3968 if (escaping) 3969 size++; 3970 } 3971 else 3972 { 3973 if (c == ' ' || c == '\t') 3974 needEscape = true; 3975 escaping = false; 3976 } 3977 } 3978 3979 import std.ascii : isDigit; 3980 // Empty arguments need to be specified as "" 3981 if (!arg.length) 3982 needEscape = true; 3983 else 3984 // Arguments ending with digits need to be escaped, 3985 // to disambiguate with 1>file redirection syntax 3986 if (isDigit(arg[$-1])) 3987 needEscape = true; 3988 3989 if (!needEscape) 3990 return allocator(arg.length)[] = arg; 3991 3992 // Construct result string. 3993 3994 auto buf = allocator(size); 3995 size_t p = size; 3996 buf[--p] = '"'; 3997 escaping = true; 3998 foreach_reverse (char c; arg) 3999 { 4000 if (c == '"') 4001 escaping = true; 4002 else 4003 if (c != '\\') 4004 escaping = false; 4005 4006 buf[--p] = c; 4007 if (escaping) 4008 buf[--p] = '\\'; 4009 } 4010 buf[--p] = '"'; 4011 assert(p == 0); 4012 4013 return buf; 4014 } 4015 4016 version (Windows) version (StdUnittest) 4017 { 4018 private: 4019 import core.stdc.stddef; 4020 import core.stdc.wchar_ : wcslen; 4021 import core.sys.windows.shellapi : CommandLineToArgvW; 4022 import core.sys.windows.winbase; 4023 import core.sys.windows.winnt; 4024 import std.array; 4025 4026 string[] parseCommandLine(string line) 4027 { 4028 import std.algorithm.iteration : map; 4029 import std.array : array; 4030 import std.conv : to; 4031 auto lpCommandLine = (to!(WCHAR[])(line) ~ '\0').ptr; 4032 int numArgs; 4033 auto args = CommandLineToArgvW(lpCommandLine, &numArgs); 4034 scope(exit) LocalFree(args); 4035 return args[0 .. numArgs] 4036 .map!(arg => to!string(arg[0 .. wcslen(arg)])) 4037 .array(); 4038 } 4039 4040 @system unittest 4041 { 4042 import std.conv : text; 4043 string[] testStrings = [ 4044 `Hello`, 4045 `Hello, world`, 4046 `Hello, "world"`, 4047 `C:\`, 4048 `C:\dmd`, 4049 `C:\Program Files\`, 4050 ]; 4051 4052 enum CHARS = `_x\" *&^` ~ "\t"; // _ is placeholder for nothing 4053 foreach (c1; CHARS) 4054 foreach (c2; CHARS) 4055 foreach (c3; CHARS) 4056 foreach (c4; CHARS) 4057 testStrings ~= [c1, c2, c3, c4].replace("_", ""); 4058 4059 foreach (s; testStrings) 4060 { 4061 auto q = escapeWindowsArgument(s); 4062 auto args = parseCommandLine("Dummy.exe " ~ q); 4063 assert(args.length == 2, s ~ " => " ~ q ~ " #" ~ text(args.length-1)); 4064 assert(args[1] == s, s ~ " => " ~ q ~ " => " ~ args[1]); 4065 } 4066 } 4067 } 4068 4069 private string escapePosixArgument(scope const(char)[] arg) @trusted pure nothrow 4070 { 4071 import std.exception : assumeUnique; 4072 auto buf = escapePosixArgumentImpl!charAllocator(arg); 4073 return assumeUnique(buf); 4074 } 4075 4076 private char[] escapePosixArgumentImpl(alias allocator)(scope const(char)[] arg) 4077 @safe nothrow 4078 if (is(typeof(allocator(size_t.init)[0] = char.init))) 4079 { 4080 bool needQuoting = { 4081 import std.ascii : isAlphaNum, isDigit; 4082 import std.algorithm.comparison : among; 4083 4084 // Empty arguments need to be specified as '' 4085 if (arg.length == 0) 4086 return true; 4087 // Arguments ending with digits need to be escaped, 4088 // to disambiguate with 1>file redirection syntax 4089 if (isDigit(arg[$-1])) 4090 return true; 4091 4092 // Obtained using: 4093 // for n in $(seq 1 255) ; do 4094 // c=$(printf \\$(printf "%o" $n)) 4095 // q=$(/bin/printf '%q' "$c") 4096 // if [[ "$q" == "$c" ]] ; then printf "%s, " "'$c'" ; fi 4097 // done 4098 // printf '\n' 4099 foreach (char c; arg) 4100 if (!isAlphaNum(c) && !c.among('%', '+', ',', '-', '.', '/', ':', '@', ']', '_')) 4101 return true; 4102 return false; 4103 }(); 4104 if (!needQuoting) 4105 { 4106 auto buf = allocator(arg.length); 4107 buf[] = arg; 4108 return buf; 4109 } 4110 4111 // '\'' means: close quoted part of argument, append an escaped 4112 // single quote, and reopen quotes 4113 4114 // Below code is equivalent to: 4115 // return `'` ~ std.array.replace(arg, `'`, `'\''`) ~ `'`; 4116 4117 size_t size = 1 + arg.length + 1; 4118 foreach (char c; arg) 4119 if (c == '\'') 4120 size += 3; 4121 4122 auto buf = allocator(size); 4123 size_t p = 0; 4124 buf[p++] = '\''; 4125 foreach (char c; arg) 4126 if (c == '\'') 4127 { 4128 buf[p .. p+4] = `'\''`; 4129 p += 4; 4130 } 4131 else 4132 buf[p++] = c; 4133 buf[p++] = '\''; 4134 assert(p == size); 4135 4136 return buf; 4137 } 4138 4139 /** 4140 Escapes a filename to be used for shell redirection with $(LREF spawnShell), 4141 $(LREF pipeShell) or $(LREF executeShell). 4142 */ 4143 string escapeShellFileName(scope const(char)[] fileName) @trusted pure nothrow 4144 { 4145 // The unittest for this function requires special 4146 // preparation - see below. 4147 4148 version (Windows) 4149 { 4150 // If a file starts with &, it can cause cmd.exe to misinterpret 4151 // the file name as the stream redirection syntax: 4152 // command > "&foo.txt" 4153 // gets interpreted as 4154 // command >&foo.txt 4155 // Prepend .\ to disambiguate. 4156 4157 if (fileName.length && fileName[0] == '&') 4158 return cast(string)(`".\` ~ fileName ~ '"'); 4159 4160 return cast(string)('"' ~ fileName ~ '"'); 4161 } 4162 else 4163 return escapePosixArgument(fileName); 4164 } 4165 4166 // Loop generating strings with random characters 4167 //version = unittest_burnin; 4168 4169 version (unittest_burnin) 4170 @system unittest 4171 { 4172 // There are no readily-available commands on all platforms suitable 4173 // for properly testing command escaping. The behavior of CMD's "echo" 4174 // built-in differs from the POSIX program, and Windows ports of POSIX 4175 // environments (Cygwin, msys, gnuwin32) may interfere with their own 4176 // "echo" ports. 4177 4178 // To run this unit test, create std_process_unittest_helper.d with the 4179 // following content and compile it: 4180 // import std.stdio, std.array; void main(string[] args) { write(args.join("\0")); } 4181 // Then, test this module with: 4182 // rdmd --main -unittest -version=unittest_burnin process.d 4183 4184 import std.file : readText, remove; 4185 import std.format : format; 4186 import std.path : absolutePath; 4187 import std.random : uniform; 4188 4189 auto helper = absolutePath("std_process_unittest_helper"); 4190 assert(executeShell(helper ~ " hello").output.split("\0")[1..$] == ["hello"], "Helper malfunction"); 4191 4192 void test(string[] s, string fn) 4193 { 4194 string e; 4195 string[] g; 4196 4197 e = escapeShellCommand(helper ~ s); 4198 { 4199 scope(failure) writefln("executeShell() failed.\nExpected:\t%s\nEncoded:\t%s", s, [e]); 4200 auto result = executeShell(e); 4201 assert(result.status == 0, "std_process_unittest_helper failed"); 4202 g = result.output.split("\0")[1..$]; 4203 } 4204 assert(s == g, format("executeShell() test failed.\nExpected:\t%s\nGot:\t\t%s\nEncoded:\t%s", s, g, [e])); 4205 4206 e = escapeShellCommand(helper ~ s) ~ ">" ~ escapeShellFileName(fn); 4207 { 4208 scope(failure) writefln( 4209 "executeShell() with redirect failed.\nExpected:\t%s\nFilename:\t%s\nEncoded:\t%s", s, [fn], [e]); 4210 auto result = executeShell(e); 4211 assert(result.status == 0, "std_process_unittest_helper failed"); 4212 assert(!result.output.length, "No output expected, got:\n" ~ result.output); 4213 g = readText(fn).split("\0")[1..$]; 4214 } 4215 remove(fn); 4216 assert(s == g, 4217 format("executeShell() with redirect test failed.\nExpected:\t%s\nGot:\t\t%s\nEncoded:\t%s", s, g, [e])); 4218 } 4219 4220 while (true) 4221 { 4222 string[] args; 4223 foreach (n; 0 .. uniform(1, 4)) 4224 { 4225 string arg; 4226 foreach (l; 0 .. uniform(0, 10)) 4227 { 4228 dchar c; 4229 while (true) 4230 { 4231 version (Windows) 4232 { 4233 // As long as DMD's system() uses CreateProcessA, 4234 // we can't reliably pass Unicode 4235 c = uniform(0, 128); 4236 } 4237 else 4238 c = uniform!ubyte(); 4239 4240 if (c == 0) 4241 continue; // argv-strings are zero-terminated 4242 version (Windows) 4243 if (c == '\r' || c == '\n') 4244 continue; // newlines are unescapable on Windows 4245 break; 4246 } 4247 arg ~= c; 4248 } 4249 args ~= arg; 4250 } 4251 4252 // generate filename 4253 string fn; 4254 foreach (l; 0 .. uniform(1, 10)) 4255 { 4256 dchar c; 4257 while (true) 4258 { 4259 version (Windows) 4260 c = uniform(0, 128); // as above 4261 else 4262 c = uniform!ubyte(); 4263 4264 if (c == 0 || c == '/') 4265 continue; // NUL and / are the only characters 4266 // forbidden in POSIX filenames 4267 version (Windows) 4268 if (c < '\x20' || c == '<' || c == '>' || c == ':' || 4269 c == '"' || c == '\\' || c == '|' || c == '?' || c == '*') 4270 continue; // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx 4271 break; 4272 } 4273 4274 fn ~= c; 4275 } 4276 fn = fn[0..$/2] ~ "_testfile_" ~ fn[$/2..$]; 4277 4278 test(args, fn); 4279 } 4280 } 4281 4282 // ============================================================================= 4283 // Everything below this line was part of the old std.process, and most of 4284 // it will be deprecated and removed. 4285 // ============================================================================= 4286 4287 4288 /* 4289 Copyright: Copyright The D Language Foundation 2007 - 2009. 4290 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 4291 Authors: $(HTTP digitalmars.com, Walter Bright), 4292 $(HTTP erdani.org, Andrei Alexandrescu), 4293 $(HTTP thecybershadow.net, Vladimir Panteleev) 4294 Source: $(PHOBOSSRC std/_process.d) 4295 */ 4296 /* 4297 Copyright The D Language Foundation 2007 - 2009. 4298 Distributed under the Boost Software License, Version 1.0. 4299 (See accompanying file LICENSE_1_0.txt or copy at 4300 http://www.boost.org/LICENSE_1_0.txt) 4301 */ 4302 4303 4304 import core.stdc.errno; 4305 import core.stdc.stdlib; 4306 import core.stdc.string; 4307 import core.thread; 4308 4309 version (Windows) 4310 { 4311 import std.file, std.format, std.random; 4312 } 4313 version (Posix) 4314 { 4315 import core.sys.posix.stdlib; 4316 } 4317 4318 private void toAStringz(in string[] a, const(char)**az) 4319 { 4320 import std.string : toStringz; 4321 foreach (string s; a) 4322 { 4323 *az++ = toStringz(s); 4324 } 4325 *az = null; 4326 } 4327 4328 4329 /* ========================================================== */ 4330 4331 //version (Windows) 4332 //{ 4333 // int spawnvp(int mode, string pathname, string[] argv) 4334 // { 4335 // char** argv_ = cast(char**) core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length)); 4336 // scope(exit) core.stdc.stdlib.free(argv_); 4337 // 4338 // toAStringz(argv, argv_); 4339 // 4340 // return spawnvp(mode, pathname.tempCString(), argv_); 4341 // } 4342 //} 4343 4344 // Incorporating idea (for spawnvp() on Posix) from Dave Fladebo 4345 4346 enum { _P_WAIT, _P_NOWAIT, _P_OVERLAY } 4347 version (Windows) extern(C) int spawnvp(int, scope const(char) *, scope const(char*)*); 4348 alias P_WAIT = _P_WAIT; 4349 alias P_NOWAIT = _P_NOWAIT; 4350 4351 /* ========================================================== */ 4352 4353 version (StdDdoc) 4354 { 4355 /** 4356 Replaces the current process by executing a command, `pathname`, with 4357 the arguments in `argv`. 4358 4359 $(BLUE This function is Posix-Only.) 4360 4361 Typically, the first element of `argv` is 4362 the command being executed, i.e. $(D argv[0] == pathname). The 'p' 4363 versions of `exec` search the PATH environment variable for $(D 4364 pathname). The 'e' versions additionally take the new process' 4365 environment variables as an array of strings of the form key=value. 4366 4367 Does not return on success (the current process will have been 4368 replaced). Returns -1 on failure with no indication of the 4369 underlying error. 4370 4371 Windows_specific: 4372 These functions are only supported on POSIX platforms, as the Windows 4373 operating systems do not provide the ability to overwrite the current 4374 process image with another. In single-threaded programs it is possible 4375 to approximate the effect of `execv*` by using $(LREF spawnProcess) 4376 and terminating the current process once the child process has returned. 4377 For example: 4378 --- 4379 auto commandLine = [ "program", "arg1", "arg2" ]; 4380 version (Posix) 4381 { 4382 execv(commandLine[0], commandLine); 4383 throw new Exception("Failed to execute program"); 4384 } 4385 else version (Windows) 4386 { 4387 import core.stdc.stdlib : _Exit; 4388 _Exit(wait(spawnProcess(commandLine))); 4389 } 4390 --- 4391 This is, however, NOT equivalent to POSIX' `execv*`. For one thing, the 4392 executed program is started as a separate process, with all this entails. 4393 Secondly, in a multithreaded program, other threads will continue to do 4394 work while the current thread is waiting for the child process to complete. 4395 4396 A better option may sometimes be to terminate the current program immediately 4397 after spawning the child process. This is the behaviour exhibited by the 4398 $(LINK2 http://msdn.microsoft.com/en-us/library/431x4c1w.aspx,`__exec`) 4399 functions in Microsoft's C runtime library, and it is how D's now-deprecated 4400 Windows `execv*` functions work. Example: 4401 --- 4402 auto commandLine = [ "program", "arg1", "arg2" ]; 4403 version (Posix) 4404 { 4405 execv(commandLine[0], commandLine); 4406 throw new Exception("Failed to execute program"); 4407 } 4408 else version (Windows) 4409 { 4410 spawnProcess(commandLine); 4411 import core.stdc.stdlib : _exit; 4412 _exit(0); 4413 } 4414 --- 4415 */ 4416 int execv(in string pathname, in string[] argv); 4417 ///ditto 4418 int execve(in string pathname, in string[] argv, in string[] envp); 4419 /// ditto 4420 int execvp(in string pathname, in string[] argv); 4421 /// ditto 4422 int execvpe(in string pathname, in string[] argv, in string[] envp); 4423 } 4424 else version (Posix) 4425 { 4426 int execv(in string pathname, in string[] argv) 4427 { 4428 return execv_(pathname, argv); 4429 } 4430 int execve(in string pathname, in string[] argv, in string[] envp) 4431 { 4432 return execve_(pathname, argv, envp); 4433 } 4434 int execvp(in string pathname, in string[] argv) 4435 { 4436 return execvp_(pathname, argv); 4437 } 4438 int execvpe(in string pathname, in string[] argv, in string[] envp) 4439 { 4440 return execvpe_(pathname, argv, envp); 4441 } 4442 } 4443 4444 // Move these C declarations to druntime if we decide to keep the D wrappers 4445 extern(C) 4446 { 4447 int execv(scope const(char) *, scope const(char *)*); 4448 int execve(scope const(char)*, scope const(char*)*, scope const(char*)*); 4449 int execvp(scope const(char)*, scope const(char*)*); 4450 version (Windows) int execvpe(scope const(char)*, scope const(char*)*, scope const(char*)*); 4451 } 4452 4453 private int execv_(in string pathname, in string[] argv) 4454 { 4455 import core.exception : OutOfMemoryError; 4456 import std.exception : enforce; 4457 auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length)); 4458 enforce!OutOfMemoryError(argv_ !is null, "Out of memory in std.process."); 4459 scope(exit) core.stdc.stdlib.free(argv_); 4460 4461 toAStringz(argv, argv_); 4462 4463 return execv(pathname.tempCString(), argv_); 4464 } 4465 4466 private int execve_(in string pathname, in string[] argv, in string[] envp) 4467 { 4468 import core.exception : OutOfMemoryError; 4469 import std.exception : enforce; 4470 auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length)); 4471 enforce!OutOfMemoryError(argv_ !is null, "Out of memory in std.process."); 4472 scope(exit) core.stdc.stdlib.free(argv_); 4473 auto envp_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + envp.length)); 4474 enforce!OutOfMemoryError(envp_ !is null, "Out of memory in std.process."); 4475 scope(exit) core.stdc.stdlib.free(envp_); 4476 4477 toAStringz(argv, argv_); 4478 toAStringz(envp, envp_); 4479 4480 return execve(pathname.tempCString(), argv_, envp_); 4481 } 4482 4483 private int execvp_(in string pathname, in string[] argv) 4484 { 4485 import core.exception : OutOfMemoryError; 4486 import std.exception : enforce; 4487 auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length)); 4488 enforce!OutOfMemoryError(argv_ !is null, "Out of memory in std.process."); 4489 scope(exit) core.stdc.stdlib.free(argv_); 4490 4491 toAStringz(argv, argv_); 4492 4493 return execvp(pathname.tempCString(), argv_); 4494 } 4495 4496 private int execvpe_(in string pathname, in string[] argv, in string[] envp) 4497 { 4498 version (Posix) 4499 { 4500 import std.array : split; 4501 import std.conv : to; 4502 // Is pathname rooted? 4503 if (pathname[0] == '/') 4504 { 4505 // Yes, so just call execve() 4506 return execve(pathname, argv, envp); 4507 } 4508 else 4509 { 4510 // No, so must traverse PATHs, looking for first match 4511 string[] envPaths = split( 4512 to!string(core.stdc.stdlib.getenv("PATH")), ":"); 4513 int iRet = 0; 4514 4515 // Note: if any call to execve() succeeds, this process will cease 4516 // execution, so there's no need to check the execve() result through 4517 // the loop. 4518 4519 foreach (string pathDir; envPaths) 4520 { 4521 string composite = cast(string) (pathDir ~ "/" ~ pathname); 4522 4523 iRet = execve(composite, argv, envp); 4524 } 4525 if (0 != iRet) 4526 { 4527 iRet = execve(pathname, argv, envp); 4528 } 4529 4530 return iRet; 4531 } 4532 } 4533 else version (Windows) 4534 { 4535 import core.exception : OutOfMemoryError; 4536 import std.exception : enforce; 4537 auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length)); 4538 enforce!OutOfMemoryError(argv_ !is null, "Out of memory in std.process."); 4539 scope(exit) core.stdc.stdlib.free(argv_); 4540 auto envp_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + envp.length)); 4541 enforce!OutOfMemoryError(envp_ !is null, "Out of memory in std.process."); 4542 scope(exit) core.stdc.stdlib.free(envp_); 4543 4544 toAStringz(argv, argv_); 4545 toAStringz(envp, envp_); 4546 4547 return execvpe(pathname.tempCString(), argv_, envp_); 4548 } 4549 else 4550 { 4551 static assert(0); 4552 } // version 4553 } 4554 4555 version (StdDdoc) 4556 { 4557 /**************************************** 4558 * Start up the browser and set it to viewing the page at url. 4559 */ 4560 void browse(scope const(char)[] url); 4561 } 4562 else 4563 version (Windows) 4564 { 4565 import core.sys.windows.shellapi, core.sys.windows.winuser; 4566 4567 pragma(lib,"shell32.lib"); 4568 4569 void browse(scope const(char)[] url) nothrow @nogc @trusted 4570 { 4571 ShellExecuteW(null, "open", url.tempCStringW(), null, null, SW_SHOWNORMAL); 4572 } 4573 } 4574 else version (Posix) 4575 { 4576 import core.stdc.stdio; 4577 import core.stdc.string; 4578 import core.sys.posix.unistd; 4579 4580 void browse(scope const(char)[] url) nothrow @nogc @safe 4581 { 4582 const buffer = url.tempCString(); // Retain buffer until end of scope 4583 const(char)*[3] args; 4584 4585 // Trusted because it's called with a zero-terminated literal 4586 const(char)* browser = (() @trusted => core.stdc.stdlib.getenv("BROWSER"))(); 4587 if (browser) 4588 { 4589 // String already zero-terminated 4590 browser = (() @trusted => strdup(browser))(); 4591 args[0] = browser; 4592 } 4593 else 4594 { 4595 version (OSX) 4596 { 4597 args[0] = "open"; 4598 } 4599 else 4600 { 4601 //args[0] = "x-www-browser"; // doesn't work on some systems 4602 args[0] = "xdg-open"; 4603 } 4604 } 4605 4606 args[1] = buffer; 4607 args[2] = null; 4608 4609 auto childpid = core.sys.posix.unistd.fork(); 4610 if (childpid == 0) 4611 { 4612 // Trusted because args and all entries are always zero-terminated 4613 (() @trusted { 4614 core.sys.posix.unistd.execvp(args[0], &args[0]); 4615 perror(args[0]); 4616 core.sys.posix.unistd._exit(1); 4617 })(); 4618 assert(0, "Child failed to exec"); 4619 } 4620 if (browser) 4621 // Trusted because it's allocated via strdup above 4622 (() @trusted => free(cast(void*) browser))(); 4623 4624 version (StdUnittest) 4625 { 4626 // Verify that the test script actually suceeds 4627 int status; 4628 const check = (() @trusted => waitpid(childpid, &status, 0))(); 4629 assert(check != -1); 4630 assert(status == 0); 4631 } 4632 } 4633 } 4634 else 4635 static assert(0, "os not supported"); 4636 4637 // Verify attributes are consistent between all implementations 4638 @safe @nogc nothrow unittest 4639 { 4640 if (false) 4641 browse(""); 4642 } 4643 4644 version (Windows) { /* Doesn't use BROWSER */ } 4645 else 4646 @system unittest 4647 { 4648 import std.conv : text; 4649 import std.range : repeat; 4650 immutable string url = text("http://", repeat('x', 249)); 4651 4652 TestScript prog = `if [ "$1" != "` ~ url ~ `" ]; then exit 1; fi`; 4653 environment["BROWSER"] = prog.path; 4654 browse(url); 4655 }