Version bump.
[fwd] / blast.c
CommitLineData
a8ceabf6 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
24typedef struct blast {
25 conn c;
26 sel_timer t;
27} blast;
28
29static struct sockaddr_in sin;
30static sel_state sel;
31static struct timeval ctv = { 0, 500000 };
32static sel_timer sec;
33static unsigned count = 0;
34
35static void timers(void);
36
37static 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
51static void newconn(blast *b);
52
53static void retry(struct timeval *tv, void *p)
54{
55 blast *b = p;
56 newconn(b);
57}
58
59static 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
69static 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);
a8ceabf6 78 }
0d3d364b 79 newconn(b);
a8ceabf6 80}
81
82static void timeout(struct timeval *tv, void *p)
83{
84 blast *b = p;
85 conn_kill(&b->c);
86 newconn(b);
87}
88
89static 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
105static 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);
0d3d364b 113 if (conn_init(&b->c, &sel, fd, (struct sockaddr *)&sin, sizeof(sin),
114 connected, b))
115 goto fail;
a8ceabf6 116 sel_addtimer(&sel, &b->t, &tv, timeout, b);
a8ceabf6 117 return;
118
119fail:
120 backoff(b);
121}
122
123int 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}