When command is run via CLI (run.py -e "CMD"), the CMD string can contain $KEY, and KEY will be replaced with corresponding value from environ. The patch implements this also for commands run via REST or via runscript.
Fixes #868 Signed-off-by: Justin Cinkelj <[email protected]> --- core/commands.cc | 31 ++++++++++++++++++ loader.cc | 15 +-------- tests/tst-commands.cc | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 14 deletions(-) diff --git a/core/commands.cc b/core/commands.cc index 7bddfbb..ff77c1c 100644 --- a/core/commands.cc +++ b/core/commands.cc @@ -92,6 +92,32 @@ parse_command_line_min(const std::string line, bool &ok) } /* +Everything after first $ is assumed to be env var. +So with AA=aa, "$AA" -> "aa", and "X$AA" => "Xaa" +More than one $ is not supported, and "${AA}" is also not. +*/ +void expand_environ_vars(std::vector<std::vector<std::string>>& result) +{ + std::vector<std::vector<std::string>>::iterator cmd_iter; + std::vector<std::string>::iterator line_iter; + for (cmd_iter=result.begin(); cmd_iter!=result.end(); cmd_iter++) { + for (line_iter=cmd_iter->begin(); line_iter!=cmd_iter->end(); line_iter++) { + size_t pos; + if ((pos = line_iter->find_first_of('$')) != std::string::npos) { + std::string new_word = line_iter->substr(0, pos); + std::string key = line_iter->substr(pos+1); + auto tmp = getenv(key.c_str()); + //debug(" new_word=%s, key=%s, tmp=%s\n", new_word.c_str(), key.c_str(), tmp); + if (tmp) { + new_word += tmp; + } + *line_iter = new_word; + } + } + } +} + +/* In each runscript line, first N args starting with - are options. Parse options and remove them from result. @@ -193,6 +219,9 @@ runscript_expand(const std::vector<std::string>& cmd, bool &ok, bool &is_runscri ok = false; return result2; } + // Replace env vars found inside script. + // Options, script command and script command parameters can be set via env vars. + expand_environ_vars(result3); // process and remove options from command runscript_process_options(result3); result2.insert(result2.end(), result3.begin(), result3.end()); @@ -210,6 +239,8 @@ parse_command_line(const std::string line, bool &ok) { std::vector<std::vector<std::string> > result, result2; result = parse_command_line_min(line, ok); + // First replace environ variables in input command line. + expand_environ_vars(result); /* If command starts with runscript, we need to read actual command to diff --git a/loader.cc b/loader.cc index 834ea45..0f99ba5 100644 --- a/loader.cc +++ b/loader.cc @@ -316,20 +316,7 @@ std::vector<std::vector<std::string> > prepare_commands(int ac, char** av) // concatenate everything for (auto i = 0; i < ac; i++) { - std::string arg(""); - char* env = strchr(av[i],'$'); - if (av[i] && env) { - *env = '\0'; - env++; - auto tmp = getenv(env); - arg = av[i]; - if (tmp) { - arg += tmp; - } - } else { - arg = av[i]; - } - line += arg + " "; + line += std::string(av[i]) + " "; } commands = osv::parse_command_line(line, ok); diff --git a/tests/tst-commands.cc b/tests/tst-commands.cc index 7d0a679..a9f1e94 100644 --- a/tests/tst-commands.cc +++ b/tests/tst-commands.cc @@ -672,6 +672,91 @@ static bool test_runscript_with_env() return false; } + unsetenv("ASDF"); + + return true; +} + +static bool test_runscript_with_env_in_script() +{ + std::ofstream of1("/myscript", std::ios::out | std::ios::binary); + of1 << "--env=ASDF=ttrt --env=PORT=$THEPORT /$THEPROG pp1a $PRM1b pp1c $PRM1d\n"; + of1.close(); + + std::vector<std::vector<std::string> > result; + std::vector<std::string> cmd = { "/prog1" }; + size_t expected_size[] = {6}; + bool ok; + + // those two are set during command parsing + if (NULL != getenv("ASDF")) { + return false; + } + if (NULL != getenv("PORT")) { + return false; + } + // those are required during command parsing + if (0 != setenv("THEPORT", "4321", 1)) { + return false; + } + if (0 != setenv("THEPROG", "prog1", 1)) { + return false; + } + if (0 != setenv("PRM1b", "pp1b", 1)) { + return false; + } + if (0 != setenv("PRM1d", "pp1d", 1)) { + return false; + } + + result = osv::parse_command_line( + std::string("runscript \"/myscript\"; "), + ok); + + if (!ok) { + return false; + } + + if (result.size() != 1) { + return false; + } + + for (size_t i = 0; i < result.size(); i++) { + if (result[i].size() != expected_size[i]) { + return false; + } + if (result[i][0] != cmd[i]) { + return false; + } + } + + if (result[0][1] != std::string("pp1a")) { + return false; + } + if (result[0][2] != std::string("pp1b")) { + return false; + } + if (result[0][3] != std::string("pp1c")) { + return false; + } + if (result[0][4] != std::string("pp1d")) { + return false; + } + + if (std::string("ttrt") != getenv("ASDF")) { + return false; + } + if (std::string("4321") != getenv("PORT")) { + return false; + } + + unsetenv("ASDF"); + unsetenv("PORT"); + unsetenv("THEPORT"); + unsetenv("THEPROG"); + unsetenv("PRM1b"); + unsetenv("PRM1d "); + return true; } @@ -703,6 +788,8 @@ int main(int argc, char *argv[]) "runscript multiple lines, multiple commands per line, with args and quotes"); report(test_runscript_with_env(), "runscript with --env"); + report(test_runscript_with_env_in_script(), + "runscript with --env in script"); printf("SUMMARY: %d tests, %d failures\n", tests, fails); return 0; } -- 2.9.3 -- You received this message because you are subscribed to the Google Groups "OSv Development" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
