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