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