From f5c72b5e74ea7aaf1375f763f977e3249c6b7fc4 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Wed, 12 Jun 2019 11:08:22 +0300 Subject: Check accept components and inet built-in services. * src/comp.c (component_verify,component_finish): Fix check for the presense of the "command" statement. * src/pies.c (component_keywords): Reorder some entries for the consistency of config-help output. * tests/.gitignore: Add new files. * tests/Makefile.am: Add new tests and noinst programs. * tests/accept.at: New test. * tests/builtin.at: New test. * tests/chargen.c: New file. * tests/readtime.c: New file. * tests/recvfd.c: Rewrite for testing both accept and pass-fd components. * tests/passfd.at: Pass -s option to recvfd * tests/testsuite.at: Add new tests. * tests/lines.c: Minor changes. * tests/nt.c: Minor changes. * tests/to.c: Minor changes. --- src/comp.c | 44 ++++++--------- src/pies.c | 14 ++--- tests/.gitignore | 2 + tests/Makefile.am | 7 ++- tests/accept.at | 54 ++++++++++++++++++ tests/builtin.at | 131 +++++++++++++++++++++++++++++++++++++++++++ tests/chargen.c | 109 ++++++++++++++++++++++++++++++++++++ tests/lines.c | 17 ++++++ tests/nt.c | 4 +- tests/passfd.at | 2 +- tests/readtime.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/recvfd.c | 55 ++++++++++++------ tests/testsuite.at | 2 + tests/to.c | 3 +- 14 files changed, 550 insertions(+), 55 deletions(-) create mode 100644 tests/accept.at create mode 100644 tests/builtin.at create mode 100644 tests/chargen.c create mode 100644 tests/readtime.c diff --git a/src/comp.c b/src/comp.c index 7056855..6eeeeed 100644 --- a/src/comp.c +++ b/src/comp.c @@ -595,10 +595,9 @@ component_verify (struct component *comp, grecs_locus_t *locus) "%s", _("\"internal\" used with \"command\"")); } } - else if (!comp->argv) - COMPERR (grecs_error, - "%s", _("missing command line")); - + else if (!comp->command) + COMPERR (grecs_error, "%s", _("no 'command' statement")); + if (ISCF_TCPMUX (comp->flags)) { comp->mode = pies_comp_inetd; @@ -736,9 +735,8 @@ component_verify (struct component *comp, grecs_locus_t *locus) void component_finish (struct component *comp, grecs_locus_t *locus) { - if (!comp->command) + if (component_verify (comp, locus)) { - grecs_error (locus, 0, "%s", _("no 'command' statement")); component_free (comp); return; } @@ -752,7 +750,7 @@ component_finish (struct component *comp, grecs_locus_t *locus) comp->argv[2] = grecs_strdup (comp->command); comp->argv[3] = NULL; } - else + else if (comp->command) { struct wordsplit ws; if (wordsplit (comp->command, &ws, WRDSF_DEFFLAGS)) @@ -773,29 +771,21 @@ component_finish (struct component *comp, grecs_locus_t *locus) if (comp->privs.groups) comp->privs.groups->cmp = list_str_cmp; - if (component_verify (comp, locus)) + if (grecs_list_size (comp->prereq) == 1) { - component_free (comp); - } - else - { - size_t n = grecs_list_size (comp->prereq); - if (n == 1) + const char *item = grecs_list_index (comp->prereq, 0); + if (strcmp (item, "all") == 0) { - const char *item = grecs_list_index (comp->prereq, 0); - if (strcmp (item, "all") == 0) - { - struct component *p; + struct component *p; - grecs_list_clear (comp->prereq); - for (p = comp->prev; p; p = p->prev) - grecs_list_push (comp->prereq, grecs_strdup (comp->tag)); - } - else if (strcmp (item, "none") == 0) - { - grecs_list_free (comp->prereq); - comp->prereq = NULL; - } + grecs_list_clear (comp->prereq); + for (p = comp->prev; p; p = p->prev) + grecs_list_push (comp->prereq, grecs_strdup (comp->tag)); + } + else if (strcmp (item, "none") == 0) + { + grecs_list_free (comp->prereq); + comp->prereq = NULL; } } } diff --git a/src/pies.c b/src/pies.c index 73d33a0..ff36269 100644 --- a/src/pies.c +++ b/src/pies.c @@ -1300,6 +1300,13 @@ struct grecs_keyword component_keywords[] = { grecs_type_string, GRECS_DFLT, NULL, offsetof (struct component, runlevels), _cb_runlevels }, + {"pass-fd-socket", + N_("name"), + N_("Pass fd through this socket."), + grecs_type_string, GRECS_DFLT, + NULL, offsetof (struct component, pass_fd_socket), + NULL, + }, {"pass-fd-timeout", NULL, N_("Time to wait for pass-fd socket to become available."), @@ -1352,13 +1359,6 @@ struct grecs_keyword component_keywords[] = { grecs_type_int, GRECS_DFLT, NULL, offsetof (struct component, socket_type), _cb_socket_type }, - {"pass-fd-socket", - N_("name"), - N_("Pass fd through this socket."), - grecs_type_string, GRECS_DFLT, - NULL, offsetof (struct component, pass_fd_socket), - NULL, - }, {"acl", NULL, N_("Define connection ACL."), diff --git a/tests/.gitignore b/tests/.gitignore index 7d2613a..43d3848 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -9,3 +9,5 @@ testsuite.log to nt recvfd +readtime +chargen diff --git a/tests/Makefile.am b/tests/Makefile.am index 1fe78dc..4631a2c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -49,6 +49,8 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac TESTSUITE_AT = \ testsuite.at\ + accept.at\ + builtin.at\ control.at\ cyclic.at\ env.at\ @@ -68,7 +70,7 @@ TESTSUITE_AT = \ TESTSUITE = $(srcdir)/testsuite M4=m4 -noinst_PROGRAMS = envtest to lines nt recvfd +noinst_PROGRAMS = envtest to lines nt recvfd readtime chargen nt_SOURCES = nt.c iobuf.h AM_CPPFLAGS = \ -I$(top_srcdir)/lib\ @@ -78,7 +80,8 @@ AM_CPPFLAGS = \ LDADD = \ ../lib/libpies.a\ - @GRECS_LDADD@ + @GRECS_LDADD@\ + ../gnu/libgnu.a AUTOTEST = $(AUTOM4TE) --language=autotest $(TESTSUITE): package.m4 $(TESTSUITE_AT) diff --git a/tests/accept.at b/tests/accept.at new file mode 100644 index 0000000..f76a05a --- /dev/null +++ b/tests/accept.at @@ -0,0 +1,54 @@ +# This file is part of GNU pies testsuite. -*- Autotest -*- +# Copyright (C) 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 . +AT_SETUP([accept component]) +AT_CHECK([ +PIES_XFAIL_CHECK +PIES_CONTROL_INIT + +AT_DATA([input], +[now is +the time +stop +]) + +: ${PIES_TEST_INET_SOCKET:=unix://$PWD/in.sock} + +cat > pies.conf <<_EOT +component pfd { + command "recvfd $auxdir/in.test $PWD/inlog"; + mode accept; + socket "$PIES_TEST_INET_SOCKET"; + stderr file "$PWD/log.err"; +} +component controller { + command "nt $PIES_TEST_INET_SOCKET -i input"; +} +_EOT + +set -e +to 10 \ + pies --foreground --stderr \ + --config-file control.conf --config-file pies.conf --debug 1 2>errlog + +cat inlog +cat log.err >&2 +], +[0], +[now is +the time +stop +]) +AT_CLEANUP \ No newline at end of file diff --git a/tests/builtin.at b/tests/builtin.at new file mode 100644 index 0000000..40f65fa --- /dev/null +++ b/tests/builtin.at @@ -0,0 +1,131 @@ +# 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 . +AT_BANNER([inetd built-in services]) + +# IT_BUILTIN([SERVICE],[INPUT],[OUTPUT]) +m4_define([IT_BUILTIN], +[AT_SETUP([$1]) +AT_KEYWORDS([inetd builtin internal $1]) +AT_CHECK([ +PIES_XFAIL_CHECK +PIES_CONTROL_INIT +AT_DATA([input], +[$2]) +m4_if([$1],[qotd],[AT_DATA([qotd],[$3])]) +: ${PIES_TEST_INET_SOCKET:=unix://$PWD/in.sock} +cat > pies.conf <<_EOT +m4_if([$1],[qotd],[qotd-file "$PWD/qotd"; +]) +component in { + mode inetd; + socket "$PIES_TEST_INET_SOCKET"; + flags internal; + service $1; + stderr file "$PWD/log.err"; +} +component controller { + command "nt $PIES_TEST_INET_SOCKET -i input -o output"; + return-code * { + action disable; + exec "piesctl --url '$PIES_CTLSOCK' --no-netrc shutdown"; + } +} +_EOT +set -e +to 10 \ + pies --foreground --stderr \ + --config-file control.conf --config-file pies.conf --debug 1 2>errlog + +cat output m4_if([$1],[qotd],[| tr -d '\r']) +cat log.err >&2 +], +[0], +[$3]) +AT_CLEANUP +]) + +# IT_SPECIAL(SERVICE,COMMAND) +m4_define([IT_SPECIAL], +[AT_SETUP([$1]) +AT_KEYWORDS([inetd builtin internal $1]) +AT_CHECK([ +PIES_XFAIL_CHECK +PIES_CONTROL_INIT +: ${PIES_TEST_INET_SOCKET:=unix://$PWD/in.sock} +cat > pies.conf <<_EOT +component in { + mode inetd; + socket "$PIES_TEST_INET_SOCKET"; + flags internal; + service $1; + stderr file "$PWD/in.err"; +} +component controller { + command "$2 '$PIES_TEST_INET_SOCKET'"; + stderr file "$PWD/controller.err"; + return-code * { + action disable; + exec "echo \$PIES_STATUS > $PWD/status ; piesctl --url '$PIES_CTLSOCK' --no-netrc shutdown"; + } +} +_EOT +set -e +to 10 \ + pies --foreground --stderr \ + --config-file control.conf --config-file pies.conf --debug 1 2>errlog + +if test -s in.err; then + echo >&2 "in.err:" + cat controller.err >&2 +fi +if test -s controller.err; then + echo >&2 "controller.err:" + cat controller.err >&2 +fi +exit `cat status` +], +[0]) +AT_CLEANUP +]) + +# ## +IT_BUILTIN([echo], +[one +two +three +], +[one +two +three +]) + +IT_BUILTIN([discard], +[one +two +three +]) + +IT_SPECIAL([time],[readtime]) +IT_SPECIAL([daytime],[readtime -H]) +IT_SPECIAL([chargen],[chargen]) +IT_BUILTIN([qotd],[], +[A useful debugging and measurement tool is a quote of the day service. +A quote of the day service simply sends a short message without regard +to the input. +]) + +m4_popdef([IT_BUILTIN]) +m4_popdef([IT_SPECIAL]) diff --git a/tests/chargen.c b/tests/chargen.c new file mode 100644 index 0000000..a3b3290 --- /dev/null +++ b/tests/chargen.c @@ -0,0 +1,109 @@ +/* This file is part of GNU Pies testsuite. + Copyright (C) 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 . */ + +#include +#include +#include +#include +#include + +enum { LINESIZ = 72 }; + +static int +next_char (int stop) +{ + static int ch = 0; + static int i = 0; + static int c; + int ret; + + switch (i++) + { + case 0: + do + ch = (ch + 1) % 128; + while (!c_isprint (ch)); + if (ch == stop) + return 0; + c = ch; + break; + + case LINESIZ: + return '\r'; + + case LINESIZ+1: + i = 0; + return '\n'; + } + + ret = c; + do + c = (c + 1) % 128; + while (!c_isprint (c)); + + return ret; +} + +int +main (int argc, char **argv) +{ + struct pies_url *url; + int fd; + FILE *fp; + unsigned n; + int c, first; + char *progname = argv[0]; + + if (argc != 2) + { + fprintf (stderr, "usage: %s URL\n", progname); + fprintf (stderr, "Tests the character generator protocol\n"); + return 64; + } + + if (pies_url_create (&url, argv[1])) + { + perror (argv[0]); + return 64; + } + + fd = url_connect (url, NULL); + fp = fdopen (fd, "r"); + + first = next_char (0); + c = first; + do + { + int in = fgetc (fp); + if (in == EOF) + { + fprintf (stderr, "%s: unexpected EOF in %u\n", progname, n); + return 1; + } + if (in != c) + { + fprintf (stderr, "%s: got %d instead of %d in %u\n", + progname, in, c, n); + return 1; + } + n++; + } + while ((c = next_char (first)) != 0); + return 0; +} + + + diff --git a/tests/lines.c b/tests/lines.c index 106491f..f7f8443 100644 --- a/tests/lines.c +++ b/tests/lines.c @@ -1,3 +1,19 @@ +/* This file is part of GNU Pies testsuite. + Copyright (C) 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 . */ + #include int @@ -11,6 +27,7 @@ main (int argc, char **argv) if (argc != 2) { fprintf (stderr, "usage: %s FILE\n", progname); + fprintf (stderr, "Prints number of lines in FILE.\n"); return 1; } diff --git a/tests/nt.c b/tests/nt.c index 98a4077..876ffb1 100644 --- a/tests/nt.c +++ b/tests/nt.c @@ -311,7 +311,9 @@ redirect (int sfd, char const *name) static void usage (FILE *fp) { - fprintf (fp, "usage: nt [-i FILE] [-o FILE] URL\n"); + fprintf (fp, "usage: nt [-i IFILE] [-o OFILE] URL\n"); + fprintf (fp, "Reads data from stdin (or IFILE) and sends them to URL.\n"); + fprintf (fp, "Reads replies from URL and sends them to stdout (or OFILE).\n"); } int diff --git a/tests/passfd.at b/tests/passfd.at index 4a685a3..8a7eecb 100644 --- a/tests/passfd.at +++ b/tests/passfd.at @@ -29,7 +29,7 @@ PIES_FD_SOCKET=$PWD/pfd.sock cat > pies.conf <<_EOT component pfd { - command "recvfd '$PIES_FD_SOCKET' $auxdir/in.test $PWD/inlog"; + command "recvfd -s '$PIES_FD_SOCKET' $auxdir/in.test $PWD/inlog"; mode pass-fd; pass-fd-timeout 3; pass-fd-socket "$PIES_FD_SOCKET"; diff --git a/tests/readtime.c b/tests/readtime.c new file mode 100644 index 0000000..f7cc36a --- /dev/null +++ b/tests/readtime.c @@ -0,0 +1,161 @@ +/* This file is part of GNU Pies testsuite. + Copyright (C) 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 . */ + +#include +#include +#include +#include +#include +#include +#include + +char *progname; +int precision = 2; + +void +usage (FILE *fp, int status) +{ + fprintf (fp, "usage: %s [-v] [-p PREC] URL\n", progname); + fprintf (fp, "Reads time stamp from URL as per RFC868.\n"); + fprintf (fp, "Exits with status 0 if it is within PREC seconds from the current time.\n"); + fprintf (fp, "Default PREC is %d seconds.\n", precision); + exit (status); +} + +enum { SEVENTY_YEARS = (unsigned long)25567 * 24 * 60 * 60 }; + +int +main (int argc, char **argv) +{ + int c, fd; + struct pies_url *url; + time_t now; + int human_time = 0; + union + { + char s[80]; + uint32_t u; + } buf; + uint32_t t, d; + ssize_t n; + int verbose = 0; + + progname = argv[0]; + setlocale (LC_ALL, "C"); + + while ((c = getopt (argc, argv, "Hhvp:")) != EOF) + { + switch (c) + { + case 'H': + human_time = 1; + break; + + case 'h': + usage (stdout, 0); + break; + + case 'p': + precision = atoi (optarg); + if (precision <= 0) + { + fprintf (stderr, "%s: bad precision\n", progname); + exit (1); + } + break; + + case 'v': + verbose++; + break; + + default: + exit (64); + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) + usage (stderr, 64); + + if (pies_url_create (&url, argv[0])) + { + perror (argv[0]); + return 64; + } + + fd = url_connect (url, NULL); + time (&now); + n = read (fd, &buf, sizeof (buf)); + if (n == -1) + { + perror ("read"); + exit (1); + } + + if (human_time) + { + struct tm daytime; + char *p; + + if (buf.s[n-1] == '\n') + { + buf.s[--n] = 0; + if (buf.s[n-1] == '\r') + buf.s[--n] = 0; + } + if (verbose > 1) + printf ("got %*.*s\n", (int)n, (int)n, buf.s); + p = strptime (buf.s, "%a %b %d %H:%M:%S %Y", &daytime); + if (!p) + { + fprintf (stderr, "%s: unable to parse time '%s'\n", progname, buf.s); + exit (1); + } + if (*p) + { + fprintf (stderr, "%s: trailing garbage: '%s'\n", progname, p); + } + t = mktime (&daytime); + } + else + { + if (n < sizeof (buf.u)) + { + fprintf (stderr, "%s: read %d bytes\n", progname, (int)n); + exit (1); + } + t = ntohl (buf.u); + if (verbose > 1) + printf ("got %lu\n", (unsigned long) t); + t -= SEVENTY_YEARS; + } + + if (t > now) + d = t - now; + else + d = now - t; + + if (d > precision) + { + fprintf (stderr, "%s: time diff %lu\n", progname, (unsigned long) d); + exit (1); + } + else if (verbose) + printf ("OK\n"); + return 0; +} diff --git a/tests/recvfd.c b/tests/recvfd.c index 82455c7..4da42ca 100644 --- a/tests/recvfd.c +++ b/tests/recvfd.c @@ -30,13 +30,13 @@ char const *progname; void -usage (void) +usage (FILE *fp, int status) { - fprintf (stderr, "usage: %s SOCKET COMMAND ARGS...\n", progname); - fprintf (stderr, "Test tool for pass-fd pies components.\n"); - fprintf (stderr, "Listens on the file descriptor obtained from SOCKET.\n"); - fprintf (stderr, "For each connection, execs COMMAND ARGS as a separate process.\n"); - exit (64); + fprintf (fp, "usage: %s [-s SOCKET] COMMAND ARGS...\n", progname); + fprintf (fp, "Test tool for accept and pass-fd pies components.\n"); + fprintf (fp, "Listens on the file descriptor, either 0 or obtained from SOCKET.\n"); + fprintf (fp, "For each connection, execs COMMAND ARGS as a separate process.\n"); + exit (status); } static int @@ -162,21 +162,44 @@ sigquit (int sig) int main (int argc, char **argv) { - int sfd, fd; + int c; + int fd; + char *socket_name = NULL; progname = argv[0]; - if (argc < 3) - usage (); - - sfd = listen_socket (argv[1]); - - argc -= 2; - argv += 2; + while ((c = getopt (argc, argv, "hs:")) != EOF) + { + switch (c) + { + case 'h': + usage (stdout, 0); + break; - fd = get_fd (sfd); - close (sfd); + case 's': + socket_name = optarg; + break; + default: + exit (64); + } + } + + argc -= optind; + argv += optind; + + if (argc == 0) + usage (stderr, 64); + + if (socket_name) + { + int sfd = listen_socket (socket_name); + fd = get_fd (sfd); + close (sfd); + } + else + fd = 0; + signal (SIGCHLD, sigchld); signal (SIGTERM, sigquit); signal (SIGHUP, sigquit); diff --git a/tests/testsuite.at b/tests/testsuite.at index 843d235..5c3f785 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -71,7 +71,9 @@ m4_include([shutdown.at]) m4_include([shell.at]) m4_include([inet.at]) m4_include([maxinst.at]) +m4_include([builtin.at]) m4_include([passfd.at]) +m4_include([accept.at]) m4_include([envop.at]) m4_include([env.at]) \ No newline at end of file diff --git a/tests/to.c b/tests/to.c index 0bfabac..78e5f97 100644 --- a/tests/to.c +++ b/tests/to.c @@ -51,7 +51,8 @@ main (int argc, char **argv) if (argc < 3) { - fprintf (stderr, "usage: %s TIMEOUT COMMAND ...\n", progname); + fprintf (stderr, "usage: %s TIMEOUT COMMAND ARGS...\n", progname); + fprintf (stderr, "Runs command with a timeout.\n"); exit (1); } errno = 0; -- cgit v1.2.1