test-fdleak.c revision 1.4
1/* $NetBSD: test-fdleak.c,v 1.4 2015/07/10 14:20:35 christos Exp $ */ 2 3/* 4 * Copyright (c) 2012 Ross Lagerwall <rosslagerwall@gmail.com> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include "event2/event-config.h" 30 31#ifdef _WIN32 32#define WIN32_LEAN_AND_MEAN 33#include <windows.h> 34#endif 35#include <string.h> 36#include <stdlib.h> 37#include <errno.h> 38#ifdef EVENT__HAVE_SYS_TIME_H 39#include <sys/time.h> 40#endif 41#ifdef EVENT__HAVE_SYS_RESOURCE_H 42#include <sys/resource.h> 43#endif 44#ifdef EVENT__HAVE_NETINET_IN_H 45#include <netinet/in.h> 46#endif 47 48#include "event2/event.h" 49#include "event2/bufferevent.h" 50#include "event2/buffer.h" 51#include "event2/listener.h" 52 53/* Number of requests to make. Setting this too high might result in the machine 54 running out of ephemeral ports */ 55#ifdef _WIN32 56#define MAX_REQUESTS 1000 57#else 58#define MAX_REQUESTS 4000 59#endif 60 61/* Provide storage for the address, both for the server & the clients */ 62static struct sockaddr_in saddr; 63 64/* Number of sucessful requests so far */ 65static int num_requests; 66 67static void start_client(struct event_base *base); 68 69static void 70my_perror(const char *s) 71{ 72 fprintf(stderr, "%s: %s", 73 s, evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR())); 74} 75 76/* 77=============================================== 78Server functions 79=============================================== 80*/ 81 82/* Read a byte from the client and write it back */ 83static void 84server_read_cb(struct bufferevent *bev, void *ctx) 85{ 86 while (evbuffer_get_length(bufferevent_get_input(bev))) { 87 unsigned char tmp; 88 bufferevent_read(bev, &tmp, 1); 89 bufferevent_write(bev, &tmp, 1); 90 } 91} 92 93/* Wait for an EOF and then free the bufferevent */ 94static void 95server_event_cb(struct bufferevent *bev, short events, void *ctx) 96{ 97 if (events & BEV_EVENT_ERROR) { 98 my_perror("Error from bufferevent"); 99 exit(1); 100 } else if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) { 101 bufferevent_free(bev); 102 } 103} 104 105/* Accept a client socket and set it up to for reading & writing */ 106static void 107listener_accept_cb(struct evconnlistener *listener, evutil_socket_t sock, 108 struct sockaddr *addr, int len, void *ptr) 109{ 110 struct event_base *base = evconnlistener_get_base(listener); 111 struct bufferevent *bev = bufferevent_socket_new(base, sock, 112 BEV_OPT_CLOSE_ON_FREE); 113 114 bufferevent_setcb(bev, server_read_cb, NULL, server_event_cb, NULL); 115 bufferevent_enable(bev, EV_READ|EV_WRITE); 116} 117 118/* Start the server listening on a random port and start the first client. */ 119static void 120start_loop(void) 121{ 122 struct event_base *base; 123 struct evconnlistener *listener; 124 struct sockaddr_storage ss; 125 ev_socklen_t socklen = sizeof(ss); 126 evutil_socket_t fd; 127 128 base = event_base_new(); 129 if (base == NULL) { 130 puts("Could not open event base!"); 131 exit(1); 132 } 133 134 listener = evconnlistener_new_bind(base, listener_accept_cb, NULL, 135 LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, 136 -1, (struct sockaddr *)&saddr, sizeof(saddr)); 137 if (listener == NULL) { 138 my_perror("Could not create listener!"); 139 exit(1); 140 } 141 fd = evconnlistener_get_fd(listener); 142 if (fd < 0) { 143 puts("Couldn't get fd from listener"); 144 exit(1); 145 } 146 if (getsockname(fd, (struct sockaddr *)&ss, &socklen) < 0) { 147 my_perror("getsockname()"); 148 exit(1); 149 } 150 memcpy(&saddr, &ss, sizeof(saddr)); 151 if (saddr.sin_family != AF_INET) { 152 puts("AF mismatch from getsockname()."); 153 exit(1); 154 } 155 156 start_client(base); 157 158 event_base_dispatch(base); 159} 160 161/* 162=============================================== 163Client functions 164=============================================== 165*/ 166 167/* Check that the server sends back the same byte that the client sent. 168 If MAX_REQUESTS have been reached, exit. Otherwise, start another client. */ 169static void 170client_read_cb(struct bufferevent *bev, void *ctx) 171{ 172 unsigned char tmp; 173 struct event_base *base = bufferevent_get_base(bev); 174 175 bufferevent_read(bev, &tmp, 1); 176 if (tmp != 'A') { 177 puts("Incorrect data received!"); 178 exit(2); 179 } 180 bufferevent_free(bev); 181 182 num_requests++; 183 if (num_requests == MAX_REQUESTS) { 184 event_base_loopbreak(base); 185 } else { 186 start_client(base); 187 } 188} 189 190/* Send a byte to the server. */ 191static void 192client_event_cb(struct bufferevent *bev, short events, void *ctx) 193{ 194 if (events & BEV_EVENT_CONNECTED) { 195 unsigned char tmp = 'A'; 196 bufferevent_write(bev, &tmp, 1); 197 } else if (events & BEV_EVENT_ERROR) { 198 puts("Client socket got error!"); 199 exit(2); 200 } 201 202 bufferevent_enable(bev, EV_READ); 203} 204 205/* Open a client socket to connect to localhost on sin */ 206static void 207start_client(struct event_base *base) 208{ 209 struct bufferevent *bev = bufferevent_socket_new(base, -1, 210 BEV_OPT_CLOSE_ON_FREE); 211 bufferevent_setcb(bev, client_read_cb, NULL, client_event_cb, NULL); 212 213 if (bufferevent_socket_connect(bev, (struct sockaddr *)&saddr, 214 sizeof(saddr)) < 0) { 215 my_perror("Could not connect!"); 216 bufferevent_free(bev); 217 exit(2); 218 } 219} 220 221int 222main(int argc, char **argv) 223{ 224#ifdef EVENT__HAVE_SETRLIMIT 225 /* Set the fd limit to a low value so that any fd leak is caught without 226 making many requests. */ 227 struct rlimit rl; 228 rl.rlim_cur = rl.rlim_max = 20; 229 if (setrlimit(RLIMIT_NOFILE, &rl) == -1) { 230 my_perror("setrlimit"); 231 exit(3); 232 } 233#endif 234 235#ifdef _WIN32 236 WSADATA WSAData; 237 WSAStartup(0x101, &WSAData); 238#endif 239 240 /* Set up an address, used by both client & server. */ 241 memset(&saddr, 0, sizeof(saddr)); 242 saddr.sin_family = AF_INET; 243 saddr.sin_addr.s_addr = htonl(0x7f000001); 244 saddr.sin_port = 0; /* Tell the implementation to pick a port. */ 245 246 start_loop(); 247 248 return 0; 249} 250 251/* XXX why does this test cause so much latency sometimes (OSX 10.5)? */ 252