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