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.

Reply via email to