Apache Mesos
shell.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_OS_WINDOWS_SHELL_HPP__
14 #define __STOUT_OS_WINDOWS_SHELL_HPP__
15 
16 #include <process.h>
17 #include <stdarg.h> // For va_list, va_start, etc.
18 
19 #include <algorithm>
20 #include <array>
21 #include <map>
22 #include <string>
23 #include <vector>
24 
25 #include <stout/error.hpp>
26 #include <stout/foreach.hpp>
27 #include <stout/none.hpp>
28 #include <stout/option.hpp>
29 #include <stout/os.hpp>
30 #include <stout/try.hpp>
31 #include <stout/windows.hpp>
32 
33 #include <stout/os/int_fd.hpp>
34 #include <stout/os/pipe.hpp>
35 
37 
38 namespace internal {
39 namespace windows {
40 
41 // Retrieves system environment in a `std::map`, ignoring
42 // the current process's environment variables.
44 {
45  std::map<std::wstring, std::wstring> system_env;
46  wchar_t* env_entry = nullptr;
47 
48  // Get the system environment.
49  // The third parameter (bool) tells the function *not* to inherit
50  // variables from the current process.
51  if (!::CreateEnvironmentBlock((LPVOID*)&env_entry, nullptr, FALSE)) {
52  return None();
53  }
54 
55  // Save the environment block in order to destroy it later.
56  wchar_t* env_block = env_entry;
57 
58  while (*env_entry != L'\0') {
59  // Each environment block contains the environment variables as follows:
60  // Var1=Value1\0
61  // Var2=Value2\0
62  // Var3=Value3\0
63  // ...
64  // VarN=ValueN\0\0
65  // The name of an environment variable cannot include an equal sign (=).
66 
67  // Construct a string from the pointer up to the first '\0',
68  // e.g. "Var1=Value1\0", then split into name and value.
69  std::wstring entry(env_entry);
70  std::wstring::size_type separator = entry.find(L"=");
71  std::wstring var_name(entry.substr(0, separator));
72  std::wstring varVal(entry.substr(separator + 1));
73 
74  // Mesos variables are upper case. Convert system variables to
75  // match the name provided by the scheduler in case of a collision.
76  // This is safe because Windows environment variables are case insensitive.
78  var_name.begin(), var_name.end(), var_name.begin(), ::towupper);
79 
80  // The system environment has priority.
81  system_env.insert_or_assign(var_name.data(), varVal.data());
82 
83  // Advance the pointer the length of the entry string plus the '\0'.
84  env_entry += entry.length() + 1;
85  }
86 
87  ::DestroyEnvironmentBlock(env_block);
88 
89  return system_env;
90 }
91 
92 
93 // Creates a null-terminated array of null-terminated strings that will be
94 // passed to `CreateProcessW` as the `lpEnvironment` argument, as described by
95 // MSDN[1]. This array needs to be sorted in alphabetical order, but the `map`
96 // already takes care of that. Note that this function explicitly handles
97 // UTF-16 environments, so it must be used in conjunction with the
98 // `CREATE_UNICODE_ENVIRONMENT` flag.
99 //
100 // NOTE: This function will add the system's environment variables into
101 // the returned string. These variables take precedence over the provided
102 // `env` and are generally necessary in order to launch things on Windows.
103 //
104 // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx
106  const Option<std::map<std::string, std::string>>& env)
107 {
108  if (env.isNone() || (env.isSome() && env.get().size() == 0)) {
109  return None();
110  }
111 
113 
114  // The system environment must be non-empty.
115  // No subprocesses will be able to launch if the system environment is blank.
116  CHECK(system_env.isSome() && system_env.get().size() > 0);
117 
118  std::map<std::wstring, std::wstring> combined_env;
119 
120  // Populate the combined environment first with the system environment.
121  foreachpair (const std::wstring& key,
122  const std::wstring& value,
123  system_env.get()) {
124  combined_env[key] = value;
125  }
126 
127  // Now override with the supplied environment.
128  foreachpair (const std::string& key,
129  const std::string& value,
130  env.get()) {
131  combined_env[wide_stringify(key)] = wide_stringify(value);
132  }
133 
134  std::wstring env_string;
135  foreachpair (const std::wstring& key,
136  const std::wstring& value,
137  combined_env) {
138  env_string += key + L'=' + value + L'\0';
139  }
140 
141  // Append final null terminating character.
142  env_string.push_back(L'\0');
143  return env_string;
144 }
145 
146 
147 // Concatenates multiple command-line arguments and escapes the values.
148 // NOTE: This is necessary even when using Windows APIs that "appear"
149 // to take arguments as a list, because those APIs will themselves
150 // concatenate command-line arguments *without* escaping them.
151 //
152 // This function escapes arguments with the following rules:
153 // 1) Any argument with a space, tab, newline, vertical tab,
154 // or double-quote must be surrounded in double-quotes.
155 // 2) Backslashes at the very end of an argument must be escaped.
156 // 3) Backslashes that precede a double-quote must be escaped.
157 // The double-quote must also be escaped.
158 //
159 // NOTE: The below algorithm is adapted from Daniel Colascione's public domain
160 // algorithm for quoting command line arguments on Windows for `CreateProcess`.
161 //
162 // https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
163 // NOLINT(whitespace/line_length)
164 inline std::wstring stringify_args(const std::vector<std::string>& argv)
165 {
166  std::wstring command;
167  for (auto argit = argv.cbegin(); argit != argv.cend(); ++argit) {
168  std::wstring arg = wide_stringify(*argit);
169  // Don't quote empty arguments or those without troublesome characters.
170  if (!arg.empty() && arg.find_first_of(L" \t\n\v\"") == arg.npos) {
171  command.append(arg);
172  } else {
173  // Beginning double quotation mark.
174  command.push_back(L'"');
175  for (auto it = arg.cbegin(); it != arg.cend(); ++it) {
176  // Count existent backslashes in argument.
177  unsigned int backslashes = 0;
178  while (it != arg.cend() && *it == L'\\') {
179  ++it;
180  ++backslashes;
181  }
182 
183  if (it == arg.cend()) {
184  // Escape all backslashes, but let the terminating double quotation
185  // mark we add below be interpreted as a metacharacter.
186  command.append(backslashes * 2, L'\\');
187  break;
188  } else if (*it == L'"') {
189  // Escape all backslashes and the following double quotation mark.
190  command.append(backslashes * 2 + 1, L'\\');
191  command.push_back(*it);
192  } else {
193  // Backslashes aren't special here.
194  command.append(backslashes, L'\\');
195  command.push_back(*it);
196  }
197  }
198 
199  // Terminating double quotation mark.
200  command.push_back(L'"');
201  }
202  // Space separate arguments (but don't append at end).
203  if (argit != argv.cend() - 1) {
204  command.push_back(L' ');
205  }
206  }
207  // Append final null terminating character.
208  command.push_back(L'\0');
209  return command;
210 }
211 
212 
213 struct ProcessData {
217 };
218 
219 
220 // Provides an interface for creating a child process on Windows.
221 //
222 // The `command` argument is given for compatibility, and is ignored. This is
223 // because the `CreateProcess` will use `argv[0]` as the command to be executed,
224 // and will perform a `PATH` lookup. If `command` were to be used instead,
225 // `CreateProcess` would require an absolute path.
226 //
227 // If `create_suspended` is `true`, the process will not be started, and the
228 // caller must use `ResumeThread` to start the process.
229 //
230 // The caller can specify explicit `stdin`, `stdout`, and `stderr` handles,
231 // in that order, for the process via the `pipes` argument.
232 //
233 // NOTE: If `pipes` are specified, they will be temporarily set to
234 // inheritable, and then set to uninheritable. This is a side effect
235 // on each `HANDLE`.
236 //
237 // The return value is a `ProcessData` struct, with the process and thread
238 // handles each saved in a `SharedHandle`, ensuring they are closed when struct
239 // goes out of scope.
241  const std::string& command,
242  const std::vector<std::string>& argv,
243  const Option<std::map<std::string, std::string>>& environment,
244  const bool create_suspended = false,
245  const Option<std::array<int_fd, 3>>& pipes = None(),
246  const std::vector<int_fd>& whitelist_fds = {})
247 {
248  // TODO(andschwa): Assert that `command` and `argv[0]` are the same.
249  const std::wstring arg_string = stringify_args(argv);
250  std::vector<wchar_t> arg_buffer(arg_string.begin(), arg_string.end());
251  arg_buffer.push_back(L'\0');
252 
253  // Create the process with a Unicode environment and extended
254  // startup info.
255  DWORD creation_flags =
256  CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT;
257  if (create_suspended) {
258  creation_flags |= CREATE_SUSPENDED;
259  }
260 
261  // Construct the environment that will be passed to `::CreateProcessW`.
263  std::vector<wchar_t> env_buffer;
264  if (env_string.isSome()) {
265  // This string contains the necessary null characters.
266  env_buffer.assign(env_string.get().begin(), env_string.get().end());
267  }
268 
269  wchar_t* process_env = env_buffer.empty() ? nullptr : env_buffer.data();
270 
271  PROCESS_INFORMATION process_info = {};
272 
273  STARTUPINFOEXW startup_info_ex = {};
274  startup_info_ex.StartupInfo.cb = sizeof(startup_info_ex);
275 
276  // Windows provides a way to whitelist a set of handles to be
277  // inherited by the child process.
278  // https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873
279  // (1) We're setting the pipe handles and whitelisted handles to be
280  // temporarily inheritable.
281  // (2) We're explicitly whitelisting the handles using a Windows API.
282  // (3) We're then setting the handles to back to non-inheritable
283  // after the child process has been created.
284  std::vector<HANDLE> handles;
285  if (pipes.isSome()) {
286  // Each of these handles must be inheritable.
287  foreach (const int_fd& fd, pipes.get()) {
288  handles.emplace_back(static_cast<HANDLE>(fd));
289  const Try<Nothing> inherit = set_inherit(fd, true);
290  if (inherit.isError()) {
291  return Error(inherit.error());
292  }
293  }
294 
295  // Hook up the stdin/out/err pipes and use the `STARTF_USESTDHANDLES`
296  // flag to instruct the child to use them [1].
297  // A more user-friendly example can be found in [2].
298  //
299  // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/ms686331(v=vs.85).aspx
300  // [2] https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx
301  startup_info_ex.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
302  startup_info_ex.StartupInfo.hStdInput = std::get<0>(pipes.get());
303  startup_info_ex.StartupInfo.hStdOutput = std::get<1>(pipes.get());
304  startup_info_ex.StartupInfo.hStdError = std::get<2>(pipes.get());
305  }
306 
307  foreach (const int_fd& fd, whitelist_fds) {
308  handles.emplace_back(static_cast<HANDLE>(fd));
309  const Try<Nothing> inherit = set_inherit(fd, true);
310  if (inherit.isError()) {
311  return Error(inherit.error());
312  }
313  }
314 
315  Result<std::shared_ptr<AttributeList>> attribute_list =
317 
318  if (attribute_list.isError()) {
319  return Error(attribute_list.error());
320  }
321 
322  if (attribute_list.isSome()) {
323  startup_info_ex.lpAttributeList = attribute_list->get();
324  }
325 
326  const BOOL result = ::CreateProcessW(
327  // This is replaced by the first token of `arg_buffer` string.
328  static_cast<LPCWSTR>(nullptr),
329  static_cast<LPWSTR>(arg_buffer.data()),
330  static_cast<LPSECURITY_ATTRIBUTES>(nullptr),
331  static_cast<LPSECURITY_ATTRIBUTES>(nullptr),
332  TRUE, // Inherit parent process handles (such as those in `pipes`).
333  creation_flags,
334  static_cast<LPVOID>(process_env),
335  static_cast<LPCWSTR>(nullptr), // Inherit working directory.
336  &startup_info_ex.StartupInfo,
337  &process_info);
338 
339  // Save the error from the previous call so that we can proceed to
340  // always revert the inheritance of the handles, and then report
341  // this error, if there was one.
342  const DWORD create_process_error = ::GetLastError();
343 
344  // NOTE: The MSDN documentation for `CreateProcess` states that it
345  // returns before the process has "finished initialization," but is
346  // not clear on precisely what initialization entails. It would seem
347  // that this does not affect inherited handles, as it stands to
348  // reason that the system call to `CreateProcess` causes inheritable
349  // handles to become inherited, and not some "initialization" of the
350  // child process. However, if an inheritance race condition
351  // manifests, this assumption should be re-evaluated.
352  if (pipes.isSome()) {
353  // These handles should no longer be inheritable. This prevents other child
354  // processes from accidentally inheriting the wrong handles.
355  //
356  // NOTE: This is explicit, and does not take into account the
357  // previous inheritance semantics of each `HANDLE`. It is assumed
358  // that users of this function send non-inheritable handles.
359  foreach (const int_fd& fd, pipes.get()) {
360  const Try<Nothing> inherit = set_inherit(fd, false);
361  if (inherit.isError()) {
362  return Error(inherit.error());
363  }
364  }
365  }
366 
367  foreach (const int_fd& fd, whitelist_fds) {
368  const Try<Nothing> inherit = set_inherit(fd, false);
369  if (inherit.isError()) {
370  return Error(inherit.error());
371  }
372  }
373 
374  if (result == FALSE) {
375  return WindowsError(
376  create_process_error,
377  "Failed to call `CreateProcess`: " + stringify(arg_string));
378  }
379 
380  return ProcessData{SharedHandle{process_info.hProcess, ::CloseHandle},
381  SharedHandle{process_info.hThread, ::CloseHandle},
382  static_cast<pid_t>(process_info.dwProcessId)};
383 }
384 
385 } // namespace windows {
386 } // namespace internal {
387 
388 namespace os {
389 namespace Shell {
390 
391 // Canonical constants used as platform-dependent args to `exec` calls.
392 // `name` is the command name, `arg0` is the first argument received
393 // by the callee, usually the command name and `arg1` is the second
394 // command argument received by the callee.
395 constexpr const char* name = "cmd.exe";
396 constexpr const char* arg0 = "cmd.exe";
397 constexpr const char* arg1 = "/c";
398 
399 } // namespace Shell {
400 
401 // Runs a shell command (with `cmd.exe`) with optional arguments.
402 //
403 // This assumes that a successful execution will result in the exit
404 // code for the command to be `0`; in this case, the contents of the
405 // `Try` will be the contents of `stdout`.
406 //
407 // If the exit code is non-zero, we will return an appropriate error
408 // message; but *not* `stderr`.
409 //
410 // If the caller needs to examine the contents of `stderr` it should
411 // be redirected to `stdout` (using, e.g., `2>&1 || exit /b 0` in the
412 // command string). The `|| exit /b 0` is required to obtain a success
413 // exit code in case of errors, and still obtain `stderr`, as piped to
414 // `stdout`.
415 template <typename... T>
416 Try<std::string> shell(const std::string& fmt, const T&... t)
417 {
418  using std::array;
419  using std::string;
420  using std::vector;
421 
422  const Try<string> command = strings::internal::format(fmt, t...);
423  if (command.isError()) {
424  return Error(command.error());
425  }
426 
427  // This function is intended to pass the arguments to the default
428  // shell, so we first add the arguments `cmd.exe cmd.exe /c`,
429  // followed by the command and arguments given.
430  vector<string> args = {os::Shell::name, os::Shell::arg0, os::Shell::arg1};
431 
432  { // Minimize the lifetime of the system allocated buffer.
433  //
434  // NOTE: This API returns a pointer to an array of `wchar_t*`,
435  // similar to `argv`. Each pointer to a null-terminated Unicode
436  // string represents an individual argument found on the command
437  // line. We use this because we cannot just split on whitespace.
438  int argc;
439  const std::unique_ptr<wchar_t*, decltype(&::LocalFree)> argv(
440  ::CommandLineToArgvW(wide_stringify(command.get()).data(), &argc),
441  &::LocalFree);
442  if (argv == nullptr) {
443  return WindowsError();
444  }
445 
446  for (int i = 0; i < argc; ++i) {
447  args.push_back(stringify(std::wstring(argv.get()[i])));
448  }
449  }
450 
451  // This function is intended to return only the `stdout` of the
452  // command; but since we have to redirect all of `stdin`, `stdout`,
453  // `stderr` if we want to redirect any one of them, we redirect
454  // `stdin` and `stderr` to `NUL`, and `stdout` to a pipe.
455  Try<int_fd> stdin_ = os::open(os::DEV_NULL, O_RDONLY);
456  if (stdin_.isError()) {
457  return Error(stdin_.error());
458  }
459 
460  Try<array<int_fd, 2>> stdout_ = os::pipe();
461  if (stdout_.isError()) {
462  return Error(stdout_.error());
463  }
464 
465  Try<int_fd> stderr_ = os::open(os::DEV_NULL, O_WRONLY);
466  if (stderr_.isError()) {
467  return Error(stderr_.error());
468  }
469 
470  // Ensure the file descriptors are closed when we leave this scope.
471  struct Closer
472  {
473  vector<int_fd> fds;
474  ~Closer()
475  {
476  foreach (int_fd& fd, fds) {
477  os::close(fd);
478  }
479  }
480  } closer = {{stdin_.get(), stdout_.get()[0], stderr_.get()}};
481 
482  array<int_fd, 3> pipes = {stdin_.get(), stdout_.get()[1], stderr_.get()};
483 
484  using namespace ::internal::windows;
485 
486  Try<ProcessData> process_data =
487  create_process(args.front(), args, None(), false, pipes);
488 
489  if (process_data.isError()) {
490  return Error(process_data.error());
491  }
492 
493  // Close the child end of the stdout pipe and then read until EOF.
494  os::close(stdout_.get()[1]);
495  string out;
496  Result<string> part = None();
497  do {
498  part = os::read(stdout_.get()[0], 1024);
499  if (part.isSome()) {
500  out += part.get();
501  }
502  } while (part.isSome());
503 
504  // Wait for the process synchronously.
505  ::WaitForSingleObject(process_data->process_handle.get_handle(), INFINITE);
506 
507  DWORD status;
508  if (!::GetExitCodeProcess(
509  process_data->process_handle.get_handle(), &status)) {
510  return Error("Failed to `GetExitCodeProcess`: " + command.get());
511  }
512 
513  if (status == 0) {
514  return out;
515  }
516 
517  return Error(
518  "Failed to execute '" + command.get() +
519  "'; the command was either "
520  "not found or exited with a non-zero exit status: " +
521  stringify(status));
522 }
523 
524 
525 template<typename... T>
526 inline int execlp(const char* file, T... t) = delete;
527 
528 
529 // Executes a command by calling "<command> <arguments...>", and
530 // returns after the command has been completed. Returns the process exit
531 // code on success and `None` on error.
533  const std::string& command,
534  const std::vector<std::string>& arguments,
535  const Option<std::map<std::string, std::string>>& environment = None())
536 {
537  using namespace ::internal::windows;
538 
539  Try<ProcessData> process_data =
540  create_process(command, arguments, environment);
541 
542  if (process_data.isError()) {
543  LOG(WARNING) << process_data.error();
544  return None();
545  }
546 
547  // Wait for the process synchronously.
548  ::WaitForSingleObject(process_data->process_handle.get_handle(), INFINITE);
549 
550  DWORD status;
551  if (!::GetExitCodeProcess(
552  process_data->process_handle.get_handle(), &status)) {
553  LOG(WARNING) << "Failed to `GetExitCodeProcess`: " << command;
554  return None();
555  }
556 
557  // Return the child exit code.
558  return static_cast<int>(status);
559 }
560 
561 
562 // Executes a command by calling "cmd /c <command>", and returns
563 // after the command has been completed. Returns the process exit
564 // code on success and `None` on error.
565 //
566 // Note: Be cautious about shell injection
567 // (https://en.wikipedia.org/wiki/Code_injection#Shell_injection)
568 // when using this method and use proper validation and sanitization
569 // on the `command`. For this reason in general `os::spawn` is
570 // preferred if a shell is not required.
571 inline Option<int> system(const std::string& command)
572 {
573  return os::spawn(Shell::name, {Shell::arg0, Shell::arg1, command});
574 }
575 
576 
577 // In order to emulate the semantics of `execvp`, `os::spawn` waits for the new
578 // process to exit, and returns its error code, which is propogated back to the
579 // parent via `exit` here.
580 inline int execvp(
581  const std::string& command,
582  const std::vector<std::string>& argv)
583 {
584  exit(os::spawn(command, argv).getOrElse(-1));
585  return -1;
586 }
587 
588 
589 // NOTE: This function can accept `Argv` and `Envp` constructs through their
590 // explicit type conversions, but unlike the POSIX implementations, it cannot
591 // accept the raw forms.
592 inline int execvpe(
593  const std::string& command,
594  const std::vector<std::string>& argv,
595  const std::map<std::string, std::string>& envp)
596 {
597  exit(os::spawn(command, argv, envp).getOrElse(-1));
598  return -1;
599 }
600 
601 } // namespace os {
602 
603 #endif // __STOUT_OS_WINDOWS_SHELL_HPP__
Result< std::shared_ptr< AttributeList > > create_attributes_list_for_handles(const std::vector< HANDLE > &handles)
Definition: inherit.hpp:32
std::wstring stringify_args(const std::vector< std::string > &argv)
Definition: shell.hpp:164
constexpr const char * arg1
Definition: shell.hpp:45
Try< std::array< int, 2 > > pipe()
Definition: pipe.hpp:33
Definition: errorbase.hpp:36
Definition: option.hpp:28
T & get()&
Definition: try.hpp:73
Try< ProcessData > create_process(const std::string &command, const std::vector< std::string > &argv, const Option< std::map< std::string, std::string >> &environment, const bool create_suspended=false, const Option< std::array< int_fd, 3 >> &pipes=None(), const std::vector< int_fd > &whitelist_fds={})
Definition: shell.hpp:240
Definition: windows.hpp:72
Definition: check.hpp:33
int execlp(const char *file, T...t)
Definition: shell.hpp:189
Definition: shell.hpp:213
Try< int_fd > open(const std::string &path, int oflag, mode_t mode=0)
Definition: open.hpp:35
Result< ProcessStatus > status(pid_t pid)
Definition: proc.hpp:166
Definition: error.hpp:108
Try< Nothing > set_inherit(const int_fd &fd, const bool inherit)
Definition: inherit.hpp:98
Option< int > spawn(const std::string &command, const std::vector< std::string > &arguments)
Definition: shell.hpp:162
Definition: posix_signalhandler.hpp:23
Definition: check.hpp:30
bool isSome() const
Definition: option.hpp:115
constexpr const char * arg0
Definition: shell.hpp:44
Definition: attributes.hpp:25
Environment * environment
DWORD pid_t
Definition: windows.hpp:181
URI file(const std::string &path)
Creates a file URI with the given path on the local host.
Definition: file.hpp:33
Try< Nothing > close(int fd)
Definition: close.hpp:24
SharedHandle process_handle
Definition: shell.hpp:214
SharedHandle thread_handle
Definition: shell.hpp:215
const T & get() const &
Definition: option.hpp:118
#define foreachpair(KEY, VALUE, ELEMS)
Definition: foreach.hpp:51
const T & get() const
Definition: result.hpp:115
static Try error(const E &e)
Definition: try.hpp:42
process::Future< Nothing > transform(process::Owned< Reader< T >> &&reader, const std::function< std::string(const T &)> &func, process::http::Pipe::Writer writer)
This is a helper function that reads records from a Reader, applies a transformation to the records a...
Definition: recordio.hpp:112
Result< std::string > read(int_fd fd, size_t size)
Definition: read.hpp:55
Try< std::string > shell(const std::string &fmt, const T &...t)
Runs a shell command with optional arguments.
Definition: shell.hpp:73
Option< std::map< std::wstring, std::wstring > > get_system_env()
Definition: shell.hpp:43
Definition: none.hpp:27
Definition: attributes.hpp:24
bool isError() const
Definition: try.hpp:71
int execvpe(const std::string &command, const std::vector< std::string > &argv, const std::map< std::string, std::string > &envp)
Definition: shell.hpp:592
constexpr char DEV_NULL[]
Definition: constants.hpp:30
int execvp(const std::string &command, const std::vector< std::string > &argv)
Definition: shell.hpp:580
Option< std::wstring > create_process_env(const Option< std::map< std::string, std::string >> &env)
Definition: shell.hpp:105
bool isSome() const
Definition: result.hpp:111
pid_t pid
Definition: shell.hpp:216
Try< std::string > format(const std::string &fmt, va_list args)
Definition: format.hpp:68
int int_fd
Definition: int_fd.hpp:35
std::string stringify(int flags)
Option< int > spawn(const std::string &command, const std::vector< std::string > &arguments, const Option< std::map< std::string, std::string >> &environment=None())
Definition: shell.hpp:532
constexpr const char * name
Definition: shell.hpp:43
Option< int > system(const std::string &command)
Definition: shell.hpp:133