1 /*
2 * Copyright (C) 2007, 2008 Ted Bullock <tbullock@comlore.com>
3 *
4 * This file is part of httperf, a web server performance measurment tool.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 *
11 * In addition, as a special exception, the copyright holders give permission
12 * to link the code of this work with the OpenSSL project's "OpenSSL" library
13 * (or with modified versions of it that use the same license as the "OpenSSL"
14 * library), and distribute linked combinations including the two. You must
15 * obey the GNU General Public License in all respects for all of the code
16 * used other than "OpenSSL". If you modify this file, you may extend this
17 * exception to your version of the file, but you are not obligated to do so.
18 * If you do not wish to do so, delete this exception statement from your
19 * version.
20 *
21 * This program is distributed in the hope that it will be useful, but WITHOUT
22 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
24 * more details.
25 *
26 * You should have received a copy of the GNU General Public License along
27 * with this program; if not, write to the Free Software Foundation, Inc., 51
28 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
29 */
30
31 #include "config.h"
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h> /* For strrchr() */
36 #include <signal.h>
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <unistd.h>
41 #include <inttypes.h>
42 #include <sys/time.h>
43
44 #include <generic_types.h>
45 #include <event.h>
46 #include <evdns.h>
47
48 #include <list.h>
49
50 /*
51 * Events allocated onto the heap
52 */
53 static struct List *active_events = NULL;
54
55 static const char *prog_name = NULL;
56 static unsigned long num_conn = 0, num_closed = 0;
57 static struct timeval start_time;
58 static struct sockaddr_in server_addr;
59
60 static char *server = NULL;
61 static int desired = 0; /* Number of desired connections */
62 static int port = 0;
63
64 /*
65 * Frees the linked list of active event structures
66 */
67 static void
cleanup()68 cleanup()
69 {
70 if (active_events != NULL) {
71 while (!is_list_empty(active_events)) {
72 Any_Type a = list_pop(active_events);
73 struct event *evsock = (struct event *) a.vp;
74 event_del(evsock);
75 free(a.vp);
76 }
77 list_free(active_events);
78 }
79 }
80
81 /*
82 * Signal handler callback to be executed by event_dispatch upon receipt of
83 * SIGINT (usually Control-C
84 */
85 void
sigint_exit(int fd,short event,void * arg)86 sigint_exit(int fd, short event, void *arg)
87 {
88 struct event *signal_int = arg;
89 struct timeval stop_time;
90 double delta_t = 0;
91
92 gettimeofday(&stop_time, NULL);
93
94 delta_t = ((stop_time.tv_sec - start_time.tv_sec)
95 + 1e-6 * (stop_time.tv_usec - start_time.tv_usec));
96
97 printf("%s: Total conns created = %lu; close() rate = %g conn/sec\n",
98 prog_name, num_conn, num_closed / delta_t);
99
100 #ifdef DEBUG
101 printf("%s: caught SIGINT... Exiting.\n", __func__);
102 #endif /* DEBUG */
103
104 evdns_shutdown(0);
105 event_del(signal_int);
106
107 cleanup();
108 }
109
110 /*
111 * Connection disconnect handler. Once a connection is dropped by the remote
112 * host, this function is executed and a new connection is established.
113 *
114 * Note, that this re-uses the event structure originally allocated in
115 * dns_lookup_callback
116 */
117 void
reconnect(int sd,short event,void * arg)118 reconnect(int sd, short event, void *arg)
119 {
120 struct sockaddr_in sin = server_addr;
121 struct event *evsock = (struct event *) arg;
122
123 close(sd);
124 num_closed++;
125
126 sd = socket(AF_INET, SOCK_STREAM, 0);
127
128 if (sd == -1) {
129 perror("socket");
130 exit(EXIT_FAILURE);
131 }
132
133 if (connect(sd, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
134 perror("connect");
135 cleanup();
136 exit(EXIT_FAILURE);
137 }
138
139 event_set(evsock, sd, EV_READ, reconnect, evsock);
140 event_add(evsock, NULL);
141 num_conn++;
142
143 }
144
145 /*
146 * For the explanation of these parameters, please refer to the libevent evdns
147 * callback API
148 *
149 * Upon receipt of a valid dns lookup result, attempts to open `desired`
150 * connections and allocates memory for the associated event structures
151 */
152 void
dns_lookup_callback(int result,char type,int count,int ttl,void * addresses,void * arg)153 dns_lookup_callback(int result, char type, int count, int ttl, void *addresses,
154 void *arg)
155 {
156 uint32_t i;
157 uint8_t oct[4];
158 struct in_addr *address_list = (struct in_addr *) addresses;
159
160 if (result != DNS_ERR_NONE) {
161 printf("DNS Lookup: result(%s)\n",
162 evdns_err_to_string(result));
163 exit(EXIT_FAILURE);
164 }
165
166 memset(&server_addr, 0, sizeof(server_addr));
167 server_addr.sin_family = AF_INET;
168 server_addr.sin_port = htons(port);
169 server_addr.sin_addr = address_list[0];
170
171 /*
172 * Echo the resolved address
173 */
174 printf("Resolved %s\n\t(%s)\n", server, inet_ntoa(address_list[0]));
175
176 /*
177 * Open the number of `desired` connections
178 */
179 for (i = 0; i < desired; i++) {
180 struct sockaddr_in sin;
181 int sd = socket(AF_INET, SOCK_STREAM, 0);
182
183 struct event *evsock = NULL;
184
185 if (sd == -1) {
186 perror("socket");
187 continue;
188 }
189
190 sin = server_addr;
191
192 if (connect(sd, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
193 perror("connect");
194 cleanup();
195 exit(EXIT_FAILURE);
196 }
197
198 evsock = (struct event *) malloc(sizeof(struct event));
199
200 if (evsock == NULL) {
201 cleanup();
202 exit(EXIT_FAILURE);
203 }
204
205 event_set(evsock, sd, EV_READ, reconnect, evsock);
206 list_push(active_events, (Any_Type) (void *) evsock);
207 num_conn++;
208
209 event_add(evsock, NULL);
210
211 }
212 }
213
214 int
main(int argc,char * argv[])215 main(int argc, char *argv[])
216 {
217 struct event signal_int;
218
219 active_events = list_create();
220 if (active_events == NULL)
221 goto init_failure;
222
223 event_init();
224 evdns_init();
225
226 /*
227 * Initalize one event
228 */
229 event_set(&signal_int, SIGINT, EV_SIGNAL, sigint_exit, &signal_int);
230
231 event_add(&signal_int, NULL);
232
233 prog_name = strrchr(argv[0], '/');
234 if (prog_name)
235 ++prog_name;
236 else
237 prog_name = argv[0];
238
239 if (argc != 4) {
240 fprintf(stderr, "Usage: `%s server port numidle'\n",
241 prog_name);
242 goto init_failure;
243 }
244
245 server = argv[1];
246 port = atoi(argv[2]);
247 desired = atoi(argv[3]);
248
249 printf("%s: Using libevent-%s for %s event notification system.\n"
250 "Control-c to exit\n\n", prog_name, event_get_version(),
251 event_get_method());
252
253 gettimeofday(&start_time, NULL);
254
255 evdns_resolve_ipv4(server, 0, dns_lookup_callback, NULL);
256
257 event_dispatch();
258
259 /*
260 * Event loop will only exit upon receiving SIGINT. Make sure we pass
261 * this on to the parent process
262 */
263 if (signal(SIGINT, SIG_DFL) < 0)
264 perror("signal");
265 else
266 kill(getpid(), SIGINT);
267
268 init_failure:
269 cleanup();
270 exit(EXIT_FAILURE);
271
272 /*
273 * Should never reach here
274 */
275 return 0;
276 }
277