From f05b2ae9528232bcefe4691cc5a3d350c85b6d10 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Sun, 23 Jun 2019 10:51:26 +0200 Subject: [PATCH] exec: add support for env variables, working directory. non-shell mode --- common | 2 +- etc/examples/nodes/exec.conf | 7 ++- include/villas/nodes/exec.hpp | 4 ++ lib/nodes/exec.cpp | 79 +++++++++++++++++++++---- tests/integration/pipe-loopback-exec.sh | 2 +- 5 files changed, 79 insertions(+), 15 deletions(-) diff --git a/common b/common index 7215f711a..719ad763c 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 7215f711a80bf1dde1059925417b0591589be448 +Subproject commit 719ad763cce242e418b82b63bf596dfccb6c725b diff --git a/etc/examples/nodes/exec.conf b/etc/examples/nodes/exec.conf index 75e3c86ad..aee5328fc 100644 --- a/etc/examples/nodes/exec.conf +++ b/etc/examples/nodes/exec.conf @@ -3,6 +3,11 @@ nodes = { type = "exec" format = "villas.human" flush = true - exec = "tee test" + exec = [ "tee test" ] + shell = true + working_directory = "/tmp" + environment = { + MYVAR = "TESTVAL" + } } } diff --git a/include/villas/nodes/exec.hpp b/include/villas/nodes/exec.hpp index adae7f101..5b5cc9c5a 100644 --- a/include/villas/nodes/exec.hpp +++ b/include/villas/nodes/exec.hpp @@ -44,7 +44,11 @@ struct exec { std::unique_ptr proc; bool flush; + bool shell; + std::string working_dir; std::string command; + villas::utils::Popen::arg_list arguments; + villas::utils::Popen::env_map environment; struct format_type *format; struct io io; }; diff --git a/lib/nodes/exec.cpp b/lib/nodes/exec.cpp index 7d7623947..dea9ec1fa 100644 --- a/lib/nodes/exec.cpp +++ b/lib/nodes/exec.cpp @@ -26,7 +26,9 @@ #include #include #include +#include +using namespace villas; using namespace villas::utils; int exec_parse(struct node *n, json_t *cfg) @@ -36,26 +38,79 @@ int exec_parse(struct node *n, json_t *cfg) json_error_t err; int ret, flush = 0; - const char *command; - const char *format = "villas.human"; + json_t *json_exec; + json_t *json_env = nullptr; - ret = json_unpack_ex(cfg, &err, 0, "{ s: s, s?: s, s?: b }", - "exec", &command, + const char *wd = nullptr; + const char *format = "villas.human"; + int shell = -1; + + ret = json_unpack_ex(cfg, &err, 0, "{ s: o, s?: s, s?: b, s?: o, s?: b, s?: s }", + "exec", &json_exec, "format", &format, - "flush", &flush + "flush", &flush, + "environment", &json_env, + "shell", &shell, + "working_directory", &wd ); if (ret) - jerror(&err, "Failed to parse configuration of node %s", node_name(n)); + throw ConfigError(cfg, err, "node-config-node-exec"); - e->command = command; e->flush = flush; + e->shell = shell < 0 ? json_is_string(json_exec) : shell; + + e->arguments.clear(); + e->environment.clear(); + + if (json_is_string(json_exec)) { + if (shell == 0) + throw ConfigError(json_exec, "node-config-node-exec-shell", "The exec setting must be an array if shell mode is disabled."); + + e->command = json_string_value(json_exec); + } + else if (json_is_array(json_exec)) { + if (shell == 1) + throw ConfigError(json_exec, "node-config-node-exec-shell", "The exec setting must be a string if shell mode is enabled."); + + if (json_array_size(json_exec) < 1) + throw ConfigError(json_exec, "node-config-node-exec-exec", "At least one argument must be given"); + + size_t i; + json_t *json_arg; + json_array_foreach(json_exec, i, json_arg) { + if (!json_is_string(json_arg)) + throw ConfigError(json_arg, "node-config-node-exec-exec", "All arguments must be of string type"); + + if (i == 0) + e->command = json_string_value(json_arg); + else + e->arguments.push_back(json_string_value(json_arg)); + } + } + + if (json_env) { + /* obj is a JSON object */ + const char *key; + json_t *json_value; + + json_object_foreach(json_env, key, json_value) { + if (!json_is_string(json_value)) + throw ConfigError(json_value, "node-config-node-exec-environment", "Environment variables must be of string type"); + + e->environment[key] = json_string_value(json_value); + } + } e->format = format_type_lookup(format); - if (!e->format) - error("Invalid format '%s' for node %s", format, node_name(n)); + if (!e->format) { + json_t *json_format = json_object_get(cfg, "format"); + throw ConfigError(json_format, "node-config-node-exec-format", "Invalid format: {)", format); + } - if (!(e->format->flags & IO_NEWLINES)) - error("The exec node-type currently supports only line-delimited formats"); + if (!(e->format->flags & IO_NEWLINES)) { + json_t *json_format = json_object_get(cfg, "format"); + throw ConfigError(json_format, "node-config-node-exec-format", "Only line-delimited formats are currently supported"); + } return 0; } @@ -75,7 +130,7 @@ int exec_prepare(struct node *n) return ret; /* Start subprocess */ - e->proc = std::make_unique(e->command); + e->proc = std::make_unique(e->command, e->arguments, e->environment, e->working_dir, e->shell); debug(2, "Started sub-process with pid=%d", e->proc->getPid()); return 0; diff --git a/tests/integration/pipe-loopback-exec.sh b/tests/integration/pipe-loopback-exec.sh index 7dba94358..f5ddd9934 100755 --- a/tests/integration/pipe-loopback-exec.sh +++ b/tests/integration/pipe-loopback-exec.sh @@ -44,7 +44,7 @@ cat > ${CONFIG_FILE} << EOF "type" : "exec", "format" : "${FORMAT}", - "command" : "cat" + "exec" : "cat" } } }