From f50a208f9df348cede2ba50b4f435351d8d3f19e Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Mon, 3 Jun 2019 13:17:13 +0300 Subject: Finish the env re-implementation * NEWS: Document the "env" statement and the PIES_MASTER_PID environment variable. Version 1.3.91 * configure.ac: Version 1.3.91 * doc/pies.texi: Document the new "env" statement syntax. Provide instructions on how to convert legacy "env" statement to the new form. * lib/envop.c (environ_unset): Take reference value as argument. If supplied, unset the variable only if its value matches the reference one. * lib/envop.h (environ_unset): Change proto. * src/pies.c (parse_legacy_env): Minor changes. (_cb_env_unset): Allow to specify value. * src/progman.c (run_command): Define PIES_MASTER_PID. * tests/env.at: Check the legacy env syntax. * tests/envop.at: Additional checks. --- NEWS | 46 ++++++++++++- configure.ac | 2 +- doc/pies.texi | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- lib/envop.c | 14 ++-- lib/envop.h | 2 +- src/pies.c | 10 ++- src/progman.c | 7 +- tests/env.at | 72 +++++++++++++++++++- tests/envop.at | 23 +++++++ 9 files changed, 361 insertions(+), 27 deletions(-) diff --git a/NEWS b/NEWS index f34a6f0..db79097 100644 --- a/NEWS +++ b/NEWS @@ -1,10 +1,10 @@ -GNU Pies NEWS -- history of user-visible changes. 2019-05-31 +GNU Pies NEWS -- history of user-visible changes. 2019-06-03 See the end of file for copying conditions. Please send Pies bug reports to or -Version 1.3.90 (git) +Version 1.3.91 (git) * New option --no-init @@ -38,7 +38,41 @@ contains redirections, pipes, etc. E.g. * Improved cyclic dependency diagnostics -* Fix a bug in 'env' statement +* New 'env' statement + +The 'env' statement has been re-implemented as a compound statement. +It can contain the following sub-statements: + +** clear yes +Clears the environment + +** keep NAME +Keeps the variable NAME when clearing the environment. Implies +"clear yes". NAME can be a globbing pattern, in which case all +variables matching the pattern are retained. + +** set "NAME=VALUE" +Sets the environment variable for the component. VALUE is subject +to variable expansion. + +** unset NAME +Unsets the variable. NAME can be a globbing pattern, in which case all +variables matching the pattern are unset. + +Example: + + env { + clear yes + keep PATH + keep MANPATH + keep "LC_*" + set "MANPATH=$MANPATH${MANPATH:+:}/usr/local/man" + } + +* Legacy 'env' statement. + +Support for the old one-line syntax of "env" is retained for +backward compatibility. Previous versions applied unnecessary word splitting if given a single argument. This is now fixed, so that e.g. the following @@ -47,6 +81,12 @@ X to have the value "foo bar": env "X=foo bar" +* New environment variable available in commands started from return-code + +Programs started via "exec" statement in the "return-code" block +obtain the PID of the master pies process in environment variable +PIES_MASTER_PID. + Version 1.3, 2016-10-01 diff --git a/configure.ac b/configure.ac index a3c0b34..ccfd9fc 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ # along with GNU Pies. If not, see . AC_PREREQ([2.63]) -AC_INIT([GNU Pies], [1.3.90], [bug-pies@gnu.org.ua]) +AC_INIT([GNU Pies], [1.3.91], [bug-pies@gnu.org.ua]) AC_CONFIG_SRCDIR([src/pies.h]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_HEADERS([config.h]) diff --git a/doc/pies.texi b/doc/pies.texi index b77a40c..ccb9f0e 100644 --- a/doc/pies.texi +++ b/doc/pies.texi @@ -122,6 +122,7 @@ Component Statement * Prerequisites:: * Component Privileges:: * Resources:: +* Environment:: * Actions Before Startup:: * Exit Actions:: * Output Redirectors:: @@ -712,7 +713,7 @@ compatibility with the @sc{c} preprocessor. @end table @node Component Statement -@section Component Statement +@section The @code{component} Statement @kwindex component @deffn {Config} component @@ -937,6 +938,7 @@ substatements. * Prerequisites:: * Component Privileges:: * Resources:: +* Environment:: * Actions Before Startup:: * Exit Actions:: * Output Redirectors:: @@ -1038,7 +1040,122 @@ Set the umask. The @var{number} must be an octal value not greater than @samp{777}. The default umask is inherited at startup. @end deffn -@deffn {Config: component} env @var{args} +@deffn {Config: component} max-instances @var{n} +Sets the maximum number of simultaneously running instances of this +component. +@end deffn + +@node Environment +@subsection Environment +Normally all components inherit the environment of the master +@command{pies} process. You can modify this environment using +the @code{env} statement. It has two variants: @dfn{compound} and @dfn{legacy}. +The legacy one-line statement was used in @command{pies} versions +up to 1.3 and is still retained for backward compatibility. It is +described in @ref{env legacy syntax}. This subsection describes the +modern compount syntax. + +@deffn {Config: component} env @{ ... @} +The compound @code{env} statement has the following syntax: + +@example +@group +env @{ + clear @var{bool}; + keep @var{pattern}; + set "@var{name}=@var{value}"; + unset @var{pattern}; +@} +@end group +@end example +@end deffn + +Statements inside the @code{env} block define operations that +modify the environment. The @code{clear} and @code{keep} statements +are executed first. Then, the @code{set} and @code{unset} statements +are applied in the order of their appearance in the configuration. + +@deffn {env} clear @var{bool} +If @var{bool} is @samp{yes}, all environment variables will be cleared +(unset). The resulting environment will be empty, unless one or more +@code{keep} statements are also given (see below). The @code{clear} +statement is always executed first. +@end deffn + +@deffn {env} keep @var{pattern} +Declares variables matching @var{pattern} (a globbing pattern) as +exempt from clearing. This statement implies @code{clear}. + +For example, the following configuration fragment removes from the +environment all variables except @samp{HOME}, @samp{USER}, +@samp{PATH}, and variables beginning with @samp{LC_}: + +@example +@group +env @{ + clear yes; + keep HOME; + keep USER; + keep PATH; + keep "LC_*"; +@} +@end group +@end example +@end deffn + +@deffn {env} keep "@var{name}=@var{value}" +Retains the variable @var{name}, if it has the given value. Note, that +the argument must be quoted. +@end deffn + +@deffn {env} set "@var{name}=@var{value}" +Assigns @var{value} to environment variable @var{name}. The value is +subject to @dfn{variable expansion} using the same syntax as in shell. +The @code{set} and @code{unset} (see below) statements are executed in +order of their appearance. For example + +@example +@group +env @{ + set "MYLIB=$HOME/lib"; + set "LD_LIBRARY_PATH=$LD_LIBRARY_PATH$@{LD_LIBRARY_PATH:+:@}$MYLIB"; +@} +@end group +@end example +@end deffn + +@deffn {env} unset @var{pattern} +Unset environment variables matching @var{pattern}. The following +will unset the @env{LOGIN} variable: + +@example +unset LOGIN; +@end example + +@noindent +The following will unset all variables starting with @samp{LD_}: + +@example +unset "LD_*"; +@end example + +@noindent +Notice, that patterns containing wildcard characters must be quoted. +@end deffn + +@menu +* env legacy syntax:: +@end menu + +@node env legacy syntax +@subsubsection @code{env}: legacy syntax. +Up to version 1.3 @command{pies} implemented the one-line variant of +the @code{env} statement. The use of this legacy syntax is +discouraged. It is supported for backward compatibility only and will +be removed in future versions. This subsection describes the legacy +syntax. + +@deffn {legacy syntax} env @var{args} Set program environment. Arguments are a whitespace-delimited list of specifiers. The @@ -1049,17 +1166,63 @@ following specifiers are understood: Clear the environment. This is understood only when used as a first word in @var{args}. +The modern syntax equivalent is: + +@example +@group +env @{ + clear yes; +@} +@end group +@end example + @item -@var{name} -Unset the environment variable @var{name}. +Unset the environment variable @var{name}. The modern syntax +equivalent is + +@example +@group +env @{ + unset @var{name}; +@} +@end group +@end example @item -@var{name}=@var{val} Unset the environment variable @var{name} only if its value is @var{val}. +The modern syntax equivalent is: + +@example +@group +env @{ + unset "@var{name}=@var{val}"; +@} +@end group +@end example @item @var{name} -Retain the environment variable @var{name}. +Retain the environment variable @var{name}. The modern syntax +equivalent is + +@example +@group +env @{ + keep @var{name}; +@} +@end group +@end example @item @var{name}=@var{value} -Define environment variable @var{name} to have given @var{value}. +Define environment variable @var{name} to have given @var{value}. The +modern syntax equivalent is: + +@example +@group +env @{ + keep "@var{name}=@var{value}"; +@} +@end group +@end example @item @var{name}+=@var{value} Retain variable @var{name} and append @var{value} to its existing @@ -1077,18 +1240,42 @@ 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. +In modern syntax, use shell variable references, e.g.: + +@example +@group +env @{ + set "PATH=$@{PATH@}$@{PATH:+:@}/sbin"; +@} +@end group +@end example + @item @var{name}=+@var{value} Retain variable @var{name} and prepend @var{value} to its existing value. If no such variable is present in the environment, it is created and @var{value} is assigned to it. However, if @var{value} ends with a punctuation character, this character is removed from it -before assignment. -@end table -@end deffn +before assignment. -@deffn {Config: component} max-instances @var{n} -Sets the maximum number of simultaneously running instances of this -component. +In modern syntax, use shell variable references, e.g. instead of +doing + +@example +env PATH=+/sbin: +@end example + +@noindent +use + +@example +@group +env @{ + set "PATH=/sbin$@{PATH:+:@}$PATH"; +@} +@end group +@end example + +@end table @end deffn @node Actions Before Startup @@ -1186,6 +1373,9 @@ additional variables: @item PIES_VERSION The @command{pies} version number (@value{VERSION}). +@item PIES_MASTER_PID +PID of the master @command{pies} process. + @item PIES_COMPONENT Tag of the terminated component (@pxref{Component Statement, tag}). diff --git a/lib/envop.c b/lib/envop.c index 79083f7..80a8b6c 100644 --- a/lib/envop.c +++ b/lib/envop.c @@ -173,7 +173,7 @@ environ_set (environ_t *env, char const *name, char const *value) return -1; } if (!value) - return environ_unset (env, name); + return environ_unset (env, name, value); ws.ws_env = (char const **) env->env_base; if (wordsplit (value, &ws, @@ -218,18 +218,21 @@ environ_set (environ_t *env, char const *name, char const *value) } int -environ_unset (environ_t *env, char const *name) +environ_unset (environ_t *env, char const *name, char const *refval) { ssize_t n; + char *val; if (!env || !name) { errno = EINVAL; return -1; } - n = getenvind (env, name, NULL); + n = getenvind (env, name, &val); if (n == -1) return ENOENT; + if (refval && strcmp (val, refval)) + return ENOENT; free (env->env_base[n]); memmove (env->env_base + n, env->env_base + n + 1, @@ -457,7 +460,10 @@ envop_exec (struct envop_entry *op, environ_t *env) break; case envop_unset: - environ_unset_glob (env, op->name); + if (op->value) + environ_unset (env, op->name, op->value); + else + environ_unset_glob (env, op->name); break; case envop_keep: diff --git a/lib/envop.h b/lib/envop.h index 054152e..82be3a5 100644 --- a/lib/envop.h +++ b/lib/envop.h @@ -27,7 +27,7 @@ environ_t *environ_create (char **); void environ_free (environ_t *env); int environ_add (environ_t *env, char const *def); int environ_set (environ_t *env, char const *name, char const *val); -int environ_unset (environ_t *env, char const *name); +int environ_unset (environ_t *env, char const *name, char const *val); int environ_unset_glob (environ_t *env, const char *pattern); static inline char ** environ_ptr (environ_t *env) diff --git a/src/pies.c b/src/pies.c index 2387cef..fe8d9b4 100644 --- a/src/pies.c +++ b/src/pies.c @@ -613,7 +613,7 @@ parse_legacy_env (char **argv, envop_t **envop) { msize = 2*len + 9 + vlen + 1; mem = grecs_malloc (msize); - snprintf (mem, msize, "${%s:-}${%s+%c}%s", + snprintf (mem, msize, "${%s:-}${%s:+%c}%s", name, name, value[0], value + 1); } else @@ -635,7 +635,7 @@ parse_legacy_env (char **argv, envop_t **envop) msize = 2*len + 10 + vlen + 1; mem = grecs_malloc (msize); - snprintf (mem, msize, "%s${%s+%c}${%s:-}", + snprintf (mem, msize, "%s${%s:+%c}${%s:-}", value, name, c, name); } else @@ -785,11 +785,15 @@ _cb_env_unset (enum grecs_callback_command cmd, void *varptr, grecs_value_t *value, void *cb_data) { struct component *comp = varptr; + char *p; if (assert_scalar_stmt (locus, cmd) || assert_grecs_value_type (locus, value, GRECS_TYPE_STRING)) return 1; - if (envop_entry_add (&comp->envop, envop_unset, value->v.string, NULL)) + p = strchr (value->v.string, '='); + if (p) + *p++ = 0; + if (envop_entry_add (&comp->envop, envop_unset, value->v.string, p)) grecs_error (locus, errno, "envop_entry_add"); return 0; } diff --git a/src/progman.c b/src/progman.c index b75a2db..a6dc418 100644 --- a/src/progman.c +++ b/src/progman.c @@ -821,7 +821,7 @@ prog_start_prologue (struct prog *prog) { size_t i; for (i = 0; sockenv_var[i]; i++) - environ_unset (prog->v.p.env, sockenv_var[i]); + environ_unset (prog->v.p.env, sockenv_var[i], NULL); } envop_exec (prog->v.p.comp->envop, prog->v.p.env); if (init_process) @@ -1978,10 +1978,12 @@ static void run_command (struct action *act, struct prog *prog, unsigned retcode, pid_t child_pid) { - pid_t pid; + pid_t pid, master_pid; char *argv[4]; char buf[INT_BUFSIZE_BOUND (uintmax_t)]; + master_pid = getpid (); + /* FIXME: optionally set output redirectors for this command? */ pid = fork (); @@ -1998,6 +2000,7 @@ run_command (struct action *act, struct prog *prog, unsigned retcode, setsid (); setenv ("PIES_VERSION", PACKAGE_VERSION, 1); setenv ("PIES_COMPONENT", prog_tag (prog), 1); + setenv ("PIES_MASTER_PID", umaxtostr (master_pid, buf), 1); setenv ("PIES_PID", umaxtostr (child_pid, buf), 1); if (retcode & STATUS_SIG_BIT) setenv ("PIES_SIGNAL", umaxtostr (STATUS_CODE (retcode), buf), 1); diff --git a/tests/env.at b/tests/env.at index 18b0e52..430bf10 100644 --- a/tests/env.at +++ b/tests/env.at @@ -15,7 +15,7 @@ component envtest { stdout file "$PWD/log"; return-code 0 { action disable; - exec "piesctl --url unix:///$PWD/pies.ctl --no-netrc shutdown"; + exec "$abs_top_builddir/src/piesctl --url unix:///$PWD/pies.ctl --no-netrc shutdown"; } } _EOT @@ -23,7 +23,7 @@ _EOT to 5 \ envtest -exec \ $abs_top_builddir/src/pies --foreground --stderr \ - --config-file control.conf --config-file envtest.conf 2>errlog + --config-file control.conf --config-file envtest.conf --debug 1 2>errlog cat log ], [0], @@ -68,3 +68,71 @@ USER="user" ]) m4_popdef([ENVTEST]) + +# ############################## +# Legacy syntax +# ############################## + +AT_BANNER([env: legacy syntax]) + +m4_pushdef([ENVTEST], +[AT_SETUP([$1]) +AT_KEYWORDS([env $2]) +AT_CHECK([ +PIES_XFAIL_CHECK +PIES_CONTROL_INIT +cat > envtest.conf <<_EOT +component envtest { + env $3; + command "$abs_builddir/envtest -clone"; + chdir $PWD; + stdout file "$PWD/log"; + return-code 0 { + action disable; + exec "$abs_top_builddir/src/piesctl --url unix:///$PWD/pies.ctl --no-netrc shutdown"; + } +} +_EOT + +to 5 \ + envtest -exec \ + $abs_top_builddir/src/pies --foreground --stderr \ + --config-file control.conf --config-file envtest.conf --debug 1 2>errlog +cat log +], +[0], +[$4]) +AT_CLEANUP]) + +ENVTEST([clear],[clear],[-],[]) +ENVTEST([keep],[keep],[- USER LOGIN], +[LOGIN="user" +USER="user" +]) + +ENVTEST([set],[set],["FOO=bar" "BAR=bar"], +[BAR="bar" +FOO="bar" +HOME="/home/user" +LC_ALL="C" +LC_CTYPE="C" +LC_MESSAGES="C" +LC_NUMERIC="C" +LOGIN="user" +PATH="/usr/local/bin:/usr/bin:/bin" +PIES_INSTANCE="pies" +PWD="/home" +USER="user" +]) + +ENVTEST([unset],[unset],["-LC_NUMERIC" "-LC_CTYPE" "-LC_MESSAGES"], +[HOME="/home/user" +LC_ALL="C" +LOGIN="user" +PATH="/usr/local/bin:/usr/bin:/bin" +PIES_INSTANCE="pies" +PWD="/home" +USER="user" +]) + + diff --git a/tests/envop.at b/tests/envop.at index 6f634fb..2b857f5 100644 --- a/tests/envop.at +++ b/tests/envop.at @@ -97,5 +97,28 @@ PWD="/home" USER="user" ]) +ENVTEST([unset with value],[unset],[-unset 'LOGIN=user'], +[HOME="/home/user" +LC_ALL="C" +LC_CTYPE="C" +LC_MESSAGES="C" +LC_NUMERIC="C" +PATH="/usr/local/bin:/usr/bin:/bin" +PWD="/home" +USER="user" +]) + +ENVTEST([unset with value (mismatch)],[unset],[-unset 'LOGIN=another'], +[HOME="/home/user" +LC_ALL="C" +LC_CTYPE="C" +LC_MESSAGES="C" +LC_NUMERIC="C" +LOGIN="user" +PATH="/usr/local/bin:/usr/bin:/bin" +PWD="/home" +USER="user" +]) + m4_popdef([ENVTEST]) -- cgit v1.2.1