X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/tripe/blobdiff_plain/073cc23b4f8d5e9c4d74e9a15a6c0f13a55cf4e6..1e14551ab904b85db4e1f11c1a37c18ed0a0ac58:/uslip/uslip.c diff --git a/uslip/uslip.c b/uslip/uslip.c index 245f1ec0..74a5b17e 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 { @@ -134,12 +139,11 @@ static pkq_node *make_pkqnode(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,8 +191,8 @@ 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) @@ -209,21 +212,27 @@ static void do_gobble_in(int fd, unsigned mode, void *p) break; else { moan("read (gobble): %s", strerror(errno)); + if (g->done) g->done(g, errno, g->p); gobbler_close(g); break; } } else if (n == 0) { + if (g->done) g->done(g, 0, g->p); gobbler_close(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); @@ -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; @@ -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 *p) +{ + 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 sink_close(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, sink_close, c); +} + +static void sink(void) { connloop(sink_connected); } + +/* --- Flooding --- */ + +static void flood_close(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, flood_close, 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); }