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