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