From a481392e697ce2cdedf6731ff248d6dab7b5f569 Mon Sep 17 00:00:00 2001 From: George Mrejea Date: Mon, 28 Oct 2019 10:09:50 +0200 Subject: [PATCH] (FACT-2054) Facter::Core::Execution.execute expands shell builtins --- .../inc/leatherman/execution/execution.hpp | 8 ++++-- execution/src/execution.cc | 4 +-- execution/src/posix/execution.cc | 28 ++++++++++++++++++- execution/src/windows/execution.cc | 2 +- execution/tests/posix/execution.cc | 18 ++++++++++++ 5 files changed, 53 insertions(+), 7 deletions(-) diff --git a/execution/inc/leatherman/execution/execution.hpp b/execution/inc/leatherman/execution/execution.hpp index 5f11714c..2862b1ce 100644 --- a/execution/inc/leatherman/execution/execution.hpp +++ b/execution/inc/leatherman/execution/execution.hpp @@ -252,17 +252,19 @@ namespace leatherman { namespace execution { * Searches the given paths for the given executable file. * @param file The file to search for. * @param directories The directories to search. + * @param expand default true - expand first command to absolute path if founded in @param directories * @return Returns the full path or empty if the file could not be found. */ - std::string which(std::string const& file, std::vector const& directories = lth_util::environment::search_paths()); + std::string which(std::string const& file, std::vector const& directories = lth_util::environment::search_paths(), bool expand = true); /** - * Expands the executable in the command to the full path. + * Expands the executable in the command to the full path, expand is true, otherwise does not expand * @param command The command to expand. * @param directories The directories to search. + * @param expand default true - expand first command to absolute path if founded in @param directories * @return Returns the expanded command if the executable was found or empty if it was not found.. */ - std::string expand_command(std::string const& command, std::vector const& directories = lth_util::environment::search_paths()); + std::string expand_command(std::string const& command, std::vector const& directories = lth_util::environment::search_paths(), bool expand = true); /** * Executes the given program. diff --git a/execution/src/execution.cc b/execution/src/execution.cc index 09dfd829..59729dec 100644 --- a/execution/src/execution.cc +++ b/execution/src/execution.cc @@ -93,7 +93,7 @@ namespace leatherman { namespace execution { LOG_DEBUG("executing command: {1}", command_line.str()); } - string expand_command(string const& command, vector const& directories) + string expand_command(string const& command, vector const& directories, bool expand) { string result = command; boost::trim(result); @@ -126,7 +126,7 @@ namespace leatherman { namespace execution { } } - file = which(file, directories); + file = which(file, directories, expand); if (file.empty()) { return {}; } diff --git a/execution/src/posix/execution.cc b/execution/src/posix/execution.cc index 9ba8b30b..a181ae7a 100644 --- a/execution/src/posix/execution.cc +++ b/execution/src/posix/execution.cc @@ -26,6 +26,7 @@ using namespace leatherman::util::posix; using namespace leatherman::execution; using namespace leatherman::logging; using namespace leatherman::file_util; +using namespace boost::algorithm; using namespace boost::filesystem; // Declare environ for OSX @@ -139,8 +140,33 @@ namespace leatherman { namespace execution { return fs.st_mode & S_IXOTH; } - string which(string const& file, vector const& directories) + bool is_builtin(string const& file) { + string data; + string cmd = "type "; + cmd.append(file); + const int buffer_offset = 25; + const int max_buffer = buffer_offset + file.length(); + char buffer[max_buffer]; + FILE *stream = popen(cmd.c_str(), "r"); + if (stream != nullptr) { + rewind(stream); + if (fgets(buffer, max_buffer, stream) != NULL){ + data.append(buffer); + } + pclose(stream); + } + + return contains(data, "builtin"); + } + + string which(string const& file, vector const& directories, bool expand) + { + // if it is builtin, just return it + if ( !expand && is_builtin(file) ) { + return file; + } + // If the file is already absolute, return it if it's executable path p = file; boost::system::error_code ec; diff --git a/execution/src/windows/execution.cc b/execution/src/windows/execution.cc index 683823b4..7a3bb3aa 100644 --- a/execution/src/windows/execution.cc +++ b/execution/src/windows/execution.cc @@ -72,7 +72,7 @@ namespace leatherman { namespace execution { return isfile; } - string which(string const& file, vector const& directories) + string which(string const& file, vector const& directories, bool expand) { // On Windows, everything has execute permission; Ruby determined // executability based on extension {com, exe, bat, cmd}. We'll do the diff --git a/execution/tests/posix/execution.cc b/execution/tests/posix/execution.cc index c39479ec..b6ed40c4 100644 --- a/execution/tests/posix/execution.cc +++ b/execution/tests/posix/execution.cc @@ -115,6 +115,24 @@ SCENARIO("expanding command paths with execution::expand_command") { REQUIRE(expand_command("not_executable", { EXEC_TESTS_DIRECTORY "/fixtures" }) == ""); } } + GIVEN("set expand to false with a built-in executable") { + THEN("the executable is not expanded to an absolute path") { + boost::format fmt = boost::format("cd %1% && ls") % EXEC_TESTS_DIRECTORY; + std::string fmtStr = boost::str(fmt); + vector lines; + string command = expand_command(fmtStr, lines, false ); + REQUIRE( command == "cd " EXEC_TESTS_DIRECTORY " && ls"); + } + } + GIVEN("set expand to true with a built-in executable") { + THEN("the executable absolute path is not found") { + boost::format fmt = boost::format("cd %1% && ls") % EXEC_TESTS_DIRECTORY; + std::string fmtStr = boost::str(fmt); + vector lines; + string command = expand_command(fmtStr, lines, true ); + REQUIRE( command == ""); + } + } } SCENARIO("executing commands with execution::execute") {