aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2019-06-09 18:54:58 +0300
committerSergey Poznyakoff <gray@gnu.org>2019-06-09 18:54:58 +0300
commitc4dbd40477bb658cdc5c5e01b8f80057e587cd88 (patch)
tree73534b2cd2fff99e16c26ed4be8cf115cf4b3960
parentc33922f128403f5d05d24f19abeaad2368ce5467 (diff)
downloadpies-c4dbd40477bb658cdc5c5e01b8f80057e587cd88.tar.gz
pies-c4dbd40477bb658cdc5c5e01b8f80057e587cd88.tar.bz2
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.
-rw-r--r--lib/Makefile.am1
-rw-r--r--lib/libpies.h3
-rw-r--r--lib/urlconn.c157
-rw-r--r--src/piesctl.c120
-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.c44
12 files changed, 757 insertions, 119 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 7b4eb42..4eb6041 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -30,12 +30,13 @@ libpies_a_SOURCES=\
proctitle.c\
pp.c\
safe_strcmp.c\
split3.c\
strtotok.c\
url.c\
+ urlconn.c\
wildmatch.c
libpies_a_LIBADD=\
$(LIBOBJS)\
@GRECS_LDADD@
diff --git a/lib/libpies.h b/lib/libpies.h
index 76b70fe..40364a6 100644
--- a/lib/libpies.h
+++ b/lib/libpies.h
@@ -99,8 +99,11 @@ union pies_sockaddr_storage
/* addrfmt.c */
void sockaddr_to_str (const struct sockaddr *sa, int salen,
char *bufptr, size_t buflen,
size_t *plen);
char *sockaddr_to_astr (const struct sockaddr *sa, int salen);
+/* urlconn.c */
+int url_connect (struct pies_url *url, struct grecs_sockaddr *source_addr);
+
diff --git a/lib/urlconn.c b/lib/urlconn.c
new file mode 100644
index 0000000..5994626
--- /dev/null
+++ b/lib/urlconn.c
@@ -0,0 +1,157 @@
+/* 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 <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libpies.h>
+#include <grecs.h>
+
+int
+url_connect (struct pies_url *url, struct grecs_sockaddr *source_addr)
+{
+ int fd;
+ union pies_sockaddr_storage addr;
+ socklen_t socklen;
+ int flags;
+
+ if (strcmp (url->scheme, "unix") == 0
+ || strcmp (url->scheme, "file") == 0
+ || strcmp (url->scheme, "socket") == 0)
+ {
+ struct stat st;
+
+ if (url->port)
+ {
+ grecs_error (NULL, 0, _("%s: invalid connection type: "
+ "port is meaningless for UNIX sockets"),
+ url->string);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (strlen (url->path) > sizeof addr.s_un.sun_path)
+ {
+ grecs_error (NULL, 0,
+ _("%s: UNIX socket name too long"),
+ url->path);
+ errno = EINVAL;
+ return -1;
+ }
+
+ addr.s.sa_family = PF_UNIX;
+ socklen = sizeof (addr.s_un);
+ strcpy (addr.s_un.sun_path, url->path);
+
+ if (stat (url->path, &st))
+ {
+ if (errno != ENOENT)
+ {
+ grecs_error (NULL, errno, _("%s: %s failed"), url->path, "stat");
+ return -1;
+ }
+ }
+ else
+ {
+ if (!S_ISSOCK (st.st_mode))
+ {
+ grecs_error (NULL, 0, _("%s: not a socket"), url->path);
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ }
+ else if (strcmp (url->scheme, "inet") == 0)
+ {
+ struct addrinfo hints;
+ struct addrinfo *res;
+ int rc;
+
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = 0;
+ hints.ai_family = AF_INET;
+
+ rc = getaddrinfo (url->host, url->port_s, &hints, &res);
+ switch (rc)
+ {
+ case 0:
+ break;
+
+ case EAI_SYSTEM:
+ grecs_error (NULL, errno, "%s", _("cannot parse address"));
+ return -1;
+
+ case EAI_BADFLAGS:
+ case EAI_SOCKTYPE:
+ grecs_error (NULL, 0, _("%s:%d: internal error converting address"),
+ __FILE__,__LINE__);
+ return -1;
+
+ case EAI_MEMORY:
+ grecs_alloc_die ();
+
+ default:
+ grecs_error (NULL, 0, "getaddrinfo: %s", gai_strerror(rc));
+ return -1;
+ }
+
+ socklen = sizeof (addr.s_in);
+ memcpy (&addr.s_in, res->ai_addr, socklen);
+ freeaddrinfo (res);
+ }
+
+ fd = socket (addr.s.sa_family, SOCK_STREAM, 0);
+ if (fd == -1)
+ {
+ grecs_error (NULL, errno, _("%s: %s failed"), url->string, "socket");
+ return -1;
+ }
+
+ if ((flags = fcntl (fd, F_GETFD, 0)) == -1
+ || fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1)
+ grecs_error (NULL, 0, _("%s: cannot set close-on-exec: %s"),
+ url->string, strerror (errno));
+
+ if (source_addr)
+ {
+ if (source_addr->sa->sa_family != addr.s.sa_family)
+ grecs_error (NULL, 0,
+ _("source and destination address family differ"));
+ else if (bind (fd, source_addr->sa, source_addr->len) < 0)
+ {
+ grecs_error (NULL, errno, _("%s: %s failed"), url->string, "bind");
+ close (fd);
+ return -1;
+ }
+ }
+
+ if (connect (fd, &addr.s, socklen))
+ {
+ grecs_error (NULL, errno, _("%s: %s failed"), url->string, "connect");
+ close (fd);
+ return -1;
+ }
+
+ return fd;
+}
diff --git a/src/piesctl.c b/src/piesctl.c
index 2e35aea..aa983ee 100644
--- a/src/piesctl.c
+++ b/src/piesctl.c
@@ -412,129 +412,28 @@ shttp_io_free (struct shttp_io *io)
}
static struct shttp_connection *
shttp_connect (struct pies_url *url, struct grecs_sockaddr *source_addr)
{
int fd;
- union pies_sockaddr_storage addr;
- socklen_t socklen;
- int flags;
+ struct pies_url *conn_url;
struct shttp_connection *conn;
FILE *fp;
- if (strcmp (url->scheme, "unix") == 0
- || strcmp (url->scheme, "file") == 0
- || strcmp (url->scheme, "socket") == 0)
- {
- struct stat st;
-
- if (url->port)
- {
- grecs_error (NULL, 0, _("%s: invalid connection type: "
- "port is meaningless for UNIX sockets"),
- url->string);
- return NULL;
- }
-
- if (strlen (url->path) > sizeof addr.s_un.sun_path)
- {
- errno = EINVAL;
- grecs_error (NULL, 0,
- _("%s: UNIX socket name too long"),
- url->path);
- return NULL;
- }
-
- addr.s.sa_family = PF_UNIX;
- socklen = sizeof (addr.s_un);
- strcpy (addr.s_un.sun_path, url->path);
-
- if (stat (url->path, &st))
- {
- if (errno != ENOENT)
- {
- grecs_error (NULL, errno, _("%s: %s failed"), url->path, "stat");
- return NULL;
- }
- }
- else
- {
- if (!S_ISSOCK (st.st_mode))
- {
- grecs_error (NULL, 0, _("%s: not a socket"), url->path);
- return NULL;
- }
- }
- }
- else if (strcmp (url->scheme, "inet") == 0)
- {
- short pnum;
- struct hostent *hp;
-
- addr.s_in.sin_family = PF_INET;
- socklen = sizeof (addr.s_in);
-
- pnum = url->port ? url->port : 8080;
-
- hp = gethostbyname (url->host);
- if (!hp)
- {
- grecs_error (NULL, 0, _("%s: unknown host name"), url->string);
- return NULL;
- }
-
- addr.s_in.sin_family = hp->h_addrtype;
- switch (hp->h_addrtype)
- {
- case AF_INET:
- memmove (&addr.s_in.sin_addr, hp->h_addr, 4);
- addr.s_in.sin_port = htons (pnum);
- break;
+ if (pies_url_copy (&conn_url, url))
+ grecs_alloc_die ();
- default:
- grecs_error (NULL, 0,
- _("%s: invalid connection type: "
- "unsupported address family"),
- url->string);
- return NULL;
- }
- }
- else
+ if (conn_url->port == 0 && strcmp (conn_url->scheme, "inet") == 0)
{
- grecs_error (NULL, 0, _("%s: unsupported protocol"), url->string);
- return NULL;
+ conn_url->port_s = grecs_strdup ("8080");
+ conn_url->port = 8080;
}
-
- fd = socket (addr.s.sa_family, SOCK_STREAM, 0);
+ fd = url_connect (conn_url, source_addr);
if (fd == -1)
{
- grecs_error (NULL, errno, _("%s: %s failed"), url->string, "socket");
- return NULL;
- }
-
- if ((flags = fcntl (fd, F_GETFD, 0)) == -1
- || fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1)
- grecs_error (NULL, 0, _("%s: cannot set close-on-exec: %s"),
- url->string, strerror (errno));
-
- if (source_addr)
- {
- if (source_addr->sa->sa_family != addr.s.sa_family)
- grecs_error (NULL, 0,
- _("source and destination address family differ"));
- else if (bind (fd, source_addr->sa, source_addr->len) < 0)
- {
- grecs_error (NULL, errno, _("%s: %s failed"), url->string, "bind");
- exit (EX_UNAVAILABLE);
- }
- }
-
- if (connect (fd, &addr.s, socklen))
- {
- grecs_error (NULL, errno, _("%s: %s failed"), url->string, "connect");
- close (fd);
+ pies_url_destroy (&conn_url);
return NULL;
}
fp = fdopen (fd, "w+");
if (!fp)
{
@@ -542,14 +441,13 @@ shttp_connect (struct pies_url *url, struct grecs_sockaddr *source_addr)
close (fd);
return NULL;
}
conn = grecs_zalloc (sizeof (*conn));
conn->fp = fp;
- if (pies_url_copy (&conn->url, url))
- grecs_alloc_die ();
+ conn->url = conn_url;
if (!no_netrc_option)
netrc_scan (conn->url);
return conn;
}
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,12 +1,31 @@
+#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;
@@ -27,12 +46,15 @@ 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);
}
@@ -42,14 +64,29 @@ main (int argc, char **argv)
execvp (argv[0], argv);
perror (argv[0]);
exit (127);
}
alarm (n);
- ret = wait (&status);
+ 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);
if (ret != pid)
{
perror ("wait");
exit (127);
}
@@ -64,11 +101,6 @@ main (int argc, char **argv)
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.