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