PageRenderTime 62ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/std/process.d

http://github.com/jcd/phobos
D | 3445 lines | 2043 code | 295 blank | 1107 comment | 385 complexity | c9ae7d4412c9f60edf2d104d11570b7e MD5 | raw file
  1. // Written in the D programming language.
  2. /**
  3. Functions for starting and interacting with other processes, and for
  4. working with the current _process' execution environment.
  5. Process_handling:
  6. $(UL $(LI
  7. $(LREF spawnProcess) spawns a new _process, optionally assigning it an
  8. arbitrary set of standard input, output, and error streams.
  9. The function returns immediately, leaving the child _process to execute
  10. in parallel with its parent. All other functions in this module that
  11. spawn processes are built around $(D spawnProcess).)
  12. $(LI
  13. $(LREF wait) makes the parent _process wait for a child _process to
  14. terminate. In general one should always do this, to avoid
  15. child processes becoming "zombies" when the parent _process exits.
  16. Scope guards are perfect for this – see the $(LREF spawnProcess)
  17. documentation for examples. $(LREF tryWait) is similar to $(D wait),
  18. but does not block if the _process has not yet terminated.)
  19. $(LI
  20. $(LREF pipeProcess) also spawns a child _process which runs
  21. in parallel with its parent. However, instead of taking
  22. arbitrary streams, it automatically creates a set of
  23. pipes that allow the parent to communicate with the child
  24. through the child's standard input, output, and/or error streams.
  25. This function corresponds roughly to C's $(D popen) function.)
  26. $(LI
  27. $(LREF execute) starts a new _process and waits for it
  28. to complete before returning. Additionally, it captures
  29. the _process' standard output and error streams and returns
  30. the output of these as a string.)
  31. $(LI
  32. $(LREF spawnShell), $(LREF pipeShell) and $(LREF executeShell) work like
  33. $(D spawnProcess), $(D pipeProcess) and $(D execute), respectively,
  34. except that they take a single command string and run it through
  35. the current user's default command interpreter.
  36. $(D executeShell) corresponds roughly to C's $(D system) function.)
  37. $(LI
  38. $(LREF kill) attempts to terminate a running _process.)
  39. )
  40. The following table compactly summarises the different _process creation
  41. functions and how they relate to each other:
  42. $(BOOKTABLE,
  43. $(TR $(TH )
  44. $(TH Runs program directly)
  45. $(TH Runs shell command))
  46. $(TR $(TD Low-level _process creation)
  47. $(TD $(LREF spawnProcess))
  48. $(TD $(LREF spawnShell)))
  49. $(TR $(TD Automatic input/output redirection using pipes)
  50. $(TD $(LREF pipeProcess))
  51. $(TD $(LREF pipeShell)))
  52. $(TR $(TD Execute and wait for completion, collect output)
  53. $(TD $(LREF execute))
  54. $(TD $(LREF executeShell)))
  55. )
  56. Other_functionality:
  57. $(UL
  58. $(LI
  59. $(LREF pipe) is used to create unidirectional pipes.)
  60. $(LI
  61. $(LREF environment) is an interface through which the current _process'
  62. environment variables can be read and manipulated.)
  63. $(LI
  64. $(LREF escapeShellCommand) and $(LREF escapeShellFileName) are useful
  65. for constructing shell command lines in a portable way.)
  66. )
  67. Authors:
  68. $(LINK2 https://github.com/kyllingstad, Lars Tandle Kyllingstad),
  69. $(LINK2 https://github.com/schveiguy, Steven Schveighoffer),
  70. $(WEB thecybershadow.net, Vladimir Panteleev)
  71. Copyright:
  72. Copyright (c) 2013, the authors. All rights reserved.
  73. Source:
  74. $(PHOBOSSRC std/_process.d)
  75. Macros:
  76. WIKI=Phobos/StdProcess
  77. OBJECTREF=$(D $(LINK2 object.html#$0,$0))
  78. LREF=$(D $(LINK2 #.$0,$0))
  79. */
  80. module std.process;
  81. version (Posix)
  82. {
  83. import core.stdc.errno;
  84. import core.stdc.string;
  85. import core.sys.posix.stdio;
  86. import core.sys.posix.unistd;
  87. import core.sys.posix.sys.wait;
  88. }
  89. version (Windows)
  90. {
  91. import core.stdc.stdio;
  92. import core.sys.windows.windows;
  93. import std.utf;
  94. import std.windows.syserror;
  95. }
  96. import std.algorithm;
  97. import std.array;
  98. import std.conv;
  99. import std.exception;
  100. import std.path;
  101. import std.stdio;
  102. import std.string;
  103. import std.internal.processinit;
  104. // When the DMC runtime is used, we have to use some custom functions
  105. // to convert between Windows file handles and FILE*s.
  106. version (Win32) version (DigitalMars) version = DMC_RUNTIME;
  107. // Some of the following should be moved to druntime.
  108. private
  109. {
  110. // Microsoft Visual C Runtime (MSVCRT) declarations.
  111. version (Windows)
  112. {
  113. version (DMC_RUNTIME) { } else
  114. {
  115. import core.stdc.stdint;
  116. extern(C)
  117. {
  118. int _fileno(FILE* stream);
  119. HANDLE _get_osfhandle(int fd);
  120. int _open_osfhandle(HANDLE osfhandle, int flags);
  121. FILE* _fdopen(int fd, const (char)* mode);
  122. int _close(int fd);
  123. }
  124. enum
  125. {
  126. STDIN_FILENO = 0,
  127. STDOUT_FILENO = 1,
  128. STDERR_FILENO = 2,
  129. }
  130. enum
  131. {
  132. _O_RDONLY = 0x0000,
  133. _O_APPEND = 0x0004,
  134. _O_TEXT = 0x4000,
  135. }
  136. }
  137. }
  138. // POSIX API declarations.
  139. version (Posix)
  140. {
  141. version (OSX)
  142. {
  143. extern(C) char*** _NSGetEnviron() nothrow;
  144. private __gshared const(char**)* environPtr;
  145. extern(C) void std_process_shared_static_this() { environPtr = _NSGetEnviron(); }
  146. const(char**) environ() @property @trusted nothrow { return *environPtr; }
  147. }
  148. else
  149. {
  150. // Made available by the C runtime:
  151. extern(C) extern __gshared const char** environ;
  152. }
  153. unittest
  154. {
  155. new Thread({assert(environ !is null);}).start();
  156. }
  157. }
  158. } // private
  159. // =============================================================================
  160. // Functions and classes for process management.
  161. // =============================================================================
  162. /**
  163. Spawns a new _process, optionally assigning it an arbitrary set of standard
  164. input, output, and error streams.
  165. The function returns immediately, leaving the child _process to execute
  166. in parallel with its parent. It is recommended to always call $(LREF wait)
  167. on the returned $(LREF Pid), as detailed in the documentation for $(D wait).
  168. Command_line:
  169. There are four overloads of this function. The first two take an array
  170. of strings, $(D args), which should contain the program name as the
  171. zeroth element and any command-line arguments in subsequent elements.
  172. The third and fourth versions are included for convenience, and may be
  173. used when there are no command-line arguments. They take a single string,
  174. $(D program), which specifies the program name.
  175. Unless a directory is specified in $(D args[0]) or $(D program),
  176. $(D spawnProcess) will search for the program in a platform-dependent
  177. manner. On POSIX systems, it will look for the executable in the
  178. directories listed in the PATH environment variable, in the order
  179. they are listed. On Windows, it will search for the executable in
  180. the following sequence:
  181. $(OL
  182. $(LI The directory from which the application loaded.)
  183. $(LI The current directory for the parent process.)
  184. $(LI The 32-bit Windows system directory.)
  185. $(LI The 16-bit Windows system directory.)
  186. $(LI The Windows directory.)
  187. $(LI The directories listed in the PATH environment variable.)
  188. )
  189. ---
  190. // Run an executable called "prog" located in the current working
  191. // directory:
  192. auto pid = spawnProcess("./prog");
  193. scope(exit) wait(pid);
  194. // We can do something else while the program runs. The scope guard
  195. // ensures that the process is waited for at the end of the scope.
  196. ...
  197. // Run DMD on the file "myprog.d", specifying a few compiler switches:
  198. auto dmdPid = spawnProcess(["dmd", "-O", "-release", "-inline", "myprog.d" ]);
  199. if (wait(dmdPid) != 0)
  200. writeln("Compilation failed!");
  201. ---
  202. Environment_variables:
  203. By default, the child process inherits the environment of the parent
  204. process, along with any additional variables specified in the $(D env)
  205. parameter. If the same variable exists in both the parent's environment
  206. and in $(D env), the latter takes precedence.
  207. If the $(LREF Config.newEnv) flag is set in $(D config), the child
  208. process will $(I not) inherit the parent's environment. Its entire
  209. environment will then be determined by $(D env).
  210. ---
  211. wait(spawnProcess("myapp", ["foo" : "bar"], Config.newEnv));
  212. ---
  213. Standard_streams:
  214. The optional arguments $(D stdin), $(D stdout) and $(D stderr) may
  215. be used to assign arbitrary $(XREF stdio,File) objects as the standard
  216. input, output and error streams, respectively, of the child process. The
  217. former must be opened for reading, while the latter two must be opened for
  218. writing. The default is for the child process to inherit the standard
  219. streams of its parent.
  220. ---
  221. // Run DMD on the file myprog.d, logging any error messages to a
  222. // file named errors.log.
  223. auto logFile = File("errors.log", "w");
  224. auto pid = spawnProcess(["dmd", "myprog.d"],
  225. std.stdio.stdin,
  226. std.stdio.stdout,
  227. logFile);
  228. if (wait(pid) != 0)
  229. writeln("Compilation failed. See errors.log for details.");
  230. ---
  231. Note that if you pass a $(D File) object that is $(I not)
  232. one of the standard input/output/error streams of the parent process,
  233. that stream will by default be $(I closed) in the parent process when
  234. this function returns. See the $(LREF Config) documentation below for
  235. information about how to disable this behaviour.
  236. Beware of buffering issues when passing $(D File) objects to
  237. $(D spawnProcess). The child process will inherit the low-level raw
  238. read/write offset associated with the underlying file descriptor, but
  239. it will not be aware of any buffered data. In cases where this matters
  240. (e.g. when a file should be aligned before being passed on to the
  241. child process), it may be a good idea to use unbuffered streams, or at
  242. least ensure all relevant buffers are flushed.
  243. Params:
  244. args = An array which contains the program name as the zeroth element
  245. and any command-line arguments in the following elements.
  246. stdin = The standard input stream of the child process.
  247. This can be any $(XREF stdio,File) that is opened for reading.
  248. By default the child process inherits the parent's input
  249. stream.
  250. stdout = The standard output stream of the child process.
  251. This can be any $(XREF stdio,File) that is opened for writing.
  252. By default the child process inherits the parent's output stream.
  253. stderr = The standard error stream of the child process.
  254. This can be any $(XREF stdio,File) that is opened for writing.
  255. By default the child process inherits the parent's error stream.
  256. env = Additional environment variables for the child process.
  257. config = Flags that control process creation. See $(LREF Config)
  258. for an overview of available flags.
  259. Returns:
  260. A $(LREF Pid) object that corresponds to the spawned process.
  261. Throws:
  262. $(LREF ProcessException) on failure to start the process.$(BR)
  263. $(XREF stdio,StdioException) on failure to pass one of the streams
  264. to the child process (Windows only).$(BR)
  265. $(CXREF exception,RangeError) if $(D args) is empty.
  266. */
  267. Pid spawnProcess(in char[][] args,
  268. File stdin = std.stdio.stdin,
  269. File stdout = std.stdio.stdout,
  270. File stderr = std.stdio.stderr,
  271. const string[string] env = null,
  272. Config config = Config.none)
  273. @trusted // TODO: Should be @safe
  274. {
  275. version (Windows) auto args2 = escapeShellArguments(args);
  276. else version (Posix) alias args2 = args;
  277. return spawnProcessImpl(args2, stdin, stdout, stderr, env, config);
  278. }
  279. /// ditto
  280. Pid spawnProcess(in char[][] args,
  281. const string[string] env,
  282. Config config = Config.none)
  283. @trusted // TODO: Should be @safe
  284. {
  285. return spawnProcess(args,
  286. std.stdio.stdin,
  287. std.stdio.stdout,
  288. std.stdio.stderr,
  289. env,
  290. config);
  291. }
  292. /// ditto
  293. Pid spawnProcess(in char[] program,
  294. File stdin = std.stdio.stdin,
  295. File stdout = std.stdio.stdout,
  296. File stderr = std.stdio.stderr,
  297. const string[string] env = null,
  298. Config config = Config.none)
  299. @trusted
  300. {
  301. return spawnProcess((&program)[0 .. 1],
  302. stdin, stdout, stderr, env, config);
  303. }
  304. /// ditto
  305. Pid spawnProcess(in char[] program,
  306. const string[string] env,
  307. Config config = Config.none)
  308. @trusted
  309. {
  310. return spawnProcess((&program)[0 .. 1], env, config);
  311. }
  312. /*
  313. Implementation of spawnProcess() for POSIX.
  314. envz should be a zero-terminated array of zero-terminated strings
  315. on the form "var=value".
  316. */
  317. version (Posix)
  318. private Pid spawnProcessImpl(in char[][] args,
  319. File stdin,
  320. File stdout,
  321. File stderr,
  322. const string[string] env,
  323. Config config)
  324. @trusted // TODO: Should be @safe
  325. {
  326. import core.exception: RangeError;
  327. if (args.empty) throw new RangeError();
  328. const(char)[] name = args[0];
  329. if (any!isDirSeparator(name))
  330. {
  331. if (!isExecutable(name))
  332. throw new ProcessException(text("Not an executable file: ", name));
  333. }
  334. else
  335. {
  336. name = searchPathFor(name);
  337. if (name is null)
  338. throw new ProcessException(text("Executable file not found: ", name));
  339. }
  340. // Convert program name and arguments to C-style strings.
  341. auto argz = new const(char)*[args.length+1];
  342. argz[0] = toStringz(name);
  343. foreach (i; 1 .. args.length) argz[i] = toStringz(args[i]);
  344. argz[$-1] = null;
  345. // Prepare environment.
  346. auto envz = createEnv(env, !(config & Config.newEnv));
  347. // Get the file descriptors of the streams.
  348. // These could potentially be invalid, but that is OK. If so, later calls
  349. // to dup2() and close() will just silently fail without causing any harm.
  350. auto stdinFD = core.stdc.stdio.fileno(stdin.getFP());
  351. auto stdoutFD = core.stdc.stdio.fileno(stdout.getFP());
  352. auto stderrFD = core.stdc.stdio.fileno(stderr.getFP());
  353. auto id = fork();
  354. if (id < 0)
  355. throw ProcessException.newFromErrno("Failed to spawn new process");
  356. if (id == 0)
  357. {
  358. // Child process
  359. // Redirect streams and close the old file descriptors.
  360. // In the case that stderr is redirected to stdout, we need
  361. // to backup the file descriptor since stdout may be redirected
  362. // as well.
  363. if (stderrFD == STDOUT_FILENO) stderrFD = dup(stderrFD);
  364. dup2(stdinFD, STDIN_FILENO);
  365. dup2(stdoutFD, STDOUT_FILENO);
  366. dup2(stderrFD, STDERR_FILENO);
  367. // Ensure that the standard streams aren't closed on execute, and
  368. // optionally close all other file descriptors.
  369. setCLOEXEC(STDIN_FILENO, false);
  370. setCLOEXEC(STDOUT_FILENO, false);
  371. setCLOEXEC(STDERR_FILENO, false);
  372. if (!(config & Config.inheritFDs))
  373. {
  374. import core.sys.posix.sys.resource;
  375. rlimit r;
  376. getrlimit(RLIMIT_NOFILE, &r);
  377. foreach (i; 3 .. cast(int) r.rlim_cur) close(i);
  378. }
  379. // Close the old file descriptors, unless they are
  380. // either of the standard streams.
  381. if (stdinFD > STDERR_FILENO) close(stdinFD);
  382. if (stdoutFD > STDERR_FILENO) close(stdoutFD);
  383. if (stderrFD > STDERR_FILENO) close(stderrFD);
  384. // Execute program.
  385. core.sys.posix.unistd.execve(argz[0], argz.ptr, envz);
  386. // If execution fails, exit as quickly as possible.
  387. core.sys.posix.stdio.perror("spawnProcess(): Failed to execute program");
  388. core.sys.posix.unistd._exit(1);
  389. assert (0);
  390. }
  391. else
  392. {
  393. // Parent process: Close streams and return.
  394. if (stdinFD > STDERR_FILENO && !(config & Config.retainStdin))
  395. stdin.close();
  396. if (stdoutFD > STDERR_FILENO && !(config & Config.retainStdout))
  397. stdout.close();
  398. if (stderrFD > STDERR_FILENO && !(config & Config.retainStderr))
  399. stderr.close();
  400. return new Pid(id);
  401. }
  402. }
  403. /*
  404. Implementation of spawnProcess() for Windows.
  405. commandLine must contain the entire command line, properly
  406. quoted/escaped as required by CreateProcessW().
  407. envz must be a pointer to a block of UTF-16 characters on the form
  408. "var1=value1\0var2=value2\0...varN=valueN\0\0".
  409. */
  410. version (Windows)
  411. private Pid spawnProcessImpl(in char[] commandLine,
  412. File stdin,
  413. File stdout,
  414. File stderr,
  415. const string[string] env,
  416. Config config)
  417. @trusted
  418. {
  419. import core.exception: RangeError;
  420. if (commandLine.empty) throw new RangeError("Command line is empty");
  421. auto commandz = toUTFz!(wchar*)(commandLine);
  422. // Prepare environment.
  423. auto envz = createEnv(env, !(config & Config.newEnv));
  424. // Startup info for CreateProcessW().
  425. STARTUPINFO_W startinfo;
  426. startinfo.cb = startinfo.sizeof;
  427. startinfo.dwFlags = STARTF_USESTDHANDLES;
  428. // Extract file descriptors and HANDLEs from the streams and make the
  429. // handles inheritable.
  430. static void prepareStream(ref File file, DWORD stdHandle, string which,
  431. out int fileDescriptor, out HANDLE handle)
  432. {
  433. fileDescriptor = _fileno(file.getFP());
  434. if (fileDescriptor < 0) handle = GetStdHandle(stdHandle);
  435. else
  436. {
  437. version (DMC_RUNTIME) handle = _fdToHandle(fileDescriptor);
  438. else /* MSVCRT */ handle = _get_osfhandle(fileDescriptor);
  439. }
  440. DWORD dwFlags;
  441. if (GetHandleInformation(handle, &dwFlags))
  442. {
  443. if (!(dwFlags & HANDLE_FLAG_INHERIT))
  444. {
  445. if (!SetHandleInformation(handle,
  446. HANDLE_FLAG_INHERIT,
  447. HANDLE_FLAG_INHERIT))
  448. {
  449. throw new StdioException(
  450. "Failed to make "~which~" stream inheritable by child process ("
  451. ~sysErrorString(GetLastError()) ~ ')',
  452. 0);
  453. }
  454. }
  455. }
  456. }
  457. int stdinFD = -1, stdoutFD = -1, stderrFD = -1;
  458. prepareStream(stdin, STD_INPUT_HANDLE, "stdin" , stdinFD, startinfo.hStdInput );
  459. prepareStream(stdout, STD_OUTPUT_HANDLE, "stdout", stdoutFD, startinfo.hStdOutput);
  460. prepareStream(stderr, STD_ERROR_HANDLE, "stderr", stderrFD, startinfo.hStdError );
  461. // Create process.
  462. PROCESS_INFORMATION pi;
  463. DWORD dwCreationFlags =
  464. CREATE_UNICODE_ENVIRONMENT |
  465. ((config & Config.suppressConsole) ? CREATE_NO_WINDOW : 0);
  466. if (!CreateProcessW(null, commandz, null, null, true, dwCreationFlags,
  467. envz, null, &startinfo, &pi))
  468. throw ProcessException.newFromLastError("Failed to spawn new process");
  469. // figure out if we should close any of the streams
  470. if (stdinFD > STDERR_FILENO && !(config & Config.retainStdin))
  471. stdin.close();
  472. if (stdoutFD > STDERR_FILENO && !(config & Config.retainStdout))
  473. stdout.close();
  474. if (stderrFD > STDERR_FILENO && !(config & Config.retainStderr))
  475. stderr.close();
  476. // close the thread handle in the process info structure
  477. CloseHandle(pi.hThread);
  478. return new Pid(pi.dwProcessId, pi.hProcess);
  479. }
  480. // Converts childEnv to a zero-terminated array of zero-terminated strings
  481. // on the form "name=value", optionally adding those of the current process'
  482. // environment strings that are not present in childEnv. If the parent's
  483. // environment should be inherited without modification, this function
  484. // returns environ directly.
  485. version (Posix)
  486. private const(char*)* createEnv(const string[string] childEnv,
  487. bool mergeWithParentEnv)
  488. {
  489. // Determine the number of strings in the parent's environment.
  490. int parentEnvLength = 0;
  491. if (mergeWithParentEnv)
  492. {
  493. if (childEnv.length == 0) return environ;
  494. while (environ[parentEnvLength] != null) ++parentEnvLength;
  495. }
  496. // Convert the "new" variables to C-style strings.
  497. auto envz = new const(char)*[parentEnvLength + childEnv.length + 1];
  498. int pos = 0;
  499. foreach (var, val; childEnv)
  500. envz[pos++] = (var~'='~val~'\0').ptr;
  501. // Add the parent's environment.
  502. foreach (environStr; environ[0 .. parentEnvLength])
  503. {
  504. int eqPos = 0;
  505. while (environStr[eqPos] != '=' && environStr[eqPos] != '\0') ++eqPos;
  506. if (environStr[eqPos] != '=') continue;
  507. auto var = environStr[0 .. eqPos];
  508. if (var in childEnv) continue;
  509. envz[pos++] = environStr;
  510. }
  511. envz[pos] = null;
  512. return envz.ptr;
  513. }
  514. version (Posix) unittest
  515. {
  516. auto e1 = createEnv(null, false);
  517. assert (e1 != null && *e1 == null);
  518. auto e2 = createEnv(null, true);
  519. assert (e2 != null);
  520. int i = 0;
  521. for (; environ[i] != null; ++i)
  522. {
  523. assert (e2[i] != null);
  524. import core.stdc.string;
  525. assert (strcmp(e2[i], environ[i]) == 0);
  526. }
  527. assert (e2[i] == null);
  528. auto e3 = createEnv(["foo" : "bar", "hello" : "world"], false);
  529. assert (e3 != null && e3[0] != null && e3[1] != null && e3[2] == null);
  530. assert ((e3[0][0 .. 8] == "foo=bar\0" && e3[1][0 .. 12] == "hello=world\0")
  531. || (e3[0][0 .. 12] == "hello=world\0" && e3[1][0 .. 8] == "foo=bar\0"));
  532. }
  533. // Converts childEnv to a Windows environment block, which is on the form
  534. // "name1=value1\0name2=value2\0...nameN=valueN\0\0", optionally adding
  535. // those of the current process' environment strings that are not present
  536. // in childEnv. Returns null if the parent's environment should be
  537. // inherited without modification, as this is what is expected by
  538. // CreateProcess().
  539. version (Windows)
  540. private LPVOID createEnv(const string[string] childEnv,
  541. bool mergeWithParentEnv)
  542. {
  543. if (mergeWithParentEnv && childEnv.length == 0) return null;
  544. auto envz = appender!(wchar[])();
  545. void put(string var, string val)
  546. {
  547. envz.put(var);
  548. envz.put('=');
  549. envz.put(val);
  550. envz.put(cast(wchar) '\0');
  551. }
  552. // Add the variables in childEnv, removing them from parentEnv
  553. // if they exist there too.
  554. auto parentEnv = mergeWithParentEnv ? environment.toAA() : null;
  555. foreach (k, v; childEnv)
  556. {
  557. auto uk = toUpper(k);
  558. put(uk, v);
  559. if (uk in parentEnv) parentEnv.remove(uk);
  560. }
  561. // Add remaining parent environment variables.
  562. foreach (k, v; parentEnv) put(k, v);
  563. // Two final zeros are needed in case there aren't any environment vars,
  564. // and the last one does no harm when there are.
  565. envz.put("\0\0"w);
  566. return envz.data.ptr;
  567. }
  568. version (Windows) unittest
  569. {
  570. assert (createEnv(null, true) == null);
  571. assert ((cast(wchar*) createEnv(null, false))[0 .. 2] == "\0\0"w);
  572. auto e1 = (cast(wchar*) createEnv(["foo":"bar", "ab":"c"], false))[0 .. 14];
  573. assert (e1 == "FOO=bar\0AB=c\0\0"w || e1 == "AB=c\0FOO=bar\0\0"w);
  574. }
  575. // Searches the PATH variable for the given executable file,
  576. // (checking that it is in fact executable).
  577. version (Posix)
  578. private string searchPathFor(in char[] executable)
  579. @trusted //TODO: @safe nothrow
  580. {
  581. auto pathz = core.stdc.stdlib.getenv("PATH");
  582. if (pathz == null) return null;
  583. foreach (dir; splitter(to!string(pathz), ':'))
  584. {
  585. auto execPath = buildPath(dir, executable);
  586. if (isExecutable(execPath)) return execPath;
  587. }
  588. return null;
  589. }
  590. // Checks whether the file exists and can be executed by the
  591. // current user.
  592. version (Posix)
  593. private bool isExecutable(in char[] path) @trusted //TODO: @safe nothrow
  594. {
  595. return (access(toStringz(path), X_OK) == 0);
  596. }
  597. version (Posix) unittest
  598. {
  599. auto unamePath = searchPathFor("uname");
  600. assert (!unamePath.empty);
  601. assert (unamePath[0] == '/');
  602. assert (unamePath.endsWith("uname"));
  603. auto unlikely = searchPathFor("lkmqwpoialhggyaofijadsohufoiqezm");
  604. assert (unlikely is null, "Are you kidding me?");
  605. }
  606. // Sets or unsets the FD_CLOEXEC flag on the given file descriptor.
  607. version (Posix)
  608. private void setCLOEXEC(int fd, bool on)
  609. {
  610. import core.sys.posix.fcntl;
  611. auto flags = fcntl(fd, F_GETFD);
  612. if (flags >= 0)
  613. {
  614. if (on) flags |= FD_CLOEXEC;
  615. else flags &= ~(cast(typeof(flags)) FD_CLOEXEC);
  616. flags = fcntl(fd, F_SETFD, flags);
  617. }
  618. if (flags == -1)
  619. {
  620. throw new StdioException("Failed to "~(on ? "" : "un")
  621. ~"set close-on-exec flag on file descriptor");
  622. }
  623. }
  624. unittest // Command line arguments in spawnProcess().
  625. {
  626. version (Windows) TestScript prog =
  627. "if not [%~1]==[foo] ( exit 1 )
  628. if not [%~2]==[bar] ( exit 2 )
  629. exit 0";
  630. else version (Posix) TestScript prog =
  631. `if test "$1" != "foo"; then exit 1; fi
  632. if test "$2" != "bar"; then exit 2; fi
  633. exit 0`;
  634. assert (wait(spawnProcess(prog.path)) == 1);
  635. assert (wait(spawnProcess([prog.path])) == 1);
  636. assert (wait(spawnProcess([prog.path, "foo"])) == 2);
  637. assert (wait(spawnProcess([prog.path, "foo", "baz"])) == 2);
  638. assert (wait(spawnProcess([prog.path, "foo", "bar"])) == 0);
  639. }
  640. unittest // Environment variables in spawnProcess().
  641. {
  642. // We really should use set /a on Windows, but Wine doesn't support it.
  643. version (Windows) TestScript envProg =
  644. `if [%STD_PROCESS_UNITTEST1%] == [1] (
  645. if [%STD_PROCESS_UNITTEST2%] == [2] (exit 3)
  646. exit 1
  647. )
  648. if [%STD_PROCESS_UNITTEST1%] == [4] (
  649. if [%STD_PROCESS_UNITTEST2%] == [2] (exit 6)
  650. exit 4
  651. )
  652. if [%STD_PROCESS_UNITTEST2%] == [2] (exit 2)
  653. exit 0`;
  654. version (Posix) TestScript envProg =
  655. `if test "$std_process_unittest1" = ""; then
  656. std_process_unittest1=0
  657. fi
  658. if test "$std_process_unittest2" = ""; then
  659. std_process_unittest2=0
  660. fi
  661. exit $(($std_process_unittest1+$std_process_unittest2))`;
  662. environment.remove("std_process_unittest1"); // Just in case.
  663. environment.remove("std_process_unittest2");
  664. assert (wait(spawnProcess(envProg.path)) == 0);
  665. assert (wait(spawnProcess(envProg.path, null, Config.newEnv)) == 0);
  666. environment["std_process_unittest1"] = "1";
  667. assert (wait(spawnProcess(envProg.path)) == 1);
  668. assert (wait(spawnProcess(envProg.path, null, Config.newEnv)) == 0);
  669. auto env = ["std_process_unittest2" : "2"];
  670. assert (wait(spawnProcess(envProg.path, env)) == 3);
  671. assert (wait(spawnProcess(envProg.path, env, Config.newEnv)) == 2);
  672. env["std_process_unittest1"] = "4";
  673. assert (wait(spawnProcess(envProg.path, env)) == 6);
  674. assert (wait(spawnProcess(envProg.path, env, Config.newEnv)) == 6);
  675. environment.remove("std_process_unittest1");
  676. assert (wait(spawnProcess(envProg.path, env)) == 6);
  677. assert (wait(spawnProcess(envProg.path, env, Config.newEnv)) == 6);
  678. }
  679. unittest // Stream redirection in spawnProcess().
  680. {
  681. version (Windows) TestScript prog =
  682. "set /p INPUT=
  683. echo %INPUT% output %~1
  684. echo %INPUT% error %~2 1>&2";
  685. else version (Posix) TestScript prog =
  686. "read INPUT
  687. echo $INPUT output $1
  688. echo $INPUT error $2 >&2";
  689. // Pipes
  690. auto pipei = pipe();
  691. auto pipeo = pipe();
  692. auto pipee = pipe();
  693. auto pid = spawnProcess([prog.path, "foo", "bar"],
  694. pipei.readEnd, pipeo.writeEnd, pipee.writeEnd);
  695. pipei.writeEnd.writeln("input");
  696. pipei.writeEnd.flush();
  697. assert (pipeo.readEnd.readln().chomp() == "input output foo");
  698. assert (pipee.readEnd.readln().chomp().stripRight() == "input error bar");
  699. wait(pid);
  700. // Files
  701. import std.ascii, std.file, std.uuid;
  702. auto pathi = buildPath(tempDir(), randomUUID().toString());
  703. auto patho = buildPath(tempDir(), randomUUID().toString());
  704. auto pathe = buildPath(tempDir(), randomUUID().toString());
  705. std.file.write(pathi, "INPUT"~std.ascii.newline);
  706. auto filei = File(pathi, "r");
  707. auto fileo = File(patho, "w");
  708. auto filee = File(pathe, "w");
  709. pid = spawnProcess([prog.path, "bar", "baz" ], filei, fileo, filee);
  710. wait(pid);
  711. assert (readText(patho).chomp() == "INPUT output bar");
  712. assert (readText(pathe).chomp().stripRight() == "INPUT error baz");
  713. remove(pathi);
  714. remove(patho);
  715. remove(pathe);
  716. }
  717. unittest // Error handling in spawnProcess()
  718. {
  719. assertThrown!ProcessException(spawnProcess("ewrgiuhrifuheiohnmnvqweoijwf"));
  720. assertThrown!ProcessException(spawnProcess("./rgiuhrifuheiohnmnvqweoijwf"));
  721. }
  722. /**
  723. A variation on $(LREF spawnProcess) that runs the given _command through
  724. the current user's preferred _command interpreter (aka. shell).
  725. The string $(D command) is passed verbatim to the shell, and is therefore
  726. subject to its rules about _command structure, argument/filename quoting
  727. and escaping of special characters.
  728. The path to the shell executable is determined by the $(LREF userShell)
  729. function.
  730. In all other respects this function works just like $(D spawnProcess).
  731. Please refer to the $(LREF spawnProcess) documentation for descriptions
  732. of the other function parameters, the return value and any exceptions
  733. that may be thrown.
  734. ---
  735. // Run the command/program "foo" on the file named "my file.txt", and
  736. // redirect its output into foo.log.
  737. auto pid = spawnShell(`foo "my file.txt" > foo.log`);
  738. wait(pid);
  739. ---
  740. See_also:
  741. $(LREF escapeShellCommand), which may be helpful in constructing a
  742. properly quoted and escaped shell _command line for the current platform.
  743. */
  744. Pid spawnShell(in char[] command,
  745. File stdin = std.stdio.stdin,
  746. File stdout = std.stdio.stdout,
  747. File stderr = std.stdio.stderr,
  748. const string[string] env = null,
  749. Config config = Config.none)
  750. @trusted // TODO: Should be @safe
  751. {
  752. version (Windows)
  753. {
  754. auto args = escapeShellArguments(userShell, shellSwitch)
  755. ~ " " ~ command;
  756. }
  757. else version (Posix)
  758. {
  759. const(char)[][3] args;
  760. args[0] = userShell;
  761. args[1] = shellSwitch;
  762. args[2] = command;
  763. }
  764. return spawnProcessImpl(args, stdin, stdout, stderr, env, config);
  765. }
  766. /// ditto
  767. Pid spawnShell(in char[] command,
  768. const string[string] env,
  769. Config config = Config.none)
  770. @trusted // TODO: Should be @safe
  771. {
  772. return spawnShell(command,
  773. std.stdio.stdin,
  774. std.stdio.stdout,
  775. std.stdio.stderr,
  776. env,
  777. config);
  778. }
  779. unittest
  780. {
  781. version (Windows)
  782. auto cmd = "echo %FOO%";
  783. else version (Posix)
  784. auto cmd = "echo $foo";
  785. import std.file;
  786. auto tmpFile = uniqueTempPath();
  787. scope(exit) if (exists(tmpFile)) remove(tmpFile);
  788. auto redir = "> \""~tmpFile~'"';
  789. auto env = ["foo" : "bar"];
  790. assert (wait(spawnShell(cmd~redir, env)) == 0);
  791. auto f = File(tmpFile, "a");
  792. assert (wait(spawnShell(cmd, std.stdio.stdin, f, std.stdio.stderr, env)) == 0);
  793. f.close();
  794. auto output = std.file.readText(tmpFile);
  795. assert (output == "bar\nbar\n" || output == "bar\r\nbar\r\n");
  796. }
  797. /**
  798. Flags that control the behaviour of $(LREF spawnProcess) and
  799. $(LREF spawnShell).
  800. Use bitwise OR to combine flags.
  801. Example:
  802. ---
  803. auto logFile = File("myapp_error.log", "w");
  804. // Start program, suppressing the console window (Windows only),
  805. // redirect its error stream to logFile, and leave logFile open
  806. // in the parent process as well.
  807. auto pid = spawnProcess("myapp", stdin, stdout, logFile,
  808. Config.retainStderr | Config.suppressConsole);
  809. scope(exit)
  810. {
  811. auto exitCode = wait(pid);
  812. logFile.writeln("myapp exited with code ", exitCode);
  813. logFile.close();
  814. }
  815. ---
  816. */
  817. enum Config
  818. {
  819. none = 0,
  820. /**
  821. By default, the child process inherits the parent's environment,
  822. and any environment variables passed to $(LREF spawnProcess) will
  823. be added to it. If this flag is set, the only variables in the
  824. child process' environment will be those given to spawnProcess.
  825. */
  826. newEnv = 1,
  827. /**
  828. Unless the child process inherits the standard input/output/error
  829. streams of its parent, one almost always wants the streams closed
  830. in the parent when $(LREF spawnProcess) returns. Therefore, by
  831. default, this is done. If this is not desirable, pass any of these
  832. options to spawnProcess.
  833. */
  834. retainStdin = 2,
  835. retainStdout = 4, /// ditto
  836. retainStderr = 8, /// ditto
  837. /**
  838. On Windows, if the child process is a console application, this
  839. flag will prevent the creation of a console window. Otherwise,
  840. it will be ignored. On POSIX, $(D suppressConsole) has no effect.
  841. */
  842. suppressConsole = 16,
  843. /**
  844. On POSIX, open $(LINK2 http://en.wikipedia.org/wiki/File_descriptor,file descriptors)
  845. are by default inherited by the child process. As this may lead
  846. to subtle bugs when pipes or multiple threads are involved,
  847. $(LREF spawnProcess) ensures that all file descriptors except the
  848. ones that correspond to standard input/output/error are closed
  849. in the child process when it starts. Use $(D inheritFDs) to prevent
  850. this.
  851. On Windows, this option has no effect, and any handles which have been
  852. explicitly marked as inheritable will always be inherited by the child
  853. process.
  854. */
  855. inheritFDs = 32,
  856. }
  857. /// A handle that corresponds to a spawned process.
  858. final class Pid
  859. {
  860. /**
  861. The process ID number.
  862. This is a number that uniquely identifies the process on the operating
  863. system, for at least as long as the process is running. Once $(LREF wait)
  864. has been called on the $(LREF Pid), this method will return an
  865. invalid process ID.
  866. */
  867. @property int processID() const @safe pure nothrow
  868. {
  869. return _processID;
  870. }
  871. /**
  872. An operating system handle to the process.
  873. This handle is used to specify the process in OS-specific APIs.
  874. On POSIX, this function returns a $(D core.sys.posix.sys.types.pid_t)
  875. with the same value as $(LREF Pid.processID), while on Windows it returns
  876. a $(D core.sys.windows.windows.HANDLE).
  877. Once $(LREF wait) has been called on the $(LREF Pid), this method
  878. will return an invalid handle.
  879. */
  880. // Note: Since HANDLE is a reference, this function cannot be const.
  881. version (Windows)
  882. @property HANDLE osHandle() @safe pure nothrow
  883. {
  884. return _handle;
  885. }
  886. else version (Posix)
  887. @property pid_t osHandle() @safe pure nothrow
  888. {
  889. return _processID;
  890. }
  891. private:
  892. /*
  893. Pid.performWait() does the dirty work for wait() and nonBlockingWait().
  894. If block == true, this function blocks until the process terminates,
  895. sets _processID to terminated, and returns the exit code or terminating
  896. signal as described in the wait() documentation.
  897. If block == false, this function returns immediately, regardless
  898. of the status of the process. If the process has terminated, the
  899. function has the exact same effect as the blocking version. If not,
  900. it returns 0 and does not modify _processID.
  901. */
  902. version (Posix)
  903. int performWait(bool block) @trusted
  904. {
  905. if (_processID == terminated) return _exitCode;
  906. int exitCode;
  907. while(true)
  908. {
  909. int status;
  910. auto check = waitpid(_processID, &status, block ? 0 : WNOHANG);
  911. if (check == -1)
  912. {
  913. if (errno == ECHILD)
  914. {
  915. throw new ProcessException(
  916. "Process does not exist or is not a child process.");
  917. }
  918. else
  919. {
  920. // waitpid() was interrupted by a signal. We simply
  921. // restart it.
  922. assert (errno == EINTR);
  923. continue;
  924. }
  925. }
  926. if (!block && check == 0) return 0;
  927. if (WIFEXITED(status))
  928. {
  929. exitCode = WEXITSTATUS(status);
  930. break;
  931. }
  932. else if (WIFSIGNALED(status))
  933. {
  934. exitCode = -WTERMSIG(status);
  935. break;
  936. }
  937. // We check again whether the call should be blocking,
  938. // since we don't care about other status changes besides
  939. // "exited" and "terminated by signal".
  940. if (!block) return 0;
  941. // Process has stopped, but not terminated, so we continue waiting.
  942. }
  943. // Mark Pid as terminated, and cache and return exit code.
  944. _processID = terminated;
  945. _exitCode = exitCode;
  946. return exitCode;
  947. }
  948. else version (Windows)
  949. {
  950. int performWait(bool block) @trusted
  951. {
  952. if (_processID == terminated) return _exitCode;
  953. assert (_handle != INVALID_HANDLE_VALUE);
  954. if (block)
  955. {
  956. auto result = WaitForSingleObject(_handle, INFINITE);
  957. if (result != WAIT_OBJECT_0)
  958. throw ProcessException.newFromLastError("Wait failed.");
  959. }
  960. if (!GetExitCodeProcess(_handle, cast(LPDWORD)&_exitCode))
  961. throw ProcessException.newFromLastError();
  962. if (!block && _exitCode == STILL_ACTIVE) return 0;
  963. CloseHandle(_handle);
  964. _handle = INVALID_HANDLE_VALUE;
  965. _processID = terminated;
  966. return _exitCode;
  967. }
  968. ~this()
  969. {
  970. if(_handle != INVALID_HANDLE_VALUE)
  971. {
  972. CloseHandle(_handle);
  973. _handle = INVALID_HANDLE_VALUE;
  974. }
  975. }
  976. }
  977. // Special values for _processID.
  978. enum invalid = -1, terminated = -2;
  979. // OS process ID number. Only nonnegative IDs correspond to
  980. // running processes.
  981. int _processID = invalid;
  982. // Exit code cached by wait(). This is only expected to hold a
  983. // sensible value if _processID == terminated.
  984. int _exitCode;
  985. // Pids are only meant to be constructed inside this module, so
  986. // we make the constructor private.
  987. version (Windows)
  988. {
  989. HANDLE _handle = INVALID_HANDLE_VALUE;
  990. this(int pid, HANDLE handle) @safe pure nothrow
  991. {
  992. _processID = pid;
  993. _handle = handle;
  994. }
  995. }
  996. else
  997. {
  998. this(int id) @safe pure nothrow
  999. {
  1000. _processID = id;
  1001. }
  1002. }
  1003. }
  1004. /**
  1005. Waits for the process associated with $(D pid) to terminate, and returns
  1006. its exit status.
  1007. In general one should always _wait for child processes to terminate
  1008. before exiting the parent process. Otherwise, they may become
  1009. "$(WEB en.wikipedia.org/wiki/Zombie_process,zombies)" – processes
  1010. that are defunct, yet still occupy a slot in the OS process table.
  1011. If the process has already terminated, this function returns directly.
  1012. The exit code is cached, so that if wait() is called multiple times on
  1013. the same $(LREF Pid) it will always return the same value.
  1014. POSIX_specific:
  1015. If the process is terminated by a signal, this function returns a
  1016. negative number whose absolute value is the signal number.
  1017. Since POSIX restricts normal exit codes to the range 0-255, a
  1018. negative return value will always indicate termination by signal.
  1019. Signal codes are defined in the $(D core.sys.posix.signal) module
  1020. (which corresponds to the $(D signal.h) POSIX header).
  1021. Throws:
  1022. $(LREF ProcessException) on failure.
  1023. Examples:
  1024. See the $(LREF spawnProcess) documentation.
  1025. See_also:
  1026. $(LREF tryWait), for a non-blocking function.
  1027. */
  1028. int wait(Pid pid) @safe
  1029. {
  1030. assert(pid !is null, "Called wait on a null Pid.");
  1031. return pid.performWait(true);
  1032. }
  1033. unittest // Pid and wait()
  1034. {
  1035. version (Windows) TestScript prog = "exit %~1";
  1036. else version (Posix) TestScript prog = "exit $1";
  1037. assert (wait(spawnProcess([prog.path, "0"])) == 0);
  1038. assert (wait(spawnProcess([prog.path, "123"])) == 123);
  1039. auto pid = spawnProcess([prog.path, "10"]);
  1040. assert (pid.processID > 0);
  1041. version (Windows) assert (pid.osHandle != INVALID_HANDLE_VALUE);
  1042. else version (Posix) assert (pid.osHandle == pid.processID);
  1043. assert (wait(pid) == 10);
  1044. assert (wait(pid) == 10); // cached exit code
  1045. assert (pid.processID < 0);
  1046. version (Windows) assert (pid.osHandle == INVALID_HANDLE_VALUE);
  1047. else version (Posix) assert (pid.osHandle < 0);
  1048. }
  1049. /**
  1050. A non-blocking version of $(LREF wait).
  1051. If the process associated with $(D pid) has already terminated,
  1052. $(D tryWait) has the exact same effect as $(D wait).
  1053. In this case, it returns a struct where the $(D terminated) field
  1054. is set to $(D true) and the $(D status) field has the same
  1055. interpretation as the return value of $(D wait).
  1056. If the process has $(I not) yet terminated, this function differs
  1057. from $(D wait) in that does not wait for this to happen, but instead
  1058. returns immediately. The $(D terminated) field of the returned
  1059. tuple will then be set to $(D false), while the $(D status) field
  1060. will always be 0 (zero). $(D wait) or $(D tryWait) should then be
  1061. called again on the same $(D Pid) at some later time; not only to
  1062. get the exit code, but also to avoid the process becoming a "zombie"
  1063. when it finally terminates. (See $(LREF wait) for details).
  1064. Returns:
  1065. A $(D struct) which contains the fields $(D bool terminated)
  1066. and $(D int status). (This will most likely change to become a
  1067. $(D std.typecons.Tuple!(bool,"terminated",int,"status")) in the future,
  1068. but a compiler bug currently prevents this.)
  1069. Throws:
  1070. $(LREF ProcessException) on failure.
  1071. Example:
  1072. ---
  1073. auto pid = spawnProcess("dmd myapp.d");
  1074. scope(exit) wait(pid);
  1075. ...
  1076. auto dmd = tryWait(pid);
  1077. if (dmd.terminated)
  1078. {
  1079. if (dmd.status == 0) writeln("Compilation succeeded!");
  1080. else writeln("Compilation failed");
  1081. }
  1082. else writeln("Still compiling...");
  1083. ...
  1084. ---
  1085. Note that in this example, the first $(D wait) call will have no
  1086. effect if the process has already terminated by the time $(D tryWait)
  1087. is called. In the opposite case, however, the $(D scope) statement
  1088. ensures that we always wait for the process if it hasn't terminated
  1089. by the time we reach the end of the scope.
  1090. */
  1091. auto tryWait(Pid pid) @safe
  1092. {
  1093. struct TryWaitResult
  1094. {
  1095. bool terminated;
  1096. int status;
  1097. }
  1098. assert(pid !is null, "Called tryWait on a null Pid.");
  1099. auto code = pid.performWait(false);
  1100. return TryWaitResult(pid._processID == Pid.terminated, code);
  1101. }
  1102. // unittest: This function is tested together with kill() below.
  1103. /**
  1104. Attempts to terminate the process associated with $(D pid).
  1105. The effect of this function, as well as the meaning of $(D codeOrSignal),
  1106. is highly platform dependent. Details are given below. Common to all
  1107. platforms is that this function only $(I initiates) termination of the process,
  1108. and returns immediately. It does not wait for the process to end,
  1109. nor does it guarantee that the process does in fact get terminated.
  1110. Always call $(LREF wait) to wait for a process to complete, even if $(D kill)
  1111. has been called on it.
  1112. Windows_specific:
  1113. The process will be
  1114. $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms686714%28v=vs.100%29.aspx,
  1115. forcefully and abruptly terminated). If $(D codeOrSignal) is specified, it
  1116. must be a nonnegative number which will be used as the exit code of the process.
  1117. If not, the process wil exit with code 1. Do not use $(D codeOrSignal = 259),
  1118. as this is a special value (aka. $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms683189.aspx,STILL_ACTIVE))
  1119. used by Windows to signal that a process has in fact $(I not) terminated yet.
  1120. ---
  1121. auto pid = spawnProcess("some_app");
  1122. kill(pid, 10);
  1123. assert (wait(pid) == 10);
  1124. ---
  1125. POSIX_specific:
  1126. A $(LINK2 http://en.wikipedia.org/wiki/Unix_signal,signal) will be sent to
  1127. the process, whose value is given by $(D codeOrSignal). Depending on the
  1128. signal sent, this may or may not terminate the process. Symbolic constants
  1129. for various $(LINK2 http://en.wikipedia.org/wiki/Unix_signal#POSIX_signals,
  1130. POSIX signals) are defined in $(D core.sys.posix.signal), which corresponds to the
  1131. $(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html,
  1132. $(D signal.h) POSIX header). If $(D codeOrSignal) is omitted, the
  1133. $(D SIGTERM) signal will be sent. (This matches the behaviour of the
  1134. $(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/utilities/kill.html,
  1135. $(D _kill)) shell command.)
  1136. ---
  1137. import core.sys.posix.signal: SIGKILL;
  1138. auto pid = spawnProcess("some_app");
  1139. kill(pid, SIGKILL);
  1140. assert (wait(pid) == -SIGKILL); // Negative return value on POSIX!
  1141. ---
  1142. Throws:
  1143. $(LREF ProcessException) on error (e.g. if codeOrSignal is invalid).
  1144. Note that failure to terminate the process is considered a "normal"
  1145. outcome, not an error.$(BR)
  1146. */
  1147. void kill(Pid pid)
  1148. {
  1149. version (Windows) kill(pid, 1);
  1150. else version (Posix)
  1151. {
  1152. import core.sys.posix.signal: SIGTERM;
  1153. kill(pid, SIGTERM);
  1154. }
  1155. }
  1156. /// ditto
  1157. void kill(Pid pid, int codeOrSignal)
  1158. {
  1159. version (Windows)
  1160. {
  1161. if (codeOrSignal < 0) throw new ProcessException("Invalid exit code");
  1162. version (Win32)
  1163. {
  1164. // On Windows XP, TerminateProcess() appears to terminate the
  1165. // *current* process if it is passed an invalid handle...
  1166. if (pid.osHandle == INVALID_HANDLE_VALUE)
  1167. throw new ProcessException("Invalid process handle");
  1168. }
  1169. if (!TerminateProcess(pid.osHandle, codeOrSignal))
  1170. throw ProcessException.newFromLastError();
  1171. }
  1172. else version (Posix)
  1173. {
  1174. import core.sys.posix.signal;
  1175. if (kill(pid.osHandle, codeOrSignal) == -1)
  1176. throw ProcessException.newFromErrno();
  1177. }
  1178. }
  1179. unittest // tryWait() and kill()
  1180. {
  1181. import core.thread;
  1182. // The test script goes into an infinite loop.
  1183. version (Windows)
  1184. {
  1185. TestScript prog = ":loop
  1186. goto loop";
  1187. }
  1188. else version (Posix)
  1189. {
  1190. import core.sys.posix.signal: SIGTERM, SIGKILL;
  1191. TestScript prog = "while true; do sleep 1; done";
  1192. }
  1193. auto pid = spawnProcess(prog.path);
  1194. Thread.sleep(dur!"seconds"(1));
  1195. kill(pid);
  1196. version (Windows) assert (wait(pid) == 1);
  1197. else version (Posix) assert (wait(pid) == -SIGTERM);
  1198. pid = spawnProcess(prog.path);
  1199. Thread.sleep(dur!"seconds"(1));
  1200. auto s = tryWait(pid);
  1201. assert (!s.terminated && s.status == 0);
  1202. assertThrown!ProcessException(kill(pid, -123)); // Negative code not allowed.
  1203. version (Windows) kill(pid, 123);
  1204. else version (Posix) kill(pid, SIGKILL);
  1205. do { s = tryWait(pid); } while (!s.terminated);
  1206. version (Windows) assert (s.status == 123);
  1207. else version (Posix) assert (s.status == -SIGKILL);
  1208. assertThrown!ProcessException(kill(pid));
  1209. }
  1210. /**
  1211. Creates a unidirectional _pipe.
  1212. Data is written to one end of the _pipe and read from the other.
  1213. ---
  1214. auto p = pipe();
  1215. p.writeEnd.writeln("Hello World");
  1216. assert (p.readEnd.readln().chomp() == "Hello World");
  1217. ---
  1218. Pipes can, for example, be used for interprocess communication
  1219. by spawning a new process and passing one end of the _pipe to
  1220. the child, while the parent uses the other end.
  1221. (See also $(LREF pipeProcess) and $(LREF pipeShell) for an easier
  1222. way of doing this.)
  1223. ---
  1224. // Use cURL to download the dlang.org front page, pipe its
  1225. // output to grep to extract a list of links to ZIP files,
  1226. // and write the list to the file "D downloads.txt":
  1227. auto p = pipe();
  1228. auto outFile = File("D downloads.txt", "w");
  1229. auto cpid = spawnProcess(["curl", "http://dlang.org/download.html"],
  1230. std.stdio.stdin, p.writeEnd);
  1231. scope(exit) wait(cpid);
  1232. auto gpid = spawnProcess(["grep", "-o", `http://\S*\.zip`],
  1233. p.readEnd, outFile);
  1234. scope(exit) wait(gpid);
  1235. ---
  1236. Returns:
  1237. A $(LREF Pipe) object that corresponds to the created _pipe.
  1238. Throws:
  1239. $(XREF stdio,StdioException) on failure.
  1240. */
  1241. version (Posix)
  1242. Pipe pipe() @trusted //TODO: @safe
  1243. {
  1244. int[2] fds;
  1245. if (core.sys.posix.unistd.pipe(fds) != 0)
  1246. throw new StdioException("Unable to create pipe");
  1247. Pipe p;
  1248. auto readFP = fdopen(fds[0], "r");
  1249. if (readFP == null)
  1250. throw new StdioException("Cannot open read end of pipe");
  1251. p._read = File(readFP, null);
  1252. auto writeFP = fdopen(fds[1], "w");
  1253. if (writeFP == null)
  1254. throw new StdioException("Cannot open write end of pipe");
  1255. p._write = File(writeFP, null);
  1256. return p;
  1257. }
  1258. else version (Windows)
  1259. Pipe pipe() @trusted //TODO: @safe
  1260. {
  1261. // use CreatePipe to create an anonymous pipe
  1262. HANDLE readHandle;
  1263. HANDLE writeHandle;
  1264. if (!CreatePipe(&readHandle, &writeHandle, null, 0))
  1265. {
  1266. throw new StdioException(
  1267. "Error creating pipe (" ~ sysErrorString(GetLastError()) ~ ')',
  1268. 0);
  1269. }
  1270. // Create file descriptors from the handles
  1271. version (DMC_RUNTIME)
  1272. {
  1273. auto readFD = _handleToFD(readHandle, FHND_DEVICE);
  1274. auto writeFD = _handleToFD(writeHandle, FHND_DEVICE);
  1275. }
  1276. else // MSVCRT
  1277. {
  1278. auto readFD = _open_osfhandle(readHandle, _O_RDONLY);
  1279. auto writeFD = _open_osfhandle(writeHandle, _O_APPEND);
  1280. }
  1281. version (DMC_RUNTIME) alias .close _close;
  1282. if (readFD == -1 || writeFD == -1)
  1283. {
  1284. // Close file descriptors, then throw.
  1285. if (readFD >= 0) _close(readFD);
  1286. else CloseHandle(readHandle);
  1287. if (writeFD >= 0) _close(writeFD);
  1288. else CloseHandle(writeHandle);
  1289. throw new StdioException("Error creating pipe");
  1290. }
  1291. // Create FILE pointers from the file descriptors
  1292. Pipe p;
  1293. version (DMC_RUNTIME)
  1294. {
  1295. // This is a re-implementation of DMC's fdopen, but without the
  1296. // mucking with the file descriptor. POSIX standard requires the
  1297. // new fdopen'd file to retain the given file descriptor's
  1298. // position.
  1299. FILE * local_fdopen(int fd, const(char)* mode)
  1300. {
  1301. auto fp = core.stdc.stdio.fopen("NUL", mode);
  1302. if(!fp) return null;
  1303. FLOCK(fp);
  1304. auto iob = cast(_iobuf*)fp;
  1305. .close(iob._file);
  1306. iob._file = fd;
  1307. iob._flag &= ~_IOTRAN;
  1308. FUNLOCK(fp);
  1309. return fp;
  1310. }
  1311. auto readFP = local_fdopen(readFD, "r");
  1312. auto writeFP = local_fdopen(writeFD, "a");
  1313. }
  1314. else // MSVCRT
  1315. {
  1316. auto readFP = _fdopen(readFD, "r");
  1317. auto writeFP = _fdopen(writeFD, "a");
  1318. }
  1319. if (readFP == null || writeFP == null)
  1320. {
  1321. // Close streams, then throw.
  1322. if (readFP != null) fclose(readFP);
  1323. else _close(readFD);
  1324. if (writeFP != null) fclose(writeFP);
  1325. else _close(writeFD);
  1326. throw new StdioException("Cannot open pipe");
  1327. }
  1328. p._read = File(readFP, null);
  1329. p._write = File(writeFP, null);
  1330. return p;
  1331. }
  1332. /// An interface to a pipe created by the $(LREF pipe) function.
  1333. struct Pipe
  1334. {
  1335. /// The read end of the pipe.
  1336. @property File readEnd() @trusted /*TODO: @safe nothrow*/ { return _read; }
  1337. /// The write end of the pipe.
  1338. @property File writeEnd() @trusted /*TODO: @safe nothrow*/ { return _write; }
  1339. /**
  1340. Closes both ends of the pipe.
  1341. Normally it is not necessary to do this manually, as $(XREF stdio,File)
  1342. objects are automatically closed when there are no more references
  1343. to them.
  1344. Note that if either end of the pipe has been passed to a child process,
  1345. it will only be closed in the parent process. (What happens in the
  1346. child process is platform dependent.)
  1347. */
  1348. void close() @trusted //TODO: @safe nothrow
  1349. {
  1350. _read.close();
  1351. _write.close();
  1352. }
  1353. private:
  1354. File _read, _write;
  1355. }
  1356. unittest
  1357. {
  1358. auto p = pipe();
  1359. p.writeEnd.writeln("Hello World");
  1360. p.writeEnd.flush();
  1361. assert (p.readEnd.readln().chomp() == "Hello World");
  1362. p.close();
  1363. assert (!p.readEnd.isOpen);
  1364. assert (!p.writeEnd.isOpen);
  1365. }
  1366. /**
  1367. Starts a new process, creating pipes to redirect its standard
  1368. input, output and/or error streams.
  1369. $(D pipeProcess) and $(D pipeShell) are convenient wrappers around
  1370. $(LREF spawnProcess) and $(LREF spawnShell), respectively, and
  1371. automate the task of redirecting one or more of the child process'
  1372. standard streams through pipes. Like the functions they wrap,
  1373. these functions return immediately, leaving the child process to
  1374. execute in parallel with the invoking process. It is recommended
  1375. to always call $(LREF wait) on the returned $(LREF ProcessPipes.pid),
  1376. as detailed in the documentation for $(D wait).
  1377. The $(D args)/$(D program)/$(D command), $(D env) and $(D config)
  1378. parameters are forwarded straight to the underlying spawn functions,
  1379. and we refer to their documentation for details.
  1380. Params:
  1381. args = An array which contains the program name as the zeroth element
  1382. and any command-line arguments in the following elements.
  1383. (See $(LREF spawnProcess) for details.)
  1384. program = The program name, $(I without) command-line arguments.
  1385. (See $(LREF spawnProcess) for details.)
  1386. command = A shell command which is passed verbatim to the command
  1387. interpreter. (See $(LREF spawnShell) for details.)
  1388. redirect = Flags that determine which streams are redirected, and
  1389. how. See $(LREF Redirect) for an overview of available
  1390. flags.
  1391. env = Additional environment variables for the child process.
  1392. (See $(LREF spawnProcess) for details.)
  1393. config = Flags that control process creation. See $(LREF Config)
  1394. for an overview of available flags, and note that the
  1395. $(D retainStd...) flags have no effect in this function.
  1396. Returns:
  1397. A $(LREF ProcessPipes) object which contains $(XREF stdio,File)
  1398. handles that communicate with the redirected streams of the child
  1399. process, along with a $(LREF Pid) object that corresponds to the
  1400. spawned process.
  1401. Throws:
  1402. $(LREF ProcessException) on failure to start the process.$(BR)
  1403. $(XREF stdio,StdioException) on failure to redirect any of the streams.$(BR)
  1404. Example:
  1405. ---
  1406. auto pipes = pipeProcess("my_application", Redirect.stdout | Redirect.stderr);
  1407. scope(exit) wait(pipes.pid);
  1408. // Store lines of output.
  1409. string[] output;
  1410. foreach (line; pipes.stdout.byLine) output ~= line.idup;
  1411. // Store lines of errors.
  1412. string[] errors;
  1413. foreach (line; pipes.stderr.byLine) errors ~= line.idup;
  1414. ---
  1415. */
  1416. ProcessPipes pipeProcess(in char[][] args,
  1417. Redirect redirect = Redirect.all,
  1418. const string[string] env = null,
  1419. Config config = Config.none)
  1420. @trusted //TODO: @safe
  1421. {
  1422. return pipeProcessImpl!spawnProcess(args, redirect, env, config);
  1423. }
  1424. /// ditto
  1425. ProcessPipes pipeProcess(in char[] program,
  1426. Redirect redirect = Redirect.all,
  1427. const string[string] env = null,
  1428. Config config = Config.none)
  1429. @trusted
  1430. {
  1431. return pipeProcessImpl!spawnProcess(program, redirect, env, config);
  1432. }
  1433. /// ditto
  1434. ProcessPipes pipeShell(in char[] command,
  1435. Redirect redirect = Redirect.all,
  1436. const string[string] env = null,
  1437. Config config = Config.none)
  1438. @safe
  1439. {
  1440. return pipeProcessImpl!spawnShell(command, redirect, env, config);
  1441. }
  1442. // Implementation of the pipeProcess() family of functions.
  1443. private ProcessPipes pipeProcessImpl(alias spawnFunc, Cmd)
  1444. (Cmd command,
  1445. Redirect redirectFlags,
  1446. const string[string] env = null,
  1447. Config config = Config.none)
  1448. @trusted //TODO: @safe
  1449. {
  1450. File childStdin, childStdout, childStderr;
  1451. ProcessPipes pipes;
  1452. pipes._redirectFlags = redirectFlags;
  1453. if (redirectFlags & Redirect.stdin)
  1454. {
  1455. auto p = pipe();
  1456. childStdin = p.readEnd;
  1457. pipes._stdin = p.writeEnd;
  1458. }
  1459. else
  1460. {
  1461. childStdin = std.stdio.stdin;
  1462. }
  1463. if (redirectFlags & Redirect.stdout)
  1464. {
  1465. if ((redirectFlags & Redirect.stdoutToStderr) != 0)
  1466. throw new StdioException("Cannot create pipe for stdout AND "
  1467. ~"redirect it to stderr", 0);
  1468. auto p = pipe();
  1469. childStdout = p.writeEnd;
  1470. pipes._stdout = p.readEnd;
  1471. }
  1472. else
  1473. {
  1474. childStdout = std.stdio.stdout;
  1475. }
  1476. if (redirectFlags & Redirect.stderr)
  1477. {
  1478. if ((redirectFlags & Redirect.stderrToStdout) != 0)
  1479. throw new StdioException("Cannot create pipe for stderr AND "
  1480. ~"redirect it to stdout", 0);
  1481. auto p = pipe();
  1482. childStderr = p.writeEnd;
  1483. pipes._stderr = p.readEnd;
  1484. }
  1485. else
  1486. {
  1487. childStderr = std.stdio.stderr;
  1488. }
  1489. if (redirectFlags & Redirect.stdoutToStderr)
  1490. {
  1491. if (redirectFlags & Redirect.stderrToStdout)
  1492. {
  1493. // We know that neither of the other options have been
  1494. // set, so we assign the std.stdio.std* streams directly.
  1495. childStdout = std.stdio.stderr;
  1496. childStderr = std.stdio.stdout;
  1497. }
  1498. else
  1499. {
  1500. childStdout = childStderr;
  1501. }
  1502. }
  1503. else if (redirectFlags & Redirect.stderrToStdout)
  1504. {
  1505. childStderr = childStdout;
  1506. }
  1507. config &= ~(Config.retainStdin | Config.retainStdout | Config.retainStderr);
  1508. pipes._pid = spawnFunc(command, childStdin, childStdout, childStderr,
  1509. env, config);
  1510. return pipes;
  1511. }
  1512. /**
  1513. Flags that can be passed to $(LREF pipeProcess) and $(LREF pipeShell)
  1514. to specify which of the child process' standard streams are redirected.
  1515. Use bitwise OR to combine flags.
  1516. */
  1517. enum Redirect
  1518. {
  1519. /// Redirect the standard input, output or error streams, respectively.
  1520. stdin = 1,
  1521. stdout = 2, /// ditto
  1522. stderr = 4, /// ditto
  1523. /**
  1524. Redirect _all three streams. This is equivalent to
  1525. $(D Redirect.stdin | Redirect.stdout | Redirect.stderr).
  1526. */
  1527. all = stdin | stdout | stderr,
  1528. /**
  1529. Redirect the standard error stream into the standard output stream.
  1530. This can not be combined with $(D Redirect.stderr).
  1531. */
  1532. stderrToStdout = 8,
  1533. /**
  1534. Redirect the standard output stream into the standard error stream.
  1535. This can not be combined with $(D Redirect.stdout).
  1536. */
  1537. stdoutToStderr = 16,
  1538. }
  1539. unittest
  1540. {
  1541. version (Windows) TestScript prog =
  1542. "call :sub %~1 %~2 0
  1543. call :sub %~1 %~2 1
  1544. call :sub %~1 %~2 2
  1545. call :sub %~1 %~2 3
  1546. exit 3
  1547. :sub
  1548. set /p INPUT=
  1549. if -%INPUT%-==-stop- ( exit %~3 )
  1550. echo %INPUT% %~1
  1551. echo %INPUT% %~2 1>&2";
  1552. else version (Posix) TestScript prog =
  1553. `for EXITCODE in 0 1 2 3; do
  1554. read INPUT
  1555. if test "$INPUT" = stop; then break; fi
  1556. echo "$INPUT $1"
  1557. echo "$INPUT $2" >&2
  1558. done
  1559. exit $EXITCODE`;
  1560. auto pp = pipeProcess([prog.path, "bar", "baz"]);
  1561. pp.stdin.writeln("foo");
  1562. pp.stdin.flush();
  1563. assert (pp.stdout.readln().chomp() == "foo bar");
  1564. assert (pp.stderr.readln().chomp().stripRight() == "foo baz");
  1565. pp.stdin.writeln("1234567890");
  1566. pp.stdin.flush();
  1567. assert (pp.stdout.readln().chomp() == "1234567890 bar");
  1568. assert (pp.stderr.readln().chomp().stripRight() == "1234567890 baz");
  1569. pp.stdin.writeln("stop");
  1570. pp.stdin.flush();
  1571. assert (wait(pp.pid) == 2);
  1572. pp = pipeProcess([prog.path, "12345", "67890"],
  1573. Redirect.stdin | Redirect.stdout | Redirect.stderrToStdout);
  1574. pp.stdin.writeln("xyz");
  1575. pp.stdin.flush();
  1576. assert (pp.stdout.readln().chomp() == "xyz 12345");
  1577. assert (pp.stdout.readln().chomp().stripRight() == "xyz 67890");
  1578. pp.stdin.writeln("stop");
  1579. pp.stdin.flush();
  1580. assert (wait(pp.pid) == 1);
  1581. pp = pipeShell(prog.path~" AAAAA BBB",
  1582. Redirect.stdin | Redirect.stdoutToStderr | Redirect.stderr);
  1583. pp.stdin.writeln("ab");
  1584. pp.stdin.flush();
  1585. assert (pp.stderr.readln().chomp() == "ab AAAAA");
  1586. assert (pp.stderr.readln().chomp().stripRight() == "ab BBB");
  1587. pp.stdin.writeln("stop");
  1588. pp.stdin.flush();
  1589. assert (wait(pp.pid) == 1);
  1590. }
  1591. unittest
  1592. {
  1593. TestScript prog = "exit 0";
  1594. assertThrown!StdioException(pipeProcess(
  1595. prog.path,
  1596. Redirect.stdout | Redirect.stdoutToStderr));
  1597. assertThrown!StdioException(pipeProcess(
  1598. prog.path,
  1599. Redirect.stderr | Redirect.stderrToStdout));
  1600. auto p = pipeProcess(prog.path, Redirect.stdin);
  1601. assertThrown!Error(p.stdout);
  1602. assertThrown!Error(p.stderr);
  1603. wait(p.pid);
  1604. p = pipeProcess(prog.path, Redirect.stderr);
  1605. assertThrown!Error(p.stdin);
  1606. assertThrown!Error(p.stdout);
  1607. wait(p.pid);
  1608. }
  1609. /**
  1610. Object which contains $(XREF stdio,File) handles that allow communication
  1611. with a child process through its standard streams.
  1612. */
  1613. struct ProcessPipes
  1614. {
  1615. /// The $(LREF Pid) of the child process.
  1616. @property Pid pid() @safe nothrow
  1617. {
  1618. assert(_pid !is null);
  1619. return _pid;
  1620. }
  1621. /**
  1622. An $(XREF stdio,File) that allows writing to the child process'
  1623. standard input stream.
  1624. Throws:
  1625. $(OBJECTREF Error) if the child process' standard input stream hasn't
  1626. been redirected.
  1627. */
  1628. @property File stdin() @trusted //TODO: @safe nothrow
  1629. {
  1630. if ((_redirectFlags & Redirect.stdin) == 0)
  1631. throw new Error("Child process' standard input stream hasn't "
  1632. ~"been redirected.");
  1633. return _stdin;
  1634. }
  1635. /**
  1636. An $(XREF stdio,File) that allows reading from the child process'
  1637. standard output stream.
  1638. Throws:
  1639. $(OBJECTREF Error) if the child process' standard output stream hasn't
  1640. been redirected.
  1641. */
  1642. @property File stdout() @trusted //TODO: @safe nothrow
  1643. {
  1644. if ((_redirectFlags & Redirect.stdout) == 0)
  1645. throw new Error("Child process' standard output stream hasn't "
  1646. ~"been redirected.");
  1647. return _stdout;
  1648. }
  1649. /**
  1650. An $(XREF stdio,File) that allows reading from the child process'
  1651. standard error stream.
  1652. Throws:
  1653. $(OBJECTREF Error) if the child process' standard error stream hasn't
  1654. been redirected.
  1655. */
  1656. @property File stderr() @trusted //TODO: @safe nothrow
  1657. {
  1658. if ((_redirectFlags & Redirect.stderr) == 0)
  1659. throw new Error("Child process' standard error stream hasn't "
  1660. ~"been redirected.");
  1661. return _stderr;
  1662. }
  1663. private:
  1664. Redirect _redirectFlags;
  1665. Pid _pid;
  1666. File _stdin, _stdout, _stderr;
  1667. }
  1668. /**
  1669. Executes the given program or shell command and returns its exit
  1670. code and output.
  1671. $(D execute) and $(D executeShell) start a new process using
  1672. $(LREF spawnProcess) and $(LREF spawnShell), respectively, and wait
  1673. for the process to complete before returning. The functions capture
  1674. what the child process prints to both its standard output and
  1675. standard error streams, and return this together with its exit code.
  1676. ---
  1677. auto dmd = execute(["dmd", "myapp.d"]);
  1678. if (dmd.status != 0) writeln("Compilation failed:\n", dmd.output);
  1679. auto ls = executeShell("ls -l");
  1680. if (ls.status != 0) writeln("Failed to retrieve file listing");
  1681. else writeln(ls.output);
  1682. ---
  1683. The $(D args)/$(D program)/$(D command), $(D env) and $(D config)
  1684. parameters are forwarded straight to the underlying spawn functions,
  1685. and we refer to their documentation for details.
  1686. Params:
  1687. args = An array which contains the program name as the zeroth element
  1688. and any command-line arguments in the following elements.
  1689. (See $(LREF spawnProcess) for details.)
  1690. program = The program name, $(I without) command-line arguments.
  1691. (See $(LREF spawnProcess) for details.)
  1692. command = A shell command which is passed verbatim to the command
  1693. interpreter. (See $(LREF spawnShell) for details.)
  1694. env = Additional environment variables for the child process.
  1695. (See $(LREF spawnProcess) for details.)
  1696. config = Flags that control process creation. See $(LREF Config)
  1697. for an overview of available flags, and note that the
  1698. $(D retainStd...) flags have no effect in this function.
  1699. maxOutput = The maximum number of bytes of output that should be
  1700. captured.
  1701. Returns:
  1702. A $(D struct) which contains the fields $(D int status) and
  1703. $(D string output). (This will most likely change to become a
  1704. $(D std.typecons.Tuple!(int,"status",string,"output")) in the future,
  1705. but a compiler bug currently prevents this.)
  1706. POSIX_specific:
  1707. If the process is terminated by a signal, the $(D status) field of
  1708. the return value will contain a negative number whose absolute
  1709. value is the signal number. (See $(LREF wait) for details.)
  1710. Throws:
  1711. $(LREF ProcessException) on failure to start the process.$(BR)
  1712. $(XREF stdio,StdioException) on failure to capture output.
  1713. */
  1714. auto execute(in char[][] args,
  1715. const string[string] env = null,
  1716. Config config = Config.none,
  1717. size_t maxOutput = size_t.max)
  1718. @trusted //TODO: @safe
  1719. {
  1720. return executeImpl!pipeProcess(args, env, config, maxOutput);
  1721. }
  1722. /// ditto
  1723. auto execute(in char[] program,
  1724. const string[string] env = null,
  1725. Config config = Config.none,
  1726. size_t maxOutput = size_t.max)
  1727. @trusted //TODO: @safe
  1728. {
  1729. return executeImpl!pipeProcess(program, env, config, maxOutput);
  1730. }
  1731. /// ditto
  1732. auto executeShell(in char[] command,
  1733. const string[string] env = null,
  1734. Config config = Config.none,
  1735. size_t maxOutput = size_t.max)
  1736. @trusted //TODO: @safe
  1737. {
  1738. return executeImpl!pipeShell(command, env, config, maxOutput);
  1739. }
  1740. // Does the actual work for execute() and executeShell().
  1741. private auto executeImpl(alias pipeFunc, Cmd)(
  1742. Cmd commandLine,
  1743. const string[string] env = null,
  1744. Config config = Config.none,
  1745. size_t maxOutput = size_t.max)
  1746. {
  1747. auto p = pipeFunc(commandLine, Redirect.stdout | Redirect.stderrToStdout,
  1748. env, config);
  1749. auto a = appender!(ubyte[])();
  1750. enum size_t defaultChunkSize = 4096;
  1751. immutable chunkSize = min(maxOutput, defaultChunkSize);
  1752. // Store up to maxOutput bytes in a.
  1753. foreach (ubyte[] chunk; p.stdout.byChunk(chunkSize))
  1754. {
  1755. immutable size_t remain = maxOutput - a.data.length;
  1756. if (chunk.length < remain) a.put(chunk);
  1757. else
  1758. {
  1759. a.put(chunk[0 .. remain]);
  1760. break;
  1761. }
  1762. }
  1763. // Exhaust the stream, if necessary.
  1764. foreach (ubyte[] chunk; p.stdout.byChunk(defaultChunkSize)) { }
  1765. struct ProcessOutput { int status; string output; }
  1766. return ProcessOutput(wait(p.pid), cast(string) a.data);
  1767. }
  1768. unittest
  1769. {
  1770. // To avoid printing the newline characters, we use the echo|set trick on
  1771. // Windows, and printf on POSIX (neither echo -n nor echo \c are portable).
  1772. version (Windows) TestScript prog =
  1773. "echo|set /p=%~1
  1774. echo|set /p=%~2 1>&2
  1775. exit 123";
  1776. else version (Posix) TestScript prog =
  1777. `printf '%s' $1
  1778. printf '%s' $2 >&2
  1779. exit 123`;
  1780. auto r = execute([prog.path, "foo", "bar"]);
  1781. assert (r.status == 123);
  1782. assert (r.output.stripRight() == "foobar");
  1783. auto s = execute([prog.path, "Hello", "World"]);
  1784. assert (s.status == 123);
  1785. assert (s.output.stripRight() == "HelloWorld");
  1786. }
  1787. unittest
  1788. {
  1789. auto r1 = executeShell("echo foo");
  1790. assert (r1.status == 0);
  1791. assert (r1.output.chomp() == "foo");
  1792. auto r2 = executeShell("echo bar 1>&2");
  1793. assert (r2.status == 0);
  1794. assert (r2.output.chomp().stripRight() == "bar");
  1795. auto r3 = executeShell("exit 123");
  1796. assert (r3.status == 123);
  1797. assert (r3.output.empty);
  1798. }
  1799. /// An exception that signals a problem with starting or waiting for a process.
  1800. class ProcessException : Exception
  1801. {
  1802. // Standard constructor.
  1803. this(string msg, string file = __FILE__, size_t line = __LINE__)
  1804. {
  1805. super(msg, file, line);
  1806. }
  1807. // Creates a new ProcessException based on errno.
  1808. static ProcessException newFromErrno(string customMsg = null,
  1809. string file = __FILE__,
  1810. size_t line = __LINE__)
  1811. {
  1812. import core.stdc.errno;
  1813. import std.c.string;
  1814. version (linux)
  1815. {
  1816. char[1024] buf;
  1817. auto errnoMsg = to!string(
  1818. std.c.string.strerror_r(errno, buf.ptr, buf.length));
  1819. }
  1820. else
  1821. {
  1822. auto errnoMsg = to!string(std.c.string.strerror(errno));
  1823. }
  1824. auto msg = customMsg.empty ? errnoMsg
  1825. : customMsg ~ " (" ~ errnoMsg ~ ')';
  1826. return new ProcessException(msg, file, line);
  1827. }
  1828. // Creates a new ProcessException based on GetLastError() (Windows only).
  1829. version (Windows)
  1830. static ProcessException newFromLastError(string customMsg = null,
  1831. string file = __FILE__,
  1832. size_t line = __LINE__)
  1833. {
  1834. auto lastMsg = sysErrorString(GetLastError());
  1835. auto msg = customMsg.empty ? lastMsg
  1836. : customMsg ~ " (" ~ lastMsg ~ ')';
  1837. return new ProcessException(msg, file, line);
  1838. }
  1839. }
  1840. /**
  1841. Determines the path to the current user's default command interpreter.
  1842. On Windows, this function returns the contents of the COMSPEC environment
  1843. variable, if it exists. Otherwise, it returns the string $(D "cmd.exe").
  1844. On POSIX, $(D userShell) returns the contents of the SHELL environment
  1845. variable, if it exists and is non-empty. Otherwise, it returns
  1846. $(D "/bin/sh").
  1847. */
  1848. @property string userShell() @safe //TODO: nothrow
  1849. {
  1850. version (Windows) return environment.get("COMSPEC", "cmd.exe");
  1851. else version (Posix) return environment.get("SHELL", "/bin/sh");
  1852. }
  1853. // A command-line switch that indicates to the shell that it should
  1854. // interpret the following argument as a command to be executed.
  1855. version (Posix) private immutable string shellSwitch = "-c";
  1856. version (Windows) private immutable string shellSwitch = "/C";
  1857. /// Returns the process ID number of the current process.
  1858. @property int thisProcessID() @trusted //TODO: @safe nothrow
  1859. {
  1860. version (Windows) return GetCurrentProcessId();
  1861. else version (Posix) return getpid();
  1862. }
  1863. // Unittest support code: TestScript takes a string that contains a
  1864. // shell script for the current platform, and writes it to a temporary
  1865. // file. On Windows the file name gets a .cmd extension, while on
  1866. // POSIX its executable permission bit is set. The file is
  1867. // automatically deleted when the object goes out of scope.
  1868. version (unittest)
  1869. private struct TestScript
  1870. {
  1871. this(string code)
  1872. {
  1873. import std.ascii, std.file;
  1874. version (Windows)
  1875. {
  1876. auto ext = ".cmd";
  1877. auto firstLine = "@echo off";
  1878. }
  1879. else version (Posix)
  1880. {
  1881. auto ext = "";
  1882. auto firstLine = "#!/bin/sh";
  1883. }
  1884. path = uniqueTempPath()~ext;
  1885. std.file.write(path, firstLine~std.ascii.newline~code~std.ascii.newline);
  1886. version (Posix)
  1887. {
  1888. import core.sys.posix.sys.stat;
  1889. chmod(toStringz(path), octal!777);
  1890. }
  1891. }
  1892. ~this()
  1893. {
  1894. import std.file;
  1895. if (!path.empty && exists(path))
  1896. {
  1897. try { remove(path); }
  1898. catch (Exception e)
  1899. {
  1900. debug std.stdio.stderr.writeln(e.msg);
  1901. }
  1902. }
  1903. }
  1904. string path;
  1905. }
  1906. version (unittest)
  1907. private string uniqueTempPath()
  1908. {
  1909. import std.file, std.uuid;
  1910. return buildPath(tempDir(), randomUUID().toString());
  1911. }
  1912. // =============================================================================
  1913. // Functions for shell command quoting/escaping.
  1914. // =============================================================================
  1915. /*
  1916. Command line arguments exist in three forms:
  1917. 1) string or char* array, as received by main.
  1918. Also used internally on POSIX systems.
  1919. 2) Command line string, as used in Windows'
  1920. CreateProcess and CommandLineToArgvW functions.
  1921. A specific quoting and escaping algorithm is used
  1922. to distinguish individual arguments.
  1923. 3) Shell command string, as written at a shell prompt
  1924. or passed to cmd /C - this one may contain shell
  1925. control characters, e.g. > or | for redirection /
  1926. piping - thus, yet another layer of escaping is
  1927. used to distinguish them from program arguments.
  1928. Except for escapeWindowsArgument, the intermediary
  1929. format (2) is hidden away from the user in this module.
  1930. */
  1931. /**
  1932. Escapes an argv-style argument array to be used with $(LREF spawnShell),
  1933. $(LREF pipeShell) or $(LREF executeShell).
  1934. ---
  1935. string url = "http://dlang.org/";
  1936. executeShell(escapeShellCommand("wget", url, "-O", "dlang-index.html"));
  1937. ---
  1938. Concatenate multiple $(D escapeShellCommand) and
  1939. $(LREF escapeShellFileName) results to use shell redirection or
  1940. piping operators.
  1941. ---
  1942. executeShell(
  1943. escapeShellCommand("curl", "http://dlang.org/download.html") ~
  1944. "|" ~
  1945. escapeShellCommand("grep", "-o", `http://\S*\.zip`) ~
  1946. ">" ~
  1947. escapeShellFileName("D download links.txt"));
  1948. ---
  1949. Throws:
  1950. $(OBJECTREF Exception) if any part of the command line contains unescapable
  1951. characters (NUL on all platforms, as well as CR and LF on Windows).
  1952. */
  1953. string escapeShellCommand(in char[][] args...)
  1954. //TODO: @safe pure nothrow
  1955. {
  1956. return escapeShellCommandString(escapeShellArguments(args));
  1957. }
  1958. unittest
  1959. {
  1960. // This is a simple unit test without any special requirements,
  1961. // in addition to the unittest_burnin one below which requires
  1962. // special preparation.
  1963. struct TestVector { string[] args; string windows, posix; }
  1964. TestVector[] tests =
  1965. [
  1966. {
  1967. args : ["foo"],
  1968. windows : `^"foo^"`,
  1969. posix : `'foo'`
  1970. },
  1971. {
  1972. args : ["foo", "hello"],
  1973. windows : `^"foo^" ^"hello^"`,
  1974. posix : `'foo' 'hello'`
  1975. },
  1976. {
  1977. args : ["foo", "hello world"],
  1978. windows : `^"foo^" ^"hello world^"`,
  1979. posix : `'foo' 'hello world'`
  1980. },
  1981. {
  1982. args : ["foo", "hello", "world"],
  1983. windows : `^"foo^" ^"hello^" ^"world^"`,
  1984. posix : `'foo' 'hello' 'world'`
  1985. },
  1986. {
  1987. args : ["foo", `'"^\`],
  1988. windows : `^"foo^" ^"'\^"^^\\^"`,
  1989. posix : `'foo' ''\''"^\'`
  1990. },
  1991. ];
  1992. foreach (test; tests)
  1993. version (Windows)
  1994. assert(escapeShellCommand(test.args) == test.windows);
  1995. else
  1996. assert(escapeShellCommand(test.args) == test.posix );
  1997. }
  1998. private string escapeShellCommandString(string command)
  1999. //TODO: @safe pure nothrow
  2000. {
  2001. version (Windows)
  2002. return escapeWindowsShellCommand(command);
  2003. else
  2004. return command;
  2005. }
  2006. private string escapeWindowsShellCommand(in char[] command)
  2007. //TODO: @safe pure nothrow (prevented by Appender)
  2008. {
  2009. auto result = appender!string();
  2010. result.reserve(command.length);
  2011. foreach (c; command)
  2012. switch (c)
  2013. {
  2014. case '\0':
  2015. throw new Exception("Cannot put NUL in command line");
  2016. case '\r':
  2017. case '\n':
  2018. throw new Exception("CR/LF are not escapable");
  2019. case '\x01': .. case '\x09':
  2020. case '\x0B': .. case '\x0C':
  2021. case '\x0E': .. case '\x1F':
  2022. case '"':
  2023. case '^':
  2024. case '&':
  2025. case '<':
  2026. case '>':
  2027. case '|':
  2028. result.put('^');
  2029. goto default;
  2030. default:
  2031. result.put(c);
  2032. }
  2033. return result.data;
  2034. }
  2035. private string escapeShellArguments(in char[][] args...)
  2036. @trusted pure nothrow
  2037. {
  2038. char[] buf;
  2039. @safe nothrow
  2040. char[] allocator(size_t size)
  2041. {
  2042. if (buf.length == 0)
  2043. return buf = new char[size];
  2044. else
  2045. {
  2046. auto p = buf.length;
  2047. buf.length = buf.length + 1 + size;
  2048. buf[p++] = ' ';
  2049. return buf[p..p+size];
  2050. }
  2051. }
  2052. foreach (arg; args)
  2053. escapeShellArgument!allocator(arg);
  2054. return assumeUnique(buf);
  2055. }
  2056. private auto escapeShellArgument(alias allocator)(in char[] arg) @safe nothrow
  2057. {
  2058. // The unittest for this function requires special
  2059. // preparation - see below.
  2060. version (Windows)
  2061. return escapeWindowsArgumentImpl!allocator(arg);
  2062. else
  2063. return escapePosixArgumentImpl!allocator(arg);
  2064. }
  2065. /**
  2066. Quotes a command-line argument in a manner conforming to the behavior of
  2067. $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx,
  2068. CommandLineToArgvW).
  2069. */
  2070. string escapeWindowsArgument(in char[] arg) @trusted pure nothrow
  2071. {
  2072. // Rationale for leaving this function as public:
  2073. // this algorithm of escaping paths is also used in other software,
  2074. // e.g. DMD's response files.
  2075. auto buf = escapeWindowsArgumentImpl!charAllocator(arg);
  2076. return assumeUnique(buf);
  2077. }
  2078. private char[] charAllocator(size_t size) @safe pure nothrow
  2079. {
  2080. return new char[size];
  2081. }
  2082. private char[] escapeWindowsArgumentImpl(alias allocator)(in char[] arg)
  2083. @safe nothrow
  2084. if (is(typeof(allocator(size_t.init)[0] = char.init)))
  2085. {
  2086. // References:
  2087. // * http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx
  2088. // * http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx
  2089. // Calculate the total string size.
  2090. // Trailing backslashes must be escaped
  2091. bool escaping = true;
  2092. // Result size = input size + 2 for surrounding quotes + 1 for the
  2093. // backslash for each escaped character.
  2094. size_t size = 1 + arg.length + 1;
  2095. foreach_reverse (c; arg)
  2096. {
  2097. if (c == '"')
  2098. {
  2099. escaping = true;
  2100. size++;
  2101. }
  2102. else
  2103. if (c == '\\')
  2104. {
  2105. if (escaping)
  2106. size++;
  2107. }
  2108. else
  2109. escaping = false;
  2110. }
  2111. // Construct result string.
  2112. auto buf = allocator(size);
  2113. size_t p = size;
  2114. buf[--p] = '"';
  2115. escaping = true;
  2116. foreach_reverse (c; arg)
  2117. {
  2118. if (c == '"')
  2119. escaping = true;
  2120. else
  2121. if (c != '\\')
  2122. escaping = false;
  2123. buf[--p] = c;
  2124. if (escaping)
  2125. buf[--p] = '\\';
  2126. }
  2127. buf[--p] = '"';
  2128. assert(p == 0);
  2129. return buf;
  2130. }
  2131. version(Windows) version(unittest)
  2132. {
  2133. import core.sys.windows.windows;
  2134. import core.stdc.stddef;
  2135. extern (Windows) wchar_t** CommandLineToArgvW(wchar_t*, int*);
  2136. extern (C) size_t wcslen(in wchar *);
  2137. unittest
  2138. {
  2139. string[] testStrings = [
  2140. `Hello`,
  2141. `Hello, world`,
  2142. `Hello, "world"`,
  2143. `C:\`,
  2144. `C:\dmd`,
  2145. `C:\Program Files\`,
  2146. ];
  2147. enum CHARS = `_x\" *&^`; // _ is placeholder for nothing
  2148. foreach (c1; CHARS)
  2149. foreach (c2; CHARS)
  2150. foreach (c3; CHARS)
  2151. foreach (c4; CHARS)
  2152. testStrings ~= [c1, c2, c3, c4].replace("_", "");
  2153. foreach (s; testStrings)
  2154. {
  2155. auto q = escapeWindowsArgument(s);
  2156. LPWSTR lpCommandLine = (to!(wchar[])("Dummy.exe " ~ q) ~ "\0"w).ptr;
  2157. int numArgs;
  2158. LPWSTR* args = CommandLineToArgvW(lpCommandLine, &numArgs);
  2159. scope(exit) LocalFree(args);
  2160. assert(numArgs==2, s ~ " => " ~ q ~ " #" ~ text(numArgs-1));
  2161. auto arg = to!string(args[1][0..wcslen(args[1])]);
  2162. assert(arg == s, s ~ " => " ~ q ~ " => " ~ arg);
  2163. }
  2164. }
  2165. }
  2166. private string escapePosixArgument(in char[] arg) @trusted pure nothrow
  2167. {
  2168. auto buf = escapePosixArgumentImpl!charAllocator(arg);
  2169. return assumeUnique(buf);
  2170. }
  2171. private char[] escapePosixArgumentImpl(alias allocator)(in char[] arg)
  2172. @safe nothrow
  2173. if (is(typeof(allocator(size_t.init)[0] = char.init)))
  2174. {
  2175. // '\'' means: close quoted part of argument, append an escaped
  2176. // single quote, and reopen quotes
  2177. // Below code is equivalent to:
  2178. // return `'` ~ std.array.replace(arg, `'`, `'\''`) ~ `'`;
  2179. size_t size = 1 + arg.length + 1;
  2180. foreach (c; arg)
  2181. if (c == '\'')
  2182. size += 3;
  2183. auto buf = allocator(size);
  2184. size_t p = 0;
  2185. buf[p++] = '\'';
  2186. foreach (c; arg)
  2187. if (c == '\'')
  2188. {
  2189. buf[p..p+4] = `'\''`;
  2190. p += 4;
  2191. }
  2192. else
  2193. buf[p++] = c;
  2194. buf[p++] = '\'';
  2195. assert(p == size);
  2196. return buf;
  2197. }
  2198. /**
  2199. Escapes a filename to be used for shell redirection with $(LREF spawnShell),
  2200. $(LREF pipeShell) or $(LREF executeShell).
  2201. */
  2202. string escapeShellFileName(in char[] fileName) @trusted pure nothrow
  2203. {
  2204. // The unittest for this function requires special
  2205. // preparation - see below.
  2206. version (Windows)
  2207. return cast(string)('"' ~ fileName ~ '"');
  2208. else
  2209. return escapePosixArgument(fileName);
  2210. }
  2211. // Loop generating strings with random characters
  2212. //version = unittest_burnin;
  2213. version(unittest_burnin)
  2214. unittest
  2215. {
  2216. // There are no readily-available commands on all platforms suitable
  2217. // for properly testing command escaping. The behavior of CMD's "echo"
  2218. // built-in differs from the POSIX program, and Windows ports of POSIX
  2219. // environments (Cygwin, msys, gnuwin32) may interfere with their own
  2220. // "echo" ports.
  2221. // To run this unit test, create std_process_unittest_helper.d with the
  2222. // following content and compile it:
  2223. // import std.stdio, std.array; void main(string[] args) { write(args.join("\0")); }
  2224. // Then, test this module with:
  2225. // rdmd --main -unittest -version=unittest_burnin process.d
  2226. auto helper = absolutePath("std_process_unittest_helper");
  2227. assert(shell(helper ~ " hello").split("\0")[1..$] == ["hello"], "Helper malfunction");
  2228. void test(string[] s, string fn)
  2229. {
  2230. string e;
  2231. string[] g;
  2232. e = escapeShellCommand(helper ~ s);
  2233. {
  2234. scope(failure) writefln("shell() failed.\nExpected:\t%s\nEncoded:\t%s", s, [e]);
  2235. g = shell(e).split("\0")[1..$];
  2236. }
  2237. assert(s == g, format("shell() test failed.\nExpected:\t%s\nGot:\t\t%s\nEncoded:\t%s", s, g, [e]));
  2238. e = escapeShellCommand(helper ~ s) ~ ">" ~ escapeShellFileName(fn);
  2239. {
  2240. scope(failure) writefln("system() failed.\nExpected:\t%s\nFilename:\t%s\nEncoded:\t%s", s, [fn], [e]);
  2241. system(e);
  2242. g = readText(fn).split("\0")[1..$];
  2243. }
  2244. remove(fn);
  2245. assert(s == g, format("system() test failed.\nExpected:\t%s\nGot:\t\t%s\nEncoded:\t%s", s, g, [e]));
  2246. }
  2247. while (true)
  2248. {
  2249. string[] args;
  2250. foreach (n; 0..uniform(1, 4))
  2251. {
  2252. string arg;
  2253. foreach (l; 0..uniform(0, 10))
  2254. {
  2255. dchar c;
  2256. while (true)
  2257. {
  2258. version (Windows)
  2259. {
  2260. // As long as DMD's system() uses CreateProcessA,
  2261. // we can't reliably pass Unicode
  2262. c = uniform(0, 128);
  2263. }
  2264. else
  2265. c = uniform!ubyte();
  2266. if (c == 0)
  2267. continue; // argv-strings are zero-terminated
  2268. version (Windows)
  2269. if (c == '\r' || c == '\n')
  2270. continue; // newlines are unescapable on Windows
  2271. break;
  2272. }
  2273. arg ~= c;
  2274. }
  2275. args ~= arg;
  2276. }
  2277. // generate filename
  2278. string fn = "test_";
  2279. foreach (l; 0..uniform(1, 10))
  2280. {
  2281. dchar c;
  2282. while (true)
  2283. {
  2284. version (Windows)
  2285. c = uniform(0, 128); // as above
  2286. else
  2287. c = uniform!ubyte();
  2288. if (c == 0 || c == '/')
  2289. continue; // NUL and / are the only characters
  2290. // forbidden in POSIX filenames
  2291. version (Windows)
  2292. if (c < '\x20' || c == '<' || c == '>' || c == ':' ||
  2293. c == '"' || c == '\\' || c == '|' || c == '?' || c == '*')
  2294. continue; // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
  2295. break;
  2296. }
  2297. fn ~= c;
  2298. }
  2299. test(args, fn);
  2300. }
  2301. }
  2302. // =============================================================================
  2303. // Environment variable manipulation.
  2304. // =============================================================================
  2305. /**
  2306. Manipulates _environment variables using an associative-array-like
  2307. interface.
  2308. This class contains only static methods, and cannot be instantiated.
  2309. See below for examples of use.
  2310. */
  2311. abstract final class environment
  2312. {
  2313. static:
  2314. /**
  2315. Retrieves the value of the environment variable with the given $(D name).
  2316. ---
  2317. auto path = environment["PATH"];
  2318. ---
  2319. Throws:
  2320. $(OBJECTREF Exception) if the environment variable does not exist.
  2321. See_also:
  2322. $(LREF environment.get), which doesn't throw on failure.
  2323. */
  2324. string opIndex(in char[] name) @safe
  2325. {
  2326. string value;
  2327. enforce(getImpl(name, value), "Environment variable not found: "~name);
  2328. return value;
  2329. }
  2330. /**
  2331. Retrieves the value of the environment variable with the given $(D name),
  2332. or a default value if the variable doesn't exist.
  2333. Unlike $(LREF environment.opIndex), this function never throws.
  2334. ---
  2335. auto sh = environment.get("SHELL", "/bin/sh");
  2336. ---
  2337. This function is also useful in checking for the existence of an
  2338. environment variable.
  2339. ---
  2340. auto myVar = environment.get("MYVAR");
  2341. if (myVar is null)
  2342. {
  2343. // Environment variable doesn't exist.
  2344. // Note that we have to use 'is' for the comparison, since
  2345. // myVar == null is also true if the variable exists but is
  2346. // empty.
  2347. }
  2348. ---
  2349. */
  2350. string get(in char[] name, string defaultValue = null) @safe //TODO: nothrow
  2351. {
  2352. string value;
  2353. auto found = getImpl(name, value);
  2354. return found ? value : defaultValue;
  2355. }
  2356. /**
  2357. Assigns the given $(D value) to the environment variable with the given
  2358. $(D name).
  2359. If the variable does not exist, it will be created. If it already exists,
  2360. it will be overwritten.
  2361. ---
  2362. environment["foo"] = "bar";
  2363. ---
  2364. Throws:
  2365. $(OBJECTREF Exception) if the environment variable could not be added
  2366. (e.g. if the name is invalid).
  2367. */
  2368. inout(char)[] opIndexAssign(inout char[] value, in char[] name) @trusted
  2369. {
  2370. version (Posix)
  2371. {
  2372. if (core.sys.posix.stdlib.setenv(toStringz(name), toStringz(value), 1) != -1)
  2373. {
  2374. return value;
  2375. }
  2376. // The default errno error message is very uninformative
  2377. // in the most common case, so we handle it manually.
  2378. enforce(errno != EINVAL,
  2379. "Invalid environment variable name: '"~name~"'");
  2380. errnoEnforce(false,
  2381. "Failed to add environment variable");
  2382. assert(0);
  2383. }
  2384. else version (Windows)
  2385. {
  2386. enforce(
  2387. SetEnvironmentVariableW(toUTF16z(name), toUTF16z(value)),
  2388. sysErrorString(GetLastError())
  2389. );
  2390. return value;
  2391. }
  2392. else static assert(0);
  2393. }
  2394. /**
  2395. Removes the environment variable with the given $(D name).
  2396. If the variable isn't in the environment, this function returns
  2397. successfully without doing anything.
  2398. */
  2399. void remove(in char[] name) @trusted // TODO: @safe nothrow
  2400. {
  2401. version (Windows) SetEnvironmentVariableW(toUTF16z(name), null);
  2402. else version (Posix) core.sys.posix.stdlib.unsetenv(toStringz(name));
  2403. else static assert(0);
  2404. }
  2405. /**
  2406. Copies all environment variables into an associative array.
  2407. Windows_specific:
  2408. While Windows environment variable names are case insensitive, D's
  2409. built-in associative arrays are not. This function will store all
  2410. variable names in uppercase (e.g. $(D PATH)).
  2411. Throws:
  2412. $(OBJECTREF Exception) if the environment variables could not
  2413. be retrieved (Windows only).
  2414. */
  2415. string[string] toAA() @trusted
  2416. {
  2417. string[string] aa;
  2418. version (Posix)
  2419. {
  2420. for (int i=0; environ[i] != null; ++i)
  2421. {
  2422. immutable varDef = to!string(environ[i]);
  2423. immutable eq = std.string.indexOf(varDef, '=');
  2424. assert (eq >= 0);
  2425. immutable name = varDef[0 .. eq];
  2426. immutable value = varDef[eq+1 .. $];
  2427. // In POSIX, environment variables may be defined more
  2428. // than once. This is a security issue, which we avoid
  2429. // by checking whether the key already exists in the array.
  2430. // For more info:
  2431. // http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/environment-variables.html
  2432. if (name !in aa) aa[name] = value;
  2433. }
  2434. }
  2435. else version (Windows)
  2436. {
  2437. auto envBlock = GetEnvironmentStringsW();
  2438. enforce(envBlock, "Failed to retrieve environment variables.");
  2439. scope(exit) FreeEnvironmentStringsW(envBlock);
  2440. for (int i=0; envBlock[i] != '\0'; ++i)
  2441. {
  2442. auto start = i;
  2443. while (envBlock[i] != '=') ++i;
  2444. immutable name = toUTF8(toUpper(envBlock[start .. i]));
  2445. start = i+1;
  2446. while (envBlock[i] != '\0') ++i;
  2447. // Just like in POSIX systems, environment variables may be
  2448. // defined more than once in an environment block on Windows,
  2449. // and it is just as much of a security issue there. Moreso,
  2450. // in fact, due to the case insensensitivity of variable names,
  2451. // which is not handled correctly by all programs.
  2452. if (name !in aa) aa[name] = toUTF8(envBlock[start .. i]);
  2453. }
  2454. }
  2455. else static assert(0);
  2456. return aa;
  2457. }
  2458. private:
  2459. // Returns the length of an environment variable (in number of
  2460. // wchars, including the null terminator), or 0 if it doesn't exist.
  2461. version (Windows)
  2462. int varLength(LPCWSTR namez) @trusted nothrow
  2463. {
  2464. return GetEnvironmentVariableW(namez, null, 0);
  2465. }
  2466. // Retrieves the environment variable, returns false on failure.
  2467. bool getImpl(in char[] name, out string value) @trusted //TODO: nothrow
  2468. {
  2469. version (Windows)
  2470. {
  2471. const namez = toUTF16z(name);
  2472. immutable len = varLength(namez);
  2473. if (len == 0) return false;
  2474. if (len == 1)
  2475. {
  2476. value = "";
  2477. return true;
  2478. }
  2479. auto buf = new WCHAR[len];
  2480. GetEnvironmentVariableW(namez, buf.ptr, to!DWORD(buf.length));
  2481. value = toUTF8(buf[0 .. $-1]);
  2482. return true;
  2483. }
  2484. else version (Posix)
  2485. {
  2486. const vz = core.sys.posix.stdlib.getenv(toStringz(name));
  2487. if (vz == null) return false;
  2488. auto v = vz[0 .. strlen(vz)];
  2489. // Cache the last call's result.
  2490. static string lastResult;
  2491. if (v != lastResult) lastResult = v.idup;
  2492. value = lastResult;
  2493. return true;
  2494. }
  2495. else static assert(0);
  2496. }
  2497. }
  2498. unittest
  2499. {
  2500. // New variable
  2501. environment["std_process"] = "foo";
  2502. assert (environment["std_process"] == "foo");
  2503. // Set variable again
  2504. environment["std_process"] = "bar";
  2505. assert (environment["std_process"] == "bar");
  2506. // Remove variable
  2507. environment.remove("std_process");
  2508. // Remove again, should succeed
  2509. environment.remove("std_process");
  2510. // Throw on not found.
  2511. assertThrown(environment["std_process"]);
  2512. // get() without default value
  2513. assert (environment.get("std_process") == null);
  2514. // get() with default value
  2515. assert (environment.get("std_process", "baz") == "baz");
  2516. // Convert to associative array
  2517. auto aa = environment.toAA();
  2518. assert (aa.length > 0);
  2519. foreach (n, v; aa)
  2520. {
  2521. // Wine has some bugs related to environment variables:
  2522. // - Wine allows the existence of an env. variable with the name
  2523. // "\0", but GetEnvironmentVariable refuses to retrieve it.
  2524. // - If an env. variable has zero length, i.e. is "\0",
  2525. // GetEnvironmentVariable should return 1. Instead it returns
  2526. // 0, indicating the variable doesn't exist.
  2527. version (Windows) if (n.length == 0 || v.length == 0) continue;
  2528. assert (v == environment[n]);
  2529. }
  2530. }
  2531. // =============================================================================
  2532. // Everything below this line was part of the old std.process, and most of
  2533. // it will be deprecated and removed.
  2534. // =============================================================================
  2535. /*
  2536. Macros:
  2537. WIKI=Phobos/StdProcess
  2538. Copyright: Copyright Digital Mars 2007 - 2009.
  2539. License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
  2540. Authors: $(WEB digitalmars.com, Walter Bright),
  2541. $(WEB erdani.org, Andrei Alexandrescu),
  2542. $(WEB thecybershadow.net, Vladimir Panteleev)
  2543. Source: $(PHOBOSSRC std/_process.d)
  2544. */
  2545. /*
  2546. Copyright Digital Mars 2007 - 2009.
  2547. Distributed under the Boost Software License, Version 1.0.
  2548. (See accompanying file LICENSE_1_0.txt or copy at
  2549. http://www.boost.org/LICENSE_1_0.txt)
  2550. */
  2551. import core.stdc.stdlib;
  2552. import std.c.stdlib;
  2553. import core.stdc.errno;
  2554. import core.thread;
  2555. import std.c.process;
  2556. import std.c.string;
  2557. version (Windows)
  2558. {
  2559. import std.format, std.random, std.file;
  2560. }
  2561. version (Posix)
  2562. {
  2563. import core.sys.posix.stdlib;
  2564. }
  2565. version (unittest)
  2566. {
  2567. import std.file, std.conv, std.random;
  2568. }
  2569. /**
  2570. Execute $(D command) in a _command shell.
  2571. $(RED This function is scheduled for deprecation. Please use
  2572. $(LREF spawnShell) or $(LREF executeShell) instead.)
  2573. Returns: If $(D command) is null, returns nonzero if the _command
  2574. interpreter is found, and zero otherwise. If $(D command) is not
  2575. null, returns -1 on error, or the exit status of command (which may
  2576. in turn signal an error in command's execution).
  2577. Note: On Unix systems, the homonym C function (which is accessible
  2578. to D programs as $(LINK2 std_c_process.html, std.c._system))
  2579. returns a code in the same format as $(LUCKY waitpid, waitpid),
  2580. meaning that C programs must use the $(D WEXITSTATUS) macro to
  2581. extract the actual exit code from the $(D system) call. D's $(D
  2582. system) automatically extracts the exit status.
  2583. */
  2584. int system(string command)
  2585. {
  2586. if (!command.ptr) return std.c.process.system(null);
  2587. const commandz = toStringz(command);
  2588. immutable status = std.c.process.system(commandz);
  2589. if (status == -1) return status;
  2590. version (Posix)
  2591. {
  2592. if (exited(status))
  2593. return exitstatus(status);
  2594. // Abnormal termination, return -1.
  2595. return -1;
  2596. }
  2597. else version (Windows)
  2598. return status;
  2599. else
  2600. static assert(0, "system not implemented for this OS.");
  2601. }
  2602. private void toAStringz(in string[] a, const(char)**az)
  2603. {
  2604. foreach(string s; a)
  2605. {
  2606. *az++ = toStringz(s);
  2607. }
  2608. *az = null;
  2609. }
  2610. /* ========================================================== */
  2611. //version (Windows)
  2612. //{
  2613. // int spawnvp(int mode, string pathname, string[] argv)
  2614. // {
  2615. // char** argv_ = cast(char**)alloca((char*).sizeof * (1 + argv.length));
  2616. //
  2617. // toAStringz(argv, argv_);
  2618. //
  2619. // return std.c.process.spawnvp(mode, toStringz(pathname), argv_);
  2620. // }
  2621. //}
  2622. // Incorporating idea (for spawnvp() on Posix) from Dave Fladebo
  2623. alias std.c.process._P_WAIT P_WAIT;
  2624. alias std.c.process._P_NOWAIT P_NOWAIT;
  2625. int spawnvp(int mode, string pathname, string[] argv)
  2626. {
  2627. auto argv_ = cast(const(char)**)alloca((char*).sizeof * (1 + argv.length));
  2628. toAStringz(argv, argv_);
  2629. version (Posix)
  2630. {
  2631. return _spawnvp(mode, toStringz(pathname), argv_);
  2632. }
  2633. else version (Windows)
  2634. {
  2635. return std.c.process.spawnvp(mode, toStringz(pathname), argv_);
  2636. }
  2637. else
  2638. static assert(0, "spawnvp not implemented for this OS.");
  2639. }
  2640. version (Posix)
  2641. {
  2642. private import core.sys.posix.unistd;
  2643. private import core.sys.posix.sys.wait;
  2644. int _spawnvp(int mode, in char *pathname, in char **argv)
  2645. {
  2646. int retval = 0;
  2647. pid_t pid = fork();
  2648. if(!pid)
  2649. { // child
  2650. std.c.process.execvp(pathname, argv);
  2651. goto Lerror;
  2652. }
  2653. else if(pid > 0)
  2654. { // parent
  2655. if(mode == _P_NOWAIT)
  2656. {
  2657. retval = pid; // caller waits
  2658. }
  2659. else
  2660. {
  2661. while(1)
  2662. {
  2663. int status;
  2664. pid_t wpid = waitpid(pid, &status, 0);
  2665. if(exited(status))
  2666. {
  2667. retval = exitstatus(status);
  2668. break;
  2669. }
  2670. else if(signaled(status))
  2671. {
  2672. retval = -termsig(status);
  2673. break;
  2674. }
  2675. else if(stopped(status)) // ptrace support
  2676. continue;
  2677. else
  2678. goto Lerror;
  2679. }
  2680. }
  2681. return retval;
  2682. }
  2683. Lerror:
  2684. retval = errno;
  2685. char[80] buf = void;
  2686. throw new Exception(
  2687. "Cannot spawn " ~ to!string(pathname) ~ "; "
  2688. ~ to!string(strerror_r(retval, buf.ptr, buf.length))
  2689. ~ " [errno " ~ to!string(retval) ~ "]");
  2690. } // _spawnvp
  2691. private
  2692. {
  2693. alias WIFSTOPPED stopped;
  2694. alias WIFSIGNALED signaled;
  2695. alias WTERMSIG termsig;
  2696. alias WIFEXITED exited;
  2697. alias WEXITSTATUS exitstatus;
  2698. } // private
  2699. } // version (Posix)
  2700. /* ========================================================== */
  2701. /**
  2702. * Replace the current process by executing a command, $(D pathname), with
  2703. * the arguments in $(D argv).
  2704. *
  2705. * $(RED These functions are scheduled for deprecation. Please use
  2706. * $(LREF spawnShell) instead (or, alternatively, the homonymous C
  2707. * functions declared in $(D std.c.process).))
  2708. *
  2709. * Typically, the first element of $(D argv) is
  2710. * the command being executed, i.e. $(D argv[0] == pathname). The 'p'
  2711. * versions of $(D exec) search the PATH environment variable for $(D
  2712. * pathname). The 'e' versions additionally take the new process'
  2713. * environment variables as an array of strings of the form key=value.
  2714. *
  2715. * Does not return on success (the current process will have been
  2716. * replaced). Returns -1 on failure with no indication of the
  2717. * underlying error.
  2718. */
  2719. int execv(in string pathname, in string[] argv)
  2720. {
  2721. auto argv_ = cast(const(char)**)alloca((char*).sizeof * (1 + argv.length));
  2722. toAStringz(argv, argv_);
  2723. return std.c.process.execv(toStringz(pathname), argv_);
  2724. }
  2725. /** ditto */
  2726. int execve(in string pathname, in string[] argv, in string[] envp)
  2727. {
  2728. auto argv_ = cast(const(char)**)alloca((char*).sizeof * (1 + argv.length));
  2729. auto envp_ = cast(const(char)**)alloca((char*).sizeof * (1 + envp.length));
  2730. toAStringz(argv, argv_);
  2731. toAStringz(envp, envp_);
  2732. return std.c.process.execve(toStringz(pathname), argv_, envp_);
  2733. }
  2734. /** ditto */
  2735. int execvp(in string pathname, in string[] argv)
  2736. {
  2737. auto argv_ = cast(const(char)**)alloca((char*).sizeof * (1 + argv.length));
  2738. toAStringz(argv, argv_);
  2739. return std.c.process.execvp(toStringz(pathname), argv_);
  2740. }
  2741. /** ditto */
  2742. int execvpe(in string pathname, in string[] argv, in string[] envp)
  2743. {
  2744. version(Posix)
  2745. {
  2746. // Is pathname rooted?
  2747. if(pathname[0] == '/')
  2748. {
  2749. // Yes, so just call execve()
  2750. return execve(pathname, argv, envp);
  2751. }
  2752. else
  2753. {
  2754. // No, so must traverse PATHs, looking for first match
  2755. string[] envPaths = std.array.split(
  2756. to!string(core.stdc.stdlib.getenv("PATH")), ":");
  2757. int iRet = 0;
  2758. // Note: if any call to execve() succeeds, this process will cease
  2759. // execution, so there's no need to check the execve() result through
  2760. // the loop.
  2761. foreach(string pathDir; envPaths)
  2762. {
  2763. string composite = cast(string) (pathDir ~ "/" ~ pathname);
  2764. iRet = execve(composite, argv, envp);
  2765. }
  2766. if(0 != iRet)
  2767. {
  2768. iRet = execve(pathname, argv, envp);
  2769. }
  2770. return iRet;
  2771. }
  2772. }
  2773. else version(Windows)
  2774. {
  2775. auto argv_ = cast(const(char)**)alloca((char*).sizeof * (1 + argv.length));
  2776. auto envp_ = cast(const(char)**)alloca((char*).sizeof * (1 + envp.length));
  2777. toAStringz(argv, argv_);
  2778. toAStringz(envp, envp_);
  2779. return std.c.process.execvpe(toStringz(pathname), argv_, envp_);
  2780. }
  2781. else
  2782. {
  2783. static assert(0);
  2784. } // version
  2785. }
  2786. /**
  2787. * Returns the process ID of the calling process, which is guaranteed to be
  2788. * unique on the system. This call is always successful.
  2789. *
  2790. * $(RED This function is scheduled for deprecation. Please use
  2791. * $(LREF thisProcessID) instead.)
  2792. *
  2793. * Example:
  2794. * ---
  2795. * writefln("Current process id: %s", getpid());
  2796. * ---
  2797. */
  2798. alias core.thread.getpid getpid;
  2799. /**
  2800. Runs $(D_PARAM cmd) in a shell and returns its standard output. If
  2801. the process could not be started or exits with an error code,
  2802. throws ErrnoException.
  2803. $(RED This function is scheduled for deprecation. Please use
  2804. $(LREF executeShell) instead.)
  2805. Example:
  2806. ----
  2807. auto tempFilename = chomp(shell("mcookie"));
  2808. auto f = enforce(fopen(tempFilename), "w");
  2809. scope(exit)
  2810. {
  2811. fclose(f) == 0 || assert(false);
  2812. system(escapeShellCommand("rm", tempFilename));
  2813. }
  2814. ... use f ...
  2815. ----
  2816. */
  2817. string shell(string cmd)
  2818. {
  2819. version(Windows)
  2820. {
  2821. // Generate a random filename
  2822. auto a = appender!string();
  2823. foreach (ref e; 0 .. 8)
  2824. {
  2825. formattedWrite(a, "%x", rndGen.front);
  2826. rndGen.popFront();
  2827. }
  2828. auto filename = a.data;
  2829. scope(exit) if (exists(filename)) remove(filename);
  2830. // We can't use escapeShellCommands here because we don't know
  2831. // if cmd is escaped (wrapped in quotes) or not, without relying
  2832. // on shady heuristics. The current code shouldn't cause much
  2833. // trouble unless filename contained spaces (it won't).
  2834. errnoEnforce(system(cmd ~ "> " ~ filename) == 0);
  2835. return readText(filename);
  2836. }
  2837. else version(Posix)
  2838. {
  2839. File f;
  2840. f.popen(cmd, "r");
  2841. char[] line;
  2842. string result;
  2843. while (f.readln(line))
  2844. {
  2845. result ~= line;
  2846. }
  2847. f.close();
  2848. return result;
  2849. }
  2850. else
  2851. static assert(0, "shell not implemented for this OS.");
  2852. }
  2853. unittest
  2854. {
  2855. auto x = shell("echo wyda");
  2856. // @@@ This fails on wine
  2857. //assert(x == "wyda" ~ newline, text(x.length));
  2858. import std.exception; // Issue 9444
  2859. version(windows)
  2860. string cmd = "98c10ec7e253a11cdff45f807b984a81 2>NUL";
  2861. else
  2862. string cmd = "98c10ec7e253a11cdff45f807b984a81 2>/dev/null";
  2863. assertThrown!ErrnoException(shell(cmd));
  2864. }
  2865. /**
  2866. Gets the value of environment variable $(D name) as a string. Calls
  2867. $(LINK2 std_c_stdlib.html#_getenv, std.c.stdlib._getenv)
  2868. internally.
  2869. $(RED This function is scheduled for deprecation. Please use
  2870. $(LREF environment.get) instead.)
  2871. */
  2872. string getenv(in char[] name)
  2873. {
  2874. // Cache the last call's result
  2875. static string lastResult;
  2876. auto p = core.stdc.stdlib.getenv(toStringz(name));
  2877. if (!p) return null;
  2878. auto value = p[0 .. strlen(p)];
  2879. if (value == lastResult) return lastResult;
  2880. return lastResult = value.idup;
  2881. }
  2882. /**
  2883. Sets the value of environment variable $(D name) to $(D value). If the
  2884. value was written, or the variable was already present and $(D
  2885. overwrite) is false, returns normally. Otherwise, it throws an
  2886. exception. Calls $(LINK2 std_c_stdlib.html#_setenv,
  2887. std.c.stdlib._setenv) internally.
  2888. $(RED This function is scheduled for deprecation. Please use
  2889. $(LREF environment.opIndexAssign) instead.)
  2890. */
  2891. version(StdDdoc) void setenv(in char[] name, in char[] value, bool overwrite);
  2892. else version(Posix) void setenv(in char[] name, in char[] value, bool overwrite)
  2893. {
  2894. errnoEnforce(
  2895. std.c.stdlib.setenv(toStringz(name), toStringz(value), overwrite) == 0);
  2896. }
  2897. /**
  2898. Removes variable $(D name) from the environment. Calls $(LINK2
  2899. std_c_stdlib.html#_unsetenv, std.c.stdlib._unsetenv) internally.
  2900. $(RED This function is scheduled for deprecation. Please use
  2901. $(LREF environment.remove) instead.)
  2902. */
  2903. version(StdDdoc) void unsetenv(in char[] name);
  2904. else version(Posix) void unsetenv(in char[] name)
  2905. {
  2906. errnoEnforce(std.c.stdlib.unsetenv(toStringz(name)) == 0);
  2907. }
  2908. version (Posix) unittest
  2909. {
  2910. setenv("wyda", "geeba", true);
  2911. assert(getenv("wyda") == "geeba");
  2912. // Get again to make sure caching works
  2913. assert(getenv("wyda") == "geeba");
  2914. unsetenv("wyda");
  2915. assert(getenv("wyda") is null);
  2916. }
  2917. /* ////////////////////////////////////////////////////////////////////////// */
  2918. version(MainTest)
  2919. {
  2920. int main(string[] args)
  2921. {
  2922. if(args.length < 2)
  2923. {
  2924. printf("Must supply executable (and optional arguments)\n");
  2925. return 1;
  2926. }
  2927. else
  2928. {
  2929. string[] dummy_env;
  2930. dummy_env ~= "VAL0=value";
  2931. dummy_env ~= "VAL1=value";
  2932. /+
  2933. foreach(string arg; args)
  2934. {
  2935. printf("%.*s\n", arg);
  2936. }
  2937. +/
  2938. // int i = execv(args[1], args[1 .. args.length]);
  2939. // int i = execvp(args[1], args[1 .. args.length]);
  2940. int i = execvpe(args[1], args[1 .. args.length], dummy_env);
  2941. printf("exec??() has returned! Error code: %d; errno: %d\n", i, /* errno */-1);
  2942. return 0;
  2943. }
  2944. }
  2945. }
  2946. /* ////////////////////////////////////////////////////////////////////////// */
  2947. version(StdDdoc)
  2948. {
  2949. /****************************************
  2950. * Start up the browser and set it to viewing the page at url.
  2951. */
  2952. void browse(string url);
  2953. }
  2954. else
  2955. version (Windows)
  2956. {
  2957. import core.sys.windows.windows;
  2958. extern (Windows)
  2959. HINSTANCE ShellExecuteA(HWND hwnd, LPCSTR lpOperation, LPCSTR lpFile, LPCSTR lpParameters, LPCSTR lpDirectory, INT nShowCmd);
  2960. pragma(lib,"shell32.lib");
  2961. void browse(string url)
  2962. {
  2963. ShellExecuteA(null, "open", toStringz(url), null, null, SW_SHOWNORMAL);
  2964. }
  2965. }
  2966. else version (OSX)
  2967. {
  2968. import core.stdc.stdio;
  2969. import core.stdc.string;
  2970. import core.sys.posix.unistd;
  2971. void browse(string url)
  2972. {
  2973. const(char)*[5] args;
  2974. const(char)* browser = core.stdc.stdlib.getenv("BROWSER");
  2975. if (browser)
  2976. { browser = strdup(browser);
  2977. args[0] = browser;
  2978. args[1] = toStringz(url);
  2979. args[2] = null;
  2980. }
  2981. else
  2982. {
  2983. args[0] = "open".ptr;
  2984. args[1] = toStringz(url);
  2985. args[2] = null;
  2986. }
  2987. auto childpid = fork();
  2988. if (childpid == 0)
  2989. {
  2990. core.sys.posix.unistd.execvp(args[0], cast(char**)args.ptr);
  2991. perror(args[0]); // failed to execute
  2992. return;
  2993. }
  2994. if (browser)
  2995. free(cast(void*)browser);
  2996. }
  2997. }
  2998. else version (Posix)
  2999. {
  3000. import core.stdc.stdio;
  3001. import core.stdc.string;
  3002. import core.sys.posix.unistd;
  3003. void browse(string url)
  3004. {
  3005. const(char)*[3] args;
  3006. const(char)* browser = core.stdc.stdlib.getenv("BROWSER");
  3007. if (browser)
  3008. { browser = strdup(browser);
  3009. args[0] = browser;
  3010. }
  3011. else
  3012. //args[0] = "x-www-browser".ptr; // doesn't work on some systems
  3013. args[0] = "xdg-open".ptr;
  3014. args[1] = toStringz(url);
  3015. args[2] = null;
  3016. auto childpid = fork();
  3017. if (childpid == 0)
  3018. {
  3019. core.sys.posix.unistd.execvp(args[0], cast(char**)args.ptr);
  3020. perror(args[0]); // failed to execute
  3021. return;
  3022. }
  3023. if (browser)
  3024. free(cast(void*)browser);
  3025. }
  3026. }
  3027. else
  3028. static assert(0, "os not supported");