aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/Makefile.am9
-rwxr-xr-xtests/aux/in.test22
-rw-r--r--tests/inet.at38
-rw-r--r--tests/iobuf.h141
-rw-r--r--tests/nt.c339
-rw-r--r--tests/testsuite.at1
-rw-r--r--tests/to.c50
8 files changed, 590 insertions, 11 deletions
diff --git a/tests/.gitignore b/tests/.gitignore
index 459fa44..68159e3 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -4,6 +4,7 @@ envtest
lines
package.m4
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
@@ -17,13 +17,14 @@
AUXTOOLS = \
aux/respawn\
aux/retcode\
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)
MAINTAINERCLEANFILES = Makefile.in $(TESTSUITE)
## ------------ ##
@@ -49,27 +50,31 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac
TESTSUITE_AT = \
testsuite.at\
control.at\
cyclic.at\
env.at\
envop.at\
+ inet.at\
respawn.at\
redirect.at\
ret-exec.at\
ret-notify.at\
shell.at\
startup.at\
shutdown.at\
version.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 = \
../lib/libpies.a\
@GRECS_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 <http://www.gnu.org/licenses/>. */
+
+#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 <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <poll.h>
+#include <errno.h>
+#include <libpies.h>
+#include <iobuf.h>
+
+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
@@ -65,9 +65,10 @@ 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])
+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,22 +1,41 @@
+#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <signal.h>
+
+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)
{
char *progname = argv[0];
unsigned long n;
char *p;
pid_t pid, ret;
int status;
-
+
if (argc < 3)
{
fprintf (stderr, "usage: %s TIMEOUT COMMAND ...\n", progname);
exit (1);
}
errno = 0;
@@ -27,48 +46,61 @@ main (int argc, char **argv)
exit (1);
}
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);
perror (argv[0]);
exit (127);
}
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)
{
perror ("wait");
exit (127);
}
if (WIFEXITED (status))
return WEXITSTATUS (status);
-
+
if (WIFSIGNALED (status))
fprintf (stderr, "%s: %s terminated on signal %d\n", progname, argv[0],
WTERMSIG (status));
else if (WIFSTOPPED (status))
fprintf (stderr, "%s: %s stopped\n", progname, argv[0]);
else
fprintf (stderr, "%s: %s exited with unrecognized status %d\n",
progname, argv[0], status);
return 127;
}
-
-
-
-
-

Return to:

Send suggestions and report system problems to the System administrator.