Module: check_mk
Branch: master
Commit: b8ad11340307fdd0456876d08561ae5bcc6a3c27
URL:
http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=b8ad11340307fd…
Author: Jukka Aro <ja(a)mathias-kettner.de>
Date: Tue Jan 23 16:35:19 2018 +0100
Windows: allow single quotes in MRPE plugin path
Werk #5698 allowed to give MRPE plugin paths containing spaces in
double quotes. Allow giving MRPE plugin paths also in single quotes as
this seems to conform to the "common Windows behaviour" (whatever that
might be).
---
agents/windows/build_version | 2 +-
agents/windows/sections/SectionMRPE.cc | 50 ++++++++++------
agents/windows/stringutil.cc | 10 ++++
agents/windows/stringutil.h | 76 +++++++++++++++++++++++--
agents/windows/test/sections/SectionMRPETest.cc | 18 +++++-
5 files changed, 130 insertions(+), 26 deletions(-)
diff --git a/agents/windows/build_version b/agents/windows/build_version
index 5fd86fd..5caf80a 100644
--- a/agents/windows/build_version
+++ b/agents/windows/build_version
@@ -1 +1 @@
-3072
+3074
diff --git a/agents/windows/sections/SectionMRPE.cc
b/agents/windows/sections/SectionMRPE.cc
index 376f49d..e528eb9 100644
--- a/agents/windows/sections/SectionMRPE.cc
+++ b/agents/windows/sections/SectionMRPE.cc
@@ -140,12 +140,35 @@ bool SectionMRPE::produceOutputInner(std::ostream &out) {
namespace {
-void cleanQuotes(string &s) {
- if (s.front() == '"' && s.back() == '"') {
+enum class QuoteType { none, singleQuoted, doubleQuoted };
+
+inline bool quoted(QuoteType qt) { return qt != QuoteType::none; }
+
+inline QuoteType getQuoteType(const string &s) {
+ if (s.front() == '\'' && s.back() == '\'') {
+ return QuoteType::singleQuoted;
+ } else if (s.front() == '"' && s.back() == '"') {
+ return QuoteType::doubleQuoted;
+ } else {
+ return QuoteType::none;
+ }
+}
+
+void removeQuotes(string &s, QuoteType qt) {
+ if (quoted(qt)) {
s = s.substr(1, s.size() - 2);
}
}
+void wrapInQuotes(string &s, QuoteType qt) {
+ if (quoted(qt)) {
+ char quote = (qt == QuoteType::singleQuoted) ? '\'' :
'"';
+ s.reserve(s.size() + 2);
+ s.insert(0, 1, quote);
+ s.push_back(quote);
+ }
+}
+
void normalizeCommand(string &cmd) {
if (isPathRelative(cmd)) {
Environment *env = Environment::instance();
@@ -154,16 +177,10 @@ void normalizeCommand(string &cmd) {
}
ltrim(cmd);
rtrim(cmd);
- bool quoted = cmd.front() == '"' && cmd.back() ==
'"';
- if (quoted) {
- cleanQuotes(cmd);
- }
+ auto quoteType = getQuoteType(cmd);
+ removeQuotes(cmd, quoteType);
cmd.insert(0, env->agentDirectory() + "\\");
- if (quoted) {
- cmd.reserve(cmd.size() + 2);
- cmd.insert(0, 1, '"');
- cmd.push_back('"');
- }
+ wrapInQuotes(cmd, quoteType);
}
}
@@ -171,10 +188,7 @@ void normalizeCommand(string &cmd) {
template <>
mrpe_entry from_string<mrpe_entry>(const WinApiAdaptor &, const string
&value) {
- regex re("(\"([^\"]+)\"|[^\" ]+)");
- vector<string> tokens{
- sregex_token_iterator{value.cbegin(), value.cend(), re, 1},
- sregex_token_iterator{}};
+ vector<string> tokens = tokenizePossiblyQuoted(value);
if (tokens.size() < 2) {
throw StringConversionError(
@@ -182,9 +196,9 @@ mrpe_entry from_string<mrpe_entry>(const WinApiAdaptor &,
const string &value) {
"Format: SERVICEDESC COMMANDLINE");
}
- auto plugin_name = tokens[1]; // Intentional copy
+ auto plugin_name = tokens[1]; // Intentional copy
// compute plugin name, drop directory part
- cleanQuotes(plugin_name);
+ removeQuotes(plugin_name, getQuoteType(plugin_name));
for (const auto &delimiter : {"/", "\\"}) {
auto pos = plugin_name.find_last_of(delimiter);
@@ -206,7 +220,7 @@ mrpe_entry from_string<mrpe_entry>(const WinApiAdaptor &,
const string &value) {
}
auto &service_description = tokens[0];
- cleanQuotes(service_description);
+ removeQuotes(service_description, getQuoteType(service_description));
return {"", command_line, plugin_name, service_description};
}
diff --git a/agents/windows/stringutil.cc b/agents/windows/stringutil.cc
index d89efce..67e5060 100644
--- a/agents/windows/stringutil.cc
+++ b/agents/windows/stringutil.cc
@@ -79,6 +79,16 @@ char *next_word(char **line) {
return 0;
}
+template <>
+std::regex possiblyQuotedRegex<char>() {
+ return std::regex{"(\"([^\"]+)\"|'([^']+)'|[^\"
\\t]+)"};
+}
+
+template <>
+std::wregex possiblyQuotedRegex<wchar_t>() {
+ return
std::wregex{L"(\"([^\"]+)\"|'([^']+)'|[^\"
\\t]+)"};
+}
+
unsigned long long string_to_llu(const char *s) {
unsigned long long value = 0;
unsigned long long mult = 1;
diff --git a/agents/windows/stringutil.h b/agents/windows/stringutil.h
index b8cac68..15901db 100644
--- a/agents/windows/stringutil.h
+++ b/agents/windows/stringutil.h
@@ -61,11 +61,67 @@ inline void rtrim(std::string &s) {
std::vector<const char *> split_line(char *pos, int (*split_pred)(int));
char *next_word(char **line);
-inline std::vector<std::string> tokenize(const std::string &input,
- const std::string &delimiter) {
- regex re(delimiter);
- sregex_token_iterator first{input.cbegin(), input.cend(), re, -1}, last;
- return {first, last};
+template <typename CharT>
+inline std::vector<std::basic_string<CharT>> tokenizeBase(
+ const std::basic_string<CharT> &input, const std::basic_regex<CharT>
&re,
+ int submatch) {
+ return {std::regex_token_iterator<
+ typename std::basic_string<CharT>::const_iterator>{
+ input.cbegin(), input.cend(), re, submatch},
+ std::regex_token_iterator<
+ typename std::basic_string<CharT>::const_iterator>{}};
+}
+
+/**
+ * Split a string into tokens at given delimiter.
+ *
+ * @param[in] input The string to be split
+ * @param[in] delimiter The delimiter to split at
+ * @return A vector of tokens
+ */
+template <typename CharT>
+inline std::vector<std::basic_string<CharT>> tokenize(
+ const std::basic_string<CharT> &input,
+ const std::basic_string<CharT> &delimiter) {
+ return tokenizeBase(input, std::basic_regex<CharT>{delimiter}, -1);
+}
+
+/**
+ * Split a string into tokens at given delimiter.
+ *
+ * @param[in] input The string to be split
+ * @param[in] delimiter The delimiter to split at
+ * @return A vector of tokens
+ */
+template <typename CharT>
+inline std::vector<std::basic_string<CharT>> tokenize(
+ const std::basic_string<CharT> &input, const CharT *delimiter) {
+ return tokenizeBase(input, std::basic_regex<CharT>{delimiter}, -1);
+}
+
+template <typename CharT>
+inline std::basic_regex<CharT> possiblyQuotedRegex();
+template <>
+std::regex possiblyQuotedRegex<char>();
+template <>
+std::wregex possiblyQuotedRegex<wchar_t>();
+
+/**
+ * Split a string into tokens at space or tab. Substrings enclosed in single or
+ * double quotes are not split and the enclosing quotes are retained in the
+ * returned tokens.
+ *
+ * Example: ()
+ * input: This\t'is \t an' "example sentence."
+ * returned tokens: This, 'is \t an', "example sentence."
+ *
+ * @param[in] input The string to be split
+ * @return A vector of tokens
+ */
+template <typename CharT>
+inline std::vector<std::basic_string<CharT>> tokenizePossiblyQuoted(
+ const std::basic_string<CharT> &input) {
+ return tokenizeBase(input, possiblyQuotedRegex<CharT>(), 1);
}
unsigned long long string_to_llu(const char *s);
@@ -130,6 +186,16 @@ std::basic_string<SeparatorT> join(const
std::vector<ValueT> &input,
return join(input.begin(), input.end(), sep);
}
+/**
+ * Check if a path is relative or absolute. Works with both Windows and Unix
+ * style paths with backslash and forward slash separators, respectively. The
+ * presence of Windows drive letter does not affect the result for absolute or
+ * relative paths. Absolute UNC paths starting with either '\\' or '//'
are
+ * recognized as absolute paths.
+
+ * param[in] path The path to be checked
+ * return True if the path is relative, False if it is absolute
+ */
inline bool isPathRelative(const std::string &path) {
const std::array<regex, 2> regexes{
// Windows absolute path (with/without drive letter or UNC):
diff --git a/agents/windows/test/sections/SectionMRPETest.cc
b/agents/windows/test/sections/SectionMRPETest.cc
index 460d495..1c20390 100644
--- a/agents/windows/test/sections/SectionMRPETest.cc
+++ b/agents/windows/test/sections/SectionMRPETest.cc
@@ -50,7 +50,7 @@ TEST_F(wa_SectionMRPETest, from_string__relative_path_params_no_quotes)
{
}
TEST_F(wa_SectionMRPETest,
- from_string__absolute_path__with_spaces_params_with_quotes) {
+ from_string__absolute_path__with_spaces_params_with_double_quotes) {
const std::string input{
"\"foo bar\" \"\\baz qux\\quux\" corge \"grault
garply\""};
const mrpe_entry expected{"", "\"\\baz qux\\quux\" corge
\"grault garply\"",
@@ -58,7 +58,8 @@ TEST_F(wa_SectionMRPETest,
ASSERT_EQ(expected, from_string<mrpe_entry>(_mockwinapi, input));
}
-TEST_F(wa_SectionMRPETest, from_string__relative_path_params_with_quotes) {
+TEST_F(wa_SectionMRPETest,
+ from_string__relative_path_params_with_double_quotes) {
const std::string cwd{"C:\\corge"};
EXPECT_CALL(_mockwinapi, GetCurrentDirectoryA(32767, _))
.WillOnce(DoAll(SetArrayArgument<1>(cwd.cbegin(), cwd.cend()),
@@ -69,3 +70,16 @@ TEST_F(wa_SectionMRPETest,
from_string__relative_path_params_with_quotes) {
"foo"};
ASSERT_EQ(expected, from_string<mrpe_entry>(_mockwinapi, input));
}
+
+TEST_F(wa_SectionMRPETest,
+ from_string__relative_path_params_with_single_quotes) {
+ const std::string cwd{"C:\\corge"};
+ EXPECT_CALL(_mockwinapi, GetCurrentDirectoryA(32767, _))
+ .WillOnce(DoAll(SetArrayArgument<1>(cwd.cbegin(), cwd.cend()),
+ Return(cwd.size())));
+ ::Environment testEnv{true, false, &_mocklogger, _mockwinapi};
+ const std::string input{"foo 'bar baz\\qux' quux"};
+ const mrpe_entry expected{"", "'C:\\corge\\bar baz\\qux'
quux", "qux",
+ "foo"};
+ ASSERT_EQ(expected, from_string<mrpe_entry>(_mockwinapi, input));
+}