summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--lib/Makefile.am1
-rw-r--r--lib/libpies.h3
-rw-r--r--lib/urlconn.c157
-rw-r--r--src/piesctl.c122
-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
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
@@ -33,6 +33,7 @@ libpies_a_SOURCES=\
33 split3.c\ 33 split3.c\
34 strtotok.c\ 34 strtotok.c\
35 url.c\ 35 url.c\
36 urlconn.c\
36 wildmatch.c 37 wildmatch.c
37 38
38libpies_a_LIBADD=\ 39libpies_a_LIBADD=\
diff --git a/lib/libpies.h b/lib/libpies.h
index 76b70fe..40364a6 100644
--- a/lib/libpies.h
+++ b/lib/libpies.h
@@ -102,5 +102,8 @@ void sockaddr_to_str (const struct sockaddr *sa, int salen,
102 size_t *plen); 102 size_t *plen);
103char *sockaddr_to_astr (const struct sockaddr *sa, int salen); 103char *sockaddr_to_astr (const struct sockaddr *sa, int salen);
104 104
105/* urlconn.c */
106int url_connect (struct pies_url *url, struct grecs_sockaddr *source_addr);
107
105 108
106 109
diff --git a/lib/urlconn.c b/lib/urlconn.c
new file mode 100644
index 0000000..5994626
--- a/dev/null
+++ b/lib/urlconn.c
@@ -0,0 +1,157 @@
1/* This file is part of GNU Pies.
2 Copyright (C) 2019 Sergey Poznyakoff
3
4 GNU Pies is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Pies is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */
16
17#include <config.h>
18#include <sys/types.h>
19#include <sys/socket.h>
20#include <sys/un.h>
21#include <sys/stat.h>
22#include <netinet/in.h>
23#include <netdb.h>
24#include <errno.h>
25#include <unistd.h>
26#include <fcntl.h>
27#include <libpies.h>
28#include <grecs.h>
29
30int
31url_connect (struct pies_url *url, struct grecs_sockaddr *source_addr)
32{
33 int fd;
34 union pies_sockaddr_storage addr;
35 socklen_t socklen;
36 int flags;
37
38 if (strcmp (url->scheme, "unix") == 0
39 || strcmp (url->scheme, "file") == 0
40 || strcmp (url->scheme, "socket") == 0)
41 {
42 struct stat st;
43
44 if (url->port)
45 {
46 grecs_error (NULL, 0, _("%s: invalid connection type: "
47 "port is meaningless for UNIX sockets"),
48 url->string);
49 errno = EINVAL;
50 return -1;
51 }
52
53 if (strlen (url->path) > sizeof addr.s_un.sun_path)
54 {
55 grecs_error (NULL, 0,
56 _("%s: UNIX socket name too long"),
57 url->path);
58 errno = EINVAL;
59 return -1;
60 }
61
62 addr.s.sa_family = PF_UNIX;
63 socklen = sizeof (addr.s_un);
64 strcpy (addr.s_un.sun_path, url->path);
65
66 if (stat (url->path, &st))
67 {
68 if (errno != ENOENT)
69 {
70 grecs_error (NULL, errno, _("%s: %s failed"), url->path, "stat");
71 return -1;
72 }
73 }
74 else
75 {
76 if (!S_ISSOCK (st.st_mode))
77 {
78 grecs_error (NULL, 0, _("%s: not a socket"), url->path);
79 errno = EINVAL;
80 return -1;
81 }
82 }
83 }
84 else if (strcmp (url->scheme, "inet") == 0)
85 {
86 struct addrinfo hints;
87 struct addrinfo *res;
88 int rc;
89
90 memset (&hints, 0, sizeof (hints));
91 hints.ai_socktype = SOCK_STREAM;
92 hints.ai_flags = 0;
93 hints.ai_family = AF_INET;
94
95 rc = getaddrinfo (url->host, url->port_s, &hints, &res);
96 switch (rc)
97 {
98 case 0:
99 break;
100
101 case EAI_SYSTEM:
102 grecs_error (NULL, errno, "%s", _("cannot parse address"));
103 return -1;
104
105 case EAI_BADFLAGS:
106 case EAI_SOCKTYPE:
107 grecs_error (NULL, 0, _("%s:%d: internal error converting address"),
108 __FILE__,__LINE__);
109 return -1;
110
111 case EAI_MEMORY:
112 grecs_alloc_die ();
113
114 default:
115 grecs_error (NULL, 0, "getaddrinfo: %s", gai_strerror(rc));
116 return -1;
117 }
118
119 socklen = sizeof (addr.s_in);
120 memcpy (&addr.s_in, res->ai_addr, socklen);
121 freeaddrinfo (res);
122 }
123
124 fd = socket (addr.s.sa_family, SOCK_STREAM, 0);
125 if (fd == -1)
126 {
127 grecs_error (NULL, errno, _("%s: %s failed"), url->string, "socket");
128 return -1;
129 }
130
131 if ((flags = fcntl (fd, F_GETFD, 0)) == -1
132 || fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1)
133 grecs_error (NULL, 0, _("%s: cannot set close-on-exec: %s"),
134 url->string, strerror (errno));
135
136 if (source_addr)
137 {
138 if (source_addr->sa->sa_family != addr.s.sa_family)
139 grecs_error (NULL, 0,
140 _("source and destination address family differ"));
141 else if (bind (fd, source_addr->sa, source_addr->len) < 0)
142 {
143 grecs_error (NULL, errno, _("%s: %s failed"), url->string, "bind");
144 close (fd);
145 return -1;
146 }
147 }
148
149 if (connect (fd, &addr.s, socklen))
150 {
151 grecs_error (NULL, errno, _("%s: %s failed"), url->string, "connect");
152 close (fd);
153 return -1;
154 }
155
156 return fd;
157}
diff --git a/src/piesctl.c b/src/piesctl.c
index 2e35aea..aa983ee 100644
--- a/src/piesctl.c
+++ b/src/piesctl.c
@@ -415,123 +415,22 @@ static struct shttp_connection *
415shttp_connect (struct pies_url *url, struct grecs_sockaddr *source_addr) 415shttp_connect (struct pies_url *url, struct grecs_sockaddr *source_addr)
416{ 416{
417 int fd; 417 int fd;
418 union pies_sockaddr_storage addr; 418 struct pies_url *conn_url;
419 socklen_t socklen;
420 int flags;
421 struct shttp_connection *conn; 419 struct shttp_connection *conn;
422 FILE *fp; 420 FILE *fp;
423
424 if (strcmp (url->scheme, "unix") == 0
425 || strcmp (url->scheme, "file") == 0
426 || strcmp (url->scheme, "socket") == 0)
427 {
428 struct stat st;
429
430 if (url->port)
431 {
432 grecs_error (NULL, 0, _("%s: invalid connection type: "
433 "port is meaningless for UNIX sockets"),
434 url->string);
435 return NULL;
436 }
437
438 if (strlen (url->path) > sizeof addr.s_un.sun_path)
439 {
440 errno = EINVAL;
441 grecs_error (NULL, 0,
442 _("%s: UNIX socket name too long"),
443 url->path);
444 return NULL;
445 }
446
447 addr.s.sa_family = PF_UNIX;
448 socklen = sizeof (addr.s_un);
449 strcpy (addr.s_un.sun_path, url->path);
450
451 if (stat (url->path, &st))
452 {
453 if (errno != ENOENT)
454 {
455 grecs_error (NULL, errno, _("%s: %s failed"), url->path, "stat");
456 return NULL;
457 }
458 }
459 else
460 {
461 if (!S_ISSOCK (st.st_mode))
462 {
463 grecs_error (NULL, 0, _("%s: not a socket"), url->path);
464 return NULL;
465 }
466 }
467 }
468 else if (strcmp (url->scheme, "inet") == 0)
469 {
470 short pnum;
471 struct hostent *hp;
472
473 addr.s_in.sin_family = PF_INET;
474 socklen = sizeof (addr.s_in);
475
476 pnum = url->port ? url->port : 8080;
477
478 hp = gethostbyname (url->host);
479 if (!hp)
480 {
481 grecs_error (NULL, 0, _("%s: unknown host name"), url->string);
482 return NULL;
483 }
484
485 addr.s_in.sin_family = hp->h_addrtype;
486 switch (hp->h_addrtype)
487 {
488 case AF_INET:
489 memmove (&addr.s_in.sin_addr, hp->h_addr, 4);
490 addr.s_in.sin_port = htons (pnum);
491 break;
492
493 default:
494 grecs_error (NULL, 0,
495 _("%s: invalid connection type: "
496 "unsupported address family"),
497 url->string);
498 return NULL;
499 }
500 }
501 else
502 {
503 grecs_error (NULL, 0, _("%s: unsupported protocol"), url->string);
504 return NULL;
505 }
506 421
507 fd = socket (addr.s.sa_family, SOCK_STREAM, 0); 422 if (pies_url_copy (&conn_url, url))
508 if (fd == -1) 423 grecs_alloc_die ();
509 {
510 grecs_error (NULL, errno, _("%s: %s failed"), url->string, "socket");
511 return NULL;
512 }
513 424
514 if ((flags = fcntl (fd, F_GETFD, 0)) == -1 425 if (conn_url->port == 0 && strcmp (conn_url->scheme, "inet") == 0)
515 || fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1)
516 grecs_error (NULL, 0, _("%s: cannot set close-on-exec: %s"),
517 url->string, strerror (errno));
518
519 if (source_addr)
520 { 426 {
521 if (source_addr->sa->sa_family != addr.s.sa_family) 427 conn_url->port_s = grecs_strdup ("8080");
522 grecs_error (NULL, 0, 428 conn_url->port = 8080;
523 _("source and destination address family differ"));
524 else if (bind (fd, source_addr->sa, source_addr->len) < 0)
525 {
526 grecs_error (NULL, errno, _("%s: %s failed"), url->string, "bind");
527 exit (EX_UNAVAILABLE);
528 }
529 } 429 }
530 430 fd = url_connect (conn_url, source_addr);
531 if (connect (fd, &addr.s, socklen)) 431 if (fd == -1)
532 { 432 {
533 grecs_error (NULL, errno, _("%s: %s failed"), url->string, "connect"); 433 pies_url_destroy (&conn_url);
534 close (fd);
535 return NULL; 434 return NULL;
536 } 435 }
537 436
@@ -545,8 +444,7 @@ shttp_connect (struct pies_url *url, struct grecs_sockaddr *source_addr)
545 444
546 conn = grecs_zalloc (sizeof (*conn)); 445 conn = grecs_zalloc (sizeof (*conn));
547 conn->fp = fp; 446 conn->fp = fp;
548 if (pies_url_copy (&conn->url, url)) 447 conn->url = conn_url;
549 grecs_alloc_die ();
550 448
551 if (!no_netrc_option) 449 if (!no_netrc_option)
552 netrc_scan (conn->url); 450 netrc_scan (conn->url);
diff --git a/tests/.gitignore b/tests/.gitignore
index 459fa44..68159e3 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -7,3 +7,4 @@ testsuite
7testsuite.dir 7testsuite.dir
8testsuite.log 8testsuite.log
9to 9to
10nt
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 = \
20 aux/mailer\ 20 aux/mailer\
21 aux/touchfile\ 21 aux/touchfile\
22 aux/sleepexit\ 22 aux/sleepexit\
23 aux/startcheck 23 aux/startcheck\
24 aux/in.test
24 25
25EXTRA_DIST = $(TESTSUITE_AT) testsuite package.m4 $(AUXTOOLS) 26EXTRA_DIST = $(TESTSUITE_AT) testsuite package.m4 $(AUXTOOLS)
26DISTCLEANFILES = atconfig $(check_SCRIPTS) 27DISTCLEANFILES = atconfig $(check_SCRIPTS)
@@ -52,6 +53,7 @@ TESTSUITE_AT = \
52 cyclic.at\ 53 cyclic.at\
53 env.at\ 54 env.at\
54 envop.at\ 55 envop.at\
56 inet.at\
55 respawn.at\ 57 respawn.at\
56 redirect.at\ 58 redirect.at\
57 ret-exec.at\ 59 ret-exec.at\
@@ -64,9 +66,12 @@ TESTSUITE_AT = \
64TESTSUITE = $(srcdir)/testsuite 66TESTSUITE = $(srcdir)/testsuite
65M4=m4 67M4=m4
66 68
67noinst_PROGRAMS = envtest to lines 69noinst_PROGRAMS = envtest to lines nt
70nt_SOURCES = nt.c iobuf.h
68AM_CPPFLAGS = \ 71AM_CPPFLAGS = \
69 -I$(top_srcdir)/lib\ 72 -I$(top_srcdir)/lib\
73 -I$(top_srcdir)/gnu\
74 -I$(top_builddir)/gnu\
70 @GRECS_INCLUDES@ 75 @GRECS_INCLUDES@
71 76
72LDADD = \ 77LDADD = \
diff --git a/tests/aux/in.test b/tests/aux/in.test
new file mode 100755
index 0000000..4a98763
--- a/dev/null
+++ b/tests/aux/in.test
@@ -0,0 +1,22 @@
1#! /bin/sh
2
3FILE=${1:?}
4 IFS='
5'
6while read COMMAND ARG
7do
8 echo $COMMAND $ARG >> $FILE
9 case $COMMAND in
10 stop) if [ -n "$STOPCMD" ]; then
11 echo "STOP"
12 $STOPCMD
13 else
14 echo "OK $COMMAND $ARG"
15 fi
16 ;;
17 quit) exit $ARG;;
18 *) echo "OK $COMMAND $ARG"
19 ;;
20 esac
21done
22
diff --git a/tests/inet.at b/tests/inet.at
new file mode 100644
index 0000000..41cae6a
--- a/dev/null
+++ b/tests/inet.at
@@ -0,0 +1,38 @@
1AT_SETUP([inet component])
2AT_CHECK([
3PIES_XFAIL_CHECK
4PIES_CONTROL_INIT
5AT_DATA([input],
6[line 1
7line 2
8stop
9])
10: ${PIES_TEST_INET_SOCKET:=unix://$PWD/in.sock}
11cat > pies.conf <<_EOT
12component in {
13 command "$auxdir/in.test $PWD/inlog";
14 env {
15 set "STOPCMD=piesctl --url unix:///$PWD/pies.ctl --no-netrc shutdown";
16 }
17 mode inetd;
18 socket "$PIES_TEST_INET_SOCKET";
19 stderr file "$PWD/log.err";
20}
21component controller {
22 command "nt $PIES_TEST_INET_SOCKET -i input";
23}
24_EOT
25set -e
26to 10 \
27 pies --foreground --stderr \
28 --config-file control.conf --config-file pies.conf --debug 1 2>errlog
29
30cat inlog
31cat log.err >&2
32],
33[0],
34[line 1
35line 2
36stop
37])
38AT_CLEANUP
diff --git a/tests/iobuf.h b/tests/iobuf.h
new file mode 100644
index 0000000..4e43338
--- a/dev/null
+++ b/tests/iobuf.h
@@ -0,0 +1,141 @@
1/* This file is part of GNU Pies.
2 Copyright (C) 2019 Sergey Poznyakoff
3
4 GNU Pies is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Pies is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */
16
17#define IO_BUF_SIZE 512
18
19struct iobuf
20{
21 char buf[IO_BUF_SIZE]; /* Initial line buffer */
22 size_t start; /* Start of data */
23 size_t avail; /* Start of available space */
24};
25
26static inline void
27iobuf_reset (struct iobuf *bp)
28{
29 bp->start = bp->avail = 0;
30}
31
32/* Return the count of data bytes in the buffer */
33static inline size_t
34iobuf_data_size (struct iobuf *bp)
35{
36 return bp->avail - bp->start;
37}
38
39static inline char *
40iobuf_data_ptr (struct iobuf *bp)
41{
42 return bp->buf + bp->start;
43}
44
45static inline void
46iobuf_data_advance (struct iobuf *bp, size_t n)
47{
48 bp->start += n;
49 if (bp->start == bp->avail)
50 iobuf_reset (bp);
51}
52
53static inline int
54iobuf_putback (struct iobuf *bp)
55{
56 return bp->buf[--bp->start-1];
57}
58
59static inline int
60iobuf_getc (struct iobuf *bp)
61{
62 int c = -1;
63 if (iobuf_data_size (bp) > 0)
64 {
65 c = *iobuf_data_ptr (bp);
66 iobuf_data_advance(bp, 1);
67 }
68 return c;
69}
70
71/* Return the count of available bytes in the buffer */
72static inline size_t
73iobuf_avail_size (struct iobuf *bp)
74{
75 return sizeof (bp->buf) - bp->avail;
76}
77
78static inline char *
79iobuf_avail_ptr (struct iobuf *bp)
80{
81 return bp->buf + bp->avail;
82}
83
84static inline void
85iobuf_avail_advance (struct iobuf *bp, size_t n)
86{
87 bp->avail += n;
88 if (iobuf_avail_size (bp) == 0 && iobuf_data_size (bp) == 0)
89 iobuf_reset (bp);
90}
91
92static inline int
93iobuf_putc (struct iobuf *bp, int c)
94{
95 if (iobuf_avail_size (bp) > 0)
96 {
97 *iobuf_avail_ptr (bp) = c;
98 iobuf_avail_advance (bp, 1);
99 return c;
100 }
101 return -1;
102}
103
104static inline ssize_t
105iobuf_fill (struct iobuf *bp, int fd)
106{
107 ssize_t n = read (fd, iobuf_avail_ptr (bp), iobuf_avail_size (bp));
108 if (n >= 0)
109 iobuf_avail_advance (bp, n);
110 return n;
111}
112
113static inline ssize_t
114iobuf_flush (struct iobuf *bp, int fd)
115{
116 ssize_t n = write (fd, iobuf_data_ptr (bp), iobuf_data_size (bp));
117 if (n >= 0)
118 iobuf_data_advance (bp, n);
119 return n;
120}
121
122static inline ssize_t
123iobuf_copy (struct iobuf *dst, struct iobuf *src)
124{
125 ssize_t n = iobuf_data_size (src);
126 if (n > 0)
127 {
128 ssize_t avail = iobuf_avail_size (dst);
129 if (avail < n)
130 n = avail;
131 if (n)
132 {
133 memcpy (iobuf_avail_ptr (dst), iobuf_data_ptr (src), n);
134 iobuf_avail_advance (dst, n);
135 iobuf_data_advance (src, n);
136 }
137 }
138 return 0;
139}
140
141
diff --git a/tests/nt.c b/tests/nt.c
new file mode 100644
index 0000000..6ee18f8
--- a/dev/null
+++ b/tests/nt.c
@@ -0,0 +1,339 @@
1/* This file is part of GNU Pies.
2 Copyright (C) 2019 Sergey Poznyakoff
3
4 GNU Pies is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Pies is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */
16
17#include <config.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <sys/types.h>
21#include <sys/stat.h>
22#include <fcntl.h>
23#include <unistd.h>
24#include <poll.h>
25#include <errno.h>
26#include <libpies.h>
27#include <iobuf.h>
28
29typedef struct netcat_server netcat_server_t;
30typedef struct netcat_stream netcat_stream_t;
31typedef ssize_t (*netcat_stream_io_t) (netcat_server_t *srv);
32typedef int (*netcat_stream_disconnect_t) (netcat_server_t *srv);
33
34enum { IN, OUT };
35
36struct netcat_server
37{
38 char const *id;
39 netcat_server_t *next, *prev;
40 struct pollfd *pollfd;
41 int state;
42 netcat_stream_disconnect_t disconnect;
43 netcat_server_t *peer;
44 struct iobuf buf[2];
45};
46
47static netcat_server_t *server_head, *server_tail;
48
49netcat_server_t *
50netcat_server_create (char const *id, struct pollfd *pfd, int state,
51 netcat_stream_disconnect_t dis, netcat_server_t *peer)
52{
53 netcat_server_t *srv = grecs_zalloc (sizeof (*srv));
54 srv->id = id;
55 srv->pollfd = pfd;
56 srv->state = state;
57 srv->pollfd->events |= srv->state;
58 srv->disconnect = dis;
59 srv->peer = peer;
60 if (peer)
61 srv->peer->peer = srv;
62 iobuf_reset (&srv->buf[IN]);
63 iobuf_reset (&srv->buf[OUT]);
64
65 srv->prev = server_tail;
66 srv->next = NULL;
67 if (server_tail)
68 server_tail->next = srv;
69 else
70 server_head = srv;
71 server_tail = srv;
72
73 return srv;
74}
75
76void
77netcat_server_remove (netcat_server_t *srv)
78{
79 netcat_server_t *p;
80 if ((p = srv->next) != NULL)
81 p->prev = srv->prev;
82 else
83 server_tail = srv->prev;
84 if ((p = srv->prev) != NULL)
85 p->next = srv->next;
86 else
87 server_head = srv->next;
88 if (srv->peer)
89 srv->peer->peer = NULL;
90 free (srv);
91}
92
93/*
94net_reader
951. If there is free space in the IN buffer, fill it with the data read
96from the fd and return
972. Otherwise, clear the POLLIN bit.
98
99stdout_writer (peer -> net_reader)
1001. If OUT buffer is empty
101 1.1 If the peer's IN buffer is not empty, transfer data from it
102 1.2 Else raise the peers POLLIN bit and return.
1032. Write as much as possible from OUT to fd
104
105stdin_reader (same as net_reader)
106
107net_writer (peer -> stdin_reader) same as stdout_writer
108*/
109
110ssize_t
111netcat_stream_read (netcat_server_t *srv)
112{
113 ssize_t n;
114
115 if (iobuf_avail_size (&srv->buf[IN]))
116 {
117 n = iobuf_fill (&srv->buf[IN], srv->pollfd->fd);
118 if (n == -1)
119 {
120 grecs_error (NULL, errno, "%s: read", srv->id);
121 srv->pollfd->events &= ~POLLIN;
122 return -1;
123 }
124 if (n == 0)
125 {
126 /* No more input is expected */
127 srv->disconnect (srv);
128 srv->state &= ~POLLIN;
129 srv->pollfd->events &= ~POLLIN;
130 if (srv->pollfd->events == 0)
131 srv->pollfd->fd = -1;
132 }
133 }
134 else
135 {
136 srv->pollfd->events &= ~POLLIN;
137 n = 0; /* catch timeout? */
138 }
139 return n;
140}
141
142static inline int
143peer_is_state (netcat_server_t *srv, int state)
144{
145 return srv->peer && (srv->peer->state & state);
146}
147
148ssize_t
149netcat_stream_write (netcat_server_t *srv)
150{
151 ssize_t n;
152
153 if (iobuf_data_size (&srv->buf[OUT]) == 0)
154 {
155 if (!peer_is_state (srv, POLLIN))
156 {
157 // shutdown write end
158 srv->disconnect (srv);
159 srv->state &= ~POLLOUT;
160 srv->pollfd->events &= ~POLLOUT;
161 if (srv->pollfd->events == 0)
162 srv->pollfd->fd = -1;
163 return -1;
164 }
165 if (iobuf_copy (&srv->buf[OUT], &srv->peer->buf[IN]) == 0)
166 {
167 srv->peer->pollfd->events |= POLLIN;
168 return 0;
169 }
170 }
171 n = iobuf_flush (&srv->buf[OUT], srv->pollfd->fd);
172 if (n == -1)
173 {
174 grecs_error (NULL, errno, "%s: write", srv->id);
175 return -1;
176 }
177 if (n == 0)
178 {
179 // FIXME: eof
180 return -1;
181 }
182 return 0;
183}
184
185int
186disconnect_in (netcat_server_t *srv)
187{
188 return shutdown (srv->pollfd->fd, SHUT_RD);
189}
190
191int
192disconnect_out (netcat_server_t *srv)
193{
194 return shutdown (srv->pollfd->fd, SHUT_WR);
195}
196
197int
198disconnect_stdin (netcat_server_t *srv)
199{
200 return close (srv->pollfd->fd);
201}
202
203int
204disconnect_stdout (netcat_server_t *srv)
205{
206 close (srv->pollfd->fd);
207 exit (0);
208}
209
210static int
211netcat (char const *urlstr)
212{
213 int fd;
214 struct pies_url *url;
215 struct pollfd pfd[3];
216 int nfd = sizeof (pfd) / sizeof (pfd[0]);
217 netcat_server_t *srvin, *srvout, *srv;
218
219 if (pies_url_create (&url, urlstr))
220 {
221 perror (urlstr);
222 return 64;
223 }
224
225 fd = url_connect (url, NULL);
226 if (fd == -1)
227 return 1;
228
229 pfd[0].fd = 0;
230 pfd[0].events = 0;
231 srvin = netcat_server_create ("stdin", &pfd[0], POLLIN, disconnect_stdin, NULL);
232
233 pfd[1].fd = 1;
234 pfd[1].events = 0;
235 srvout = netcat_server_create ("stdout", &pfd[1], POLLOUT, disconnect_stdout, NULL);
236
237 pfd[2].fd = fd;
238 pfd[2].events = 0;
239
240 netcat_server_create ("netread", &pfd[2], POLLIN, disconnect_in, srvout);
241 netcat_server_create ("netwrite", &pfd[2], POLLOUT, disconnect_out, srvin);
242
243 while (server_head)
244 {
245 ssize_t n = poll (pfd, nfd, -1);
246 if (n == -1)
247 {
248 if (errno != EINTR)
249 grecs_error (NULL, errno, "poll");
250 continue;
251 }
252 for (srv = server_head; srv; )
253 {
254 netcat_server_t *next = srv->next;
255 int events;
256
257 events = (srv->pollfd->events|POLLHUP)
258 & (srv->pollfd->revents & (srv->state|POLLHUP));
259 if (events)
260 {
261 if (events & POLLHUP)
262 {
263 //grecs_error (NULL, 0, "HUP on %s", srv->id);
264 srv->disconnect (srv);
265 srv->pollfd->events &= ~srv->state;
266 if (srv->pollfd->events == 0)
267 srv->pollfd->fd = -1;
268 srv->state = 0;
269 }
270 else if (events & POLLIN)
271 netcat_stream_read (srv);
272 else if (events & POLLOUT)
273 netcat_stream_write (srv);
274 }
275 if (srv->state == 0 || srv->pollfd->fd == -1)
276 netcat_server_remove (srv);
277 srv = next;
278 }
279 }
280 return 0;
281}
282
283static void
284redirect (int sfd, char const *name)
285{
286 int fd;
287
288 fd = open (name, sfd ? (O_WRONLY | O_TRUNC) : O_RDONLY);
289 if (!fd)
290 {
291 perror (name);
292 exit (1);
293 }
294 if (dup2 (fd, sfd))
295 {
296 perror ("dup2");
297 exit (1);
298 }
299}
300
301static void
302usage (FILE *fp)
303{
304 fprintf (fp, "usage: nt [-i FILE] [-o FILE] URL\n");
305}
306
307int
308main (int argc, char **argv)
309{
310 int c;
311
312 while ((c = getopt (argc, argv, "i:o:")) != EOF)
313 {
314 switch (c)
315 {
316 case 'i':
317 redirect (0, optarg);
318 break;
319
320 case 'o':
321 redirect (1, optarg);
322 break;
323
324 default:
325 exit (64);
326 }
327 }
328
329 argc -= optind;
330 argv += optind;
331
332 if (argc != 1)
333 {
334 usage (stderr);
335 exit (64);
336 }
337
338 return netcat (argv[0]);
339}
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])
68m4_include([startup.at]) 68m4_include([startup.at])
69m4_include([shutdown.at]) 69m4_include([shutdown.at])
70m4_include([shell.at]) 70m4_include([shell.at])
71m4_include([inet.at])
71 72
72m4_include([envop.at]) 73m4_include([envop.at])
73m4_include([env.at]) \ No newline at end of file 74m4_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 @@
1#include <config.h>
1#include <stdlib.h> 2#include <stdlib.h>
2#include <stdio.h> 3#include <stdio.h>
3#include <errno.h> 4#include <errno.h>
4#include <unistd.h> 5#include <unistd.h>
5#include <sys/types.h> 6#include <sys/types.h>
6#include <sys/wait.h> 7#include <sys/wait.h>
8#include <signal.h>
9
10int volatile got_sigchld, got_sigalrm;
11
12void
13sighan (int sig)
14{
15 switch (sig)
16 {
17 case SIGCHLD:
18 got_sigchld = 1;
19 break;
20
21 case SIGALRM:
22 got_sigalrm = 1;
23 break;
24 }
25}
7 26
8int 27int
9main (int argc, char **argv) 28main (int argc, char **argv)
@@ -13,7 +32,7 @@ main (int argc, char **argv)
13 char *p; 32 char *p;
14 pid_t pid, ret; 33 pid_t pid, ret;
15 int status; 34 int status;
16 35
17 if (argc < 3) 36 if (argc < 3)
18 { 37 {
19 fprintf (stderr, "usage: %s TIMEOUT COMMAND ...\n", progname); 38 fprintf (stderr, "usage: %s TIMEOUT COMMAND ...\n", progname);
@@ -30,13 +49,16 @@ main (int argc, char **argv)
30 argc -= 2; 49 argc -= 2;
31 argv += 2; 50 argv += 2;
32 51
52 signal (SIGALRM, sighan);
53 signal (SIGCHLD, sighan);
54
33 pid = fork (); 55 pid = fork ();
34 if (pid == -1) 56 if (pid == -1)
35 { 57 {
36 perror ("fork"); 58 perror ("fork");
37 exit (127); 59 exit (127);
38 } 60 }
39 61
40 if (pid == 0) 62 if (pid == 0)
41 { 63 {
42 execvp (argv[0], argv); 64 execvp (argv[0], argv);
@@ -45,8 +67,23 @@ main (int argc, char **argv)
45 } 67 }
46 68
47 alarm (n); 69 alarm (n);
70 while (1)
71 {
72 pause ();
73 if (got_sigchld)
74 {
75 alarm (0);
76 break;
77 }
78 if (got_sigalrm)
79 {
80 fprintf (stderr, "%s: timed out\n", progname);
81 kill (pid, SIGKILL);
82 exit (127);
83 }
84 }
85
48 ret = wait (&status); 86 ret = wait (&status);
49 alarm (0);
50 87
51 if (ret != pid) 88 if (ret != pid)
52 { 89 {
@@ -56,7 +93,7 @@ main (int argc, char **argv)
56 93
57 if (WIFEXITED (status)) 94 if (WIFEXITED (status))
58 return WEXITSTATUS (status); 95 return WEXITSTATUS (status);
59 96
60 if (WIFSIGNALED (status)) 97 if (WIFSIGNALED (status))
61 fprintf (stderr, "%s: %s terminated on signal %d\n", progname, argv[0], 98 fprintf (stderr, "%s: %s terminated on signal %d\n", progname, argv[0],
62 WTERMSIG (status)); 99 WTERMSIG (status));
@@ -67,8 +104,3 @@ main (int argc, char **argv)
67 progname, argv[0], status); 104 progname, argv[0], status);
68 return 127; 105 return 127;
69} 106}
70
71
72
73
74

Return to:

Send suggestions and report system problems to the System administrator.