1 // Written in the D programming language.
4 Functions for starting and interacting with other processes, and for
5 working with the current process' execution environment.
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`.)
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.)
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.)
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.)
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.)
40 $(LREF kill) attempts to terminate a running process.)
43 The following table compactly summarises the different process creation
44 functions and how they relate to each other:
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)
57 $(TD $(LREF executeShell)))
63 $(LREF pipe) is used to create unidirectional pipes.)
65 $(LREF environment) is an interface through which the current process'
66 environment variables can be read and manipulated.)
68 $(LREF escapeShellCommand) and $(LREF escapeShellFileName) are useful
69 for constructing shell command lines in a portable way.)
73 $(LINK2 https://github.com/kyllingstad, Lars Tandle Kyllingstad),
74 $(LINK2 https://github.com/schveiguy, Steven Schveighoffer),
75 $(HTTP thecybershadow.net, Vladimir Panteleev)
77 Copyright (c) 2013, the authors. All rights reserved.
79 $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
81 $(PHOBOSSRC std/process.d)
83 OBJECTREF=$(REF1 $0, object)
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).
92 import core.thread : ThreadID;
96 import core.sys.posix.sys.wait;
97 import core.sys.posix.unistd;
101 import core.stdc.stdio;
102 import core.sys.windows.winbase;
103 import core.sys.windows.winnt;
105 import std.windows.syserror;
108 import std.internal.cstring;
109 import std.range.primitives;
111 import std.traits : isSomeChar;
118 version = iOSDerived;
123 version = iOSDerived;
125 else version (WatchOS)
128 version = iOSDerived;
131 // When the DMC runtime is used, we have to use some custom functions
132 // to convert between Windows file handles and FILE*s.
133 version (Win32) version (CRuntime_DigitalMars) version = DMC_RUNTIME;
136 // Some of the following should be moved to druntime.
139 // Microsoft Visual C Runtime (MSVCRT) declarations.
142 version (DMC_RUNTIME) { } else
144 import core.stdc.stdint;
154 // POSIX API declarations.
159 extern(C) char*** _NSGetEnviron() nothrow;
160 const(char**) getEnvironPtr() @trusted
162 return *_NSGetEnviron;
167 // Made available by the C runtime:
168 extern(C) extern __gshared const char** environ;
169 const(char**) getEnvironPtr() @trusted
177 import core.thread : Thread;
178 new Thread({assert(getEnvironPtr !is null);}).start();
183 // =============================================================================
184 // Environment variable manipulation.
185 // =============================================================================
188 Manipulates _environment variables using an associative-array-like
191 This class contains only static methods, and cannot be instantiated.
192 See below for examples of use.
194 abstract final class environment
196 static import core.sys.posix.stdlib;
197 import core.stdc.errno : errno, EINVAL;
201 Retrieves the value of the environment variable with the given `name`.
203 auto path = environment["PATH"];
207 $(OBJECTREF Exception) if the environment variable does not exist,
208 or $(REF UTFException, std,utf) if the variable contains invalid UTF-16
209 characters (Windows only).
212 $(LREF environment.get), which doesn't throw on failure.
214 string opIndex(scope const(char)[] name) @safe
216 import std.exception : enforce;
217 return get(name, null).enforce("Environment variable not found: "~name);
221 Retrieves the value of the environment variable with the given `name`,
222 or a default value if the variable doesn't exist.
224 Unlike $(LREF environment.opIndex), this function never throws on Posix.
226 auto sh = environment.get("SHELL", "/bin/sh");
228 This function is also useful in checking for the existence of an
229 environment variable.
231 auto myVar = environment.get("MYVAR");
234 // Environment variable doesn't exist.
235 // Note that we have to use 'is' for the comparison, since
236 // myVar == null is also true if the variable exists but is
241 name = name of the environment variable to retrieve
242 defaultValue = default value to return if the environment variable doesn't exist.
245 the value of the environment variable if found, otherwise
246 `null` if the environment doesn't exist.
249 $(REF UTFException, std,utf) if the variable contains invalid UTF-16
250 characters (Windows only).
252 string get(scope const(char)[] name, string defaultValue = null) @safe
255 getImpl(name, (result) { value = result ? cachedToString(result) : defaultValue; });
260 Assigns the given `value` to the environment variable with the given
262 If `value` is null the variable is removed from environment.
264 If the variable does not exist, it will be created. If it already exists,
265 it will be overwritten.
267 environment["foo"] = "bar";
271 $(OBJECTREF Exception) if the environment variable could not be added
272 (e.g. if the name is invalid).
275 On some platforms, modifying environment variables may not be allowed in
276 multi-threaded programs. See e.g.
277 $(LINK2 https://www.gnu.org/software/libc/manual/html_node/Environment-Access.html#Environment-Access, glibc).
279 inout(char)[] opIndexAssign(return inout char[] value, scope const(char)[] name) @trusted
283 import std.exception : enforce, errnoEnforce;
289 if (core.sys.posix.stdlib.setenv(name.tempCString(), value.tempCString(), 1) != -1)
293 // The default errno error message is very uninformative
294 // in the most common case, so we handle it manually.
295 enforce(errno != EINVAL,
296 "Invalid environment variable name: '"~name~"'");
298 "Failed to add environment variable");
301 else version (Windows)
303 import std.exception : enforce;
305 SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW()),
306 sysErrorString(GetLastError())
310 else static assert(0);
314 Removes the environment variable with the given `name`.
316 If the variable isn't in the environment, this function returns
317 successfully without doing anything.
320 On some platforms, modifying environment variables may not be allowed in
321 multi-threaded programs. See e.g.
322 $(LINK2 https://www.gnu.org/software/libc/manual/html_node/Environment-Access.html#Environment-Access, glibc).
324 void remove(scope const(char)[] name) @trusted nothrow @nogc
326 version (Windows) SetEnvironmentVariableW(name.tempCStringW(), null);
327 else version (Posix) core.sys.posix.stdlib.unsetenv(name.tempCString());
328 else static assert(0);
332 Identify whether a variable is defined in the environment.
334 Because it doesn't return the value, this function is cheaper than `get`.
335 However, if you do need the value as well, you should just check the
336 return of `get` for `null` instead of using this function first.
341 if ("MY_ENV_FLAG" in environment)
345 if ("MY_ENV_VAR" in environment)
346 doSomething(environment["MY_ENV_VAR"]);
349 if (auto var = environment.get("MY_ENV_VAR"))
353 bool opBinaryRight(string op : "in")(scope const(char)[] name) @trusted
356 return core.sys.posix.stdlib.getenv(name.tempCString()) !is null;
357 else version (Windows)
359 SetLastError(NO_ERROR);
360 if (GetEnvironmentVariableW(name.tempCStringW, null, 0) > 0)
362 immutable err = GetLastError();
364 return true; // zero-length environment variable on Wine / XP
365 if (err == ERROR_ENVVAR_NOT_FOUND)
367 // Some other Windows error, throw.
368 throw new WindowsException(err);
370 else static assert(0);
374 Copies all environment variables into an associative array.
377 While Windows environment variable names are case insensitive, D's
378 built-in associative arrays are not. This function will store all
379 variable names in uppercase (e.g. `PATH`).
382 $(OBJECTREF Exception) if the environment variables could not
383 be retrieved (Windows only).
385 string[string] toAA() @trusted
387 import std.conv : to;
391 auto environ = getEnvironPtr;
392 for (int i=0; environ[i] != null; ++i)
394 import std.string : indexOf;
396 immutable varDef = to!string(environ[i]);
397 immutable eq = indexOf(varDef, '=');
400 immutable name = varDef[0 .. eq];
401 immutable value = varDef[eq+1 .. $];
403 // In POSIX, environment variables may be defined more
404 // than once. This is a security issue, which we avoid
405 // by checking whether the key already exists in the array.
407 // http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/environment-variables.html
408 if (name !in aa) aa[name] = value;
411 else version (Windows)
413 import std.exception : enforce;
414 import std.uni : toUpper;
415 auto envBlock = GetEnvironmentStringsW();
416 enforce(envBlock, "Failed to retrieve environment variables.");
417 scope(exit) FreeEnvironmentStringsW(envBlock);
419 for (int i=0; envBlock[i] != '\0'; ++i)
422 while (envBlock[i] != '=') ++i;
423 immutable name = toUTF8(toUpper(envBlock[start .. i]));
426 while (envBlock[i] != '\0') ++i;
428 // Ignore variables with empty names. These are used internally
429 // by Windows to keep track of each drive's individual current
434 // Just like in POSIX systems, environment variables may be
435 // defined more than once in an environment block on Windows,
436 // and it is just as much of a security issue there. Moreso,
437 // in fact, due to the case insensensitivity of variable names,
438 // which is not handled correctly by all programs.
439 auto val = toUTF8(envBlock[start .. i]);
440 if (name !in aa) aa[name] = val is null ? "" : val;
443 else static assert(0);
448 version (Windows) alias OSChar = WCHAR;
449 else version (Posix) alias OSChar = char;
451 // Retrieves the environment variable. Calls `sink` with a
452 // temporary buffer of OS characters, or `null` if the variable
454 void getImpl(scope const(char)[] name, scope void delegate(const(OSChar)[]) @safe sink) @trusted
458 // first we ask windows how long the environment variable is,
459 // then we try to read it in to a buffer of that length. Lots
460 // of error conditions because the windows API is nasty.
462 import std.conv : to;
463 const namezTmp = name.tempCStringW();
466 // clear error because GetEnvironmentVariable only says it sets it
467 // if the environment variable is missing, not on other errors.
468 SetLastError(NO_ERROR);
469 // len includes terminating null
470 immutable len = GetEnvironmentVariableW(namezTmp, null, 0);
473 immutable err = GetLastError();
474 if (err == ERROR_ENVVAR_NOT_FOUND)
476 if (err != NO_ERROR) // Some other Windows error, throw.
477 throw new WindowsException(err);
485 // lenRead is either the number of bytes read w/o null - if buf was long enough - or
486 // the number of bytes necessary *including* null if buf wasn't long enough
487 immutable lenRead = GetEnvironmentVariableW(namezTmp, buf.ptr, to!DWORD(buf.length));
490 immutable err = GetLastError();
491 if (err == NO_ERROR) // sucessfully read a 0-length variable
493 if (err == ERROR_ENVVAR_NOT_FOUND) // variable didn't exist
495 // some other windows error
496 throw new WindowsException(err);
498 assert(lenRead != buf.length, "impossible according to msft docs");
499 if (lenRead < buf.length) // the buffer was long enough
500 return sink(buf[0 .. lenRead]);
501 // resize and go around again, because the environment variable grew
502 buf.length = lenRead;
507 import core.stdc.string : strlen;
509 const vz = core.sys.posix.stdlib.getenv(name.tempCString());
510 if (vz == null) return sink(null);
511 return sink(vz[0 .. strlen(vz)]);
513 else static assert(0);
516 string cachedToString(C)(scope const(C)[] v) @safe
518 import std.algorithm.comparison : equal;
520 // Cache the last call's result.
521 static string lastResult;
524 // Return non-null array for blank result to distinguish from
525 // not-present result.
528 else if (!v.equal(lastResult))
530 import std.conv : to;
531 lastResult = v.to!string;
539 import std.exception : assertThrown;
541 environment["std_process"] = "foo";
542 assert(environment["std_process"] == "foo");
543 assert("std_process" in environment);
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);
551 environment.remove("std_process");
552 assert("std_process" !in environment);
554 // Remove again, should succeed
555 environment.remove("std_process");
556 assert("std_process" !in environment);
558 // Throw on not found.
559 assertThrown(environment["std_process"]);
561 // get() without default value
562 assert(environment.get("std_process") is null);
564 // get() with default value
565 assert(environment.get("std_process", "baz") == "baz");
567 // get() on an empty (but present) value
568 environment["std_process"] = "";
569 auto res = environment.get("std_process");
570 assert(res !is null);
572 assert("std_process" in environment);
574 // Important to do the following round-trip after the previous test
575 // because it tests toAA with an empty var
577 // Convert to associative array
578 auto aa = environment.toAA();
579 assert(aa.length > 0);
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).
587 assert(v == environment[n]);
590 // ... and back again.
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);
600 // Setting null must have the same effect as remove
601 environment["std_process"] = null;
602 assert("std_process" !in environment);
605 // =============================================================================
606 // Functions and classes for process management.
607 // =============================================================================
610 * Returns the process ID of the current process,
611 * which is guaranteed to be unique on the system.
615 * writefln("Current process ID: %d", thisProcessID);
618 @property int thisProcessID() @trusted nothrow //TODO: @safe
620 version (Windows) return GetCurrentProcessId();
621 else version (Posix) return core.sys.posix.unistd.getpid();
626 * Returns the process ID of the current thread,
627 * which is guaranteed to be unique within the current process.
630 * A $(REF ThreadID, core,thread) value for the calling thread.
634 * writefln("Current thread ID: %s", thisThreadID);
637 @property ThreadID thisThreadID() @trusted nothrow //TODO: @safe
640 return GetCurrentThreadId();
644 import core.sys.posix.pthread : pthread_self;
645 return pthread_self();
654 pidA = thisProcessID;
658 auto t = new Thread({
659 pidB = thisProcessID;
665 assert(pidA == pidB);
666 assert(tidA != tidB);
670 package(std) string uniqueTempPath() @safe
672 import std.file : tempDir;
673 import std.path : buildPath;
674 import std.uuid : randomUUID;
675 // Path should contain spaces to test escaping whitespace
676 return buildPath(tempDir(), "std.process temporary file " ~
677 randomUUID().toString());
681 version (iOSDerived) {}
685 Spawns a new process, optionally assigning it an arbitrary set of standard
686 input, output, and error streams.
688 The function returns immediately, leaving the child process to execute
689 in parallel with its parent. It is recommended to always call $(LREF wait)
690 on the returned $(LREF Pid) unless the process was spawned with
691 `Config.detached` flag, as detailed in the documentation for `wait`.
694 There are four overloads of this function. The first two take an array
695 of strings, `args`, which should contain the program name as the
696 zeroth element and any command-line arguments in subsequent elements.
697 The third and fourth versions are included for convenience, and may be
698 used when there are no command-line arguments. They take a single string,
699 `program`, which specifies the program name.
701 Unless a directory is specified in `args[0]` or `program`,
702 `spawnProcess` will search for the program in a platform-dependent
703 manner. On POSIX systems, it will look for the executable in the
704 directories listed in the PATH environment variable, in the order
705 they are listed. On Windows, it will search for the executable in
706 the following sequence:
708 $(LI The directory from which the application loaded.)
709 $(LI The current directory for the parent process.)
710 $(LI The 32-bit Windows system directory.)
711 $(LI The 16-bit Windows system directory.)
712 $(LI The Windows directory.)
713 $(LI The directories listed in the PATH environment variable.)
716 // Run an executable called "prog" located in the current working
718 auto pid = spawnProcess("./prog");
719 scope(exit) wait(pid);
720 // We can do something else while the program runs. The scope guard
721 // ensures that the process is waited for at the end of the scope.
724 // Run DMD on the file "myprog.d", specifying a few compiler switches:
725 auto dmdPid = spawnProcess(["dmd", "-O", "-release", "-inline", "myprog.d" ]);
726 if (wait(dmdPid) != 0)
727 writeln("Compilation failed!");
730 Environment_variables:
731 By default, the child process inherits the environment of the parent
732 process, along with any additional variables specified in the `env`
733 parameter. If the same variable exists in both the parent's environment
734 and in `env`, the latter takes precedence.
736 If the $(LREF Config.newEnv) flag is set in `config`, the child
737 process will $(I not) inherit the parent's environment. Its entire
738 environment will then be determined by `env`.
740 wait(spawnProcess("myapp", ["foo" : "bar"], Config.newEnv));
744 The optional arguments `stdin`, `stdout` and `stderr` may
745 be used to assign arbitrary $(REF File, std,stdio) objects as the standard
746 input, output and error streams, respectively, of the child process. The
747 former must be opened for reading, while the latter two must be opened for
748 writing. The default is for the child process to inherit the standard
749 streams of its parent.
751 // Run DMD on the file myprog.d, logging any error messages to a
752 // file named errors.log.
753 auto logFile = File("errors.log", "w");
754 auto pid = spawnProcess(["dmd", "myprog.d"],
759 writeln("Compilation failed. See errors.log for details.");
762 Note that if you pass a `File` object that is $(I not)
763 one of the standard input/output/error streams of the parent process,
764 that stream will by default be $(I closed) in the parent process when
765 this function returns. See the $(LREF Config) documentation below for
766 information about how to disable this behaviour.
768 Beware of buffering issues when passing `File` objects to
769 `spawnProcess`. The child process will inherit the low-level raw
770 read/write offset associated with the underlying file descriptor, but
771 it will not be aware of any buffered data. In cases where this matters
772 (e.g. when a file should be aligned before being passed on to the
773 child process), it may be a good idea to use unbuffered streams, or at
774 least ensure all relevant buffers are flushed.
777 args = An array which contains the program name as the zeroth element
778 and any command-line arguments in the following elements.
779 stdin = The standard input stream of the child process.
780 This can be any $(REF File, std,stdio) that is opened for reading.
781 By default the child process inherits the parent's input
783 stdout = The standard output stream of the child process.
784 This can be any $(REF File, std,stdio) that is opened for writing.
785 By default the child process inherits the parent's output stream.
786 stderr = The standard error stream of the child process.
787 This can be any $(REF File, std,stdio) that is opened for writing.
788 By default the child process inherits the parent's error stream.
789 env = Additional environment variables for the child process.
790 config = Flags that control process creation. See $(LREF Config)
791 for an overview of available flags.
792 workDir = The working directory for the new process.
793 By default the child process inherits the parent's working
797 A $(LREF Pid) object that corresponds to the spawned process.
800 $(LREF ProcessException) on failure to start the process.$(BR)
801 $(REF StdioException, std,stdio) on failure to pass one of the streams
802 to the child process (Windows only).$(BR)
803 $(REF RangeError, core,exception) if `args` is empty.
805 Pid spawnProcess(scope const(char[])[] args,
806 File stdin = std.stdio.stdin,
807 File stdout = std.stdio.stdout,
808 File stderr = std.stdio.stderr,
809 const string[string] env = null,
810 Config config = Config.none,
811 scope const char[] workDir = null)
816 const commandLine = escapeShellArguments(args);
817 const program = args.length ? args[0] : null;
818 return spawnProcessWin(commandLine, program, stdin, stdout, stderr, env, config, workDir);
822 return spawnProcessPosix(args, stdin, stdout, stderr, env, config, workDir);
829 Pid spawnProcess(scope const(char[])[] args,
830 const string[string] env,
831 Config config = Config.none,
832 scope const(char)[] workDir = null)
833 @trusted // TODO: Should be @safe
835 return spawnProcess(args,
845 Pid spawnProcess(scope const(char)[] program,
846 File stdin = std.stdio.stdin,
847 File stdout = std.stdio.stdout,
848 File stderr = std.stdio.stderr,
849 const string[string] env = null,
850 Config config = Config.none,
851 scope const(char)[] workDir = null)
854 return spawnProcess((&program)[0 .. 1],
855 stdin, stdout, stderr, env, config, workDir);
859 Pid spawnProcess(scope const(char)[] program,
860 const string[string] env,
861 Config config = Config.none,
862 scope const(char)[] workDir = null)
865 return spawnProcess((&program)[0 .. 1], env, config, workDir);
868 version (Posix) private enum InternalError : ubyte
880 Implementation of spawnProcess() for POSIX.
882 envz should be a zero-terminated array of zero-terminated strings
883 on the form "var=value".
886 private Pid spawnProcessPosix(scope const(char[])[] args,
890 scope const string[string] env,
892 scope const(char)[] workDir)
893 @trusted // TODO: Should be @safe
895 import core.exception : RangeError;
896 import std.algorithm.searching : any;
897 import std.conv : text;
898 import std.path : isDirSeparator;
899 import std.string : toStringz;
901 if (args.empty) throw new RangeError();
902 const(char)[] name = args[0];
903 if (!any!isDirSeparator(name))
905 name = searchPathFor(name);
907 throw new ProcessException(text("Executable file not found: ", args[0]));
910 // Convert program name and arguments to C-style strings.
911 auto argz = new const(char)*[args.length+1];
912 argz[0] = toStringz(name);
913 foreach (i; 1 .. args.length) argz[i] = toStringz(args[i]);
916 // Prepare environment.
917 auto envz = createEnv(env, !(config.flags & Config.Flags.newEnv));
919 // Open the working directory.
920 // We use open in the parent and fchdir in the child
921 // so that most errors (directory doesn't exist, not a directory)
922 // can be propagated as exceptions before forking.
924 scope(exit) if (workDirFD >= 0) close(workDirFD);
927 import core.sys.posix.fcntl : open, O_RDONLY, stat_t, fstat, S_ISDIR;
928 workDirFD = open(workDir.tempCString(), O_RDONLY);
930 throw ProcessException.newFromErrno("Failed to open working directory");
932 if (fstat(workDirFD, &s) < 0)
933 throw ProcessException.newFromErrno("Failed to stat working directory");
934 if (!S_ISDIR(s.st_mode))
935 throw new ProcessException("Not a directory: " ~ cast(string) workDir);
938 static int getFD(ref File f) { return core.stdc.stdio.fileno(f.getFP()); }
940 // Get the file descriptors of the streams.
941 // These could potentially be invalid, but that is OK. If so, later calls
942 // to dup2() and close() will just silently fail without causing any harm.
943 auto stdinFD = getFD(stdin);
944 auto stdoutFD = getFD(stdout);
945 auto stderrFD = getFD(stderr);
947 // We don't have direct access to the errors that may happen in a child process.
948 // So we use this pipe to deliver them.
950 if (core.sys.posix.unistd.pipe(forkPipe) == 0)
951 setCLOEXEC(forkPipe[1], true);
953 throw ProcessException.newFromErrno("Could not create pipe to check startup of child");
954 scope(exit) close(forkPipe[0]);
957 To create detached process, we use double fork technique
958 but we don't have a direct access to the second fork pid from the caller side thus use a pipe.
959 We also can't reuse forkPipe for that purpose
960 because we can't predict the order in which pid and possible error will be written
961 since the first and the second forks will run in parallel.
964 if (config.flags & Config.Flags.detached)
966 if (core.sys.posix.unistd.pipe(pidPipe) != 0)
967 throw ProcessException.newFromErrno("Could not create pipe to get process pid");
968 setCLOEXEC(pidPipe[1], true);
970 scope(exit) if (config.flags & Config.Flags.detached) close(pidPipe[0]);
972 static void abortOnError(int forkPipeOut, InternalError errorType, int error) nothrow
974 core.sys.posix.unistd.write(forkPipeOut, &errorType, errorType.sizeof);
975 core.sys.posix.unistd.write(forkPipeOut, &error, error.sizeof);
977 core.sys.posix.unistd._exit(1);
981 void closePipeWriteEnds()
984 if (config.flags & Config.Flags.detached)
988 auto id = core.sys.posix.unistd.fork();
991 closePipeWriteEnds();
992 throw ProcessException.newFromErrno("Failed to spawn new process");
995 void forkChild() nothrow @nogc
997 static import core.sys.posix.stdio;
1001 // no need for the read end of pipe on child side
1002 if (config.flags & Config.Flags.detached)
1005 immutable forkPipeOut = forkPipe[1];
1006 immutable pidPipeOut = pidPipe[1];
1008 // Set the working directory.
1011 if (fchdir(workDirFD) < 0)
1013 // Fail. It is dangerous to run a program
1014 // in an unexpected working directory.
1015 abortOnError(forkPipeOut, InternalError.chdir, .errno);
1022 // Redirect streams and close the old file descriptors.
1023 // In the case that stderr is redirected to stdout, we need
1024 // to backup the file descriptor since stdout may be redirected
1026 if (stderrFD == STDOUT_FILENO) stderrFD = dup(stderrFD);
1027 dup2(stdinFD, STDIN_FILENO);
1028 dup2(stdoutFD, STDOUT_FILENO);
1029 dup2(stderrFD, STDERR_FILENO);
1031 // Ensure that the standard streams aren't closed on execute, and
1032 // optionally close all other file descriptors.
1033 setCLOEXEC(STDIN_FILENO, false);
1034 setCLOEXEC(STDOUT_FILENO, false);
1035 setCLOEXEC(STDERR_FILENO, false);
1037 if (!(config.flags & Config.Flags.inheritFDs))
1039 // NOTE: malloc() and getrlimit() are not on the POSIX async
1040 // signal safe functions list, but practically this should
1041 // not be a problem. Java VM and CPython also use malloc()
1042 // in its own implementation via opendir().
1043 import core.stdc.stdlib : malloc;
1044 import core.sys.posix.poll : pollfd, poll, POLLNVAL;
1045 import core.sys.posix.sys.resource : rlimit, getrlimit, RLIMIT_NOFILE;
1047 // Get the maximum number of file descriptors that could be open.
1049 if (getrlimit(RLIMIT_NOFILE, &r) != 0)
1051 abortOnError(forkPipeOut, InternalError.getrlimit, .errno);
1053 immutable maxDescriptors = cast(int) r.rlim_cur;
1055 // The above, less stdin, stdout, and stderr
1056 immutable maxToClose = maxDescriptors - 3;
1058 // Call poll() to see which ones are actually open:
1059 auto pfds = cast(pollfd*) malloc(pollfd.sizeof * maxToClose);
1062 abortOnError(forkPipeOut, InternalError.malloc, .errno);
1064 foreach (i; 0 .. maxToClose)
1068 pfds[i].revents = 0;
1070 if (poll(pfds, maxToClose, 0) >= 0)
1072 foreach (i; 0 .. maxToClose)
1074 // don't close pipe write end
1075 if (pfds[i].fd == forkPipeOut) continue;
1076 // POLLNVAL will be set if the file descriptor is invalid.
1077 if (!(pfds[i].revents & POLLNVAL)) close(pfds[i].fd);
1082 // Fall back to closing everything.
1083 foreach (i; 3 .. maxDescriptors)
1085 if (i == forkPipeOut) continue;
1090 else // This is already done if we don't inherit descriptors.
1092 // Close the old file descriptors, unless they are
1093 // either of the standard streams.
1094 if (stdinFD > STDERR_FILENO) close(stdinFD);
1095 if (stdoutFD > STDERR_FILENO) close(stdoutFD);
1096 if (stderrFD > STDERR_FILENO) close(stderrFD);
1099 if (config.preExecFunction !is null)
1101 if (config.preExecFunction() != true)
1103 abortOnError(forkPipeOut, InternalError.preExec, .errno);
1108 core.sys.posix.unistd.execve(argz[0], argz.ptr, envz);
1110 // If execution fails, exit as quickly as possible.
1111 abortOnError(forkPipeOut, InternalError.exec, .errno);
1114 if (config.flags & Config.Flags.detached)
1116 auto secondFork = core.sys.posix.unistd.fork();
1117 if (secondFork == 0)
1122 else if (secondFork == -1)
1124 auto secondForkErrno = .errno;
1126 abortOnError(forkPipeOut, InternalError.doubleFork, secondForkErrno);
1130 core.sys.posix.unistd.write(pidPipeOut, &secondFork, pid_t.sizeof);
1149 closePipeWriteEnds();
1150 auto status = InternalError.noerror;
1151 auto readExecResult = core.sys.posix.unistd.read(forkPipe[0], &status, status.sizeof);
1152 // Save error number just in case if subsequent "waitpid" fails and overrides errno
1153 immutable lastError = .errno;
1155 if (config.flags & Config.Flags.detached)
1157 // Forked child exits right after creating second fork. So it should be safe to wait here.
1158 import core.sys.posix.sys.wait : waitpid;
1160 waitpid(id, &waitResult, 0);
1163 if (readExecResult == -1)
1164 throw ProcessException.newFromErrno(lastError, "Could not read from pipe to get child status");
1167 if (status != InternalError.noerror)
1170 readExecResult = read(forkPipe[0], &error, error.sizeof);
1172 final switch (status)
1174 case InternalError.chdir:
1175 errorMsg = "Failed to set working directory";
1177 case InternalError.getrlimit:
1178 errorMsg = "getrlimit failed";
1180 case InternalError.exec:
1181 errorMsg = "Failed to execute '" ~ cast(string) name ~ "'";
1183 case InternalError.doubleFork:
1184 // Can happen only when starting detached process
1185 assert(config.flags & Config.Flags.detached);
1186 errorMsg = "Failed to fork twice";
1188 case InternalError.malloc:
1189 errorMsg = "Failed to allocate memory";
1191 case InternalError.preExec:
1192 errorMsg = "Failed to execute preExecFunction";
1194 case InternalError.noerror:
1197 if (readExecResult == error.sizeof)
1198 throw ProcessException.newFromErrno(error, errorMsg);
1199 throw new ProcessException(errorMsg);
1201 else if (config.flags & Config.Flags.detached)
1204 if (read(pidPipe[0], &id, id.sizeof) != id.sizeof)
1205 throw ProcessException.newFromErrno("Could not read from pipe to get detached process id");
1208 // Parent process: Close streams and return.
1209 if (!(config.flags & Config.Flags.retainStdin ) && stdinFD > STDERR_FILENO
1210 && stdinFD != getFD(std.stdio.stdin ))
1212 if (!(config.flags & Config.Flags.retainStdout) && stdoutFD > STDERR_FILENO
1213 && stdoutFD != getFD(std.stdio.stdout))
1215 if (!(config.flags & Config.Flags.retainStderr) && stderrFD > STDERR_FILENO
1216 && stderrFD != getFD(std.stdio.stderr))
1218 return new Pid(id, owned);
1225 import std.concurrency : ownerTid, receiveTimeout, send, spawn;
1226 import std.datetime : seconds;
1230 sigaddset(&ss, SIGINT);
1231 pthread_sigmask(SIG_BLOCK, &ss, null);
1234 preExecFunction: () @trusted @nogc nothrow {
1235 // Reset signal handlers
1237 if (sigfillset(&ss) != 0)
1241 if (sigprocmask(SIG_UNBLOCK, &ss, null) != 0)
1249 auto pid = spawnProcess(["sleep", "10000"],
1262 // kill the spawned process with SIGINT
1263 // and send its return code
1264 spawn((shared Pid pid) {
1265 auto p = cast() pid;
1267 auto code = wait(p);
1269 send(ownerTid, code);
1270 }, cast(shared) pid);
1272 auto received = receiveTimeout(3.seconds, (int) {});
1277 Implementation of spawnProcess() for Windows.
1279 commandLine must contain the entire command line, properly
1280 quoted/escaped as required by CreateProcessW().
1282 envz must be a pointer to a block of UTF-16 characters on the form
1283 "var1=value1\0var2=value2\0...varN=valueN\0\0".
1286 private Pid spawnProcessWin(scope const(char)[] commandLine,
1287 scope const(char)[] program,
1291 scope const string[string] env,
1293 scope const(char)[] workDir)
1296 import core.exception : RangeError;
1297 import std.conv : text;
1299 if (commandLine.empty) throw new RangeError("Command line is empty");
1301 // Prepare environment.
1302 auto envz = createEnv(env, !(config.flags & Config.Flags.newEnv));
1304 // Startup info for CreateProcessW().
1305 STARTUPINFO_W startinfo;
1306 startinfo.cb = startinfo.sizeof;
1307 static int getFD(ref File f) { return f.isOpen ? f.fileno : -1; }
1309 // Extract file descriptors and HANDLEs from the streams and make the
1310 // handles inheritable.
1311 static void prepareStream(ref File file, DWORD stdHandle, string which,
1312 out int fileDescriptor, out HANDLE handle)
1314 enum _NO_CONSOLE_FILENO = cast(HANDLE)-2;
1315 fileDescriptor = getFD(file);
1317 if (fileDescriptor >= 0)
1318 handle = file.windowsHandle;
1319 // Windows GUI applications have a fd but not a valid Windows HANDLE.
1320 if (handle is null || handle == INVALID_HANDLE_VALUE || handle == _NO_CONSOLE_FILENO)
1321 handle = GetStdHandle(stdHandle);
1324 if (GetHandleInformation(handle, &dwFlags))
1326 if (!(dwFlags & HANDLE_FLAG_INHERIT))
1328 if (!SetHandleInformation(handle,
1329 HANDLE_FLAG_INHERIT,
1330 HANDLE_FLAG_INHERIT))
1332 throw new StdioException(
1333 "Failed to make "~which~" stream inheritable by child process ("
1334 ~sysErrorString(GetLastError()) ~ ')',
1340 int stdinFD = -1, stdoutFD = -1, stderrFD = -1;
1341 prepareStream(stdin, STD_INPUT_HANDLE, "stdin" , stdinFD, startinfo.hStdInput );
1342 prepareStream(stdout, STD_OUTPUT_HANDLE, "stdout", stdoutFD, startinfo.hStdOutput);
1343 prepareStream(stderr, STD_ERROR_HANDLE, "stderr", stderrFD, startinfo.hStdError );
1345 if ((startinfo.hStdInput != null && startinfo.hStdInput != INVALID_HANDLE_VALUE)
1346 || (startinfo.hStdOutput != null && startinfo.hStdOutput != INVALID_HANDLE_VALUE)
1347 || (startinfo.hStdError != null && startinfo.hStdError != INVALID_HANDLE_VALUE))
1348 startinfo.dwFlags = STARTF_USESTDHANDLES;
1351 PROCESS_INFORMATION pi;
1352 DWORD dwCreationFlags =
1353 CREATE_UNICODE_ENVIRONMENT |
1354 ((config.flags & Config.Flags.suppressConsole) ? CREATE_NO_WINDOW : 0);
1355 // workaround until https://issues.dlang.org/show_bug.cgi?id=14696 is fixed
1356 auto pworkDir = workDir.tempCStringW();
1357 if (!CreateProcessW(null, commandLine.tempCStringW().buffPtr,
1358 null, null, true, dwCreationFlags,
1359 envz, workDir.length ? pworkDir : null, &startinfo, &pi))
1360 throw ProcessException.newFromLastError("Failed to spawn process \"" ~ cast(string) program ~ '"');
1362 // figure out if we should close any of the streams
1363 if (!(config.flags & Config.Flags.retainStdin ) && stdinFD > STDERR_FILENO
1364 && stdinFD != getFD(std.stdio.stdin ))
1366 if (!(config.flags & Config.Flags.retainStdout) && stdoutFD > STDERR_FILENO
1367 && stdoutFD != getFD(std.stdio.stdout))
1369 if (!(config.flags & Config.Flags.retainStderr) && stderrFD > STDERR_FILENO
1370 && stderrFD != getFD(std.stdio.stderr))
1373 // close the thread handle in the process info structure
1374 CloseHandle(pi.hThread);
1375 if (config.flags & Config.Flags.detached)
1377 CloseHandle(pi.hProcess);
1378 return new Pid(pi.dwProcessId);
1380 return new Pid(pi.dwProcessId, pi.hProcess);
1383 // Converts childEnv to a zero-terminated array of zero-terminated strings
1384 // on the form "name=value", optionally adding those of the current process'
1385 // environment strings that are not present in childEnv. If the parent's
1386 // environment should be inherited without modification, this function
1387 // returns environ directly.
1389 private const(char*)* createEnv(const string[string] childEnv,
1390 bool mergeWithParentEnv)
1392 // Determine the number of strings in the parent's environment.
1393 int parentEnvLength = 0;
1394 auto environ = getEnvironPtr;
1395 if (mergeWithParentEnv)
1397 if (childEnv.length == 0) return environ;
1398 while (environ[parentEnvLength] != null) ++parentEnvLength;
1401 // Convert the "new" variables to C-style strings.
1402 auto envz = new const(char)*[parentEnvLength + childEnv.length + 1];
1404 foreach (var, val; childEnv)
1405 envz[pos++] = (var~'='~val~'\0').ptr;
1407 // Add the parent's environment.
1408 foreach (environStr; environ[0 .. parentEnvLength])
1411 while (environStr[eqPos] != '=' && environStr[eqPos] != '\0') ++eqPos;
1412 if (environStr[eqPos] != '=') continue;
1413 auto var = environStr[0 .. eqPos];
1414 if (var in childEnv) continue;
1415 envz[pos++] = environStr;
1421 version (Posix) @system unittest
1423 auto e1 = createEnv(null, false);
1424 assert(e1 != null && *e1 == null);
1426 auto e2 = createEnv(null, true);
1429 auto environ = getEnvironPtr;
1430 for (; environ[i] != null; ++i)
1432 assert(e2[i] != null);
1433 import core.stdc.string;
1434 assert(strcmp(e2[i], environ[i]) == 0);
1436 assert(e2[i] == null);
1438 auto e3 = createEnv(["foo" : "bar", "hello" : "world"], false);
1439 assert(e3 != null && e3[0] != null && e3[1] != null && e3[2] == null);
1440 assert((e3[0][0 .. 8] == "foo=bar\0" && e3[1][0 .. 12] == "hello=world\0")
1441 || (e3[0][0 .. 12] == "hello=world\0" && e3[1][0 .. 8] == "foo=bar\0"));
1445 // Converts childEnv to a Windows environment block, which is on the form
1446 // "name1=value1\0name2=value2\0...nameN=valueN\0\0", optionally adding
1447 // those of the current process' environment strings that are not present
1448 // in childEnv. Returns null if the parent's environment should be
1449 // inherited without modification, as this is what is expected by
1452 private LPVOID createEnv(const string[string] childEnv,
1453 bool mergeWithParentEnv)
1455 if (mergeWithParentEnv && childEnv.length == 0) return null;
1456 import std.array : appender;
1457 import std.uni : toUpper;
1458 auto envz = appender!(wchar[])();
1459 void put(string var, string val)
1464 envz.put(cast(wchar) '\0');
1467 // Add the variables in childEnv, removing them from parentEnv
1468 // if they exist there too.
1469 auto parentEnv = mergeWithParentEnv ? environment.toAA() : null;
1470 foreach (k, v; childEnv)
1472 auto uk = toUpper(k);
1474 if (uk in parentEnv) parentEnv.remove(uk);
1477 // Add remaining parent environment variables.
1478 foreach (k, v; parentEnv) put(k, v);
1480 // Two final zeros are needed in case there aren't any environment vars,
1481 // and the last one does no harm when there are.
1483 return envz.data.ptr;
1486 version (Windows) @system unittest
1488 assert(createEnv(null, true) == null);
1489 assert((cast(wchar*) createEnv(null, false))[0 .. 2] == "\0\0"w);
1490 auto e1 = (cast(wchar*) createEnv(["foo":"bar", "ab":"c"], false))[0 .. 14];
1491 assert(e1 == "FOO=bar\0AB=c\0\0"w || e1 == "AB=c\0FOO=bar\0\0"w);
1494 // Searches the PATH variable for the given executable file,
1495 // (checking that it is in fact executable).
1497 package(std) string searchPathFor(scope const(char)[] executable)
1500 import std.algorithm.iteration : splitter;
1501 import std.conv : text;
1502 import std.path : chainPath;
1506 environment.getImpl("PATH",
1507 (scope const(char)[] path)
1512 foreach (dir; splitter(path, ":"))
1514 auto execPath = chainPath(dir, executable);
1515 if (isExecutable(execPath))
1517 result = text(execPath);
1526 // Checks whether the file exists and can be executed by the
1529 private bool isExecutable(R)(R path) @trusted nothrow @nogc
1530 if (isInputRange!R && isSomeChar!(ElementEncodingType!R))
1532 return (access(path.tempCString(), X_OK) == 0);
1535 version (Posix) @safe unittest
1537 import std.algorithm;
1538 auto lsPath = searchPathFor("ls");
1539 assert(!lsPath.empty);
1540 assert(lsPath[0] == '/');
1541 assert(lsPath.endsWith("ls"));
1542 auto unlikely = searchPathFor("lkmqwpoialhggyaofijadsohufoiqezm");
1543 assert(unlikely is null, "Are you kidding me?");
1546 // Sets or unsets the FD_CLOEXEC flag on the given file descriptor.
1548 private void setCLOEXEC(int fd, bool on) nothrow @nogc
1550 import core.sys.posix.fcntl : fcntl, F_GETFD, FD_CLOEXEC, F_SETFD;
1551 auto flags = fcntl(fd, F_GETFD);
1554 if (on) flags |= FD_CLOEXEC;
1555 else flags &= ~(cast(typeof(flags)) FD_CLOEXEC);
1556 flags = fcntl(fd, F_SETFD, flags);
1558 assert(flags != -1 || .errno == EBADF);
1561 @system unittest // Command line arguments in spawnProcess().
1563 version (Windows) TestScript prog =
1564 "if not [%~1]==[foo] ( exit 1 )
1565 if not [%~2]==[bar] ( exit 2 )
1567 else version (Posix) TestScript prog =
1568 `if test "$1" != "foo"; then exit 1; fi
1569 if test "$2" != "bar"; then exit 2; fi
1571 assert(wait(spawnProcess(prog.path)) == 1);
1572 assert(wait(spawnProcess([prog.path])) == 1);
1573 assert(wait(spawnProcess([prog.path, "foo"])) == 2);
1574 assert(wait(spawnProcess([prog.path, "foo", "baz"])) == 2);
1575 assert(wait(spawnProcess([prog.path, "foo", "bar"])) == 0);
1578 // test that file descriptors are correctly closed / left open.
1579 // ideally this would be done by the child process making libc
1580 // calls, but we make do...
1581 version (Posix) @system unittest
1583 import core.sys.posix.fcntl : open, O_RDONLY;
1584 import core.sys.posix.unistd : close;
1585 import std.algorithm.searching : canFind, findSplitBefore;
1586 import std.array : split;
1587 import std.conv : to;
1588 static import std.file;
1589 import std.functional : reverseArgs;
1590 import std.path : buildPath;
1592 auto directory = uniqueTempPath();
1593 std.file.mkdir(directory);
1594 scope(exit) std.file.rmdirRecurse(directory);
1595 auto path = buildPath(directory, "tmp");
1596 std.file.write(path, null);
1597 auto fd = open(path.tempCString, O_RDONLY);
1598 scope(exit) close(fd);
1600 // command >&2 (or any other number) checks whethether that number
1601 // file descriptor is open.
1602 // Can't use this for arbitrary descriptors as many shells only support
1603 // single digit fds.
1604 TestScript testDefaults = `command >&0 && command >&1 && command >&2`;
1605 assert(execute(testDefaults.path).status == 0);
1606 assert(execute(testDefaults.path, null, Config.inheritFDs).status == 0);
1608 // Try a few different methods to check whether there are any
1609 // incorrectly-open files.
1612 // try /proc/<pid>/fd/ on linux
1615 TestScript proc = "ls /proc/$$/fd";
1616 auto procRes = execute(proc.path, null);
1617 if (procRes.status == 0)
1619 auto fdStr = fd.to!string;
1620 assert(!procRes.output.split.canFind(fdStr));
1621 assert(execute(proc.path, null, Config.inheritFDs)
1622 .output.split.canFind(fdStr));
1627 // try fuser (might sometimes need permissions)
1628 TestScript fuser = "echo $$ && fuser -f " ~ path;
1629 auto fuserRes = execute(fuser.path, null);
1630 if (fuserRes.status == 0)
1632 assert(!reverseArgs!canFind(fuserRes
1633 .output.findSplitBefore("\n").expand));
1634 assert(reverseArgs!canFind(execute(fuser.path, null, Config.inheritFDs)
1635 .output.findSplitBefore("\n").expand));
1639 // last resort, try lsof (not available on all Posix)
1640 TestScript lsof = "lsof -p$$";
1641 auto lsofRes = execute(lsof.path, null);
1642 if (lsofRes.status == 0)
1644 assert(!lsofRes.output.canFind(path));
1645 assert(execute(lsof.path, null, Config.inheritFDs).output.canFind(path));
1649 std.stdio.stderr.writeln(__FILE__, ':', __LINE__,
1650 ": Warning: Couldn't find any way to check open files");
1655 @system unittest // Environment variables in spawnProcess().
1657 // We really should use set /a on Windows, but Wine doesn't support it.
1658 version (Windows) TestScript envProg =
1659 `if [%STD_PROCESS_UNITTEST1%] == [1] (
1660 if [%STD_PROCESS_UNITTEST2%] == [2] (exit 3)
1663 if [%STD_PROCESS_UNITTEST1%] == [4] (
1664 if [%STD_PROCESS_UNITTEST2%] == [2] (exit 6)
1667 if [%STD_PROCESS_UNITTEST2%] == [2] (exit 2)
1669 version (Posix) TestScript envProg =
1670 `if test "$std_process_unittest1" = ""; then
1671 std_process_unittest1=0
1673 if test "$std_process_unittest2" = ""; then
1674 std_process_unittest2=0
1676 exit $(($std_process_unittest1+$std_process_unittest2))`;
1678 environment.remove("std_process_unittest1"); // Just in case.
1679 environment.remove("std_process_unittest2");
1680 assert(wait(spawnProcess(envProg.path)) == 0);
1681 assert(wait(spawnProcess(envProg.path, null, Config.newEnv)) == 0);
1683 environment["std_process_unittest1"] = "1";
1684 assert(wait(spawnProcess(envProg.path)) == 1);
1685 assert(wait(spawnProcess(envProg.path, null, Config.newEnv)) == 0);
1687 auto env = ["std_process_unittest2" : "2"];
1688 assert(wait(spawnProcess(envProg.path, env)) == 3);
1689 assert(wait(spawnProcess(envProg.path, env, Config.newEnv)) == 2);
1691 env["std_process_unittest1"] = "4";
1692 assert(wait(spawnProcess(envProg.path, env)) == 6);
1693 assert(wait(spawnProcess(envProg.path, env, Config.newEnv)) == 6);
1695 environment.remove("std_process_unittest1");
1696 assert(wait(spawnProcess(envProg.path, env)) == 6);
1697 assert(wait(spawnProcess(envProg.path, env, Config.newEnv)) == 6);
1700 @system unittest // Stream redirection in spawnProcess().
1702 import std.path : buildPath;
1704 version (Windows) TestScript prog =
1706 echo %INPUT% output %~1
1707 echo %INPUT% error %~2 1>&2
1709 else version (Posix) TestScript prog =
1711 echo $INPUT output $1
1712 echo $INPUT error $2 >&2
1713 echo done > \"$3\"";
1716 void testPipes(Config config)
1718 import std.file, std.uuid, core.thread, std.exception;
1719 auto pipei = pipe();
1720 auto pipeo = pipe();
1721 auto pipee = pipe();
1722 auto done = buildPath(tempDir(), randomUUID().toString());
1723 auto pid = spawnProcess([prog.path, "foo", "bar", done],
1724 pipei.readEnd, pipeo.writeEnd, pipee.writeEnd, null, config);
1725 pipei.writeEnd.writeln("input");
1726 pipei.writeEnd.flush();
1727 assert(pipeo.readEnd.readln().chomp() == "input output foo");
1728 assert(pipee.readEnd.readln().chomp().stripRight() == "input error bar");
1729 if (config.flags & Config.Flags.detached)
1730 while (!done.exists) Thread.sleep(10.msecs);
1733 while (remove(done).collectException) Thread.sleep(10.msecs);
1737 void testFiles(Config config)
1739 import std.ascii, std.file, std.uuid, core.thread, std.exception;
1740 auto pathi = buildPath(tempDir(), randomUUID().toString());
1741 auto patho = buildPath(tempDir(), randomUUID().toString());
1742 auto pathe = buildPath(tempDir(), randomUUID().toString());
1743 std.file.write(pathi, "INPUT"~std.ascii.newline);
1744 auto filei = File(pathi, "r");
1745 auto fileo = File(patho, "w");
1746 auto filee = File(pathe, "w");
1747 auto done = buildPath(tempDir(), randomUUID().toString());
1748 auto pid = spawnProcess([prog.path, "bar", "baz", done], filei, fileo, filee, null, config);
1749 if (config.flags & Config.Flags.detached)
1750 while (!done.exists) Thread.sleep(10.msecs);
1753 assert(readText(patho).chomp() == "INPUT output bar");
1754 assert(readText(pathe).chomp().stripRight() == "INPUT error baz");
1755 while (remove(pathi).collectException) Thread.sleep(10.msecs);
1756 while (remove(patho).collectException) Thread.sleep(10.msecs);
1757 while (remove(pathe).collectException) Thread.sleep(10.msecs);
1758 while (remove(done).collectException) Thread.sleep(10.msecs);
1761 testPipes(Config.none);
1762 testFiles(Config.none);
1763 testPipes(Config.detached);
1764 testFiles(Config.detached);
1767 @system unittest // Error handling in spawnProcess()
1769 import std.algorithm.searching : canFind;
1770 import std.exception : assertThrown, collectExceptionMsg;
1772 static void testNotFoundException(string program)
1774 assert(collectExceptionMsg!ProcessException(spawnProcess(program)).canFind(program));
1775 assert(collectExceptionMsg!ProcessException(spawnProcess(program, null, Config.detached)).canFind(program));
1777 testNotFoundException("ewrgiuhrifuheiohnmnvqweoijwf");
1778 testNotFoundException("./rgiuhrifuheiohnmnvqweoijwf");
1780 // can't execute malformed file with executable permissions
1783 import std.path : buildPath;
1784 import std.file : remove, write, setAttributes, tempDir;
1785 import core.sys.posix.sys.stat : S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP, S_IROTH, S_IXOTH;
1786 import std.conv : to;
1787 string deleteme = buildPath(tempDir(), "deleteme.std.process.unittest.pid") ~ to!string(thisProcessID);
1788 write(deleteme, "");
1789 scope(exit) remove(deleteme);
1790 setAttributes(deleteme, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
1791 assertThrown!ProcessException(spawnProcess(deleteme));
1792 assertThrown!ProcessException(spawnProcess(deleteme, null, Config.detached));
1796 @system unittest // Specifying a working directory.
1800 TestScript prog = "echo foo>bar";
1802 auto directory = uniqueTempPath();
1804 scope(exit) rmdirRecurse(directory);
1806 auto pid = spawnProcess([prog.path], null, Config.none, directory);
1808 assert(exists(buildPath(directory, "bar")));
1811 @system unittest // Specifying a bad working directory.
1813 import std.exception : assertThrown;
1815 TestScript prog = "echo";
1817 auto directory = uniqueTempPath();
1818 assertThrown!ProcessException(spawnProcess([prog.path], null, Config.none, directory));
1819 assertThrown!ProcessException(spawnProcess([prog.path], null, Config.detached, directory));
1821 std.file.write(directory, "foo");
1822 scope(exit) remove(directory);
1823 assertThrown!ProcessException(spawnProcess([prog.path], null, Config.none, directory));
1824 assertThrown!ProcessException(spawnProcess([prog.path], null, Config.detached, directory));
1826 // can't run in directory if user does not have search permission on this directory
1829 if (core.sys.posix.unistd.getuid() != 0)
1831 import core.sys.posix.sys.stat : S_IRUSR;
1832 auto directoryNoSearch = uniqueTempPath();
1833 mkdir(directoryNoSearch);
1834 scope(exit) rmdirRecurse(directoryNoSearch);
1835 setAttributes(directoryNoSearch, S_IRUSR);
1836 assertThrown!ProcessException(spawnProcess(prog.path, null, Config.none, directoryNoSearch));
1837 assertThrown!ProcessException(spawnProcess(prog.path, null, Config.detached, directoryNoSearch));
1842 @system unittest // Specifying empty working directory.
1844 TestScript prog = "";
1846 string directory = "";
1847 assert(directory.ptr && !directory.length);
1848 spawnProcess([prog.path], null, Config.none, directory).wait();
1851 // Reopening the standard streams (https://issues.dlang.org/show_bug.cgi?id=13258)
1858 spawnShell("echo foo").wait();
1859 spawnShell("echo bar").wait();
1862 auto tmpFile = uniqueTempPath();
1863 scope(exit) if (exists(tmpFile)) remove(tmpFile);
1866 auto oldOut = std.stdio.stdout;
1867 scope(exit) std.stdio.stdout = oldOut;
1869 std.stdio.stdout = File(tmpFile, "w");
1871 std.stdio.stdout.close();
1874 auto lines = readText(tmpFile).splitLines();
1875 assert(lines == ["foo", "bar"]);
1878 // MSVCRT workaround (https://issues.dlang.org/show_bug.cgi?id=14422)
1882 auto fn = uniqueTempPath();
1883 scope(exit) if (exists(fn)) remove(fn);
1884 std.file.write(fn, "AAAAAAAAAA");
1886 auto f = File(fn, "a");
1887 spawnProcess(["cmd", "/c", "echo BBBBB"], std.stdio.stdin, f).wait();
1889 auto data = readText(fn);
1890 assert(data == "AAAAAAAAAABBBBB\r\n", data);
1893 // https://issues.dlang.org/show_bug.cgi?id=20765
1894 // Test that running processes with relative path works in conjunction
1895 // with indicating a workDir.
1896 version (Posix) @system unittest
1898 import std.file : mkdir, write, setAttributes, rmdirRecurse;
1899 import std.conv : octal;
1901 auto dir = uniqueTempPath();
1903 scope(exit) rmdirRecurse(dir);
1904 write(dir ~ "/program", "#!/bin/sh\necho Hello");
1905 setAttributes(dir ~ "/program", octal!700);
1907 assert(execute(["./program"], null, Config.none, size_t.max, dir).output == "Hello\n");
1911 A variation on $(LREF spawnProcess) that runs the given _command through
1912 the current user's preferred _command interpreter (aka. shell).
1914 The string `command` is passed verbatim to the shell, and is therefore
1915 subject to its rules about _command structure, argument/filename quoting
1916 and escaping of special characters.
1917 The path to the shell executable defaults to $(LREF nativeShell).
1919 In all other respects this function works just like `spawnProcess`.
1920 Please refer to the $(LREF spawnProcess) documentation for descriptions
1921 of the other function parameters, the return value and any exceptions
1924 // Run the command/program "foo" on the file named "my file.txt", and
1925 // redirect its output into foo.log.
1926 auto pid = spawnShell(`foo "my file.txt" > foo.log`);
1931 $(LREF escapeShellCommand), which may be helpful in constructing a
1932 properly quoted and escaped shell _command line for the current platform.
1934 Pid spawnShell(scope const(char)[] command,
1935 File stdin = std.stdio.stdin,
1936 File stdout = std.stdio.stdout,
1937 File stderr = std.stdio.stderr,
1938 scope const string[string] env = null,
1939 Config config = Config.none,
1940 scope const(char)[] workDir = null,
1941 scope string shellPath = nativeShell)
1942 @trusted // See reason below
1946 // CMD does not parse its arguments like other programs.
1947 // It does not use CommandLineToArgvW.
1948 // Instead, it treats the first and last quote specially.
1949 // See CMD.EXE /? for details.
1950 const commandLine = escapeShellFileName(shellPath)
1951 ~ ` ` ~ shellSwitch ~ ` "` ~ command ~ `"`;
1952 return spawnProcessWin(commandLine, shellPath, stdin, stdout, stderr, env, config, workDir);
1954 else version (Posix)
1956 const(char)[][3] args;
1957 args[0] = shellPath;
1958 args[1] = shellSwitch;
1960 /* The passing of args converts the static array, which is initialized with `scope` pointers,
1961 * to a dynamic array, which is also a scope parameter. So, it is a scope pointer to a
1962 * scope pointer, which although is safely used here, D doesn't allow transitive scope.
1963 * See https://github.com/dlang/dmd/pull/10951
1965 return spawnProcessPosix(args, stdin, stdout, stderr, env, config, workDir);
1972 Pid spawnShell(scope const(char)[] command,
1973 scope const string[string] env,
1974 Config config = Config.none,
1975 scope const(char)[] workDir = null,
1976 scope string shellPath = nativeShell)
1977 @trusted // TODO: Should be @safe
1979 return spawnShell(command,
1992 auto cmd = "echo %FOO%";
1993 else version (Posix)
1994 auto cmd = "echo $foo";
1996 auto tmpFile = uniqueTempPath();
1997 scope(exit) if (exists(tmpFile)) remove(tmpFile);
1998 auto redir = "> \""~tmpFile~'"';
1999 auto env = ["foo" : "bar"];
2000 assert(wait(spawnShell(cmd~redir, env)) == 0);
2001 auto f = File(tmpFile, "a");
2002 version (CRuntime_Microsoft) f.seek(0, SEEK_END); // MSVCRT probably seeks to the end when writing, not before
2003 assert(wait(spawnShell(cmd, std.stdio.stdin, f, std.stdio.stderr, env)) == 0);
2005 auto output = std.file.readText(tmpFile);
2006 assert(output == "bar\nbar\n" || output == "bar\r\nbar\r\n");
2013 import std.conv : text;
2014 TestScript prog = "echo %0 %*";
2015 auto outputFn = uniqueTempPath();
2016 scope(exit) if (exists(outputFn)) remove(outputFn);
2017 auto args = [`a b c`, `a\b\c\`, `a"b"c"`];
2018 auto result = executeShell(
2019 escapeShellCommand([prog.path] ~ args)
2021 escapeShellFileName(outputFn));
2022 assert(result.status == 0);
2023 auto args2 = outputFn.readText().strip().parseCommandLine()[1..$];
2024 assert(args == args2, text(args2));
2029 Options that control the behaviour of process creation functions in this
2030 module. Most options only apply to $(LREF spawnProcess) and
2035 auto logFile = File("myapp_error.log", "w");
2037 // Start program, suppressing the console window (Windows only),
2038 // redirect its error stream to logFile, and leave logFile open
2039 // in the parent process as well.
2040 auto pid = spawnProcess("myapp", stdin, stdout, logFile,
2041 Config.retainStderr | Config.suppressConsole);
2044 auto exitCode = wait(pid);
2045 logFile.writeln("myapp exited with code ", exitCode);
2054 Use bitwise OR to combine flags.
2061 By default, the child process inherits the parent's environment,
2062 and any environment variables passed to $(LREF spawnProcess) will
2063 be added to it. If this flag is set, the only variables in the
2064 child process' environment will be those given to spawnProcess.
2069 Unless the child process inherits the standard input/output/error
2070 streams of its parent, one almost always wants the streams closed
2071 in the parent when $(LREF spawnProcess) returns. Therefore, by
2072 default, this is done. If this is not desirable, pass any of these
2073 options to spawnProcess.
2076 retainStdout = 4, /// ditto
2077 retainStderr = 8, /// ditto
2080 On Windows, if the child process is a console application, this
2081 flag will prevent the creation of a console window. Otherwise,
2082 it will be ignored. On POSIX, `suppressConsole` has no effect.
2084 suppressConsole = 16,
2087 On POSIX, open $(LINK2 http://en.wikipedia.org/wiki/File_descriptor,file descriptors)
2088 are by default inherited by the child process. As this may lead
2089 to subtle bugs when pipes or multiple threads are involved,
2090 $(LREF spawnProcess) ensures that all file descriptors except the
2091 ones that correspond to standard input/output/error are closed
2092 in the child process when it starts. Use `inheritFDs` to prevent
2095 On Windows, this option has no effect, and any handles which have been
2096 explicitly marked as inheritable will always be inherited by the child
2102 Spawn process in detached state. This removes the need in calling
2103 $(LREF wait) to clean up the process resources.
2106 Calling $(LREF wait) or $(LREF kill) with the resulting `Pid` is invalid.
2111 By default, the $(LREF execute) and $(LREF executeShell) functions
2112 will capture child processes' both stdout and stderr. This can be
2113 undesirable if the standard output is to be processed or otherwise
2114 used by the invoking program, as `execute`'s result would then
2115 contain a mix of output and warning/error messages.
2117 Specify this flag when calling `execute` or `executeShell` to
2118 cause invoked processes' stderr stream to be sent to $(REF stderr,
2119 std,stdio), and only capture and return standard output.
2121 This flag has no effect on $(LREF spawnProcess) or $(LREF spawnShell).
2123 stderrPassThrough = 128,
2125 Flags flags; /// ditto
2128 For backwards compatibility, and cases when only flags need to
2129 be specified in the `Config`, these allow building `Config`
2130 instances using flag names only.
2132 enum Config none = Config.init;
2133 enum Config newEnv = Config(Flags.newEnv); /// ditto
2134 enum Config retainStdin = Config(Flags.retainStdin); /// ditto
2135 enum Config retainStdout = Config(Flags.retainStdout); /// ditto
2136 enum Config retainStderr = Config(Flags.retainStderr); /// ditto
2137 enum Config suppressConsole = Config(Flags.suppressConsole); /// ditto
2138 enum Config inheritFDs = Config(Flags.inheritFDs); /// ditto
2139 enum Config detached = Config(Flags.detached); /// ditto
2140 enum Config stderrPassThrough = Config(Flags.stderrPassThrough); /// ditto
2141 Config opUnary(string op)()
2142 if (is(typeof(mixin(op ~ q{flags}))))
2144 return Config(mixin(op ~ q{flags}));
2146 Config opBinary(string op)(Config other)
2147 if (is(typeof(mixin(q{flags} ~ op ~ q{other.flags}))))
2149 return Config(mixin(q{flags} ~ op ~ q{other.flags}));
2151 Config opOpAssign(string op)(Config other)
2152 if (is(typeof(mixin(q{flags} ~ op ~ q{=other.flags}))))
2154 return Config(mixin(q{flags} ~ op ~ q{=other.flags}));
2160 A function that is called before `exec` in $(LREF spawnProcess).
2161 It returns `true` if succeeded and otherwise returns `false`.
2164 Please note that the code in this function must only use
2165 async-signal-safe functions.)
2167 On Windows, this member is not available.
2169 bool function() nothrow @nogc @safe preExecFunction;
2171 else version (Posix)
2173 bool function() nothrow @nogc @safe preExecFunction;
2177 // https://issues.dlang.org/show_bug.cgi?id=22125
2180 Config c = Config.retainStdin;
2181 c |= Config.retainStdout;
2182 c |= Config.retainStderr;
2183 c &= ~Config.retainStderr;
2184 assert(c == (Config.retainStdin | Config.retainStdout));
2187 /// A handle that corresponds to a spawned process.
2191 The process ID number.
2193 This is a number that uniquely identifies the process on the operating
2194 system, for at least as long as the process is running. Once $(LREF wait)
2195 has been called on the $(LREF Pid), this method will return an
2196 invalid (negative) process ID.
2198 @property int processID() const @safe pure nothrow
2204 An operating system handle to the process.
2206 This handle is used to specify the process in OS-specific APIs.
2207 On POSIX, this function returns a `core.sys.posix.sys.types.pid_t`
2208 with the same value as $(LREF Pid.processID), while on Windows it returns
2209 a `core.sys.windows.windows.HANDLE`.
2211 Once $(LREF wait) has been called on the $(LREF Pid), this method
2212 will return an invalid handle.
2214 // Note: Since HANDLE is a reference, this function cannot be const.
2216 @property HANDLE osHandle() @nogc @safe pure nothrow
2220 else version (Posix)
2221 @property pid_t osHandle() @nogc @safe pure nothrow
2228 Pid.performWait() does the dirty work for wait() and nonBlockingWait().
2230 If block == true, this function blocks until the process terminates,
2231 sets _processID to terminated, and returns the exit code or terminating
2232 signal as described in the wait() documentation.
2234 If block == false, this function returns immediately, regardless
2235 of the status of the process. If the process has terminated, the
2236 function has the exact same effect as the blocking version. If not,
2237 it returns 0 and does not modify _processID.
2240 int performWait(bool block) @trusted
2242 import std.exception : enforce;
2243 enforce!ProcessException(owned, "Can't wait on a detached process");
2244 if (_processID == terminated) return _exitCode;
2249 auto check = waitpid(_processID, &status, block ? 0 : WNOHANG);
2252 if (errno == ECHILD)
2254 throw new ProcessException(
2255 "Process does not exist or is not a child process.");
2259 // waitpid() was interrupted by a signal. We simply
2261 assert(errno == EINTR);
2265 if (!block && check == 0) return 0;
2266 if (WIFEXITED(status))
2268 exitCode = WEXITSTATUS(status);
2271 else if (WIFSIGNALED(status))
2273 exitCode = -WTERMSIG(status);
2276 // We check again whether the call should be blocking,
2277 // since we don't care about other status changes besides
2278 // "exited" and "terminated by signal".
2279 if (!block) return 0;
2281 // Process has stopped, but not terminated, so we continue waiting.
2283 // Mark Pid as terminated, and cache and return exit code.
2284 _processID = terminated;
2285 _exitCode = exitCode;
2288 else version (Windows)
2290 int performWait(const bool block, const DWORD timeout = INFINITE) @trusted
2292 import std.exception : enforce;
2293 enforce!ProcessException(owned, "Can't wait on a detached process");
2294 if (_processID == terminated) return _exitCode;
2295 assert(_handle != INVALID_HANDLE_VALUE);
2298 auto result = WaitForSingleObject(_handle, timeout);
2299 if (result != WAIT_OBJECT_0)
2301 // Wait time exceeded `timeout` milliseconds?
2302 if (result == WAIT_TIMEOUT && timeout != INFINITE)
2305 throw ProcessException.newFromLastError("Wait failed.");
2308 if (!GetExitCodeProcess(_handle, cast(LPDWORD)&_exitCode))
2309 throw ProcessException.newFromLastError();
2310 if (!block && _exitCode == STILL_ACTIVE) return 0;
2311 CloseHandle(_handle);
2312 _handle = INVALID_HANDLE_VALUE;
2313 _processID = terminated;
2317 int performWait(Duration timeout) @safe
2319 import std.exception : enforce;
2320 const msecs = timeout.total!"msecs";
2322 // Limit this implementation the maximum wait time offered by
2323 // WaitForSingleObject. One could theoretically break up larger
2324 // durations into multiple waits but (DWORD.max - 1).msecs
2325 // (> 7 weeks, 17 hours) should be enough for the usual case.
2326 // DWORD.max is reserved for INFINITE
2327 enforce!ProcessException(msecs < DWORD.max, "Timeout exceeds maximum wait time!");
2328 return performWait(true, cast(DWORD) msecs);
2333 if (_handle != INVALID_HANDLE_VALUE)
2335 CloseHandle(_handle);
2336 _handle = INVALID_HANDLE_VALUE;
2341 // Special values for _processID.
2342 enum invalid = -1, terminated = -2;
2344 // OS process ID number. Only nonnegative IDs correspond to
2345 // running processes.
2346 int _processID = invalid;
2348 // Exit code cached by wait(). This is only expected to hold a
2349 // sensible value if _processID == terminated.
2352 // Whether the process can be waited for by wait() for or killed by kill().
2353 // False if process was started as detached. True otherwise.
2356 // Pids are only meant to be constructed inside this module, so
2357 // we make the constructor private.
2360 HANDLE _handle = INVALID_HANDLE_VALUE;
2361 this(int pid, HANDLE handle) @safe pure nothrow
2367 this(int pid) @safe pure nothrow
2375 this(int id, bool owned) @safe pure nothrow
2385 Waits for the process associated with `pid` to terminate, and returns
2388 In general one should always _wait for child processes to terminate
2389 before exiting the parent process unless the process was spawned as detached
2390 (that was spawned with `Config.detached` flag).
2391 Otherwise, they may become "$(HTTP en.wikipedia.org/wiki/Zombie_process,zombies)"
2392 – processes that are defunct, yet still occupy a slot in the OS process table.
2393 You should not and must not wait for detached processes, since you don't own them.
2395 If the process has already terminated, this function returns directly.
2396 The exit code is cached, so that if wait() is called multiple times on
2397 the same $(LREF Pid) it will always return the same value.
2400 If the process is terminated by a signal, this function returns a
2401 negative number whose absolute value is the signal number.
2402 Since POSIX restricts normal exit codes to the range 0-255, a
2403 negative return value will always indicate termination by signal.
2404 Signal codes are defined in the `core.sys.posix.signal` module
2405 (which corresponds to the `signal.h` POSIX header).
2408 $(LREF ProcessException) on failure or on attempt to wait for detached process.
2411 See the $(LREF spawnProcess) documentation.
2414 $(LREF tryWait), for a non-blocking function.
2416 int wait(Pid pid) @safe
2418 assert(pid !is null, "Called wait on a null Pid.");
2419 return pid.performWait(true);
2423 @system unittest // Pid and wait()
2425 version (Windows) TestScript prog = "exit %~1";
2426 else version (Posix) TestScript prog = "exit $1";
2427 assert(wait(spawnProcess([prog.path, "0"])) == 0);
2428 assert(wait(spawnProcess([prog.path, "123"])) == 123);
2429 auto pid = spawnProcess([prog.path, "10"]);
2430 assert(pid.processID > 0);
2431 version (Windows) assert(pid.osHandle != INVALID_HANDLE_VALUE);
2432 else version (Posix) assert(pid.osHandle == pid.processID);
2433 assert(wait(pid) == 10);
2434 assert(wait(pid) == 10); // cached exit code
2435 assert(pid.processID < 0);
2436 version (Windows) assert(pid.osHandle == INVALID_HANDLE_VALUE);
2437 else version (Posix) assert(pid.osHandle < 0);
2440 private import std.typecons : Tuple;
2443 Waits until either the process associated with `pid` terminates or the
2444 elapsed time exceeds the given timeout.
2446 If the process terminates within the given duration it behaves exactly like
2447 `wait`, except that it returns a tuple `(true, exit code)`.
2449 If the process does not terminate within the given duration it will stop
2450 waiting and return `(false, 0).`
2452 The timeout may not exceed `(uint.max - 1).msecs` (~ 7 weeks, 17 hours).
2454 $(BLUE This function is Windows-Only.)
2457 An $(D std.typecons.Tuple!(bool, "terminated", int, "status")).
2460 $(LREF ProcessException) on failure or on attempt to wait for detached process.
2463 See the $(LREF spawnProcess) documentation.
2466 $(LREF wait), for a blocking function without timeout.
2467 $(LREF tryWait), for a non-blocking function without timeout.
2470 Tuple!(bool, "terminated", int, "status") waitTimeout(Pid pid, Duration timeout) @safe;
2472 else version (Windows)
2473 Tuple!(bool, "terminated", int, "status") waitTimeout(Pid pid, Duration timeout) @safe
2475 assert(pid !is null, "Called wait on a null Pid.");
2476 auto code = pid.performWait(timeout);
2477 return typeof(return)(pid._processID == Pid.terminated, code);
2481 @system unittest // Pid and waitTimeout()
2483 import std.exception : collectException;
2484 import std.typecons : tuple;
2486 TestScript prog = ":Loop\ngoto Loop;";
2487 auto pid = spawnProcess(prog.path);
2489 // Doesn't block longer than one second
2490 assert(waitTimeout(pid, 1.seconds) == tuple(false, 0));
2493 assert(waitTimeout(pid, 1.seconds) == tuple(true, 1)); // exit 1 because the process is killed
2495 // Rejects timeouts exceeding the Windows API capabilities
2496 const dur = DWORD.max.msecs;
2497 const ex = collectException!ProcessException(waitTimeout(pid, dur));
2499 assert(ex.msg == "Timeout exceeds maximum wait time!");
2503 A non-blocking version of $(LREF wait).
2505 If the process associated with `pid` has already terminated,
2506 `tryWait` has the exact same effect as `wait`.
2507 In this case, it returns a tuple where the `terminated` field
2508 is set to `true` and the `status` field has the same
2509 interpretation as the return value of `wait`.
2511 If the process has $(I not) yet terminated, this function differs
2512 from `wait` in that does not wait for this to happen, but instead
2513 returns immediately. The `terminated` field of the returned
2514 tuple will then be set to `false`, while the `status` field
2515 will always be 0 (zero). `wait` or `tryWait` should then be
2516 called again on the same `Pid` at some later time; not only to
2517 get the exit code, but also to avoid the process becoming a "zombie"
2518 when it finally terminates. (See $(LREF wait) for details).
2521 An $(D std.typecons.Tuple!(bool, "terminated", int, "status")).
2524 $(LREF ProcessException) on failure or on attempt to wait for detached process.
2528 auto pid = spawnProcess("dmd myapp.d");
2529 scope(exit) wait(pid);
2531 auto dmd = tryWait(pid);
2534 if (dmd.status == 0) writeln("Compilation succeeded!");
2535 else writeln("Compilation failed");
2537 else writeln("Still compiling...");
2540 Note that in this example, the first `wait` call will have no
2541 effect if the process has already terminated by the time `tryWait`
2542 is called. In the opposite case, however, the `scope` statement
2543 ensures that we always wait for the process if it hasn't terminated
2544 by the time we reach the end of the scope.
2546 auto tryWait(Pid pid) @safe
2548 import std.typecons : Tuple;
2549 assert(pid !is null, "Called tryWait on a null Pid.");
2550 auto code = pid.performWait(false);
2551 return Tuple!(bool, "terminated", int, "status")(pid._processID == Pid.terminated, code);
2553 // unittest: This function is tested together with kill() below.
2557 Attempts to terminate the process associated with `pid`.
2559 The effect of this function, as well as the meaning of `codeOrSignal`,
2560 is highly platform dependent. Details are given below. Common to all
2561 platforms is that this function only $(I initiates) termination of the process,
2562 and returns immediately. It does not wait for the process to end,
2563 nor does it guarantee that the process does in fact get terminated.
2565 Always call $(LREF wait) to wait for a process to complete, even if `kill`
2566 has been called on it.
2570 $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms686714%28v=vs.100%29.aspx,
2571 forcefully and abruptly terminated). If `codeOrSignal` is specified, it
2572 must be a nonnegative number which will be used as the exit code of the process.
2573 If not, the process wil exit with code 1. Do not use $(D codeOrSignal = 259),
2574 as this is a special value (aka. $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms683189.aspx,STILL_ACTIVE))
2575 used by Windows to signal that a process has in fact $(I not) terminated yet.
2577 auto pid = spawnProcess("some_app");
2579 assert(wait(pid) == 10);
2583 A $(LINK2 http://en.wikipedia.org/wiki/Unix_signal,signal) will be sent to
2584 the process, whose value is given by `codeOrSignal`. Depending on the
2585 signal sent, this may or may not terminate the process. Symbolic constants
2586 for various $(LINK2 http://en.wikipedia.org/wiki/Unix_signal#POSIX_signals,
2587 POSIX signals) are defined in `core.sys.posix.signal`, which corresponds to the
2588 $(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html,
2589 `signal.h` POSIX header). If `codeOrSignal` is omitted, the
2590 `SIGTERM` signal will be sent. (This matches the behaviour of the
2591 $(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/utilities/kill.html,
2592 `_kill`) shell command.)
2594 import core.sys.posix.signal : SIGKILL;
2595 auto pid = spawnProcess("some_app");
2597 assert(wait(pid) == -SIGKILL); // Negative return value on POSIX!
2601 $(LREF ProcessException) on error (e.g. if codeOrSignal is invalid).
2602 or on attempt to kill detached process.
2603 Note that failure to terminate the process is considered a "normal"
2604 outcome, not an error.$(BR)
2608 version (Windows) kill(pid, 1);
2609 else version (Posix)
2611 import core.sys.posix.signal : SIGTERM;
2617 void kill(Pid pid, int codeOrSignal)
2619 import std.exception : enforce;
2620 enforce!ProcessException(pid.owned, "Can't kill detached process");
2623 if (codeOrSignal < 0) throw new ProcessException("Invalid exit code");
2624 // On Windows, TerminateProcess() appears to terminate the
2625 // *current* process if it is passed an invalid handle...
2626 if (pid.osHandle == INVALID_HANDLE_VALUE)
2627 throw new ProcessException("Invalid process handle");
2628 if (!TerminateProcess(pid.osHandle, codeOrSignal))
2629 throw ProcessException.newFromLastError();
2631 else version (Posix)
2633 import core.sys.posix.signal : kill;
2634 if (kill(pid.osHandle, codeOrSignal) == -1)
2635 throw ProcessException.newFromErrno();
2639 @system unittest // tryWait() and kill()
2642 import std.exception : assertThrown;
2643 // The test script goes into an infinite loop.
2646 TestScript prog = ":loop
2649 else version (Posix)
2651 import core.sys.posix.signal : SIGTERM, SIGKILL;
2652 TestScript prog = "while true; do sleep 1; done";
2654 auto pid = spawnProcess(prog.path);
2655 // Android appears to automatically kill sleeping processes very quickly,
2656 // so shorten the wait before killing here.
2658 Thread.sleep(dur!"msecs"(5));
2660 Thread.sleep(dur!"msecs"(500));
2662 version (Windows) assert(wait(pid) == 1);
2663 else version (Posix) assert(wait(pid) == -SIGTERM);
2665 pid = spawnProcess(prog.path);
2666 Thread.sleep(dur!"msecs"(500));
2667 auto s = tryWait(pid);
2668 assert(!s.terminated && s.status == 0);
2669 assertThrown!ProcessException(kill(pid, -123)); // Negative code not allowed.
2670 version (Windows) kill(pid, 123);
2671 else version (Posix) kill(pid, SIGKILL);
2672 do { s = tryWait(pid); } while (!s.terminated);
2673 version (Windows) assert(s.status == 123);
2674 else version (Posix) assert(s.status == -SIGKILL);
2675 assertThrown!ProcessException(kill(pid));
2678 @system unittest // wait() and kill() detached process
2681 import std.exception : assertThrown;
2682 TestScript prog = "exit 0";
2683 auto pid = spawnProcess([prog.path], null, Config.detached);
2685 This sleep is needed because we can't wait() for detached process to end
2686 and therefore TestScript destructor may run at the same time as /bin/sh tries to start the script.
2687 This leads to the annoying message like "/bin/sh: 0: Can't open /tmp/std.process temporary file" to appear when running tests.
2688 It does not happen in unittests with non-detached processes because we always wait() for them to finish.
2690 Thread.sleep(500.msecs);
2692 version (Windows) assert(pid.osHandle == INVALID_HANDLE_VALUE);
2693 assertThrown!ProcessException(wait(pid));
2694 assertThrown!ProcessException(kill(pid));
2699 Creates a unidirectional _pipe.
2701 Data is written to one end of the _pipe and read from the other.
2704 p.writeEnd.writeln("Hello World");
2706 assert(p.readEnd.readln().chomp() == "Hello World");
2708 Pipes can, for example, be used for interprocess communication
2709 by spawning a new process and passing one end of the _pipe to
2710 the child, while the parent uses the other end.
2711 (See also $(LREF pipeProcess) and $(LREF pipeShell) for an easier
2714 // Use cURL to download the dlang.org front page, pipe its
2715 // output to grep to extract a list of links to ZIP files,
2716 // and write the list to the file "D downloads.txt":
2718 auto outFile = File("D downloads.txt", "w");
2719 auto cpid = spawnProcess(["curl", "http://dlang.org/download.html"],
2720 std.stdio.stdin, p.writeEnd);
2721 scope(exit) wait(cpid);
2722 auto gpid = spawnProcess(["grep", "-o", `http://\S*\.zip`],
2723 p.readEnd, outFile);
2724 scope(exit) wait(gpid);
2728 A $(LREF Pipe) object that corresponds to the created _pipe.
2731 $(REF StdioException, std,stdio) on failure.
2734 Pipe pipe() @trusted //TODO: @safe
2736 import core.sys.posix.stdio : fdopen;
2738 if (core.sys.posix.unistd.pipe(fds) != 0)
2739 throw new StdioException("Unable to create pipe");
2741 auto readFP = fdopen(fds[0], "r");
2743 throw new StdioException("Cannot open read end of pipe");
2744 p._read = File(readFP, null);
2745 auto writeFP = fdopen(fds[1], "w");
2746 if (writeFP == null)
2747 throw new StdioException("Cannot open write end of pipe");
2748 p._write = File(writeFP, null);
2751 else version (Windows)
2752 Pipe pipe() @trusted //TODO: @safe
2754 // use CreatePipe to create an anonymous pipe
2757 if (!CreatePipe(&readHandle, &writeHandle, null, 0))
2759 throw new StdioException(
2760 "Error creating pipe (" ~ sysErrorString(GetLastError()) ~ ')',
2766 CloseHandle(readHandle);
2767 CloseHandle(writeHandle);
2773 p._read .windowsHandleOpen(readHandle , "r");
2774 p._write.windowsHandleOpen(writeHandle, "a");
2779 throw new StdioException("Error attaching pipe (" ~ e.msg ~ ")",
2785 /// An interface to a pipe created by the $(LREF pipe) function.
2788 /// The read end of the pipe.
2789 @property File readEnd() @safe nothrow { return _read; }
2792 /// The write end of the pipe.
2793 @property File writeEnd() @safe nothrow { return _write; }
2797 Closes both ends of the pipe.
2799 Normally it is not necessary to do this manually, as $(REF File, std,stdio)
2800 objects are automatically closed when there are no more references
2803 Note that if either end of the pipe has been passed to a child process,
2804 it will only be closed in the parent process. (What happens in the
2805 child process is platform dependent.)
2808 $(REF ErrnoException, std,exception) if an error occurs.
2824 p.writeEnd.writeln("Hello World");
2826 assert(p.readEnd.readln().chomp() == "Hello World");
2828 assert(!p.readEnd.isOpen);
2829 assert(!p.writeEnd.isOpen);
2834 Starts a new process, creating pipes to redirect its standard
2835 input, output and/or error streams.
2837 `pipeProcess` and `pipeShell` are convenient wrappers around
2838 $(LREF spawnProcess) and $(LREF spawnShell), respectively, and
2839 automate the task of redirecting one or more of the child process'
2840 standard streams through pipes. Like the functions they wrap,
2841 these functions return immediately, leaving the child process to
2842 execute in parallel with the invoking process. It is recommended
2843 to always call $(LREF wait) on the returned $(LREF ProcessPipes.pid),
2844 as detailed in the documentation for `wait`.
2846 The `args`/`program`/`command`, `env` and `config`
2847 parameters are forwarded straight to the underlying spawn functions,
2848 and we refer to their documentation for details.
2851 args = An array which contains the program name as the zeroth element
2852 and any command-line arguments in the following elements.
2853 (See $(LREF spawnProcess) for details.)
2854 program = The program name, $(I without) command-line arguments.
2855 (See $(LREF spawnProcess) for details.)
2856 command = A shell command which is passed verbatim to the command
2857 interpreter. (See $(LREF spawnShell) for details.)
2858 redirect = Flags that determine which streams are redirected, and
2859 how. See $(LREF Redirect) for an overview of available
2861 env = Additional environment variables for the child process.
2862 (See $(LREF spawnProcess) for details.)
2863 config = Flags that control process creation. See $(LREF Config)
2864 for an overview of available flags, and note that the
2865 `retainStd...` flags have no effect in this function.
2866 workDir = The working directory for the new process.
2867 By default the child process inherits the parent's working
2869 shellPath = The path to the shell to use to run the specified program.
2870 By default this is $(LREF nativeShell).
2873 A $(LREF ProcessPipes) object which contains $(REF File, std,stdio)
2874 handles that communicate with the redirected streams of the child
2875 process, along with a $(LREF Pid) object that corresponds to the
2879 $(LREF ProcessException) on failure to start the process.$(BR)
2880 $(REF StdioException, std,stdio) on failure to redirect any of the streams.$(BR)
2884 // my_application writes to stdout and might write to stderr
2885 auto pipes = pipeProcess("my_application", Redirect.stdout | Redirect.stderr);
2886 scope(exit) wait(pipes.pid);
2888 // Store lines of output.
2890 foreach (line; pipes.stdout.byLine) output ~= line.idup;
2892 // Store lines of errors.
2894 foreach (line; pipes.stderr.byLine) errors ~= line.idup;
2897 // sendmail expects to read from stdin
2898 pipes = pipeProcess(["/usr/bin/sendmail", "-t"], Redirect.stdin);
2899 pipes.stdin.writeln("To: you");
2900 pipes.stdin.writeln("From: me");
2901 pipes.stdin.writeln("Subject: dlang");
2902 pipes.stdin.writeln("");
2903 pipes.stdin.writeln(message);
2905 // a single period tells sendmail we are finished
2906 pipes.stdin.writeln(".");
2908 // but at this point sendmail might not see it, we need to flush
2909 pipes.stdin.flush();
2911 // sendmail happens to exit on ".", but some you have to close the file:
2912 pipes.stdin.close();
2914 // otherwise this wait will wait forever
2919 ProcessPipes pipeProcess(scope const(char[])[] args,
2920 Redirect redirect = Redirect.all,
2921 const string[string] env = null,
2922 Config config = Config.none,
2923 scope const(char)[] workDir = null)
2926 return pipeProcessImpl!spawnProcess(args, redirect, env, config, workDir);
2930 ProcessPipes pipeProcess(scope const(char)[] program,
2931 Redirect redirect = Redirect.all,
2932 const string[string] env = null,
2933 Config config = Config.none,
2934 scope const(char)[] workDir = null)
2937 return pipeProcessImpl!spawnProcess(program, redirect, env, config, workDir);
2941 ProcessPipes pipeShell(scope const(char)[] command,
2942 Redirect redirect = Redirect.all,
2943 const string[string] env = null,
2944 Config config = Config.none,
2945 scope const(char)[] workDir = null,
2946 string shellPath = nativeShell)
2949 return pipeProcessImpl!spawnShell(command,
2957 // Implementation of the pipeProcess() family of functions.
2958 private ProcessPipes pipeProcessImpl(alias spawnFunc, Cmd, ExtraSpawnFuncArgs...)
2960 Redirect redirectFlags,
2961 const string[string] env = null,
2962 Config config = Config.none,
2963 scope const(char)[] workDir = null,
2964 ExtraSpawnFuncArgs extraArgs = ExtraSpawnFuncArgs.init)
2965 @trusted //TODO: @safe
2967 File childStdin, childStdout, childStderr;
2969 pipes._redirectFlags = redirectFlags;
2971 if (redirectFlags & Redirect.stdin)
2974 childStdin = p.readEnd;
2975 pipes._stdin = p.writeEnd;
2979 childStdin = std.stdio.stdin;
2982 if (redirectFlags & Redirect.stdout)
2984 if ((redirectFlags & Redirect.stdoutToStderr) != 0)
2985 throw new StdioException("Cannot create pipe for stdout AND "
2986 ~"redirect it to stderr", 0);
2988 childStdout = p.writeEnd;
2989 pipes._stdout = p.readEnd;
2993 childStdout = std.stdio.stdout;
2996 if (redirectFlags & Redirect.stderr)
2998 if ((redirectFlags & Redirect.stderrToStdout) != 0)
2999 throw new StdioException("Cannot create pipe for stderr AND "
3000 ~"redirect it to stdout", 0);
3002 childStderr = p.writeEnd;
3003 pipes._stderr = p.readEnd;
3007 childStderr = std.stdio.stderr;
3010 if (redirectFlags & Redirect.stdoutToStderr)
3012 if (redirectFlags & Redirect.stderrToStdout)
3014 // We know that neither of the other options have been
3015 // set, so we assign the std.stdio.std* streams directly.
3016 childStdout = std.stdio.stderr;
3017 childStderr = std.stdio.stdout;
3021 childStdout = childStderr;
3024 else if (redirectFlags & Redirect.stderrToStdout)
3026 childStderr = childStdout;
3029 config.flags &= ~(Config.Flags.retainStdin | Config.Flags.retainStdout | Config.Flags.retainStderr);
3030 pipes._pid = spawnFunc(command, childStdin, childStdout, childStderr,
3031 env, config, workDir, extraArgs);
3037 Flags that can be passed to $(LREF pipeProcess) and $(LREF pipeShell)
3038 to specify which of the child process' standard streams are redirected.
3039 Use bitwise OR to combine flags.
3043 /// Redirect the standard input, output or error streams, respectively.
3045 stdout = 2, /// ditto
3046 stderr = 4, /// ditto
3049 Redirect _all three streams. This is equivalent to
3050 $(D Redirect.stdin | Redirect.stdout | Redirect.stderr).
3052 all = stdin | stdout | stderr,
3055 Redirect the standard error stream into the standard output stream.
3056 This can not be combined with `Redirect.stderr`.
3061 Redirect the standard output stream into the standard error stream.
3062 This can not be combined with `Redirect.stdout`.
3064 stdoutToStderr = 16,
3070 version (Windows) TestScript prog =
3071 "call :sub %~1 %~2 0
3079 if -%INPUT%-==-stop- ( exit %~3 )
3081 echo %INPUT% %~2 1>&2";
3082 else version (Posix) TestScript prog =
3083 `for EXITCODE in 0 1 2 3; do
3085 if test "$INPUT" = stop; then break; fi
3087 echo "$INPUT $2" >&2
3090 auto pp = pipeProcess([prog.path, "bar", "baz"]);
3091 pp.stdin.writeln("foo");
3093 assert(pp.stdout.readln().chomp() == "foo bar");
3094 assert(pp.stderr.readln().chomp().stripRight() == "foo baz");
3095 pp.stdin.writeln("1234567890");
3097 assert(pp.stdout.readln().chomp() == "1234567890 bar");
3098 assert(pp.stderr.readln().chomp().stripRight() == "1234567890 baz");
3099 pp.stdin.writeln("stop");
3101 assert(wait(pp.pid) == 2);
3103 pp = pipeProcess([prog.path, "12345", "67890"],
3104 Redirect.stdin | Redirect.stdout | Redirect.stderrToStdout);
3105 pp.stdin.writeln("xyz");
3107 assert(pp.stdout.readln().chomp() == "xyz 12345");
3108 assert(pp.stdout.readln().chomp().stripRight() == "xyz 67890");
3109 pp.stdin.writeln("stop");
3111 assert(wait(pp.pid) == 1);
3113 pp = pipeShell(escapeShellCommand(prog.path, "AAAAA", "BBB"),
3114 Redirect.stdin | Redirect.stdoutToStderr | Redirect.stderr);
3115 pp.stdin.writeln("ab");
3117 assert(pp.stderr.readln().chomp() == "ab AAAAA");
3118 assert(pp.stderr.readln().chomp().stripRight() == "ab BBB");
3119 pp.stdin.writeln("stop");
3121 assert(wait(pp.pid) == 1);
3126 import std.exception : assertThrown;
3127 TestScript prog = "exit 0";
3128 assertThrown!StdioException(pipeProcess(
3130 Redirect.stdout | Redirect.stdoutToStderr));
3131 assertThrown!StdioException(pipeProcess(
3133 Redirect.stderr | Redirect.stderrToStdout));
3134 auto p = pipeProcess(prog.path, Redirect.stdin);
3135 assertThrown!Error(p.stdout);
3136 assertThrown!Error(p.stderr);
3138 p = pipeProcess(prog.path, Redirect.stderr);
3139 assertThrown!Error(p.stdin);
3140 assertThrown!Error(p.stdout);
3145 Object which contains $(REF File, std,stdio) handles that allow communication
3146 with a child process through its standard streams.
3150 /// The $(LREF Pid) of the child process.
3151 @property Pid pid() @safe nothrow
3157 An $(REF File, std,stdio) that allows writing to the child process'
3158 standard input stream.
3161 $(OBJECTREF Error) if the child process' standard input stream hasn't
3164 @property File stdin() @safe nothrow
3166 if ((_redirectFlags & Redirect.stdin) == 0)
3167 throw new Error("Child process' standard input stream hasn't "
3168 ~"been redirected.");
3173 An $(REF File, std,stdio) that allows reading from the child process'
3174 standard output stream.
3177 $(OBJECTREF Error) if the child process' standard output stream hasn't
3180 @property File stdout() @safe nothrow
3182 if ((_redirectFlags & Redirect.stdout) == 0)
3183 throw new Error("Child process' standard output stream hasn't "
3184 ~"been redirected.");
3189 An $(REF File, std,stdio) that allows reading from the child process'
3190 standard error stream.
3193 $(OBJECTREF Error) if the child process' standard error stream hasn't
3196 @property File stderr() @safe nothrow
3198 if ((_redirectFlags & Redirect.stderr) == 0)
3199 throw new Error("Child process' standard error stream hasn't "
3200 ~"been redirected.");
3205 Redirect _redirectFlags;
3207 File _stdin, _stdout, _stderr;
3213 Executes the given program or shell command and returns its exit
3216 `execute` and `executeShell` start a new process using
3217 $(LREF spawnProcess) and $(LREF spawnShell), respectively, and wait
3218 for the process to complete before returning. The functions capture
3219 what the child process prints to both its standard output and
3220 standard error streams, and return this together with its exit code.
3222 auto dmd = execute(["dmd", "myapp.d"]);
3223 if (dmd.status != 0) writeln("Compilation failed:\n", dmd.output);
3225 auto ls = executeShell("ls -l");
3226 if (ls.status != 0) writeln("Failed to retrieve file listing");
3227 else writeln(ls.output);
3230 The `args`/`program`/`command`, `env` and `config`
3231 parameters are forwarded straight to the underlying spawn functions,
3232 and we refer to their documentation for details.
3235 args = An array which contains the program name as the zeroth element
3236 and any command-line arguments in the following elements.
3237 (See $(LREF spawnProcess) for details.)
3238 program = The program name, $(I without) command-line arguments.
3239 (See $(LREF spawnProcess) for details.)
3240 command = A shell command which is passed verbatim to the command
3241 interpreter. (See $(LREF spawnShell) for details.)
3242 env = Additional environment variables for the child process.
3243 (See $(LREF spawnProcess) for details.)
3244 config = Flags that control process creation. See $(LREF Config)
3245 for an overview of available flags, and note that the
3246 `retainStd...` flags have no effect in this function.
3247 maxOutput = The maximum number of bytes of output that should be
3249 workDir = The working directory for the new process.
3250 By default the child process inherits the parent's working
3252 shellPath = The path to the shell to use to run the specified program.
3253 By default this is $(LREF nativeShell).
3257 An $(D std.typecons.Tuple!(int, "status", string, "output")).
3260 If the process is terminated by a signal, the `status` field of
3261 the return value will contain a negative number whose absolute
3262 value is the signal number. (See $(LREF wait) for details.)
3265 $(LREF ProcessException) on failure to start the process.$(BR)
3266 $(REF StdioException, std,stdio) on failure to capture output.
3268 auto execute(scope const(char[])[] args,
3269 const string[string] env = null,
3270 Config config = Config.none,
3271 size_t maxOutput = size_t.max,
3272 scope const(char)[] workDir = null)
3275 return executeImpl!pipeProcess(args, env, config, maxOutput, workDir);
3279 auto execute(scope const(char)[] program,
3280 const string[string] env = null,
3281 Config config = Config.none,
3282 size_t maxOutput = size_t.max,
3283 scope const(char)[] workDir = null)
3286 return executeImpl!pipeProcess(program, env, config, maxOutput, workDir);
3290 auto executeShell(scope const(char)[] command,
3291 const string[string] env = null,
3292 Config config = Config.none,
3293 size_t maxOutput = size_t.max,
3294 scope const(char)[] workDir = null,
3295 string shellPath = nativeShell)
3298 return executeImpl!pipeShell(command,
3306 // Does the actual work for execute() and executeShell().
3307 private auto executeImpl(alias pipeFunc, Cmd, ExtraPipeFuncArgs...)(
3309 const string[string] env = null,
3310 Config config = Config.none,
3311 size_t maxOutput = size_t.max,
3312 scope const(char)[] workDir = null,
3313 ExtraPipeFuncArgs extraArgs = ExtraPipeFuncArgs.init)
3314 @trusted //TODO: @safe
3316 import std.algorithm.comparison : min;
3317 import std.array : appender;
3318 import std.typecons : Tuple;
3320 auto redirect = (config.flags & Config.Flags.stderrPassThrough)
3322 : Redirect.stdout | Redirect.stderrToStdout;
3324 auto p = pipeFunc(commandLine, redirect,
3325 env, config, workDir, extraArgs);
3327 auto a = appender!string;
3328 enum size_t defaultChunkSize = 4096;
3329 immutable chunkSize = min(maxOutput, defaultChunkSize);
3331 // Store up to maxOutput bytes in a.
3332 foreach (ubyte[] chunk; p.stdout.byChunk(chunkSize))
3334 immutable size_t remain = maxOutput - a.data.length;
3336 if (chunk.length < remain) a.put(chunk);
3339 a.put(chunk[0 .. remain]);
3343 // Exhaust the stream, if necessary.
3344 foreach (ubyte[] chunk; p.stdout.byChunk(defaultChunkSize)) { }
3346 return Tuple!(int, "status", string, "output")(wait(p.pid), a.data);
3352 // To avoid printing the newline characters, we use the echo|set trick on
3353 // Windows, and printf on POSIX (neither echo -n nor echo \c are portable).
3354 version (Windows) TestScript prog =
3356 echo|set /p=%~2 1>&2
3358 else version (Android) TestScript prog =
3362 else version (Posix) TestScript prog =
3366 auto r = execute([prog.path, "foo", "bar"]);
3367 assert(r.status == 123);
3368 assert(r.output.stripRight() == "foobar");
3369 auto s = execute([prog.path, "Hello", "World"]);
3370 assert(s.status == 123);
3371 assert(s.output.stripRight() == "HelloWorld");
3377 auto r1 = executeShell("echo foo");
3378 assert(r1.status == 0);
3379 assert(r1.output.chomp() == "foo");
3380 auto r2 = executeShell("echo bar 1>&2");
3381 assert(r2.status == 0);
3382 assert(r2.output.chomp().stripRight() == "bar");
3383 auto r3 = executeShell("exit 123");
3384 assert(r3.status == 123);
3385 assert(r3.output.empty);
3390 // Temporarily disable output to stderr so as to not spam the build log.
3391 import std.stdio : stderr;
3392 import std.typecons : Tuple;
3393 import std.file : readText, exists, remove;
3394 import std.traits : ReturnType;
3396 ReturnType!executeShell r;
3397 auto tmpname = uniqueTempPath;
3398 scope(exit) if (exists(tmpname)) remove(tmpname);
3400 // Open a new scope to minimize code ran with stderr redirected.
3402 stderr.open(tmpname, "w");
3403 scope(exit) stderr = t;
3404 r = executeShell("echo D rox>&2", null, Config.stderrPassThrough);
3406 assert(r.status == 0);
3407 assert(r.output.empty);
3408 auto witness = readText(tmpname);
3409 import std.ascii : newline;
3410 assert(witness == "D rox" ~ newline, "'" ~ witness ~ "'");
3415 import std.typecons : Tuple;
3416 void foo() //Just test the compilation
3418 auto ret1 = execute(["dummy", "arg"]);
3419 auto ret2 = executeShell("dummy arg");
3420 static assert(is(typeof(ret1) == typeof(ret2)));
3422 Tuple!(int, string) ret3 = execute(["dummy", "arg"]);
3426 /// An exception that signals a problem with starting or waiting for a process.
3427 class ProcessException : Exception
3429 import std.exception : basicExceptionCtors;
3430 mixin basicExceptionCtors;
3432 // Creates a new ProcessException based on errno.
3433 static ProcessException newFromErrno(string customMsg = null,
3434 string file = __FILE__,
3435 size_t line = __LINE__)
3437 import core.stdc.errno : errno;
3438 return newFromErrno(errno, customMsg, file, line);
3441 // ditto, but error number is provided by caller
3442 static ProcessException newFromErrno(int error,
3443 string customMsg = null,
3444 string file = __FILE__,
3445 size_t line = __LINE__)
3447 import std.exception : errnoString;
3448 auto errnoMsg = errnoString(error);
3449 auto msg = customMsg.empty ? errnoMsg
3450 : customMsg ~ " (" ~ errnoMsg ~ ')';
3451 return new ProcessException(msg, file, line);
3454 // Creates a new ProcessException based on GetLastError() (Windows only).
3456 static ProcessException newFromLastError(string customMsg = null,
3457 string file = __FILE__,
3458 size_t line = __LINE__)
3460 auto lastMsg = sysErrorString(GetLastError());
3461 auto msg = customMsg.empty ? lastMsg
3462 : customMsg ~ " (" ~ lastMsg ~ ')';
3463 return new ProcessException(msg, file, line);
3469 Determines the path to the current user's preferred command interpreter.
3471 On Windows, this function returns the contents of the COMSPEC environment
3472 variable, if it exists. Otherwise, it returns the result of $(LREF nativeShell).
3474 On POSIX, `userShell` returns the contents of the SHELL environment
3475 variable, if it exists and is non-empty. Otherwise, it returns the result of
3476 $(LREF nativeShell).
3478 @property string userShell() @safe
3480 version (Windows) return environment.get("COMSPEC", nativeShell);
3481 else version (Posix) return environment.get("SHELL", nativeShell);
3485 The platform-specific native shell path.
3487 This function returns `"cmd.exe"` on Windows, `"/bin/sh"` on POSIX, and
3488 `"/system/bin/sh"` on Android.
3490 @property string nativeShell() @safe @nogc pure nothrow
3492 version (Windows) return "cmd.exe";
3493 else version (Android) return "/system/bin/sh";
3494 else version (Posix) return "/bin/sh";
3497 // A command-line switch that indicates to the shell that it should
3498 // interpret the following argument as a command to be executed.
3499 version (Posix) private immutable string shellSwitch = "-c";
3500 version (Windows) private immutable string shellSwitch = "/C";
3502 // Unittest support code: TestScript takes a string that contains a
3503 // shell script for the current platform, and writes it to a temporary
3504 // file. On Windows the file name gets a .cmd extension, while on
3505 // POSIX its executable permission bit is set. The file is
3506 // automatically deleted when the object goes out of scope.
3507 version (StdUnittest)
3508 private struct TestScript
3510 this(string code) @system
3512 // @system due to chmod
3513 import std.ascii : newline;
3514 import std.file : write;
3518 auto firstLine = "@echo off";
3520 else version (Posix)
3523 auto firstLine = "#!" ~ nativeShell;
3525 path = uniqueTempPath()~ext;
3526 write(path, firstLine ~ newline ~ code ~ newline);
3529 import core.sys.posix.sys.stat : chmod;
3530 import std.conv : octal;
3531 chmod(path.tempCString(), octal!777);
3537 import std.file : remove, exists;
3538 if (!path.empty && exists(path))
3540 try { remove(path); }
3543 debug std.stdio.stderr.writeln(e.msg);
3552 // =============================================================================
3553 // Functions for shell command quoting/escaping.
3554 // =============================================================================
3558 Command line arguments exist in three forms:
3559 1) string or char* array, as received by main.
3560 Also used internally on POSIX systems.
3561 2) Command line string, as used in Windows'
3562 CreateProcess and CommandLineToArgvW functions.
3563 A specific quoting and escaping algorithm is used
3564 to distinguish individual arguments.
3565 3) Shell command string, as written at a shell prompt
3566 or passed to cmd /C - this one may contain shell
3567 control characters, e.g. > or | for redirection /
3568 piping - thus, yet another layer of escaping is
3569 used to distinguish them from program arguments.
3571 Except for escapeWindowsArgument, the intermediary
3572 format (2) is hidden away from the user in this module.
3576 Escapes an argv-style argument array to be used with $(LREF spawnShell),
3577 $(LREF pipeShell) or $(LREF executeShell).
3579 string url = "http://dlang.org/";
3580 executeShell(escapeShellCommand("wget", url, "-O", "dlang-index.html"));
3583 Concatenate multiple `escapeShellCommand` and
3584 $(LREF escapeShellFileName) results to use shell redirection or
3588 escapeShellCommand("curl", "http://dlang.org/download.html") ~
3590 escapeShellCommand("grep", "-o", `http://\S*\.zip`) ~
3592 escapeShellFileName("D download links.txt"));
3596 $(OBJECTREF Exception) if any part of the command line contains unescapable
3597 characters (NUL on all platforms, as well as CR and LF on Windows).
3599 string escapeShellCommand(scope const(char[])[] args...) @safe pure
3605 // Do not ^-escape the first argument (the program path),
3606 // as the shell parses it differently from parameters.
3607 // ^-escaping a program path that contains spaces will fail.
3608 string result = escapeShellFileName(args[0]);
3609 if (args.length > 1)
3611 result ~= " " ~ escapeShellCommandString(
3612 escapeShellArguments(args[1..$]));
3618 return escapeShellCommandString(escapeShellArguments(args));
3624 // This is a simple unit test without any special requirements,
3625 // in addition to the unittest_burnin one below which requires
3626 // special preparation.
3628 struct TestVector { string[] args; string windows, posix; }
3629 TestVector[] tests =
3633 windows : `"foo bar"`,
3637 args : ["foo bar", "hello"],
3638 windows : `"foo bar" hello`,
3639 posix : `'foo bar' 'hello'`
3642 args : ["foo bar", "hello world"],
3643 windows : `"foo bar" ^"hello world^"`,
3644 posix : `'foo bar' 'hello world'`
3647 args : ["foo bar", "hello", "world"],
3648 windows : `"foo bar" hello world`,
3649 posix : `'foo bar' 'hello' 'world'`
3652 args : ["foo bar", `'"^\`],
3653 windows : `"foo bar" ^"'\^"^^\\^"`,
3654 posix : `'foo bar' ''\''"^\'`
3658 foreach (test; tests)
3660 assert(escapeShellCommand(test.args) == test.windows);
3662 assert(escapeShellCommand(test.args) == test.posix );
3665 private string escapeShellCommandString(return scope string command) @safe pure
3668 return escapeWindowsShellCommand(command);
3673 private string escapeWindowsShellCommand(scope const(char)[] command) @safe pure
3675 import std.array : appender;
3676 auto result = appender!string();
3677 result.reserve(command.length);
3679 foreach (c; command)
3683 throw new Exception("Cannot put NUL in command line");
3686 throw new Exception("CR/LF are not escapable");
3687 case '\x01': .. case '\x09':
3688 case '\x0B': .. case '\x0C':
3689 case '\x0E': .. case '\x1F':
3704 private string escapeShellArguments(scope const(char[])[] args...)
3705 @trusted pure nothrow
3707 import std.exception : assumeUnique;
3711 char[] allocator(size_t size)
3713 if (buf.length == 0)
3714 return buf = new char[size];
3717 auto p = buf.length;
3718 buf.length = buf.length + 1 + size;
3720 return buf[p .. p+size];
3725 escapeShellArgument!allocator(arg);
3726 return assumeUnique(buf);
3729 private auto escapeShellArgument(alias allocator)(scope const(char)[] arg) @safe nothrow
3731 // The unittest for this function requires special
3732 // preparation - see below.
3735 return escapeWindowsArgumentImpl!allocator(arg);
3737 return escapePosixArgumentImpl!allocator(arg);
3741 Quotes a command-line argument in a manner conforming to the behavior of
3742 $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx,
3743 CommandLineToArgvW).
3745 string escapeWindowsArgument(scope const(char)[] arg) @trusted pure nothrow
3747 // Rationale for leaving this function as public:
3748 // this algorithm of escaping paths is also used in other software,
3749 // e.g. DMD's response files.
3750 import std.exception : assumeUnique;
3751 auto buf = escapeWindowsArgumentImpl!charAllocator(arg);
3752 return assumeUnique(buf);
3756 private char[] charAllocator(size_t size) @safe pure nothrow
3758 return new char[size];
3762 private char[] escapeWindowsArgumentImpl(alias allocator)(scope const(char)[] arg)
3764 if (is(typeof(allocator(size_t.init)[0] = char.init)))
3767 // * http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx
3768 // * http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx
3770 // Check if the string needs to be escaped,
3771 // and calculate the total string size.
3773 // Trailing backslashes must be escaped
3774 bool escaping = true;
3775 bool needEscape = false;
3776 // Result size = input size + 2 for surrounding quotes + 1 for the
3777 // backslash for each escaped character.
3778 size_t size = 1 + arg.length + 1;
3780 foreach_reverse (char c; arg)
3796 if (c == ' ' || c == '\t')
3802 import std.ascii : isDigit;
3803 // Empty arguments need to be specified as ""
3807 // Arguments ending with digits need to be escaped,
3808 // to disambiguate with 1>file redirection syntax
3809 if (isDigit(arg[$-1]))
3813 return allocator(arg.length)[] = arg;
3815 // Construct result string.
3817 auto buf = allocator(size);
3821 foreach_reverse (char c; arg)
3839 version (Windows) version (StdUnittest)
3842 import core.stdc.stddef;
3843 import core.stdc.wchar_ : wcslen;
3844 import core.sys.windows.shellapi : CommandLineToArgvW;
3845 import core.sys.windows.winbase;
3846 import core.sys.windows.winnt;
3849 string[] parseCommandLine(string line)
3851 import std.algorithm.iteration : map;
3852 import std.array : array;
3853 import std.conv : to;
3854 auto lpCommandLine = (to!(WCHAR[])(line) ~ '\0').ptr;
3856 auto args = CommandLineToArgvW(lpCommandLine, &numArgs);
3857 scope(exit) LocalFree(args);
3858 return args[0 .. numArgs]
3859 .map!(arg => to!string(arg[0 .. wcslen(arg)]))
3865 import std.conv : text;
3866 string[] testStrings = [
3872 `C:\Program Files\`,
3875 enum CHARS = `_x\" *&^` ~ "\t"; // _ is placeholder for nothing
3880 testStrings ~= [c1, c2, c3, c4].replace("_", "");
3882 foreach (s; testStrings)
3884 auto q = escapeWindowsArgument(s);
3885 auto args = parseCommandLine("Dummy.exe " ~ q);
3886 assert(args.length == 2, s ~ " => " ~ q ~ " #" ~ text(args.length-1));
3887 assert(args[1] == s, s ~ " => " ~ q ~ " => " ~ args[1]);
3892 private string escapePosixArgument(scope const(char)[] arg) @trusted pure nothrow
3894 import std.exception : assumeUnique;
3895 auto buf = escapePosixArgumentImpl!charAllocator(arg);
3896 return assumeUnique(buf);
3899 private char[] escapePosixArgumentImpl(alias allocator)(scope const(char)[] arg)
3901 if (is(typeof(allocator(size_t.init)[0] = char.init)))
3903 // '\'' means: close quoted part of argument, append an escaped
3904 // single quote, and reopen quotes
3906 // Below code is equivalent to:
3907 // return `'` ~ std.array.replace(arg, `'`, `'\''`) ~ `'`;
3909 size_t size = 1 + arg.length + 1;
3910 foreach (char c; arg)
3914 auto buf = allocator(size);
3917 foreach (char c; arg)
3920 buf[p .. p+4] = `'\''`;
3932 Escapes a filename to be used for shell redirection with $(LREF spawnShell),
3933 $(LREF pipeShell) or $(LREF executeShell).
3935 string escapeShellFileName(scope const(char)[] fileName) @trusted pure nothrow
3937 // The unittest for this function requires special
3938 // preparation - see below.
3942 // If a file starts with &, it can cause cmd.exe to misinterpret
3943 // the file name as the stream redirection syntax:
3944 // command > "&foo.txt"
3945 // gets interpreted as
3946 // command >&foo.txt
3947 // Prepend .\ to disambiguate.
3949 if (fileName.length && fileName[0] == '&')
3950 return cast(string)(`".\` ~ fileName ~ '"');
3952 return cast(string)('"' ~ fileName ~ '"');
3955 return escapePosixArgument(fileName);
3958 // Loop generating strings with random characters
3959 //version = unittest_burnin;
3961 version (unittest_burnin)
3964 // There are no readily-available commands on all platforms suitable
3965 // for properly testing command escaping. The behavior of CMD's "echo"
3966 // built-in differs from the POSIX program, and Windows ports of POSIX
3967 // environments (Cygwin, msys, gnuwin32) may interfere with their own
3970 // To run this unit test, create std_process_unittest_helper.d with the
3971 // following content and compile it:
3972 // import std.stdio, std.array; void main(string[] args) { write(args.join("\0")); }
3973 // Then, test this module with:
3974 // rdmd --main -unittest -version=unittest_burnin process.d
3976 auto helper = absolutePath("std_process_unittest_helper");
3977 assert(executeShell(helper ~ " hello").output.split("\0")[1..$] == ["hello"], "Helper malfunction");
3979 void test(string[] s, string fn)
3984 e = escapeShellCommand(helper ~ s);
3986 scope(failure) writefln("executeShell() failed.\nExpected:\t%s\nEncoded:\t%s", s, [e]);
3987 auto result = executeShell(e);
3988 assert(result.status == 0, "std_process_unittest_helper failed");
3989 g = result.output.split("\0")[1..$];
3991 assert(s == g, format("executeShell() test failed.\nExpected:\t%s\nGot:\t\t%s\nEncoded:\t%s", s, g, [e]));
3993 e = escapeShellCommand(helper ~ s) ~ ">" ~ escapeShellFileName(fn);
3995 scope(failure) writefln(
3996 "executeShell() with redirect failed.\nExpected:\t%s\nFilename:\t%s\nEncoded:\t%s", s, [fn], [e]);
3997 auto result = executeShell(e);
3998 assert(result.status == 0, "std_process_unittest_helper failed");
3999 assert(!result.output.length, "No output expected, got:\n" ~ result.output);
4000 g = readText(fn).split("\0")[1..$];
4004 format("executeShell() with redirect test failed.\nExpected:\t%s\nGot:\t\t%s\nEncoded:\t%s", s, g, [e]));
4010 foreach (n; 0 .. uniform(1, 4))
4013 foreach (l; 0 .. uniform(0, 10))
4020 // As long as DMD's system() uses CreateProcessA,
4021 // we can't reliably pass Unicode
4022 c = uniform(0, 128);
4025 c = uniform!ubyte();
4028 continue; // argv-strings are zero-terminated
4030 if (c == '\r' || c == '\n')
4031 continue; // newlines are unescapable on Windows
4039 // generate filename
4041 foreach (l; 0 .. uniform(1, 10))
4047 c = uniform(0, 128); // as above
4049 c = uniform!ubyte();
4051 if (c == 0 || c == '/')
4052 continue; // NUL and / are the only characters
4053 // forbidden in POSIX filenames
4055 if (c < '\x20' || c == '<' || c == '>' || c == ':' ||
4056 c == '"' || c == '\\' || c == '|' || c == '?' || c == '*')
4057 continue; // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
4063 fn = fn[0..$/2] ~ "_testfile_" ~ fn[$/2..$];
4069 // =============================================================================
4070 // Everything below this line was part of the old std.process, and most of
4071 // it will be deprecated and removed.
4072 // =============================================================================
4076 Copyright: Copyright The D Language Foundation 2007 - 2009.
4077 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
4078 Authors: $(HTTP digitalmars.com, Walter Bright),
4079 $(HTTP erdani.org, Andrei Alexandrescu),
4080 $(HTTP thecybershadow.net, Vladimir Panteleev)
4081 Source: $(PHOBOSSRC std/_process.d)
4084 Copyright The D Language Foundation 2007 - 2009.
4085 Distributed under the Boost Software License, Version 1.0.
4086 (See accompanying file LICENSE_1_0.txt or copy at
4087 http://www.boost.org/LICENSE_1_0.txt)
4091 import core.stdc.errno;
4092 import core.stdc.stdlib;
4093 import core.stdc.string;
4098 import std.file, std.format, std.random;
4102 import core.sys.posix.stdlib;
4105 private void toAStringz(in string[] a, const(char)**az)
4107 import std.string : toStringz;
4108 foreach (string s; a)
4110 *az++ = toStringz(s);
4116 /* ========================================================== */
4120 // int spawnvp(int mode, string pathname, string[] argv)
4122 // char** argv_ = cast(char**) core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length));
4123 // scope(exit) core.stdc.stdlib.free(argv_);
4125 // toAStringz(argv, argv_);
4127 // return spawnvp(mode, pathname.tempCString(), argv_);
4131 // Incorporating idea (for spawnvp() on Posix) from Dave Fladebo
4133 enum { _P_WAIT, _P_NOWAIT, _P_OVERLAY }
4134 version (Windows) extern(C) int spawnvp(int, scope const(char) *, scope const(char*)*);
4135 alias P_WAIT = _P_WAIT;
4136 alias P_NOWAIT = _P_NOWAIT;
4138 /* ========================================================== */
4143 Replaces the current process by executing a command, `pathname`, with
4144 the arguments in `argv`.
4146 $(BLUE This function is Posix-Only.)
4148 Typically, the first element of `argv` is
4149 the command being executed, i.e. $(D argv[0] == pathname). The 'p'
4150 versions of `exec` search the PATH environment variable for $(D
4151 pathname). The 'e' versions additionally take the new process'
4152 environment variables as an array of strings of the form key=value.
4154 Does not return on success (the current process will have been
4155 replaced). Returns -1 on failure with no indication of the
4159 These functions are only supported on POSIX platforms, as the Windows
4160 operating systems do not provide the ability to overwrite the current
4161 process image with another. In single-threaded programs it is possible
4162 to approximate the effect of `execv*` by using $(LREF spawnProcess)
4163 and terminating the current process once the child process has returned.
4166 auto commandLine = [ "program", "arg1", "arg2" ];
4169 execv(commandLine[0], commandLine);
4170 throw new Exception("Failed to execute program");
4172 else version (Windows)
4174 import core.stdc.stdlib : _Exit;
4175 _Exit(wait(spawnProcess(commandLine)));
4178 This is, however, NOT equivalent to POSIX' `execv*`. For one thing, the
4179 executed program is started as a separate process, with all this entails.
4180 Secondly, in a multithreaded program, other threads will continue to do
4181 work while the current thread is waiting for the child process to complete.
4183 A better option may sometimes be to terminate the current program immediately
4184 after spawning the child process. This is the behaviour exhibited by the
4185 $(LINK2 http://msdn.microsoft.com/en-us/library/431x4c1w.aspx,`__exec`)
4186 functions in Microsoft's C runtime library, and it is how D's now-deprecated
4187 Windows `execv*` functions work. Example:
4189 auto commandLine = [ "program", "arg1", "arg2" ];
4192 execv(commandLine[0], commandLine);
4193 throw new Exception("Failed to execute program");
4195 else version (Windows)
4197 spawnProcess(commandLine);
4198 import core.stdc.stdlib : _exit;
4203 int execv(in string pathname, in string[] argv);
4205 int execve(in string pathname, in string[] argv, in string[] envp);
4207 int execvp(in string pathname, in string[] argv);
4209 int execvpe(in string pathname, in string[] argv, in string[] envp);
4211 else version (Posix)
4213 int execv(in string pathname, in string[] argv)
4215 return execv_(pathname, argv);
4217 int execve(in string pathname, in string[] argv, in string[] envp)
4219 return execve_(pathname, argv, envp);
4221 int execvp(in string pathname, in string[] argv)
4223 return execvp_(pathname, argv);
4225 int execvpe(in string pathname, in string[] argv, in string[] envp)
4227 return execvpe_(pathname, argv, envp);
4231 // Move these C declarations to druntime if we decide to keep the D wrappers
4234 int execv(scope const(char) *, scope const(char *)*);
4235 int execve(scope const(char)*, scope const(char*)*, scope const(char*)*);
4236 int execvp(scope const(char)*, scope const(char*)*);
4237 version (Windows) int execvpe(scope const(char)*, scope const(char*)*, scope const(char*)*);
4240 private int execv_(in string pathname, in string[] argv)
4242 import core.exception : OutOfMemoryError;
4243 import std.exception : enforce;
4244 auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length));
4245 enforce!OutOfMemoryError(argv_ !is null, "Out of memory in std.process.");
4246 scope(exit) core.stdc.stdlib.free(argv_);
4248 toAStringz(argv, argv_);
4250 return execv(pathname.tempCString(), argv_);
4253 private int execve_(in string pathname, in string[] argv, in string[] envp)
4255 import core.exception : OutOfMemoryError;
4256 import std.exception : enforce;
4257 auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length));
4258 enforce!OutOfMemoryError(argv_ !is null, "Out of memory in std.process.");
4259 scope(exit) core.stdc.stdlib.free(argv_);
4260 auto envp_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + envp.length));
4261 enforce!OutOfMemoryError(envp_ !is null, "Out of memory in std.process.");
4262 scope(exit) core.stdc.stdlib.free(envp_);
4264 toAStringz(argv, argv_);
4265 toAStringz(envp, envp_);
4267 return execve(pathname.tempCString(), argv_, envp_);
4270 private int execvp_(in string pathname, in string[] argv)
4272 import core.exception : OutOfMemoryError;
4273 import std.exception : enforce;
4274 auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length));
4275 enforce!OutOfMemoryError(argv_ !is null, "Out of memory in std.process.");
4276 scope(exit) core.stdc.stdlib.free(argv_);
4278 toAStringz(argv, argv_);
4280 return execvp(pathname.tempCString(), argv_);
4283 private int execvpe_(in string pathname, in string[] argv, in string[] envp)
4287 import std.array : split;
4288 import std.conv : to;
4289 // Is pathname rooted?
4290 if (pathname[0] == '/')
4292 // Yes, so just call execve()
4293 return execve(pathname, argv, envp);
4297 // No, so must traverse PATHs, looking for first match
4298 string[] envPaths = split(
4299 to!string(core.stdc.stdlib.getenv("PATH")), ":");
4302 // Note: if any call to execve() succeeds, this process will cease
4303 // execution, so there's no need to check the execve() result through
4306 foreach (string pathDir; envPaths)
4308 string composite = cast(string) (pathDir ~ "/" ~ pathname);
4310 iRet = execve(composite, argv, envp);
4314 iRet = execve(pathname, argv, envp);
4320 else version (Windows)
4322 import core.exception : OutOfMemoryError;
4323 import std.exception : enforce;
4324 auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length));
4325 enforce!OutOfMemoryError(argv_ !is null, "Out of memory in std.process.");
4326 scope(exit) core.stdc.stdlib.free(argv_);
4327 auto envp_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + envp.length));
4328 enforce!OutOfMemoryError(envp_ !is null, "Out of memory in std.process.");
4329 scope(exit) core.stdc.stdlib.free(envp_);
4331 toAStringz(argv, argv_);
4332 toAStringz(envp, envp_);
4334 return execvpe(pathname.tempCString(), argv_, envp_);
4344 /****************************************
4345 * Start up the browser and set it to viewing the page at url.
4347 void browse(scope const(char)[] url);
4352 import core.sys.windows.shellapi, core.sys.windows.winuser;
4354 pragma(lib,"shell32.lib");
4356 void browse(scope const(char)[] url) nothrow @nogc @trusted
4358 ShellExecuteW(null, "open", url.tempCStringW(), null, null, SW_SHOWNORMAL);
4361 else version (Posix)
4363 import core.stdc.stdio;
4364 import core.stdc.string;
4365 import core.sys.posix.unistd;
4367 void browse(scope const(char)[] url) nothrow @nogc @safe
4369 const(char)*[3] args;
4371 // Trusted because it's called with a zero-terminated literal
4372 const(char)* browser = (() @trusted => core.stdc.stdlib.getenv("BROWSER"))();
4375 // String already zero-terminated
4376 browser = (() @trusted => strdup(browser))();
4387 //args[0] = "x-www-browser"; // doesn't work on some systems
4388 args[0] = "xdg-open";
4392 const buffer = url.tempCString(); // Retain buffer until end of scope
4396 auto childpid = core.sys.posix.unistd.fork();
4399 // Trusted because args and all entries are always zero-terminated
4401 core.sys.posix.unistd.execvp(args[0], &args[0]) ||
4402 perror(args[0]) // failed to execute
4407 // Trusted because it's allocated via strdup above
4408 (() @trusted => free(cast(void*) browser))();
4410 version (StdUnittest)
4412 // Verify that the test script actually suceeds
4414 const check = (() @trusted => waitpid(childpid, &status, 0))();
4415 assert(check != -1);
4416 assert(status == 0);
4421 static assert(0, "os not supported");
4423 // Verify attributes are consistent between all implementations
4424 @safe @nogc nothrow unittest
4430 version (Windows) { /* Doesn't use BROWSER */ }
4434 import std.conv : text;
4435 import std.range : repeat;
4436 immutable string url = text("http://", repeat('x', 249));
4438 TestScript prog = `if [ "$1" != "` ~ url ~ `" ]; then exit 1; fi`;
4439 environment["BROWSER"] = prog.path;