diff options
-rw-r--r-- | NEWS | 22 | ||||
-rw-r--r-- | doc/pies.texi | 21 | ||||
-rw-r--r-- | src/comp.c | 24 | ||||
-rw-r--r-- | src/pies.c | 48 | ||||
-rw-r--r-- | src/pies.h | 5 | ||||
-rw-r--r-- | tests/Makefile.am | 1 | ||||
-rwxr-xr-x | tests/aux/respawn | 2 | ||||
-rw-r--r-- | tests/respawn.at | 2 | ||||
-rw-r--r-- | tests/shell.at | 58 | ||||
-rw-r--r-- | tests/testsuite.at | 1 |
10 files changed, 137 insertions, 47 deletions
@@ -1,7 +1,7 @@ -GNU Pies NEWS -- history of user-visible changes. 2019-05-28 +GNU Pies NEWS -- history of user-visible changes. 2019-05-31 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) @@ -22,14 +22,34 @@ all other components have been terminated. Any number of startup or shutdwon components can be defined in the configuration file. Such multiple components are run simultaneously, unless required otherwise by their "prerequisites" and "dependents" statements. +* New component flag: shell + +The 'shell' flag instructs pies to run the component via "/bin/sh -c +$command". Use it if the command should undergo variable expansion, +contains redirections, pipes, etc. E.g. + + component X { + flags shell; + command "if [ -n "$X" ]; then foo; else bar; fi" + } + * Improved cyclic dependency diagnostics +* Fix a bug in 'env' statement + +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" + 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/doc/pies.texi b/doc/pies.texi index 012ac88..1a798e1 100644 --- a/doc/pies.texi +++ b/doc/pies.texi @@ -173,13 +173,17 @@ starts and controls execution of external programs. In this document these programs will be referred to as @dfn{components}. Each component is a stand-alone program, which is executed in the foreground. Upon startup, @command{pies} reads the list of components from its configuration file, starts them, and remains in the -background, controlling their execution. +background, controlling their execution. Each component is +defined by the name of the external program to be run and its +arguments (command line). The program is normally run directly +(via @code{exec}), but you can instruct @command{pies} to run +it via @code{sh -c} as well. The standard output and standard error streams of a component can be redirected to a file or to an arbitrary @command{syslog} channel. The way of handling each component, and in particular the action to be taken upon its termination is determined by the component's @dfn{mode}. @@ -867,12 +871,27 @@ their standard input to be open (e.g.@: @command{pppd nodetach}). @kwindex precious @item precious Mark this component as @dfn{precious}. Precious components are never disabled by @command{pies}, even if they respawn too fast. +@kwindex shell +@item shell +Run command as @code{/bin/sh -c "$command"}. Use this flag if command +contains shell-specific features, such as I/O redirections, pipes, +variables or the like. You can change the shell program using the +@code{program} statement. For example, to use Korn shell: + +@example +component X @{ + flags shell; + program "/bin/ksh"; + command "myprog $HOME"; +@} +@end example + @kwindex wait @item wait This flag is valid only for @samp{inetd} components. It has the same meaning as @samp{wait} in @file{inetd.conf} file, i.e. it tells @command{pies} to wait for the server program to return. @xref{inetd.conf, wait}. @@ -168,12 +168,13 @@ component_free (struct component *comp) { size_t i; component_unlink (comp); free (comp->tag); free (comp->program); + free (comp->command); if (comp->argv) { for (i = 0; i < comp->argc; i++) free (comp->argv[i]); free (comp->argv); } @@ -744,12 +745,35 @@ component_verify (struct component *comp, grecs_locus_t *locus) #undef COMPERR } void component_finish (struct component *comp, grecs_locus_t *locus) { + if (comp->flags & CF_SHELL) + { + comp->argc = 3; + comp->argv = grecs_calloc (comp->argc + 1, sizeof (comp->argv[0])); + comp->argv[0] = grecs_strdup (comp->program ? comp->program : "/bin/sh"); + comp->argv[1] = grecs_strdup ("-c"); + comp->argv[2] = grecs_strdup (comp->command); + comp->argv[3] = NULL; + } + else + { + struct wordsplit ws; + if (wordsplit (comp->command, &ws, WRDSF_DEFFLAGS)) + { + grecs_error (locus, 0, "wordsplit: %s", + wordsplit_strerror (&ws)); + component_free (comp); + return; + } + wordsplit_get_words (&ws, &comp->argc, &comp->argv); + wordsplit_free (&ws); + } + if (comp->prereq) comp->prereq->cmp = list_str_cmp; if (comp->depend) comp->depend->cmp = list_str_cmp; if (comp->privs.groups) comp->privs.groups->cmp = list_str_cmp; @@ -521,43 +521,12 @@ config_array_to_argv (grecs_value_t *val, grecs_locus_t *locus, size_t *pargc) if (pargc) *pargc = argc; return argv; } static int -_cb_command (enum grecs_callback_command cmd, - grecs_locus_t *locus, - void *varptr, grecs_value_t *value, void *cb_data) -{ - struct component *comp = varptr; - struct wordsplit ws; - - switch (value->type) - { - case GRECS_TYPE_STRING: - if (wordsplit (value->v.string, &ws, WRDSF_DEFFLAGS)) - { - grecs_error (locus, 0, "wordsplit: %s", strerror (errno)); - return 1; - } - wordsplit_get_words (&ws, &comp->argc, &comp->argv); - wordsplit_free (&ws); - break; - - case GRECS_TYPE_ARRAY: - comp->argv = config_array_to_argv (value, locus, &comp->argc); - break; - - case GRECS_TYPE_LIST: - grecs_error (locus, 0, _("unexpected list")); - return 1; - } - return 0; -} - -static int _cb_umask (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { mode_t *pmode = varptr; char *p; @@ -586,19 +555,15 @@ _cb_env (enum grecs_callback_command cmd, char ***penv = varptr; struct wordsplit ws; switch (value->type) { case GRECS_TYPE_STRING: - if (wordsplit (value->v.string, &ws, WRDSF_DEFFLAGS)) - { - grecs_error (locus, 0, "wordsplit: %s", strerror (errno)); - return 1; - } - wordsplit_get_words (&ws, &argc, &argv); - wordsplit_free (&ws); + argv = grecs_calloc (2, sizeof (argv[0])); + argv[0] = grecs_strdup (value->v.string); + argv[1] = NULL; break; case GRECS_TYPE_ARRAY: argv = config_array_to_argv (value, locus, NULL); break; @@ -884,12 +849,13 @@ str_to_cf (const char *string, int *flags) { "tcpmuxplus", CF_TCPMUXPLUS }, { "internal", CF_INTERNAL }, { "sockenv", CF_SOCKENV }, { "resolve", CF_RESOLVE }, { "siggroup", CF_SIGGROUP }, { "nullinput", CF_NULLINPUT }, + { "shell", CF_SHELL }, { NULL } }; if (len > 2 && memcmp (string, "no", 2) == 0) { neg++; @@ -1011,15 +977,15 @@ struct grecs_keyword component_keywords[] = { NULL, }, {"command", NULL, N_("Command line."), grecs_type_string, GRECS_DFLT, - NULL, 0, - _cb_command, - }, + NULL, offsetof (struct component, command), + NULL, + }, {"prerequisites", N_("list"), N_("List of prerequisites."), grecs_type_string, GRECS_LIST, NULL, offsetof (struct component, prereq), NULL, }, @@ -203,16 +203,16 @@ enum pies_comp_mode it */ #define CF_INTERNAL 0x020 /* An internal inetd service */ #define CF_SOCKENV 0x040 /* Component wants socket information in the environment */ #define CF_RESOLVE 0x080 /* Resolve IP addresses */ #define CF_SIGGROUP 0x100 /* Send signals to the process group */ - #define CF_NULLINPUT 0x200 /* Provide null input stream */ +#define CF_SHELL 0x400 /* Invoke via sh -c */ -#define CF_REMOVE 0x400 /* Marked for removal */ +#define CF_REMOVE 0xf000 /* Marked for removal */ #define ISCF_TCPMUX(f) ((f) & (CF_TCPMUX | CF_TCPMUXPLUS)) struct prog; struct component @@ -223,12 +223,13 @@ struct component size_t ref_count; /* Reference count. */ struct prog *prog; /* Prog associated with this component. */ enum pies_comp_mode mode; char *tag; /* Entry tag (for diagnostics purposes) */ char *program; /* Program name */ + char *command; /* Full command line */ size_t argc; /* Number of command line arguments */ char **argv; /* Program command line */ char **env; /* Program environment */ char *dir; /* Working directory */ struct grecs_list *prereq; /* Prerequisites */ struct grecs_list *depend; /* Dependency targets */ diff --git a/tests/Makefile.am b/tests/Makefile.am index b404ed4..39899c8 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -49,12 +49,13 @@ TESTSUITE_AT = \ control.at\ cyclic.at\ respawn.at\ redirect.at\ ret-exec.at\ ret-notify.at\ + shell.at\ startup.at\ shutdown.at\ version.at TESTSUITE = $(srcdir)/testsuite M4=m4 diff --git a/tests/aux/respawn b/tests/aux/respawn index 11d59f6..cd9687a 100755 --- a/tests/aux/respawn +++ b/tests/aux/respawn @@ -2,13 +2,13 @@ # usage: respawn [-append] [-stderr FILE] [-stdout FILE] [-sleep TIME] # [-pid FILE] [-tag STRING] [-exit CODE] append=0 unset name -time=10 +time=1 tag=$0 code=0 while [ $# -ne 0 ] do arg=$1 diff --git a/tests/respawn.at b/tests/respawn.at index 4a8e3a7..6b51ca6 100644 --- a/tests/respawn.at +++ b/tests/respawn.at @@ -31,13 +31,13 @@ pies --config-file control.conf --config-file pies.conf n=0 while : do if test -f $comp_pid_file; then lines=`wc -l $comp_pid_file | awk '{print $1}'` - if test "$lines" -ge 3 ; then + if test "$lines" -ge 2 ; then break fi fi sleep 1 n=$(($n + 1)) if test $n -gt 35; then diff --git a/tests/shell.at b/tests/shell.at new file mode 100644 index 0000000..0ef2271 --- /dev/null +++ b/tests/shell.at @@ -0,0 +1,58 @@ +# This file is part of GNU pies testsuite. -*- Autotest -*- +# Copyright (C) 2016-2019 Sergey Poznyakoff +# +# GNU pies is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU pies is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# 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/>. + +AT_SETUP([flags shell]) + +AT_CHECK([ +PIES_XFAIL_CHECK +PIES_CONTROL_INIT +outfile=$PWD/respawn.out +cat > pies.conf <<_EOT +component test { + mode respawn; + flags shell; + command "$auxdir/respawn -tag respawn > $outfile"; +} +_EOT + +pies --config-file control.conf --config-file pies.conf + +n=0 +while : +do + if test -f $outfile; then + lines=`wc -l $outfile | awk '{print $1}'` + if test "$lines" -ge 2 ; then + break + fi + fi + sleep 1 + n=$(($n + 1)) + if test $n -gt 35; then + echo >&2 "timed out" + break + fi +done + +PIES_STOP +head -n3 $outfile +], +[0], +[respawn: start +respawn: stop +]) + +AT_CLEANUP diff --git a/tests/testsuite.at b/tests/testsuite.at index 6775ee7..28d1f9d 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -64,6 +64,7 @@ AT_BANNER([Components]) m4_include([respawn.at]) m4_include([redirect.at]) m4_include([ret-exec.at]) m4_include([ret-notify.at]) m4_include([startup.at]) m4_include([shutdown.at]) +m4_include([shell.at]) |