diff options
-rw-r--r-- | NEWS | 46 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | doc/pies.texi | 212 | ||||
-rw-r--r-- | lib/envop.c | 14 | ||||
-rw-r--r-- | lib/envop.h | 2 | ||||
-rw-r--r-- | src/pies.c | 10 | ||||
-rw-r--r-- | src/progman.c | 7 | ||||
-rw-r--r-- | tests/env.at | 72 | ||||
-rw-r--r-- | tests/envop.at | 23 |
9 files changed, 361 insertions, 27 deletions
@@ -1,13 +1,13 @@ -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 <bug-pies@gnu.org> or <bug-pies@gnu.org.ua> -Version 1.3.90 (git) +Version 1.3.91 (git) * New option --no-init The --no-init option instructs pies not to switch to init mode if its PID is 1. Use this option if you run pies as a process manager in a docker container. @@ -35,21 +35,61 @@ contains redirections, pipes, etc. E.g. flags shell; command "if [ -n "$X" ]; then foo; else bar; fi" } * 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 statement is processed correctly and defines a single variable 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 * SysV-style Init GNU Pies can now be used as init process daemon - the first process diff --git a/configure.ac b/configure.ac index a3c0b34..ccfd9fc 100644 --- a/configure.ac +++ b/configure.ac @@ -12,13 +12,13 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. 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]) AM_INIT_AUTOMAKE([1.11 gnits tar-ustar dist-bzip2 std-options silent-rules]) # Enable silent rules by default: diff --git a/doc/pies.texi b/doc/pies.texi index b77a40c..ccb9f0e 100644 --- a/doc/pies.texi +++ b/doc/pies.texi @@ -119,12 +119,13 @@ Configuration File Syntax Component Statement * Prerequisites:: * Component Privileges:: * Resources:: +* Environment:: * Actions Before Startup:: * Exit Actions:: * Output Redirectors:: * Inetd-Style Components:: * Meta1-Style Components:: * Visibility:: @@ -709,13 +710,13 @@ If the latter is absent, the remembered file name does not change. @item # @var{num} "@var{file}" This is a special form of @code{#line} statement, understood for compatibility with the @sc{c} preprocessor. @end table @node Component Statement -@section Component Statement +@section The @code{component} Statement @kwindex component @deffn {Config} component The @code{component} statement defines a new component: @end deffn @@ -934,12 +935,13 @@ The following subsections describe the rest of @samp{component} substatements. @menu * Prerequisites:: * Component Privileges:: * Resources:: +* Environment:: * Actions Before Startup:: * Exit Actions:: * Output Redirectors:: * Inetd-Style Components:: * Meta1-Style Components:: * Visibility:: @@ -1035,34 +1037,195 @@ in version @value{VERSION}. @deffn {Config: component} umask @var{number} 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 following specifiers are understood: @table @asis @item - (a dash) 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 value. If no such variable is present in the environment, it is created and @var{value} is assigned to it. However, if @var{value} begins with a punctuation character, this character is removed from it @@ -1074,24 +1237,48 @@ PATH+=:/sbin @end example 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 @subsection Actions Before Startup The statements described in this subsection specify actions to perform @@ -1183,12 +1370,15 @@ environment from the main @command{pies} process with the following additional variables: @table @env @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}). @item PIES_PID PID of the terminated component. diff --git a/lib/envop.c b/lib/envop.c index 79083f7..80a8b6c 100644 --- a/lib/envop.c +++ b/lib/envop.c @@ -170,13 +170,13 @@ environ_set (environ_t *env, char const *name, char const *value) if (!name) { errno = EINVAL; 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, WRDSF_NOSPLIT | WRDSF_QUOTE | WRDSF_NOCMD /* FIXME */ @@ -215,24 +215,27 @@ environ_set (environ_t *env, char const *name, char const *value) return -1; } return 0; } 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, (env->env_count - n) * sizeof (env->env_base[0])); env->env_count--; return 0; @@ -454,13 +457,16 @@ envop_exec (struct envop_entry *op, environ_t *env) case envop_set: if (environ_set (env, op->name, op->value)) return -1; 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: break; default: diff --git a/lib/envop.h b/lib/envop.h index 054152e..82be3a5 100644 --- a/lib/envop.h +++ b/lib/envop.h @@ -24,13 +24,13 @@ struct environ typedef struct environ environ_t; 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) { return env->env_base; } @@ -610,13 +610,13 @@ parse_legacy_env (char **argv, envop_t **envop) { name[--len] = 0; if (c_ispunct (value[0])) { 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 { msize = len + vlen + 6; snprintf (mem, msize, "${%s:-}%s", name, value); @@ -632,13 +632,13 @@ parse_legacy_env (char **argv, envop_t **envop) { int c = value[vlen-1]; value[--vlen] = 0; 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 { msize = len + vlen + 6; snprintf (mem, msize, "%s${%s:-}", value, name); @@ -782,17 +782,21 @@ _cb_env_set (enum grecs_callback_command cmd, static int _cb_env_unset (enum grecs_callback_command cmd, grecs_locus_t *locus, 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; } struct grecs_keyword cb_env_keywords[] = { { "clear", diff --git a/src/progman.c b/src/progman.c index b75a2db..a6dc418 100644 --- a/src/progman.c +++ b/src/progman.c @@ -818,13 +818,13 @@ prog_start_prologue (struct prog *prog) prog->v.p.env = environ_create (environ); if (prog->v.p.comp->flags & CF_SOCKENV) { 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) { size_t i; for (i = 0; sysvinit_environ_hint[i]; i++) @@ -1975,16 +1975,18 @@ status_matches_p (struct action *act, unsigned status) } 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 (); if (pid == (pid_t) -1) { logmsg (LOG_ERR, "fork: %s", strerror (errno)); @@ -1995,12 +1997,13 @@ run_command (struct action *act, struct prog *prog, unsigned retcode, { debug (1, (_("executing %s"), act->command)); /* Child */ 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); else setenv ("PIES_STATUS", 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 @@ -12,21 +12,21 @@ component envtest { } command "$abs_builddir/envtest -clone"; chdir $PWD; 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 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], [$4]) AT_CLEANUP]) @@ -65,6 +65,74 @@ LOGIN="user" PATH="/usr/local/bin:/usr/bin:/bin" PIES_INSTANCE="pies" 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 @@ -94,8 +94,31 @@ ENVTEST([unset wildcard],[unset],[-unset 'LC_*'], LOGIN="user" PATH="/usr/local/bin:/usr/bin:/bin" 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]) |