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