aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2016-08-20 07:58:36 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2016-08-20 08:10:18 +0300
commit3ea52272c8ffa360b01af63d5a50f9442a596fc5 (patch)
tree205e65226ddc9370fe15abb2fc28900181bce5e1
parentf1c724be131b8f89ef46a13d6181b0e037420bf1 (diff)
downloadrush-3ea52272c8ffa360b01af63d5a50f9442a596fc5.tar.gz
rush-3ea52272c8ffa360b01af63d5a50f9442a596fc5.tar.bz2
Bugfixes
* src/config.c (TOK_ENV): New flag. (toktab): Mark "env" with this flag. (parse_input_buf): If TOK_ENV bit is set, do environment variable expansion. * src/rush.c (env_setup): Bugfixes. Make sure it operates exactly as documented. (run_transforms): Major cleanup. * tests/env.at: New tests. * NEWS: Document the changes. * doc/rush.rc.5: Likewise. * doc/rush.texi: Likewise.
-rw-r--r--NEWS23
-rw-r--r--doc/rush.rc.510
-rw-r--r--doc/rush.texi13
-rw-r--r--src/config.c11
-rw-r--r--src/rush.c355
-rw-r--r--tests/env.at17
6 files changed, 276 insertions, 153 deletions
diff --git a/NEWS b/NEWS
index 3c1118f..e8ea77a 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-GNU Rush NEWS -- history of user-visible changes. 2016-08-17
+GNU Rush NEWS -- history of user-visible changes. 2016-08-20
Copyright (C) 2008-2011, 2014, 2016 Sergey Poznyakoff
See the end of file for copying conditions.
@@ -6,12 +6,18 @@ Please send bug reports to <bug-rush@gnu.org.ua>
Version 1.7.91 (Git)
-* Select the most suitable time representation for the duration field,
+* rushlast and rushwho
+
+Select the most suitable time representation for the duration field,
depending on the requested width.
-* If chroot is requested, re-read the password database after chrooting.
+* chroot handling
+
+If chroot is requested, re-read the password database after chrooting.
-* Set supplementary groups when switching to user privileges.
+* Supplementary user groups
+
+Set supplementary groups when switching to user privileges.
* Change provisions for interactive shell usage
@@ -19,6 +25,15 @@ Interactive rules are marked with the keyword "interactive". Only
such rules are considered when rush is invoked without the -c option.
The support of the old (global) "interactive" keyword is discontinued.
+* The env statement
+
+The env statement can contain references to the unmodified environment
+variables. E.g. this is now valid:
+
+ env PATH=/sbin:$PATH
+
+* Testsuite is provided
+
* Minor fix in TXPMUX code.
* Fix CVE-2013-6889
diff --git a/doc/rush.rc.5 b/doc/rush.rc.5
index 4f7968b..54792b9 100644
--- a/doc/rush.rc.5
+++ b/doc/rush.rc.5
@@ -13,7 +13,7 @@
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with GNU Rush. If not, see <http://www.gnu.org/licenses/>.
-.TH RUSH.RC 1 "August 19, 2016" "RUSH.RC" "Rush User Reference"
+.TH RUSH.RC 1 "August 20, 2016" "RUSH.RC" "Rush User Reference"
.SH NAME
rush.rc \- configuration rules for rush.
.SH DESCRIPTION
@@ -429,8 +429,12 @@ The use of the \fBL\fR resource automatically enables forked mode.
.TP
\fIenv\fR \fIARG\fR...
Modifies the execution environment. Arguments are a list of
-specifiers separated by any amount of whitespace. The following
-specifiers are allowed:
+specifiers separated by any amount of whitespace. Each
+specifier can contain references to variables from the inherited
+environment. The reference syntax is the same as in
+.BR sh (1).
+
+The following specifiers are allowed:
.RS
.TP
\fI\-\fR (a dash)
diff --git a/doc/rush.texi b/doc/rush.texi
index 1eb09a1..494648b 100644
--- a/doc/rush.texi
+++ b/doc/rush.texi
@@ -1105,8 +1105,11 @@ program will be executed.
Modify the environment.
@end deffn
-Its argument is a whitespace-delimited list of specifiers. The
-following specifiers are understood:
+Its arguments are a whitespace-delimited list of specifiers. Each
+specifier can contain references to variables from the inherited
+environment. The reference syntax is the same as in Bourne shell.
+
+The following specifiers are understood:
@table @asis
@item - (a dash)
@@ -1141,6 +1144,12 @@ In this example, if @env{PATH} exists, @samp{:/sbin} will be appended
to it. Otherwise, it will be created and @samp{/sbin} will be
assigned to it.
+This is roughly equivalent to
+
+@smallexample
+PATH=$PATH:/sbin
+@end smallexample
+
@item @var{name}=+@var{value}
Retain variable @var{name} and prepend @var{value} to its value. If
no such variable is present in the environment, it is created and
diff --git a/src/config.c b/src/config.c
index 8793342..233bde9 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1416,6 +1416,7 @@ _parse_newgroup(input_buf_ptr ibuf, struct rush_rule *rule,
#define TOK_NEWBUF 0x10 /* Token may create new input buffer */
#define TOK_CRT 0x24 /* Index after the token may contain ^ */
#define TOK_SED 0x42 /* Arguments contain sed-exprs */
+#define TOK_ENV 0x80 /* Expand environment variables */
#define TOK_DFL TOK_ARG|TOK_RUL
#define TOK_DFLN TOK_ARGN|TOK_RUL
@@ -1449,7 +1450,7 @@ struct token toktab[] = {
{ KW("chroot"), TOK_DFL, _parse_chroot },
{ KW("limits"), TOK_DFL, _parse_limits },
{ KW("chdir"), TOK_DFL, _parse_chdir },
- { KW("env"), TOK_DFLN, _parse_env },
+ { KW("env"), TOK_DFLN|TOK_ENV, _parse_env },
{ KW("fork"), TOK_DFL, _parse_fork },
{ KW("acct"), TOK_DFL, _parse_acct },
{ KW("post-socket"), TOK_DFL, _parse_post_socket },
@@ -1577,7 +1578,7 @@ parse_input_buf(input_buf_ptr ibuf)
}
}
- if (tok->flags & (TOK_ARG || TOK_ARGN) && !(val && *val)) {
+ if (tok->flags & (TOK_ARG | TOK_ARGN) && !(val && *val)) {
logmsg(LOG_NOTICE,
_("%s:%d: invalid statement: missing value"),
ibuf->file, ibuf->line);
@@ -1592,6 +1593,12 @@ parse_input_buf(input_buf_ptr ibuf)
if (tok->flags & TOK_SED)
flags |= WRDSF_SED_EXPR;
+ if (tok->flags & TOK_ENV) {
+ flags &= ~WRDSF_NOVAR;
+ flags |= WRDSF_ENV;
+ ws.ws_env = (const char **) environ;
+ }
+
ws.ws_comment = "#";
if (wordsplit(val, &ws, flags)) {
logmsg(LOG_NOTICE,
diff --git a/src/rush.c b/src/rush.c
index 72b1cd2..17ddb3b 100644
--- a/src/rush.c
+++ b/src/rush.c
@@ -348,11 +348,16 @@ expand_tilde(const char *dir, const char *home)
res = xstrdup(dir);
return res;
}
-
+
+/* Find variable NAME in environment ENV.
+ On success, store the index of the ENV slot in *IDX,
+ the offset of the value (position right past '=') in *VALOFF, and
+ return 0 (IDX and/or VALOFF can be NULL, if that info is not needed).
+ Return -1 if NAME was not found. */
static int
find_env_pos(char **env, char *name, int *idx, int *valoff)
{
- int nlen = strcspn(name, "?+=");
+ int nlen = strcspn(name, "+=");
int i;
for (i = 0; env[i]; i++) {
@@ -368,6 +373,10 @@ find_env_pos(char **env, char *name, int *idx, int *valoff)
return -1;
}
+/* Find variable NAME in environment ENV.
+ On success, return pointer to the variable assignment (if VAL is 0),
+ or to the value (if VAL is 1).
+ Return NULL if NAME is not present in ENV. */
static char *
find_env_ptr(char **env, char *name, int val)
{
@@ -377,8 +386,9 @@ find_env_ptr(char **env, char *name, int val)
return val ? env[i] + j : env[i];
}
+/* Return 1 if ENV contains a matching unset statement for variable NAME. */
static int
-locate_unset(char **env, const char *name)
+var_is_unset(char **env, const char *name)
{
volatile int i;
int nlen = strcspn(name, "=");
@@ -433,7 +443,7 @@ env_setup(char **env)
{
char **old_env = environ;
char **new_env;
- int count, i, n;
+ int count, i, j, n;
if (!env)
return old_env;
@@ -460,24 +470,26 @@ env_setup(char **env)
if (old_env)
for (i = 0; old_env[i]; i++) {
- if (!locate_unset(env, old_env[i]))
+ if (!var_is_unset(env, old_env[i]))
new_env[n++] = old_env[i];
}
for (i = 0; env[i]; i++) {
char *p;
- if (env[i][0] == '-') {
+ if (env[i][0] == '-')
/* Skip unset directives. */
continue;
- } if ((p = strchr(env[i], '='))) {
+
+ /* Find the slot for the variable. Use next available
+ slot if there's no such variable in new_env */
+ if (find_env_pos(new_env, env[i], &j, NULL))
+ j = n;
+
+ if ((p = strchr(env[i], '='))) {
if (p == env[i])
continue; /* Ignore erroneous entry */
if (p[-1] == '+') {
- int j;
-
- if (find_env_pos(new_env, env[i], &j, NULL))
- j = n++;
new_env[j] = env_concat(env[i],
p - env[i] - 1,
find_env_ptr(environ,
@@ -485,31 +497,26 @@ env_setup(char **env)
1),
p + 1);
} else if (p[1] == '+') {
- int j;
-
- if (find_env_pos(new_env, env[i], &j, NULL))
- j = n++;
new_env[j] = env_concat(env[i],
p - env[i],
p + 2,
find_env_ptr(environ,
env[i],
1));
- } else if (p[-1] == '?') {
- if (find_env_pos(environ, env[i], NULL, NULL))
- new_env[n++] = p + 1;
} else
- new_env[n++] = env[i];
- } else {
- p = find_env_ptr(environ, env[i], 0);
- if (p)
- new_env[n++] = p;
- }
+ new_env[j] = env[i];
+ } else if ((p = find_env_ptr(environ, env[i], 0)))
+ new_env[j] = p;
+ else
+ continue;
+ /* Adjust environment size */
+ if (j == n)
+ ++n;
}
new_env[n] = NULL;
return new_env;
}
-
+
void
reparse_cmdline(struct rush_request *req)
{
@@ -553,134 +560,198 @@ assign_string(char **pstr, char *val)
free(*pstr);
*pstr = val;
}
+
+static int
+transform_cmdline_fun(struct rush_request *req, struct transform_node *node,
+ char *val, char **return_val)
+{
+ char *p;
+
+ debug(2, "%s", _("Transforming command line"));
+ if (node->pattern) {
+ char *val = rush_expand_string(node->pattern, req);
+ p = transform_string(node->v.trans, val);
+ free(val);
+ } else
+ p = transform_string(node->v.trans, req->cmdline);
+ assign_string(&req->cmdline, p);
+ debug(2, _("Command line: %s"), req->cmdline);
+ return 0;
+}
+
+int
+transform_setcmd_fun(struct rush_request *req, struct transform_node *node,
+ char *val, char **return_val)
+{
+ debug(2, "%s", _("Setting command line"));
+ assign_string(&req->cmdline, xstrdup(val));
+ debug(2, _("Command line: %s"), req->cmdline);
+ return 0;
+}
+
+int
+transform_arg_fun(struct rush_request *req, struct transform_node *node,
+ char *val, char **return_val)
+{
+ char *p = transform_string(node->v.trans, val);
+ assign_string(return_val, p);
+ return 1;
+}
+
+int
+transform_map_fun(struct rush_request *req, struct transform_node *node,
+ char *val, char **return_val)
+{
+ char *p;
+ debug(2,
+ _("Transformation map: %s, %s, %s, %u, %u, %s"),
+ node->v.map.file,
+ node->v.map.delim,
+ node->v.map.key,
+ node->v.map.key_field,
+ node->v.map.val_field,
+ node->v.map.defval);
+ p = map_string(&node->v.map, req);
+ if (p) {
+ assign_string(return_val, p);
+ return 1;
+ }
+ return 0;
+}
+
+int
+transform_delarg_fun(struct rush_request *req, struct transform_node *node,
+ char *val, char **return_val)
+{
+ int i, arg_no, arg_end;
+
+ arg_no = get_arg_no(node->arg_no, req);
+ arg_end = get_arg_no(node->v.arg_end, req);
+ if (arg_end < arg_no) {
+ int x = arg_end;
+ arg_end = arg_no;
+ arg_no = x;
+ }
+ debug(2, _("Deleting arguments %d-%d"), arg_no, arg_end);
+ if (arg_no == 0 || arg_end == 0)
+ die(config_error,
+ &req->i18n, _("Deleting argv[0] is prohibited"));
+ for (i = arg_no; i <= arg_end; i++)
+ free(req->argv[i]);
+ i = arg_end - arg_no + 1;
+ memmove(req->argv + arg_no,
+ req->argv + arg_end + 1,
+ (req->argc - i) * sizeof(req->argv[0]));
+ req->argc -= i;
+ return 1;
+}
+
+int
+transform_setarg_fun(struct rush_request *req, struct transform_node *node,
+ char *val, char **return_val)
+{
+ assign_string(return_val, xstrdup(val));
+ return 1;
+}
+
+
+/* Transform flags */
+#define XFORM_DFL 0x00 /* Default: nothing */
+#define XFORM_CMDLINE 0x01 /* Function operates on entire command line */
+#define XFORM_VALUE 0x02 /* Function needs value */
+#define XFORM_CHARGV 0x04 /* Function can change argv */
+
+struct transform_function
+{
+ int flags;
+ int (*func)(struct rush_request *req, struct transform_node *node,
+ char *val, char **return_val);
+};
+
+static struct transform_function transform_funtab[] = {
+ [transform_cmdline] = {
+ XFORM_CMDLINE,
+ transform_cmdline_fun
+ },
+ [transform_setcmd] = {
+ XFORM_CMDLINE|XFORM_VALUE,
+ transform_setcmd_fun
+ },
+ [transform_arg] = {
+ XFORM_VALUE,
+ transform_arg_fun
+ },
+ [transform_map] = {
+ XFORM_VALUE,
+ transform_map_fun
+ },
+ [transform_delarg] = {
+ XFORM_CHARGV,
+ transform_delarg_fun
+ },
+ [transform_setarg] = {
+ XFORM_VALUE,
+ transform_setarg_fun
+ }
+};
+static int transform_count =
+ sizeof(transform_funtab)/sizeof(transform_funtab[0]);
void
run_transforms(struct rush_rule *rule, struct rush_request *req)
{
struct transform_node *node;
- char *p;
- int i, arg_no, arg_end;
- int args_transformed = 0;
char *val, **target;
char *mem = NULL;
+ int args_transformed = 0;
+ int res;
+ int flags;
-#define GET_TGT_VAL() \
- if (node->progmod) { \
- arg_no = 0; \
- target = &req->prog; \
- val = PROGFILE(req); \
- debug(2, _("Modifying program name (%s)"), val); \
- } else { \
- arg_no = get_arg_no(node->arg_no, req); \
- target = &req->argv[arg_no]; \
- val = *target; \
- args_transformed = 1; \
- debug(2, _("Modifying argv[%d]"), arg_no); \
- } \
- if (node->pattern) { \
- val = mem = rush_expand_string(node->pattern, req); \
- } else { \
- mem = NULL; \
- }
-#define FREE_VAL() do { \
- if (mem) { \
- free(mem); \
- mem = NULL; \
- } \
- } while (0)
-
-
for (node = rule->transform_head; node; node = node->next) {
- switch (node->type) {
- case transform_cmdline:
- if (args_transformed) {
- rebuild_cmdline(req);
- args_transformed = 0;
- }
- debug(2, "%s", _("Transforming command line"));
- if (node->pattern) {
- val = rush_expand_string(node->pattern, req);
- p = transform_string(node->v.trans, val);
- free(val);
- } else
- p = transform_string(node->v.trans,
- req->cmdline);
- free(req->cmdline);
- req->cmdline = p;
- debug(2, _("Command line: %s"), req->cmdline);
- reparse_cmdline(req);
- break;
-
- case transform_setcmd:
- GET_TGT_VAL();
- if (args_transformed) {
- rebuild_cmdline(req);
- args_transformed = 0;
- }
- debug(2, "%s", _("Setting command line"));
- free(req->cmdline);
- req->cmdline = xstrdup(val);
- debug(2, _("Command line: %s"), req->cmdline);
- reparse_cmdline(req);
- FREE_VAL();
- break;
-
- case transform_arg:
- GET_TGT_VAL();
- p = transform_string(node->v.trans, val);
- assign_string(target, p);
- FREE_VAL();
- break;
-
- case transform_map:
- GET_TGT_VAL();
- debug(2,
- _("Transformation map: %s, %s, %s, %u, %u, %s"),
- node->v.map.file,
- node->v.map.delim,
- node->v.map.key,
- node->v.map.key_field,
- node->v.map.val_field,
- node->v.map.defval);
- p = map_string(&node->v.map, req);
- if (p)
- assign_string(target, p);
- else
- args_transformed = 0;
- FREE_VAL();
- break;
+ if (node->type < 0 || node->type >= transform_count)
+ die(system_error, &req->i18n,
+ _("%s:%d: internal error"), __FILE__, __LINE__);
- case transform_delarg:
- arg_no = get_arg_no(node->arg_no, req);
- arg_end = get_arg_no(node->v.arg_end, req);
- if (arg_end < arg_no) {
- int x = arg_end;
- arg_end = arg_no;
- arg_no = x;
+ val = NULL;
+ target = NULL;
+ flags = transform_funtab[node->type].flags;
+
+ if ((flags & XFORM_CMDLINE) && args_transformed) {
+ rebuild_cmdline(req);
+ args_transformed = 0;
+ }
+
+ if (flags & XFORM_VALUE) {
+ if (node->progmod) {
+ target = &req->prog;
+ val = PROGFILE(req);
+ debug(2, _("Modifying program name (%s)"), val);
+ flags &= ~XFORM_CHARGV;
+ } else {
+ int arg_no = get_arg_no(node->arg_no, req);
+ target = &req->argv[arg_no];
+ val = *target;
+ flags |= XFORM_CHARGV;
+ debug(2, _("Modifying argv[%d]"), arg_no);
}
- debug(2, _("Deleting arguments %d-%d"),
- arg_no, arg_end);
- if (arg_no == 0 || arg_end == 0)
- die(config_error,
- &req->i18n, _("Deleting argv[0] is prohibited"));
- for (i = arg_no; i <= arg_end; i++)
- free(req->argv[i]);
- i = arg_end - arg_no + 1;
- memmove(req->argv + arg_no,
- req->argv + arg_end + 1,
- (req->argc - i)
- * sizeof(req->argv[0]));
- req->argc -= i;
- args_transformed = 1;
- break;
-
- case transform_setarg:
- GET_TGT_VAL();
- assign_string(target, xstrdup(val));
- FREE_VAL();
- break;
- }
- }
+ if (node->pattern) {
+ mem = rush_expand_string(node->pattern, req);
+ val = mem;
+ } else {
+ mem = NULL;
+ }
+ }
+ res = transform_funtab[node->type].func(req, node,
+ val, target);
+ if (flags & XFORM_CHARGV)
+ args_transformed = res;
+ if (flags & XFORM_CMDLINE)
+ reparse_cmdline(req);
+ if (mem) {
+ free(mem);
+ mem = NULL;
+ }
+ }
if (args_transformed)
rebuild_cmdline(req);
diff --git a/tests/env.at b/tests/env.at
index 9ae91ad..a410bbc 100644
--- a/tests/env.at
+++ b/tests/env.at
@@ -188,4 +188,21 @@ rule
[])
m4_popdef([RUSH_ENVIRON])
+m4_pushdef([RUSH_ENVIRON],[-i MYPATH=/bin:/usr/bin])
+AT_RUSH_TEST([
+rule
+ env MYPATH=/usr/local/bin:\$MYPATH
+],
+[environ],
+[command],
+[0],
+[{
+ "environ":[[
+ "MYPATH=/usr/local/bin:/bin:/usr/bin"
+ ]]
+}
+],
+[])
+m4_popdef([RUSH_ENVIRON])
+
AT_CLEANUP

Return to:

Send suggestions and report system problems to the System administrator.