Newer
Older
dub_jkp / source / stdx / process.d
  1. // Written in the D programming language.
  2.  
  3. /**
  4. Functions for starting and interacting with other processes, and for
  5. working with the current process' execution environment.
  6.  
  7. Process_handling:
  8. $(UL $(LI
  9. $(LREF spawnProcess) spawns a new _process, optionally assigning it an
  10. arbitrary set of standard input, output, and error streams.
  11. The function returns immediately, leaving the child _process to execute
  12. in parallel with its parent. All other functions in this module that
  13. spawn processes are built around $(D spawnProcess).)
  14. $(LI
  15. $(LREF wait) makes the parent _process wait for a child _process to
  16. terminate. In general one should always do this, to avoid
  17. child _processes becoming "zombies" when the parent _process exits.
  18. Scope guards are perfect for this – see the $(LREF spawnProcess)
  19. documentation for examples.)
  20. $(LI
  21. $(LREF pipeProcess) also spawns a child _process which runs
  22. in parallel with its parent. However, instead of taking
  23. arbitrary streams, it automatically creates a set of
  24. pipes that allow the parent to communicate with the child
  25. through the child's standard input, output, and/or error streams.
  26. This function corresponds roughly to C's $(D popen) function.)
  27. $(LI
  28. $(LREF execute) starts a new _process and waits for it
  29. to complete before returning. Additionally, it captures
  30. the _process' standard output and error streams and returns
  31. the output of these as a string.)
  32. $(LI
  33. $(LREF spawnShell), $(LREF pipeShell) and $(LREF shell) work like
  34. $(D spawnProcess), $(D pipeProcess) and $(D execute), respectively,
  35. except that they take a single command string and run it through
  36. the current user's default command interpreter.
  37. $(D shell) corresponds roughly to C's $(D system) function.)
  38. $(LI
  39. $(LREF kill) attempts to terminate a running process.)
  40. )
  41. Unless the directory of the executable file is explicitly specified, all
  42. functions will search for it in the directories specified in the PATH
  43. environment variable.
  44.  
  45. Other_functionality:
  46. $(UL
  47. $(LI
  48. $(LREF pipe) is used to create unidirectional pipes.)
  49. $(LI
  50. $(LREF environment) is an interface through which the current process'
  51. environment variables can be read and manipulated.)
  52. )
  53.  
  54. Authors:
  55. $(LINK2 https://github.com/kyllingstad, Lars Tandle Kyllingstad),
  56. $(LINK2 https://github.com/schveiguy, Steven Schveighoffer),
  57. $(LINK2 https://github.com/cybershadow, Vladimir Panteleev)
  58. Copyright:
  59. Copyright (c) 2013, the authors. All rights reserved.
  60. Source:
  61. $(PHOBOSSRC std/_process.d)
  62. Macros:
  63. WIKI=Phobos/StdProcess
  64. OBJECTREF=$(D $(LINK2 object.html#$0,$0))
  65. */
  66. module stdx.process;
  67.  
  68. version (Posix)
  69. {
  70. import core.stdc.errno;
  71. import core.stdc.string;
  72. import core.sys.posix.stdio;
  73. import core.sys.posix.unistd;
  74. import core.sys.posix.sys.wait;
  75. }
  76. version (Windows)
  77. {
  78. import core.stdc.stdio;
  79. import core.sys.windows.windows;
  80. import std.utf;
  81. import std.windows.syserror;
  82. }
  83. import std.algorithm;
  84. import std.array;
  85. import std.conv;
  86. import std.exception;
  87. import std.path;
  88. import std.stdio;
  89. import std.string;
  90. import std.typecons;
  91.  
  92.  
  93. // When the DMC runtime is used, we have to use some custom functions
  94. // to convert between Windows file handles and FILE*s.
  95. version (Win32) version (DigitalMars) version = DMC_RUNTIME;
  96.  
  97.  
  98. // Some of the following should be moved to druntime.
  99. private:
  100.  
  101. // Windows API declarations.
  102. version (Windows)
  103. {
  104. extern(Windows) BOOL GetHandleInformation(HANDLE hObject,
  105. LPDWORD lpdwFlags);
  106. extern(Windows) BOOL SetHandleInformation(HANDLE hObject,
  107. DWORD dwMask,
  108. DWORD dwFlags);
  109. extern(Windows) BOOL TerminateProcess(HANDLE hProcess,
  110. UINT uExitCode);
  111. extern(Windows) LPWSTR* CommandLineToArgvW(LPCWSTR lpCmdLine,
  112. int* pNumArgs);
  113. enum
  114. {
  115. HANDLE_FLAG_INHERIT = 0x1,
  116. HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x2,
  117. }
  118. enum CREATE_UNICODE_ENVIRONMENT = 0x400;
  119. }
  120.  
  121. // Microsoft Visual C Runtime (MSVCRT) declarations.
  122. version (Windows)
  123. {
  124. version (DMC_RUNTIME) { } else
  125. {
  126. import core.stdc.stdint;
  127. extern(C)
  128. {
  129. int _fileno(FILE* stream);
  130. HANDLE _get_osfhandle(int fd);
  131. int _open_osfhandle(HANDLE osfhandle, int flags);
  132. FILE* _fdopen(int fd, const (char)* mode);
  133. int _close(int fd);
  134. }
  135. enum
  136. {
  137. STDIN_FILENO = 0,
  138. STDOUT_FILENO = 1,
  139. STDERR_FILENO = 2,
  140. }
  141. enum
  142. {
  143. _O_RDONLY = 0x0000,
  144. _O_APPEND = 0x0004,
  145. _O_TEXT = 0x4000,
  146. }
  147. }
  148. }
  149.  
  150. // POSIX API declarations.
  151. version (Posix)
  152. {
  153. version (OSX)
  154. {
  155. // https://www.gnu.org/software/gnulib/manual/html_node/environ.html
  156. extern(C) char*** _NSGetEnviron();
  157. __gshared const char** environ;
  158. shared static this() { environ = *_NSGetEnviron(); }
  159. }
  160. else
  161. {
  162. // Made available by the C runtime:
  163. extern(C) extern __gshared const char** environ;
  164. }
  165. }
  166.  
  167.  
  168. // Actual module classes/functions start here.
  169. public:
  170.  
  171.  
  172. // =============================================================================
  173. // Functions and classes for process management.
  174. // =============================================================================
  175.  
  176.  
  177. /**
  178. Spawns a new _process, optionally assigning it an
  179. arbitrary set of standard input, output, and error streams.
  180. The function returns immediately, leaving the child _process to execute
  181. in parallel with its parent.
  182.  
  183. Command_line:
  184. There are four overloads of this function. The first two take an array
  185. of strings, $(D args), which should contain the program name as the
  186. zeroth element and any command-line arguments in subsequent elements.
  187. The third and fourth versions are included for convenience, and may be
  188. used when there are no command-line arguments. They take a single string,
  189. $(D program), which specifies the program name.
  190.  
  191. Unless a directory is specified in $(D args[0]) or $(D program),
  192. $(D spawnProcess) will search for the program in the directories listed
  193. in the PATH environment variable. To run an executable in the current
  194. directory, use $(D "./$(I executable_name)").
  195. ---
  196. // Run an executable called "prog" located in the current working
  197. // directory:
  198. auto pid = spawnProcess("./prog");
  199. scope(exit) wait(pid);
  200. // We can do something else while the program runs. The scope guard
  201. // ensures that the process is waited for at the end of the scope.
  202. ...
  203.  
  204. // Run DMD on the file "myprog.d", specifying a few compiler switches:
  205. auto dmdPid = spawnProcess(["dmd", "-O", "-release", "-inline", "myprog.d" ]);
  206. if (wait(dmdPid) != 0)
  207. writeln("Compilation failed!");
  208. ---
  209.  
  210. Environment_variables:
  211. With the first and third $(D spawnProcess) overloads, one can specify
  212. the environment variables of the child process using the $(D environmentVars)
  213. parameter. With the second and fourth overload, the child process inherits
  214. its parent's environment variables.
  215.  
  216. To make the child inherit the parent's environment $(I plus) one or more
  217. additional variables, first use $(D $(LREF environment).$(LREF toAA)) to
  218. obtain an associative array that contains the parent's environment
  219. variables, and add the new variables to it before passing it to
  220. $(D spawnProcess).
  221. ---
  222. auto envVars = environment.toAA();
  223. envVars["FOO"] = "bar";
  224. wait(spawnProcess("prog", envVars));
  225. ---
  226.  
  227. Standard_streams:
  228. The optional arguments $(D stdin_), $(D stdout_) and $(D stderr_) may
  229. be used to assign arbitrary $(XREF stdio,File) objects as the standard
  230. input, output and error streams, respectively, of the child process. The
  231. former must be opened for reading, while the latter two must be opened for
  232. writing. The default is for the child process to inherit the standard
  233. streams of its parent.
  234. ---
  235. // Run DMD on the file myprog.d, logging any error messages to a
  236. // file named errors.log.
  237. auto logFile = File("errors.log", "w");
  238. auto pid = spawnProcess(["dmd", "myprog.d"],
  239. std.stdio.stdin,
  240. std.stdio.stdout,
  241. logFile);
  242. if (wait(pid) != 0)
  243. writeln("Compilation failed. See errors.log for details.");
  244. ---
  245.  
  246. Note that if you pass a $(D File) object that is $(I not)
  247. one of the standard input/output/error streams of the parent process,
  248. that stream will by default be $(I closed) in the parent process when
  249. this function returns. See the $(LREF Config) documentation below for
  250. information about how to disable this behaviour.
  251.  
  252. Beware of buffering issues when passing $(D File) objects to
  253. $(D spawnProcess). The child process will inherit the low-level raw
  254. read/write offset associated with the underlying file descriptor, but
  255. it will not be aware of any buffered data. In cases where this matters
  256. (e.g. when a file should be aligned before being passed on to the
  257. child process), it may be a good idea to use unbuffered streams, or at
  258. least ensure all relevant buffers are flushed.
  259.  
  260. Params:
  261. args = An array which contains the program name as the first element
  262. and any command-line arguments in the following elements.
  263. program = The program name, $(I without) command-line arguments.
  264. environmentVars = The environment variables for the child process may
  265. be specified using this parameter. By default it is $(D null),
  266. which means that, the child process inherits the environment of
  267. the parent process.
  268. stdin_ = The standard input stream of the child process.
  269. This can be any $(XREF stdio,File) that is opened for reading.
  270. By default the child process inherits the parent's input
  271. stream.
  272. stdout_ = The standard output stream of the child process.
  273. This can be any $(XREF stdio,File) that is opened for writing.
  274. By default the child process inherits the parent's output
  275. stream.
  276. stderr_ = The standard error stream of the child process.
  277. This can be any $(XREF stdio,File) that is opened for writing.
  278. By default the child process inherits the parent's error
  279. stream.
  280. config = Options that control the behaviour of $(D spawnProcess).
  281. See the $(LREF Config) documentation for details.
  282.  
  283. Returns:
  284. A $(LREF Pid) object that corresponds to the spawned process.
  285.  
  286. Throws:
  287. $(LREF ProcessException) on failure to start the process.$(BR)
  288. $(XREF stdio,StdioException) on failure to pass one of the streams
  289. to the child process (Windows only).$(BR)
  290. $(CXREF exception,RangeError) if $(D args) is empty.
  291. */
  292. Pid spawnProcess(in char[][] args,
  293. const string[string] environmentVars,
  294. File stdin_ = std.stdio.stdin,
  295. File stdout_ = std.stdio.stdout,
  296. File stderr_ = std.stdio.stderr,
  297. Config config = Config.none)
  298. @trusted // TODO: Should be @safe
  299. {
  300. version (Windows) auto args2 = escapeShellArguments(args);
  301. else version (Posix) alias args2 = args;
  302. return spawnProcessImpl(args2, toEnvz(environmentVars),
  303. stdin_, stdout_, stderr_, config);
  304. }
  305.  
  306. /// ditto
  307. Pid spawnProcess(in char[][] args,
  308. File stdin_ = std.stdio.stdin,
  309. File stdout_ = std.stdio.stdout,
  310. File stderr_ = std.stdio.stderr,
  311. Config config = Config.none)
  312. @trusted // TODO: Should be @safe
  313. {
  314. version (Windows) auto args2 = escapeShellArguments(args);
  315. else version (Posix) alias args2 = args;
  316. return spawnProcessImpl(args2, null, stdin_, stdout_, stderr_, config);
  317. }
  318.  
  319. /// ditto
  320. Pid spawnProcess(in char[] program,
  321. const string[string] environmentVars,
  322. File stdin_ = std.stdio.stdin,
  323. File stdout_ = std.stdio.stdout,
  324. File stderr_ = std.stdio.stderr,
  325. Config config = Config.none)
  326. @trusted
  327. {
  328. return spawnProcess((&program)[0 .. 1], environmentVars,
  329. stdin_, stdout_, stderr_, config);
  330. }
  331.  
  332. /// ditto
  333. Pid spawnProcess(in char[] program,
  334. File stdin_ = std.stdio.stdin,
  335. File stdout_ = std.stdio.stdout,
  336. File stderr_ = std.stdio.stderr,
  337. Config config = Config.none)
  338. @trusted
  339. {
  340. return spawnProcess((&program)[0 .. 1],
  341. stdin_, stdout_, stderr_, config);
  342. }
  343.  
  344. /*
  345. Implementation of spawnProcess() for POSIX.
  346.  
  347. envz should be a zero-terminated array of zero-terminated strings
  348. on the form "var=value".
  349. */
  350. version (Posix)
  351. private Pid spawnProcessImpl(in char[][] args,
  352. const(char*)* envz,
  353. File stdin_,
  354. File stdout_,
  355. File stderr_,
  356. Config config)
  357. @trusted // TODO: Should be @safe
  358. {
  359. const(char)[] name = args[0];
  360. if (any!isDirSeparator(name))
  361. {
  362. if (!isExecutable(name))
  363. throw new ProcessException(text("Not an executable file: ", name));
  364. }
  365. else
  366. {
  367. name = searchPathFor(name);
  368. if (name is null)
  369. throw new ProcessException(text("Executable file not found: ", name));
  370. }
  371.  
  372. // Convert program name and arguments to C-style strings.
  373. auto argz = new const(char)*[args.length+1];
  374. argz[0] = toStringz(name);
  375. foreach (i; 1 .. args.length) argz[i] = toStringz(args[i]);
  376. argz[$-1] = null;
  377.  
  378. // Use parent's environment variables?
  379. if (envz is null) envz = environ;
  380.  
  381. // Get the file descriptors of the streams.
  382. // These could potentially be invalid, but that is OK. If so, later calls
  383. // to dup2() and close() will just silently fail without causing any harm.
  384. auto stdinFD = core.stdc.stdio.fileno(stdin_.getFP());
  385. auto stdoutFD = core.stdc.stdio.fileno(stdout_.getFP());
  386. auto stderrFD = core.stdc.stdio.fileno(stderr_.getFP());
  387.  
  388. auto id = fork();
  389. if (id < 0)
  390. throw ProcessException.newFromErrno("Failed to spawn new process");
  391. if (id == 0)
  392. {
  393. // Child process
  394.  
  395. // Redirect streams and close the old file descriptors.
  396. // In the case that stderr is redirected to stdout, we need
  397. // to backup the file descriptor since stdout may be redirected
  398. // as well.
  399. if (stderrFD == STDOUT_FILENO) stderrFD = dup(stderrFD);
  400. dup2(stdinFD, STDIN_FILENO);
  401. dup2(stdoutFD, STDOUT_FILENO);
  402. dup2(stderrFD, STDERR_FILENO);
  403.  
  404. // Close the old file descriptors, unless they are
  405. // either of the standard streams.
  406. if (stdinFD > STDERR_FILENO) close(stdinFD);
  407. if (stdoutFD > STDERR_FILENO) close(stdoutFD);
  408. if (stderrFD > STDERR_FILENO) close(stderrFD);
  409.  
  410. // Execute program.
  411. execve(argz[0], argz.ptr, envz);
  412.  
  413. // If execution fails, exit as quickly as possible.
  414. perror("spawnProcess(): Failed to execute program");
  415. _exit(1);
  416. assert (0);
  417. }
  418. else
  419. {
  420. // Parent process: Close streams and return.
  421. if (stdinFD > STDERR_FILENO && !(config & Config.noCloseStdin))
  422. stdin_.close();
  423. if (stdoutFD > STDERR_FILENO && !(config & Config.noCloseStdout))
  424. stdout_.close();
  425. if (stderrFD > STDERR_FILENO && !(config & Config.noCloseStderr))
  426. stderr_.close();
  427. return new Pid(id);
  428. }
  429. }
  430.  
  431. /*
  432. Implementation of spawnProcess() for Windows.
  433.  
  434. commandLine must contain the entire command line, properly
  435. quoted/escaped as required by CreateProcessW().
  436.  
  437. envz must be a pointer to a block of UTF-16 characters on the form
  438. "var1=value1\0var2=value2\0...varN=valueN\0\0".
  439. */
  440. version (Windows)
  441. private Pid spawnProcessImpl(in char[] commandLine,
  442. LPVOID envz,
  443. File stdin_,
  444. File stdout_,
  445. File stderr_,
  446. Config config)
  447. @trusted
  448. {
  449. auto commandz = toUTFz!(wchar*)(commandLine);
  450.  
  451. // Startup info for CreateProcessW().
  452. STARTUPINFO_W startinfo;
  453. startinfo.cb = startinfo.sizeof;
  454. startinfo.dwFlags = STARTF_USESTDHANDLES;
  455.  
  456. // Extract file descriptors and HANDLEs from the streams and make the
  457. // handles inheritable.
  458. static void prepareStream(ref File file, DWORD stdHandle, string which,
  459. out int fileDescriptor, out HANDLE handle)
  460. {
  461. fileDescriptor = _fileno(file.getFP());
  462. if (fileDescriptor < 0) handle = GetStdHandle(stdHandle);
  463. else
  464. {
  465. version (DMC_RUNTIME) handle = _fdToHandle(fileDescriptor);
  466. else /* MSVCRT */ handle = _get_osfhandle(fileDescriptor);
  467. }
  468. DWORD dwFlags;
  469. GetHandleInformation(handle, &dwFlags);
  470. if (!(dwFlags & HANDLE_FLAG_INHERIT))
  471. {
  472. if (!SetHandleInformation(handle,
  473. HANDLE_FLAG_INHERIT,
  474. HANDLE_FLAG_INHERIT))
  475. {
  476. throw new StdioException(
  477. "Failed to make "~which~" stream inheritable by child process ("
  478. ~sysErrorString(GetLastError()) ~ ')',
  479. 0);
  480. }
  481. }
  482. }
  483. int stdinFD = -1, stdoutFD = -1, stderrFD = -1;
  484. prepareStream(stdin_, STD_INPUT_HANDLE, "stdin" , stdinFD, startinfo.hStdInput );
  485. prepareStream(stdout_, STD_OUTPUT_HANDLE, "stdout", stdoutFD, startinfo.hStdOutput);
  486. prepareStream(stderr_, STD_ERROR_HANDLE, "stderr", stderrFD, startinfo.hStdError );
  487.  
  488. // Create process.
  489. PROCESS_INFORMATION pi;
  490. DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT |
  491. ((config & Config.gui) ? CREATE_NO_WINDOW : 0);
  492. if (!CreateProcessW(null, commandz, null, null, true, dwCreationFlags,
  493. envz, null, &startinfo, &pi))
  494. throw ProcessException.newFromLastError("Failed to spawn new process");
  495.  
  496. // figure out if we should close any of the streams
  497. if (stdinFD > STDERR_FILENO && !(config & Config.noCloseStdin))
  498. stdin_.close();
  499. if (stdoutFD > STDERR_FILENO && !(config & Config.noCloseStdout))
  500. stdout_.close();
  501. if (stderrFD > STDERR_FILENO && !(config & Config.noCloseStderr))
  502. stderr_.close();
  503.  
  504. // close the thread handle in the process info structure
  505. CloseHandle(pi.hThread);
  506.  
  507. return new Pid(pi.dwProcessId, pi.hProcess);
  508. }
  509.  
  510. // Searches the PATH variable for the given executable file,
  511. // (checking that it is in fact executable).
  512. version (Posix)
  513. private string searchPathFor(in char[] executable)
  514. @trusted //TODO: @safe nothrow
  515. {
  516. auto pathz = core.stdc.stdlib.getenv("PATH");
  517. if (pathz == null) return null;
  518.  
  519. foreach (dir; splitter(to!string(pathz), ':'))
  520. {
  521. auto execPath = buildPath(dir, executable);
  522. if (isExecutable(execPath)) return execPath;
  523. }
  524.  
  525. return null;
  526. }
  527.  
  528. // Converts a string[string] array to a C array of C strings
  529. // on the form "key=value".
  530. version (Posix)
  531. private const(char)** toEnvz(const string[string] env)
  532. @trusted //TODO: @safe pure nothrow
  533. {
  534. alias const(char)* stringz_t;
  535. auto envz = new stringz_t[](env.length+1);
  536. int i = 0;
  537. foreach (k, v; env) envz[i++] = (k~'='~v~'\0').ptr;
  538. envz[i] = null;
  539. return envz.ptr;
  540. }
  541.  
  542. // Converts a string[string] array to a block of 16-bit
  543. // characters on the form "key=value\0key=value\0...\0\0"
  544. version (Windows)
  545. private LPVOID toEnvz(const string[string] env)
  546. @trusted //TODO: @safe pure nothrow
  547. {
  548. auto envz = appender!(wchar[])();
  549. foreach(k, v; env)
  550. {
  551. envz.put(k);
  552. envz.put('=');
  553. envz.put(v);
  554. envz.put('\0');
  555. }
  556. envz.put('\0');
  557. return envz.data.ptr;
  558. }
  559.  
  560. // Checks whether the file exists and can be executed by the
  561. // current user.
  562. version (Posix)
  563. private bool isExecutable(in char[] path) @trusted //TODO: @safe nothrow
  564. {
  565. return (access(toStringz(path), X_OK) == 0);
  566. }
  567.  
  568. unittest
  569. {
  570. TestScript prog1 = "exit 0";
  571. assert (wait(spawnProcess(prog1.path)) == 0);
  572.  
  573. TestScript prog2 = "exit 123";
  574. auto pid2 = spawnProcess([prog2.path]);
  575. assert (wait(pid2) == 123);
  576. assert (wait(pid2) == 123); // Exit code is cached.
  577.  
  578. version (Windows) TestScript prog3 =
  579. "if not -%1-==-foo- ( exit 1 )
  580. if not -%2-==-bar- ( exit 1 )
  581. exit 0";
  582. else version (Posix) TestScript prog3 =
  583. `if test "$1" != "foo"; then exit 1; fi
  584. if test "$2" != "bar"; then exit 1; fi
  585. exit 0`;
  586. assert (wait(spawnProcess([ prog3.path, "foo", "bar"])) == 0);
  587. assert (wait(spawnProcess(prog3.path)) == 1);
  588.  
  589. version (Windows) TestScript prog4 =
  590. "if %hello%==world ( exit 0 )
  591. exit 1";
  592. version (Posix) TestScript prog4 =
  593. "if test $hello = world; then exit 0; fi
  594. exit 1";
  595. auto env = [ "hello" : "world" ];
  596. assert (wait(spawnProcess(prog4.path, env)) == 0);
  597. assert (wait(spawnProcess([prog4.path], env)) == 0);
  598.  
  599. version (Windows) TestScript prog5 =
  600. "set /p INPUT=
  601. echo %INPUT% output %1
  602. echo %INPUT% error %2 1>&2";
  603. else version (Posix) TestScript prog5 =
  604. "read INPUT
  605. echo $INPUT output $1
  606. echo $INPUT error $2 >&2";
  607. auto pipe5i = pipe();
  608. auto pipe5o = pipe();
  609. auto pipe5e = pipe();
  610. auto pid5 = spawnProcess([ prog5.path, "foo", "bar" ],
  611. pipe5i.readEnd, pipe5o.writeEnd, pipe5e.writeEnd);
  612. pipe5i.writeEnd.writeln("input");
  613. pipe5i.writeEnd.flush();
  614. assert (pipe5o.readEnd.readln().chomp() == "input output foo");
  615. assert (pipe5e.readEnd.readln().chomp().stripRight() == "input error bar");
  616. wait(pid5);
  617.  
  618. import std.ascii, std.file, std.uuid;
  619. auto path6i = buildPath(tempDir(), randomUUID().toString());
  620. auto path6o = buildPath(tempDir(), randomUUID().toString());
  621. auto path6e = buildPath(tempDir(), randomUUID().toString());
  622. std.file.write(path6i, "INPUT"~std.ascii.newline);
  623. auto file6i = File(path6i, "r");
  624. auto file6o = File(path6o, "w");
  625. auto file6e = File(path6e, "w");
  626. auto pid6 = spawnProcess([prog5.path, "bar", "baz" ],
  627. file6i, file6o, file6e);
  628. wait(pid6);
  629. assert (readText(path6o).chomp() == "INPUT output bar");
  630. assert (readText(path6e).chomp().stripRight() == "INPUT error baz");
  631. remove(path6i);
  632. remove(path6o);
  633. remove(path6e);
  634. }
  635.  
  636.  
  637. /**
  638. A variation on $(LREF spawnProcess) that runs the given _command through
  639. the current user's preferred _command interpreter (aka. shell).
  640.  
  641. The string $(D command) is passed verbatim to the shell, and is therefore
  642. subject to its rules about _command structure, argument/filename quoting
  643. and escaping of special characters.
  644. The path to the shell executable is determined by the $(LREF userShell)
  645. function.
  646.  
  647. In all other respects this function works just like $(D spawnProcess).
  648. Please refer to the $(LREF spawnProcess) documentation for descriptions
  649. of the other function parameters, the return value and any exceptions
  650. that may be thrown.
  651. ---
  652. // Run the command/program "foo" on the file named "my file.txt", and
  653. // redirect its output into foo.log.
  654. auto pid = spawnShell(`foo "my file.txt" > foo.log`);
  655. wait(pid);
  656. ---
  657.  
  658. See_also:
  659. $(LREF escapeShellCommand), which may be helpful in constructing a
  660. properly quoted and escaped shell command line for the current plattform,
  661. from an array of separate arguments.
  662. */
  663. Pid spawnShell(in char[] command,
  664. const string[string] environmentVars,
  665. File stdin_ = std.stdio.stdin,
  666. File stdout_ = std.stdio.stdout,
  667. File stderr_ = std.stdio.stderr,
  668. Config config = Config.none)
  669. @trusted // TODO: Should be @safe
  670. {
  671. return spawnShellImpl(command, toEnvz(environmentVars),
  672. stdin_, stdout_, stderr_, config);
  673. }
  674.  
  675. /// ditto
  676. Pid spawnShell(in char[] command,
  677. File stdin_ = std.stdio.stdin,
  678. File stdout_ = std.stdio.stdout,
  679. File stderr_ = std.stdio.stderr,
  680. Config config = Config.none)
  681. @trusted // TODO: Should be @safe
  682. {
  683. return spawnShellImpl(command, null, stdin_, stdout_, stderr_, config);
  684. }
  685.  
  686. // Implementation of spawnShell() for Windows.
  687. version(Windows)
  688. private Pid spawnShellImpl(in char[] command,
  689. LPVOID envz,
  690. File stdin_ = std.stdio.stdin,
  691. File stdout_ = std.stdio.stdout,
  692. File stderr_ = std.stdio.stderr,
  693. Config config = Config.none)
  694. @trusted // TODO: Should be @safe
  695. {
  696. auto scmd = escapeShellArguments(userShell, shellSwitch) ~ " " ~ command;
  697. return spawnProcessImpl(scmd, envz, stdin_, stdout_, stderr_, config);
  698. }
  699.  
  700. // Implementation of spawnShell() for POSIX.
  701. version(Posix)
  702. private Pid spawnShellImpl(in char[] command,
  703. const char** envz,
  704. File stdin_ = std.stdio.stdin,
  705. File stdout_ = std.stdio.stdout,
  706. File stderr_ = std.stdio.stderr,
  707. Config config = Config.none)
  708. @trusted // TODO: Should be @safe
  709. {
  710. const(char)[][3] args;
  711. args[0] = userShell;
  712. args[1] = shellSwitch;
  713. args[2] = command;
  714. return spawnProcessImpl(args, envz, stdin_, stdout_, stderr_, config);
  715. }
  716.  
  717.  
  718.  
  719. /**
  720. Flags that control the behaviour of $(LREF spawnProcess) and
  721. $(LREF spawnShell).
  722.  
  723. Use bitwise OR to combine flags.
  724.  
  725. Example:
  726. ---
  727. auto logFile = File("myapp_error.log", "w");
  728.  
  729. // Start program in a console window (Windows only), redirect
  730. // its error stream to logFile, and leave logFile open in the
  731. // parent process as well.
  732. auto pid = spawnProcess("myapp", stdin, stdout, logFile,
  733. Config.noCloseStderr | Config.gui);
  734. scope(exit)
  735. {
  736. auto exitCode = wait(pid);
  737. logFile.writeln("myapp exited with code ", exitCode);
  738. logFile.close();
  739. }
  740. ---
  741. */
  742. enum Config
  743. {
  744. none = 0,
  745.  
  746. /**
  747. Unless the child process inherits the standard
  748. input/output/error streams of its parent, one almost
  749. always wants the streams closed in the parent when
  750. $(LREF spawnProcess) returns. Therefore, by default, this
  751. is done. If this is not desirable, pass any of these
  752. options to spawnProcess.
  753. */
  754. noCloseStdin = 1,
  755. noCloseStdout = 2, /// ditto
  756. noCloseStderr = 4, /// ditto
  757.  
  758. /**
  759. On Windows, the child process will by default be run in
  760. a console window. This option wil cause it to run in "GUI mode"
  761. instead, i.e., without a console. On POSIX, it has no effect.
  762. */
  763. gui = 8,
  764. }
  765.  
  766.  
  767. /// A handle that corresponds to a spawned process.
  768. final class Pid
  769. {
  770. /**
  771. The process ID number.
  772.  
  773. This is a number that uniquely identifies the process on the operating
  774. system, for at least as long as the process is running. Once $(LREF wait)
  775. has been called on the $(LREF Pid), this method will return an
  776. invalid process ID.
  777. */
  778. @property int processID() const @safe pure nothrow
  779. {
  780. return _processID;
  781. }
  782.  
  783. /**
  784. An operating system handle to the process.
  785.  
  786. This handle is used to specify the process in OS-specific APIs.
  787. On POSIX, this function returns a $(D core.sys.posix.sys.types.pid_t)
  788. with the same value as $(LREF processID), while on Windows it returns
  789. a $(D core.sys.windows.windows.HANDLE).
  790.  
  791. Once $(LREF wait) has been called on the $(LREF Pid), this method
  792. will return an invalid handle.
  793. */
  794. // Note: Since HANDLE is a reference, this function cannot be const.
  795. version (Windows)
  796. @property HANDLE osHandle() @safe pure nothrow
  797. {
  798. return _handle;
  799. }
  800. else version (Posix)
  801. @property pid_t osHandle() @safe pure nothrow
  802. {
  803. return _processID;
  804. }
  805.  
  806. private:
  807. /*
  808. Pid.performWait() does the dirty work for wait() and nonBlockingWait().
  809.  
  810. If block == true, this function blocks until the process terminates,
  811. sets _processID to terminated, and returns the exit code or terminating
  812. signal as described in the wait() documentation.
  813.  
  814. If block == false, this function returns immediately, regardless
  815. of the status of the process. If the process has terminated, the
  816. function has the exact same effect as the blocking version. If not,
  817. it returns 0 and does not modify _processID.
  818. */
  819. version (Posix)
  820. int performWait(bool block) @trusted
  821. {
  822. if (_processID == terminated) return _exitCode;
  823. int exitCode;
  824. while(true)
  825. {
  826. int status;
  827. auto check = waitpid(_processID, &status, block ? 0 : WNOHANG);
  828. if (check == -1)
  829. {
  830. if (errno == ECHILD)
  831. {
  832. throw new ProcessException(
  833. "Process does not exist or is not a child process.");
  834. }
  835. else
  836. {
  837. // waitpid() was interrupted by a signal. We simply
  838. // restart it.
  839. assert (errno == EINTR);
  840. continue;
  841. }
  842. }
  843. if (!block && check == 0) return 0;
  844. if (WIFEXITED(status))
  845. {
  846. exitCode = WEXITSTATUS(status);
  847. break;
  848. }
  849. else if (WIFSIGNALED(status))
  850. {
  851. exitCode = -WTERMSIG(status);
  852. break;
  853. }
  854. // We check again whether the call should be blocking,
  855. // since we don't care about other status changes besides
  856. // "exited" and "terminated by signal".
  857. if (!block) return 0;
  858.  
  859. // Process has stopped, but not terminated, so we continue waiting.
  860. }
  861. // Mark Pid as terminated, and cache and return exit code.
  862. _processID = terminated;
  863. _exitCode = exitCode;
  864. return exitCode;
  865. }
  866. else version (Windows)
  867. {
  868. int performWait(bool block) @trusted
  869. {
  870. if (_processID == terminated) return _exitCode;
  871. assert (_handle != INVALID_HANDLE_VALUE);
  872. if (block)
  873. {
  874. auto result = WaitForSingleObject(_handle, INFINITE);
  875. if (result != WAIT_OBJECT_0)
  876. throw ProcessException.newFromLastError("Wait failed.");
  877. }
  878. if (!GetExitCodeProcess(_handle, cast(LPDWORD)&_exitCode))
  879. throw ProcessException.newFromLastError();
  880. if (!block && _exitCode == STILL_ACTIVE) return 0;
  881. CloseHandle(_handle);
  882. _handle = INVALID_HANDLE_VALUE;
  883. _processID = terminated;
  884. return _exitCode;
  885. }
  886.  
  887. ~this()
  888. {
  889. if(_handle != INVALID_HANDLE_VALUE)
  890. {
  891. CloseHandle(_handle);
  892. _handle = INVALID_HANDLE_VALUE;
  893. }
  894. }
  895. }
  896.  
  897. // Special values for _processID.
  898. enum invalid = -1, terminated = -2;
  899.  
  900. // OS process ID number. Only nonnegative IDs correspond to
  901. // running processes.
  902. int _processID = invalid;
  903.  
  904. // Exit code cached by wait(). This is only expected to hold a
  905. // sensible value if _processID == terminated.
  906. int _exitCode;
  907.  
  908. // Pids are only meant to be constructed inside this module, so
  909. // we make the constructor private.
  910. version (Windows)
  911. {
  912. HANDLE _handle;
  913. this(int pid, HANDLE handle) @safe pure nothrow
  914. {
  915. _processID = pid;
  916. _handle = handle;
  917. }
  918. }
  919. else
  920. {
  921. this(int id) @safe pure nothrow
  922. {
  923. _processID = id;
  924. }
  925. }
  926. }
  927.  
  928.  
  929. /**
  930. Waits for the process associated with $(D pid) to terminate, and returns
  931. its exit status.
  932.  
  933. In general one should always _wait for child processes to terminate
  934. before exiting the parent process. Otherwise, they may become
  935. "$(WEB en.wikipedia.org/wiki/Zombie_process,zombies)" – processes
  936. that are defunct, yet still occupy a slot in the OS process table.
  937.  
  938. If the process has already terminated, this function returns directly.
  939. The exit code is cached, so that if wait() is called multiple times on
  940. the same $(LREF Pid) it will always return the same value.
  941.  
  942. POSIX_specific:
  943. If the process is terminated by a signal, this function returns a
  944. negative number whose absolute value is the signal number.
  945. Since POSIX restricts normal exit codes to the range 0-255, a
  946. negative return value will always indicate termination by signal.
  947. Signal codes are defined in the $(D core.sys.posix.signal) module
  948. (which corresponds to the $(D signal.h) POSIX header).
  949.  
  950. Throws:
  951. $(LREF ProcessException) on failure.
  952.  
  953. Examples:
  954. See the $(LREF spawnProcess) documentation.
  955.  
  956. See_also:
  957. $(LREF tryWait), for a non-blocking function.
  958. */
  959. int wait(Pid pid) @safe
  960. {
  961. assert(pid !is null, "Called wait on a null Pid.");
  962. return pid.performWait(true);
  963. }
  964.  
  965.  
  966. /**
  967. A non-blocking version of $(LREF wait).
  968.  
  969. If the process associated with $(D pid) has already terminated,
  970. $(D tryWait) has the exact same effect as $(D wait).
  971. In this case, it returns a tuple where the $(D terminated) field
  972. is set to $(D true) and the $(D status) field has the same
  973. interpretation as the return value of $(D wait).
  974.  
  975. If the process has $(I not) yet terminated, this function differs
  976. from $(D wait) in that does not wait for this to happen, but instead
  977. returns immediately. The $(D terminated) field of the returned
  978. tuple will then be set to $(D false), while the $(D status) field
  979. will always be 0 (zero). $(D wait) or $(D tryWait) should then be
  980. called again on the same $(D Pid) at some later time; not only to
  981. get the exit code, but also to avoid the process becoming a "zombie"
  982. when it finally terminates. (See $(LREF wait) for details).
  983.  
  984. Throws:
  985. $(LREF ProcessException) on failure.
  986.  
  987. Example:
  988. ---
  989. auto pid = spawnProcess("dmd myapp.d");
  990. scope(exit) wait(pid);
  991. ...
  992. auto dmd = tryWait(pid);
  993. if (dmd.terminated)
  994. {
  995. if (dmd.status == 0) writeln("Compilation succeeded!");
  996. else writeln("Compilation failed");
  997. }
  998. else writeln("Still compiling...");
  999. ...
  1000. ---
  1001. Note that in this example, the first $(D wait) call will have no
  1002. effect if the process has already terminated by the time $(D tryWait)
  1003. is called. In the opposite case, however, the $(D scope) statement
  1004. ensures that we always wait for the process if it hasn't terminated
  1005. by the time we reach the end of the scope.
  1006. */
  1007. Tuple!(bool, "terminated", int, "status") tryWait(Pid pid) @safe
  1008. {
  1009. assert(pid !is null, "Called tryWait on a null Pid.");
  1010. auto code = pid.performWait(false);
  1011. return typeof(return)(pid._processID == Pid.terminated, code);
  1012. }
  1013.  
  1014.  
  1015. /**
  1016. Attempts to terminate the process associated with $(D pid).
  1017.  
  1018. The effect of this function, as well as the meaning of $(D codeOrSignal),
  1019. is highly platform dependent. Details are given below. Common to all
  1020. platforms is that this function only $(I initiates) termination of the process,
  1021. and returns immediately. It does not wait for the process to end,
  1022. nor does it guarantee that the process does in fact get terminated.
  1023.  
  1024. Always call $(LREF wait) to wait for a process to complete, even if $(D kill)
  1025. has been called on it.
  1026.  
  1027. Windows_specific:
  1028. The process will be
  1029. $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms686714%28v=vs.100%29.aspx,
  1030. forcefully and abruptly terminated). If $(D codeOrSignal) is specified, it
  1031. will be used as the exit code of the process. If not, the process wil exit
  1032. with code 1. Do not use $(D codeOrSignal = 259), as this is a special value
  1033. (aka. $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms683189.aspx,
  1034. STILL_ACTIVE)) used by Windows to signal that a process has in fact $(I not)
  1035. terminated yet.
  1036. ---
  1037. auto pid = spawnProcess("some_app");
  1038. kill(pid, 10);
  1039. assert (wait(pid) == 10);
  1040. ---
  1041.  
  1042. POSIX_specific:
  1043. A $(LINK2 http://en.wikipedia.org/wiki/Unix_signal,signal) will be sent to
  1044. the process, whose value is given by $(D codeOrSignal). Depending on the
  1045. signal sent, this may or may not terminate the process. Symbolic constants
  1046. for various $(LINK2 http://en.wikipedia.org/wiki/Unix_signal#POSIX_signals,
  1047. POSIX signals) are defined in $(D core.sys.posix.signal), which corresponds to the
  1048. $(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html,
  1049. $(D signal.h) POSIX header). If $(D codeOrSignal) is omitted, the
  1050. $(D SIGTERM) signal will be sent. (This matches the behaviour of the
  1051. $(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/utilities/kill.html,
  1052. $(D _kill)) shell command.)
  1053. ---
  1054. import core.sys.posix.signal: SIGKILL;
  1055. auto pid = spawnProcess("some_app");
  1056. kill(pid, SIGKILL);
  1057. assert (wait(pid) == -SIGKILL); // Negative return value on POSIX!
  1058. ---
  1059.  
  1060. Throws:
  1061. $(LREF ProcessException) if the operating system reports an error.
  1062. (Note that this does not include failure to terminate the process,
  1063. which is considered a "normal" outcome.)$(BR)
  1064. $(OBJECTREF Error) if $(D codeOrSignal) is negative.
  1065. */
  1066. void kill(Pid pid)
  1067. {
  1068. version (Windows) kill(pid, 1);
  1069. else version (Posix)
  1070. {
  1071. import core.sys.posix.signal: SIGTERM;
  1072. kill(pid, SIGTERM);
  1073. }
  1074. }
  1075.  
  1076. /// ditto
  1077. void kill(Pid pid, int codeOrSignal)
  1078. {
  1079. version (Windows) enum errMsg = "Invalid exit code";
  1080. else version (Posix) enum errMsg = "Invalid signal";
  1081. if (codeOrSignal < 0) throw new Error(errMsg);
  1082.  
  1083. version (Windows)
  1084. {
  1085. if (!TerminateProcess(pid.osHandle, codeOrSignal))
  1086. throw ProcessException.newFromLastError();
  1087. }
  1088. else version (Posix)
  1089. {
  1090. import core.sys.posix.signal;
  1091. if (kill(pid.osHandle, codeOrSignal) == -1)
  1092. throw ProcessException.newFromErrno();
  1093. }
  1094. }
  1095.  
  1096. unittest
  1097. {
  1098. // The test script goes into an infinite loop.
  1099. version (Windows)
  1100. {
  1101. TestScript prog = "loop:
  1102. goto loop";
  1103. }
  1104. else version (Posix)
  1105. {
  1106. import core.sys.posix.signal: SIGTERM, SIGKILL;
  1107. TestScript prog = "while true; do; done";
  1108. }
  1109. auto pid = spawnProcess(prog.path);
  1110. kill(pid);
  1111. version (Windows) assert (wait(pid) == 1);
  1112. else version (Posix) assert (wait(pid) == -SIGTERM);
  1113.  
  1114. pid = spawnProcess(prog.path);
  1115. auto s = tryWait(pid);
  1116. assert (!s.terminated && s.status == 0);
  1117. version (Windows) kill(pid, 123);
  1118. else version (Posix) kill(pid, SIGKILL);
  1119. do { s = tryWait(pid); } while (!s.terminated);
  1120. version (Windows) assert (s.status == 123);
  1121. else version (Posix) assert (s.status == -SIGKILL);
  1122. }
  1123.  
  1124.  
  1125. private File encapPipeAsFile(FILE* fil)
  1126. {
  1127. static struct Impl
  1128. {
  1129. FILE * handle = null; // Is null iff this Impl is closed by another File
  1130. uint refs = uint.max / 2;
  1131. bool isPipe;
  1132. }
  1133. auto f = File.wrapFile(fil);
  1134. auto imp = *cast(Impl**)&f;
  1135. imp.refs = 1;
  1136. imp.isPipe = true;
  1137. return f;
  1138. }
  1139.  
  1140. /**
  1141. Creates a unidirectional _pipe.
  1142.  
  1143. Data is written to one end of the _pipe and read from the other.
  1144. ---
  1145. auto p = pipe();
  1146. p.writeEnd.writeln("Hello World");
  1147. assert (p.readEnd.readln().chomp() == "Hello World");
  1148. ---
  1149. Pipes can, for example, be used for interprocess communication
  1150. by spawning a new process and passing one end of the _pipe to
  1151. the child, while the parent uses the other end.
  1152. (See also $(LREF pipeProcess) and $(LREF pipeShell) for an easier
  1153. way of doing this.)
  1154. ---
  1155. // Use cURL to download the dlang.org front page, pipe its
  1156. // output to grep to extract a list of links to ZIP files,
  1157. // and write the list to the file "D downloads.txt":
  1158. auto p = pipe();
  1159. auto outFile = File("D downloads.txt", "w");
  1160. auto cpid = spawnProcess(["curl", "http://dlang.org/download.html"],
  1161. std.stdio.stdin, p.writeEnd);
  1162. scope(exit) wait(cpid);
  1163. auto gpid = spawnProcess(["grep", "-o", `http://\S*\.zip`],
  1164. p.readEnd, outFile);
  1165. scope(exit) wait(gpid);
  1166. ---
  1167.  
  1168. Returns:
  1169. A $(LREF Pipe) object that corresponds to the created _pipe.
  1170.  
  1171. Throws:
  1172. $(XREF stdio,StdioException) on failure.
  1173. */
  1174. version (Posix)
  1175. Pipe pipe() @trusted //TODO: @safe
  1176. {
  1177. int[2] fds;
  1178. errnoEnforce(core.sys.posix.unistd.pipe(fds) == 0,
  1179. "Unable to create pipe");
  1180. Pipe p;
  1181. auto readFP = fdopen(fds[0], "r");
  1182. if (readFP == null)
  1183. throw new StdioException("Cannot open read end of pipe");
  1184. p._read = encapPipeAsFile(readFP);
  1185. auto writeFP = fdopen(fds[1], "w");
  1186. if (writeFP == null)
  1187. throw new StdioException("Cannot open write end of pipe");
  1188. p._write = encapPipeAsFile(writeFP);
  1189. return p;
  1190. }
  1191. else version (Windows)
  1192. Pipe pipe() @trusted //TODO: @safe
  1193. {
  1194. // use CreatePipe to create an anonymous pipe
  1195. HANDLE readHandle;
  1196. HANDLE writeHandle;
  1197. if (!CreatePipe(&readHandle, &writeHandle, null, 0))
  1198. {
  1199. throw new StdioException(
  1200. "Error creating pipe (" ~ sysErrorString(GetLastError()) ~ ')',
  1201. 0);
  1202. }
  1203.  
  1204. // Create file descriptors from the handles
  1205. version (DMC_RUNTIME)
  1206. {
  1207. auto readFD = _handleToFD(readHandle, FHND_DEVICE);
  1208. auto writeFD = _handleToFD(writeHandle, FHND_DEVICE);
  1209. }
  1210. else // MSVCRT
  1211. {
  1212. auto readFD = _open_osfhandle(readHandle, _O_RDONLY);
  1213. auto writeFD = _open_osfhandle(writeHandle, _O_APPEND);
  1214. }
  1215. version (DMC_RUNTIME) alias .close _close;
  1216. if (readFD == -1 || writeFD == -1)
  1217. {
  1218. // Close file descriptors, then throw.
  1219. if (readFD >= 0) _close(readFD);
  1220. else CloseHandle(readHandle);
  1221. if (writeFD >= 0) _close(writeFD);
  1222. else CloseHandle(writeHandle);
  1223. throw new StdioException("Error creating pipe");
  1224. }
  1225.  
  1226. // Create FILE pointers from the file descriptors
  1227. Pipe p;
  1228. version (DMC_RUNTIME)
  1229. {
  1230. // This is a re-implementation of DMC's fdopen, but without the
  1231. // mucking with the file descriptor. POSIX standard requires the
  1232. // new fdopen'd file to retain the given file descriptor's
  1233. // position.
  1234. FILE * local_fdopen(int fd, const(char)* mode)
  1235. {
  1236. auto fp = core.stdc.stdio.fopen("NUL", mode);
  1237. if(!fp) return null;
  1238. FLOCK(fp);
  1239. auto iob = cast(_iobuf*)fp;
  1240. .close(iob._file);
  1241. iob._file = fd;
  1242. iob._flag &= ~_IOTRAN;
  1243. FUNLOCK(fp);
  1244. return fp;
  1245. }
  1246.  
  1247. auto readFP = local_fdopen(readFD, "r");
  1248. auto writeFP = local_fdopen(writeFD, "a");
  1249. }
  1250. else // MSVCRT
  1251. {
  1252. auto readFP = _fdopen(readFD, "r");
  1253. auto writeFP = _fdopen(writeFD, "a");
  1254. }
  1255. if (readFP == null || writeFP == null)
  1256. {
  1257. // Close streams, then throw.
  1258. if (readFP != null) fclose(readFP);
  1259. else _close(readFD);
  1260. if (writeFP != null) fclose(writeFP);
  1261. else _close(writeFD);
  1262. throw new StdioException("Cannot open pipe");
  1263. }
  1264. p._read = encapPipeAsFile(readFP);
  1265. p._write = encapPipeAsFile(writeFP);
  1266. return p;
  1267. }
  1268.  
  1269.  
  1270. /// An interface to a pipe created by the $(LREF pipe) function.
  1271. struct Pipe
  1272. {
  1273. /// The read end of the pipe.
  1274. @property File readEnd() @trusted /*TODO: @safe nothrow*/ { return _read; }
  1275.  
  1276.  
  1277. /// The write end of the pipe.
  1278. @property File writeEnd() @trusted /*TODO: @safe nothrow*/ { return _write; }
  1279.  
  1280.  
  1281. /**
  1282. Closes both ends of the pipe.
  1283.  
  1284. Normally it is not necessary to do this manually, as $(XREF stdio,File)
  1285. objects are automatically closed when there are no more references
  1286. to them.
  1287.  
  1288. Note that if either end of the pipe has been passed to a child process,
  1289. it will only be closed in the parent process. (What happens in the
  1290. child process is platform dependent.)
  1291. */
  1292. void close() @trusted //TODO: @safe nothrow
  1293. {
  1294. _read.close();
  1295. _write.close();
  1296. }
  1297.  
  1298. private:
  1299. File _read, _write;
  1300. }
  1301.  
  1302. unittest
  1303. {
  1304. auto p = pipe();
  1305. p.writeEnd.writeln("Hello World");
  1306. p.writeEnd.flush();
  1307. assert (p.readEnd.readln().chomp() == "Hello World");
  1308. }
  1309.  
  1310.  
  1311. /**
  1312. Starts a new process, creating pipes to redirect its standard
  1313. input, output and/or error streams.
  1314.  
  1315. These functions return immediately, leaving the child process to
  1316. execute in parallel with the parent.
  1317. $(LREF pipeShell) invokes the user's _command interpreter, as
  1318. determined by $(LREF userShell), to execute the given program or
  1319. _command.
  1320.  
  1321. Returns:
  1322. A $(LREF ProcessPipes) object which contains $(XREF stdio,File)
  1323. handles that communicate with the redirected streams of the child
  1324. process, along with the $(LREF Pid) of the process.
  1325.  
  1326. Throws:
  1327. $(LREF ProcessException) on failure to start the process.$(BR)
  1328. $(XREF stdio,StdioException) on failure to create pipes.$(BR)
  1329. $(OBJECTREF Error) if $(D redirectFlags) is an invalid combination of flags.
  1330.  
  1331. Example:
  1332. ---
  1333. auto pipes = pipeProcess("my_application", Redirect.stdout | Redirect.stderr);
  1334. scope(exit) wait(pipes.pid);
  1335.  
  1336. // Store lines of output.
  1337. string[] output;
  1338. foreach (line; pipes.stdout.byLine) output ~= line.idup;
  1339.  
  1340. // Store lines of errors.
  1341. string[] errors;
  1342. foreach (line; pipes.stderr.byLine) errors ~= line.idup;
  1343. ---
  1344. */
  1345. ProcessPipes pipeProcess(string program,
  1346. Redirect redirectFlags = Redirect.all)
  1347. @trusted
  1348. {
  1349. return pipeProcessImpl!spawnProcess(program, redirectFlags);
  1350. }
  1351.  
  1352. /// ditto
  1353. ProcessPipes pipeProcess(string[] args,
  1354. Redirect redirectFlags = Redirect.all)
  1355. @trusted //TODO: @safe
  1356. {
  1357. return pipeProcessImpl!spawnProcess(args, redirectFlags);
  1358. }
  1359.  
  1360. /// ditto
  1361. ProcessPipes pipeShell(string command, Redirect redirectFlags = Redirect.all)
  1362. @safe
  1363. {
  1364. return pipeProcessImpl!spawnShell(command, redirectFlags);
  1365. }
  1366.  
  1367. // Implementation of the pipeProcess() family of functions.
  1368. private ProcessPipes pipeProcessImpl(alias spawnFunc, Cmd)
  1369. (Cmd command, Redirect redirectFlags)
  1370. @trusted //TODO: @safe
  1371. {
  1372. File childStdin, childStdout, childStderr;
  1373. ProcessPipes pipes;
  1374. pipes._redirectFlags = redirectFlags;
  1375.  
  1376. if (redirectFlags & Redirect.stdin)
  1377. {
  1378. auto p = pipe();
  1379. childStdin = p.readEnd;
  1380. pipes._stdin = p.writeEnd;
  1381. }
  1382. else
  1383. {
  1384. childStdin = std.stdio.stdin;
  1385. }
  1386.  
  1387. if (redirectFlags & Redirect.stdout)
  1388. {
  1389. if ((redirectFlags & Redirect.stdoutToStderr) != 0)
  1390. throw new Error("Invalid combination of options: Redirect.stdout | "
  1391. ~"Redirect.stdoutToStderr");
  1392. auto p = pipe();
  1393. childStdout = p.writeEnd;
  1394. pipes._stdout = p.readEnd;
  1395. }
  1396. else
  1397. {
  1398. childStdout = std.stdio.stdout;
  1399. }
  1400.  
  1401. if (redirectFlags & Redirect.stderr)
  1402. {
  1403. if ((redirectFlags & Redirect.stderrToStdout) != 0)
  1404. throw new Error("Invalid combination of options: Redirect.stderr | "
  1405. ~"Redirect.stderrToStdout");
  1406. auto p = pipe();
  1407. childStderr = p.writeEnd;
  1408. pipes._stderr = p.readEnd;
  1409. }
  1410. else
  1411. {
  1412. childStderr = std.stdio.stderr;
  1413. }
  1414.  
  1415. if (redirectFlags & Redirect.stdoutToStderr)
  1416. {
  1417. if (redirectFlags & Redirect.stderrToStdout)
  1418. {
  1419. // We know that neither of the other options have been
  1420. // set, so we assign the std.stdio.std* streams directly.
  1421. childStdout = std.stdio.stderr;
  1422. childStderr = std.stdio.stdout;
  1423. }
  1424. else
  1425. {
  1426. childStdout = childStderr;
  1427. }
  1428. }
  1429. else if (redirectFlags & Redirect.stderrToStdout)
  1430. {
  1431. childStderr = childStdout;
  1432. }
  1433.  
  1434. pipes._pid = spawnFunc(command, null, childStdin, childStdout, childStderr);
  1435. return pipes;
  1436. }
  1437.  
  1438.  
  1439. /**
  1440. Flags that can be passed to $(LREF pipeProcess) and $(LREF pipeShell)
  1441. to specify which of the child process' standard streams are redirected.
  1442. Use bitwise OR to combine flags.
  1443. */
  1444. enum Redirect
  1445. {
  1446. /// Redirect the standard input, output or error streams, respectively.
  1447. stdin = 1,
  1448. stdout = 2, /// ditto
  1449. stderr = 4, /// ditto
  1450.  
  1451. /**
  1452. Redirect _all three streams. This is equivalent to
  1453. $(D Redirect.stdin | Redirect.stdout | Redirect.stderr).
  1454. */
  1455. all = stdin | stdout | stderr,
  1456.  
  1457. /**
  1458. Redirect the standard error stream into the standard output stream.
  1459. This can not be combined with $(D Redirect.stderr).
  1460. */
  1461. stderrToStdout = 8,
  1462.  
  1463. /**
  1464. Redirect the standard output stream into the standard error stream.
  1465. This can not be combined with $(D Redirect.stdout).
  1466. */
  1467. stdoutToStderr = 16,
  1468. }
  1469.  
  1470. unittest
  1471. {
  1472. version (Windows) TestScript prog =
  1473. "call :sub %1 %2 0
  1474. call :sub %1 %2 1
  1475. call :sub %1 %2 2
  1476. call :sub %1 %2 3
  1477. exit 3
  1478.  
  1479. :sub
  1480. set /p INPUT=
  1481. if -%INPUT%-==-stop- ( exit %3 )
  1482. echo %INPUT% %1
  1483. echo %INPUT% %2 1>&2";
  1484. else version (Posix) TestScript prog =
  1485. `for EXITCODE in 0 1 2 3; do
  1486. read INPUT
  1487. if test "$INPUT" = stop; then break; fi
  1488. echo "$INPUT $1"
  1489. echo "$INPUT $2" >&2
  1490. done
  1491. exit $EXITCODE`;
  1492. auto pp = pipeProcess([prog.path, "bar", "baz"]);
  1493. pp.stdin.writeln("foo");
  1494. pp.stdin.flush();
  1495. assert (pp.stdout.readln().chomp() == "foo bar");
  1496. assert (pp.stderr.readln().chomp().stripRight() == "foo baz");
  1497. pp.stdin.writeln("1234567890");
  1498. pp.stdin.flush();
  1499. assert (pp.stdout.readln().chomp() == "1234567890 bar");
  1500. assert (pp.stderr.readln().chomp().stripRight() == "1234567890 baz");
  1501. pp.stdin.writeln("stop");
  1502. pp.stdin.flush();
  1503. assert (wait(pp.pid) == 2);
  1504.  
  1505. pp = pipeProcess([prog.path, "12345", "67890"],
  1506. Redirect.stdin | Redirect.stdout | Redirect.stderrToStdout);
  1507. pp.stdin.writeln("xyz");
  1508. pp.stdin.flush();
  1509. assert (pp.stdout.readln().chomp() == "xyz 12345");
  1510. assert (pp.stdout.readln().chomp().stripRight() == "xyz 67890");
  1511. pp.stdin.writeln("stop");
  1512. pp.stdin.flush();
  1513. assert (wait(pp.pid) == 1);
  1514.  
  1515. pp = pipeShell(prog.path~" AAAAA BBB",
  1516. Redirect.stdin | Redirect.stdoutToStderr | Redirect.stderr);
  1517. pp.stdin.writeln("ab");
  1518. pp.stdin.flush();
  1519. assert (pp.stderr.readln().chomp() == "ab AAAAA");
  1520. assert (pp.stderr.readln().chomp().stripRight() == "ab BBB");
  1521. pp.stdin.writeln("stop");
  1522. pp.stdin.flush();
  1523. assert (wait(pp.pid) == 1);
  1524. }
  1525.  
  1526.  
  1527. /**
  1528. Object which contains $(XREF stdio,File) handles that allow communication
  1529. with a child process through its standard streams.
  1530. */
  1531. struct ProcessPipes
  1532. {
  1533. /// The $(LREF Pid) of the child process.
  1534. @property Pid pid() @safe nothrow
  1535. {
  1536. assert(_pid !is null);
  1537. return _pid;
  1538. }
  1539.  
  1540. /**
  1541. An $(XREF stdio,File) that allows writing to the child process'
  1542. standard input stream.
  1543.  
  1544. Throws:
  1545. $(OBJECTREF Error) if the child process' standard input stream hasn't
  1546. been redirected.
  1547. */
  1548. @property File stdin() @trusted //TODO: @safe nothrow
  1549. {
  1550. if ((_redirectFlags & Redirect.stdin) == 0)
  1551. throw new Error("Child process' standard input stream hasn't "
  1552. ~"been redirected.");
  1553. return _stdin;
  1554. }
  1555.  
  1556. /**
  1557. An $(XREF stdio,File) that allows reading from the child process'
  1558. standard output stream.
  1559.  
  1560. Throws:
  1561. $(OBJECTREF Error) if the child process' standard output stream hasn't
  1562. been redirected.
  1563. */
  1564. @property File stdout() @trusted //TODO: @safe nothrow
  1565. {
  1566. if ((_redirectFlags & Redirect.stdout) == 0)
  1567. throw new Error("Child process' standard output stream hasn't "
  1568. ~"been redirected.");
  1569. return _stdout;
  1570. }
  1571.  
  1572. /**
  1573. An $(XREF stdio,File) that allows reading from the child process'
  1574. standard error stream.
  1575.  
  1576. Throws:
  1577. $(OBJECTREF Error) if the child process' standard error stream hasn't
  1578. been redirected.
  1579. */
  1580. @property File stderr() @trusted //TODO: @safe nothrow
  1581. {
  1582. if ((_redirectFlags & Redirect.stderr) == 0)
  1583. throw new Error("Child process' standard error stream hasn't "
  1584. ~"been redirected.");
  1585. return _stderr;
  1586. }
  1587.  
  1588. private:
  1589. Redirect _redirectFlags;
  1590. Pid _pid;
  1591. File _stdin, _stdout, _stderr;
  1592. }
  1593.  
  1594.  
  1595. /**
  1596. Executes the given program and returns its exit code and output.
  1597.  
  1598. This function blocks until the program terminates.
  1599. The $(D output) string includes what the program writes to its
  1600. standard error stream as well as its standard output stream.
  1601. ---
  1602. auto dmd = execute("dmd", "myapp.d");
  1603. if (dmd.status != 0) writeln("Compilation failed:\n", dmd.output);
  1604. ---
  1605.  
  1606. POSIX_specific:
  1607. If the process is terminated by a signal, the $(D status) field of
  1608. the return value will contain a negative number whose absolute
  1609. value is the signal number. (See $(LREF wait) for details.)
  1610.  
  1611. Throws:
  1612. $(LREF ProcessException) on failure to start the process.$(BR)
  1613. $(XREF stdio,StdioException) on failure to capture output.
  1614. */
  1615. Tuple!(int, "status", string, "output") execute(string[] args...)
  1616. @trusted //TODO: @safe
  1617. {
  1618. auto p = pipeProcess(args, Redirect.stdout | Redirect.stderrToStdout);
  1619. return processOutput(p, size_t.max);
  1620. }
  1621.  
  1622. unittest
  1623. {
  1624. // To avoid printing the newline characters, we use the echo|set trick on
  1625. // Windows, and printf on POSIX (neither echo -n nor echo \c are portable).
  1626. version (Windows) TestScript prog =
  1627. "echo|set /p=%1
  1628. echo|set /p=%2 1>&2
  1629. exit 123";
  1630. else version (Posix) TestScript prog =
  1631. `printf '%s' $1
  1632. printf '%s' $2 >&2
  1633. exit 123`;
  1634. auto r = execute([prog.path, "foo", "bar"]);
  1635. assert (r.status == 123);
  1636. assert (r.output.stripRight() == "foobar");
  1637. auto s = execute(prog.path, "Hello", "World");
  1638. assert (s.status == 123);
  1639. assert (s.output.stripRight() == "HelloWorld");
  1640. }
  1641.  
  1642.  
  1643. /**
  1644. Executes $(D _command) in the user's default _shell and returns its
  1645. exit code and output.
  1646.  
  1647. This function blocks until the command terminates.
  1648. The $(D output) string includes what the command writes to its
  1649. standard error stream as well as its standard output stream.
  1650. The path to the _command interpreter is given by $(LREF userShell).
  1651. ---
  1652. auto ls = shell("ls -l");
  1653. writefln("ls exited with code %s and said: %s", ls.status, ls.output);
  1654. ---
  1655.  
  1656. POSIX_specific:
  1657. If the process is terminated by a signal, the $(D status) field of
  1658. the return value will contain a negative number whose absolute
  1659. value is the signal number. (See $(LREF wait) for details.)
  1660.  
  1661. Throws:
  1662. $(LREF ProcessException) on failure to start the process.$(BR)
  1663. $(XREF stdio,StdioException) on failure to capture output.
  1664. */
  1665. Tuple!(int, "status", string, "output") shell(string command)
  1666. @trusted //TODO: @safe
  1667. {
  1668. auto p = pipeShell(command, Redirect.stdout | Redirect.stderrToStdout);
  1669. return processOutput(p, size_t.max);
  1670. }
  1671.  
  1672. unittest
  1673. {
  1674. auto r1 = shell("echo foo");
  1675. assert (r1.status == 0);
  1676. assert (r1.output.chomp() == "foo");
  1677. auto r2 = shell("echo bar 1>&2");
  1678. assert (r2.status == 0);
  1679. assert (r2.output.chomp().stripRight() == "bar");
  1680. auto r3 = shell("exit 123");
  1681. assert (r3.status == 123);
  1682. assert (r3.output.empty);
  1683. }
  1684.  
  1685. // Collects the output and exit code for execute() and shell().
  1686. private Tuple!(int, "status", string, "output") processOutput(
  1687. ref ProcessPipes pp,
  1688. size_t maxData)
  1689. {
  1690. Appender!(ubyte[]) a;
  1691. enum chunkSize = 4096;
  1692. foreach (ubyte[] chunk; pp.stdout.byChunk(chunkSize))
  1693. {
  1694. a.put(chunk);
  1695. if (a.data().length + chunkSize > maxData) break;
  1696. }
  1697.  
  1698. typeof(return) r;
  1699. r.output = cast(string) a.data;
  1700. r.status = wait(pp.pid);
  1701. return r;
  1702. }
  1703.  
  1704.  
  1705.  
  1706. /// An exception that signals a problem with starting or waiting for a process.
  1707. class ProcessException : Exception
  1708. {
  1709. // Standard constructor.
  1710. this(string msg, string file = __FILE__, size_t line = __LINE__)
  1711. {
  1712. super(msg, file, line);
  1713. }
  1714.  
  1715. // Creates a new ProcessException based on errno.
  1716. static ProcessException newFromErrno(string customMsg = null,
  1717. string file = __FILE__,
  1718. size_t line = __LINE__)
  1719. {
  1720. import core.stdc.errno;
  1721. import std.c.string;
  1722. version (linux)
  1723. {
  1724. char[1024] buf;
  1725. auto errnoMsg = to!string(
  1726. std.c.string.strerror_r(errno, buf.ptr, buf.length));
  1727. }
  1728. else
  1729. {
  1730. auto errnoMsg = to!string(std.c.string.strerror(errno));
  1731. }
  1732. auto msg = customMsg.empty() ? errnoMsg
  1733. : customMsg ~ " (" ~ errnoMsg ~ ')';
  1734. return new ProcessException(msg, file, line);
  1735. }
  1736.  
  1737. // Creates a new ProcessException based on GetLastError() (Windows only).
  1738. version (Windows)
  1739. static ProcessException newFromLastError(string customMsg = null,
  1740. string file = __FILE__,
  1741. size_t line = __LINE__)
  1742. {
  1743. auto lastMsg = sysErrorString(GetLastError());
  1744. auto msg = customMsg.empty() ? lastMsg
  1745. : customMsg ~ " (" ~ lastMsg ~ ')';
  1746. return new ProcessException(msg, file, line);
  1747. }
  1748. }
  1749.  
  1750.  
  1751. /**
  1752. Determines the path to the current user's default command interpreter.
  1753.  
  1754. On Windows, this function returns the contents of the COMSPEC environment
  1755. variable, if it exists. Otherwise, it returns the string $(D "cmd.exe").
  1756.  
  1757. On POSIX, $(D userShell) returns the contents of the SHELL environment
  1758. variable, if it exists and is non-empty. Otherwise, it returns
  1759. $(D "/bin/sh").
  1760. */
  1761. @property string userShell() @safe //TODO: nothrow
  1762. {
  1763. version (Windows) return environment.get("COMSPEC", "cmd.exe");
  1764. else version (Posix) return environment.get("SHELL", "/bin/sh");
  1765. }
  1766.  
  1767.  
  1768. // A command-line switch that indicates to the shell that it should
  1769. // interpret the following argument as a command to be executed.
  1770. version (Posix) private immutable string shellSwitch = "-c";
  1771. version (Windows) private immutable string shellSwitch = "/C";
  1772.  
  1773.  
  1774. /// Returns the process ID number of the current process.
  1775. @property int thisProcessID() @trusted //TODO: @safe nothrow
  1776. {
  1777. version (Windows) return GetCurrentProcessId();
  1778. else version (Posix) return getpid();
  1779. }
  1780.  
  1781.  
  1782. // Unittest support code: TestScript takes a string that contains a
  1783. // shell script for the current platform, and writes it to a temporary
  1784. // file. On Windows the file name gets a .cmd extension, while on
  1785. // POSIX its executable permission bit is set. The file is
  1786. // automatically deleted when the object goes out of scope.
  1787. version (unittest)
  1788. private struct TestScript
  1789. {
  1790. this(string code)
  1791. {
  1792. import std.ascii, std.file, std.uuid;
  1793. version (Windows)
  1794. {
  1795. auto ext = ".cmd";
  1796. auto firstLine = "@echo off";
  1797. }
  1798. else version (Posix)
  1799. {
  1800. auto ext = "";
  1801. auto firstLine = "#!/bin/sh";
  1802. }
  1803. path = buildPath(tempDir(), randomUUID().toString()~ext);
  1804. std.file.write(path, firstLine~std.ascii.newline~code~std.ascii.newline);
  1805. version (Posix)
  1806. {
  1807. import core.sys.posix.sys.stat;
  1808. chmod(toStringz(path), octal!777);
  1809. }
  1810. }
  1811.  
  1812. ~this()
  1813. {
  1814. import std.file;
  1815. if (!path.empty && exists(path)) remove(path);
  1816. }
  1817.  
  1818. string path;
  1819. }
  1820.  
  1821.  
  1822. // =============================================================================
  1823. // Functions for shell command quoting/escaping.
  1824. // =============================================================================
  1825.  
  1826.  
  1827. /*
  1828. Command line arguments exist in three forms:
  1829. 1) string or char* array, as received by main.
  1830. Also used internally on POSIX systems.
  1831. 2) Command line string, as used in Windows'
  1832. CreateProcess and CommandLineToArgvW functions.
  1833. A specific quoting and escaping algorithm is used
  1834. to distinguish individual arguments.
  1835. 3) Shell command string, as written at a shell prompt
  1836. or passed to cmd /C - this one may contain shell
  1837. control characters, e.g. > or | for redirection /
  1838. piping - thus, yet another layer of escaping is
  1839. used to distinguish them from program arguments.
  1840.  
  1841. Except for escapeWindowsArgument, the intermediary
  1842. format (2) is hidden away from the user in this module.
  1843. */
  1844.  
  1845. /**
  1846. Escapes an argv-style argument array to be used with $(LREF spawnShell),
  1847. $(LREF pipeShell) or $(LREF shell).
  1848. ---
  1849. string url = "http://dlang.org/";
  1850. shell(escapeShellCommand("wget", url, "-O", "dlang-index.html"));
  1851. ---
  1852.  
  1853. Concatenate multiple $(D escapeShellCommand) and
  1854. $(LREF escapeShellFileName) results to use shell redirection or
  1855. piping operators.
  1856. ---
  1857. shell(
  1858. escapeShellCommand("curl", "http://dlang.org/download.html") ~
  1859. "|" ~
  1860. escapeShellCommand("grep", "-o", `http://\S*\.zip`) ~
  1861. ">" ~
  1862. escapeShellFileName("D download links.txt"));
  1863. ---
  1864. */
  1865. string escapeShellCommand(in char[][] args...)
  1866. //TODO: @safe pure nothrow
  1867. {
  1868. return escapeShellCommandString(escapeShellArguments(args));
  1869. }
  1870.  
  1871.  
  1872. private string escapeShellCommandString(string command)
  1873. //TODO: @safe pure nothrow
  1874. {
  1875. version (Windows)
  1876. return escapeWindowsShellCommand(command);
  1877. else
  1878. return command;
  1879. }
  1880.  
  1881. string escapeWindowsShellCommand(in char[] command)
  1882. //TODO: @safe pure nothrow (prevented by Appender)
  1883. {
  1884. auto result = appender!string();
  1885. result.reserve(command.length);
  1886.  
  1887. foreach (c; command)
  1888. switch (c)
  1889. {
  1890. case '\0':
  1891. assert(0, "Cannot put NUL in command line");
  1892. case '\r':
  1893. case '\n':
  1894. assert(0, "CR/LF are not escapable");
  1895. case '\x01': .. case '\x09':
  1896. case '\x0B': .. case '\x0C':
  1897. case '\x0E': .. case '\x1F':
  1898. case '"':
  1899. case '^':
  1900. case '&':
  1901. case '<':
  1902. case '>':
  1903. case '|':
  1904. result.put('^');
  1905. goto default;
  1906. default:
  1907. result.put(c);
  1908. }
  1909. return result.data;
  1910. }
  1911.  
  1912. private string escapeShellArguments(in char[][] args...)
  1913. @trusted pure nothrow
  1914. {
  1915. char[] buf;
  1916.  
  1917. @safe nothrow
  1918. char[] allocator(size_t size)
  1919. {
  1920. if (buf.length == 0)
  1921. return buf = new char[size];
  1922. else
  1923. {
  1924. auto p = buf.length;
  1925. buf.length = buf.length + 1 + size;
  1926. buf[p++] = ' ';
  1927. return buf[p..p+size];
  1928. }
  1929. }
  1930.  
  1931. foreach (arg; args)
  1932. escapeShellArgument!allocator(arg);
  1933. return assumeUnique(buf);
  1934. }
  1935.  
  1936. private auto escapeShellArgument(alias allocator)(in char[] arg) @safe nothrow
  1937. {
  1938. // The unittest for this function requires special
  1939. // preparation - see below.
  1940.  
  1941. version (Windows)
  1942. return escapeWindowsArgumentImpl!allocator(arg);
  1943. else
  1944. return escapePosixArgumentImpl!allocator(arg);
  1945. }
  1946.  
  1947. /**
  1948. Quotes a command-line argument in a manner conforming to the behavior of
  1949. $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx,
  1950. CommandLineToArgvW).
  1951. */
  1952. string escapeWindowsArgument(in char[] arg) @trusted pure nothrow
  1953. {
  1954. // Rationale for leaving this function as public:
  1955. // this algorithm of escaping paths is also used in other software,
  1956. // e.g. DMD's response files.
  1957.  
  1958. auto buf = escapeWindowsArgumentImpl!charAllocator(arg);
  1959. return assumeUnique(buf);
  1960. }
  1961.  
  1962.  
  1963. private char[] charAllocator(size_t size) @safe pure nothrow
  1964. {
  1965. return new char[size];
  1966. }
  1967.  
  1968.  
  1969. private char[] escapeWindowsArgumentImpl(alias allocator)(in char[] arg)
  1970. @safe nothrow
  1971. if (is(typeof(allocator(size_t.init)[0] = char.init)))
  1972. {
  1973. // References:
  1974. // * http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx
  1975. // * http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx
  1976.  
  1977. // Calculate the total string size.
  1978.  
  1979. // Trailing backslashes must be escaped
  1980. bool escaping = true;
  1981. // Result size = input size + 2 for surrounding quotes + 1 for the
  1982. // backslash for each escaped character.
  1983. size_t size = 1 + arg.length + 1;
  1984.  
  1985. foreach_reverse (c; arg)
  1986. {
  1987. if (c == '"')
  1988. {
  1989. escaping = true;
  1990. size++;
  1991. }
  1992. else
  1993. if (c == '\\')
  1994. {
  1995. if (escaping)
  1996. size++;
  1997. }
  1998. else
  1999. escaping = false;
  2000. }
  2001.  
  2002. // Construct result string.
  2003.  
  2004. auto buf = allocator(size);
  2005. size_t p = size;
  2006. buf[--p] = '"';
  2007. escaping = true;
  2008. foreach_reverse (c; arg)
  2009. {
  2010. if (c == '"')
  2011. escaping = true;
  2012. else
  2013. if (c != '\\')
  2014. escaping = false;
  2015.  
  2016. buf[--p] = c;
  2017. if (escaping)
  2018. buf[--p] = '\\';
  2019. }
  2020. buf[--p] = '"';
  2021. assert(p == 0);
  2022.  
  2023. return buf;
  2024. }
  2025.  
  2026. version(Windows) version(unittest)
  2027. {
  2028. import core.sys.windows.windows;
  2029. import core.stdc.stddef;
  2030.  
  2031. extern (Windows) wchar_t** CommandLineToArgvW(wchar_t*, int*);
  2032. extern (C) size_t wcslen(in wchar *);
  2033.  
  2034. unittest
  2035. {
  2036. string[] testStrings = [
  2037. `Hello`,
  2038. `Hello, world`,
  2039. `Hello, "world"`,
  2040. `C:\`,
  2041. `C:\dmd`,
  2042. `C:\Program Files\`,
  2043. ];
  2044.  
  2045. enum CHARS = `_x\" *&^`; // _ is placeholder for nothing
  2046. foreach (c1; CHARS)
  2047. foreach (c2; CHARS)
  2048. foreach (c3; CHARS)
  2049. foreach (c4; CHARS)
  2050. testStrings ~= [c1, c2, c3, c4].replace("_", "");
  2051.  
  2052. foreach (s; testStrings)
  2053. {
  2054. auto q = escapeWindowsArgument(s);
  2055. LPWSTR lpCommandLine = (to!(wchar[])("Dummy.exe " ~ q) ~ "\0"w).ptr;
  2056. int numArgs;
  2057. LPWSTR* args = CommandLineToArgvW(lpCommandLine, &numArgs);
  2058. scope(exit) LocalFree(args);
  2059. assert(numArgs==2, s ~ " => " ~ q ~ " #" ~ text(numArgs-1));
  2060. auto arg = to!string(args[1][0..wcslen(args[1])]);
  2061. assert(arg == s, s ~ " => " ~ q ~ " => " ~ arg);
  2062. }
  2063. }
  2064. }
  2065.  
  2066. private string escapePosixArgument(in char[] arg) @trusted pure nothrow
  2067. {
  2068. auto buf = escapePosixArgumentImpl!charAllocator(arg);
  2069. return assumeUnique(buf);
  2070. }
  2071.  
  2072. private char[] escapePosixArgumentImpl(alias allocator)(in char[] arg)
  2073. @safe nothrow
  2074. if (is(typeof(allocator(size_t.init)[0] = char.init)))
  2075. {
  2076. // '\'' means: close quoted part of argument, append an escaped
  2077. // single quote, and reopen quotes
  2078.  
  2079. // Below code is equivalent to:
  2080. // return `'` ~ std.array.replace(arg, `'`, `'\''`) ~ `'`;
  2081.  
  2082. size_t size = 1 + arg.length + 1;
  2083. foreach (c; arg)
  2084. if (c == '\'')
  2085. size += 3;
  2086.  
  2087. auto buf = allocator(size);
  2088. size_t p = 0;
  2089. buf[p++] = '\'';
  2090. foreach (c; arg)
  2091. if (c == '\'')
  2092. {
  2093. buf[p..p+4] = `'\''`;
  2094. p += 4;
  2095. }
  2096. else
  2097. buf[p++] = c;
  2098. buf[p++] = '\'';
  2099. assert(p == size);
  2100.  
  2101. return buf;
  2102. }
  2103.  
  2104. /**
  2105. Escapes a filename to be used for shell redirection with $(LREF spawnShell),
  2106. $(LREF pipeShell) or $(LREF shell).
  2107. */
  2108. string escapeShellFileName(in char[] fileName) @trusted pure nothrow
  2109. {
  2110. // The unittest for this function requires special
  2111. // preparation - see below.
  2112.  
  2113. version (Windows)
  2114. return cast(string)('"' ~ fileName ~ '"');
  2115. else
  2116. return escapePosixArgument(fileName);
  2117. }
  2118.  
  2119. // Loop generating strings with random characters
  2120. //version = unittest_burnin;
  2121.  
  2122. version(unittest_burnin)
  2123. unittest
  2124. {
  2125. // There are no readily-available commands on all platforms suitable
  2126. // for properly testing command escaping. The behavior of CMD's "echo"
  2127. // built-in differs from the POSIX program, and Windows ports of POSIX
  2128. // environments (Cygwin, msys, gnuwin32) may interfere with their own
  2129. // "echo" ports.
  2130.  
  2131. // To run this unit test, create std_process_unittest_helper.d with the
  2132. // following content and compile it:
  2133. // import std.stdio, std.array; void main(string[] args) { write(args.join("\0")); }
  2134. // Then, test this module with:
  2135. // rdmd --main -unittest -version=unittest_burnin process.d
  2136.  
  2137. auto helper = absolutePath("std_process_unittest_helper");
  2138. assert(shell(helper ~ " hello").split("\0")[1..$] == ["hello"], "Helper malfunction");
  2139.  
  2140. void test(string[] s, string fn)
  2141. {
  2142. string e;
  2143. string[] g;
  2144.  
  2145. e = escapeShellCommand(helper ~ s);
  2146. {
  2147. scope(failure) writefln("shell() failed.\nExpected:\t%s\nEncoded:\t%s", s, [e]);
  2148. g = shell(e).split("\0")[1..$];
  2149. }
  2150. assert(s == g, format("shell() test failed.\nExpected:\t%s\nGot:\t\t%s\nEncoded:\t%s", s, g, [e]));
  2151.  
  2152. e = escapeShellCommand(helper ~ s) ~ ">" ~ escapeShellFileName(fn);
  2153. {
  2154. scope(failure) writefln("system() failed.\nExpected:\t%s\nFilename:\t%s\nEncoded:\t%s", s, [fn], [e]);
  2155. system(e);
  2156. g = readText(fn).split("\0")[1..$];
  2157. }
  2158. remove(fn);
  2159. assert(s == g, format("system() test failed.\nExpected:\t%s\nGot:\t\t%s\nEncoded:\t%s", s, g, [e]));
  2160. }
  2161.  
  2162. while (true)
  2163. {
  2164. string[] args;
  2165. foreach (n; 0..uniform(1, 4))
  2166. {
  2167. string arg;
  2168. foreach (l; 0..uniform(0, 10))
  2169. {
  2170. dchar c;
  2171. while (true)
  2172. {
  2173. version (Windows)
  2174. {
  2175. // As long as DMD's system() uses CreateProcessA,
  2176. // we can't reliably pass Unicode
  2177. c = uniform(0, 128);
  2178. }
  2179. else
  2180. c = uniform!ubyte();
  2181.  
  2182. if (c == 0)
  2183. continue; // argv-strings are zero-terminated
  2184. version (Windows)
  2185. if (c == '\r' || c == '\n')
  2186. continue; // newlines are unescapable on Windows
  2187. break;
  2188. }
  2189. arg ~= c;
  2190. }
  2191. args ~= arg;
  2192. }
  2193.  
  2194. // generate filename
  2195. string fn = "test_";
  2196. foreach (l; 0..uniform(1, 10))
  2197. {
  2198. dchar c;
  2199. while (true)
  2200. {
  2201. version (Windows)
  2202. c = uniform(0, 128); // as above
  2203. else
  2204. c = uniform!ubyte();
  2205.  
  2206. if (c == 0 || c == '/')
  2207. continue; // NUL and / are the only characters
  2208. // forbidden in POSIX filenames
  2209. version (Windows)
  2210. if (c < '\x20' || c == '<' || c == '>' || c == ':' ||
  2211. c == '"' || c == '\\' || c == '|' || c == '?' || c == '*')
  2212. continue; // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
  2213. break;
  2214. }
  2215.  
  2216. fn ~= c;
  2217. }
  2218.  
  2219. test(args, fn);
  2220. }
  2221. }
  2222.  
  2223.  
  2224. // =============================================================================
  2225. // Environment variable manipulation.
  2226. // =============================================================================
  2227.  
  2228.  
  2229. /**
  2230. Manipulates _environment variables using an associative-array-like
  2231. interface.
  2232.  
  2233. This class contains only static methods, and cannot be instantiated.
  2234. See below for examples of use.
  2235. */
  2236. abstract final class environment
  2237. {
  2238. static:
  2239. /**
  2240. Retrieves the value of the environment variable with the given $(D name).
  2241.  
  2242. If no such variable exists, this function throws an $(D Exception).
  2243. See also $(LREF get), which doesn't throw on failure.
  2244. ---
  2245. auto path = environment["PATH"];
  2246. ---
  2247. */
  2248. string opIndex(string name) @safe
  2249. {
  2250. string value;
  2251. enforce(getImpl(name, value), "Environment variable not found: "~name);
  2252. return value;
  2253. }
  2254.  
  2255. /**
  2256. Retrieves the value of the environment variable with the given $(D name),
  2257. or a default value if the variable doesn't exist.
  2258.  
  2259. Unlike $(LREF opIndex), this function never throws.
  2260. ---
  2261. auto sh = environment.get("SHELL", "/bin/sh");
  2262. ---
  2263. This function is also useful in checking for the existence of an
  2264. environment variable.
  2265. ---
  2266. auto myVar = environment.get("MYVAR");
  2267. if (myVar is null)
  2268. {
  2269. // Environment variable doesn't exist.
  2270. // Note that we have to use 'is' for the comparison, since
  2271. // myVar == null is also true if the variable exists but is
  2272. // empty.
  2273. }
  2274. ---
  2275. */
  2276. string get(string name, string defaultValue = null) @safe //TODO: nothrow
  2277. {
  2278. string value;
  2279. auto found = getImpl(name, value);
  2280. return found ? value : defaultValue;
  2281. }
  2282.  
  2283. /**
  2284. Assigns the given $(D value) to the environment variable with the given
  2285. $(D name).
  2286.  
  2287. If the variable does not exist, it will be created. If it already exists,
  2288. it will be overwritten.
  2289. ---
  2290. environment["foo"] = "bar";
  2291. ---
  2292. */
  2293. string opIndexAssign(string value, string name) @trusted
  2294. {
  2295. version (Posix)
  2296. {
  2297. if (core.sys.posix.stdlib.setenv(toStringz(name), toStringz(value), 1) != -1)
  2298. {
  2299. return value;
  2300. }
  2301. // The default errno error message is very uninformative
  2302. // in the most common case, so we handle it manually.
  2303. enforce(errno != EINVAL,
  2304. "Invalid environment variable name: '"~name~"'");
  2305. errnoEnforce(false,
  2306. "Failed to add environment variable");
  2307. assert(0);
  2308. }
  2309. else version (Windows)
  2310. {
  2311. enforce(
  2312. SetEnvironmentVariableW(toUTF16z(name), toUTF16z(value)),
  2313. sysErrorString(GetLastError())
  2314. );
  2315. return value;
  2316. }
  2317. else static assert(0);
  2318. }
  2319.  
  2320. /**
  2321. Removes the environment variable with the given $(D name).
  2322.  
  2323. If the variable isn't in the environment, this function returns
  2324. successfully without doing anything.
  2325. */
  2326. void remove(string name) @trusted // TODO: @safe nothrow
  2327. {
  2328. version (Windows) SetEnvironmentVariableW(toUTF16z(name), null);
  2329. else version (Posix) core.sys.posix.stdlib.unsetenv(toStringz(name));
  2330. else static assert(0);
  2331. }
  2332.  
  2333. /**
  2334. Copies all environment variables into an associative array.
  2335.  
  2336. Windows_specific:
  2337. While Windows environment variable names are case insensitive, D's
  2338. built-in associative arrays are not. This function will store all
  2339. variable names in uppercase (e.g. $(D PATH)).
  2340. */
  2341. string[string] toAA() @trusted
  2342. {
  2343. string[string] aa;
  2344. version (Posix)
  2345. {
  2346. for (int i=0; environ[i] != null; ++i)
  2347. {
  2348. immutable varDef = to!string(environ[i]);
  2349. immutable eq = std.string.indexOf(varDef, '=');
  2350. assert (eq >= 0);
  2351.  
  2352. immutable name = varDef[0 .. eq];
  2353. immutable value = varDef[eq+1 .. $];
  2354.  
  2355. // In POSIX, environment variables may be defined more
  2356. // than once. This is a security issue, which we avoid
  2357. // by checking whether the key already exists in the array.
  2358. // For more info:
  2359. // http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/environment-variables.html
  2360. if (name !in aa) aa[name] = value;
  2361. }
  2362. }
  2363. else version (Windows)
  2364. {
  2365. auto envBlock = GetEnvironmentStringsW();
  2366. enforce(envBlock, "Failed to retrieve environment variables.");
  2367. scope(exit) FreeEnvironmentStringsW(envBlock);
  2368.  
  2369. for (int i=0; envBlock[i] != '\0'; ++i)
  2370. {
  2371. auto start = i;
  2372. while (envBlock[i] != '=') ++i;
  2373. immutable name = toUTF8(toUpper(envBlock[start .. i]));
  2374.  
  2375. start = i+1;
  2376. while (envBlock[i] != '\0') ++i;
  2377. // Just like in POSIX systems, environment variables may be
  2378. // defined more than once in an environment block on Windows,
  2379. // and it is just as much of a security issue there. Moreso,
  2380. // in fact, due to the case insensensitivity of variable names,
  2381. // which is not handled correctly by all programs.
  2382. if (name !in aa) aa[name] = toUTF8(envBlock[start .. i]);
  2383. }
  2384. }
  2385. else static assert(0);
  2386. return aa;
  2387. }
  2388.  
  2389. private:
  2390. // Returns the length of an environment variable (in number of
  2391. // wchars, including the null terminator), or 0 if it doesn't exist.
  2392. version (Windows)
  2393. int varLength(LPCWSTR namez) @trusted nothrow
  2394. {
  2395. return GetEnvironmentVariableW(namez, null, 0);
  2396. }
  2397.  
  2398. // Retrieves the environment variable, returns false on failure.
  2399. bool getImpl(string name, out string value) @trusted //TODO: nothrow
  2400. {
  2401. version (Windows)
  2402. {
  2403. const namez = toUTF16z(name);
  2404. immutable len = varLength(namez);
  2405. if (len == 0) return false;
  2406. if (len == 1)
  2407. {
  2408. value = "";
  2409. return true;
  2410. }
  2411.  
  2412. auto buf = new WCHAR[len];
  2413. GetEnvironmentVariableW(namez, buf.ptr, to!DWORD(buf.length));
  2414. value = toUTF8(buf[0 .. $-1]);
  2415. return true;
  2416. }
  2417. else version (Posix)
  2418. {
  2419. const vz = core.sys.posix.stdlib.getenv(toStringz(name));
  2420. if (vz == null) return false;
  2421. auto v = vz[0 .. strlen(vz)];
  2422.  
  2423. // Cache the last call's result.
  2424. static string lastResult;
  2425. if (v != lastResult) lastResult = v.idup;
  2426. value = lastResult;
  2427. return true;
  2428. }
  2429. else static assert(0);
  2430. }
  2431. }
  2432.  
  2433. unittest
  2434. {
  2435. // New variable
  2436. environment["std_process"] = "foo";
  2437. assert (environment["std_process"] == "foo");
  2438.  
  2439. // Set variable again
  2440. environment["std_process"] = "bar";
  2441. assert (environment["std_process"] == "bar");
  2442.  
  2443. // Remove variable
  2444. environment.remove("std_process");
  2445.  
  2446. // Remove again, should succeed
  2447. environment.remove("std_process");
  2448.  
  2449. // Throw on not found.
  2450. assertThrown(environment["std_process"]);
  2451.  
  2452. // get() without default value
  2453. assert (environment.get("std_process") == null);
  2454.  
  2455. // get() with default value
  2456. assert (environment.get("std_process", "baz") == "baz");
  2457.  
  2458. // Convert to associative array
  2459. auto aa = environment.toAA();
  2460. assert (aa.length > 0);
  2461. foreach (n, v; aa)
  2462. {
  2463. // Wine has some bugs related to environment variables:
  2464. // - Wine allows the existence of an env. variable with the name
  2465. // "\0", but GetEnvironmentVariable refuses to retrieve it.
  2466. // - If an env. variable has zero length, i.e. is "\0",
  2467. // GetEnvironmentVariable should return 1. Instead it returns
  2468. // 0, indicating the variable doesn't exist.
  2469. version (Windows) if (n.length == 0 || v.length == 0) continue;
  2470.  
  2471. assert (v == environment[n]);
  2472. }
  2473. }