From c4dbd40477bb658cdc5c5e01b8f80057e587cd88 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Sun, 9 Jun 2019 18:54:58 +0300 Subject: Test inetd components. * lib/Makefile.am: Add urlconn.c * lib/libpies.h (url_connect): New proto. * lib/urlconn.c: New file. * src/piesctl.c (shttp_connect): Use url_connect. * tests/.gitignore: Update. * tests/Makefile.am: New auxtool: aux/in.test New test: inet.at * tests/aux/in.test: New file. * tests/inet.at: New file. * tests/iobuf.h: New file. * tests/nt.c: New file. * tests/testsuite.at: Add new test. * tests/to.c: explicitly terminate the child process on timeout. --- tests/.gitignore | 1 + tests/Makefile.am | 9 +- tests/aux/in.test | 22 ++++ tests/inet.at | 38 ++++++ tests/iobuf.h | 141 ++++++++++++++++++++++ tests/nt.c | 339 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/testsuite.at | 1 + tests/to.c | 50 ++++++-- 8 files changed, 590 insertions(+), 11 deletions(-) create mode 100755 tests/aux/in.test create mode 100644 tests/inet.at create mode 100644 tests/iobuf.h create mode 100644 tests/nt.c (limited to 'tests') diff --git a/tests/.gitignore b/tests/.gitignore index 459fa44..68159e3 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -7,3 +7,4 @@ testsuite testsuite.dir testsuite.log to +nt diff --git a/tests/Makefile.am b/tests/Makefile.am index 14f5b4f..0290f61 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -20,7 +20,8 @@ AUXTOOLS = \ aux/mailer\ aux/touchfile\ aux/sleepexit\ - aux/startcheck + aux/startcheck\ + aux/in.test EXTRA_DIST = $(TESTSUITE_AT) testsuite package.m4 $(AUXTOOLS) DISTCLEANFILES = atconfig $(check_SCRIPTS) @@ -52,6 +53,7 @@ TESTSUITE_AT = \ cyclic.at\ env.at\ envop.at\ + inet.at\ respawn.at\ redirect.at\ ret-exec.at\ @@ -64,9 +66,12 @@ TESTSUITE_AT = \ TESTSUITE = $(srcdir)/testsuite M4=m4 -noinst_PROGRAMS = envtest to lines +noinst_PROGRAMS = envtest to lines nt +nt_SOURCES = nt.c iobuf.h AM_CPPFLAGS = \ -I$(top_srcdir)/lib\ + -I$(top_srcdir)/gnu\ + -I$(top_builddir)/gnu\ @GRECS_INCLUDES@ LDADD = \ diff --git a/tests/aux/in.test b/tests/aux/in.test new file mode 100755 index 0000000..4a98763 --- /dev/null +++ b/tests/aux/in.test @@ -0,0 +1,22 @@ +#! /bin/sh + +FILE=${1:?} +IFS=' +' +while read COMMAND ARG +do + echo $COMMAND $ARG >> $FILE + case $COMMAND in + stop) if [ -n "$STOPCMD" ]; then + echo "STOP" + $STOPCMD + else + echo "OK $COMMAND $ARG" + fi + ;; + quit) exit $ARG;; + *) echo "OK $COMMAND $ARG" + ;; + esac +done + diff --git a/tests/inet.at b/tests/inet.at new file mode 100644 index 0000000..41cae6a --- /dev/null +++ b/tests/inet.at @@ -0,0 +1,38 @@ +AT_SETUP([inet component]) +AT_CHECK([ +PIES_XFAIL_CHECK +PIES_CONTROL_INIT +AT_DATA([input], +[line 1 +line 2 +stop +]) +: ${PIES_TEST_INET_SOCKET:=unix://$PWD/in.sock} +cat > pies.conf <<_EOT +component in { + command "$auxdir/in.test $PWD/inlog"; + env { + set "STOPCMD=piesctl --url unix:///$PWD/pies.ctl --no-netrc shutdown"; + } + mode inetd; + 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], +[line 1 +line 2 +stop +]) +AT_CLEANUP diff --git a/tests/iobuf.h b/tests/iobuf.h new file mode 100644 index 0000000..4e43338 --- /dev/null +++ b/tests/iobuf.h @@ -0,0 +1,141 @@ +/* This file is part of GNU Pies. + 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 . */ + +#define IO_BUF_SIZE 512 + +struct iobuf +{ + char buf[IO_BUF_SIZE]; /* Initial line buffer */ + size_t start; /* Start of data */ + size_t avail; /* Start of available space */ +}; + +static inline void +iobuf_reset (struct iobuf *bp) +{ + bp->start = bp->avail = 0; +} + +/* Return the count of data bytes in the buffer */ +static inline size_t +iobuf_data_size (struct iobuf *bp) +{ + return bp->avail - bp->start; +} + +static inline char * +iobuf_data_ptr (struct iobuf *bp) +{ + return bp->buf + bp->start; +} + +static inline void +iobuf_data_advance (struct iobuf *bp, size_t n) +{ + bp->start += n; + if (bp->start == bp->avail) + iobuf_reset (bp); +} + +static inline int +iobuf_putback (struct iobuf *bp) +{ + return bp->buf[--bp->start-1]; +} + +static inline int +iobuf_getc (struct iobuf *bp) +{ + int c = -1; + if (iobuf_data_size (bp) > 0) + { + c = *iobuf_data_ptr (bp); + iobuf_data_advance(bp, 1); + } + return c; +} + +/* Return the count of available bytes in the buffer */ +static inline size_t +iobuf_avail_size (struct iobuf *bp) +{ + return sizeof (bp->buf) - bp->avail; +} + +static inline char * +iobuf_avail_ptr (struct iobuf *bp) +{ + return bp->buf + bp->avail; +} + +static inline void +iobuf_avail_advance (struct iobuf *bp, size_t n) +{ + bp->avail += n; + if (iobuf_avail_size (bp) == 0 && iobuf_data_size (bp) == 0) + iobuf_reset (bp); +} + +static inline int +iobuf_putc (struct iobuf *bp, int c) +{ + if (iobuf_avail_size (bp) > 0) + { + *iobuf_avail_ptr (bp) = c; + iobuf_avail_advance (bp, 1); + return c; + } + return -1; +} + +static inline ssize_t +iobuf_fill (struct iobuf *bp, int fd) +{ + ssize_t n = read (fd, iobuf_avail_ptr (bp), iobuf_avail_size (bp)); + if (n >= 0) + iobuf_avail_advance (bp, n); + return n; +} + +static inline ssize_t +iobuf_flush (struct iobuf *bp, int fd) +{ + ssize_t n = write (fd, iobuf_data_ptr (bp), iobuf_data_size (bp)); + if (n >= 0) + iobuf_data_advance (bp, n); + return n; +} + +static inline ssize_t +iobuf_copy (struct iobuf *dst, struct iobuf *src) +{ + ssize_t n = iobuf_data_size (src); + if (n > 0) + { + ssize_t avail = iobuf_avail_size (dst); + if (avail < n) + n = avail; + if (n) + { + memcpy (iobuf_avail_ptr (dst), iobuf_data_ptr (src), n); + iobuf_avail_advance (dst, n); + iobuf_data_advance (src, n); + } + } + return 0; +} + + diff --git a/tests/nt.c b/tests/nt.c new file mode 100644 index 0000000..6ee18f8 --- /dev/null +++ b/tests/nt.c @@ -0,0 +1,339 @@ +/* This file is part of GNU Pies. + 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 +#include +#include +#include +#include + +typedef struct netcat_server netcat_server_t; +typedef struct netcat_stream netcat_stream_t; +typedef ssize_t (*netcat_stream_io_t) (netcat_server_t *srv); +typedef int (*netcat_stream_disconnect_t) (netcat_server_t *srv); + +enum { IN, OUT }; + +struct netcat_server +{ + char const *id; + netcat_server_t *next, *prev; + struct pollfd *pollfd; + int state; + netcat_stream_disconnect_t disconnect; + netcat_server_t *peer; + struct iobuf buf[2]; +}; + +static netcat_server_t *server_head, *server_tail; + +netcat_server_t * +netcat_server_create (char const *id, struct pollfd *pfd, int state, + netcat_stream_disconnect_t dis, netcat_server_t *peer) +{ + netcat_server_t *srv = grecs_zalloc (sizeof (*srv)); + srv->id = id; + srv->pollfd = pfd; + srv->state = state; + srv->pollfd->events |= srv->state; + srv->disconnect = dis; + srv->peer = peer; + if (peer) + srv->peer->peer = srv; + iobuf_reset (&srv->buf[IN]); + iobuf_reset (&srv->buf[OUT]); + + srv->prev = server_tail; + srv->next = NULL; + if (server_tail) + server_tail->next = srv; + else + server_head = srv; + server_tail = srv; + + return srv; +} + +void +netcat_server_remove (netcat_server_t *srv) +{ + netcat_server_t *p; + if ((p = srv->next) != NULL) + p->prev = srv->prev; + else + server_tail = srv->prev; + if ((p = srv->prev) != NULL) + p->next = srv->next; + else + server_head = srv->next; + if (srv->peer) + srv->peer->peer = NULL; + free (srv); +} + +/* +net_reader +1. If there is free space in the IN buffer, fill it with the data read +from the fd and return +2. Otherwise, clear the POLLIN bit. + +stdout_writer (peer -> net_reader) +1. If OUT buffer is empty + 1.1 If the peer's IN buffer is not empty, transfer data from it + 1.2 Else raise the peers POLLIN bit and return. +2. Write as much as possible from OUT to fd + +stdin_reader (same as net_reader) + +net_writer (peer -> stdin_reader) same as stdout_writer +*/ + +ssize_t +netcat_stream_read (netcat_server_t *srv) +{ + ssize_t n; + + if (iobuf_avail_size (&srv->buf[IN])) + { + n = iobuf_fill (&srv->buf[IN], srv->pollfd->fd); + if (n == -1) + { + grecs_error (NULL, errno, "%s: read", srv->id); + srv->pollfd->events &= ~POLLIN; + return -1; + } + if (n == 0) + { + /* No more input is expected */ + srv->disconnect (srv); + srv->state &= ~POLLIN; + srv->pollfd->events &= ~POLLIN; + if (srv->pollfd->events == 0) + srv->pollfd->fd = -1; + } + } + else + { + srv->pollfd->events &= ~POLLIN; + n = 0; /* catch timeout? */ + } + return n; +} + +static inline int +peer_is_state (netcat_server_t *srv, int state) +{ + return srv->peer && (srv->peer->state & state); +} + +ssize_t +netcat_stream_write (netcat_server_t *srv) +{ + ssize_t n; + + if (iobuf_data_size (&srv->buf[OUT]) == 0) + { + if (!peer_is_state (srv, POLLIN)) + { + // shutdown write end + srv->disconnect (srv); + srv->state &= ~POLLOUT; + srv->pollfd->events &= ~POLLOUT; + if (srv->pollfd->events == 0) + srv->pollfd->fd = -1; + return -1; + } + if (iobuf_copy (&srv->buf[OUT], &srv->peer->buf[IN]) == 0) + { + srv->peer->pollfd->events |= POLLIN; + return 0; + } + } + n = iobuf_flush (&srv->buf[OUT], srv->pollfd->fd); + if (n == -1) + { + grecs_error (NULL, errno, "%s: write", srv->id); + return -1; + } + if (n == 0) + { + // FIXME: eof + return -1; + } + return 0; +} + +int +disconnect_in (netcat_server_t *srv) +{ + return shutdown (srv->pollfd->fd, SHUT_RD); +} + +int +disconnect_out (netcat_server_t *srv) +{ + return shutdown (srv->pollfd->fd, SHUT_WR); +} + +int +disconnect_stdin (netcat_server_t *srv) +{ + return close (srv->pollfd->fd); +} + +int +disconnect_stdout (netcat_server_t *srv) +{ + close (srv->pollfd->fd); + exit (0); +} + +static int +netcat (char const *urlstr) +{ + int fd; + struct pies_url *url; + struct pollfd pfd[3]; + int nfd = sizeof (pfd) / sizeof (pfd[0]); + netcat_server_t *srvin, *srvout, *srv; + + if (pies_url_create (&url, urlstr)) + { + perror (urlstr); + return 64; + } + + fd = url_connect (url, NULL); + if (fd == -1) + return 1; + + pfd[0].fd = 0; + pfd[0].events = 0; + srvin = netcat_server_create ("stdin", &pfd[0], POLLIN, disconnect_stdin, NULL); + + pfd[1].fd = 1; + pfd[1].events = 0; + srvout = netcat_server_create ("stdout", &pfd[1], POLLOUT, disconnect_stdout, NULL); + + pfd[2].fd = fd; + pfd[2].events = 0; + + netcat_server_create ("netread", &pfd[2], POLLIN, disconnect_in, srvout); + netcat_server_create ("netwrite", &pfd[2], POLLOUT, disconnect_out, srvin); + + while (server_head) + { + ssize_t n = poll (pfd, nfd, -1); + if (n == -1) + { + if (errno != EINTR) + grecs_error (NULL, errno, "poll"); + continue; + } + for (srv = server_head; srv; ) + { + netcat_server_t *next = srv->next; + int events; + + events = (srv->pollfd->events|POLLHUP) + & (srv->pollfd->revents & (srv->state|POLLHUP)); + if (events) + { + if (events & POLLHUP) + { + //grecs_error (NULL, 0, "HUP on %s", srv->id); + srv->disconnect (srv); + srv->pollfd->events &= ~srv->state; + if (srv->pollfd->events == 0) + srv->pollfd->fd = -1; + srv->state = 0; + } + else if (events & POLLIN) + netcat_stream_read (srv); + else if (events & POLLOUT) + netcat_stream_write (srv); + } + if (srv->state == 0 || srv->pollfd->fd == -1) + netcat_server_remove (srv); + srv = next; + } + } + return 0; +} + +static void +redirect (int sfd, char const *name) +{ + int fd; + + fd = open (name, sfd ? (O_WRONLY | O_TRUNC) : O_RDONLY); + if (!fd) + { + perror (name); + exit (1); + } + if (dup2 (fd, sfd)) + { + perror ("dup2"); + exit (1); + } +} + +static void +usage (FILE *fp) +{ + fprintf (fp, "usage: nt [-i FILE] [-o FILE] URL\n"); +} + +int +main (int argc, char **argv) +{ + int c; + + while ((c = getopt (argc, argv, "i:o:")) != EOF) + { + switch (c) + { + case 'i': + redirect (0, optarg); + break; + + case 'o': + redirect (1, optarg); + break; + + default: + exit (64); + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) + { + usage (stderr); + exit (64); + } + + return netcat (argv[0]); +} diff --git a/tests/testsuite.at b/tests/testsuite.at index e340a2b..822ac7b 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -68,6 +68,7 @@ m4_include([ret-notify.at]) m4_include([startup.at]) m4_include([shutdown.at]) m4_include([shell.at]) +m4_include([inet.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 6511a54..6874bfd 100644 --- a/tests/to.c +++ b/tests/to.c @@ -1,9 +1,28 @@ +#include #include #include #include #include #include #include +#include + +int volatile got_sigchld, got_sigalrm; + +void +sighan (int sig) +{ + switch (sig) + { + case SIGCHLD: + got_sigchld = 1; + break; + + case SIGALRM: + got_sigalrm = 1; + break; + } +} int main (int argc, char **argv) @@ -13,7 +32,7 @@ main (int argc, char **argv) char *p; pid_t pid, ret; int status; - + if (argc < 3) { fprintf (stderr, "usage: %s TIMEOUT COMMAND ...\n", progname); @@ -30,13 +49,16 @@ main (int argc, char **argv) argc -= 2; argv += 2; + signal (SIGALRM, sighan); + signal (SIGCHLD, sighan); + pid = fork (); if (pid == -1) { perror ("fork"); exit (127); } - + if (pid == 0) { execvp (argv[0], argv); @@ -45,8 +67,23 @@ main (int argc, char **argv) } alarm (n); + while (1) + { + pause (); + if (got_sigchld) + { + alarm (0); + break; + } + if (got_sigalrm) + { + fprintf (stderr, "%s: timed out\n", progname); + kill (pid, SIGKILL); + exit (127); + } + } + ret = wait (&status); - alarm (0); if (ret != pid) { @@ -56,7 +93,7 @@ main (int argc, char **argv) if (WIFEXITED (status)) return WEXITSTATUS (status); - + if (WIFSIGNALED (status)) fprintf (stderr, "%s: %s terminated on signal %d\n", progname, argv[0], WTERMSIG (status)); @@ -67,8 +104,3 @@ main (int argc, char **argv) progname, argv[0], status); return 127; } - - - - - -- cgit v1.2.1