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