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.
