chiark / gitweb /
Track @lbuf@ changes in mLib.
[fwd] / blast.c
1 #include <assert.h>
2 #include <ctype.h>
3 #include <errno.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 #include <sys/types.h>
9 #include <sys/time.h>
10 #include <unistd.h>
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 #include <netdb.h>
15
16 #include <mLib/alloc.h>
17 #include <mLib/conn.h>
18 #include <mLib/mdwopt.h>
19 #include <mLib/quis.h>
20 #include <mLib/report.h>
21 #include <mLib/sel.h>
22 #include <mLib/tv.h>
23
24 typedef struct blast {
25   conn c;
26   sel_timer t;
27 } blast;
28
29 static struct sockaddr_in sin;
30 static sel_state sel;
31 static struct timeval ctv = { 0, 500000 };
32 static sel_timer sec;
33 static unsigned count = 0;
34
35 static void timers(void);
36
37 static void stats(struct timeval *tv, void *p)
38 {
39   static char baton[5] = "/-\\|";
40   static char *bt = baton;
41   struct timeval ttv;
42   printf("\r%c %u connections/second", *bt++, count);
43   if (!*bt)
44     bt = baton;
45   fflush(stdout);
46   count = 0;
47   TV_ADDL(&ttv, tv, 1, 0);
48   sel_addtimer(&sel, &sec, &ttv, stats, p);
49 }
50
51 static void newconn(blast *b);
52
53 static void retry(struct timeval *tv, void *p)
54 {
55   blast *b = p;
56   newconn(b);
57 }
58
59 static void backoff(blast *b)
60 {
61   struct timeval tv;
62   unsigned r = rand();
63   double q = RAND_MAX / 5;
64   gettimeofday(&tv, 0);
65   TV_ADDL(&tv, &tv, r / q, (r % (unsigned)q) * (MILLION / q));
66   sel_addtimer(&sel, &b->t, &tv, retry, b);
67 }
68
69 static void connected(int fd, void *p)
70 {
71   blast *b = p;
72   sel_rmtimer(&b->t);
73   if (fd == -1)
74     backoff(b);
75   else {
76     count++;
77     close(fd);
78   }
79   newconn(b);
80 }
81
82 static void timeout(struct timeval *tv, void *p)
83 {
84   blast *b = p;
85   conn_kill(&b->c);
86   newconn(b);
87 }
88
89 static void timers(void)
90 {
91   struct tab { void (*func)(struct timeval *tv, void *p); const char *name; }
92   tab[] = { { retry, "retry" }, {timeout, "timeout"}, { stats, "stats" }, { 0,
93   0 }};
94   sel_timer *t = sel.timers;
95   while (t) {
96     struct tab *q; for (q = tab; q->func != t->func; q++) ;
97     assert(t->prev->next == t);
98     printf("%i.%06i  %p  %s\n", t->tv.tv_sec, t->tv.tv_usec, t->p, q->name);
99     assert(t != t->next);
100     t = t->next;
101   }
102   puts("");
103 }
104     
105 static void newconn(blast *b)
106 {
107   int fd = socket(PF_INET, SOCK_STREAM, 0);
108   struct timeval tv;
109   if (fd < 0)
110     goto fail;
111   gettimeofday(&tv, 0);
112   TV_ADD(&tv, &tv, &ctv);
113   if (conn_init(&b->c, &sel, fd, (struct sockaddr *)&sin, sizeof(sin),
114                 connected, b))
115     goto fail;
116   sel_addtimer(&sel, &b->t, &tv, timeout, b);
117   return;
118
119 fail:
120   backoff(b);
121 }
122
123 int main(int argc, char *argv[])
124 {
125   blast *b;
126   size_t n = 256;
127
128   ego(argv[0]);
129
130   for (;;) {
131     int i = getopt(argc, argv, "t:n:");
132     if (i < 0)
133       break;
134     switch (i) {
135       case 't': {
136         double t = strtod(optarg, 0);
137         double s = modf(t, &t);
138         ctv.tv_sec = t;
139         ctv.tv_usec = s * MILLION;
140       } break;
141       case 'n':
142         n = atoi(optarg);
143         break;
144       default:
145         exit(1);
146     }
147   }
148
149   argv += optind;
150   argc -= optind;
151   if (argc != 2) {
152     pquis(stderr, "Usage: $ [-t time] [-n count] host port\n");
153     exit(1);
154   }
155
156   sel_init(&sel);
157   sin.sin_family = AF_INET;
158
159   {
160     struct hostent *h = gethostbyname(argv[0]);
161     if (!h)
162       die(1, "bad hostname `%s'", argv[0]);
163     memcpy(&sin.sin_addr, h->h_addr, sizeof(struct in_addr));
164   }
165
166   if (isdigit((unsigned char)argv[1][0]))
167     sin.sin_port = htons(atoi(argv[1]));
168   else {
169     struct servent *s = getservbyname(argv[1], "tcp");
170     if (!s)
171       die(1, "bad service name `%s'", argv[1]);
172     sin.sin_port = s->s_port;
173   }
174
175   b = xmalloc(n * sizeof(blast));
176
177   {
178     int i;
179     for (i = 0; i < n; i++)
180       newconn(&b[i]);
181   }
182
183   {
184     struct timeval tv;
185     gettimeofday(&tv, 0);
186     tv.tv_sec++;
187     sel_addtimer(&sel, &sec, &tv, stats, 0);
188   }
189
190   for (;;) {
191     sel_select(&sel);
192   }
193 }