Currently runscript accepts only one option. By reusing
parse_options_parse that can be easily extended with additional options.

Variant of parse_options_parse accepting vector<string> instead of
argc,argv was added, for easier interfacing.

Fixes #819

Signed-off-by: Justin Cinkelj <[email protected]>
---
 core/commands.cc      | 39 ++++++++++++++++++++++++++++++++++++++
 loader.cc             | 22 ++++++++++++++++++++++
 tests/tst-commands.cc | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 113 insertions(+)

diff --git a/core/commands.cc b/core/commands.cc
index b2b3a60..a5879f4 100644
--- a/core/commands.cc
+++ b/core/commands.cc
@@ -22,6 +22,11 @@ namespace qi = boost::spirit::qi;
 namespace ascii = boost::spirit::ascii;
 using boost::spirit::ascii::space;
 
+#include <boost/program_options.hpp>
+void parse_options_parse(std::vector<std::string>& cmd,
+    const boost::program_options::options_description& desc,
+    boost::program_options::variables_map& vars);
+
 namespace osv {
 
 typedef std::string::const_iterator sciter;
@@ -90,6 +95,38 @@ parse_command_line_min(const std::string line, bool &ok)
 }
 
 /*
+In each runscript line, first N args starting with - are options.
+Parse options and remove them from result.
+
+Options are applied immediately, just as in loader.cc parse_options().
+So if two scripts set the same environment variable, then the last one wins.
+Applying all options before running any command is also safer than trying to
+apply options for each script at script execution (second script would modify
+environment setup by the first script, causing a race).
+*/
+static void runscript_process_options(std::vector<std::vector<std::string> >& 
result) {
+    namespace bpo = boost::program_options;
+    bpo::options_description desc("OSv runscript options");
+    desc.add_options()
+        ("env", bpo::value<std::vector<std::string>>(), "set Unix-like 
environment variable (putenv())");
+
+    for (size_t ii=0; ii<result.size(); ii++) {
+        auto cmd = result[ii];
+        bpo::variables_map vars;
+        parse_options_parse(cmd, desc, vars);
+
+        if (vars.count("env")) {
+            for (auto t : vars["env"].as<std::vector<std::string>>()) {
+                debug("Setting in environment: %s\n", t);
+                putenv(strdup(t.c_str()));
+            }
+        }
+
+        result[ii] = cmd;
+    }
+}
+
+/*
 If cmd starts with "runcript file", read content of file and
 return vector of all programs to be run.
 File can contain multiple commands per line.
@@ -125,6 +162,8 @@ std::vector<std::vector<std::string>> 
runscript_expand(const std::vector<std::st
                 ok = false;
                 return result2;
             }
+            // process and remove options from command
+            runscript_process_options(result3);
             result2.insert(result2.end(), result3.begin(), result3.end());
             line_num++;
         }
diff --git a/loader.cc b/loader.cc
index 47a547f..11a07c8 100644
--- a/loader.cc
+++ b/loader.cc
@@ -177,6 +177,28 @@ std::tuple<int, char**> parse_options_parse(int ac, char** 
av,
     return std::make_tuple(ac, av);
 }
 
+void parse_options_parse(std::vector<std::string>& cmd,
+    const boost::program_options::options_description& desc,
+    boost::program_options::variables_map& vars)
+{
+    int ac, ii;
+    char** av;
+
+    ac = cmd.size();
+    av = (char**)malloc(sizeof(char*) * (ac+1));
+    av[ac] = nullptr;
+    ii = 0;
+    for (auto prm : cmd) {
+        av[ii++] = strdup(prm.c_str());
+    }
+
+    auto ac_av = parse_options_parse(ac, av, desc, vars);
+    cmd.erase(cmd.begin(), cmd.begin() + (ac - std::get<0>(ac_av)));
+    for (ii=0; ii<ac; ii++) {
+        free(av[ii]);
+    }
+}
+
 std::tuple<int, char**> parse_options(int ac, char** av) /**/
 {
     namespace bpo = boost::program_options;
diff --git a/tests/tst-commands.cc b/tests/tst-commands.cc
index de0c143..7d0a679 100644
--- a/tests/tst-commands.cc
+++ b/tests/tst-commands.cc
@@ -625,6 +625,56 @@ static bool 
test_runscript_multiline_multiple_commands_per_line_with_args_quotes
     return true;
 }
 
+static bool test_runscript_with_env()
+{
+    std::ofstream of1("/myscript", std::ios::out | std::ios::binary);
+    of1 << "--env=ASDF=ttrt /prog1 pp1a pp1b\n";
+    of1.close();
+
+    std::vector<std::vector<std::string> > result;
+    std::vector<std::string> cmd = { "/prog1" };
+    size_t expected_size[] = {4};
+    bool ok;
+
+    if (NULL != getenv("ASDF")) {
+        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 (std::string("ttrt") != getenv("ASDF")) {
+        return false;
+    }
+
+    return true;
+}
+
 int main(int argc, char *argv[])
 {
     report(test_parse_simplest(), "simplest command line");
@@ -651,6 +701,8 @@ int main(int argc, char *argv[])
            "runscript multiple lines");
     
report(test_runscript_multiline_multiple_commands_per_line_with_args_quotes(),
            "runscript multiple lines, multiple commands per line, with args 
and quotes");
+    report(test_runscript_with_env(),
+           "runscript with --env");
     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