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