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