Windows: Implemented `os::shell`.

Review: https://reviews.apache.org/r/47874/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/e8083452
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/e8083452
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/e8083452

Branch: refs/heads/master
Commit: e80834526ed30726dbb7bb5ec3736df8c8f450a1
Parents: f61e835
Author: Daniel Pravat <[email protected]>
Authored: Mon May 30 18:30:30 2016 -0700
Committer: Joris Van Remoortere <[email protected]>
Committed: Mon May 30 18:30:43 2016 -0700

----------------------------------------------------------------------
 .../stout/include/stout/os/windows/shell.hpp    | 60 ++++++++++++++++++--
 1 file changed, 56 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/e8083452/3rdparty/stout/include/stout/os/windows/shell.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/include/stout/os/windows/shell.hpp 
b/3rdparty/stout/include/stout/os/windows/shell.hpp
index ea6ae5a..f84eed4 100644
--- a/3rdparty/stout/include/stout/os/windows/shell.hpp
+++ b/3rdparty/stout/include/stout/os/windows/shell.hpp
@@ -36,11 +36,63 @@ namespace Shell {
 
 } // namespace Shell {
 
-// Runs a shell command formatted with varargs and return the return value
-// of the command. Optionally, the output is returned via an argument.
-// TODO(vinod): Pass an istream object that can provide input to the command.
+/**
+ * Runs a shell command with optional arguments.
+ *
+ * This assumes that a successful execution will result in the exit code
+ * for the command to be `EXIT_SUCCESS`; in this case, the contents
+ * of the `Try` will be the contents of `stdout`.
+ *
+ * If the exit code is non-zero or the process was signaled, we will
+ * return an appropriate error message; but *not* `stderr`.
+ *
+ * If the caller needs to examine the contents of `stderr` it should
+ * be redirected to `stdout` (using, e.g., "2>&1 || true" in the command
+ * string).  The `|| true` is required to obtain a success exit
+ * code in case of errors, and still obtain `stderr`, as piped to
+ * `stdout`.
+ *
+ * @param fmt the formatting string that contains the command to execute
+ *   in the underlying shell.
+ * @param t optional arguments for `fmt`.
+ *
+ * @return the output from running the specified command with the shell; or
+ *   an error message if the command's exit code is non-zero.
+ */
 template <typename... T>
-Try<std::string> shell(const std::string& fmt, const T... t) = delete;
+Try<std::string> shell(const std::string& fmt, const T&... t)
+{
+  const Try<std::string> command = strings::internal::format(fmt, t...);
+  if (command.isError()) {
+    return Error(command.error());
+  }
+
+  FILE* file;
+  std::ostringstream stdoutstr;
+
+  if ((file = _popen(command.get().c_str(), "r")) == NULL) {
+    return Error("Failed to run '" + command.get() + "'");
+  }
+
+  char line[1024];
+  // NOTE(vinod): Ideally the if and while loops should be interchanged. But
+  // we get a broken pipe error if we don't read the output and simply close.
+  while (fgets(line, sizeof(line), file) != NULL) {
+    stdoutstr << line;
+  }
+
+  if (ferror(file) != 0) {
+    _pclose(file); // Ignoring result since we already have an error.
+    return Error("Error reading output of '" + command.get() + "'");
+  }
+
+  int status;
+  if ((status = _pclose(file)) == -1) {
+    return Error("Failed to get status of '" + command.get() + "'");
+  }
+
+  return stdoutstr.str();
+}
 
 // Executes a command by calling "cmd /c <command>", and returns
 // after the command has been completed. Returns 0 if succeeds, and

Reply via email to