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
