+/*----- 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); }
+