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