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