1 /*******************************************************************************
2 
3   Copyright:
4       Copyright (c) 2006 Juan Jose Comellas.
5       Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH.
6       All rights reserved.
7 
8   License:
9       Tango Dual License: 3-Clause BSD License / Academic Free License v3.0.
10       See LICENSE_TANGO.txt for details.
11 
12   Authors: Juan Jose Comellas <juanjo@comellas.com.ar>
13 
14 *******************************************************************************/
15 
16 module ocean.sys.Process;
17 
18 import ocean.meta.types.Qualifiers;
19 import ocean.core.Verify;
20 import ocean.core.Array : copy;
21 import ocean.io.model.IFile;
22 import ocean.io.Console;
23 import ocean.sys.Common;
24 import ocean.sys.Pipe;
25 import ocean.core.ExceptionDefinitions;
26 import ocean.text.convert.Formatter;
27 import ocean.text.Util;
28 
29 version (unittest) import ocean.core.Test;
30 
31 import core.stdc.stdlib;
32 import core.stdc.string;
33 
34 import core.stdc.errno;
35 import ocean.stdc.posix.fcntl;
36 import core.sys.posix.unistd;
37 import core.sys.posix.sys.wait;
38 
39 private
40 {
41     __gshared extern (C) extern char** environ;
42 }
43 
44 debug (Process)
45 {
46     import ocean.io.Stdout;
47 }
48 
49 
50 /**
51  * Redirect flags for processes.  Defined outside process class to cut down on
52  * verbosity.
53  */
54 enum Redirect
55 {
56     /**
57      * Redirect none of the standard handles
58      */
59     None = 0,
60 
61     /**
62      * Redirect the stdout handle to a pipe.
63      */
64     Output = 1,
65 
66     /**
67      * Redirect the stderr handle to a pipe.
68      */
69     Error = 2,
70 
71     /**
72      * Redirect the stdin handle to a pipe.
73      */
74     Input = 4,
75 
76     /**
77      * Redirect all three handles to pipes (default).
78      */
79     All = Output | Error | Input,
80 
81     /**
82      * Send stderr to stdout's handle.  Note that the stderr PipeConduit will
83      * be null.
84      */
85     ErrorToOutput = 0x10,
86 
87     /**
88      * Send stdout to stderr's handle.  Note that the stdout PipeConduit will
89      * be null.
90      */
91     OutputToError = 0x20,
92 }
93 
94 /**
95  * The Process class is used to start external programs and communicate with
96  * them via their standard input, output and error streams.
97  *
98  * You can pass either the command line or an array of arguments to execute,
99  * either in the constructor or to the args property. The environment
100  * variables can be set in a similar way using the env property and you can
101  * set the program's working directory via the workDir property.
102  *
103  * To actually start a process you need to use the execute() method. Once the
104  * program is running you will be able to write to its standard input via the
105  * stdin OutputStream and you will be able to read from its standard output and
106  * error through the stdout and stderr InputStream respectively.
107  *
108  * You can check whether the process is running or not with the isRunning()
109  * method and you can get its process ID via the pid property.
110  *
111  * After you are done with the process, or if you just want to wait for it to
112  * end, you need to call the wait() method which will return once the process
113  * is no longer running.
114  *
115  * To stop a running process you must use kill() method. If you do this you
116  * cannot call the wait() method. Once the kill() method returns the process
117  * will be already dead.
118  *
119  * After calling either wait() or kill(), and no more data is expected on the
120  * pipes, you should call close() as this will clean the pipes. Not doing this
121  * may lead to a depletion of the available file descriptors for the main
122  * process if many processes are created.
123  *
124  * Examples:
125  * ---
126  * try
127  * {
128  *     auto p = new Process ("ls -al", null);
129  *     p.execute;
130  *
131  *     Stdout.formatln ("Output from {}:", p.programName);
132  *     Stdout.copy (p.stdout).flush;
133  *     auto result = p.wait;
134  *
135  *     Stdout.formatln ("Process '{}' ({}) exited with reason {}, status {}",
136  *                      p.programName, p.pid, cast(int) result.reason, result.status);
137  * }
138  * catch (ProcessException e)
139  *        Stdout.formatln ("Process execution failed: {}", e);
140  * ---
141  *
142  * ---
143  *    // Example how to pipe two processes together:
144  *    auto p1 = new Process("ls");
145  *    auto p2 = new Process("head");
146  *
147  *    p1.execute();
148  *    p2.execute();
149  *
150  *    p2.stdin.copy(p1.stdout);
151  *
152  *    p2.wait();
153  *    Stdout.copy(p2.stdout);
154  *
155  * ---
156  */
157 class Process
158 {
159     /**
160      * Result returned by wait().
161      */
162     public struct Result
163     {
164         /**
165          * Reasons returned by wait() indicating why the process is no
166          * longer running.
167          */
168         public enum
169         {
170             Exit,
171             Signal,
172             Stop,
173             Continue,
174             Error
175         }
176 
177         public int reason;
178         public int status;
179 
180         /// Formatter-compatible string formatting function
181         public void toString (scope FormatterSink sink)
182         {
183             switch (reason)
184             {
185                 case Exit:
186                     sformat(sink,
187                         "Process exited normally with return code {}", status);
188                     break;
189 
190                 case Signal:
191                     sformat(sink, "Process was killed with signal {}", status);
192                     break;
193 
194                 case Stop:
195                     sformat(sink, "Process was stopped with signal {}", status);
196                     break;
197 
198                 case Continue:
199                     sformat(sink, "Process was resumed with signal {}", status);
200                     break;
201 
202                 case Error:
203                     sformat(sink, "Process failed with error code {}: {}",
204                         reason, SysError.lookup(status));
205                     break;
206 
207                 default:
208                     sformat(sink, "Unknown process result {}", reason);
209                     break;
210             }
211         }
212 
213         /// Convenience overload that format this as a string
214         public istring toString ()
215         {
216             return format("{}", &this);
217         }
218     }
219 
220     static immutable uint DefaultStdinBufferSize    = 512;
221     static immutable uint DefaultStdoutBufferSize   = 8192;
222     static immutable uint DefaultStderrBufferSize   = 512;
223     static immutable Redirect DefaultRedirectFlags  = Redirect.All;
224 
225     private cstring[]        _args;
226     private istring[istring] _env;
227     private cstring          _workDir;
228     private PipeConduit      _stdin;
229     private PipeConduit      _stdout;
230     private PipeConduit      _stderr;
231     private bool             _running = false;
232     private bool             _copyEnv = false;
233     private Redirect         _redirect = DefaultRedirectFlags;
234 
235     private pid_t _pid = cast(pid_t) -1;
236 
237     /**
238      * Constructor (variadic version).  Note that by default, the environment
239      * will not be copied.
240      *
241      * Params:
242      * args     = array of strings with the process' arguments.  If there is
243      *            exactly one argument, it is considered to contain the entire
244      *            command line including parameters.  If you pass only one
245      *            argument, spaces that are not intended to separate
246      *            parameters should be embedded in quotes.  The arguments can
247      *            also be empty.
248      *            Note: The class will use only slices, .dup when necessary.
249      *
250      */
251     public this(const(mstring)[] args ...)
252     {
253         if(args.length == 1)
254             _args = splitArgs(args[0]);
255         else
256             _args.copy(args);
257     }
258 
259     ///
260     unittest
261     {
262         void example ( )
263         {
264             auto p1 = new Process("myprogram", "first argument", "second", "third");
265             auto p2 = new Process("myprogram \"first argument\" second third");
266         }
267     }
268 
269     /**
270      * Constructor (variadic version, with environment copy).
271      *
272      * Params:
273      * copyEnv  = if true, the environment is copied from the current process.
274      * args     = array of strings with the process' arguments.  If there is
275      *            exactly one argument, it is considered to contain the entire
276      *            command line including parameters.  If you pass only one
277      *            argument, spaces that are not intended to separate
278      *            parameters should be embedded in quotes.  The arguments can
279      *            also be empty.
280      *            Note: The class will use only slices, .dup when necessary.
281      */
282     public this(bool copyEnv, const(mstring)[] args ...)
283     {
284         _copyEnv = copyEnv;
285         this(args);
286     }
287 
288     ///
289     unittest
290     {
291         void example ( )
292         {
293             auto p1 = new Process(true, "myprogram", "first argument", "second", "third");
294             auto p2 = new Process(true, "myprogram \"first argument\" second third");
295         }
296     }
297 
298     /**
299      * Constructor.
300      *
301      * Params:
302      * command  = string with the process' command line; arguments that have
303      *            embedded whitespace must be enclosed in inside double-quotes (").
304      *            Note: The class will use only slices, .dup when necessary.
305      * env      = associative array of strings with the process' environment
306      *            variables; the variable name must be the key of each entry.
307      */
308     public this(cstring command, istring[istring] env)
309     {
310         verify(command.length > 0);
311         _args = splitArgs(command);
312         _env = env;
313     }
314 
315     ///
316     unittest
317     {
318         void example ( )
319         {
320             cstring command = "myprogram \"first argument\" second third";
321             istring[istring] env;
322 
323             // Environment variables
324             env["MYVAR1"] = "first";
325             env["MYVAR2"] = "second";
326 
327             auto p = new Process(command, env);
328         }
329     }
330 
331     /**
332      * Constructor.
333      *
334      * Params:
335      * args     = array of strings with the process' arguments; the first
336      *            argument must be the process' name; the arguments can be
337      *            empty.
338      *            Note: The class will use only slices, .dup when necessary.
339      * env      = associative array of strings with the process' environment
340      *            variables; the variable name must be the key of each entry.
341      */
342     public this(const(mstring)[] args, istring[istring] env)
343     {
344         verify(args.length > 0);
345         verify(args[0].length > 0);
346         _args.copy(args);
347         _env = env;
348     }
349 
350     ///
351     unittest
352     {
353         void example ( )
354         {
355              istring[] args;
356              istring[istring] env;
357 
358              // Process name
359              args ~= "myprogram";
360              // Process arguments
361              args ~= "first argument";
362              args ~= "second";
363              args ~= "third";
364 
365              // Environment variables
366              env["MYVAR1"] = "first";
367              env["MYVAR2"] = "second";
368 
369              auto p = new Process(args, env);
370         }
371     }
372 
373     /**
374      * Indicate whether the process is running or not.
375      */
376     public bool isRunning()
377     {
378         return _running;
379     }
380 
381     /**
382      * Return the running process' ID.
383      *
384      * Returns: an int with the process ID if the process is running;
385      *          -1 if not.
386      */
387     public int pid()
388     {
389         return cast(int) _pid;
390     }
391 
392     /**
393      * Return the process' executable filename.
394      */
395     public cstring programName()
396     {
397         return (_args !is null ? _args[0] : null);
398     }
399 
400     /**
401      * Set the process' executable filename.
402      */
403     public cstring programName(cstring name)
404     {
405         if (_args.length == 0)
406         {
407             _args.length = 1;
408         }
409         return _args[0] = name;
410     }
411 
412     /**
413      * Set the process' executable filename, return 'this' for chaining
414      */
415     public Process setProgramName(cstring name)
416     {
417         programName = name;
418         return this;
419     }
420 
421     /**
422      * Return an array with the process' arguments.
423      */
424     public cstring[] args()
425     {
426         return _args;
427     }
428 
429     /**
430      * Set the process' arguments from the arguments received by the method.
431      *
432      * Remarks:
433      * The first element of the array must be the name of the process'
434      * executable.
435      *
436      * Returns: the arguments that were set.
437      */
438     public cstring[] args(cstring progname, const(mstring)[] args ...)
439     {
440         return _args.copy(progname ~ args);
441     }
442 
443     ///
444     unittest
445     {
446         void example ( )
447         {
448             auto p = new Process;
449             p.args("myprogram", "first", "second argument", "third");
450         }
451     }
452 
453     /**
454      * Set the process' command and arguments from an array.
455      *
456      * Remarks:
457      * The first element of the array must be the name of the process'
458      * executable.
459      *
460      * Returns: the arguments that were set.
461      *
462      */
463 
464     public void argsWithCommand(const(mstring)[] args)
465     {
466         _args.copy(args);
467     }
468 
469     ///
470     unittest
471     {
472         void example ( )
473         {
474             auto p = new Process;
475             p.argsWithCommand(["myprogram", "first", "second argument", "third"]);
476         }
477     }
478 
479     /**
480      * Set the process' arguments from the arguments received by the method.
481      *
482      * Remarks:
483      * The first element of the array must be the name of the process'
484      * executable.
485      *
486      * Returns: a reference to this for chaining
487      *
488      */
489     public Process setArgs(cstring progname, const(mstring)[] args ...)
490     {
491         this.args(progname, args);
492         return this;
493     }
494 
495     ///
496     unittest
497     {
498         void example ( )
499         {
500             auto p = new Process;
501             p.setArgs("myprogram", "first", "second argument", "third").execute();
502         }
503     }
504 
505     /**
506      * If true, the environment from the current process will be copied to the
507      * child process.
508      */
509     public bool copyEnv()
510     {
511         return _copyEnv;
512     }
513 
514     /**
515      * Set the copyEnv flag.  If set to true, then the environment will be
516      * copied from the current process.  If set to false, then the environment
517      * is set from the env field.
518      */
519     public bool copyEnv(bool b)
520     {
521         return _copyEnv = b;
522     }
523 
524     /**
525      * Set the copyEnv flag.  If set to true, then the environment will be
526      * copied from the current process.  If set to false, then the environment
527      * is set from the env field.
528      *
529      * Returns:
530      *   A reference to this for chaining
531      */
532     public Process setCopyEnv(bool b)
533     {
534         _copyEnv = b;
535         return this;
536     }
537 
538     /**
539      * Return an associative array with the process' environment variables.
540      *
541      * Note that if copyEnv is set to true, this value is ignored.
542      */
543     public istring[istring] env()
544     {
545         return _env;
546     }
547 
548     /**
549      * Set the process' environment variables from the associative array
550      * received by the method.
551      *
552      * This also clears the copyEnv flag.
553      *
554      * Params:
555      * env  = associative array of strings containing the environment
556      *        variables for the process. The variable name should be the key
557      *        used for each entry.
558      *
559      * Returns: the env set.
560      * Examples:
561      * ---
562      * istring[istring] env;
563      *
564      * env["MYVAR1"] = "first";
565      * env["MYVAR2"] = "second";
566      *
567      * p.env = env;
568      * ---
569      */
570     public istring[istring] env(istring[istring] env)
571     {
572         _copyEnv = false;
573         return _env = env;
574     }
575 
576     /**
577      * Set the process' environment variables from the associative array
578      * received by the method.  Returns a 'this' reference for chaining.
579      *
580      * This also clears the copyEnv flag.
581      *
582      * Params:
583      * env  = associative array of strings containing the environment
584      *        variables for the process. The variable name should be the key
585      *        used for each entry.
586      *
587      * Returns: A reference to this process object
588      */
589     public Process setEnv(istring[istring] env)
590     {
591         _copyEnv = false;
592         _env = env;
593         return this;
594     }
595 
596     ///
597     unittest
598     {
599         void example ( )
600         {
601             auto p = new Process;
602             istring[istring] env;
603             env["MYVAR1"] = "first";
604             env["MYVAR2"] = "second";
605             p.setEnv(env).execute();
606         }
607     }
608 
609     /**
610      * Return an UTF-8 string with the process' command line.
611      */
612     public override istring toString()
613     {
614         istring command;
615 
616         for (uint i = 0; i < _args.length; ++i)
617         {
618             if (i > 0)
619             {
620                 command ~= ' ';
621             }
622             if (contains(_args[i], ' ') || _args[i].length == 0)
623             {
624                 command ~= '"';
625                 command ~= _args[i].substitute("\\", "\\\\").substitute(`"`, `\"`);
626                 command ~= '"';
627             }
628             else
629             {
630                 command ~= _args[i].substitute("\\", "\\\\").substitute(`"`, `\"`);
631             }
632         }
633         return command;
634     }
635 
636     /**
637      * Return the working directory for the process.
638      *
639      * Returns: a string with the working directory; null if the working
640      *          directory is the current directory.
641      */
642     public cstring workDir()
643     {
644         return _workDir;
645     }
646 
647     /**
648      * Set the working directory for the process.
649      *
650      * Params:
651      * dir  = a string with the working directory; null if the working
652      *         directory is the current directory.
653      *
654      * Returns: the directory set.
655      */
656     public cstring workDir(cstring dir)
657     {
658         return _workDir = dir;
659     }
660 
661     /**
662      * Set the working directory for the process.  Returns a 'this' reference
663      * for chaining
664      *
665      * Params:
666      * dir  = a string with the working directory; null if the working
667      *         directory is the current directory.
668      *
669      * Returns: a reference to this process.
670      */
671     public Process setWorkDir(cstring dir)
672     {
673         _workDir = dir;
674         return this;
675     }
676 
677     /**
678      * Get the redirect flags for the process.
679      *
680      * The redirect flags are used to determine whether stdout, stderr, or
681      * stdin are redirected.  The flags are an or'd combination of which
682      * standard handles to redirect.  A redirected handle creates a pipe,
683      * whereas a non-redirected handle simply points to the same handle this
684      * process is pointing to.
685      *
686      * You can also redirect stdout or stderr to each other.  The flags to
687      * redirect a handle to a pipe and to redirect it to another handle are
688      * mutually exclusive.  In the case both are specified, the redirect to
689      * the other handle takes precedent.  It is illegal to specify both
690      * redirection from stdout to stderr and from stderr to stdout.  If both
691      * of these are specified, an exception is thrown.
692      *
693      * If redirected to a pipe, once the process is executed successfully, its
694      * input and output can be manipulated through the stdin, stdout and
695      * stderr member PipeConduit's.  Note that if you redirect for example
696      * stderr to stdout, and you redirect stdout to a pipe, only stdout will
697      * be non-null.
698      */
699     public Redirect redirect()
700     {
701         return _redirect;
702     }
703 
704     /**
705      * Set the redirect flags for the process.
706      */
707     public Redirect redirect(Redirect flags)
708     {
709         return _redirect = flags;
710     }
711 
712     /**
713      * Set the redirect flags for the process.  Return a reference to this
714      * process for chaining.
715      */
716     public Process setRedirect(Redirect flags)
717     {
718         _redirect = flags;
719         return this;
720     }
721 
722     /**
723      * Get the GUI flag.
724      *
725      * This flag indicates on Windows systems that the CREATE_NO_WINDOW flag
726      * should be set on CreateProcess.  Although this is a specific windows
727      * flag, it is present on posix systems as a noop for compatibility.
728      *
729      * Without this flag, a console window will be allocated if it doesn't
730      * already exist.
731      */
732     public bool gui()
733     {
734         return false;
735     }
736 
737     /**
738      * Set the GUI flag.
739      *
740      * This flag indicates on Windows systems that the CREATE_NO_WINDOW flag
741      * should be set on CreateProcess.  Although this is a specific windows
742      * flag, it is present on posix systems as a noop for compatibility.
743      *
744      * Without this flag, a console window will be allocated if it doesn't
745      * already exist.
746      */
747     public bool gui(bool value)
748     {
749         return false;
750     }
751 
752     /**
753      * Set the GUI flag.  Returns a reference to this process for chaining.
754      *
755      * This flag indicates on Windows systems that the CREATE_NO_WINDOW flag
756      * should be set on CreateProcess.  Although this is a specific windows
757      * flag, it is present on posix systems as a noop for compatibility.
758      *
759      * Without this flag, a console window will be allocated if it doesn't
760      * already exist.
761      */
762     public Process setGui(bool value)
763     {
764         return this;
765     }
766 
767     /**
768      * Return the running process' standard input pipe.
769      *
770      * Returns: a write-only PipeConduit connected to the child
771      *          process' stdin.
772      *
773      * Remarks:
774      * The stream will be null if no child process has been executed, or the
775      * standard input stream was not redirected.
776      */
777     public PipeConduit stdin()
778     {
779         return _stdin;
780     }
781 
782     /**
783      * Return the running process' standard output pipe.
784      *
785      * Returns: a read-only PipeConduit connected to the child
786      *          process' stdout.
787      *
788      * Remarks:
789      * The stream will be null if no child process has been executed, or the
790      * standard output stream was not redirected.
791      */
792     public PipeConduit stdout()
793     {
794         return _stdout;
795     }
796 
797     /**
798      * Return the running process' standard error pipe.
799      *
800      * Returns: a read-only PipeConduit connected to the child
801      *          process' stderr.
802      *
803      * Remarks:
804      * The stream will be null if no child process has been executed, or the
805      * standard error stream was not redirected.
806      */
807     public PipeConduit stderr()
808     {
809         return _stderr;
810     }
811 
812     /**
813      * Pipes used during execute(). They are member variables so that they
814      * can be reused by later calls to execute().
815      * Note that any file handles created during execute() will remain open
816      * and stored in these pipes, unless they are explicitly closed.
817      */
818     Pipe pin, pout, perr, pexec;
819 
820     /**
821      * Execute a process using the arguments that were supplied to the
822      * constructor or to the args property.
823      *
824      * Once the process is executed successfully, its input and output can be
825      * manipulated through the stdin, stdout and
826      * stderr member PipeConduit's.
827      *
828      * Returns:
829      * A reference to this process object for chaining.
830      *
831      * Throws:
832      * ProcessCreateException if the process could not be created
833      * successfully; ProcessForkException if the call to the fork()
834      * system call failed (on POSIX-compatible platforms).
835      *
836      * Remarks:
837      * The process must not be running and the list of arguments must
838      * not be empty before calling this method.
839      */
840     public Process execute()
841     {
842         verify(!_running);
843         verify(_args.length > 0 && _args[0] !is null);
844 
845         // We close the pipes that could have been left open from a previous
846         // execution.
847         cleanPipes();
848 
849         // validate the redirection flags
850         if((_redirect & (Redirect.OutputToError | Redirect.ErrorToOutput)) == (Redirect.OutputToError | Redirect.ErrorToOutput))
851             throw new ProcessCreateException(_args[0], "Illegal redirection flags");
852 
853         // Are we redirecting stdout and stderr?
854         bool redirected_output = (_redirect & (Redirect.Output | Redirect.OutputToError)) == Redirect.Output;
855         bool redirected_error  = (_redirect & (Redirect.Error | Redirect.ErrorToOutput)) == Redirect.Error;
856 
857         if(_redirect & Redirect.Input)
858         {
859             if ( ! pin )
860             {
861                 pin = new Pipe(DefaultStdinBufferSize);
862             }
863             else
864             {
865                 pin.recreate(DefaultStdinBufferSize);
866             }
867         }
868 
869         if( redirected_output )
870         {
871             if ( ! pout )
872             {
873                 pout = new Pipe(DefaultStdoutBufferSize);
874             }
875             else
876             {
877                 pout.recreate(DefaultStdoutBufferSize);
878             }
879         }
880 
881         if( redirected_error )
882         {
883             if ( ! perr)
884             {
885                 perr = new Pipe(DefaultStderrBufferSize);
886             }
887             else
888             {
889                 perr.recreate(DefaultStderrBufferSize);
890             }
891         }
892 
893         // This pipe is used to propagate the result of the call to
894         // execv*() from the child process to the parent process.
895         if (! pexec)
896         {
897             pexec = new Pipe(8);
898         }
899         else
900         {
901             pexec.recreate(8);
902         }
903 
904         int status = 0;
905 
906         _pid = fork();
907         if (_pid >= 0)
908         {
909             if (_pid != 0)
910             {
911                 // Parent process
912                 if(_redirect & Redirect.Input)
913                 {
914                     _stdin = pin.sink;
915                     pin.source.close();
916                 }
917 
918                 if( redirected_output )
919                 {
920                     _stdout = pout.source;
921                     pout.sink.close();
922                 }
923 
924                 if(redirected_error)
925                 {
926                     _stderr = perr.source;
927                     perr.sink.close();
928                 }
929 
930                 pexec.sink.close();
931 
932                 try
933                 {
934                     pexec.source.input.read((cast(byte*) &status)[0 .. status.sizeof]);
935                 }
936                 catch (Exception e)
937                 {
938                     // Everything's OK, the pipe was closed after the call to execv*()
939                 }
940 
941                 pexec.source.close();
942 
943                 if (status == 0)
944                 {
945                     _running = true;
946                 }
947                 else
948                 {
949                     // We set errno to the value that was sent through
950                     // the pipe from the child process
951                     errno = status;
952                     _running = false;
953 
954                     throw new ProcessCreateException(_args[0]);
955                 }
956             }
957             else
958             {
959                 // Child process
960                 int rc;
961                 char*[] argptr;
962                 char*[] envptr;
963 
964                 // Note that for all the pipes, we can close both ends
965                 // because dup2 opens a duplicate file descriptor to the
966                 // same resource.
967 
968                 // Replace stdin with the "read" pipe
969                 if(_redirect & Redirect.Input)
970                 {
971                     if (dup2(pin.source.fileHandle(), STDIN_FILENO) < 0)
972                         throw new Exception("dup2 < 0");
973                     pin.sink.close();
974                     pin.source.close();
975                 }
976 
977                 // Replace stdout with the "write" pipe
978                 if( redirected_output )
979                 {
980                     if (dup2(pout.sink.fileHandle(), STDOUT_FILENO) < 0)
981                         throw new Exception("dup2 < 0");
982                     pout.source.close();
983                     pout.sink.close();
984                 }
985 
986                 // Replace stderr with the "write" pipe
987                 if( redirected_error )
988                 {
989                     if (dup2(perr.sink.fileHandle(), STDERR_FILENO) < 0)
990                         throw new Exception("dup2 < 0");
991                     perr.source.close();
992                     perr.sink.close();
993                 }
994 
995                 // Check for redirection from stdout to stderr or vice
996                 // versa
997                 if(_redirect & Redirect.OutputToError)
998                 {
999                     if(dup2(STDERR_FILENO, STDOUT_FILENO) < 0)
1000                         throw new Exception("dup2 < 0");
1001                 }
1002 
1003                 if(_redirect & Redirect.ErrorToOutput)
1004                 {
1005                     if(dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
1006                         throw new Exception("dup2 < 0");
1007                 }
1008 
1009                 // We close the unneeded part of the execv*() notification pipe
1010                 pexec.source.close();
1011 
1012                 // Set the "write" pipe so that it closes upon a successful
1013                 // call to execv*()
1014                 if (fcntl(cast(int) pexec.sink.fileHandle(), F_SETFD, FD_CLOEXEC) == 0)
1015                 {
1016                     // Convert the arguments and the environment variables to
1017                     // the format expected by the execv() family of functions.
1018                     argptr = toNullEndedArray(_args);
1019                     envptr = (_copyEnv ? null : toNullEndedArray(_env));
1020 
1021                     // Switch to the working directory if it has been set.
1022                     if (_workDir.length > 0)
1023                     {
1024                         chdir((_workDir ~ "\0").ptr);
1025                     }
1026 
1027                     // Replace the child fork with a new process. We always use the
1028                     // system PATH to look for executables that don't specify
1029                     // directories in their names.
1030                     rc = execvpe(_args[0], argptr, envptr);
1031                     if (rc == -1)
1032                     {
1033                         Cerr("Failed to exec ")(_args[0])(": ")(SysError.lastMsg).newline;
1034 
1035                         try
1036                         {
1037                             status = errno;
1038 
1039                             // Propagate the child process' errno value to
1040                             // the parent process.
1041                             pexec.sink.output.write((cast(byte*) &status)[0 .. status.sizeof]);
1042                         }
1043                         catch (Exception e)
1044                         {
1045                         }
1046                         exit(errno);
1047                     }
1048                     exit(errno);
1049                 }
1050                 else
1051                 {
1052                     Cerr("Failed to set notification pipe to close-on-exec for ")
1053                         (_args[0])(": ")(SysError.lastMsg).newline;
1054                     exit(errno);
1055                 }
1056             }
1057         }
1058         else
1059         {
1060             throw new ProcessForkException(_pid);
1061         }
1062         return this;
1063     }
1064 
1065 
1066     /**
1067      * Unconditionally wait for a process to end and return the reason and
1068      * status code why the process ended.
1069      *
1070      * Returns:
1071      * The return value is a Result struct, which has two members:
1072      * reason and status. The reason can take the
1073      * following values:
1074      *
1075      * Process.Result.Exit: the child process exited normally;
1076      *                      status has the process' return
1077      *                      code.
1078      *
1079      * Process.Result.Signal: the child process was killed by a signal;
1080      *                        status has the signal number
1081      *                        that killed the process.
1082      *
1083      * Process.Result.Stop: the process was stopped; status
1084      *                      has the signal number that was used to stop
1085      *                      the process.
1086      *
1087      * Process.Result.Continue: the process had been previously stopped
1088      *                          and has now been restarted;
1089      *                          status has the signal number
1090      *                          that was used to continue the process.
1091      *
1092      * Process.Result.Error: We could not properly wait on the child
1093      *                       process; status has the
1094      *                       errno value if the process was
1095      *                       running and -1 if not.
1096      *
1097      * Remarks:
1098      * You can only call wait() on a running process once. The Signal, Stop
1099      * and Continue reasons will only be returned on POSIX-compatible
1100      * platforms.
1101      * Calling wait() will not clean the pipes as the parent process may still
1102      * want the remaining output. It is however recommended to call close()
1103      * when no more content is expected, as this will close the pipes.
1104      */
1105     public Result wait()
1106     {
1107         Result result;
1108 
1109         if (_running)
1110         {
1111             int rc;
1112 
1113             // We clean up the process related data and set the _running
1114             // flag to false once we're done waiting for the process to
1115             // finish.
1116             //
1117             // IMPORTANT: we don't delete the open pipes so that the parent
1118             //            process can get whatever the child process left on
1119             //            these pipes before dying.
1120             scope(exit)
1121             {
1122                 _running = false;
1123             }
1124 
1125             // Wait for child process to end.
1126             if (waitpid(_pid, &rc, 0) != -1)
1127             {
1128                 if (WIFEXITED(rc))
1129                 {
1130                     result.reason = Result.Exit;
1131                     result.status = WEXITSTATUS(rc);
1132                     if (result.status != 0)
1133                     {
1134                         debug (Process)
1135                             Stdout.formatln("Child process '{0}' ({1}) returned with code {2}\n",
1136                                             _args[0], _pid, result.status);
1137                     }
1138                 }
1139                 else
1140                 {
1141                     if (WIFSIGNALED(rc))
1142                     {
1143                         result.reason = Result.Signal;
1144                         result.status = WTERMSIG(rc);
1145 
1146                         debug (Process)
1147                             Stdout.formatln("Child process '{0}' ({1}) was killed prematurely "
1148                                             ~ "with signal {2}",
1149                                             _args[0], _pid, result.status);
1150                     }
1151                     else if (WIFSTOPPED(rc))
1152                     {
1153                         result.reason = Result.Stop;
1154                         result.status = WSTOPSIG(rc);
1155 
1156                         debug (Process)
1157                             Stdout.formatln("Child process '{0}' ({1}) was stopped "
1158                                             ~ "with signal {2}",
1159                                             _args[0], _pid, result.status);
1160                     }
1161                     else if (WIFCONTINUED(rc))
1162                     {
1163                         result.reason = Result.Stop;
1164                         result.status = WSTOPSIG(rc);
1165 
1166                         debug (Process)
1167                             Stdout.formatln("Child process '{0}' ({1}) was continued "
1168                                             ~ "with signal {2}",
1169                                             _args[0], _pid, result.status);
1170                     }
1171                     else
1172                     {
1173                         result.reason = Result.Error;
1174                         result.status = rc;
1175 
1176                         debug (Process)
1177                             Stdout.formatln("Child process '{0}' ({1}) failed "
1178                                             ~ "with unknown exit status {2}\n",
1179                                             _args[0], _pid, result.status);
1180                     }
1181                 }
1182             }
1183             else
1184             {
1185                 result.reason = Result.Error;
1186                 result.status = errno;
1187 
1188                 debug (Process)
1189                     Stdout.formatln("Could not wait on child process '{0}' ({1}): ({2}) {3}",
1190                                     _args[0], _pid, result.status, SysError.lastMsg);
1191             }
1192         }
1193         else
1194         {
1195             result.reason = Result.Error;
1196             result.status = -1;
1197 
1198             debug (Process)
1199                 Stdout.formatln("Child process '{0}' is not running", _args[0]);
1200         }
1201         return result;
1202     }
1203 
1204     /**
1205      * Kill a running process. This method will not return until the process
1206      * has been killed.
1207      *
1208      * Throws:
1209      * ProcessKillException if the process could not be killed;
1210      * ProcessWaitException if we could not wait on the process after
1211      * killing it.
1212      *
1213      * Remarks:
1214      * After calling this method you will not be able to call wait() on the
1215      * process.
1216      * Killing the process does not clean the attached pipes as the parent
1217      * process may still want/need the remaining content. However, it is
1218      * recommended to call close() on the process when it is no longer needed
1219      * as this will clean the pipes.
1220      */
1221     public void kill()
1222     {
1223         if (_running)
1224         {
1225             int rc;
1226 
1227             verify(_pid > 0);
1228 
1229             if (.kill(_pid, SIGTERM) != -1)
1230             {
1231                 // We clean up the process related data and set the _running
1232                 // flag to false once we're done waiting for the process to
1233                 // finish.
1234                 //
1235                 // IMPORTANT: we don't delete the open pipes so that the parent
1236                 //            process can get whatever the child process left on
1237                 //            these pipes before dying.
1238                 scope(exit)
1239                 {
1240                     _running = false;
1241                 }
1242 
1243                 // FIXME: is this loop really needed?
1244                 for (uint i = 0; i < 100; i++)
1245                 {
1246                     rc = waitpid(pid, null, WNOHANG | WUNTRACED);
1247                     if (rc == _pid)
1248                     {
1249                         break;
1250                     }
1251                     else if (rc == -1)
1252                     {
1253                         throw new ProcessWaitException(cast(int) _pid);
1254                     }
1255                     usleep(50000);
1256                 }
1257             }
1258             else
1259             {
1260                 throw new ProcessKillException(_pid);
1261             }
1262         }
1263         else
1264         {
1265             debug (Process)
1266                 Stdout.print("Tried to kill an invalid process");
1267         }
1268     }
1269 
1270     /**
1271      * Split a string containing the command line used to invoke a program
1272      * and return and array with the parsed arguments. The double-quotes (")
1273      * character can be used to specify arguments with embedded spaces.
1274      * e.g. first "second param" third
1275      */
1276     protected static cstring[] splitArgs(cstring command, cstring delims = " \t\r\n")
1277     {
1278         verify(
1279             !contains(delims, '"'),
1280             "The argument delimiter string cannot contain a double "
1281                 ~ "quotes ('\"') character"
1282         );
1283 
1284         enum State
1285         {
1286             Start,
1287             FindDelimiter,
1288             InsideQuotes
1289         }
1290 
1291         cstring[]   args = null;
1292         cstring[]   chunks = null;
1293         int         start = -1;
1294         char        c;
1295         int         i;
1296         State       state = State.Start;
1297 
1298         // Append an argument to the 'args' array using the 'chunks' array
1299         // and the current position in the 'command' string as the source.
1300         void appendChunksAsArg()
1301         {
1302             size_t argPos;
1303 
1304             if (chunks.length > 0)
1305             {
1306                 // Create the array element corresponding to the argument by
1307                 // appending the first chunk.
1308                 args   ~= chunks[0];
1309                 argPos  = args.length - 1;
1310 
1311                 for (uint chunkPos = 1; chunkPos < chunks.length; ++chunkPos)
1312                 {
1313                     args[argPos] ~= chunks[chunkPos];
1314                 }
1315 
1316                 if (start != -1)
1317                 {
1318                     args[argPos] ~= command[start .. i];
1319                 }
1320                 chunks.length = 0;
1321             }
1322             else
1323             {
1324                 if (start != -1)
1325                 {
1326                     args ~= command[start .. i];
1327                 }
1328             }
1329             start = -1;
1330         }
1331 
1332         for (i = 0; i < command.length; i++)
1333         {
1334             c = command[i];
1335 
1336             switch (state)
1337             {
1338                 // Start looking for an argument.
1339                 case State.Start:
1340                     if (c == '"')
1341                     {
1342                         state = State.InsideQuotes;
1343                     }
1344                     else if (!contains(delims, c))
1345                     {
1346                         start = i;
1347                         state = State.FindDelimiter;
1348                     }
1349                     else
1350                     {
1351                         appendChunksAsArg();
1352                     }
1353                     break;
1354 
1355                 // Find the ending delimiter for an argument.
1356                 case State.FindDelimiter:
1357                     if (c == '"')
1358                     {
1359                         // If we find a quotes character this means that we've
1360                         // found a quoted section of an argument. (e.g.
1361                         // abc"def"ghi). The quoted section will be appended
1362                         // to the preceding part of the argument. This is also
1363                         // what Unix shells do (i.e. a"b"c becomes abc).
1364                         if (start != -1)
1365                         {
1366                             chunks ~= command[start .. i];
1367                             start = -1;
1368                         }
1369                         state = State.InsideQuotes;
1370                     }
1371                     else if (contains(delims, c))
1372                     {
1373                         appendChunksAsArg();
1374                         state = State.Start;
1375                     }
1376                     break;
1377 
1378                 // Inside a quoted argument or section of an argument.
1379                 case State.InsideQuotes:
1380                     if (start == -1)
1381                     {
1382                         start = i;
1383                     }
1384 
1385                     if (c == '"')
1386                     {
1387                         chunks ~= command[start .. i];
1388                         start = -1;
1389                         state = State.Start;
1390                     }
1391                     break;
1392 
1393                 default:
1394                     assert(false, "Invalid state in Process.splitArgs");
1395             }
1396         }
1397 
1398         // Add the last argument (if there is one)
1399         appendChunksAsArg();
1400 
1401         return args;
1402     }
1403 
1404     /**
1405      * Close and delete any pipe that may have been left open in a previous
1406      * execution of a child process.
1407      */
1408     protected void cleanPipes()
1409     {
1410         // We re-uses Pipe objects, so simply close them, if they
1411         // have been used before
1412         if ( pin !is null )   pin.close();
1413         if ( pout !is null )  pout.close();
1414         if ( perr !is null )  perr.close();
1415         if ( pexec !is null ) pexec.close();
1416     }
1417 
1418     /**
1419      * Explicitly close any resources held by this process object. It is recommended
1420      * to always call this when you are done with the process.
1421      */
1422     public void close()
1423     {
1424         this.cleanPipes;
1425     }
1426 
1427     /**
1428      * Convert an array of strings to an array of pointers to char with
1429      * a terminating null character (C strings). The resulting array
1430      * has a null pointer at the end. This is the format expected by
1431      * the execv*() family of POSIX functions.
1432      */
1433     protected static char*[] toNullEndedArray(cstring[] src)
1434     {
1435         if (src !is null)
1436         {
1437             char*[] dest = new char*[src.length + 1];
1438             auto i = src.length;
1439 
1440             // Add terminating null pointer to the array
1441             dest[i] = null;
1442 
1443             while (i > 0)
1444             {
1445                 --i;
1446                 // Add a terminating null character to each string
1447                 auto cstr = src[i];
1448                 if (cstr.length == 0)
1449                     dest[i] = "\0".dup.ptr;
1450                 else if (cstr[$ - 1] == '\0')
1451                     dest[i] = cstr.dup.ptr;
1452                 else
1453                     dest[i] = (cstr ~ "\0").ptr;
1454             }
1455             return dest;
1456         }
1457         else
1458         {
1459             return null;
1460         }
1461     }
1462 
1463     /**
1464      * Convert an associative array of strings to an array of pointers to
1465      * char with a terminating null character (C strings). The resulting
1466      * array has a null pointer at the end. This is the format expected by
1467      * the execv*() family of POSIX functions for environment variables.
1468      */
1469     protected static char*[] toNullEndedArray(istring[istring] src)
1470     {
1471         char*[] dest;
1472 
1473         foreach (key, value; src)
1474         {
1475             dest ~= (key.dup ~ '=' ~ value ~ '\0').ptr;
1476         }
1477 
1478         dest ~= null;
1479         return dest;
1480     }
1481 
1482     /**
1483      * Execute a process by looking up a file in the system path, passing
1484      * the array of arguments and the the environment variables. This
1485      * method is a combination of the execve() and execvp() POSIX system
1486      * calls.
1487      */
1488     protected static int execvpe(cstring filename, char*[] argv, char*[] envp)
1489     {
1490         verify(filename.length > 0);
1491 
1492         int rc = -1;
1493         char* str;
1494 
1495         if (!contains(filename, FileConst.PathSeparatorChar) &&
1496             (str = getenv("PATH".ptr)) !is null)
1497         {
1498             auto pathList = delimit(str[0 .. strlen(str)], ":");
1499 
1500             mstring path_buf;
1501 
1502             foreach (path; pathList)
1503             {
1504                 if (path[$-1] != FileConst.PathSeparatorChar)
1505                 {
1506                     path_buf.length = path.length + 1 + filename.length + 1;
1507                     assumeSafeAppend(path_buf);
1508 
1509                     path_buf[] = path ~ FileConst.PathSeparatorChar ~ filename ~ '\0';
1510                 }
1511                 else
1512                 {
1513                     path_buf.length = path.length +filename.length + 1;
1514                     assumeSafeAppend(path_buf);
1515 
1516                     path_buf[] = path ~ filename ~ '\0';
1517                 }
1518 
1519                 rc = execve(path_buf.ptr, argv.ptr, (envp.length == 0 ? environ : envp.ptr));
1520 
1521                 // If the process execution failed because of an error
1522                 // other than ENOENT (No such file or directory) we
1523                 // abort the loop.
1524                 if (rc == -1 && SysError.lastCode !is ENOENT)
1525                 {
1526                     break;
1527                 }
1528             }
1529         }
1530         else
1531         {
1532             debug (Process)
1533                 Stdout.formatln("Calling execve('{0}', argv[{1}], {2})",
1534                                 (argv[0])[0 .. strlen(argv[0])],
1535                                 argv.length, (envp.length > 0 ? "envp" : "null"));
1536 
1537             rc = execve(argv[0], argv.ptr, (envp.length == 0 ? environ : envp.ptr));
1538         }
1539         return rc;
1540     }
1541 }
1542 
1543 
1544 /**
1545  * Exception thrown when the process cannot be created.
1546  */
1547 class ProcessCreateException: ProcessException
1548 {
1549     public this (cstring command, istring message = SysError.lastMsg,
1550                  istring file = __FILE__, uint line = __LINE__)
1551     {
1552         super(
1553             format("Could not create process for '{}': {}", command, message),
1554             file, line);
1555     }
1556 }
1557 
1558 /**
1559  * Exception thrown when the parent process cannot be forked.
1560  *
1561  * This exception will only be thrown on POSIX-compatible platforms.
1562  */
1563 class ProcessForkException: ProcessException
1564 {
1565     public this (int pid, istring file = __FILE__, uint line = __LINE__)
1566     {
1567         super(
1568             format("Could not fork process {}: {}", pid, SysError.lastMsg),
1569             file, line);
1570     }
1571 }
1572 
1573 /**
1574  * Exception thrown when the process cannot be killed.
1575  */
1576 class ProcessKillException: ProcessException
1577 {
1578     public this (int pid, istring file = __FILE__, uint line = __LINE__)
1579     {
1580         super(
1581             format("Could not kill process {}: {}", pid, SysError.lastMsg),
1582             file, line);
1583     }
1584 }
1585 
1586 /**
1587  * Exception thrown when the parent process tries to wait on the child
1588  * process and fails.
1589  */
1590 class ProcessWaitException: ProcessException
1591 {
1592     public this (int pid, istring file = __FILE__, uint line = __LINE__)
1593     {
1594         super(
1595             format("Could not wait on process {}: {}", pid, SysError.lastMsg),
1596             file, line);
1597     }
1598 }
1599 
1600 extern (C) uint sleep (uint s);
1601 
1602 unittest
1603 {
1604     istring message = "hello world";
1605     istring command = "echo " ~ message;
1606 
1607     try
1608     {
1609         auto p = new Process(command, null);
1610         p.execute();
1611         char[255] buffer;
1612 
1613         auto nread = p.stdout.read(buffer);
1614         test(nread != p.stdout.Eof);
1615         test(buffer[0..nread] == message ~ "\n");
1616 
1617         nread = p.stdout.read(buffer);
1618         test(nread == p.stdout.Eof);
1619 
1620         auto result = p.wait();
1621 
1622         test(result.reason == Process.Result.Exit && result.status == 0);
1623     }
1624     catch (ProcessException e)
1625     {
1626         test(false, e.message());
1627     }
1628 }
1629 
1630 // check differently qualified argument calls
1631 unittest
1632 {
1633     auto p = new Process("aaa", "bbb", "ccc");
1634     mstring s = "xxxx".dup;
1635     p.argsWithCommand([ s, "aaa", "bbb"]);
1636     p.programName("huh");
1637 }
1638 
1639 // check non-literals arguments
1640 unittest
1641 {
1642     istring[] args = [ "aaa", "bbb" ];
1643     auto p = new Process(args);
1644     p.argsWithCommand(args);
1645 }