diff options
-rw-r--r-- | lib/Makefile.am | 1 | ||||
-rw-r--r-- | lib/libpies.h | 3 | ||||
-rw-r--r-- | lib/urlconn.c | 157 | ||||
-rw-r--r-- | src/piesctl.c | 122 | ||||
-rw-r--r-- | tests/.gitignore | 1 | ||||
-rw-r--r-- | tests/Makefile.am | 9 | ||||
-rwxr-xr-x | tests/aux/in.test | 22 | ||||
-rw-r--r-- | tests/inet.at | 38 | ||||
-rw-r--r-- | tests/iobuf.h | 141 | ||||
-rw-r--r-- | tests/nt.c | 339 | ||||
-rw-r--r-- | tests/testsuite.at | 1 | ||||
-rw-r--r-- | tests/to.c | 50 |
12 files changed, 761 insertions, 123 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; - - default: - grecs_error (NULL, 0, - _("%s: invalid connection type: " - "unsupported address family"), - url->string); - return NULL; - } - } - else - { - grecs_error (NULL, 0, _("%s: unsupported protocol"), url->string); - return NULL; - } - fd = socket (addr.s.sa_family, SOCK_STREAM, 0); - if (fd == -1) - { - grecs_error (NULL, errno, _("%s: %s failed"), url->string, "socket"); - return NULL; - } + if (pies_url_copy (&conn_url, url)) + grecs_alloc_die (); - 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 (conn_url->port == 0 && strcmp (conn_url->scheme, "inet") == 0) { - 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); - } + conn_url->port_s = grecs_strdup ("8080"); + conn_url->port = 8080; } - - if (connect (fd, &addr.s, socklen)) + fd = url_connect (conn_url, source_addr); + if (fd == -1) { - 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 @@ -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; } - - - - - |