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