Home | History | Annotate | Download | only in examples
      1 /*
      2  *  EpollTest by Davide Libenzi ( Epoll functionality tester )
      3  *  Copyright (C) 2003  Davide Libenzi
      4  *
      5  *  This program is free software; you can redistribute it and/or modify
      6  *  it under the terms of the GNU General Public License as published by
      7  *  the Free Software Foundation; either version 2 of the License, or
      8  *  (at your option) any later version.
      9  *
     10  *  This program is distributed in the hope that it will be useful,
     11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  *  GNU General Public License for more details.
     14  *
     15  *  You should have received a copy of the GNU General Public License
     16  *  along with this program; if not, write to the Free Software
     17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     18  *
     19  *  Davide Libenzi <davidel (at) xmailserver.org>
     20  *
     21  */
     22 
     23 #include <stdio.h>
     24 #include <stdlib.h>
     25 #include <unistd.h>
     26 #include <unistd.h>
     27 #include <fcntl.h>
     28 #include <stdarg.h>
     29 #include <string.h>
     30 #include <assert.h>
     31 #include <limits.h>
     32 #include <ctype.h>
     33 #include <time.h>
     34 #include <errno.h>
     35 #include <signal.h>
     36 #include <sys/types.h>
     37 #include <sys/stat.h>
     38 #include <sys/time.h>
     39 #include <sys/socket.h>
     40 #include <sched.h>
     41 #include <sys/file.h>
     42 #include <sys/ioctl.h>
     43 #include <sys/mman.h>
     44 #include <sys/select.h>
     45 #include <sys/wait.h>
     46 #include <netinet/in.h>
     47 #include <netinet/tcp.h>
     48 #include <arpa/inet.h>
     49 #include <arpa/nameser.h>
     50 #include <netdb.h>
     51 #include <syslog.h>
     52 #include <glob.h>
     53 #include <semaphore.h>
     54 
     55 /*
     56  * You need the Portable Coroutine Library (PCL) to build this source.
     57  * You can find a copy of PCL source code at :
     58  *
     59  *             http://www.xmailserver.org/libpcl.html
     60  */
     61 #include <pcl.h>
     62 
     63 #include "epoll.h"
     64 #include "dbllist.h"
     65 
     66 #define CO_STD_STACK_SIZE		(2 * 4096)
     67 #define STD_SCHED_TIMEOUT		1000
     68 /* you might need to increase "net.ipv4.tcp_max_syn_backlog" to use this value */
     69 #define STD_LISTEN_SIZE			2048
     70 #define DATA_BUFFER_SIZE		2048
     71 #define MIN_AHEAD_SPACE			(DATA_BUFFER_SIZE / 12)
     72 #define STD_MESSAGE_SIZE		128
     73 #define STD_SERVER_PORT			8080
     74 #define MAX_DEFAULT_FDS			20000
     75 
     76 struct eph_conn {
     77 	struct list_head lnk;
     78 	int sfd;
     79 	unsigned int events, revents;
     80 	coroutine_t co;
     81 	int nbytes, rindex;
     82 	char buffer[DATA_BUFFER_SIZE];
     83 };
     84 
     85 static int kdpfd;
     86 static struct list_head close_list;
     87 static struct epoll_event *events;
     88 static int maxfds, numfds = 0;
     89 static int chash_size;
     90 static struct list_head *chash;
     91 static int msgsize = STD_MESSAGE_SIZE, port = STD_SERVER_PORT,
     92     maxsfd = MAX_DEFAULT_FDS, stksize = CO_STD_STACK_SIZE;
     93 struct sockaddr_in saddr;
     94 static volatile unsigned long httpresp = 0;
     95 static int nreqsess = 1;
     96 static char httpreq[512] = "";
     97 
     98 int eph_socket(int domain, int type, int protocol)
     99 {
    100 	int sfd = socket(domain, type, protocol), flags = 1;
    101 
    102 	if (sfd == -1)
    103 		return -1;
    104 	if (ioctl(sfd, FIONBIO, &flags) &&
    105 	    ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||
    106 	     fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0)) {
    107 		close(sfd);
    108 		return -1;
    109 	}
    110 	return sfd;
    111 }
    112 
    113 int eph_close(int sfd)
    114 {
    115 	close(sfd);
    116 	return 0;
    117 }
    118 
    119 static int eph_new_conn(int sfd, void *func)
    120 {
    121 	struct eph_conn *conn = malloc(sizeof(struct eph_conn));
    122 	struct epoll_event ev;
    123 
    124 	if (!conn)
    125 		return -1;
    126 
    127 	memset(conn, 0, sizeof(*conn));
    128 	DBL_INIT_LIST_HEAD(&conn->lnk);
    129 	conn->sfd = sfd;
    130 	conn->events = 0;
    131 	conn->revents = 0;
    132 	conn->nbytes = conn->rindex = 0;
    133 	if (!(conn->co = co_create(func, conn, NULL, stksize))) {
    134 		free(conn);
    135 		return -1;
    136 	}
    137 
    138 	DBL_LIST_ADDT(&conn->lnk, &chash[sfd % chash_size]);
    139 
    140 	ev.events = 0;
    141 	ev.data.ptr = conn;
    142 	if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, sfd, &ev) < 0) {
    143 		fprintf(stderr, "epoll set insertion error: fd=%d\n", sfd);
    144 
    145 		DBL_LIST_DEL(&conn->lnk);
    146 		co_delete(conn->co);
    147 		free(conn);
    148 		return -1;
    149 	}
    150 
    151 	++numfds;
    152 
    153 	co_call(conn->co);
    154 
    155 	return 0;
    156 }
    157 
    158 static void eph_exit_conn(struct eph_conn *conn)
    159 {
    160 	struct epoll_event ev;
    161 
    162 	if (epoll_ctl(kdpfd, EPOLL_CTL_DEL, conn->sfd, &ev) < 0) {
    163 		fprintf(stderr, "epoll set deletion error: fd=%d\n", conn->sfd);
    164 
    165 	}
    166 
    167 	DBL_LIST_DEL(&conn->lnk);
    168 	DBL_LIST_ADDT(&conn->lnk, &close_list);
    169 
    170 	eph_close(conn->sfd);
    171 	conn->sfd = -1;
    172 
    173 	--numfds;
    174 
    175 	co_exit();
    176 }
    177 
    178 static void eph_free_conns(void)
    179 {
    180 	struct eph_conn *conn;
    181 
    182 	while (!DBL_LIST_EMTPY(&close_list)) {
    183 		conn = DBL_LIST_ENTRY(close_list.pNext, struct eph_conn, lnk);
    184 
    185 		DBL_LIST_DEL(&conn->lnk);
    186 		free(conn);
    187 	}
    188 }
    189 
    190 static int eph_mod_conn(struct eph_conn *conn, unsigned int events)
    191 {
    192 	struct epoll_event ev;
    193 
    194 	ev.events = events;
    195 	ev.data.ptr = conn;
    196 	if (epoll_ctl(kdpfd, EPOLL_CTL_MOD, conn->sfd, &ev) < 0) {
    197 		fprintf(stderr, "epoll set modify error: fd=%d\n", conn->sfd);
    198 		return -1;
    199 	}
    200 	return 0;
    201 }
    202 
    203 int eph_connect(struct eph_conn *conn, const struct sockaddr *serv_addr,
    204 		socklen_t addrlen)
    205 {
    206 
    207 	if (connect(conn->sfd, serv_addr, addrlen) == -1) {
    208 		if (errno != EWOULDBLOCK && errno != EINPROGRESS)
    209 			return -1;
    210 		if (!(conn->events & EPOLLOUT)) {
    211 			conn->events = EPOLLOUT | EPOLLERR | EPOLLHUP;
    212 			if (eph_mod_conn(conn, conn->events) < 0)
    213 				return -1;
    214 		}
    215 		co_resume();
    216 		if (conn->revents & (EPOLLERR | EPOLLHUP))
    217 			return -1;
    218 	}
    219 	return 0;
    220 }
    221 
    222 int eph_read(struct eph_conn *conn, char *buf, int nbyte)
    223 {
    224 	int n;
    225 
    226 	while ((n = read(conn->sfd, buf, nbyte)) < 0) {
    227 		if (errno == EINTR)
    228 			continue;
    229 		if (errno != EAGAIN && errno != EWOULDBLOCK)
    230 			return -1;
    231 		if (!(conn->events & EPOLLIN)) {
    232 			conn->events = EPOLLIN | EPOLLERR | EPOLLHUP;
    233 			if (eph_mod_conn(conn, conn->events) < 0)
    234 				return -1;
    235 		}
    236 		co_resume();
    237 	}
    238 	return n;
    239 }
    240 
    241 int eph_write(struct eph_conn *conn, char const *buf, int nbyte)
    242 {
    243 	int n;
    244 
    245 	while ((n = write(conn->sfd, buf, nbyte)) < 0) {
    246 		if (errno == EINTR)
    247 			continue;
    248 		if (errno != EAGAIN && errno != EWOULDBLOCK)
    249 			return -1;
    250 		if (!(conn->events & EPOLLOUT)) {
    251 			conn->events = EPOLLOUT | EPOLLERR | EPOLLHUP;
    252 			if (eph_mod_conn(conn, conn->events) < 0)
    253 				return -1;
    254 		}
    255 		co_resume();
    256 	}
    257 	return n;
    258 }
    259 
    260 int eph_accept(struct eph_conn *conn, struct sockaddr *addr, int *addrlen)
    261 {
    262 	int sfd, flags = 1;
    263 
    264 	while ((sfd = accept(conn->sfd, addr, (socklen_t *) addrlen)) < 0) {
    265 		if (errno == EINTR)
    266 			continue;
    267 		if (errno != EAGAIN && errno != EWOULDBLOCK)
    268 			return -1;
    269 		if (!(conn->events & EPOLLIN)) {
    270 			conn->events = EPOLLIN | EPOLLERR | EPOLLHUP;
    271 			if (eph_mod_conn(conn, conn->events) < 0)
    272 				return -1;
    273 		}
    274 		co_resume();
    275 	}
    276 	if (ioctl(sfd, FIONBIO, &flags) &&
    277 	    ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||
    278 	     fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0)) {
    279 		close(sfd);
    280 		return -1;
    281 	}
    282 	return sfd;
    283 }
    284 
    285 static int eph_create_conn(int domain, int type, int protocol, void *func)
    286 {
    287 	int sfd = eph_socket(domain, type, protocol);
    288 
    289 	return sfd != -1 ? eph_new_conn(sfd, func) : -1;
    290 }
    291 
    292 static int eph_read_data(struct eph_conn *conn)
    293 {
    294 	int nbytes;
    295 
    296 	if (conn->rindex && conn->rindex < conn->nbytes) {
    297 		memmove(conn->buffer, conn->buffer + conn->rindex,
    298 			conn->nbytes - conn->rindex);
    299 		conn->nbytes -= conn->rindex;
    300 	} else
    301 		conn->nbytes = 0;
    302 
    303 	conn->rindex = 0;
    304 
    305 	if ((nbytes = eph_read(conn, conn->buffer + conn->nbytes,
    306 			       sizeof(conn->buffer) - conn->nbytes)) <= 0)
    307 		return -1;
    308 
    309 	conn->nbytes += nbytes;
    310 
    311 	return 0;
    312 }
    313 
    314 static int eph_write_data(struct eph_conn *conn, char const *buf, int nbyte)
    315 {
    316 	int wbytes, wcurr;
    317 
    318 	for (wbytes = 0; wbytes < nbyte;) {
    319 		if ((wcurr = eph_write(conn, buf + wbytes, nbyte - wbytes)) < 0)
    320 			break;
    321 		wbytes += wcurr;
    322 	}
    323 
    324 	return wbytes;
    325 }
    326 
    327 static char *eph_read_line(struct eph_conn *conn)
    328 {
    329 	char *nline, *line;
    330 
    331 	for (;;) {
    332 		if (conn->nbytes > conn->rindex) {
    333 			if ((nline = memchr(conn->buffer + conn->rindex, '\n',
    334 					    conn->nbytes - conn->rindex))) {
    335 				line = conn->buffer + conn->rindex;
    336 				conn->rindex += (nline - line) + 1;
    337 				for (; nline > line && nline[-1] == '\r';
    338 				     nline--) ;
    339 				*nline = '\0';
    340 				return line;
    341 			}
    342 		}
    343 		if (eph_read_data(conn) < 0)
    344 			break;
    345 	}
    346 	return NULL;
    347 }
    348 
    349 static int eph_parse_request(struct eph_conn *conn)
    350 {
    351 	char *line;
    352 
    353 	if (!(line = eph_read_line(conn)))
    354 		return -1;
    355 
    356 	for (;;) {
    357 		if (!(line = eph_read_line(conn)))
    358 			return -1;
    359 
    360 		if (*line == '\0')
    361 			break;
    362 	}
    363 
    364 	return 0;
    365 }
    366 
    367 static int eph_send_response(struct eph_conn *conn)
    368 {
    369 	static int resplen = -1;
    370 	static char *resp = NULL;
    371 
    372 	if (resp == NULL) {
    373 		msgsize = ((msgsize + 63) / 64) * 64;
    374 
    375 		resp = malloc(msgsize + 256);
    376 
    377 		sprintf(resp,
    378 			"HTTP/1.1 200 OK\r\n"
    379 			"Server: dp server\r\n"
    380 			"Content-Type: text/plain\r\n"
    381 			"Content-Length: %d\r\n" "\r\n", msgsize);
    382 
    383 		while (msgsize > 0) {
    384 			strcat(resp,
    385 			       "01234567890123\r\n"
    386 			       "01234567890123\r\n"
    387 			       "01234567890123\r\n" "01234567890123\r\n");
    388 			msgsize -= 64;
    389 		}
    390 
    391 		resplen = strlen(resp);
    392 	}
    393 
    394 	if (eph_write_data(conn, resp, resplen) != resplen)
    395 		return -1;
    396 
    397 	return 0;
    398 }
    399 
    400 static void *eph_httpd(void *data)
    401 {
    402 	struct eph_conn *conn = (struct eph_conn *)data;
    403 
    404 	while (eph_parse_request(conn) == 0) {
    405 		eph_send_response(conn);
    406 
    407 	}
    408 
    409 	eph_exit_conn(conn);
    410 	return data;
    411 }
    412 
    413 static void *eph_acceptor(void *data)
    414 {
    415 	struct eph_conn *conn = (struct eph_conn *)data;
    416 	struct sockaddr_in addr;
    417 	int sfd, addrlen = sizeof(addr);
    418 
    419 	while ((sfd =
    420 		eph_accept(conn, (struct sockaddr *)&addr, &addrlen)) != -1) {
    421 		if (eph_new_conn(sfd, eph_httpd) < 0) {
    422 			eph_close(sfd);
    423 
    424 		}
    425 	}
    426 	eph_exit_conn(conn);
    427 	return data;
    428 }
    429 
    430 static struct eph_conn *eph_find(int sfd)
    431 {
    432 	struct list_head *head = &chash[sfd % chash_size], *lnk;
    433 	struct eph_conn *conn;
    434 
    435 	DBL_LIST_FOR_EACH(lnk, head) {
    436 		conn = DBL_LIST_ENTRY(lnk, struct eph_conn, lnk);
    437 
    438 		if (conn->sfd == sfd)
    439 			return conn;
    440 	}
    441 	return NULL;
    442 }
    443 
    444 static int eph_runqueue(void)
    445 {
    446 	int i;
    447 	struct list_head *head, *lnk;
    448 	struct eph_conn *conn;
    449 
    450 	for (i = 0; i < chash_size; i++) {
    451 		head = &chash[i];
    452 		for (lnk = head->pNext; lnk != head;) {
    453 			conn = DBL_LIST_ENTRY(lnk, struct eph_conn, lnk);
    454 
    455 			lnk = lnk->pNext;
    456 			co_call(conn->co);
    457 		}
    458 	}
    459 	return 0;
    460 }
    461 
    462 unsigned long long eph_mstics(void)
    463 {
    464 
    465 	struct timeval tv;
    466 
    467 	if (gettimeofday(&tv, NULL) != 0)
    468 		return (0);
    469 
    470 	return (1000 * (unsigned long long)tv.tv_sec +
    471 		(unsigned long long)tv.tv_usec / 1000);
    472 
    473 }
    474 
    475 int eph_init(void)
    476 {
    477 	int i;
    478 
    479 	if (!
    480 	    (events = malloc(maxsfd * sizeof(struct epoll_event)))) {
    481 		perror("malloc()");
    482 		return -1;
    483 	}
    484 
    485 	if ((kdpfd = epoll_create(maxsfd)) < 0) {
    486 		perror("epoll_create");
    487 		return -1;
    488 	}
    489 
    490 	if (!
    491 	    (chash = malloc(maxsfd * sizeof(struct list_head)))) {
    492 		perror("malloc()");
    493 		free(events);
    494 		close(kdpfd);
    495 		return -1;
    496 	}
    497 
    498 	maxfds = maxsfd;
    499 	chash_size = maxfds;
    500 	for (i = 0; i < maxfds; i++)
    501 		DBL_INIT_LIST_HEAD(&chash[i]);
    502 
    503 	DBL_INIT_LIST_HEAD(&close_list);
    504 
    505 	return 0;
    506 }
    507 
    508 int eph_cleanup(void)
    509 {
    510 
    511 	free(events);
    512 	free(chash);
    513 	close(kdpfd);
    514 	return 0;
    515 }
    516 
    517 static int eph_scheduler(int loop, unsigned int timeout)
    518 {
    519 	int i, nfds;
    520 	struct eph_conn *conn;
    521 	struct epoll_event *cevents;
    522 
    523 	do {
    524 		nfds = epoll_wait(kdpfd, events, maxfds, timeout);
    525 
    526 		for (i = 0, cevents = events; i < nfds; i++, cevents++) {
    527 			conn = cevents->data.ptr;
    528 			if (conn->sfd != -1) {
    529 				conn->revents = cevents->events;
    530 
    531 				if (conn->revents & conn->events)
    532 					co_call(conn->co);
    533 			}
    534 		}
    535 #if 0
    536 		if (nfds <= 0)
    537 			eph_runqueue();
    538 #endif
    539 		eph_free_conns();
    540 	} while (loop);
    541 
    542 	return 0;
    543 }
    544 
    545 #if defined(DPHTTPD)
    546 
    547 void eph_usage(char const *prgname)
    548 {
    549 
    550 	fprintf(stderr,
    551 		"use: %s [--msgsize nbytes (%d)] [--port nbr (%d)] [--maxfds nfds (%d)]\n\t[--stksize bytes (%d)]\n",
    552 		prgname, msgsize, port, maxsfd, stksize);
    553 
    554 }
    555 
    556 int main(int argc, char *argv[])
    557 {
    558 	int i, sfd, flags = 1;
    559 	struct linger ling = { 0, 0 };
    560 	struct sockaddr_in addr;
    561 
    562 	for (i = 1; i < argc; i++) {
    563 		if (strcmp(argv[i], "--msgsize") == 0) {
    564 			if (++i < argc)
    565 				msgsize = atoi(argv[i]);
    566 			continue;
    567 		}
    568 		if (strcmp(argv[i], "--port") == 0) {
    569 			if (++i < argc)
    570 				port = atoi(argv[i]);
    571 			continue;
    572 		}
    573 		if (strcmp(argv[i], "--maxfds") == 0) {
    574 			if (++i < argc)
    575 				maxsfd = atoi(argv[i]);
    576 			continue;
    577 		}
    578 		if (strcmp(argv[i], "--stksize") == 0) {
    579 			if (++i < argc)
    580 				stksize = atoi(argv[i]);
    581 			continue;
    582 		}
    583 
    584 		eph_usage(argv[0]);
    585 		return 1;
    586 	}
    587 
    588 	if (eph_init() == -1) {
    589 
    590 		return 2;
    591 	}
    592 
    593 	if ((sfd = eph_socket(AF_INET, SOCK_STREAM, 0)) == -1) {
    594 
    595 		eph_cleanup();
    596 		return 3;
    597 	}
    598 
    599 	setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
    600 	setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags));
    601 	setsockopt(sfd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
    602 
    603 	addr.sin_family = AF_INET;
    604 	addr.sin_port = htons(port);
    605 	addr.sin_addr.s_addr = htonl(INADDR_ANY);
    606 	if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
    607 
    608 		eph_close(sfd);
    609 		eph_cleanup();
    610 		return 4;
    611 	}
    612 
    613 	listen(sfd, STD_LISTEN_SIZE);
    614 
    615 	if (eph_new_conn(sfd, (void *)eph_acceptor) == -1) {
    616 
    617 		eph_close(sfd);
    618 		eph_cleanup();
    619 		return 5;
    620 	}
    621 
    622 	do {
    623 		eph_scheduler(0, STD_SCHED_TIMEOUT);
    624 	} while (numfds);
    625 
    626 	eph_cleanup();
    627 	return 0;
    628 }
    629 
    630 #endif /* #if defined(DPHTTPD) */
    631 
    632 #if defined(HTTP_BLASTER)
    633 
    634 static void *eph_http_session(void *data)
    635 {
    636 	int i, rlen = strlen(httpreq), ava;
    637 	struct eph_conn *conn = (struct eph_conn *)data;
    638 
    639 	if (eph_connect(conn, (struct sockaddr *)&saddr, sizeof(saddr)) == 0) {
    640 		for (i = 0; i < nreqsess; i++) {
    641 			if (eph_write_data(conn, httpreq, rlen) == rlen) {
    642 				static char const *clent = "Content-Length:";
    643 				int length = -1, clens = strlen(clent);
    644 				char *line;
    645 				static char buf[2048];
    646 
    647 				while ((line = eph_read_line(conn))) {
    648 					if (*line == '\0')
    649 						break;
    650 					if (strncasecmp(line, clent, clens) ==
    651 					    0) {
    652 						for (line += clens;
    653 						     *line == ' '; line++) ;
    654 						length = atoi(line);
    655 					}
    656 				}
    657 				if (length < 0)
    658 					goto sess_out;
    659 				if ((ava = conn->nbytes - conn->rindex) > 0) {
    660 					if (ava > length)
    661 						ava = length;
    662 					length -= ava;
    663 					conn->rindex += ava;
    664 				}
    665 				++httpresp;
    666 				while (length > 0) {
    667 					int rsiz =
    668 					    length >
    669 					    sizeof(buf) ? sizeof(buf) : length;
    670 
    671 					if ((rsiz =
    672 					     eph_read(conn, buf, rsiz)) <= 0)
    673 						goto sess_out;
    674 					length -= rsiz;
    675 				}
    676 			} else
    677 				goto sess_out;
    678 		}
    679 	}
    680 sess_out:
    681 	eph_exit_conn(conn);
    682 	return data;
    683 }
    684 
    685 void eph_usage(char const *prgname)
    686 {
    687 
    688 	fprintf(stderr,
    689 		"use: %s  --server serv	 --port nprt  --numconns ncon  [--nreq nreq (%d)]\n"
    690 		"[--maxconns ncon] [--url url ('/')] [--stksize bytes (%d)]\n",
    691 		prgname, nreqsess, stksize);
    692 
    693 }
    694 
    695 int main(int argc, char *argv[])
    696 {
    697 	int i, nconns = 0, totconns = 0, maxconns = 0;
    698 	unsigned long resplast;
    699 	unsigned long long tinit, tlast, tcurr;
    700 	struct hostent *he;
    701 	char const *server = NULL, *url = "/";
    702 	struct in_addr inadr;
    703 
    704 	for (i = 1; i < argc; i++) {
    705 		if (strcmp(argv[i], "--server") == 0) {
    706 			if (++i < argc)
    707 				server = argv[i];
    708 			continue;
    709 		}
    710 		if (strcmp(argv[i], "--port") == 0) {
    711 			if (++i < argc)
    712 				port = atoi(argv[i]);
    713 			continue;
    714 		}
    715 		if (strcmp(argv[i], "--maxconns") == 0) {
    716 			if (++i < argc)
    717 				maxconns = atoi(argv[i]);
    718 			continue;
    719 		}
    720 		if (strcmp(argv[i], "--numconns") == 0) {
    721 			if (++i < argc) {
    722 				nconns = atoi(argv[i]);
    723 				if (nconns > maxsfd)
    724 					maxsfd = nconns + nconns >> 1 + 1;
    725 			}
    726 			continue;
    727 		}
    728 		if (strcmp(argv[i], "--nreq") == 0) {
    729 			if (++i < argc)
    730 				nreqsess = atoi(argv[i]);
    731 			continue;
    732 		}
    733 		if (strcmp(argv[i], "--url") == 0) {
    734 			if (++i < argc)
    735 				url = argv[i];
    736 			continue;
    737 		}
    738 		if (strcmp(argv[i], "--stksize") == 0) {
    739 			if (++i < argc)
    740 				stksize = atoi(argv[i]);
    741 			continue;
    742 		}
    743 
    744 		eph_usage(argv[0]);
    745 		return 1;
    746 	}
    747 
    748 	if (!server || !nconns) {
    749 		eph_usage(argv[0]);
    750 		return 2;
    751 	}
    752 
    753 	sprintf(httpreq,
    754 		"GET %s HTTP/1.1\r\n"
    755 		"Host: %s\r\n" "Connection: keepalive\r\n" "\r\n", url, server);
    756 
    757 	if (inet_aton(server, &inadr) == 0) {
    758 		if ((he = gethostbyname(server)) == NULL) {
    759 			fprintf(stderr, "unable to resolve: %s\n", server);
    760 			return (-1);
    761 		}
    762 
    763 		memcpy(&inadr.s_addr, he->h_addr_list[0], he->h_length);
    764 	}
    765 	saddr.sin_family = AF_INET;
    766 	saddr.sin_port = htons(port);
    767 	memcpy(&saddr.sin_addr, &inadr.s_addr, 4);
    768 
    769 	if (eph_init() == -1) {
    770 
    771 		return 2;
    772 	}
    773 
    774 	resplast = 0;
    775 	tinit = tlast = eph_mstics();
    776 
    777 	for (; numfds || (!maxconns || totconns < maxconns);) {
    778 		int nfds = numfds, errs = 0, diffconns = nconns - numfds;
    779 
    780 		while (numfds < nconns && (!maxconns || totconns < maxconns)) {
    781 			eph_create_conn(AF_INET, SOCK_STREAM, 0,
    782 					eph_http_session);
    783 			if (nfds == numfds) {
    784 				++errs;
    785 				if (errs > 32) {
    786 					fprintf(stderr,
    787 						"unable to connect: server=%s errors=%d\n",
    788 						server, errs);
    789 					goto main_exit;
    790 				}
    791 			} else
    792 				++totconns;
    793 			nfds = numfds;
    794 		}
    795 
    796 		eph_scheduler(0, STD_SCHED_TIMEOUT);
    797 
    798 		tcurr = eph_mstics();
    799 		if ((tcurr - tlast) >= 1000) {
    800 			printf
    801 			    ("rate = %lu  avg = %lu  totconns = %d  diff = %d  resp = %ld  nfds = %d\n",
    802 			     (unsigned long)((1000 * (httpresp - resplast)) /
    803 					     (tcurr - tlast)),
    804 			     (unsigned long)((1000 * httpresp) /
    805 					     (tcurr - tinit)), totconns,
    806 			     diffconns, httpresp, numfds);
    807 
    808 			tlast = tcurr;
    809 			resplast = httpresp;
    810 		}
    811 	}
    812 
    813 main_exit:
    814 	eph_cleanup();
    815 	return 0;
    816 }
    817 
    818 #endif /* #if defined(HTTP_BLASTER) */
    819 
    820 #if defined(PIPETESTER)
    821 
    822 int eph_createcgi(char **args, void *func)
    823 {
    824 	int fds[2], flags = 1;
    825 	pid_t chpid;
    826 
    827 	if (pipe(fds)) {
    828 		perror("pipe");
    829 		return -1;
    830 	}
    831 	chpid = fork();
    832 	if (chpid == -1) {
    833 		perror("fork");
    834 		close(fds[0]), close(fds[1]);
    835 		return -1;
    836 	} else if (chpid == 0) {
    837 		close(fds[0]);
    838 		dup2(fds[1], 1);
    839 		close(fds[1]);
    840 		execvp(args[0], args);
    841 		perror("exec");
    842 		exit(1);
    843 	}
    844 	close(fds[1]);
    845 	if (ioctl(fds[0], FIONBIO, &flags) &&
    846 	    ((flags = fcntl(fds[0], F_GETFL, 0)) < 0 ||
    847 	     fcntl(fds[0], F_SETFL, flags | O_NONBLOCK) < 0)) {
    848 		close(fds[0]);
    849 		return -1;
    850 	}
    851 	fprintf(stdout, "child-run=%d  fd=%d\n", chpid, fds[0]), fflush(stdout);
    852 	return eph_new_conn(fds[0], func);
    853 }
    854 
    855 int eph_createpipetest(int size, int tsleep, int ttime, void *func)
    856 {
    857 	int fds[2], flags = 1;
    858 	pid_t chpid;
    859 
    860 	if (pipe(fds)) {
    861 		perror("pipe");
    862 		return -1;
    863 	}
    864 	chpid = fork();
    865 	if (chpid == -1) {
    866 		perror("fork");
    867 		close(fds[0]), close(fds[1]);
    868 		return -1;
    869 	} else if (chpid == 0) {
    870 		int i;
    871 		char *buff = malloc(size + 1);
    872 		close(fds[0]);
    873 		dup2(fds[1], 1);
    874 		close(fds[1]);
    875 
    876 		srand(getpid() * time(NULL));
    877 		for (i = 0; i < (size - 1); i++) {
    878 			if (i && !(i % 64))
    879 				buff[i] = '\n';
    880 			else
    881 				buff[i] = '0' + (rand() % 10);
    882 		}
    883 		buff[i++] = '\n';
    884 		buff[i] = '\0';
    885 		ttime += (ttime * rand()) / RAND_MAX - (ttime >> 1);
    886 		ttime *= 1000;
    887 		while (ttime > 0) {
    888 			usleep(tsleep * 1000);
    889 			fputs(buff, stdout), fflush(stdout);
    890 			ttime -= tsleep;
    891 		}
    892 		free(buff);
    893 		exit(0);
    894 	}
    895 	close(fds[1]);
    896 	if (ioctl(fds[0], FIONBIO, &flags) &&
    897 	    ((flags = fcntl(fds[0], F_GETFL, 0)) < 0 ||
    898 	     fcntl(fds[0], F_SETFL, flags | O_NONBLOCK) < 0)) {
    899 		close(fds[0]);
    900 		return -1;
    901 	}
    902 	fprintf(stdout, "child-run=%d  fd=%d\n", chpid, fds[0]), fflush(stdout);
    903 	return eph_new_conn(fds[0], func);
    904 }
    905 
    906 static void *eph_pipe_session(void *data)
    907 {
    908 	struct eph_conn *conn = (struct eph_conn *)data;
    909 	int nbytes, totbytes = 0;
    910 	char buff[257];
    911 
    912 	while ((nbytes = eph_read(conn, buff, sizeof(buff))) > 0) {
    913 		fprintf(stdout, "[%p] %d bytes readed\n", conn, nbytes),
    914 		    fflush(stdout);
    915 		totbytes += nbytes;
    916 	}
    917 	fprintf(stdout, "[%p] exit - totbytes=%d\n", conn, totbytes),
    918 	    fflush(stdout);
    919 	eph_exit_conn(conn);
    920 	return data;
    921 }
    922 
    923 void eph_sigchld(int sig)
    924 {
    925 	int status;
    926 	pid_t pid;
    927 
    928 	while ((pid = waitpid(0, &status, WNOHANG)) > 0) {
    929 		fprintf(stdout, "child-dead=%d\n", pid), fflush(stdout);
    930 	}
    931 	signal(SIGCHLD, eph_sigchld);
    932 }
    933 
    934 void eph_usage(char const *prgname)
    935 {
    936 
    937 	fprintf(stderr,
    938 		"use: %s  [--ncgis ncgi]  [--cgi cgi] [--stksize bytes (%d)]\n",
    939 		prgname, stksize);
    940 
    941 }
    942 
    943 int main(int argc, char *argv[])
    944 {
    945 	int i, ncgis = 8;
    946 	char *cgi = NULL;
    947 	char *args[16];
    948 
    949 	for (i = 1; i < argc; i++) {
    950 		if (strcmp(argv[i], "--ncgis") == 0) {
    951 			if (++i < argc)
    952 				ncgis = atoi(argv[i]);
    953 			continue;
    954 		}
    955 		if (strcmp(argv[i], "--cgi") == 0) {
    956 			if (++i < argc)
    957 				cgi = argv[i];
    958 			continue;
    959 		}
    960 		if (strcmp(argv[i], "--stksize") == 0) {
    961 			if (++i < argc)
    962 				stksize = atoi(argv[i]);
    963 			continue;
    964 		}
    965 
    966 		eph_usage(argv[0]);
    967 		return 1;
    968 	}
    969 
    970 	signal(SIGCHLD, eph_sigchld);
    971 	signal(SIGPIPE, SIG_IGN);
    972 
    973 	if (eph_init() == -1) {
    974 
    975 		return 2;
    976 	}
    977 
    978 	if (cgi) {
    979 		args[0] = cgi;
    980 		args[1] = NULL;
    981 
    982 		for (i = 0; i < ncgis; i++)
    983 			eph_createcgi(args, eph_pipe_session);
    984 	} else {
    985 		for (i = 0; i < ncgis; i++)
    986 			eph_createpipetest(256, 250, 8, eph_pipe_session);
    987 	}
    988 
    989 	while (numfds > 0)
    990 		eph_scheduler(0, STD_SCHED_TIMEOUT);
    991 
    992 	eph_cleanup();
    993 	return 0;
    994 }
    995 
    996 #endif /* #if defined(PIPETESTER) */
    997