1 // Written in the D programming language.2 3 /**
4 Functions for starting and interacting with other processes, and for
5 working with the current process' execution environment.
6 7 Process_handling:
8 $(UL $(LI
9 $(LREF spawnProcess) spawns a new process, optionally assigning it an
10 arbitrary set of standard input, output, and error streams.
11 The function returns immediately, leaving the child process to execute
12 in parallel with its parent. All other functions in this module that
13 spawn processes are built around `spawnProcess`.)
14 $(LI
15 $(LREF wait) makes the parent process wait for a child process to
16 terminate. In general one should always do this, to avoid
17 child processes becoming "zombies" when the parent process exits.
18 Scope guards are perfect for this – see the $(LREF spawnProcess)
19 documentation for examples. $(LREF tryWait) is similar to `wait`,
20 but does not block if the process has not yet terminated.)
21 $(LI
22 $(LREF pipeProcess) also spawns a child process which runs
23 in parallel with its parent. However, instead of taking
24 arbitrary streams, it automatically creates a set of
25 pipes that allow the parent to communicate with the child
26 through the child's standard input, output, and/or error streams.
27 This function corresponds roughly to C's `popen` function.)
28 $(LI
29 $(LREF execute) starts a new process and waits for it
30 to complete before returning. Additionally, it captures
31 the process' standard output and error streams and returns
32 the output of these as a string.)
33 $(LI
34 $(LREF spawnShell), $(LREF pipeShell) and $(LREF executeShell) work like
35 `spawnProcess`, `pipeProcess` and `execute`, respectively,
36 except that they take a single command string and run it through
37 the current user's default command interpreter.
38 `executeShell` corresponds roughly to C's `system` function.)
39 $(LI
40 $(LREF kill) attempts to terminate a running process.)
41 )
42 43 The following table compactly summarises the different process creation
44 functions and how they relate to each other:
45 $(BOOKTABLE,
46 $(TR $(TH )
47 $(TH Runs program directly)
48 $(TH Runs shell command))
49 $(TR $(TD Low-level process creation)
50 $(TD $(LREF spawnProcess))
51 $(TD $(LREF spawnShell)))
52 $(TR $(TD Automatic input/output redirection using pipes)
53 $(TD $(LREF pipeProcess))
54 $(TD $(LREF pipeShell)))
55 $(TR $(TD Execute and wait for completion, collect output)
56 $(TD $(LREF execute))
57 $(TD $(LREF executeShell)))
58 )
59 60 Other_functionality:
61 $(UL
62 $(LI
63 $(LREF pipe) is used to create unidirectional pipes.)
64 $(LI
65 $(LREF environment) is an interface through which the current process'
66 environment variables can be read and manipulated.)
67 $(LI
68 $(LREF escapeShellCommand) and $(LREF escapeShellFileName) are useful
69 for constructing shell command lines in a portable way.)
70 )
71 72 Authors:
73 $(LINK2 https://github.com/kyllingstad, Lars Tandle Kyllingstad),
74 $(LINK2 https://github.com/schveiguy, Steven Schveighoffer),
75 $(HTTP thecybershadow.net, Vladimir Panteleev)
76 Copyright:
77 Copyright (c) 2013, the authors. All rights reserved.
78 License:
79 $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
80 Source:
81 $(PHOBOSSRC std/process.d)
82 Macros:
83 OBJECTREF=$(REF1 $0, object)
84 85 Note:
86 Most of the functionality in this module is not available on iOS, tvOS
87 and watchOS. The only functions available on those platforms are:
88 $(LREF environment), $(LREF thisProcessID) and $(LREF thisThreadID).
89 */90 modulestd.process;
91 92 version (WebAssembly) {} else:
93 94 importcore.thread : ThreadID;
95 96 version (Posix)
97 {
98 importcore.sys.posix.sys.wait;
99 importcore.sys.posix.unistd;
100 }
101 version (Windows)
102 {
103 importcore.stdc.stdio;
104 importcore.sys.windows.winbase;
105 importcore.sys.windows.winnt;
106 importstd.utf;
107 importstd.windows.syserror;
108 }
109 110 importstd.internal.cstring;
111 importstd.range;
112 importstd.stdio;
113 114 version (OSX)
115 version = Darwin;
116 elseversion (iOS)
117 {
118 version = Darwin;
119 version = iOSDerived;
120 }
121 elseversion (TVOS)
122 {
123 version = Darwin;
124 version = iOSDerived;
125 }
126 elseversion (WatchOS)
127 {
128 version = Darwin;
129 version = iOSDerived;
130 }
131 132 // When the DMC runtime is used, we have to use some custom functions133 // to convert between Windows file handles and FILE*s.134 version (Win32) version (CRuntime_DigitalMars) version = DMC_RUNTIME;
135 136 137 // Some of the following should be moved to druntime.138 private139 {
140 // Microsoft Visual C Runtime (MSVCRT) declarations.141 version (Windows)
142 {
143 version (DMC_RUNTIME) { } else144 {
145 importcore.stdc.stdint;
146 enum147 {
148 STDIN_FILENO = 0,
149 STDOUT_FILENO = 1,
150 STDERR_FILENO = 2,
151 }
152 }
153 }
154 155 // POSIX API declarations.156 version (Posix)
157 {
158 version (Darwin)
159 {
160 extern(C) char*** _NSGetEnviron() nothrow;
161 const(char**) getEnvironPtr() @trusted162 {
163 return *_NSGetEnviron;
164 }
165 }
166 else167 {
168 // Made available by the C runtime:169 extern(C) extern__gsharedconstchar** environ;
170 const(char**) getEnvironPtr() @trusted171 {
172 returnenviron;
173 }
174 }
175 176 @systemunittest177 {
178 importcore.thread : Thread;
179 newThread({assert(getEnvironPtr !isnull);}).start();
180 }
181 }
182 } // private183 184 // =============================================================================185 // Environment variable manipulation.186 // =============================================================================187 188 /**
189 Manipulates _environment variables using an associative-array-like
190 interface.
191 192 This class contains only static methods, and cannot be instantiated.
193 See below for examples of use.
194 */195 abstractfinalclassenvironment196 {
197 staticimportcore.sys.posix.stdlib;
198 importcore.stdc.errno : errno, EINVAL;
199 200 static:
201 /**
202 Retrieves the value of the environment variable with the given `name`.
203 ---
204 auto path = environment["PATH"];
205 ---
206 207 Throws:
208 $(OBJECTREF Exception) if the environment variable does not exist,
209 or $(REF UTFException, std,utf) if the variable contains invalid UTF-16
210 characters (Windows only).
211 212 See_also:
213 $(LREF environment.get), which doesn't throw on failure.
214 */215 stringopIndex(scopeconst(char)[] name) @safe216 {
217 importstd.exception : enforce;
218 returnget(name, null).enforce("Environment variable not found: "~name);
219 }
220 221 /**
222 Retrieves the value of the environment variable with the given `name`,
223 or a default value if the variable doesn't exist.
224 225 Unlike $(LREF environment.opIndex), this function never throws on Posix.
226 ---
227 auto sh = environment.get("SHELL", "/bin/sh");
228 ---
229 This function is also useful in checking for the existence of an
230 environment variable.
231 ---
232 auto myVar = environment.get("MYVAR");
233 if (myVar is null)
234 {
235 // Environment variable doesn't exist.
236 // Note that we have to use 'is' for the comparison, since
237 // myVar == null is also true if the variable exists but is
238 // empty.
239 }
240 ---
241 Params:
242 name = name of the environment variable to retrieve
243 defaultValue = default value to return if the environment variable doesn't exist.
244 245 Returns:
246 the value of the environment variable if found, otherwise
247 `null` if the environment doesn't exist.
248 249 Throws:
250 $(REF UTFException, std,utf) if the variable contains invalid UTF-16
251 characters (Windows only).
252 */253 stringget(scopeconst(char)[] name, stringdefaultValue = null) @safe254 {
255 stringvalue;
256 getImpl(name, (result) { value = result ? cachedToString(result) : defaultValue; });
257 returnvalue;
258 }
259 260 /**
261 Assigns the given `value` to the environment variable with the given
262 `name`.
263 If `value` is null the variable is removed from environment.
264 265 If the variable does not exist, it will be created. If it already exists,
266 it will be overwritten.
267 ---
268 environment["foo"] = "bar";
269 ---
270 271 Throws:
272 $(OBJECTREF Exception) if the environment variable could not be added
273 (e.g. if the name is invalid).
274 275 Note:
276 On some platforms, modifying environment variables may not be allowed in
277 multi-threaded programs. See e.g.
278 $(LINK2 https://www.gnu.org/software/libc/manual/html_node/Environment-Access.html#Environment-Access, glibc).
279 */280 inout(char)[] opIndexAssign(returnscopeinoutchar[] value, scopeconst(char)[] name) @trusted281 {
282 version (Posix)
283 {
284 importstd.exception : enforce, errnoEnforce;
285 if (valueisnull)
286 {
287 remove(name);
288 returnvalue;
289 }
290 if (core.sys.posix.stdlib.setenv(name.tempCString(), value.tempCString(), 1) != -1)
291 {
292 returnvalue;
293 }
294 // The default errno error message is very uninformative295 // in the most common case, so we handle it manually.296 enforce(errno != EINVAL,
297 "Invalid environment variable name: '"~name~"'");
298 errnoEnforce(false,
299 "Failed to add environment variable");
300 assert(0);
301 }
302 elseversion (Windows)
303 {
304 importstd.windows.syserror : wenforce;
305 wenforce(
306 SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW()),
307 );
308 returnvalue;
309 }
310 elsestaticassert(0);
311 }
312 313 /**
314 Removes the environment variable with the given `name`.
315 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 voidremove(scopeconst(char)[] name) @trustednothrow @nogc325 {
326 version (Windows) SetEnvironmentVariableW(name.tempCStringW(), null);
327 elseversion (Posix) core.sys.posix.stdlib.unsetenv(name.tempCString());
328 elsestaticassert(0);
329 }
330 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 boolopBinaryRight(stringop : "in")(scopeconst(char)[] name) @trusted354 {
355 version (Posix)
356 returncore.sys.posix.stdlib.getenv(name.tempCString()) !isnull;
357 elseversion (Windows)
358 {
359 SetLastError(NO_ERROR);
360 if (GetEnvironmentVariableW(name.tempCStringW, null, 0) > 0)
361 returntrue;
362 immutableerr = GetLastError();
363 if (err == NO_ERROR)
364 returntrue; // zero-length environment variable on Wine / XP365 if (err == ERROR_ENVVAR_NOT_FOUND)
366 returnfalse;
367 // Some other Windows error, throw.368 thrownewWindowsException(err);
369 }
370 elsestaticassert(0);
371 }
372 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() @trusted386 {
387 importstd.conv : to;
388 string[string] aa;
389 version (Posix)
390 {
391 autoenviron = getEnvironPtr;
392 for (inti=0; environ[i] != null; ++i)
393 {
394 importstd.string : indexOf;
395 396 immutablevarDef = to!string(environ[i]);
397 immutableeq = indexOf(varDef, '=');
398 assert(eq >= 0);
399 400 immutablename = varDef[0 .. eq];
401 immutablevalue = varDef[eq+1 .. $];
402 403 // In POSIX, environment variables may be defined more404 // than once. This is a security issue, which we avoid405 // 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.html408 if (name !inaa) aa[name] = value;
409 }
410 }
411 elseversion (Windows)
412 {
413 importstd.exception : enforce;
414 importstd.uni : toUpper;
415 autoenvBlock = GetEnvironmentStringsW();
416 enforce(envBlock, "Failed to retrieve environment variables.");
417 scope(exit) FreeEnvironmentStringsW(envBlock);
418 419 for (inti=0; envBlock[i] != '\0'; ++i)
420 {
421 autostart = i;
422 while (envBlock[i] != '=') ++i;
423 immutablename = 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 internally429 // by Windows to keep track of each drive's individual current430 // directory.431 if (!name.length)
432 continue;
433 434 // Just like in POSIX systems, environment variables may be435 // 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 autoval = toUTF8(envBlock[start .. i]);
440 if (name !inaa) aa[name] = valisnull ? "" : val;
441 }
442 }
443 elsestaticassert(0);
444 returnaa;
445 }
446 447 private:
448 version (Windows) aliasOSChar = WCHAR;
449 elseversion (Posix) aliasOSChar = char;
450 451 // Retrieves the environment variable. Calls `sink` with a452 // temporary buffer of OS characters, or `null` if the variable453 // doesn't exist.454 voidgetImpl(scopeconst(char)[] name, scopevoiddelegate(const(OSChar)[]) @safesink) @trusted455 {
456 version (Windows)
457 {
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. Lots460 // of error conditions because the windows API is nasty.461 462 importstd.conv : to;
463 constnamezTmp = name.tempCStringW();
464 WCHAR[] buf;
465 466 // clear error because GetEnvironmentVariable only says it sets it467 // if the environment variable is missing, not on other errors.468 SetLastError(NO_ERROR);
469 // len includes terminating null470 immutablelen = GetEnvironmentVariableW(namezTmp, null, 0);
471 if (len == 0)
472 {
473 immutableerr = GetLastError();
474 if (err == ERROR_ENVVAR_NOT_FOUND)
475 returnsink(null);
476 if (err != NO_ERROR) // Some other Windows error, throw.477 thrownewWindowsException(err);
478 }
479 if (len <= 1)
480 returnsink("");
481 buf.length = len;
482 483 while (true)
484 {
485 // lenRead is either the number of bytes read w/o null - if buf was long enough - or486 // the number of bytes necessary *including* null if buf wasn't long enough487 immutablelenRead = GetEnvironmentVariableW(namezTmp, buf.ptr, to!DWORD(buf.length));
488 if (lenRead == 0)
489 {
490 immutableerr = GetLastError();
491 if (err == NO_ERROR) // sucessfully read a 0-length variable492 returnsink("");
493 if (err == ERROR_ENVVAR_NOT_FOUND) // variable didn't exist494 returnsink(null);
495 // some other windows error496 thrownewWindowsException(err);
497 }
498 assert(lenRead != buf.length, "impossible according to msft docs");
499 if (lenRead < buf.length) // the buffer was long enough500 returnsink(buf[0 .. lenRead]);
501 // resize and go around again, because the environment variable grew502 buf.length = lenRead;
503 }
504 }
505 elseversion (Posix)
506 {
507 importcore.stdc.string : strlen;
508 509 constvz = core.sys.posix.stdlib.getenv(name.tempCString());
510 if (vz == null) returnsink(null);
511 returnsink(vz[0 .. strlen(vz)]);
512 }
513 elsestaticassert(0);
514 }
515 516 stringcachedToString(C)(scopeconst(C)[] v) @safe517 {
518 importstd.algorithm.comparison : equal;
519 520 // Cache the last call's result.521 staticstringlastResult;
522 if (v.empty)
523 {
524 // Return non-null array for blank result to distinguish from525 // not-present result.526 lastResult = "";
527 }
528 elseif (!v.equal(lastResult))
529 {
530 importstd.conv : to;
531 lastResult = v.to!string;
532 }
533 returnlastResult;
534 }
535 }
536 537 @safeunittest538 {
539 importstd.exception : assertThrown;
540 // New variable541 environment["std_process"] = "foo";
542 assert(environment["std_process"] == "foo");
543 assert("std_process"inenvironment);
544 545 // Set variable again (also tests length 1 case)546 environment["std_process"] = "b";
547 assert(environment["std_process"] == "b");
548 assert("std_process"inenvironment);
549 550 // Remove variable551 environment.remove("std_process");
552 assert("std_process" !inenvironment);
553 554 // Remove again, should succeed555 environment.remove("std_process");
556 assert("std_process" !inenvironment);
557 558 // Throw on not found.559 assertThrown(environment["std_process"]);
560 561 // get() without default value562 assert(environment.get("std_process") isnull);
563 564 // get() with default value565 assert(environment.get("std_process", "baz") == "baz");
566 567 // get() on an empty (but present) value568 environment["std_process"] = "";
569 autores = environment.get("std_process");
570 assert(res !isnull);
571 assert(res == "");
572 assert("std_process"inenvironment);
573 574 // Important to do the following round-trip after the previous test575 // because it tests toAA with an empty var576 577 // Convert to associative array578 autoaa = environment.toAA();
579 assert(aa.length > 0);
580 foreach (n, v; aa)
581 {
582 // Wine has some bugs related to environment variables:583 // - Wine allows the existence of an env. variable with the name584 // "\0", but GetEnvironmentVariable refuses to retrieve it.585 // As of 2.067 we filter these out anyway (see comment in toAA).586 587 assert(v == environment[n]);
588 }
589 590 // ... and back again.591 foreach (n, v; aa)
592 environment[n] = v;
593 594 // Complete the roundtrip595 autoaa2 = environment.toAA();
596 importstd.conv : text;
597 assert(aa == aa2, text(aa, " != ", aa2));
598 assert("std_process"inenvironment);
599 600 // Setting null must have the same effect as remove601 environment["std_process"] = null;
602 assert("std_process" !inenvironment);
603 }
604 605 // =============================================================================606 // Functions and classes for process management.607 // =============================================================================608 609 /**
610 * Returns the process ID of the current process,
611 * which is guaranteed to be unique on the system.
612 *
613 * Example:
614 * ---
615 * writefln("Current process ID: %d", thisProcessID);
616 * ---
617 */618 @propertyintthisProcessID() @trustednothrow @nogc//TODO: @safe619 {
620 version (Windows) returnGetCurrentProcessId();
621 elseversion (Posix) returncore.sys.posix.unistd.getpid();
622 }
623 624 625 /**
626 * Returns the process ID of the current thread,
627 * which is guaranteed to be unique within the current process.
628 *
629 * Returns:
630 * A $(REF ThreadID, core,thread) value for the calling thread.
631 *
632 * Example:
633 * ---
634 * writefln("Current thread ID: %s", thisThreadID);
635 * ---
636 */637 @propertyThreadIDthisThreadID() @trustednothrow @nogc//TODO: @safe638 {
639 version (Windows)
640 returnGetCurrentThreadId();
641 else642 version (Posix)
643 {
644 importcore.sys.posix.pthread : pthread_self;
645 returnpthread_self();
646 }
647 }
648 649 650 @systemunittest651 {
652 intpidA, pidB;
653 ThreadIDtidA, tidB;
654 pidA = thisProcessID;
655 tidA = thisThreadID;
656 657 importcore.thread;
658 autot = newThread({
659 pidB = thisProcessID;
660 tidB = thisThreadID;
661 });
662 t.start();
663 t.join();
664 665 assert(pidA == pidB);
666 assert(tidA != tidB);
667 }
668 669 670 package(std) stringuniqueTempPath() @safe671 {
672 importstd.file : tempDir;
673 importstd.path : buildPath;
674 importstd.uuid : randomUUID;
675 // Path should contain spaces to test escaping whitespace676 returnbuildPath(tempDir(), "std.process temporary file " ~
677 randomUUID().toString());
678 }
679 680 681 version (iOSDerived) {}
682 else:
683 684 /**
685 Spawns a new process, optionally assigning it an arbitrary set of standard
686 input, output, and error streams.
687 688 The function returns immediately, leaving the child process to execute
689 in parallel with its parent. It is recommended to always call $(LREF wait)
690 on the returned $(LREF Pid) unless the process was spawned with
691 `Config.detached` flag, as detailed in the documentation for `wait`.
692 693 Command_line:
694 There are four overloads of this function. The first two take an array
695 of strings, `args`, which should contain the program name as the
696 zeroth element and any command-line arguments in subsequent elements.
697 The third and fourth versions are included for convenience, and may be
698 used when there are no command-line arguments. They take a single string,
699 `program`, which specifies the program name.
700 701 Unless a directory is specified in `args[0]` or `program`,
702 `spawnProcess` will search for the program in a platform-dependent
703 manner. On POSIX systems, it will look for the executable in the
704 directories listed in the PATH environment variable, in the order
705 they are listed. On Windows, it will search for the executable in
706 the following sequence:
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:
718 auto pid = spawnProcess("./prog");
719 scope(exit) wait(pid);
720 // We can do something else while the program runs. The scope guard
721 // ensures that the process is waited for at the end of the scope.
722 ...
723 724 // Run DMD on the file "myprog.d", specifying a few compiler switches:
725 auto dmdPid = spawnProcess(["dmd", "-O", "-release", "-inline", "myprog.d" ]);
726 if (wait(dmdPid) != 0)
727 writeln("Compilation failed!");
728 ---
729 730 Environment_variables:
731 By default, the child process inherits the environment of the parent
732 process, along with any additional variables specified in the `env`
733 parameter. If the same variable exists in both the parent's environment
734 and in `env`, the latter takes precedence.
735 736 If the $(LREF Config.newEnv) flag is set in `config`, the child
737 process will $(I not) inherit the parent's environment. Its entire
738 environment will then be determined by `env`.
739 ---
740 wait(spawnProcess("myapp", ["foo" : "bar"], Config.newEnv));
741 ---
742 743 Standard_streams:
744 The optional arguments `stdin`, `stdout` and `stderr` may
745 be used to assign arbitrary $(REF File, std,stdio) objects as the standard
746 input, output and error streams, respectively, of the child process. The
747 former must be opened for reading, while the latter two must be opened for
748 writing. The default is for the child process to inherit the standard
749 streams of its parent.
750 ---
751 // Run DMD on the file myprog.d, logging any error messages to a
752 // file named errors.log.
753 auto logFile = File("errors.log", "w");
754 auto pid = spawnProcess(["dmd", "myprog.d"],
755 std.stdio.stdin,
756 std.stdio.stdout,
757 logFile);
758 if (wait(pid) != 0)
759 writeln("Compilation failed. See errors.log for details.");
760 ---
761 762 Note that if you pass a `File` object that is $(I not)
763 one of the standard input/output/error streams of the parent process,
764 that stream will by default be $(I closed) in the parent process when
765 this function returns. See the $(LREF Config) documentation below for
766 information about how to disable this behaviour.
767 768 Beware of buffering issues when passing `File` objects to
769 `spawnProcess`. The child process will inherit the low-level raw
770 read/write offset associated with the underlying file descriptor, but
771 it will not be aware of any buffered data. In cases where this matters
772 (e.g. when a file should be aligned before being passed on to the
773 child process), it may be a good idea to use unbuffered streams, or at
774 least ensure all relevant buffers are flushed.
775 776 Params:
777 args = An array which contains the program name as the zeroth element
778 and any command-line arguments in the following elements.
779 stdin = The standard input stream of the child process.
780 This can be any $(REF File, std,stdio) that is opened for reading.
781 By default the child process inherits the parent's input
782 stream.
783 stdout = The standard output stream of the child process.
784 This can be any $(REF File, std,stdio) that is opened for writing.
785 By default the child process inherits the parent's output stream.
786 stderr = The standard error stream of the child process.
787 This can be any $(REF File, std,stdio) that is opened for writing.
788 By default the child process inherits the parent's error stream.
789 env = Additional environment variables for the child process.
790 config = Flags that control process creation. See $(LREF Config)
791 for an overview of available flags.
792 workDir = The working directory for the new process.
793 By default the child process inherits the parent's working
794 directory.
795 796 Returns:
797 A $(LREF Pid) object that corresponds to the spawned process.
798 799 Throws:
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 */805 PidspawnProcess(scopeconst(char[])[] args,
806 Filestdin = std.stdio.stdin,
807 Filestdout = std.stdio.stdout,
808 Filestderr = std.stdio.stderr,
809 conststring[string] env = null,
810 Configconfig = Config.none,
811 scopeconstchar[] workDir = null)
812 @safe813 {
814 version (Windows)
815 {
816 constcommandLine = escapeShellArguments(args);
817 constprogram = args.length ? args[0] : null;
818 returnspawnProcessWin(commandLine, program, stdin, stdout, stderr, env, config, workDir);
819 }
820 elseversion (Posix)
821 {
822 returnspawnProcessPosix(args, stdin, stdout, stderr, env, config, workDir);
823 }
824 else825 staticassert(0);
826 }
827 828 /// ditto829 PidspawnProcess(scopeconst(char[])[] args,
830 conststring[string] env,
831 Configconfig = Config.none,
832 scopeconst(char)[] workDir = null)
833 @trusted// TODO: Should be @safe834 {
835 returnspawnProcess(args,
836 std.stdio.stdin,
837 std.stdio.stdout,
838 std.stdio.stderr,
839 env,
840 config,
841 workDir);
842 }
843 844 /// ditto845 PidspawnProcess(scopeconst(char)[] program,
846 Filestdin = std.stdio.stdin,
847 Filestdout = std.stdio.stdout,
848 Filestderr = std.stdio.stderr,
849 conststring[string] env = null,
850 Configconfig = Config.none,
851 scopeconst(char)[] workDir = null)
852 @trusted853 {
854 returnspawnProcess((&program)[0 .. 1],
855 stdin, stdout, stderr, env, config, workDir);
856 }
857 858 /// ditto859 PidspawnProcess(scopeconst(char)[] program,
860 conststring[string] env,
861 Configconfig = Config.none,
862 scopeconst(char)[] workDir = null)
863 @trusted864 {
865 returnspawnProcess((&program)[0 .. 1], env, config, workDir);
866 }
867 868 version (Posix) privateenumInternalError : ubyte869 {
870 noerror,
871 exec,
872 chdir,
873 getrlimit,
874 doubleFork,
875 malloc,
876 preExec,
877 }
878 879 /*
880 Implementation of spawnProcess() for POSIX.
881 882 envz should be a zero-terminated array of zero-terminated strings
883 on the form "var=value".
884 */885 version (Posix)
886 privatePidspawnProcessPosix(scopeconst(char[])[] args,
887 Filestdin,
888 Filestdout,
889 Filestderr,
890 scopeconststring[string] env,
891 Configconfig,
892 scopeconst(char)[] workDir)
893 @trusted// TODO: Should be @safe894 {
895 importcore.exception : RangeError;
896 importstd.algorithm.searching : any;
897 importstd.conv : text;
898 importstd.path : isDirSeparator;
899 importstd.string : toStringz;
900 901 if (args.empty) thrownewRangeError();
902 const(char)[] name = args[0];
903 if (!any!isDirSeparator(name))
904 {
905 name = searchPathFor(name);
906 if (nameisnull)
907 thrownewProcessException(text("Executable file not found: ", args[0]));
908 }
909 910 // Convert program name and arguments to C-style strings.911 autoargz = newconst(char)*[args.length+1];
912 argz[0] = toStringz(name);
913 foreach (i; 1 .. args.length) argz[i] = toStringz(args[i]);
914 argz[$-1] = null;
915 916 // Prepare environment.917 autoenvz = createEnv(env, !(config.flags & Config.Flags.newEnv));
918 919 // Open the working directory.920 // We use open in the parent and fchdir in the child921 // so that most errors (directory doesn't exist, not a directory)922 // can be propagated as exceptions before forking.923 intworkDirFD = -1;
924 scope(exit) if (workDirFD >= 0) close(workDirFD);
925 if (workDir.length)
926 {
927 importcore.sys.posix.fcntl : open, O_RDONLY, stat_t, fstat, S_ISDIR;
928 workDirFD = open(workDir.tempCString(), O_RDONLY);
929 if (workDirFD < 0)
930 throwProcessException.newFromErrno("Failed to open working directory");
931 stat_ts;
932 if (fstat(workDirFD, &s) < 0)
933 throwProcessException.newFromErrno("Failed to stat working directory");
934 if (!S_ISDIR(s.st_mode))
935 thrownewProcessException("Not a directory: " ~ cast(string) workDir);
936 }
937 938 staticintgetFD(refFilef) { returncore.stdc.stdio.fileno(f.getFP()); }
939 940 // Get the file descriptors of the streams.941 // These could potentially be invalid, but that is OK. If so, later calls942 // to dup2() and close() will just silently fail without causing any harm.943 autostdinFD = getFD(stdin);
944 autostdoutFD = getFD(stdout);
945 autostderrFD = getFD(stderr);
946 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 else953 throwProcessException.newFromErrno("Could not create pipe to check startup of child");
954 scope(exit) close(forkPipe[0]);
955 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 throwProcessException.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]);
971 972 staticvoidabortOnError(intforkPipeOut, InternalErrorerrorType, interror) nothrow973 {
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 }
980 981 voidclosePipeWriteEnds()
982 {
983 close(forkPipe[1]);
984 if (config.flags & Config.Flags.detached)
985 close(pidPipe[1]);
986 }
987 988 autoid = core.sys.posix.unistd.fork();
989 if (id < 0)
990 {
991 closePipeWriteEnds();
992 throwProcessException.newFromErrno("Failed to spawn new process");
993 }
994 995 voidforkChild() nothrow @nogc996 {
997 staticimportcore.sys.posix.stdio;
998 999 // Child process1000 1001 // no need for the read end of pipe on child side1002 if (config.flags & Config.Flags.detached)
1003 close(pidPipe[0]);
1004 close(forkPipe[0]);
1005 immutableforkPipeOut = forkPipe[1];
1006 immutablepidPipeOut = pidPipe[1];
1007 1008 // Set the working directory.1009 if (workDirFD >= 0)
1010 {
1011 if (fchdir(workDirFD) < 0)
1012 {
1013 // Fail. It is dangerous to run a program1014 // in an unexpected working directory.1015 abortOnError(forkPipeOut, InternalError.chdir, .errno);
1016 }
1017 close(workDirFD);
1018 }
1019 1020 voidexecProcess()
1021 {
1022 // Redirect streams and close the old file descriptors.1023 // In the case that stderr is redirected to stdout, we need1024 // to backup the file descriptor since stdout may be redirected1025 // 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);
1030 1031 // Ensure that the standard streams aren't closed on execute, and1032 // optionally close all other file descriptors.1033 setCLOEXEC(STDIN_FILENO, false);
1034 setCLOEXEC(STDOUT_FILENO, false);
1035 setCLOEXEC(STDERR_FILENO, false);
1036 1037 if (!(config.flags & Config.Flags.inheritFDs))
1038 {
1039 // NOTE: malloc() and getrlimit() are not on the POSIX async1040 // signal safe functions list, but practically this should1041 // not be a problem. Java VM and CPython also use malloc()1042 // in its own implementation via opendir().1043 importcore.stdc.stdlib : malloc;
1044 importcore.sys.posix.poll : pollfd, poll, POLLNVAL;
1045 importcore.sys.posix.sys.resource : rlimit, getrlimit, RLIMIT_NOFILE;
1046 1047 // Get the maximum number of file descriptors that could be open.1048 rlimitr;
1049 if (getrlimit(RLIMIT_NOFILE, &r) != 0)
1050 {
1051 abortOnError(forkPipeOut, InternalError.getrlimit, .errno);
1052 }
1053 immutablemaxDescriptors = cast(int) r.rlim_cur;
1054 1055 // The above, less stdin, stdout, and stderr1056 immutablemaxToClose = maxDescriptors - 3;
1057 1058 // Call poll() to see which ones are actually open:1059 autopfds = cast(pollfd*) malloc(pollfd.sizeof * maxToClose);
1060 if (pfdsisnull)
1061 {
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 end1075 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 }
1079 }
1080 else1081 {
1082 // Fall back to closing everything.1083 foreach (i; 3 .. maxDescriptors)
1084 {
1085 if (i == forkPipeOut) continue;
1086 close(i);
1087 }
1088 }
1089 }
1090 else// This is already done if we don't inherit descriptors.1091 {
1092 // Close the old file descriptors, unless they are1093 // 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);
1097 }
1098 1099 if (config.preExecFunction !isnull)
1100 {
1101 if (config.preExecFunction() != true)
1102 {
1103 abortOnError(forkPipeOut, InternalError.preExec, .errno);
1104 }
1105 }
1106 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);
1112 }
1113 1114 if (config.flags & Config.Flags.detached)
1115 {
1116 autosecondFork = core.sys.posix.unistd.fork();
1117 if (secondFork == 0)
1118 {
1119 close(pidPipeOut);
1120 execProcess();
1121 }
1122 elseif (secondFork == -1)
1123 {
1124 autosecondForkErrno = .errno;
1125 close(pidPipeOut);
1126 abortOnError(forkPipeOut, InternalError.doubleFork, secondForkErrno);
1127 }
1128 else1129 {
1130 core.sys.posix.unistd.write(pidPipeOut, &secondFork, pid_t.sizeof);
1131 close(pidPipeOut);
1132 close(forkPipeOut);
1133 _exit(0);
1134 }
1135 }
1136 else1137 {
1138 execProcess();
1139 }
1140 }
1141 1142 if (id == 0)
1143 {
1144 forkChild();
1145 assert(0);
1146 }
1147 else1148 {
1149 closePipeWriteEnds();
1150 autostatus = InternalError.noerror;
1151 autoreadExecResult = core.sys.posix.unistd.read(forkPipe[0], &status, status.sizeof);
1152 // Save error number just in case if subsequent "waitpid" fails and overrides errno1153 immutablelastError = .errno;
1154 1155 if (config.flags & Config.Flags.detached)
1156 {
1157 // Forked child exits right after creating second fork. So it should be safe to wait here.1158 importcore.sys.posix.sys.wait : waitpid;
1159 intwaitResult;
1160 waitpid(id, &waitResult, 0);
1161 }
1162 1163 if (readExecResult == -1)
1164 throwProcessException.newFromErrno(lastError, "Could not read from pipe to get child status");
1165 1166 boolowned = true;
1167 if (status != InternalError.noerror)
1168 {
1169 interror;
1170 readExecResult = read(forkPipe[0], &error, error.sizeof);
1171 stringerrorMsg;
1172 finalswitch (status)
1173 {
1174 caseInternalError.chdir:
1175 errorMsg = "Failed to set working directory";
1176 break;
1177 caseInternalError.getrlimit:
1178 errorMsg = "getrlimit failed";
1179 break;
1180 caseInternalError.exec:
1181 errorMsg = "Failed to execute '" ~ cast(string) name ~ "'";
1182 break;
1183 caseInternalError.doubleFork:
1184 // Can happen only when starting detached process1185 assert(config.flags & Config.Flags.detached);
1186 errorMsg = "Failed to fork twice";
1187 break;
1188 caseInternalError.malloc:
1189 errorMsg = "Failed to allocate memory";
1190 break;
1191 caseInternalError.preExec:
1192 errorMsg = "Failed to execute preExecFunction";
1193 break;
1194 caseInternalError.noerror:
1195 assert(false);
1196 }
1197 if (readExecResult == error.sizeof)
1198 throwProcessException.newFromErrno(error, errorMsg);
1199 thrownewProcessException(errorMsg);
1200 }
1201 elseif (config.flags & Config.Flags.detached)
1202 {
1203 owned = false;
1204 if (read(pidPipe[0], &id, id.sizeof) != id.sizeof)
1205 throwProcessException.newFromErrno("Could not read from pipe to get detached process id");
1206 }
1207 1208 // Parent process: Close streams and return.1209 if (!(config.flags & Config.Flags.retainStdin ) && stdinFD > STDERR_FILENO1210 && stdinFD != getFD(std.stdio.stdin ))
1211 stdin.close();
1212 if (!(config.flags & Config.Flags.retainStdout) && stdoutFD > STDERR_FILENO1213 && stdoutFD != getFD(std.stdio.stdout))
1214 stdout.close();
1215 if (!(config.flags & Config.Flags.retainStderr) && stderrFD > STDERR_FILENO1216 && stderrFD != getFD(std.stdio.stderr))
1217 stderr.close();
1218 returnnewPid(id, owned);
1219 }
1220 }
1221 1222 version (Posix)
1223 @systemunittest1224 {
1225 importstd.concurrency : ownerTid, receiveTimeout, send, spawn;
1226 importstd.datetime : seconds;
1227 1228 sigset_tss;
1229 sigemptyset(&ss);
1230 sigaddset(&ss, SIGINT);
1231 pthread_sigmask(SIG_BLOCK, &ss, null);
1232 1233 Configconfig = {
1234 preExecFunction: () @trusted @nogcnothrow {
1235 // Reset signal handlers1236 sigset_tss;
1237 if (sigfillset(&ss) != 0)
1238 {
1239 returnfalse;
1240 }
1241 if (sigprocmask(SIG_UNBLOCK, &ss, null) != 0)
1242 {
1243 returnfalse;
1244 }
1245 returntrue;
1246 },
1247 };
1248 1249 autopid = 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 }
1261 1262 // kill the spawned process with SIGINT1263 // and send its return code1264 spawn((sharedPidpid) {
1265 autop = cast() pid;
1266 kill(p, SIGINT);
1267 autocode = wait(p);
1268 assert(code < 0);
1269 send(ownerTid, code);
1270 }, cast(shared) pid);
1271 1272 autoreceived = receiveTimeout(3.seconds, (int) {});
1273 assert(received);
1274 }
1275 1276 /*
1277 Implementation of spawnProcess() for Windows.
1278 1279 commandLine must contain the entire command line, properly
1280 quoted/escaped as required by CreateProcessW().
1281 1282 envz must be a pointer to a block of UTF-16 characters on the form
1283 "var1=value1\0var2=value2\0...varN=valueN\0\0".
1284 */1285 version (Windows)
1286 privatePidspawnProcessWin(scopeconst(char)[] commandLine,
1287 scopeconst(char)[] program,
1288 Filestdin,
1289 Filestdout,
1290 Filestderr,
1291 scopeconststring[string] env,
1292 Configconfig,
1293 scopeconst(char)[] workDir)
1294 @trusted1295 {
1296 importcore.exception : RangeError;
1297 importstd.conv : text;
1298 1299 if (commandLine.empty) thrownewRangeError("Command line is empty");
1300 1301 // Prepare environment.1302 autoenvz = createEnv(env, !(config.flags & Config.Flags.newEnv));
1303 1304 // Startup info for CreateProcessW().1305 STARTUPINFO_Wstartinfo;
1306 startinfo.cb = startinfo.sizeof;
1307 staticintgetFD(refFilef) { returnf.isOpen ? f.fileno : -1; }
1308 1309 // Extract file descriptors and HANDLEs from the streams and make the1310 // handles inheritable.1311 staticvoidprepareStream(refFilefile, DWORDstdHandle, stringwhich,
1312 outintfileDescriptor, outHANDLEhandle)
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 (handleisnull || handle == INVALID_HANDLE_VALUE || handle == _NO_CONSOLE_FILENO)
1321 handle = GetStdHandle(stdHandle);
1322 1323 DWORDdwFlags;
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 thrownewStdioException(
1333 "Failed to make "~which~" stream inheritable by child process ("1334 ~generateSysErrorMsg() ~ ')',
1335 0);
1336 }
1337 }
1338 }
1339 }
1340 intstdinFD = -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 );
1344 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;
1349 1350 // Create process.1351 PROCESS_INFORMATIONpi;
1352 DWORDdwCreationFlags =
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 fixed1356 autopworkDir = workDir.tempCStringW();
1357 if (!CreateProcessW(null, commandLine.tempCStringW().buffPtr,
1358 null, null, true, dwCreationFlags,
1359 envz, workDir.length ? pworkDir : null, &startinfo, &pi))
1360 throwProcessException.newFromLastError("Failed to spawn process \"" ~ cast(string) program ~ '"');
1361 1362 // figure out if we should close any of the streams1363 if (!(config.flags & Config.Flags.retainStdin ) && stdinFD > STDERR_FILENO1364 && stdinFD != getFD(std.stdio.stdin ))
1365 stdin.close();
1366 if (!(config.flags & Config.Flags.retainStdout) && stdoutFD > STDERR_FILENO1367 && stdoutFD != getFD(std.stdio.stdout))
1368 stdout.close();
1369 if (!(config.flags & Config.Flags.retainStderr) && stderrFD > STDERR_FILENO1370 && stderrFD != getFD(std.stdio.stderr))
1371 stderr.close();
1372 1373 // close the thread handle in the process info structure1374 CloseHandle(pi.hThread);
1375 if (config.flags & Config.Flags.detached)
1376 {
1377 CloseHandle(pi.hProcess);
1378 returnnewPid(pi.dwProcessId);
1379 }
1380 returnnewPid(pi.dwProcessId, pi.hProcess);
1381 }
1382 1383 // Converts childEnv to a zero-terminated array of zero-terminated strings1384 // on the form "name=value", optionally adding those of the current process'1385 // environment strings that are not present in childEnv. If the parent's1386 // environment should be inherited without modification, this function1387 // returns environ directly.1388 version (Posix)
1389 privateconst(char*)* createEnv(conststring[string] childEnv,
1390 boolmergeWithParentEnv) @system1391 {
1392 // Determine the number of strings in the parent's environment.1393 intparentEnvLength = 0;
1394 autoenviron = getEnvironPtr;
1395 if (mergeWithParentEnv)
1396 {
1397 if (childEnv.length == 0) returnenviron;
1398 while (environ[parentEnvLength] != null) ++parentEnvLength;
1399 }
1400 1401 // Convert the "new" variables to C-style strings.1402 autoenvz = newconst(char)*[parentEnvLength + childEnv.length + 1];
1403 intpos = 0;
1404 foreach (var, val; childEnv)
1405 envz[pos++] = (var~'='~val~'\0').ptr;
1406 1407 // Add the parent's environment.1408 foreach (environStr; environ[0 .. parentEnvLength])
1409 {
1410 inteqPos = 0;
1411 while (environStr[eqPos] != '=' && environStr[eqPos] != '\0') ++eqPos;
1412 if (environStr[eqPos] != '=') continue;
1413 autovar = environStr[0 .. eqPos];
1414 if (varinchildEnv) continue;
1415 envz[pos++] = environStr;
1416 }
1417 envz[pos] = null;
1418 returnenvz.ptr;
1419 }
1420 1421 version (Posix) @systemunittest1422 {
1423 autoe1 = createEnv(null, false);
1424 assert(e1 != null && *e1 == null);
1425 1426 autoe2 = createEnv(null, true);
1427 assert(e2 != null);
1428 inti = 0;
1429 autoenviron = getEnvironPtr;
1430 for (; environ[i] != null; ++i)
1431 {
1432 assert(e2[i] != null);
1433 importcore.stdc.string : strcmp;
1434 assert(strcmp(e2[i], environ[i]) == 0);
1435 }
1436 assert(e2[i] == null);
1437 1438 autoe3 = 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"));
1442 }
1443 1444 1445 // Converts childEnv to a Windows environment block, which is on the form1446 // "name1=value1\0name2=value2\0...nameN=valueN\0\0", optionally adding1447 // those of the current process' environment strings that are not present1448 // in childEnv. Returns null if the parent's environment should be1449 // inherited without modification, as this is what is expected by1450 // CreateProcess().1451 version (Windows)
1452 privateLPVOIDcreateEnv(conststring[string] childEnv,
1453 boolmergeWithParentEnv)
1454 {
1455 if (mergeWithParentEnv && childEnv.length == 0) returnnull;
1456 importstd.array : appender;
1457 importstd.uni : toUpper;
1458 autoenvz = appender!(wchar[])();
1459 voidput(stringvar, stringval)
1460 {
1461 envz.put(var);
1462 envz.put('=');
1463 envz.put(val);
1464 envz.put(cast(wchar) '\0');
1465 }
1466 1467 // Add the variables in childEnv, removing them from parentEnv1468 // if they exist there too.1469 autoparentEnv = mergeWithParentEnv ? environment.toAA() : null;
1470 foreach (k, v; childEnv)
1471 {
1472 autouk = toUpper(k);
1473 put(uk, v);
1474 if (ukinparentEnv) parentEnv.remove(uk);
1475 }
1476 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 returnenvz.data.ptr;
1484 }
1485 1486 version (Windows) @systemunittest1487 {
1488 assert(createEnv(null, true) == null);
1489 assert((cast(wchar*) createEnv(null, false))[0 .. 2] == "\0\0"w);
1490 autoe1 = (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);
1492 }
1493 1494 // Searches the PATH variable for the given executable file,1495 // (checking that it is in fact executable).1496 version (Posix)
1497 package(std) stringsearchPathFor(scopeconst(char)[] executable)
1498 @safe1499 {
1500 importstd.algorithm.iteration : splitter;
1501 importstd.conv : to;
1502 importstd.path : chainPath;
1503 1504 typeof(return) result;
1505 1506 environment.getImpl("PATH",
1507 (scopeconst(char)[] path)
1508 {
1509 if (!path)
1510 return;
1511 1512 foreach (dir; splitter(path, ":"))
1513 {
1514 autoexecPath = chainPath(dir, executable);
1515 if (isExecutable(execPath))
1516 {
1517 result = execPath.to!(typeof(result));
1518 return;
1519 }
1520 }
1521 });
1522 1523 returnresult;
1524 }
1525 1526 // Checks whether the file exists and can be executed by the1527 // current user.1528 version (Posix)
1529 privateboolisExecutable(R)(Rpath) @trustednothrow @nogc1530 if (isSomeFiniteCharInputRange!R)
1531 {
1532 return (access(path.tempCString(), X_OK) == 0);
1533 }
1534 1535 version (Posix) @safeunittest1536 {
1537 importstd.algorithm;
1538 autolsPath = searchPathFor("ls");
1539 assert(!lsPath.empty);
1540 assert(lsPath[0] == '/');
1541 assert(lsPath.endsWith("ls"));
1542 autounlikely = searchPathFor("lkmqwpoialhggyaofijadsohufoiqezm");
1543 assert(unlikelyisnull, "Are you kidding me?");
1544 }
1545 1546 // Sets or unsets the FD_CLOEXEC flag on the given file descriptor.1547 version (Posix)
1548 privatevoidsetCLOEXEC(intfd, boolon) nothrow @nogc1549 {
1550 importcore.sys.posix.fcntl : fcntl, F_GETFD, FD_CLOEXEC, F_SETFD;
1551 autoflags = fcntl(fd, F_GETFD);
1552 if (flags >= 0)
1553 {
1554 if (on) flags |= FD_CLOEXEC;
1555 elseflags &= ~(cast(typeof(flags)) FD_CLOEXEC);
1556 flags = fcntl(fd, F_SETFD, flags);
1557 }
1558 assert(flags != -1 || .errno == EBADF);
1559 }
1560 1561 @systemunittest// Command line arguments in spawnProcess().1562 {
1563 version (Windows) TestScriptprog =
1564 "if not [%~1]==[foo] ( exit 1 )
1565 if not [%~2]==[bar] ( exit 2 )
1566 exit 0";
1567 elseversion (Posix) TestScriptprog =
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);
1576 }
1577 1578 // test that file descriptors are correctly closed / left open.1579 // ideally this would be done by the child process making libc1580 // calls, but we make do...1581 version (Posix) @systemunittest1582 {
1583 importcore.stdc.errno : errno;
1584 importcore.sys.posix.fcntl : open, O_RDONLY;
1585 importcore.sys.posix.unistd : close;
1586 importstd.algorithm.searching : canFind, findSplitBefore;
1587 importstd.array : split;
1588 importstd.conv : to;
1589 staticimportstd.file;
1590 importstd.functional : reverseArgs;
1591 importstd.path : buildPath;
1592 1593 autodirectory = uniqueTempPath();
1594 std.file.mkdir(directory);
1595 scope(exit) std.file.rmdirRecurse(directory);
1596 autopath = buildPath(directory, "tmp");
1597 std.file.write(path, null);
1598 errno = 0;
1599 autofd = open(path.tempCString, O_RDONLY);
1600 if (fd == -1)
1601 {
1602 importcore.stdc.string : strerror;
1603 importstd.stdio : stderr;
1604 importstd.string : fromStringz;
1605 1606 // For the CI logs1607 stderr.writefln("%s: could not open '%s': %s",
1608 __FUNCTION__, path, strerror(errno).fromStringz);
1609 // TODO: should we retry here instead?1610 return;
1611 }
1612 scope(exit) close(fd);
1613 1614 // command >&2 (or any other number) checks whethether that number1615 // file descriptor is open.1616 // Can't use this for arbitrary descriptors as many shells only support1617 // single digit fds.1618 TestScripttestDefaults = `command >&0 && command >&1 && command >&2`;
1619 assert(execute(testDefaults.path).status == 0);
1620 assert(execute(testDefaults.path, null, Config.inheritFDs).status == 0);
1621 1622 // Try a few different methods to check whether there are any1623 // incorrectly-open files.1624 voidtestFDs()
1625 {
1626 // try /proc/<pid>/fd/ on linux1627 version (linux)
1628 {
1629 TestScriptproc = "ls /proc/$$/fd";
1630 autoprocRes = execute(proc.path, null);
1631 if (procRes.status == 0)
1632 {
1633 autofdStr = 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 }
1640 1641 // try fuser (might sometimes need permissions)1642 TestScriptfuser = "echo $$ && fuser -f " ~ path;
1643 autofuserRes = execute(fuser.path, null);
1644 if (fuserRes.status == 0)
1645 {
1646 assert(!reverseArgs!canFind(fuserRes1647 .output.findSplitBefore("\n").expand));
1648 assert(reverseArgs!canFind(execute(fuser.path, null, Config.inheritFDs)
1649 .output.findSplitBefore("\n").expand));
1650 return;
1651 }
1652 1653 // last resort, try lsof (not available on all Posix)1654 TestScriptlsof = "lsof -p$$";
1655 autolsofRes = execute(lsof.path, null);
1656 if (lsofRes.status == 0)
1657 {
1658 assert(!lsofRes.output.canFind(path));
1659 autolsofOut = 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 }
1665 return;
1666 }
1667 1668 std.stdio.stderr.writeln(__FILE__, ':', __LINE__,
1669 ": Warning: Couldn't find any way to check open files");
1670 }
1671 testFDs();
1672 }
1673 1674 @systemunittest// Environment variables in spawnProcess().1675 {
1676 // We really should use set /a on Windows, but Wine doesn't support it.1677 version (Windows) TestScriptenvProg =
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) TestScriptenvProg =
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))`;
1696 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);
1701 1702 environment["std_process_unittest1"] = "1";
1703 assert(wait(spawnProcess(envProg.path)) == 1);
1704 assert(wait(spawnProcess(envProg.path, null, Config.newEnv)) == 0);
1705 1706 autoenv = ["std_process_unittest2" : "2"];
1707 assert(wait(spawnProcess(envProg.path, env)) == 3);
1708 assert(wait(spawnProcess(envProg.path, env, Config.newEnv)) == 2);
1709 1710 env["std_process_unittest1"] = "4";
1711 assert(wait(spawnProcess(envProg.path, env)) == 6);
1712 assert(wait(spawnProcess(envProg.path, env, Config.newEnv)) == 6);
1713 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 }
1718 1719 @systemunittest// Stream redirection in spawnProcess().1720 {
1721 importstd.path : buildPath;
1722 importstd.string;
1723 version (Windows) TestScriptprog =
1724 "set /p INPUT=
1725 echo %INPUT% output %~1
1726 echo %INPUT% error %~2 1>&2
1727 echo done > %3";
1728 elseversion (Posix) TestScriptprog =
1729 "read INPUT
1730 echo $INPUT output $1
1731 echo $INPUT error $2 >&2
1732 echo done > \"$3\"";
1733 1734 // Pipes1735 voidtestPipes(Configconfig)
1736 {
1737 importstd.file : tempDir, exists, remove;
1738 importstd.uuid : randomUUID;
1739 importstd.exception : collectException;
1740 autopipei = pipe();
1741 autopipeo = pipe();
1742 autopipee = pipe();
1743 autodone = buildPath(tempDir(), randomUUID().toString());
1744 autopid = spawnProcess([prog.path, "foo", "bar", done],
1745 pipei.readEnd, pipeo.writeEnd, pipee.writeEnd, null, config);
1746 pipei.writeEnd.writeln("input");
1747 pipei.writeEnd.flush();
1748 assert(pipeo.readEnd.readln().chomp() == "input output foo");
1749 assert(pipee.readEnd.readln().chomp().stripRight() == "input error bar");
1750 if (config.flags & Config.Flags.detached)
1751 while (!done.exists) Thread.sleep(10.msecs);
1752 else1753 wait(pid);
1754 while (remove(done).collectException) Thread.sleep(10.msecs);
1755 }
1756 1757 // Files1758 voidtestFiles(Configconfig)
1759 {
1760 importstd.ascii : newline;
1761 importstd.file : tempDir, exists, remove, readText, write;
1762 importstd.uuid : randomUUID;
1763 importstd.exception : collectException;
1764 autopathi = buildPath(tempDir(), randomUUID().toString());
1765 autopatho = buildPath(tempDir(), randomUUID().toString());
1766 autopathe = buildPath(tempDir(), randomUUID().toString());
1767 write(pathi, "INPUT" ~ newline);
1768 autofilei = File(pathi, "r");
1769 autofileo = File(patho, "w");
1770 autofilee = File(pathe, "w");
1771 autodone = buildPath(tempDir(), randomUUID().toString());
1772 autopid = spawnProcess([prog.path, "bar", "baz", done], filei, fileo, filee, null, config);
1773 if (config.flags & Config.Flags.detached)
1774 while (!done.exists) Thread.sleep(10.msecs);
1775 else1776 wait(pid);
1777 assert(readText(patho).chomp() == "INPUT output bar");
1778 assert(readText(pathe).chomp().stripRight() == "INPUT error baz");
1779 while (remove(pathi).collectException) Thread.sleep(10.msecs);
1780 while (remove(patho).collectException) Thread.sleep(10.msecs);
1781 while (remove(pathe).collectException) Thread.sleep(10.msecs);
1782 while (remove(done).collectException) Thread.sleep(10.msecs);
1783 }
1784 1785 testPipes(Config.none);
1786 testFiles(Config.none);
1787 testPipes(Config.detached);
1788 testFiles(Config.detached);
1789 }
1790 1791 @systemunittest// Error handling in spawnProcess()1792 {
1793 importstd.algorithm.searching : canFind;
1794 importstd.exception : assertThrown, collectExceptionMsg;
1795 1796 staticvoidtestNotFoundException(stringprogram)
1797 {
1798 assert(collectExceptionMsg!ProcessException(spawnProcess(program)).canFind(program));
1799 assert(collectExceptionMsg!ProcessException(spawnProcess(program, null, Config.detached)).canFind(program));
1800 }
1801 testNotFoundException("ewrgiuhrifuheiohnmnvqweoijwf");
1802 testNotFoundException("./rgiuhrifuheiohnmnvqweoijwf");
1803 1804 // can't execute malformed file with executable permissions1805 version (Posix)
1806 {
1807 importstd.path : buildPath;
1808 importstd.file : remove, write, setAttributes, tempDir;
1809 importcore.sys.posix.sys.stat : S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP, S_IROTH, S_IXOTH;
1810 importstd.conv : to;
1811 stringdeleteme = buildPath(tempDir(), "deleteme.std.process.unittest.pid") ~ to!string(thisProcessID);
1812 write(deleteme, "");
1813 scope(exit) remove(deleteme);
1814 setAttributes(deleteme, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
1815 assertThrown!ProcessException(spawnProcess(deleteme));
1816 assertThrown!ProcessException(spawnProcess(deleteme, null, Config.detached));
1817 }
1818 }
1819 1820 @systemunittest// Specifying a working directory.1821 {
1822 importstd.path;
1823 importstd.file;
1824 TestScriptprog = "echo foo>bar";
1825 1826 autodirectory = uniqueTempPath();
1827 mkdir(directory);
1828 scope(exit) rmdirRecurse(directory);
1829 1830 autopid = spawnProcess([prog.path], null, Config.none, directory);
1831 wait(pid);
1832 assert(exists(buildPath(directory, "bar")));
1833 }
1834 1835 @systemunittest// Specifying a bad working directory.1836 {
1837 importstd.exception : assertThrown;
1838 importstd.file;
1839 TestScriptprog = "echo";
1840 1841 autodirectory = uniqueTempPath();
1842 assertThrown!ProcessException(spawnProcess([prog.path], null, Config.none, directory));
1843 assertThrown!ProcessException(spawnProcess([prog.path], null, Config.detached, directory));
1844 1845 std.file.write(directory, "foo");
1846 scope(exit) remove(directory);
1847 assertThrown!ProcessException(spawnProcess([prog.path], null, Config.none, directory));
1848 assertThrown!ProcessException(spawnProcess([prog.path], null, Config.detached, directory));
1849 1850 // can't run in directory if user does not have search permission on this directory1851 version (Posix)
1852 {
1853 if (core.sys.posix.unistd.getuid() != 0)
1854 {
1855 importcore.sys.posix.sys.stat : S_IRUSR;
1856 autodirectoryNoSearch = uniqueTempPath();
1857 mkdir(directoryNoSearch);
1858 scope(exit) rmdirRecurse(directoryNoSearch);
1859 setAttributes(directoryNoSearch, S_IRUSR);
1860 assertThrown!ProcessException(spawnProcess(prog.path, null, Config.none, directoryNoSearch));
1861 assertThrown!ProcessException(spawnProcess(prog.path, null, Config.detached, directoryNoSearch));
1862 }
1863 }
1864 }
1865 1866 @systemunittest// Specifying empty working directory.1867 {
1868 TestScriptprog = "";
1869 1870 stringdirectory = "";
1871 assert(directory.ptr && !directory.length);
1872 spawnProcess([prog.path], null, Config.none, directory).wait();
1873 }
1874 1875 // Reopening the standard streams (https://issues.dlang.org/show_bug.cgi?id=13258)1876 @systemunittest1877 {
1878 importstd.string;
1879 importstd.file;
1880 voidfun()
1881 {
1882 spawnShell("echo foo").wait();
1883 spawnShell("echo bar").wait();
1884 }
1885 1886 autotmpFile = uniqueTempPath();
1887 scope(exit) if (exists(tmpFile)) remove(tmpFile);
1888 1889 {
1890 autooldOut = std.stdio.stdout;
1891 scope(exit) std.stdio.stdout = oldOut;
1892 1893 std.stdio.stdout = File(tmpFile, "w");
1894 fun();
1895 std.stdio.stdout.close();
1896 }
1897 1898 autolines = readText(tmpFile).splitLines();
1899 assert(lines == ["foo", "bar"]);
1900 }
1901 1902 // MSVCRT workaround (https://issues.dlang.org/show_bug.cgi?id=14422)1903 version (Windows)
1904 @systemunittest1905 {
1906 autofn = uniqueTempPath();
1907 scope(exit) if (exists(fn)) remove(fn);
1908 std.file.write(fn, "AAAAAAAAAA");
1909 1910 autof = File(fn, "a");
1911 spawnProcess(["cmd", "/c", "echo BBBBB"], std.stdio.stdin, f).wait();
1912 1913 autodata = readText(fn);
1914 assert(data == "AAAAAAAAAABBBBB\r\n", data);
1915 }
1916 1917 // https://issues.dlang.org/show_bug.cgi?id=207651918 // Test that running processes with relative path works in conjunction1919 // with indicating a workDir.1920 version (Posix) @systemunittest1921 {
1922 importstd.file : mkdir, write, setAttributes, rmdirRecurse;
1923 importstd.conv : octal;
1924 1925 autodir = uniqueTempPath();
1926 mkdir(dir);
1927 scope(exit) rmdirRecurse(dir);
1928 write(dir ~ "/program", "#!/bin/sh\necho Hello");
1929 setAttributes(dir ~ "/program", octal!700);
1930 1931 assert(execute(["./program"], null, Config.none, size_t.max, dir).output == "Hello\n");
1932 }
1933 1934 /**
1935 A variation on $(LREF spawnProcess) that runs the given _command through
1936 the current user's preferred _command interpreter (aka. shell).
1937 1938 The string `command` is passed verbatim to the shell, and is therefore
1939 subject to its rules about _command structure, argument/filename quoting
1940 and escaping of special characters.
1941 The path to the shell executable defaults to $(LREF nativeShell).
1942 1943 In all other respects this function works just like `spawnProcess`.
1944 Please refer to the $(LREF spawnProcess) documentation for descriptions
1945 of the other function parameters, the return value and any exceptions
1946 that may be thrown.
1947 ---
1948 // Run the command/program "foo" on the file named "my file.txt", and
1949 // redirect its output into foo.log.
1950 auto pid = spawnShell(`foo "my file.txt" > foo.log`);
1951 wait(pid);
1952 ---
1953 1954 See_also:
1955 $(LREF escapeShellCommand), which may be helpful in constructing a
1956 properly quoted and escaped shell _command line for the current platform.
1957 */1958 PidspawnShell(scopeconst(char)[] command,
1959 Filestdin = std.stdio.stdin,
1960 Filestdout = std.stdio.stdout,
1961 Filestderr = std.stdio.stderr,
1962 scopeconststring[string] env = null,
1963 Configconfig = Config.none,
1964 scopeconst(char)[] workDir = null,
1965 scopestringshellPath = nativeShell)
1966 @trusted// See reason below1967 {
1968 version (Windows)
1969 {
1970 // CMD does not parse its arguments like other programs.1971 // It does not use CommandLineToArgvW.1972 // Instead, it treats the first and last quote specially.1973 // See CMD.EXE /? for details.1974 constcommandLine = escapeShellFileName(shellPath)
1975 ~ ` ` ~ shellSwitch ~ ` "` ~ command ~ `"`;
1976 returnspawnProcessWin(commandLine, shellPath, stdin, stdout, stderr, env, config, workDir);
1977 }
1978 elseversion (Posix)
1979 {
1980 const(char)[][3] args;
1981 args[0] = shellPath;
1982 args[1] = shellSwitch;
1983 args[2] = command;
1984 /* The passing of args converts the static array, which is initialized with `scope` pointers,
1985 * to a dynamic array, which is also a scope parameter. So, it is a scope pointer to a
1986 * scope pointer, which although is safely used here, D doesn't allow transitive scope.
1987 * See https://github.com/dlang/dmd/pull/10951
1988 */1989 returnspawnProcessPosix(args, stdin, stdout, stderr, env, config, workDir);
1990 }
1991 else1992 staticassert(0);
1993 }
1994 1995 /// ditto1996 PidspawnShell(scopeconst(char)[] command,
1997 scopeconststring[string] env,
1998 Configconfig = Config.none,
1999 scopeconst(char)[] workDir = null,
2000 scopestringshellPath = nativeShell)
2001 @trusted// TODO: Should be @safe2002 {
2003 returnspawnShell(command,
2004 std.stdio.stdin,
2005 std.stdio.stdout,
2006 std.stdio.stderr,
2007 env,
2008 config,
2009 workDir,
2010 shellPath);
2011 }
2012 2013 @systemunittest2014 {
2015 version (Windows)
2016 autocmd = "echo %FOO%";
2017 elseversion (Posix)
2018 autocmd = "echo $foo";
2019 importstd.file;
2020 autotmpFile = uniqueTempPath();
2021 scope(exit) if (exists(tmpFile)) remove(tmpFile);
2022 autoredir = "> \""~tmpFile~'"';
2023 autoenv = ["foo" : "bar"];
2024 assert(wait(spawnShell(cmd~redir, env)) == 0);
2025 autof = File(tmpFile, "a");
2026 version (CRuntime_Microsoft) f.seek(0, SEEK_END); // MSVCRT probably seeks to the end when writing, not before2027 assert(wait(spawnShell(cmd, std.stdio.stdin, f, std.stdio.stderr, env)) == 0);
2028 f.close();
2029 autooutput = std.file.readText(tmpFile);
2030 assert(output == "bar\nbar\n" || output == "bar\r\nbar\r\n");
2031 }
2032 2033 version (Windows)
2034 @systemunittest2035 {
2036 importstd.string;
2037 importstd.conv : text;
2038 TestScriptprog = "echo %0 %*";
2039 autooutputFn = uniqueTempPath();
2040 scope(exit) if (exists(outputFn)) remove(outputFn);
2041 autoargs = [`a b c`, `a\b\c\`, `a"b"c"`];
2042 autoresult = executeShell(
2043 escapeShellCommand([prog.path] ~ args)
2044 ~ " > " ~
2045 escapeShellFileName(outputFn));
2046 assert(result.status == 0);
2047 autoargs2 = outputFn.readText().strip().parseCommandLine()[1..$];
2048 assert(args == args2, text(args2));
2049 }
2050 2051 2052 /**
2053 Options that control the behaviour of process creation functions in this
2054 module. Most options only apply to $(LREF spawnProcess) and
2055 $(LREF spawnShell).
2056 2057 Example:
2058 ---
2059 auto logFile = File("myapp_error.log", "w");
2060 2061 // Start program, suppressing the console window (Windows only),
2062 // redirect its error stream to logFile, and leave logFile open
2063 // in the parent process as well.
2064 auto pid = spawnProcess("myapp", stdin, stdout, logFile,
2065 Config.retainStderr | Config.suppressConsole);
2066 scope(exit)
2067 {
2068 auto exitCode = wait(pid);
2069 logFile.writeln("myapp exited with code ", exitCode);
2070 logFile.close();
2071 }
2072 ---
2073 */2074 structConfig2075 {
2076 /**
2077 Flag options.
2078 Use bitwise OR to combine flags.
2079 **/2080 enumFlags2081 {
2082 none = 0,
2083 2084 /**
2085 By default, the child process inherits the parent's environment,
2086 and any environment variables passed to $(LREF spawnProcess) will
2087 be added to it. If this flag is set, the only variables in the
2088 child process' environment will be those given to spawnProcess.
2089 */2090 newEnv = 1,
2091 2092 /**
2093 Unless the child process inherits the standard input/output/error
2094 streams of its parent, one almost always wants the streams closed
2095 in the parent when $(LREF spawnProcess) returns. Therefore, by
2096 default, this is done. If this is not desirable, pass any of these
2097 options to spawnProcess.
2098 */2099 retainStdin = 2,
2100 retainStdout = 4, /// ditto2101 retainStderr = 8, /// ditto2102 2103 /**
2104 On Windows, if the child process is a console application, this
2105 flag will prevent the creation of a console window. Otherwise,
2106 it will be ignored. On POSIX, `suppressConsole` has no effect.
2107 */2108 suppressConsole = 16,
2109 2110 /**
2111 On POSIX, open $(LINK2 http://en.wikipedia.org/wiki/File_descriptor,file descriptors)
2112 are by default inherited by the child process. As this may lead
2113 to subtle bugs when pipes or multiple threads are involved,
2114 $(LREF spawnProcess) ensures that all file descriptors except the
2115 ones that correspond to standard input/output/error are closed
2116 in the child process when it starts. Use `inheritFDs` to prevent
2117 this.
2118 2119 On Windows, this option has no effect, and any handles which have been
2120 explicitly marked as inheritable will always be inherited by the child
2121 process.
2122 */2123 inheritFDs = 32,
2124 2125 /**
2126 Spawn process in detached state. This removes the need in calling
2127 $(LREF wait) to clean up the process resources.
2128 2129 Note:
2130 Calling $(LREF wait) or $(LREF kill) with the resulting `Pid` is invalid.
2131 */2132 detached = 64,
2133 2134 /**
2135 By default, the $(LREF execute) and $(LREF executeShell) functions
2136 will capture child processes' both stdout and stderr. This can be
2137 undesirable if the standard output is to be processed or otherwise
2138 used by the invoking program, as `execute`'s result would then
2139 contain a mix of output and warning/error messages.
2140 2141 Specify this flag when calling `execute` or `executeShell` to
2142 cause invoked processes' stderr stream to be sent to $(REF stderr,
2143 std,stdio), and only capture and return standard output.
2144 2145 This flag has no effect on $(LREF spawnProcess) or $(LREF spawnShell).
2146 */2147 stderrPassThrough = 128,
2148 }
2149 Flagsflags; /// ditto2150 2151 /**
2152 For backwards compatibility, and cases when only flags need to
2153 be specified in the `Config`, these allow building `Config`
2154 instances using flag names only.
2155 */2156 enumConfignone = Config.init;
2157 enumConfignewEnv = Config(Flags.newEnv); /// ditto2158 enumConfigretainStdin = Config(Flags.retainStdin); /// ditto2159 enumConfigretainStdout = Config(Flags.retainStdout); /// ditto2160 enumConfigretainStderr = Config(Flags.retainStderr); /// ditto2161 enumConfigsuppressConsole = Config(Flags.suppressConsole); /// ditto2162 enumConfiginheritFDs = Config(Flags.inheritFDs); /// ditto2163 enumConfigdetached = Config(Flags.detached); /// ditto2164 enumConfigstderrPassThrough = Config(Flags.stderrPassThrough); /// ditto2165 ConfigopUnary(stringop)()
2166 if (is(typeof(mixin(op ~ q{flags}))))
2167 {
2168 returnConfig(mixin(op ~ q{flags}));
2169 } /// ditto2170 ConfigopBinary(stringop)(Configother)
2171 if (is(typeof(mixin(q{flags} ~ op ~ q{other.flags}))))
2172 {
2173 returnConfig(mixin(q{flags} ~ op ~ q{other.flags}));
2174 } /// ditto2175 ConfigopOpAssign(stringop)(Configother)
2176 if (is(typeof(mixin(q{flags} ~ op ~ q{=other.flags}))))
2177 {
2178 returnConfig(mixin(q{flags} ~ op ~ q{=other.flags}));
2179 } /// ditto2180 2181 version (StdDdoc)
2182 {
2183 /**
2184 A function that is called before `exec` in $(LREF spawnProcess).
2185 It returns `true` if succeeded and otherwise returns `false`.
2186 2187 $(RED Warning:
2188 Please note that the code in this function must only use
2189 async-signal-safe functions.)
2190 2191 On Windows, this member is not available.
2192 */2193 boolfunction() nothrow @nogc @safepreExecFunction;
2194 }
2195 elseversion (Posix)
2196 {
2197 boolfunction() nothrow @nogc @safepreExecFunction;
2198 }
2199 }
2200 2201 // https://issues.dlang.org/show_bug.cgi?id=221252202 @safeunittest2203 {
2204 Configc = Config.retainStdin;
2205 c |= Config.retainStdout;
2206 c |= Config.retainStderr;
2207 c &= ~Config.retainStderr;
2208 assert(c == (Config.retainStdin | Config.retainStdout));
2209 }
2210 2211 /// A handle that corresponds to a spawned process.2212 finalclassPid2213 {
2214 /**
2215 The process ID number.
2216 2217 This is a number that uniquely identifies the process on the operating
2218 system, for at least as long as the process is running. Once $(LREF wait)
2219 has been called on the $(LREF Pid), this method will return an
2220 invalid (negative) process ID.
2221 */2222 @propertyintprocessID() const @safepurenothrow2223 {
2224 return_processID;
2225 }
2226 2227 /**
2228 An operating system handle to the process.
2229 2230 This handle is used to specify the process in OS-specific APIs.
2231 On POSIX, this function returns a `core.sys.posix.sys.types.pid_t`
2232 with the same value as $(LREF Pid.processID), while on Windows it returns
2233 a `core.sys.windows.windows.HANDLE`.
2234 2235 Once $(LREF wait) has been called on the $(LREF Pid), this method
2236 will return an invalid handle.
2237 */2238 // Note: Since HANDLE is a reference, this function cannot be const.2239 version (Windows)
2240 @propertyHANDLEosHandle() @nogc @safepurenothrow2241 {
2242 return_handle;
2243 }
2244 elseversion (Posix)
2245 @propertypid_tosHandle() @nogc @safepurenothrow2246 {
2247 return_processID;
2248 }
2249 2250 private:
2251 /*
2252 Pid.performWait() does the dirty work for wait() and nonBlockingWait().
2253 2254 If block == true, this function blocks until the process terminates,
2255 sets _processID to terminated, and returns the exit code or terminating
2256 signal as described in the wait() documentation.
2257 2258 If block == false, this function returns immediately, regardless
2259 of the status of the process. If the process has terminated, the
2260 function has the exact same effect as the blocking version. If not,
2261 it returns 0 and does not modify _processID.
2262 */2263 version (Posix)
2264 intperformWait(boolblock) @trusted2265 {
2266 importstd.exception : enforce;
2267 enforce!ProcessException(owned, "Can't wait on a detached process");
2268 if (_processID == terminated) return_exitCode;
2269 intexitCode;
2270 while (true)
2271 {
2272 intstatus;
2273 autocheck = waitpid(_processID, &status, block ? 0 : WNOHANG);
2274 if (check == -1)
2275 {
2276 if (errno == ECHILD)
2277 {
2278 thrownewProcessException(
2279 "Process does not exist or is not a child process.");
2280 }
2281 else2282 {
2283 // waitpid() was interrupted by a signal. We simply2284 // restart it.2285 assert(errno == EINTR);
2286 continue;
2287 }
2288 }
2289 if (!block && check == 0) return0;
2290 if (WIFEXITED(status))
2291 {
2292 exitCode = WEXITSTATUS(status);
2293 break;
2294 }
2295 elseif (WIFSIGNALED(status))
2296 {
2297 exitCode = -WTERMSIG(status);
2298 break;
2299 }
2300 // We check again whether the call should be blocking,2301 // since we don't care about other status changes besides2302 // "exited" and "terminated by signal".2303 if (!block) return0;
2304 2305 // Process has stopped, but not terminated, so we continue waiting.2306 }
2307 // Mark Pid as terminated, and cache and return exit code.2308 _processID = terminated;
2309 _exitCode = exitCode;
2310 returnexitCode;
2311 }
2312 elseversion (Windows)
2313 {
2314 intperformWait(constboolblock, constDWORDtimeout = INFINITE) @trusted2315 {
2316 importstd.exception : enforce;
2317 enforce!ProcessException(owned, "Can't wait on a detached process");
2318 if (_processID == terminated) return_exitCode;
2319 assert(_handle != INVALID_HANDLE_VALUE);
2320 if (block)
2321 {
2322 autoresult = WaitForSingleObject(_handle, timeout);
2323 if (result != WAIT_OBJECT_0)
2324 {
2325 // Wait time exceeded `timeout` milliseconds?2326 if (result == WAIT_TIMEOUT && timeout != INFINITE)
2327 return0;
2328 2329 throwProcessException.newFromLastError("Wait failed.");
2330 }
2331 }
2332 if (!GetExitCodeProcess(_handle, cast(LPDWORD)&_exitCode))
2333 throwProcessException.newFromLastError();
2334 if (!block && _exitCode == STILL_ACTIVE) return0;
2335 CloseHandle(_handle);
2336 _handle = INVALID_HANDLE_VALUE;
2337 _processID = terminated;
2338 return_exitCode;
2339 }
2340 2341 intperformWait(Durationtimeout) @safe2342 {
2343 importstd.exception : enforce;
2344 constmsecs = timeout.total!"msecs";
2345 2346 // Limit this implementation the maximum wait time offered by2347 // WaitForSingleObject. One could theoretically break up larger2348 // durations into multiple waits but (DWORD.max - 1).msecs2349 // (> 7 weeks, 17 hours) should be enough for the usual case.2350 // DWORD.max is reserved for INFINITE2351 enforce!ProcessException(msecs < DWORD.max, "Timeout exceeds maximum wait time!");
2352 returnperformWait(true, cast(DWORD) msecs);
2353 }
2354 2355 ~this()
2356 {
2357 if (_handle != INVALID_HANDLE_VALUE)
2358 {
2359 CloseHandle(_handle);
2360 _handle = INVALID_HANDLE_VALUE;
2361 }
2362 }
2363 }
2364 2365 // Special values for _processID.2366 enuminvalid = -1, terminated = -2;
2367 2368 // OS process ID number. Only nonnegative IDs correspond to2369 // running processes.2370 int_processID = invalid;
2371 2372 // Exit code cached by wait(). This is only expected to hold a2373 // sensible value if _processID == terminated.2374 int_exitCode;
2375 2376 // Whether the process can be waited for by wait() for or killed by kill().2377 // False if process was started as detached. True otherwise.2378 boolowned;
2379 2380 // Pids are only meant to be constructed inside this module, so2381 // we make the constructor private.2382 version (Windows)
2383 {
2384 HANDLE_handle = INVALID_HANDLE_VALUE;
2385 this(intpid, HANDLEhandle) @safepurenothrow2386 {
2387 _processID = pid;
2388 _handle = handle;
2389 this.owned = true;
2390 }
2391 this(intpid) @safepurenothrow2392 {
2393 _processID = pid;
2394 this.owned = false;
2395 }
2396 }
2397 else2398 {
2399 this(intid, boolowned) @safepurenothrow2400 {
2401 _processID = id;
2402 this.owned = owned;
2403 }
2404 }
2405 }
2406 2407 2408 /**
2409 Waits for the process associated with `pid` to terminate, and returns
2410 its exit status.
2411 2412 In general one should always _wait for child processes to terminate
2413 before exiting the parent process unless the process was spawned as detached
2414 (that was spawned with `Config.detached` flag).
2415 Otherwise, they may become "$(HTTP en.wikipedia.org/wiki/Zombie_process,zombies)"
2416 – processes that are defunct, yet still occupy a slot in the OS process table.
2417 You should not and must not wait for detached processes, since you don't own them.
2418 2419 If the process has already terminated, this function returns directly.
2420 The exit code is cached, so that if wait() is called multiple times on
2421 the same $(LREF Pid) it will always return the same value.
2422 2423 POSIX_specific:
2424 If the process is terminated by a signal, this function returns a
2425 negative number whose absolute value is the signal number.
2426 Since POSIX restricts normal exit codes to the range 0-255, a
2427 negative return value will always indicate termination by signal.
2428 Signal codes are defined in the `core.sys.posix.signal` module
2429 (which corresponds to the `signal.h` POSIX header).
2430 2431 Throws:
2432 $(LREF ProcessException) on failure or on attempt to wait for detached process.
2433 2434 Example:
2435 See the $(LREF spawnProcess) documentation.
2436 2437 See_also:
2438 $(LREF tryWait), for a non-blocking function.
2439 */2440 intwait(Pidpid) @safe2441 {
2442 assert(pid !isnull, "Called wait on a null Pid.");
2443 returnpid.performWait(true);
2444 }
2445 2446 2447 @systemunittest// Pid and wait()2448 {
2449 version (Windows) TestScriptprog = "exit %~1";
2450 elseversion (Posix) TestScriptprog = "exit $1";
2451 assert(wait(spawnProcess([prog.path, "0"])) == 0);
2452 assert(wait(spawnProcess([prog.path, "123"])) == 123);
2453 autopid = spawnProcess([prog.path, "10"]);
2454 assert(pid.processID > 0);
2455 version (Windows) assert(pid.osHandle != INVALID_HANDLE_VALUE);
2456 elseversion (Posix) assert(pid.osHandle == pid.processID);
2457 assert(wait(pid) == 10);
2458 assert(wait(pid) == 10); // cached exit code2459 assert(pid.processID < 0);
2460 version (Windows) assert(pid.osHandle == INVALID_HANDLE_VALUE);
2461 elseversion (Posix) assert(pid.osHandle < 0);
2462 }
2463 2464 privateimportstd.typecons : Tuple;
2465 2466 /**
2467 Waits until either the process associated with `pid` terminates or the
2468 elapsed time exceeds the given timeout.
2469 2470 If the process terminates within the given duration it behaves exactly like
2471 `wait`, except that it returns a tuple `(true, exit code)`.
2472 2473 If the process does not terminate within the given duration it will stop
2474 waiting and return `(false, 0).`
2475 2476 The timeout may not exceed `(uint.max - 1).msecs` (~ 7 weeks, 17 hours).
2477 2478 $(BLUE This function is Windows-Only.)
2479 2480 Returns:
2481 An $(D std.typecons.Tuple!(bool, "terminated", int, "status")).
2482 2483 Throws:
2484 $(LREF ProcessException) on failure or on attempt to wait for detached process.
2485 2486 Example:
2487 See the $(LREF spawnProcess) documentation.
2488 2489 See_also:
2490 $(LREF wait), for a blocking function without timeout.
2491 $(LREF tryWait), for a non-blocking function without timeout.
2492 */2493 version (StdDdoc)
2494 Tuple!(bool, "terminated", int, "status") waitTimeout(Pidpid, Durationtimeout) @safe;
2495 2496 elseversion (Windows)
2497 Tuple!(bool, "terminated", int, "status") waitTimeout(Pidpid, Durationtimeout) @safe2498 {
2499 assert(pid !isnull, "Called wait on a null Pid.");
2500 autocode = pid.performWait(timeout);
2501 returntypeof(return)(pid._processID == Pid.terminated, code);
2502 }
2503 2504 version (Windows)
2505 @systemunittest// Pid and waitTimeout()2506 {
2507 importstd.exception : collectException;
2508 importstd.typecons : tuple;
2509 2510 TestScriptprog = ":Loop\r\n" ~ "goto Loop";
2511 autopid = spawnProcess(prog.path);
2512 2513 // Doesn't block longer than one second2514 assert(waitTimeout(pid, 1.seconds) == tuple(false, 0));
2515 2516 kill(pid);
2517 assert(waitTimeout(pid, 1.seconds) == tuple(true, 1)); // exit 1 because the process is killed2518 2519 // Rejects timeouts exceeding the Windows API capabilities2520 constdur = DWORD.max.msecs;
2521 constex = collectException!ProcessException(waitTimeout(pid, dur));
2522 assert(ex);
2523 assert(ex.msg == "Timeout exceeds maximum wait time!");
2524 }
2525 2526 /**
2527 A non-blocking version of $(LREF wait).
2528 2529 If the process associated with `pid` has already terminated,
2530 `tryWait` has the exact same effect as `wait`.
2531 In this case, it returns a tuple where the `terminated` field
2532 is set to `true` and the `status` field has the same
2533 interpretation as the return value of `wait`.
2534 2535 If the process has $(I not) yet terminated, this function differs
2536 from `wait` in that does not wait for this to happen, but instead
2537 returns immediately. The `terminated` field of the returned
2538 tuple will then be set to `false`, while the `status` field
2539 will always be 0 (zero). `wait` or `tryWait` should then be
2540 called again on the same `Pid` at some later time; not only to
2541 get the exit code, but also to avoid the process becoming a "zombie"
2542 when it finally terminates. (See $(LREF wait) for details).
2543 2544 Returns:
2545 An $(D std.typecons.Tuple!(bool, "terminated", int, "status")).
2546 2547 Throws:
2548 $(LREF ProcessException) on failure or on attempt to wait for detached process.
2549 2550 Example:
2551 ---
2552 auto pid = spawnProcess("dmd myapp.d");
2553 scope(exit) wait(pid);
2554 ...
2555 auto dmd = tryWait(pid);
2556 if (dmd.terminated)
2557 {
2558 if (dmd.status == 0) writeln("Compilation succeeded!");
2559 else writeln("Compilation failed");
2560 }
2561 else writeln("Still compiling...");
2562 ...
2563 ---
2564 Note that in this example, the first `wait` call will have no
2565 effect if the process has already terminated by the time `tryWait`
2566 is called. In the opposite case, however, the `scope` statement
2567 ensures that we always wait for the process if it hasn't terminated
2568 by the time we reach the end of the scope.
2569 */2570 autotryWait(Pidpid) @safe2571 {
2572 importstd.typecons : Tuple;
2573 assert(pid !isnull, "Called tryWait on a null Pid.");
2574 autocode = pid.performWait(false);
2575 returnTuple!(bool, "terminated", int, "status")(pid._processID == Pid.terminated, code);
2576 }
2577 // unittest: This function is tested together with kill() below.2578 2579 2580 /**
2581 Attempts to terminate the process associated with `pid`.
2582 2583 The effect of this function, as well as the meaning of `codeOrSignal`,
2584 is highly platform dependent. Details are given below. Common to all
2585 platforms is that this function only $(I initiates) termination of the process,
2586 and returns immediately. It does not wait for the process to end,
2587 nor does it guarantee that the process does in fact get terminated.
2588 2589 Always call $(LREF wait) to wait for a process to complete, even if `kill`
2590 has been called on it.
2591 2592 Windows_specific:
2593 The process will be
2594 $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms686714%28v=vs.100%29.aspx,
2595 forcefully and abruptly terminated). If `codeOrSignal` is specified, it
2596 must be a nonnegative number which will be used as the exit code of the process.
2597 If not, the process wil exit with code 1. Do not use $(D codeOrSignal = 259),
2598 as this is a special value (aka. $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms683189.aspx,STILL_ACTIVE))
2599 used by Windows to signal that a process has in fact $(I not) terminated yet.
2600 ---
2601 auto pid = spawnProcess("some_app");
2602 kill(pid, 10);
2603 assert(wait(pid) == 10);
2604 ---
2605 2606 POSIX_specific:
2607 A $(LINK2 http://en.wikipedia.org/wiki/Unix_signal,signal) will be sent to
2608 the process, whose value is given by `codeOrSignal`. Depending on the
2609 signal sent, this may or may not terminate the process. Symbolic constants
2610 for various $(LINK2 http://en.wikipedia.org/wiki/Unix_signal#POSIX_signals,
2611 POSIX signals) are defined in `core.sys.posix.signal`, which corresponds to the
2612 $(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html,
2613 `signal.h` POSIX header). If `codeOrSignal` is omitted, the
2614 `SIGTERM` signal will be sent. (This matches the behaviour of the
2615 $(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/utilities/kill.html,
2616 `_kill`) shell command.)
2617 ---
2618 import core.sys.posix.signal : SIGKILL;
2619 auto pid = spawnProcess("some_app");
2620 kill(pid, SIGKILL);
2621 assert(wait(pid) == -SIGKILL); // Negative return value on POSIX!
2622 ---
2623 2624 Throws:
2625 $(LREF ProcessException) on error (e.g. if codeOrSignal is invalid).
2626 or on attempt to kill detached process.
2627 Note that failure to terminate the process is considered a "normal"
2628 outcome, not an error.$(BR)
2629 */2630 voidkill(Pidpid)
2631 {
2632 version (Windows) kill(pid, 1);
2633 elseversion (Posix)
2634 {
2635 importcore.sys.posix.signal : SIGTERM;
2636 kill(pid, SIGTERM);
2637 }
2638 }
2639 2640 /// ditto2641 voidkill(Pidpid, intcodeOrSignal)
2642 {
2643 importstd.exception : enforce;
2644 enforce!ProcessException(pid.owned, "Can't kill detached process");
2645 version (Windows)
2646 {
2647 if (codeOrSignal < 0) thrownewProcessException("Invalid exit code");
2648 // On Windows, TerminateProcess() appears to terminate the2649 // *current* process if it is passed an invalid handle...2650 if (pid.osHandle == INVALID_HANDLE_VALUE)
2651 thrownewProcessException("Invalid process handle");
2652 if (!TerminateProcess(pid.osHandle, codeOrSignal))
2653 throwProcessException.newFromLastError();
2654 }
2655 elseversion (Posix)
2656 {
2657 importcore.sys.posix.signal : kill;
2658 if (kill(pid.osHandle, codeOrSignal) == -1)
2659 throwProcessException.newFromErrno();
2660 }
2661 }
2662 2663 @systemunittest// tryWait() and kill()2664 {
2665 importcore.thread;
2666 importstd.exception : assertThrown;
2667 // The test script goes into an infinite loop.2668 version (Windows)
2669 {
2670 TestScriptprog = ":loop
2671 goto loop";
2672 }
2673 elseversion (Posix)
2674 {
2675 importcore.sys.posix.signal : SIGTERM, SIGKILL;
2676 TestScriptprog = "while true; do sleep 1; done";
2677 }
2678 autopid = spawnProcess(prog.path);
2679 // Android appears to automatically kill sleeping processes very quickly,2680 // so shorten the wait before killing here.2681 version (Android)
2682 Thread.sleep(dur!"msecs"(5));
2683 else2684 Thread.sleep(dur!"msecs"(500));
2685 kill(pid);
2686 version (Windows) assert(wait(pid) == 1);
2687 elseversion (Posix) assert(wait(pid) == -SIGTERM);
2688 2689 pid = spawnProcess(prog.path);
2690 Thread.sleep(dur!"msecs"(500));
2691 autos = tryWait(pid);
2692 assert(!s.terminated && s.status == 0);
2693 assertThrown!ProcessException(kill(pid, -123)); // Negative code not allowed.2694 version (Windows) kill(pid, 123);
2695 elseversion (Posix) kill(pid, SIGKILL);
2696 do { s = tryWait(pid); } while (!s.terminated);
2697 version (Windows) assert(s.status == 123);
2698 elseversion (Posix) assert(s.status == -SIGKILL);
2699 assertThrown!ProcessException(kill(pid));
2700 }
2701 2702 @systemunittest// wait() and kill() detached process2703 {
2704 importcore.thread;
2705 importstd.exception : assertThrown;
2706 TestScriptprog = "exit 0";
2707 autopid = spawnProcess([prog.path], null, Config.detached);
2708 /*
2709 This sleep is needed because we can't wait() for detached process to end
2710 and therefore TestScript destructor may run at the same time as /bin/sh tries to start the script.
2711 This leads to the annoying message like "/bin/sh: 0: Can't open /tmp/std.process temporary file" to appear when running tests.
2712 It does not happen in unittests with non-detached processes because we always wait() for them to finish.
2713 */2714 Thread.sleep(500.msecs);
2715 assert(!pid.owned);
2716 version (Windows) assert(pid.osHandle == INVALID_HANDLE_VALUE);
2717 assertThrown!ProcessException(wait(pid));
2718 assertThrown!ProcessException(kill(pid));
2719 }
2720 2721 2722 /**
2723 Creates a unidirectional _pipe.
2724 2725 Data is written to one end of the _pipe and read from the other.
2726 ---
2727 auto p = pipe();
2728 p.writeEnd.writeln("Hello World");
2729 p.writeEnd.flush();
2730 assert(p.readEnd.readln().chomp() == "Hello World");
2731 ---
2732 Pipes can, for example, be used for interprocess communication
2733 by spawning a new process and passing one end of the _pipe to
2734 the child, while the parent uses the other end.
2735 (See also $(LREF pipeProcess) and $(LREF pipeShell) for an easier
2736 way of doing this.)
2737 ---
2738 // Use cURL to download the dlang.org front page, pipe its
2739 // output to grep to extract a list of links to ZIP files,
2740 // and write the list to the file "D downloads.txt":
2741 auto p = pipe();
2742 auto outFile = File("D downloads.txt", "w");
2743 auto cpid = spawnProcess(["curl", "http://dlang.org/download.html"],
2744 std.stdio.stdin, p.writeEnd);
2745 scope(exit) wait(cpid);
2746 auto gpid = spawnProcess(["grep", "-o", `http://\S*\.zip`],
2747 p.readEnd, outFile);
2748 scope(exit) wait(gpid);
2749 ---
2750 2751 Returns:
2752 A $(LREF Pipe) object that corresponds to the created _pipe.
2753 2754 Throws:
2755 $(REF StdioException, std,stdio) on failure.
2756 */2757 version (Posix)
2758 Pipepipe() @trusted//TODO: @safe2759 {
2760 importcore.sys.posix.stdio : fdopen;
2761 int[2] fds;
2762 if (core.sys.posix.unistd.pipe(fds) != 0)
2763 thrownewStdioException("Unable to create pipe");
2764 Pipep;
2765 autoreadFP = fdopen(fds[0], "r");
2766 if (readFP == null)
2767 thrownewStdioException("Cannot open read end of pipe");
2768 p._read = File(readFP, null);
2769 autowriteFP = fdopen(fds[1], "w");
2770 if (writeFP == null)
2771 thrownewStdioException("Cannot open write end of pipe");
2772 p._write = File(writeFP, null);
2773 returnp;
2774 }
2775 elseversion (Windows)
2776 Pipepipe() @trusted//TODO: @safe2777 {
2778 // use CreatePipe to create an anonymous pipe2779 HANDLEreadHandle;
2780 HANDLEwriteHandle;
2781 if (!CreatePipe(&readHandle, &writeHandle, null, 0))
2782 {
2783 thrownewStdioException(
2784 "Error creating pipe (" ~ generateSysErrorMsg() ~ ')',
2785 0);
2786 }
2787 2788 scope(failure)
2789 {
2790 CloseHandle(readHandle);
2791 CloseHandle(writeHandle);
2792 }
2793 2794 try2795 {
2796 Pipep;
2797 p._read .windowsHandleOpen(readHandle , "r");
2798 p._write.windowsHandleOpen(writeHandle, "a");
2799 returnp;
2800 }
2801 catch (Exceptione)
2802 {
2803 thrownewStdioException("Error attaching pipe (" ~ e.msg ~ ")",
2804 0);
2805 }
2806 }
2807 2808 2809 /// An interface to a pipe created by the $(LREF pipe) function.2810 structPipe2811 {
2812 /// The read end of the pipe.2813 @propertyFilereadEnd() @safenothrow { return_read; }
2814 2815 2816 /// The write end of the pipe.2817 @propertyFilewriteEnd() @safenothrow { return_write; }
2818 2819 2820 /**
2821 Closes both ends of the pipe.
2822 2823 Normally it is not necessary to do this manually, as $(REF File, std,stdio)
2824 objects are automatically closed when there are no more references
2825 to them.
2826 2827 Note that if either end of the pipe has been passed to a child process,
2828 it will only be closed in the parent process. (What happens in the
2829 child process is platform dependent.)
2830 2831 Throws:
2832 $(REF ErrnoException, std,exception) if an error occurs.
2833 */2834 voidclose() @safe2835 {
2836 _read.close();
2837 _write.close();
2838 }
2839 2840 private:
2841 File_read, _write;
2842 }
2843 2844 @systemunittest2845 {
2846 importstd.string;
2847 autop = pipe();
2848 p.writeEnd.writeln("Hello World");
2849 p.writeEnd.flush();
2850 assert(p.readEnd.readln().chomp() == "Hello World");
2851 p.close();
2852 assert(!p.readEnd.isOpen);
2853 assert(!p.writeEnd.isOpen);
2854 }
2855 2856 2857 /**
2858 Starts a new process, creating pipes to redirect its standard
2859 input, output and/or error streams.
2860 2861 `pipeProcess` and `pipeShell` are convenient wrappers around
2862 $(LREF spawnProcess) and $(LREF spawnShell), respectively, and
2863 automate the task of redirecting one or more of the child process'
2864 standard streams through pipes. Like the functions they wrap,
2865 these functions return immediately, leaving the child process to
2866 execute in parallel with the invoking process. It is recommended
2867 to always call $(LREF wait) on the returned $(LREF ProcessPipes.pid),
2868 as detailed in the documentation for `wait`.
2869 2870 The `args`/`program`/`command`, `env` and `config`
2871 parameters are forwarded straight to the underlying spawn functions,
2872 and we refer to their documentation for details.
2873 2874 Params:
2875 args = An array which contains the program name as the zeroth element
2876 and any command-line arguments in the following elements.
2877 (See $(LREF spawnProcess) for details.)
2878 program = The program name, $(I without) command-line arguments.
2879 (See $(LREF spawnProcess) for details.)
2880 command = A shell command which is passed verbatim to the command
2881 interpreter. (See $(LREF spawnShell) for details.)
2882 redirect = Flags that determine which streams are redirected, and
2883 how. See $(LREF Redirect) for an overview of available
2884 flags.
2885 env = Additional environment variables for the child process.
2886 (See $(LREF spawnProcess) for details.)
2887 config = Flags that control process creation. See $(LREF Config)
2888 for an overview of available flags, and note that the
2889 `retainStd...` flags have no effect in this function.
2890 workDir = The working directory for the new process.
2891 By default the child process inherits the parent's working
2892 directory.
2893 shellPath = The path to the shell to use to run the specified program.
2894 By default this is $(LREF nativeShell).
2895 2896 Returns:
2897 A $(LREF ProcessPipes) object which contains $(REF File, std,stdio)
2898 handles that communicate with the redirected streams of the child
2899 process, along with a $(LREF Pid) object that corresponds to the
2900 spawned process.
2901 2902 Throws:
2903 $(LREF ProcessException) on failure to start the process.$(BR)
2904 $(REF StdioException, std,stdio) on failure to redirect any of the streams.$(BR)
2905 2906 Example:
2907 ---
2908 // my_application writes to stdout and might write to stderr
2909 auto pipes = pipeProcess("my_application", Redirect.stdout | Redirect.stderr);
2910 scope(exit) wait(pipes.pid);
2911 2912 // Store lines of output.
2913 string[] output;
2914 foreach (line; pipes.stdout.byLine) output ~= line.idup;
2915 2916 // Store lines of errors.
2917 string[] errors;
2918 foreach (line; pipes.stderr.byLine) errors ~= line.idup;
2919 2920 2921 // sendmail expects to read from stdin
2922 pipes = pipeProcess(["/usr/bin/sendmail", "-t"], Redirect.stdin);
2923 pipes.stdin.writeln("To: you");
2924 pipes.stdin.writeln("From: me");
2925 pipes.stdin.writeln("Subject: dlang");
2926 pipes.stdin.writeln("");
2927 pipes.stdin.writeln(message);
2928 2929 // a single period tells sendmail we are finished
2930 pipes.stdin.writeln(".");
2931 2932 // but at this point sendmail might not see it, we need to flush
2933 pipes.stdin.flush();
2934 2935 // sendmail happens to exit on ".", but some you have to close the file:
2936 pipes.stdin.close();
2937 2938 // otherwise this wait will wait forever
2939 wait(pipes.pid);
2940 2941 ---
2942 */2943 ProcessPipespipeProcess(scopeconst(char[])[] args,
2944 Redirectredirect = Redirect.all,
2945 conststring[string] env = null,
2946 Configconfig = Config.none,
2947 scopeconst(char)[] workDir = null)
2948 @safe2949 {
2950 returnpipeProcessImpl!spawnProcess(args, redirect, env, config, workDir);
2951 }
2952 2953 /// ditto2954 ProcessPipespipeProcess(scopeconst(char)[] program,
2955 Redirectredirect = Redirect.all,
2956 conststring[string] env = null,
2957 Configconfig = Config.none,
2958 scopeconst(char)[] workDir = null)
2959 @safe2960 {
2961 returnpipeProcessImpl!spawnProcess(program, redirect, env, config, workDir);
2962 }
2963 2964 /// ditto2965 ProcessPipespipeShell(scopeconst(char)[] command,
2966 Redirectredirect = Redirect.all,
2967 conststring[string] env = null,
2968 Configconfig = Config.none,
2969 scopeconst(char)[] workDir = null,
2970 stringshellPath = nativeShell)
2971 @safe2972 {
2973 returnpipeProcessImpl!spawnShell(command,
2974 redirect,
2975 env,
2976 config,
2977 workDir,
2978 shellPath);
2979 }
2980 2981 // Implementation of the pipeProcess() family of functions.2982 privateProcessPipespipeProcessImpl(aliasspawnFunc, Cmd, ExtraSpawnFuncArgs...)
2983 (scopeCmdcommand,
2984 RedirectredirectFlags,
2985 conststring[string] env = null,
2986 Configconfig = Config.none,
2987 scopeconst(char)[] workDir = null,
2988 ExtraSpawnFuncArgsextraArgs = ExtraSpawnFuncArgs.init)
2989 @trusted//TODO: @safe2990 {
2991 FilechildStdin, childStdout, childStderr;
2992 ProcessPipespipes;
2993 pipes._redirectFlags = redirectFlags;
2994 2995 if (redirectFlags & Redirect.stdin)
2996 {
2997 autop = pipe();
2998 childStdin = p.readEnd;
2999 pipes._stdin = p.writeEnd;
3000 }
3001 else3002 {
3003 childStdin = std.stdio.stdin;
3004 }
3005 3006 if (redirectFlags & Redirect.stdout)
3007 {
3008 if ((redirectFlags & Redirect.stdoutToStderr) != 0)
3009 thrownewStdioException("Cannot create pipe for stdout AND "3010 ~"redirect it to stderr", 0);
3011 autop = pipe();
3012 childStdout = p.writeEnd;
3013 pipes._stdout = p.readEnd;
3014 }
3015 else3016 {
3017 childStdout = std.stdio.stdout;
3018 }
3019 3020 if (redirectFlags & Redirect.stderr)
3021 {
3022 if ((redirectFlags & Redirect.stderrToStdout) != 0)
3023 thrownewStdioException("Cannot create pipe for stderr AND "3024 ~"redirect it to stdout", 0);
3025 autop = pipe();
3026 childStderr = p.writeEnd;
3027 pipes._stderr = p.readEnd;
3028 }
3029 else3030 {
3031 childStderr = std.stdio.stderr;
3032 }
3033 3034 if (redirectFlags & Redirect.stdoutToStderr)
3035 {
3036 if (redirectFlags & Redirect.stderrToStdout)
3037 {
3038 // We know that neither of the other options have been3039 // set, so we assign the std.stdio.std* streams directly.3040 childStdout = std.stdio.stderr;
3041 childStderr = std.stdio.stdout;
3042 }
3043 else3044 {
3045 childStdout = childStderr;
3046 }
3047 }
3048 elseif (redirectFlags & Redirect.stderrToStdout)
3049 {
3050 childStderr = childStdout;
3051 }
3052 3053 config.flags &= ~(Config.Flags.retainStdin | Config.Flags.retainStdout | Config.Flags.retainStderr);
3054 pipes._pid = spawnFunc(command, childStdin, childStdout, childStderr,
3055 env, config, workDir, extraArgs);
3056 returnpipes;
3057 }
3058 3059 3060 /**
3061 Flags that can be passed to $(LREF pipeProcess) and $(LREF pipeShell)
3062 to specify which of the child process' standard streams are redirected.
3063 Use bitwise OR to combine flags.
3064 */3065 enumRedirect3066 {
3067 /// Redirect the standard input, output or error streams, respectively.3068 stdin = 1,
3069 stdout = 2, /// ditto3070 stderr = 4, /// ditto3071 3072 /**
3073 Redirect _all three streams. This is equivalent to
3074 $(D Redirect.stdin | Redirect.stdout | Redirect.stderr).
3075 */3076 all = stdin | stdout | stderr,
3077 3078 /**
3079 Redirect the standard error stream into the standard output stream.
3080 This can not be combined with `Redirect.stderr`.
3081 */3082 stderrToStdout = 8,
3083 3084 /**
3085 Redirect the standard output stream into the standard error stream.
3086 This can not be combined with `Redirect.stdout`.
3087 */3088 stdoutToStderr = 16,
3089 }
3090 3091 @systemunittest3092 {
3093 importstd.string;
3094 version (Windows) TestScriptprog =
3095 "call :sub %~1 %~2 0
3096 call :sub %~1 %~2 1
3097 call :sub %~1 %~2 2
3098 call :sub %~1 %~2 3
3099 exit 3
3100 3101 :sub
3102 set /p INPUT=
3103 if -%INPUT%-==-stop- ( exit %~3 )
3104 echo %INPUT% %~1
3105 echo %INPUT% %~2 1>&2";
3106 elseversion (Posix) TestScriptprog =
3107 `for EXITCODE in 0 1 2 3; do
3108 read INPUT
3109 if test "$INPUT" = stop; then break; fi
3110 echo "$INPUT $1"
3111 echo "$INPUT $2" >&2
3112 done
3113 exit $EXITCODE`;
3114 autopp = pipeProcess([prog.path, "bar", "baz"]);
3115 pp.stdin.writeln("foo");
3116 pp.stdin.flush();
3117 assert(pp.stdout.readln().chomp() == "foo bar");
3118 assert(pp.stderr.readln().chomp().stripRight() == "foo baz");
3119 pp.stdin.writeln("1234567890");
3120 pp.stdin.flush();
3121 assert(pp.stdout.readln().chomp() == "1234567890 bar");
3122 assert(pp.stderr.readln().chomp().stripRight() == "1234567890 baz");
3123 pp.stdin.writeln("stop");
3124 pp.stdin.flush();
3125 assert(wait(pp.pid) == 2);
3126 3127 pp = pipeProcess([prog.path, "12345", "67890"],
3128 Redirect.stdin | Redirect.stdout | Redirect.stderrToStdout);
3129 pp.stdin.writeln("xyz");
3130 pp.stdin.flush();
3131 assert(pp.stdout.readln().chomp() == "xyz 12345");
3132 assert(pp.stdout.readln().chomp().stripRight() == "xyz 67890");
3133 pp.stdin.writeln("stop");
3134 pp.stdin.flush();
3135 assert(wait(pp.pid) == 1);
3136 3137 pp = pipeShell(escapeShellCommand(prog.path, "AAAAA", "BBB"),
3138 Redirect.stdin | Redirect.stdoutToStderr | Redirect.stderr);
3139 pp.stdin.writeln("ab");
3140 pp.stdin.flush();
3141 assert(pp.stderr.readln().chomp() == "ab AAAAA");
3142 assert(pp.stderr.readln().chomp().stripRight() == "ab BBB");
3143 pp.stdin.writeln("stop");
3144 pp.stdin.flush();
3145 assert(wait(pp.pid) == 1);
3146 }
3147 3148 @systemunittest3149 {
3150 importstd.exception : assertThrown;
3151 TestScriptprog = "exit 0";
3152 assertThrown!StdioException(pipeProcess(
3153 prog.path,
3154 Redirect.stdout | Redirect.stdoutToStderr));
3155 assertThrown!StdioException(pipeProcess(
3156 prog.path,
3157 Redirect.stderr | Redirect.stderrToStdout));
3158 autop = pipeProcess(prog.path, Redirect.stdin);
3159 assertThrown!Error(p.stdout);
3160 assertThrown!Error(p.stderr);
3161 wait(p.pid);
3162 p = pipeProcess(prog.path, Redirect.stderr);
3163 assertThrown!Error(p.stdin);
3164 assertThrown!Error(p.stdout);
3165 wait(p.pid);
3166 }
3167 3168 /**
3169 Object which contains $(REF File, std,stdio) handles that allow communication
3170 with a child process through its standard streams.
3171 */3172 structProcessPipes3173 {
3174 /// The $(LREF Pid) of the child process.3175 @propertyPidpid() @safenothrow3176 {
3177 return_pid;
3178 }
3179 3180 /**
3181 An $(REF File, std,stdio) that allows writing to the child process'
3182 standard input stream.
3183 3184 Throws:
3185 $(OBJECTREF Error) if the child process' standard input stream hasn't
3186 been redirected.
3187 */3188 @propertyFilestdin() @safenothrow3189 {
3190 if ((_redirectFlags & Redirect.stdin) == 0)
3191 thrownewError("Child process' standard input stream hasn't "3192 ~"been redirected.");
3193 return_stdin;
3194 }
3195 3196 /**
3197 An $(REF File, std,stdio) that allows reading from the child process'
3198 standard output stream.
3199 3200 Throws:
3201 $(OBJECTREF Error) if the child process' standard output stream hasn't
3202 been redirected.
3203 */3204 @propertyFilestdout() @safenothrow3205 {
3206 if ((_redirectFlags & Redirect.stdout) == 0)
3207 thrownewError("Child process' standard output stream hasn't "3208 ~"been redirected.");
3209 return_stdout;
3210 }
3211 3212 /**
3213 An $(REF File, std,stdio) that allows reading from the child process'
3214 standard error stream.
3215 3216 Throws:
3217 $(OBJECTREF Error) if the child process' standard error stream hasn't
3218 been redirected.
3219 */3220 @propertyFilestderr() @safenothrow3221 {
3222 if ((_redirectFlags & Redirect.stderr) == 0)
3223 thrownewError("Child process' standard error stream hasn't "3224 ~"been redirected.");
3225 return_stderr;
3226 }
3227 3228 private:
3229 Redirect_redirectFlags;
3230 Pid_pid;
3231 File_stdin, _stdout, _stderr;
3232 }
3233 3234 3235 3236 /**
3237 Executes the given program or shell command and returns its exit
3238 code and output.
3239 3240 `execute` and `executeShell` start a new process using
3241 $(LREF spawnProcess) and $(LREF spawnShell), respectively, and wait
3242 for the process to complete before returning. The functions capture
3243 what the child process prints to both its standard output and
3244 standard error streams, and return this together with its exit code.
3245 ---
3246 auto dmd = execute(["dmd", "myapp.d"]);
3247 if (dmd.status != 0) writeln("Compilation failed:\n", dmd.output);
3248 3249 auto ls = executeShell("ls -l");
3250 if (ls.status != 0) writeln("Failed to retrieve file listing");
3251 else writeln(ls.output);
3252 ---
3253 3254 The `args`/`program`/`command`, `env` and `config`
3255 parameters are forwarded straight to the underlying spawn functions,
3256 and we refer to their documentation for details.
3257 3258 Params:
3259 args = An array which contains the program name as the zeroth element
3260 and any command-line arguments in the following elements.
3261 (See $(LREF spawnProcess) for details.)
3262 program = The program name, $(I without) command-line arguments.
3263 (See $(LREF spawnProcess) for details.)
3264 command = A shell command which is passed verbatim to the command
3265 interpreter. (See $(LREF spawnShell) for details.)
3266 env = Additional environment variables for the child process.
3267 (See $(LREF spawnProcess) for details.)
3268 config = Flags that control process creation. See $(LREF Config)
3269 for an overview of available flags, and note that the
3270 `retainStd...` flags have no effect in this function.
3271 maxOutput = The maximum number of bytes of output that should be
3272 captured.
3273 workDir = The working directory for the new process.
3274 By default the child process inherits the parent's working
3275 directory.
3276 shellPath = The path to the shell to use to run the specified program.
3277 By default this is $(LREF nativeShell).
3278 3279 3280 Returns:
3281 An $(D std.typecons.Tuple!(int, "status", string, "output")).
3282 3283 POSIX_specific:
3284 If the process is terminated by a signal, the `status` field of
3285 the return value will contain a negative number whose absolute
3286 value is the signal number. (See $(LREF wait) for details.)
3287 3288 Throws:
3289 $(LREF ProcessException) on failure to start the process.$(BR)
3290 $(REF StdioException, std,stdio) on failure to capture output.
3291 */3292 autoexecute(scopeconst(char[])[] args,
3293 conststring[string] env = null,
3294 Configconfig = Config.none,
3295 size_tmaxOutput = size_t.max,
3296 scopeconst(char)[] workDir = null)
3297 @safe3298 {
3299 returnexecuteImpl!pipeProcess(args, env, config, maxOutput, workDir);
3300 }
3301 3302 /// ditto3303 autoexecute(scopeconst(char)[] program,
3304 conststring[string] env = null,
3305 Configconfig = Config.none,
3306 size_tmaxOutput = size_t.max,
3307 scopeconst(char)[] workDir = null)
3308 @safe3309 {
3310 returnexecuteImpl!pipeProcess(program, env, config, maxOutput, workDir);
3311 }
3312 3313 /// ditto3314 autoexecuteShell(scopeconst(char)[] command,
3315 conststring[string] env = null,
3316 Configconfig = Config.none,
3317 size_tmaxOutput = size_t.max,
3318 scopeconst(char)[] workDir = null,
3319 stringshellPath = nativeShell)
3320 @safe3321 {
3322 returnexecuteImpl!pipeShell(command,
3323 env,
3324 config,
3325 maxOutput,
3326 workDir,
3327 shellPath);
3328 }
3329 3330 // Does the actual work for execute() and executeShell().3331 privateautoexecuteImpl(aliaspipeFunc, Cmd, ExtraPipeFuncArgs...)(
3332 CmdcommandLine,
3333 conststring[string] env = null,
3334 Configconfig = Config.none,
3335 size_tmaxOutput = size_t.max,
3336 scopeconst(char)[] workDir = null,
3337 ExtraPipeFuncArgsextraArgs = ExtraPipeFuncArgs.init)
3338 @trusted//TODO: @safe3339 {
3340 importstd.algorithm.comparison : min;
3341 importstd.array : appender;
3342 importstd.typecons : Tuple;
3343 3344 autoredirect = (config.flags & Config.Flags.stderrPassThrough)
3345 ? Redirect.stdout3346 : Redirect.stdout | Redirect.stderrToStdout;
3347 3348 autop = pipeFunc(commandLine, redirect,
3349 env, config, workDir, extraArgs);
3350 3351 autoa = appender!string;
3352 enumsize_tdefaultChunkSize = 4096;
3353 immutablechunkSize = min(maxOutput, defaultChunkSize);
3354 3355 // Store up to maxOutput bytes in a.3356 foreach (ubyte[] chunk; p.stdout.byChunk(chunkSize))
3357 {
3358 immutablesize_tremain = maxOutput - a.data.length;
3359 3360 if (chunk.length < remain) a.put(chunk);
3361 else3362 {
3363 a.put(chunk[0 .. remain]);
3364 break;
3365 }
3366 }
3367 // Exhaust the stream, if necessary.3368 foreach (ubyte[] chunk; p.stdout.byChunk(defaultChunkSize)) { }
3369 3370 returnTuple!(int, "status", string, "output")(wait(p.pid), a.data);
3371 }
3372 3373 @systemunittest3374 {
3375 importstd.string;
3376 // To avoid printing the newline characters, we use the echo|set trick on3377 // Windows, and printf on POSIX (neither echo -n nor echo \c are portable).3378 version (Windows) TestScriptprog =
3379 "echo|set /p=%~1
3380 echo|set /p=%~2 1>&2
3381 exit 123";
3382 elseversion (Android) TestScriptprog =
3383 `echo -n $1
3384 echo -n $2 >&2
3385 exit 123`;
3386 elseversion (Posix) TestScriptprog =
3387 `printf '%s' $1
3388 printf '%s' $2 >&2
3389 exit 123`;
3390 autor = execute([prog.path, "foo", "bar"]);
3391 assert(r.status == 123);
3392 assert(r.output.stripRight() == "foobar");
3393 autos = execute([prog.path, "Hello", "World"]);
3394 assert(s.status == 123);
3395 assert(s.output.stripRight() == "HelloWorld");
3396 }
3397 3398 @safeunittest3399 {
3400 importstd.string;
3401 autor1 = executeShell("echo foo");
3402 assert(r1.status == 0);
3403 assert(r1.output.chomp() == "foo");
3404 autor2 = executeShell("echo bar 1>&2");
3405 assert(r2.status == 0);
3406 assert(r2.output.chomp().stripRight() == "bar");
3407 autor3 = executeShell("exit 123");
3408 assert(r3.status == 123);
3409 assert(r3.output.empty);
3410 }
3411 3412 @systemunittest3413 {
3414 // Temporarily disable output to stderr so as to not spam the build log.3415 importstd.stdio : stderr;
3416 importstd.typecons : Tuple;
3417 importstd.file : readText, exists, remove;
3418 importstd.traits : ReturnType;
3419 3420 ReturnType!executeShellr;
3421 autotmpname = uniqueTempPath;
3422 scope(exit) if (exists(tmpname)) remove(tmpname);
3423 autot = stderr;
3424 // Open a new scope to minimize code ran with stderr redirected.3425 {
3426 stderr.open(tmpname, "w");
3427 scope(exit) stderr = t;
3428 r = executeShell("echo D rox>&2", null, Config.stderrPassThrough);
3429 }
3430 assert(r.status == 0);
3431 assert(r.output.empty);
3432 autowitness = readText(tmpname);
3433 importstd.ascii : newline;
3434 assert(witness == "D rox" ~ newline, "'" ~ witness ~ "'");
3435 }
3436 3437 @safeunittest3438 {
3439 importstd.typecons : Tuple;
3440 voidfoo() //Just test the compilation3441 {
3442 autoret1 = execute(["dummy", "arg"]);
3443 autoret2 = executeShell("dummy arg");
3444 staticassert(is(typeof(ret1) == typeof(ret2)));
3445 3446 Tuple!(int, string) ret3 = execute(["dummy", "arg"]);
3447 }
3448 }
3449 3450 /// An exception that signals a problem with starting or waiting for a process.3451 classProcessException : Exception3452 {
3453 importstd.exception : basicExceptionCtors;
3454 mixinbasicExceptionCtors;
3455 3456 // Creates a new ProcessException based on errno.3457 staticProcessExceptionnewFromErrno(stringcustomMsg = null,
3458 stringfile = __FILE__,
3459 size_tline = __LINE__)
3460 {
3461 importcore.stdc.errno : errno;
3462 returnnewFromErrno(errno, customMsg, file, line);
3463 }
3464 3465 // ditto, but error number is provided by caller3466 staticProcessExceptionnewFromErrno(interror,
3467 stringcustomMsg = null,
3468 stringfile = __FILE__,
3469 size_tline = __LINE__)
3470 {
3471 importstd.exception : errnoString;
3472 autoerrnoMsg = errnoString(error);
3473 automsg = customMsg.empty ? errnoMsg3474 : customMsg ~ " (" ~ errnoMsg ~ ')';
3475 returnnewProcessException(msg, file, line);
3476 }
3477 3478 // Creates a new ProcessException based on GetLastError() (Windows only).3479 version (Windows)
3480 staticProcessExceptionnewFromLastError(stringcustomMsg = null,
3481 stringfile = __FILE__,
3482 size_tline = __LINE__)
3483 {
3484 autolastMsg = generateSysErrorMsg();
3485 automsg = customMsg.empty ? lastMsg3486 : customMsg ~ " (" ~ lastMsg ~ ')';
3487 returnnewProcessException(msg, file, line);
3488 }
3489 }
3490 3491 3492 /**
3493 Determines the path to the current user's preferred command interpreter.
3494 3495 On Windows, this function returns the contents of the COMSPEC environment
3496 variable, if it exists. Otherwise, it returns the result of $(LREF nativeShell).
3497 3498 On POSIX, `userShell` returns the contents of the SHELL environment
3499 variable, if it exists and is non-empty. Otherwise, it returns the result of
3500 $(LREF nativeShell).
3501 */3502 @propertystringuserShell() @safe3503 {
3504 version (Windows) returnenvironment.get("COMSPEC", nativeShell);
3505 elseversion (Posix) returnenvironment.get("SHELL", nativeShell);
3506 }
3507 3508 /**
3509 The platform-specific native shell path.
3510 3511 This function returns `"cmd.exe"` on Windows, `"/bin/sh"` on POSIX, and
3512 `"/system/bin/sh"` on Android.
3513 */3514 @propertystringnativeShell() @safe @nogcpurenothrow3515 {
3516 version (Windows) return"cmd.exe";
3517 elseversion (Android) return"/system/bin/sh";
3518 elseversion (Posix) return"/bin/sh";
3519 }
3520 3521 // A command-line switch that indicates to the shell that it should3522 // interpret the following argument as a command to be executed.3523 version (Posix) privateimmutablestringshellSwitch = "-c";
3524 version (Windows) privateimmutablestringshellSwitch = "/C";
3525 3526 // Unittest support code: TestScript takes a string that contains a3527 // shell script for the current platform, and writes it to a temporary3528 // file. On Windows the file name gets a .cmd extension, while on3529 // POSIX its executable permission bit is set. The file is3530 // automatically deleted when the object goes out of scope.3531 version (StdUnittest)
3532 privatestructTestScript3533 {
3534 this(stringcode) @system3535 {
3536 // @system due to chmod3537 importstd.ascii : newline;
3538 importstd.file : write;
3539 version (Windows)
3540 {
3541 autoext = ".cmd";
3542 autofirstLine = "@echo off";
3543 }
3544 elseversion (Posix)
3545 {
3546 autoext = "";
3547 autofirstLine = "#!" ~ nativeShell;
3548 }
3549 path = uniqueTempPath()~ext;
3550 write(path, firstLine ~ newline ~ code ~ newline);
3551 version (Posix)
3552 {
3553 importcore.sys.posix.sys.stat : chmod;
3554 importstd.conv : octal;
3555 chmod(path.tempCString(), octal!777);
3556 }
3557 }
3558 3559 ~this()
3560 {
3561 importstd.file : remove, exists;
3562 if (!path.empty && exists(path))
3563 {
3564 try { remove(path); }
3565 catch (Exceptione)
3566 {
3567 debugstd.stdio.stderr.writeln(e.msg);
3568 }
3569 }
3570 }
3571 3572 stringpath;
3573 }
3574 3575 3576 // =============================================================================3577 // Functions for shell command quoting/escaping.3578 // =============================================================================3579 3580 3581 /*
3582 Command line arguments exist in three forms:
3583 1) string or char* array, as received by main.
3584 Also used internally on POSIX systems.
3585 2) Command line string, as used in Windows'
3586 CreateProcess and CommandLineToArgvW functions.
3587 A specific quoting and escaping algorithm is used
3588 to distinguish individual arguments.
3589 3) Shell command string, as written at a shell prompt
3590 or passed to cmd /C - this one may contain shell
3591 control characters, e.g. > or | for redirection /
3592 piping - thus, yet another layer of escaping is
3593 used to distinguish them from program arguments.
3594 3595 Except for escapeWindowsArgument, the intermediary
3596 format (2) is hidden away from the user in this module.
3597 */3598 3599 /**
3600 Escapes an argv-style argument array to be used with $(LREF spawnShell),
3601 $(LREF pipeShell) or $(LREF executeShell).
3602 ---
3603 string url = "http://dlang.org/";
3604 executeShell(escapeShellCommand("wget", url, "-O", "dlang-index.html"));
3605 ---
3606 3607 Concatenate multiple `escapeShellCommand` and
3608 $(LREF escapeShellFileName) results to use shell redirection or
3609 piping operators.
3610 ---
3611 executeShell(
3612 escapeShellCommand("curl", "http://dlang.org/download.html") ~
3613 "|" ~
3614 escapeShellCommand("grep", "-o", `http://\S*\.zip`) ~
3615 ">" ~
3616 escapeShellFileName("D download links.txt"));
3617 ---
3618 3619 Throws:
3620 $(OBJECTREF Exception) if any part of the command line contains unescapable
3621 characters (NUL on all platforms, as well as CR and LF on Windows).
3622 */3623 stringescapeShellCommand(scopeconst(char[])[] args...) @safepure3624 {
3625 if (args.empty)
3626 returnnull;
3627 version (Windows)
3628 {
3629 // Do not ^-escape the first argument (the program path),3630 // as the shell parses it differently from parameters.3631 // ^-escaping a program path that contains spaces will fail.3632 stringresult = escapeShellFileName(args[0]);
3633 if (args.length > 1)
3634 {
3635 result ~= " " ~ escapeShellCommandString(
3636 escapeShellArguments(args[1..$]));
3637 }
3638 returnresult;
3639 }
3640 version (Posix)
3641 {
3642 returnescapeShellCommandString(escapeShellArguments(args));
3643 }
3644 }
3645 3646 @safeunittest3647 {
3648 // This is a simple unit test without any special requirements,3649 // in addition to the unittest_burnin one below which requires3650 // special preparation.3651 3652 structTestVector { string[] args; stringwindows, posix; }
3653 TestVector[] tests =
3654 [
3655 {
3656 args : ["foo bar"],
3657 windows : `"foo bar"`,
3658 posix : `'foo bar'`3659 },
3660 {
3661 args : ["foo bar", "hello"],
3662 windows : `"foo bar" hello`,
3663 posix : `'foo bar' hello`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", "hello", "world"],
3672 windows : `"foo bar" hello world`,
3673 posix : `'foo bar' hello world`3674 },
3675 {
3676 args : ["foo bar", `'"^\`],
3677 windows : `"foo bar" ^"'\^"^^\\^"`,
3678 posix : `'foo bar' ''\''"^\'`3679 },
3680 {
3681 args : ["foo bar", ""],
3682 windows : `"foo bar" ^"^"`,
3683 posix : `'foo bar' ''`3684 },
3685 {
3686 args : ["foo bar", "2"],
3687 windows : `"foo bar" ^"2^"`,
3688 posix : `'foo bar' '2'`3689 },
3690 ];
3691 3692 foreach (test; tests)
3693 {
3694 autoactual = escapeShellCommand(test.args);
3695 version (Windows)
3696 stringexpected = test.windows;
3697 else3698 stringexpected = test.posix;
3699 assert(actual == expected, "\nExpected: " ~ expected ~ "\nGot: " ~ actual);
3700 }
3701 }
3702 3703 privatestringescapeShellCommandString(returnscopestringcommand) @safepure3704 {
3705 version (Windows)
3706 returnescapeWindowsShellCommand(command);
3707 else3708 returncommand;
3709 }
3710 3711 privatestringescapeWindowsShellCommand(scopeconst(char)[] command) @safepure3712 {
3713 importstd.array : appender;
3714 autoresult = appender!string();
3715 result.reserve(command.length);
3716 3717 foreach (c; command)
3718 switch (c)
3719 {
3720 case'\0':
3721 thrownewException("Cannot put NUL in command line");
3722 case'\r':
3723 case'\n':
3724 thrownewException("CR/LF are not escapable");
3725 case'\x01': .. case'\x09':
3726 case'\x0B': .. case'\x0C':
3727 case'\x0E': .. case'\x1F':
3728 case'"':
3729 case'^':
3730 case'&':
3731 case'<':
3732 case'>':
3733 case'|':
3734 result.put('^');
3735 gotodefault;
3736 default:
3737 result.put(c);
3738 }
3739 returnresult.data;
3740 }
3741 3742 privatestringescapeShellArguments(scopeconst(char[])[] args...)
3743 @trustedpurenothrow3744 {
3745 importstd.exception : assumeUnique;
3746 char[] buf;
3747 3748 @safenothrow3749 char[] allocator(size_tsize)
3750 {
3751 if (buf.length == 0)
3752 returnbuf = newchar[size];
3753 else3754 {
3755 autop = buf.length;
3756 buf.length = buf.length + 1 + size;
3757 buf[p++] = ' ';
3758 returnbuf[p .. p+size];
3759 }
3760 }
3761 3762 foreach (arg; args)
3763 escapeShellArgument!allocator(arg);
3764 returnassumeUnique(buf);
3765 }
3766 3767 privateautoescapeShellArgument(aliasallocator)(scopeconst(char)[] arg) @safenothrow3768 {
3769 // The unittest for this function requires special3770 // preparation - see below.3771 3772 version (Windows)
3773 returnescapeWindowsArgumentImpl!allocator(arg);
3774 else3775 returnescapePosixArgumentImpl!allocator(arg);
3776 }
3777 3778 /**
3779 Quotes a command-line argument in a manner conforming to the behavior of
3780 $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx,
3781 CommandLineToArgvW).
3782 */3783 stringescapeWindowsArgument(scopeconst(char)[] arg) @trustedpurenothrow3784 {
3785 // Rationale for leaving this function as public:3786 // this algorithm of escaping paths is also used in other software,3787 // e.g. DMD's response files.3788 importstd.exception : assumeUnique;
3789 autobuf = escapeWindowsArgumentImpl!charAllocator(arg);
3790 returnassumeUnique(buf);
3791 }
3792 3793 3794 privatechar[] charAllocator(size_tsize) @safepurenothrow3795 {
3796 returnnewchar[size];
3797 }
3798 3799 3800 privatechar[] escapeWindowsArgumentImpl(aliasallocator)(scopeconst(char)[] arg)
3801 @safenothrow3802 if (is(typeof(allocator(size_t.init)[0] = char.init)))
3803 {
3804 // References:3805 // * http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx3806 // * http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx3807 3808 // Check if the string needs to be escaped,3809 // and calculate the total string size.3810 3811 // Trailing backslashes must be escaped3812 boolescaping = true;
3813 boolneedEscape = false;
3814 // Result size = input size + 2 for surrounding quotes + 1 for the3815 // backslash for each escaped character.3816 size_tsize = 1 + arg.length + 1;
3817 3818 foreach_reverse (charc; arg)
3819 {
3820 if (c == '"')
3821 {
3822 needEscape = true;
3823 escaping = true;
3824 size++;
3825 }
3826 else3827 if (c == '\\')
3828 {
3829 if (escaping)
3830 size++;
3831 }
3832 else3833 {
3834 if (c == ' ' || c == '\t')
3835 needEscape = true;
3836 escaping = false;
3837 }
3838 }
3839 3840 importstd.ascii : isDigit;
3841 // Empty arguments need to be specified as ""3842 if (!arg.length)
3843 needEscape = true;
3844 else3845 // Arguments ending with digits need to be escaped,3846 // to disambiguate with 1>file redirection syntax3847 if (isDigit(arg[$-1]))
3848 needEscape = true;
3849 3850 if (!needEscape)
3851 returnallocator(arg.length)[] = arg;
3852 3853 // Construct result string.3854 3855 autobuf = allocator(size);
3856 size_tp = size;
3857 buf[--p] = '"';
3858 escaping = true;
3859 foreach_reverse (charc; arg)
3860 {
3861 if (c == '"')
3862 escaping = true;
3863 else3864 if (c != '\\')
3865 escaping = false;
3866 3867 buf[--p] = c;
3868 if (escaping)
3869 buf[--p] = '\\';
3870 }
3871 buf[--p] = '"';
3872 assert(p == 0);
3873 3874 returnbuf;
3875 }
3876 3877 version (Windows) version (StdUnittest)
3878 {
3879 private:
3880 importcore.stdc.stddef;
3881 importcore.stdc.wchar_ : wcslen;
3882 importcore.sys.windows.shellapi : CommandLineToArgvW;
3883 importcore.sys.windows.winbase;
3884 importcore.sys.windows.winnt;
3885 importstd.array;
3886 3887 string[] parseCommandLine(stringline)
3888 {
3889 importstd.algorithm.iteration : map;
3890 importstd.array : array;
3891 importstd.conv : to;
3892 autolpCommandLine = (to!(WCHAR[])(line) ~ '\0').ptr;
3893 intnumArgs;
3894 autoargs = CommandLineToArgvW(lpCommandLine, &numArgs);
3895 scope(exit) LocalFree(args);
3896 returnargs[0 .. numArgs]
3897 .map!(arg => to!string(arg[0 .. wcslen(arg)]))
3898 .array();
3899 }
3900 3901 @systemunittest3902 {
3903 importstd.conv : text;
3904 string[] testStrings = [
3905 `Hello`,
3906 `Hello, world`,
3907 `Hello, "world"`,
3908 `C:\`,
3909 `C:\dmd`,
3910 `C:\Program Files\`,
3911 ];
3912 3913 enumCHARS = `_x\" *&^` ~ "\t"; // _ is placeholder for nothing3914 foreach (c1; CHARS)
3915 foreach (c2; CHARS)
3916 foreach (c3; CHARS)
3917 foreach (c4; CHARS)
3918 testStrings ~= [c1, c2, c3, c4].replace("_", "");
3919 3920 foreach (s; testStrings)
3921 {
3922 autoq = escapeWindowsArgument(s);
3923 autoargs = parseCommandLine("Dummy.exe " ~ q);
3924 assert(args.length == 2, s ~ " => " ~ q ~ " #" ~ text(args.length-1));
3925 assert(args[1] == s, s ~ " => " ~ q ~ " => " ~ args[1]);
3926 }
3927 }
3928 }
3929 3930 privatestringescapePosixArgument(scopeconst(char)[] arg) @trustedpurenothrow3931 {
3932 importstd.exception : assumeUnique;
3933 autobuf = escapePosixArgumentImpl!charAllocator(arg);
3934 returnassumeUnique(buf);
3935 }
3936 3937 privatechar[] escapePosixArgumentImpl(aliasallocator)(scopeconst(char)[] arg)
3938 @safenothrow3939 if (is(typeof(allocator(size_t.init)[0] = char.init)))
3940 {
3941 boolneedQuoting = {
3942 importstd.ascii : isAlphaNum, isDigit;
3943 importstd.algorithm.comparison : among;
3944 3945 // Empty arguments need to be specified as ''3946 if (arg.length == 0)
3947 returntrue;
3948 // Arguments ending with digits need to be escaped,3949 // to disambiguate with 1>file redirection syntax3950 if (isDigit(arg[$-1]))
3951 returntrue;
3952 3953 // Obtained using:3954 // for n in $(seq 1 255) ; do3955 // c=$(printf \\$(printf "%o" $n))3956 // q=$(/bin/printf '%q' "$c")3957 // if [[ "$q" == "$c" ]] ; then printf "%s, " "'$c'" ; fi3958 // done3959 // printf '\n'3960 foreach (charc; arg)
3961 if (!isAlphaNum(c) && !c.among('%', '+', ',', '-', '.', '/', ':', '@', ']', '_'))
3962 returntrue;
3963 returnfalse;
3964 }();
3965 if (!needQuoting)
3966 {
3967 autobuf = allocator(arg.length);
3968 buf[] = arg;
3969 returnbuf;
3970 }
3971 3972 // '\'' means: close quoted part of argument, append an escaped3973 // single quote, and reopen quotes3974 3975 // Below code is equivalent to:3976 // return `'` ~ std.array.replace(arg, `'`, `'\''`) ~ `'`;3977 3978 size_tsize = 1 + arg.length + 1;
3979 foreach (charc; arg)
3980 if (c == '\'')
3981 size += 3;
3982 3983 autobuf = allocator(size);
3984 size_tp = 0;
3985 buf[p++] = '\'';
3986 foreach (charc; arg)
3987 if (c == '\'')
3988 {
3989 buf[p .. p+4] = `'\''`;
3990 p += 4;
3991 }
3992 else3993 buf[p++] = c;
3994 buf[p++] = '\'';
3995 assert(p == size);
3996 3997 returnbuf;
3998 }
3999 4000 /**
4001 Escapes a filename to be used for shell redirection with $(LREF spawnShell),
4002 $(LREF pipeShell) or $(LREF executeShell).
4003 */4004 stringescapeShellFileName(scopeconst(char)[] fileName) @trustedpurenothrow4005 {
4006 // The unittest for this function requires special4007 // preparation - see below.4008 4009 version (Windows)
4010 {
4011 // If a file starts with &, it can cause cmd.exe to misinterpret4012 // the file name as the stream redirection syntax:4013 // command > "&foo.txt"4014 // gets interpreted as4015 // command >&foo.txt4016 // Prepend .\ to disambiguate.4017 4018 if (fileName.length && fileName[0] == '&')
4019 returncast(string)(`".\` ~ fileName ~ '"');
4020 4021 returncast(string)('"' ~ fileName ~ '"');
4022 }
4023 else4024 returnescapePosixArgument(fileName);
4025 }
4026 4027 // Loop generating strings with random characters4028 //version = unittest_burnin;4029 4030 version (unittest_burnin)
4031 @systemunittest4032 {
4033 // There are no readily-available commands on all platforms suitable4034 // for properly testing command escaping. The behavior of CMD's "echo"4035 // built-in differs from the POSIX program, and Windows ports of POSIX4036 // environments (Cygwin, msys, gnuwin32) may interfere with their own4037 // "echo" ports.4038 4039 // To run this unit test, create std_process_unittest_helper.d with the4040 // following content and compile it:4041 // import std.stdio, std.array; void main(string[] args) { write(args.join("\0")); }4042 // Then, test this module with:4043 // rdmd --main -unittest -version=unittest_burnin process.d4044 4045 importstd.file : readText, remove;
4046 importstd.format : format;
4047 importstd.path : absolutePath;
4048 importstd.random : uniform;
4049 4050 autohelper = absolutePath("std_process_unittest_helper");
4051 assert(executeShell(helper ~ " hello").output.split("\0")[1..$] == ["hello"], "Helper malfunction");
4052 4053 voidtest(string[] s, stringfn)
4054 {
4055 stringe;
4056 string[] g;
4057 4058 e = escapeShellCommand(helper ~ s);
4059 {
4060 scope(failure) writefln("executeShell() failed.\nExpected:\t%s\nEncoded:\t%s", s, [e]);
4061 autoresult = executeShell(e);
4062 assert(result.status == 0, "std_process_unittest_helper failed");
4063 g = result.output.split("\0")[1..$];
4064 }
4065 assert(s == g, format("executeShell() test failed.\nExpected:\t%s\nGot:\t\t%s\nEncoded:\t%s", s, g, [e]));
4066 4067 e = escapeShellCommand(helper ~ s) ~ ">" ~ escapeShellFileName(fn);
4068 {
4069 scope(failure) writefln(
4070 "executeShell() with redirect failed.\nExpected:\t%s\nFilename:\t%s\nEncoded:\t%s", s, [fn], [e]);
4071 autoresult = executeShell(e);
4072 assert(result.status == 0, "std_process_unittest_helper failed");
4073 assert(!result.output.length, "No output expected, got:\n" ~ result.output);
4074 g = readText(fn).split("\0")[1..$];
4075 }
4076 remove(fn);
4077 assert(s == g,
4078 format("executeShell() with redirect test failed.\nExpected:\t%s\nGot:\t\t%s\nEncoded:\t%s", s, g, [e]));
4079 }
4080 4081 while (true)
4082 {
4083 string[] args;
4084 foreach (n; 0 .. uniform(1, 4))
4085 {
4086 stringarg;
4087 foreach (l; 0 .. uniform(0, 10))
4088 {
4089 dcharc;
4090 while (true)
4091 {
4092 version (Windows)
4093 {
4094 // As long as DMD's system() uses CreateProcessA,4095 // we can't reliably pass Unicode4096 c = uniform(0, 128);
4097 }
4098 else4099 c = uniform!ubyte();
4100 4101 if (c == 0)
4102 continue; // argv-strings are zero-terminated4103 version (Windows)
4104 if (c == '\r' || c == '\n')
4105 continue; // newlines are unescapable on Windows4106 break;
4107 }
4108 arg ~= c;
4109 }
4110 args ~= arg;
4111 }
4112 4113 // generate filename4114 stringfn;
4115 foreach (l; 0 .. uniform(1, 10))
4116 {
4117 dcharc;
4118 while (true)
4119 {
4120 version (Windows)
4121 c = uniform(0, 128); // as above4122 else4123 c = uniform!ubyte();
4124 4125 if (c == 0 || c == '/')
4126 continue; // NUL and / are the only characters4127 // forbidden in POSIX filenames4128 version (Windows)
4129 if (c < '\x20' || c == '<' || c == '>' || c == ':' ||
4130 c == '"' || c == '\\' || c == '|' || c == '?' || c == '*')
4131 continue; // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx4132 break;
4133 }
4134 4135 fn ~= c;
4136 }
4137 fn = fn[0..$/2] ~ "_testfile_" ~ fn[$/2..$];
4138 4139 test(args, fn);
4140 }
4141 }
4142 4143 // =============================================================================4144 // Everything below this line was part of the old std.process, and most of4145 // it will be deprecated and removed.4146 // =============================================================================4147 4148 4149 /*
4150 Copyright: Copyright The D Language Foundation 2007 - 2009.
4151 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
4152 Authors: $(HTTP digitalmars.com, Walter Bright),
4153 $(HTTP erdani.org, Andrei Alexandrescu),
4154 $(HTTP thecybershadow.net, Vladimir Panteleev)
4155 Source: $(PHOBOSSRC std/_process.d)
4156 */4157 /*
4158 Copyright The D Language Foundation 2007 - 2009.
4159 Distributed under the Boost Software License, Version 1.0.
4160 (See accompanying file LICENSE_1_0.txt or copy at
4161 http://www.boost.org/LICENSE_1_0.txt)
4162 */4163 4164 4165 importcore.stdc.errno;
4166 importcore.stdc.stdlib;
4167 importcore.stdc.string;
4168 importcore.thread;
4169 4170 version (Windows)
4171 {
4172 importstd.file, std.format, std.random;
4173 }
4174 version (Posix)
4175 {
4176 importcore.sys.posix.stdlib;
4177 }
4178 4179 privatevoidtoAStringz(instring[] a, const(char)**az) @system4180 {
4181 importstd.string : toStringz;
4182 foreach (strings; a)
4183 {
4184 *az++ = toStringz(s);
4185 }
4186 *az = null;
4187 }
4188 4189 4190 /* ========================================================== */4191 4192 //version (Windows)4193 //{4194 // int spawnvp(int mode, string pathname, string[] argv)4195 // {4196 // char** argv_ = cast(char**) core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length));4197 // scope(exit) core.stdc.stdlib.free(argv_);4198 //4199 // toAStringz(argv, argv_);4200 //4201 // return spawnvp(mode, pathname.tempCString(), argv_);4202 // }4203 //}4204 4205 // Incorporating idea (for spawnvp() on Posix) from Dave Fladebo4206 4207 enum { _P_WAIT, _P_NOWAIT, _P_OVERLAY }
4208 version (Windows) extern(C) intspawnvp(int, scopeconst(char) *, scopeconst(char*)*);
4209 aliasP_WAIT = _P_WAIT;
4210 aliasP_NOWAIT = _P_NOWAIT;
4211 4212 /* ========================================================== */4213 4214 version (StdDdoc)
4215 {
4216 /**
4217 Replaces the current process by executing a command, `pathname`, with
4218 the arguments in `argv`.
4219 4220 $(BLUE This function is Posix-Only.)
4221 4222 Typically, the first element of `argv` is
4223 the command being executed, i.e. $(D argv[0] == pathname). The 'p'
4224 versions of `exec` search the PATH environment variable for $(D
4225 pathname). The 'e' versions additionally take the new process'
4226 environment variables as an array of strings of the form key=value.
4227 4228 Does not return on success (the current process will have been
4229 replaced). Returns -1 on failure with no indication of the
4230 underlying error.
4231 4232 Windows_specific:
4233 These functions are only supported on POSIX platforms, as the Windows
4234 operating systems do not provide the ability to overwrite the current
4235 process image with another. In single-threaded programs it is possible
4236 to approximate the effect of `execv*` by using $(LREF spawnProcess)
4237 and terminating the current process once the child process has returned.
4238 For example:
4239 ---
4240 auto commandLine = [ "program", "arg1", "arg2" ];
4241 version (Posix)
4242 {
4243 execv(commandLine[0], commandLine);
4244 throw new Exception("Failed to execute program");
4245 }
4246 else version (Windows)
4247 {
4248 import core.stdc.stdlib : _Exit;
4249 _Exit(wait(spawnProcess(commandLine)));
4250 }
4251 ---
4252 This is, however, NOT equivalent to POSIX' `execv*`. For one thing, the
4253 executed program is started as a separate process, with all this entails.
4254 Secondly, in a multithreaded program, other threads will continue to do
4255 work while the current thread is waiting for the child process to complete.
4256 4257 A better option may sometimes be to terminate the current program immediately
4258 after spawning the child process. This is the behaviour exhibited by the
4259 $(LINK2 http://msdn.microsoft.com/en-us/library/431x4c1w.aspx,`__exec`)
4260 functions in Microsoft's C runtime library, and it is how D's now-deprecated
4261 Windows `execv*` functions work. Example:
4262 ---
4263 auto commandLine = [ "program", "arg1", "arg2" ];
4264 version (Posix)
4265 {
4266 execv(commandLine[0], commandLine);
4267 throw new Exception("Failed to execute program");
4268 }
4269 else version (Windows)
4270 {
4271 spawnProcess(commandLine);
4272 import core.stdc.stdlib : _exit;
4273 _exit(0);
4274 }
4275 ---
4276 */4277 intexecv(instringpathname, instring[] argv);
4278 ///ditto4279 intexecve(instringpathname, instring[] argv, instring[] envp);
4280 /// ditto4281 intexecvp(instringpathname, instring[] argv);
4282 /// ditto4283 intexecvpe(instringpathname, instring[] argv, instring[] envp);
4284 }
4285 elseversion (Posix)
4286 {
4287 intexecv(instringpathname, instring[] argv)
4288 {
4289 returnexecv_(pathname, argv);
4290 }
4291 intexecve(instringpathname, instring[] argv, instring[] envp)
4292 {
4293 returnexecve_(pathname, argv, envp);
4294 }
4295 intexecvp(instringpathname, instring[] argv)
4296 {
4297 returnexecvp_(pathname, argv);
4298 }
4299 intexecvpe(instringpathname, instring[] argv, instring[] envp)
4300 {
4301 returnexecvpe_(pathname, argv, envp);
4302 }
4303 }
4304 4305 // Move these C declarations to druntime if we decide to keep the D wrappers4306 extern(C)
4307 {
4308 intexecv(scopeconst(char) *, scopeconst(char *)*);
4309 intexecve(scopeconst(char)*, scopeconst(char*)*, scopeconst(char*)*);
4310 intexecvp(scopeconst(char)*, scopeconst(char*)*);
4311 version (Windows) intexecvpe(scopeconst(char)*, scopeconst(char*)*, scopeconst(char*)*);
4312 }
4313 4314 privateintexecv_(instringpathname, instring[] argv)
4315 {
4316 importcore.exception : OutOfMemoryError;
4317 importstd.exception : enforce;
4318 autoargv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length));
4319 enforce!OutOfMemoryError(argv_ !isnull, "Out of memory in std.process.");
4320 scope(exit) core.stdc.stdlib.free(argv_);
4321 4322 toAStringz(argv, argv_);
4323 4324 returnexecv(pathname.tempCString(), argv_);
4325 }
4326 4327 privateintexecve_(instringpathname, instring[] argv, instring[] envp)
4328 {
4329 importcore.exception : OutOfMemoryError;
4330 importstd.exception : enforce;
4331 autoargv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length));
4332 enforce!OutOfMemoryError(argv_ !isnull, "Out of memory in std.process.");
4333 scope(exit) core.stdc.stdlib.free(argv_);
4334 autoenvp_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + envp.length));
4335 enforce!OutOfMemoryError(envp_ !isnull, "Out of memory in std.process.");
4336 scope(exit) core.stdc.stdlib.free(envp_);
4337 4338 toAStringz(argv, argv_);
4339 toAStringz(envp, envp_);
4340 4341 returnexecve(pathname.tempCString(), argv_, envp_);
4342 }
4343 4344 privateintexecvp_(instringpathname, instring[] argv)
4345 {
4346 importcore.exception : OutOfMemoryError;
4347 importstd.exception : enforce;
4348 autoargv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length));
4349 enforce!OutOfMemoryError(argv_ !isnull, "Out of memory in std.process.");
4350 scope(exit) core.stdc.stdlib.free(argv_);
4351 4352 toAStringz(argv, argv_);
4353 4354 returnexecvp(pathname.tempCString(), argv_);
4355 }
4356 4357 privateintexecvpe_(instringpathname, instring[] argv, instring[] envp)
4358 {
4359 version (Posix)
4360 {
4361 importstd.array : split;
4362 importstd.conv : to;
4363 // Is pathname rooted?4364 if (pathname[0] == '/')
4365 {
4366 // Yes, so just call execve()4367 returnexecve(pathname, argv, envp);
4368 }
4369 else4370 {
4371 // No, so must traverse PATHs, looking for first match4372 string[] envPaths = split(
4373 to!string(core.stdc.stdlib.getenv("PATH")), ":");
4374 intiRet = 0;
4375 4376 // Note: if any call to execve() succeeds, this process will cease4377 // execution, so there's no need to check the execve() result through4378 // the loop.4379 4380 foreach (stringpathDir; envPaths)
4381 {
4382 stringcomposite = cast(string) (pathDir ~ "/" ~ pathname);
4383 4384 iRet = execve(composite, argv, envp);
4385 }
4386 if (0 != iRet)
4387 {
4388 iRet = execve(pathname, argv, envp);
4389 }
4390 4391 returniRet;
4392 }
4393 }
4394 elseversion (Windows)
4395 {
4396 importcore.exception : OutOfMemoryError;
4397 importstd.exception : enforce;
4398 autoargv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length));
4399 enforce!OutOfMemoryError(argv_ !isnull, "Out of memory in std.process.");
4400 scope(exit) core.stdc.stdlib.free(argv_);
4401 autoenvp_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + envp.length));
4402 enforce!OutOfMemoryError(envp_ !isnull, "Out of memory in std.process.");
4403 scope(exit) core.stdc.stdlib.free(envp_);
4404 4405 toAStringz(argv, argv_);
4406 toAStringz(envp, envp_);
4407 4408 returnexecvpe(pathname.tempCString(), argv_, envp_);
4409 }
4410 else4411 {
4412 staticassert(0);
4413 } // version4414 }
4415 4416 version (StdDdoc)
4417 {
4418 /****************************************
4419 * Start up the browser and set it to viewing the page at url.
4420 */4421 voidbrowse(scopeconst(char)[] url);
4422 }
4423 else4424 version (Windows)
4425 {
4426 importcore.sys.windows.shellapi, core.sys.windows.winuser;
4427 4428 pragma(lib,"shell32.lib");
4429 4430 voidbrowse(scopeconst(char)[] url) nothrow @nogc @trusted4431 {
4432 ShellExecuteW(null, "open", url.tempCStringW(), null, null, SW_SHOWNORMAL);
4433 }
4434 }
4435 elseversion (Posix)
4436 {
4437 importcore.stdc.stdio;
4438 importcore.stdc.string;
4439 importcore.sys.posix.unistd;
4440 4441 voidbrowse(scopeconst(char)[] url) nothrow @nogc @safe4442 {
4443 constbuffer = url.tempCString(); // Retain buffer until end of scope4444 const(char)*[3] args;
4445 4446 // Trusted because it's called with a zero-terminated literal4447 const(char)* browser = (() @trusted => core.stdc.stdlib.getenv("BROWSER"))();
4448 if (browser)
4449 {
4450 // String already zero-terminated4451 browser = (() @trusted => strdup(browser))();
4452 args[0] = browser;
4453 }
4454 else4455 {
4456 version (OSX)
4457 {
4458 args[0] = "open";
4459 }
4460 else4461 {
4462 //args[0] = "x-www-browser"; // doesn't work on some systems4463 args[0] = "xdg-open";
4464 }
4465 }
4466 4467 args[1] = buffer;
4468 args[2] = null;
4469 4470 autochildpid = core.sys.posix.unistd.fork();
4471 if (childpid == 0)
4472 {
4473 // Trusted because args and all entries are always zero-terminated4474 (() @trusted =>
4475 core.sys.posix.unistd.execvp(args[0], &args[0]) ||
4476 perror(args[0]) // failed to execute4477 )();
4478 return;
4479 }
4480 if (browser)
4481 // Trusted because it's allocated via strdup above4482 (() @trusted => free(cast(void*) browser))();
4483 4484 version (StdUnittest)
4485 {
4486 // Verify that the test script actually suceeds4487 intstatus;
4488 constcheck = (() @trusted => waitpid(childpid, &status, 0))();
4489 assert(check != -1);
4490 assert(status == 0);
4491 }
4492 }
4493 }
4494 else4495 staticassert(0, "os not supported");
4496 4497 // Verify attributes are consistent between all implementations4498 @safe @nogcnothrowunittest4499 {
4500 if (false)
4501 browse("");
4502 }
4503 4504 version (Windows) { /* Doesn't use BROWSER */ }
4505 else4506 @systemunittest4507 {
4508 importstd.conv : text;
4509 importstd.range : repeat;
4510 immutablestringurl = text("http://", repeat('x', 249));
4511 4512 TestScriptprog = `if [ "$1" != "` ~ url ~ `" ]; then exit 1; fi`;
4513 environment["BROWSER"] = prog.path;
4514 browse(url);
4515 }