diff --git a/sbin/devd/devd.cc b/sbin/devd/devd.cc index f57f41eaa7e..bb7195cafee 100644 --- a/sbin/devd/devd.cc +++ b/sbin/devd/devd.cc @@ -636,6 +636,30 @@ config::is_id_char(char ch) const ch == '-')); } +string +config::shell_quote(const string &s) +{ + string buffer; + + /* + * Enclose the string in $' ' with escapes for ' and / characters making + * it one argument and ensuring the shell won't be affected by its + * usual list of candidates. + */ + buffer.reserve(s.length() * 3 / 2); + buffer += '$'; + buffer += '\''; + for (const char &c : s) { + if (c == '\'' || c == '\\') { + buffer += '\\'; + } + buffer += c; + } + buffer += '\''; + + return buffer; +} + void config::expand_one(const char *&src, string &dst) { @@ -650,8 +674,7 @@ config::expand_one(const char *&src, string &dst) } // $(foo) -> $(foo) - // Not sure if I want to support this or not, so for now we just pass - // it through. + // This is the escape hatch for passing down shell subcommands if (*src == '(') { dst += '$'; count = 1; @@ -677,7 +700,7 @@ config::expand_one(const char *&src, string &dst) do { buffer += *src++; } while (is_id_char(*src)); - dst.append(get_variable(buffer)); + dst.append(shell_quote(get_variable(buffer))); } const string diff --git a/sbin/devd/devd.hh b/sbin/devd/devd.hh index af4ce192dec..534c4d4c5a9 100644 --- a/sbin/devd/devd.hh +++ b/sbin/devd/devd.hh @@ -173,6 +173,7 @@ protected: void parse_one_file(const char *fn); void parse_files_in_dir(const char *dirname); void expand_one(const char *&src, std::string &dst); + std::string shell_quote(const std::string &s); bool is_id_char(char) const; bool chop_var(char *&buffer, char *&lhs, char *&rhs) const; private: