X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/tripe/blobdiff_plain/073cc23b4f8d5e9c4d74e9a15a6c0f13a55cf4e6..2a74827f7d93dbf9a6fc7688c8891e5a2cbdafdc:/uslip/uslip.c?ds=sidebyside diff --git a/uslip/uslip.c b/uslip/uslip.c index 245f1ec0..7752458c 100644 --- a/uslip/uslip.c +++ b/uslip/uslip.c @@ -47,6 +47,8 @@ #include #include +#include +#include #include #include #include @@ -54,6 +56,7 @@ #include #include #include +#include #include "slip.h" @@ -81,6 +84,8 @@ typedef struct client { typedef struct gobbler { sel_file f; + void (*done)(struct gobbler *, int, void *); + void *p; } gobbler; typedef struct dribbler { @@ -130,16 +135,15 @@ static void socketaddr(struct sockaddr_un *sun, size_t *sz) static void initqueue(pkq *q) { q->head = 0; q->tail = &q->head; } -static pkq_node *make_pkqnode(void *p, size_t n) +static pkq_node *make_pkqnode(const void *p, size_t n) { pkq_node *pn; - if (!n) - return (0); + if (!n) return (0); pn = CREATE(pkq_node); pn->next = 0; pn->buf = xmalloc(n); - memcpy(pn->buf, p, n); + if (p) memcpy(pn->buf, p, n); pn->sz = n; pn->n = 0; return (pn); @@ -149,8 +153,7 @@ static int enqueue(pkq *q, pkq_node *pn) { int rc = 0; - if (!pn) - return (0); + if (!pn) return (0); rc = !q->head; pn->next = 0; *q->tail = pn; @@ -188,14 +191,14 @@ static void destroy_pkq(pkq *q) /*----- Gobblers ----------------------------------------------------------* * * A gobbler just eats everything it sees on its input descriptor. - * Eventually, when it sees end-of-file, it closes the input descriptor and - * quits. + * Eventually, when it sees end-of-file, it closes the input descriptor, + * calls a user-supplied calback function, and quits. */ -static void gobbler_close(gobbler *g) +static void close_gobbler(gobbler *g) { if (g->f.fd != -1) { sel_rmfile(&g->f); close(g->f.fd); g->f.fd = -1; } } -static void gobbler_destroy(gobbler *g) { gobbler_close(g); DESTROY(g); } +static void destroy_gobbler(gobbler *g) { close_gobbler(g); DESTROY(g); } static void do_gobble_in(int fd, unsigned mode, void *p) { @@ -209,21 +212,27 @@ static void do_gobble_in(int fd, unsigned mode, void *p) break; else { moan("read (gobble): %s", strerror(errno)); - gobbler_close(g); + if (g->done) g->done(g, errno, g->p); + close_gobbler(g); break; } } else if (n == 0) { - gobbler_close(g); + if (g->done) g->done(g, 0, g->p); + close_gobbler(g); break; } } } -static gobbler *make_gobbler(int fd) +static gobbler *make_gobbler(int fd, + void (*done)(gobbler *, int, void *), + void *p) { gobbler *g; g = CREATE(gobbler); + g->done = done; + g->p = p; sel_initfile(&sel, &g->f, fd, SEL_READ, do_gobble_in, g); sel_addfile(&g->f); do_gobble_in(fd, SEL_READ, g); @@ -311,7 +320,7 @@ static void done_client_dribble(dribbler *d, int err, void *p) { if (err) moan("write (client): %s", strerror(err)); - gobbler_destroy(p); + destroy_gobbler(p); destroy_dribbler(d); reasons--; } @@ -336,7 +345,7 @@ static void dequeue_to_waiter(void) } } -static void client_destroy(client *c) +static void destroy_client(client *c) { sel_rmfile(&c->f); close(c->f.fd); @@ -365,7 +374,7 @@ static void do_client_in(int fd, unsigned mode, void *p) break; else { moan("read (client): %s", strerror(errno)); - client_destroy(c); + destroy_client(c); return; } } else if (n == 0) { @@ -374,7 +383,7 @@ static void do_client_in(int fd, unsigned mode, void *p) if (enqueue_dribble(dribble_out, make_pkqnode(c->d.buf, c->d.len))) reasons++; } - client_destroy(c); + destroy_client(c); return; } if (c->mode == '?') { @@ -385,7 +394,7 @@ static void do_client_in(int fd, unsigned mode, void *p) break; case '<': w = CREATE(waiter); - w->g = make_gobbler(fd); + w->g = make_gobbler(fd, 0, 0); w->next = 0; w->fd = fd; *wait_tail = w; @@ -396,7 +405,7 @@ static void do_client_in(int fd, unsigned mode, void *p) return; default: moan("bad client mode `%c'", buf[0]); - client_destroy(c); + destroy_client(c); return; } } @@ -442,7 +451,7 @@ static void do_accept(int fd, unsigned mode, void *hunoz) /*----- Main daemon -------------------------------------------------------*/ -static void done_slip_dribble(dribbler *d, int err, void *p) +static void done_slip_dribble(dribbler *d, int err, void *hunoz) { if (!err) reasons--; @@ -671,9 +680,163 @@ static void shovel(int from, int to) static void put(void) { shovel(STDIN_FILENO, make_sock('>')); } static void get(void) { shovel(make_sock('<'), STDOUT_FILENO); } +/*----- Flooding and sinking ----------------------------------------------*/ + +/* --- Connection jobs --- */ + +typedef struct conninfo { + struct conninfo *next; + conn c; +} conninfo; + +#define MAXCONN 32 +static conninfo conns[MAXCONN], *freeconns; + +static void (*connhook)(int, conninfo *); + +static void connrecycle(conninfo *c) { c->next = freeconns; freeconns = c; } + +static void connerr(conninfo *c) +{ + if (errno == EAGAIN) connrecycle(c); + else die(EXIT_FAILURE, "connect: %s", strerror(errno)); +} + +static void connected(int fd, void *p) + { if (fd == -1) connerr(p); else connhook(fd, p); } + +static void conndoconnect(conninfo *c) +{ + int fd; + struct sockaddr_un sun; + size_t sz; + + if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) + die(EXIT_FAILURE, "socket: %s", strerror(errno)); + socketaddr(&sun, &sz); + if (conn_init(&c->c, &sel, fd, (struct sockaddr *)&sun, sz, connected, c)) + connerr(c); +} + +static int timerflag; + +static void conndrip(struct timeval *tv, void *hunoz) +{ + conninfo *c, *cc; + + timerflag = 0; + while (freeconns) { + c = freeconns; freeconns = 0; + while (c) { cc = c->next; conndoconnect(c); c = cc; } + } +} + +static void connloop(void (*connected)(int, conninfo *)) +{ + int i; + conninfo **cc; + sel_timer t; + struct timeval tv; + + connhook = connected; + for (i = 0, cc = &freeconns; i < MAXCONN; i++) { + *cc = &conns[i]; + cc = &conns[i].next; + } + *cc = 0; + + for (;;) { + if (freeconns && !timerflag) { + gettimeofday(&tv, 0); + TV_ADDL(&tv, &tv, 0, 10000); + sel_addtimer(&sel, &t, &tv, conndrip, 0); + timerflag = 1; + } + if (sel_select(&sel)) + die(EXIT_FAILURE, "select: %s", strerror(errno)); + } +} + +/* --- Sinking (glug glug) --- */ + +static void close_sink(gobbler *g, int err, void *p) +{ + static char baton[4] = "/-\\|"; + static int pos = 0; + static int count = 0; + static int state = '?'; + + conninfo *c = p; + + if (!err) { + if (state == '?') + state = isatty(STDOUT_FILENO) ? 'y' : 'n'; + if (state == 'y') { + if (count) count--; + else { + putchar(baton[pos]); + putchar('\b'); + fflush(stdout); + pos++; + if (pos >= sizeof(baton)) pos = 0; + count = 128; + } + } + } + connrecycle(c); +} + +static void sink_connected(int fd, conninfo *c) +{ + char dir = '<'; + + if (write(fd, &dir, 1) != 1) { + moan("write: %s (continuing)", strerror(errno)); + close(fd); + connrecycle(c); + return; + } + make_gobbler(fd, close_sink, c); +} + +static void sink(void) { connloop(sink_connected); } + +/* --- Flooding --- */ + +static void close_flood(dribbler *d, int err, void *p) +{ + conninfo *c = p; + + if (err) moan("write: %s (continuing)\n", strerror(errno)); + destroy_dribbler(d); + connrecycle(c); +} + +static void flood_connected(int fd, conninfo *c) +{ + static uint32 seq; + + dribbler *d; + pkq_node *pn; + int i; + +#define FLOOD_PKSZ 1024 + + pn = make_pkqnode(0, 1 + FLOOD_PKSZ); + pn->buf[0] = '>'; + STORE32(pn->buf + 1, seq); + for (i = 4; i < FLOOD_PKSZ; i++) + pn->buf[i + 1] = i & 0xff; + seq++; + d = make_dribbler(fd, close_flood, c); + enqueue_dribble(d, pn); +} + +static void flood(void) { connloop(flood_connected); } + /*----- Main code ---------------------------------------------------------*/ -static void usage(FILE *fp) { pquis(fp, "Usage: $ [-pg] SOCKET\n"); } +static void usage(FILE *fp) { pquis(fp, "Usage: $ [-fgps] SOCKET\n"); } static void version(void) { pquis(stdout, "$ (" PACKAGE " version " VERSION")\n"); } @@ -687,8 +850,10 @@ static void help(void) With no options, provides a SLIP interface for TrIPE.\n\ \n\ Options:\n\ + -f, --flood Send packets to TrIPE as fast as possible.\n\ + -g, --get Receive packet from TrIPE and write to stdout.\n\ -p, --put Send packet on stdin to TrIPE.\n\ - -g, --get Receive packet from TrIPE and write to stdout."); + -s, --sink Slurp packets out of TrIPE and display progress."); } int main(int argc, char *argv[]) @@ -703,15 +868,17 @@ int main(int argc, char *argv[]) { "version", 0, 0, 'v' }, { "put", 0, 0, 'p' }, { "get", 0, 0, 'g' }, + { "flood", 0, 0, 'f' }, + { "sink", 0, 0, 's' }, { 0, 0, 0, 0 } }; - i = mdwopt(argc, argv, "hvpg", opt, 0, 0, 0); + i = mdwopt(argc, argv, "hvpgfs", opt, 0, 0, 0); if (i < 0) break; switch (i) { case 'h': help(); return (0); case 'v': version(); return (0); - case 'p': case 'g': mode = i; break; + case 'p': case 'g': case 's': case 'f': mode = i; break; default: usage(stderr); exit(EXIT_FAILURE); break; } } @@ -722,6 +889,8 @@ int main(int argc, char *argv[]) case 'd': slipif(); break; case 'p': put(); break; case 'g': get(); break; + case 'f': flood(); break; + case 's': sink(); break; } return (0); }