From: Justin Cinkelj <justin.cink...@xlab.si>
Committer: Nadav Har'El <n...@scylladb.com>
Branch: master
command line: allow --env in runscript
If multiple scripts try to set the same environment variable, then the
last one wins.
Fixes #819
Signed-off-by: Justin Cinkelj <justin.cink...@xlab.si>
Message-Id: <20170109204847.779-1-justin.cink...@xlab.si>
---
diff --git a/core/commands.cc b/core/commands.cc
--- a/core/commands.cc
+++ b/core/commands.cc
@@ -12,6 +12,8 @@
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
+#include <boost/program_options.hpp>
+#include <osv/power.hh>
#include <osv/commands.hh>
#include <osv/align.hh>
#include <sys/types.h>
@@ -90,6 +92,65 @@ 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;
+ namespace bpos = boost::program_options::command_line_style;
+ // don't allow --foo bar (require --foo=bar) so we can find the first
non-option
+ // argument
+ int style = bpos::unix_style & ~(bpos::long_allow_next |
bpos::short_allow_next);
+ 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;
+
+ std::vector<const char*> args = { "dummy-string" };
+ // due to https://svn.boost.org/trac/boost/ticket/6991, we can't
terminate
+ // command line parsing on the executable name, so we need to look
for it
+ // ourselves
+ auto ac = cmd.size();
+ auto av = std::vector<const char*>();
+ av.reserve(ac);
+ for (auto& prm: cmd) {
+ av.push_back(prm.c_str());
+ }
+ auto nr_options = std::find_if(av.data(), av.data() + ac,
+ [](const char* arg) { return
arg[0] != '-'; }) - av.data();
+ std::copy(av.data(), av.data() + nr_options,
std::back_inserter(args));
+
+ try {
+ bpo::store(bpo::parse_command_line(args.size(), args.data(),
desc, style), vars);
+ } catch(std::exception &e) {
+ std::cout << e.what() << '\n';
+ std::cout << desc << '\n';
+ osv::poweroff();
+ }
+ bpo::notify(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()));
+ }
+ }
+
+ cmd.erase(cmd.begin(), cmd.begin() + nr_options);
+ 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 +186,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/tests/tst-commands.cc b/tests/tst-commands.cc
--- 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;
}
--
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 osv-dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.