[BACK]Return to tcpbench.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tcpbench

Diff for /src/usr.bin/tcpbench/tcpbench.c between version 1.10 and 1.11

version 1.10, 2009/08/13 14:26:38 version 1.11, 2009/08/28 11:59:12
Line 18 
Line 18 
 #include <sys/time.h>  #include <sys/time.h>
 #include <sys/socket.h>  #include <sys/socket.h>
 #include <sys/socketvar.h>  #include <sys/socketvar.h>
   #include <sys/resource.h>
   
 #include <net/route.h>  #include <net/route.h>
   
Line 47 
Line 48 
 #include <kvm.h>  #include <kvm.h>
 #include <nlist.h>  #include <nlist.h>
   
 #define DEFAULT_PORT            "12345"  #define DEFAULT_PORT "12345"
 #define DEFAULT_STATS_INTERVAL  1000            /* ms */  #define DEFAULT_STATS_INTERVAL 1000 /* ms */
 #define DEFAULT_BUF             256 * 1024  #define DEFAULT_BUF 256 * 1024
   #define MAX_FD 1024
   
 sig_atomic_t done = 0;  sig_atomic_t done = 0;
 sig_atomic_t print_stats = 0;  sig_atomic_t proc_slice = 0;
   
 u_int rdomain;  static u_int  rdomain;
   static char **kflag;
   static size_t Bflag;
   static int    Sflag;
   static int    rflag;
   static int    sflag;
   static int    vflag;
   
   /* stats for a single connection */
 struct statctx {  struct statctx {
         struct timeval t_start, t_last, t_cur;          struct timeval t_start, t_last;
         unsigned long long bytes;          unsigned long long bytes;
         pid_t pid;  
         u_long tcbaddr;          u_long tcbaddr;
         kvm_t *kh;  
         char **kvars;          char **kvars;
           kvm_t *kh;
 };  };
   
   /*
    * We account the mainstats here, that is the stats
    * for all connections, all variables starting with slice
    * are used to account information for the timeslice
    * between each output. Peak variables record the highest
    * between all slices so far.
    */
   static struct {
           unsigned long long slice_bytes; /* bytes for last slice */
           struct timeval t_start;         /* when we started counting */
           long double peak_mbps;          /* peak mbps so far */
           int nconns;                     /* connected clients */
   } mainstats;
   
 /* When adding variables, also add to stats_display() */  /* When adding variables, also add to stats_display() */
 static const char *allowed_kvars[] = {  static const char *allowed_kvars[] = {
         "inpcb.inp_flags",          "inpcb.inp_flags",
Line 105 
Line 127 
 static void  static void
 alarmhandler(int signo)  alarmhandler(int signo)
 {  {
         print_stats = 1;          proc_slice = 1;
         signal(signo, alarmhandler);          signal(signo, alarmhandler);
 }  }
   
Line 139 
Line 161 
 }  }
   
 static void  static void
   set_timer(int toggle)
   {
           struct itimerval itv;
   
           if (rflag <= 0)
                   return;
   
           if (toggle) {
                   itv.it_interval.tv_sec = rflag / 1000;
                   itv.it_interval.tv_usec = (rflag % 1000) * 1000;
                   itv.it_value = itv.it_interval;
           }
           else
                   bzero(&itv, sizeof(itv));
   
           setitimer(ITIMER_REAL, &itv, NULL);
   }
   
   static void
   print_header(void)
   {
           char **kv;
   
           printf("%12s %14s %12s %8s ", "elapsed_ms", "bytes", "mbps",
               "bwidth");
   
           for (kv = kflag;  kflag != NULL && *kv != NULL; kv++)
                   printf("%s%s", kv != kflag ? "," : "", *kv);
   
           printf("\n");
   }
   
   static void
 kget(kvm_t *kh, u_long addr, void *buf, int size)  kget(kvm_t *kh, u_long addr, void *buf, int size)
 {  {
         if (kvm_read(kh, addr, buf, size) != size)          if (kvm_read(kh, addr, buf, size) != size)
Line 146 
Line 201 
 }  }
   
 static u_long  static u_long
 kfind_tcb(kvm_t *kh, u_long ktcbtab, int sock, int vflag)  kfind_tcb(kvm_t *kh, u_long ktcbtab, int sock)
 {  {
         struct inpcbtable tcbtab;          struct inpcbtable tcbtab;
         struct inpcb *head, *next, *prev;          struct inpcb *head, *next, *prev;
Line 158 
Line 213 
         struct sockaddr_in *in4;          struct sockaddr_in *in4;
         struct sockaddr_in6 *in6;          struct sockaddr_in6 *in6;
         char tmp1[64], tmp2[64];          char tmp1[64], tmp2[64];
           int nretry;
   
           nretry = 10;
         melen = themlen = sizeof(struct sockaddr_storage);          melen = themlen = sizeof(struct sockaddr_storage);
         if (getsockname(sock, (struct sockaddr *)&me, &melen) == -1)          if (getsockname(sock, (struct sockaddr *)&me, &melen) == -1)
                 err(1, "getsockname");                  err(1, "getsockname");
Line 177 
Line 234 
         }          }
         if (vflag >= 2)          if (vflag >= 2)
                 fprintf(stderr, "Using PCB table at %lu\n", ktcbtab);                  fprintf(stderr, "Using PCB table at %lu\n", ktcbtab);
   retry:
         kget(kh, ktcbtab, &tcbtab, sizeof(tcbtab));          kget(kh, ktcbtab, &tcbtab, sizeof(tcbtab));
         prev = head = (struct inpcb *)&CIRCLEQ_FIRST(          prev = head = (struct inpcb *)&CIRCLEQ_FIRST(
             &((struct inpcbtable *)ktcbtab)->inpt_queue);              &((struct inpcbtable *)ktcbtab)->inpt_queue);
Line 189 
Line 246 
                 if (vflag >= 2)                  if (vflag >= 2)
                         fprintf(stderr, "Checking PCB %p\n", next);                          fprintf(stderr, "Checking PCB %p\n", next);
                 kget(kh, (u_long)next, &inpcb, sizeof(inpcb));                  kget(kh, (u_long)next, &inpcb, sizeof(inpcb));
                 if (CIRCLEQ_PREV(&inpcb, inp_queue) != prev)                  if (CIRCLEQ_PREV(&inpcb, inp_queue) != prev) {
                         errx(1, "pcb prev pointer insane");                          if (nretry--) {
                                   warnx("pcb prev pointer insane");
                                   goto retry;
                           }
                           else
                                   errx(1, "pcb prev pointer insane,"
                                        " all attempts exausted");
                   }
                 prev = next;                  prev = next;
                 next = CIRCLEQ_NEXT(&inpcb, inp_queue);                  next = CIRCLEQ_NEXT(&inpcb, inp_queue);
   
Line 306 
Line 370 
 }  }
   
 static void  static void
 stats_prepare(struct statctx *sc, int fd, kvm_t *kh, u_long ktcbtab,  stats_prepare(struct statctx *sc, int fd, kvm_t *kh, u_long ktcbtab)
     int rflag, int vflag, char **kflag)  
 {  {
         struct itimerval itv;  
         int i;  
   
         if (rflag <= 0)          if (rflag <= 0)
                 return;                  return;
         sc->kh = kh;          sc->kh = kh;
         sc->kvars = kflag;          sc->kvars = kflag;
         if (kflag)          if (kflag)
                 sc->tcbaddr = kfind_tcb(kh, ktcbtab, fd, vflag);                  sc->tcbaddr = kfind_tcb(kh, ktcbtab, fd);
         gettimeofday(&sc->t_start, NULL);          if (gettimeofday(&sc->t_start, NULL) == -1)
                   err(1, "gettimeofday");
         sc->t_last = sc->t_start;          sc->t_last = sc->t_start;
         signal(SIGALRM, alarmhandler);  
         itv.it_interval.tv_sec = rflag / 1000;  
         itv.it_interval.tv_usec = (rflag % 1000) * 1000;  
         itv.it_value = itv.it_interval;  
         setitimer(ITIMER_REAL, &itv, NULL);  
         sc->bytes = 0;          sc->bytes = 0;
         sc->pid = getpid();  
   
         printf("%8s %12s %14s %12s ", "pid", "elapsed_ms", "bytes", "Mbps");  
         if (sc->kvars != NULL) {  
                 for (i = 0; sc->kvars[i] != NULL; i++)  
                         printf("%s%s", i > 0 ? "," : "", sc->kvars[i]);  
         }  
         printf("\n");  
         fflush(stdout);  
 }  }
   
 static void  static void
 stats_update(struct statctx *sc, ssize_t n)  stats_update(struct statctx *sc, ssize_t n)
 {  {
         sc->bytes += n;          sc->bytes += n;
           mainstats.slice_bytes += n;
 }  }
   
 static void  static void
 stats_display(struct statctx *sc)  stats_cleanslice(void)
 {  {
         struct timeval t_diff;          mainstats.slice_bytes = 0;
         unsigned long long total_elapsed, since_last;  }
         size_t i;  
         struct inpcb inpcb;  
         struct tcpcb tcpcb;  
         struct socket sockb;  
   
         gettimeofday(&sc->t_cur, NULL);  static void
         timersub(&sc->t_cur, &sc->t_start, &t_diff);  stats_display(unsigned long long total_elapsed, long double mbps,
         total_elapsed = t_diff.tv_sec * 1000 + t_diff.tv_usec / 1000;      float bwperc, struct statctx *sc, struct inpcb *inpcb,
         timersub(&sc->t_cur, &sc->t_last, &t_diff);      struct tcpcb *tcpcb, struct socket *sockb)
         since_last = t_diff.tv_sec * 1000 + t_diff.tv_usec / 1000;  {
         printf("%8ld %12llu %14llu %12.3Lf ", (long)sc->pid,          int j;
             total_elapsed, sc->bytes,  
             (long double)(sc->bytes * 8) / (since_last * 1000.0));          printf("%12llu %14llu %12.3Lf %7.2f%% ", total_elapsed, sc->bytes,
         sc->t_last = sc->t_cur;              mbps, bwperc);
         sc->bytes = 0;  
   
         if (sc->kvars != NULL) {          if (sc->kvars != NULL) {
                 kupdate_stats(sc->kh, sc->tcbaddr, &inpcb, &tcpcb, &sockb);                  kupdate_stats(sc->kh, sc->tcbaddr, inpcb, tcpcb,
                 for (i = 0; sc->kvars[i] != NULL; i++) {                      sockb);
 #define P(v, f) \  
         if (strcmp(sc->kvars[i], #v) == 0) { \                  for (j = 0; sc->kvars[j] != NULL; j++) {
                 printf("%s"f, i > 0 ? "," : "", v); \  #define S(a) #a
                 continue; \  #define P(b, v, f)                                                      \
         }                          if (strcmp(sc->kvars[j], S(b.v)) == 0) {        \
                         P(inpcb.inp_flags, "0x%08x")                                  printf("%s"f, j > 0 ? "," : "", b->v);  \
                         P(sockb.so_rcv.sb_cc, "%lu")                                  continue;                               \
                         P(sockb.so_rcv.sb_hiwat, "%lu")                          }
                         P(sockb.so_snd.sb_cc, "%lu")                          P(inpcb, inp_flags, "0x%08x")
                         P(sockb.so_snd.sb_hiwat, "%lu")                          P(sockb, so_rcv.sb_cc, "%lu")
                         P(tcpcb.snd_una, "%u")                          P(sockb, so_rcv.sb_hiwat, "%lu")
                         P(tcpcb.snd_nxt, "%u")                          P(sockb, so_snd.sb_cc, "%lu")
                         P(tcpcb.snd_wl1, "%u")                          P(sockb, so_snd.sb_hiwat, "%lu")
                         P(tcpcb.snd_wl2, "%u")                          P(tcpcb, snd_una, "%u")
                         P(tcpcb.snd_wnd, "%lu")                          P(tcpcb, snd_nxt, "%u")
                         P(tcpcb.rcv_wnd, "%lu")                          P(tcpcb, snd_wl1, "%u")
                         P(tcpcb.rcv_nxt, "%u")                          P(tcpcb, snd_wl2, "%u")
                         P(tcpcb.rcv_adv, "%u")                          P(tcpcb, snd_wnd, "%lu")
                         P(tcpcb.snd_max, "%u")                          P(tcpcb, rcv_wnd, "%lu")
                         P(tcpcb.snd_cwnd, "%lu")                          P(tcpcb, rcv_nxt, "%u")
                         P(tcpcb.snd_ssthresh, "%lu")                          P(tcpcb, rcv_adv, "%u")
                         P(tcpcb.t_rcvtime, "%u")                          P(tcpcb, snd_max, "%u")
                         P(tcpcb.t_rtttime, "%u")                          P(tcpcb, snd_cwnd, "%lu")
                         P(tcpcb.t_rtseq, "%u")                          P(tcpcb, snd_ssthresh, "%lu")
                         P(tcpcb.t_srtt, "%hu")                          P(tcpcb, t_rcvtime, "%u")
                         P(tcpcb.t_rttvar, "%hu")                          P(tcpcb, t_rtttime, "%u")
                         P(tcpcb.t_rttmin, "%hu")                          P(tcpcb, t_rtseq, "%u")
                         P(tcpcb.max_sndwnd, "%lu")                          P(tcpcb, t_srtt, "%hu")
                         P(tcpcb.snd_scale, "%u")                          P(tcpcb, t_rttvar, "%hu")
                         P(tcpcb.rcv_scale, "%u")                          P(tcpcb, t_rttmin, "%hu")
                         P(tcpcb.last_ack_sent, "%u")                          P(tcpcb, max_sndwnd, "%lu")
                           P(tcpcb, snd_scale, "%u")
                           P(tcpcb, rcv_scale, "%u")
                           P(tcpcb, last_ack_sent, "%u")
   #undef S
 #undef P  #undef P
                 }                  }
         }          }
         printf("\n");          printf("\n");
         fflush(stdout);  
 }  }
   
 static void  static void
 stats_finish(struct statctx *sc)  mainstats_display(long double slice_mbps, long double avg_mbps)
 {  {
         struct itimerval itv;          printf("Conn: %3d Mbps: %12.3Lf Peak Mbps: %12.3Lf Avg Mbps: %12.3Lf\n",
               mainstats.nconns, slice_mbps, mainstats.peak_mbps, avg_mbps);
         signal(SIGALRM, SIG_DFL);  
         bzero(&itv, sizeof(itv));  
         setitimer(ITIMER_REAL, &itv, NULL);  
 }  }
   
 static void __dead  static void
 handle_connection(kvm_t *kvmh, u_long ktcbtab, int sock, int vflag,  process_slice(struct statctx *sc, size_t nsc)
     int rflag, char **kflag, int Bflag)  
 {  {
         char *buf;          unsigned long long total_elapsed, since_last;
         struct pollfd pfd;          long double mbps, slice_mbps = 0;
         ssize_t n;          float bwperc;
         int r;          nfds_t i;
         struct statctx sc;          struct timeval t_cur, t_diff;
           struct inpcb inpcb;
           struct tcpcb tcpcb;
           struct socket sockb;
   
           for (i = 0; i < nsc; i++, sc++) {
                   if (gettimeofday(&t_cur, NULL) == -1)
                           err(1, "gettimeofday");
                   if (sc->kvars != NULL) /* process kernel stats */
                           kupdate_stats(sc->kh, sc->tcbaddr, &inpcb, &tcpcb,
                               &sockb);
                   timersub(&t_cur, &sc->t_start, &t_diff);
                   total_elapsed = t_diff.tv_sec * 1000 + t_diff.tv_usec / 1000;
                   timersub(&t_cur, &sc->t_last, &t_diff);
                   since_last = t_diff.tv_sec * 1000 + t_diff.tv_usec / 1000;
                   bwperc = (sc->bytes * 100.0) / mainstats.slice_bytes;
                   mbps = (sc->bytes * 8) / (since_last * 1000.0);
                   slice_mbps += mbps;
   
                   stats_display(total_elapsed, mbps, bwperc, sc,
                       &inpcb, &tcpcb, &sockb);
   
                   sc->t_last = t_cur;
                   sc->bytes = 0;
   
         if ((buf = malloc(Bflag)) == NULL)          }
                 err(1, "malloc");  
         if ((r = fcntl(sock, F_GETFL, 0)) == -1)  
                 err(1, "fcntl(F_GETFL)");  
         r |= O_NONBLOCK;  
         if (fcntl(sock, F_SETFL, r) == -1)  
                 err(1, "fcntl(F_SETFL, O_NONBLOCK)");  
   
         signal(SIGINT, exitsighand);          /* process stats for this slice */
         signal(SIGTERM, exitsighand);          if (slice_mbps > mainstats.peak_mbps)
         signal(SIGHUP, exitsighand);                  mainstats.peak_mbps = slice_mbps;
         signal(SIGPIPE, SIG_IGN);          mainstats_display(slice_mbps, slice_mbps / mainstats.nconns);
   }
   
         bzero(&pfd, sizeof(pfd));  static int
         pfd.fd = sock;  handle_connection(struct statctx *sc, int fd, char *buf, size_t buflen)
         pfd.events = POLLIN;  {
           ssize_t n;
   
         stats_prepare(&sc, sock, kvmh, ktcbtab, rflag, vflag, kflag);  again:
           n = read(fd, buf, buflen);
         while (!done) {          if (n == -1) {
                 if (print_stats) {                  if (errno == EINTR)
                         stats_display(&sc);                          goto again;
                         print_stats = 0;                  else if (errno == EWOULDBLOCK)
                 }                          return 0;
                 if (poll(&pfd, 1, INFTIM) == -1) {                  warn("fd %d read error", fd);
                         if (errno == EINTR)  
                                 continue;                  return -1;
                         err(1, "poll");  
                 }  
                 if ((n = read(pfd.fd, buf, Bflag)) == -1) {  
                         if (errno == EINTR || errno == EAGAIN)  
                                 continue;  
                         err(1, "read");  
                 }  
                 if (n == 0) {  
                         fprintf(stderr, "%8ld closed by remote end\n",  
                             (long)getpid());  
                         done = -1;  
                         break;  
                 }  
                 if (vflag >= 3)  
                         fprintf(stderr, "read: %zd bytes\n", n);  
                 stats_update(&sc, n);  
         }          }
         stats_finish(&sc);          else if (n == 0) {
                   if (vflag)
         free(buf);                          fprintf(stderr, "%8d closed by remote end\n", fd);
         close(sock);                  close(fd);
         exit(1);                  return -1;
           }
           if (vflag >= 3)
                   fprintf(stderr, "read: %zd bytes\n", n);
   
           stats_update(sc, n);
           return 0;
 }  }
   
 static void __dead  static nfds_t
 serverloop(kvm_t *kvmh, u_long ktcbtab, struct addrinfo *aitop,  serverbind(struct pollfd *pfd, nfds_t max_nfds, struct addrinfo *aitop)
     int vflag, int rflag, char **kflag, int Sflag, int Bflag)  
 {  {
         char tmp[128];          char tmp[128];
         int r, sock, client_id, on = 1;          int sock, on = 1;
         struct addrinfo *ai;          struct addrinfo *ai;
         struct pollfd *pfd;          nfds_t lnfds;
         struct sockaddr_storage ss;  
         socklen_t sslen;  
         size_t nfds, i, j;  
   
         pfd = NULL;          lnfds = 0;
         nfds = 0;  
         for (ai = aitop; ai != NULL; ai = ai->ai_next) {          for (ai = aitop; ai != NULL; ai = ai->ai_next) {
                   if (lnfds == max_nfds) {
                           fprintf(stderr,
                               "maximum number of listening fds reached\n");
                           break;
                   }
                 saddr_ntop(ai->ai_addr, ai->ai_addrlen, tmp, sizeof(tmp));                  saddr_ntop(ai->ai_addr, ai->ai_addrlen, tmp, sizeof(tmp));
                 if (vflag)                  if (vflag)
                         fprintf(stderr, "Try listen on %s\n", tmp);                          fprintf(stderr, "Try to listen on %s\n", tmp);
                 if ((sock = socket(ai->ai_family, ai->ai_socktype,                  if ((sock = socket(ai->ai_family, ai->ai_socktype,
                     ai->ai_protocol)) == -1) {                      ai->ai_protocol)) == -1) {
                         if (ai->ai_next == NULL)                          if (ai->ai_next == NULL)
Line 532 
Line 583 
                         close(sock);                          close(sock);
                         continue;                          continue;
                 }                  }
                 if (nfds > 128)                  if (vflag >= 3)
                         break;                          fprintf(stderr, "listening on fd %d\n", sock);
                 if ((pfd = realloc(pfd, ++nfds * sizeof(*pfd))) == NULL)                  lnfds++;
                         errx(1, "realloc(pfd * %zu)", nfds);                  pfd[lnfds - 1].fd = sock;
                 pfd[nfds - 1].fd = sock;                  pfd[lnfds - 1].events = POLLIN;
                 pfd[nfds - 1].events = POLLIN;  
         }          }
         freeaddrinfo(aitop);          freeaddrinfo(aitop);
         if (nfds == 0)          if (lnfds == 0)
                 errx(1, "No working listen addresses found");                  errx(1, "No working listen addresses found");
   
         signal(SIGINT, exitsighand);          return lnfds;
         signal(SIGTERM, exitsighand);  }
         signal(SIGHUP, exitsighand);  
         signal(SIGPIPE, SIG_IGN);  
         signal(SIGCHLD, SIG_IGN);  
   
   static void
   set_listening(struct pollfd *pfd, nfds_t lfds, int toggle) {
           int i;
   
           for (i = 0; i < (int)lfds; i++) {
                   if (toggle)
                           pfd[i].events = POLLIN;
                   else
                           pfd[i].events = 0;
           }
   
   }
   static void __dead
   serverloop(kvm_t *kvmh, u_long ktcbtab, struct addrinfo *aitop)
   {
           socklen_t sslen;
           struct pollfd *pfd;
           char tmp[128], *buf;
           struct statctx *psc;
           struct sockaddr_storage ss;
           nfds_t i, nfds, lfds;
           size_t nalloc;
           int r, sock, client_id;
   
           sslen = sizeof(ss);
           nalloc = 128;
           if ((pfd = calloc(sizeof(*pfd), nalloc)) == NULL)
                   err(1, "calloc");
           if ((psc = calloc(sizeof(*psc), nalloc)) == NULL)
                   err(1, "calloc");
           if ((buf = malloc(Bflag)) == NULL)
                   err(1, "malloc");
           lfds = nfds = serverbind(pfd, nalloc - 1, aitop);
           if (vflag >= 3)
                   fprintf(stderr, "listening on %d fds\n", lfds);
         if (setpgid(0, 0) == -1)          if (setpgid(0, 0) == -1)
                 err(1, "setpgid");                  err(1, "setpgid");
   
           print_header();
   
         client_id = 0;          client_id = 0;
         while (!done) {          while (!done) {
                   if (proc_slice) {
                           process_slice(psc + lfds, nfds - lfds);
                           stats_cleanslice();
                           proc_slice = 0;
                   }
                   if (vflag >= 3)
                           fprintf(stderr, "mainstats.nconns = %u\n",
                               mainstats.nconns);
                 if ((r = poll(pfd, nfds, INFTIM)) == -1) {                  if ((r = poll(pfd, nfds, INFTIM)) == -1) {
                         if (errno == EINTR)                          if (errno == EINTR)
                                 continue;                                  continue;
                         warn("poll");                          warn("poll");
                         break;                          break;
                 }                  }
   
                 if (vflag >= 3)                  if (vflag >= 3)
                         fprintf(stderr, "poll: %d\n", r);                          fprintf(stderr, "poll: %d\n", r);
                 for (i = 0 ; r > 0 && i < nfds; i++) {                  for (i = 0 ; r > 0 && i < nfds; i++) {
                         if ((pfd[i].revents & POLLIN) == 0)                          if ((pfd[i].revents & POLLIN) == 0)
                                 continue;                                  continue;
                         if (vflag >= 3)                          if (pfd[i].fd == -1)
                                 fprintf(stderr, "fd %d active\n", pfd[i].fd);                                  errx(1, "pfd insane");
                         r--;                          r--;
                         sslen = sizeof(ss);                          if (vflag >= 3)
                         if ((sock = accept(pfd[i].fd, (struct sockaddr *)&ss,                                  fprintf(stderr, "fd %d active i = %d\n",
                             &sslen)) == -1) {                                      pfd[i].fd, i);
                                 if (errno == EINTR)                          /* new connection */
                           if (i < lfds) {
                                   if ((sock = accept(pfd[i].fd,
                                       (struct sockaddr *)&ss,
                                       &sslen)) == -1) {
                                           if (errno == EINTR)
                                                   continue;
                                           else if (errno == EMFILE ||
                                               errno == ENFILE)
                                                   set_listening(pfd, lfds, 0);
                                           warn("accept");
                                         continue;                                          continue;
                                 warn("accept");                                  }
                                 break;                                  if ((r = fcntl(sock, F_GETFL, 0)) == -1)
                                           err(1, "fcntl(F_GETFL)");
                                   r |= O_NONBLOCK;
                                   if (fcntl(sock, F_SETFL, r) == -1)
                                           err(1, "fcntl(F_SETFL, O_NONBLOCK)");
                                   saddr_ntop((struct sockaddr *)&ss, sslen,
                                       tmp, sizeof(tmp));
                                   if (vflag)
                                           fprintf(stderr,
                                               "Accepted connection %d from "
                                               "%s, fd = %d\n", client_id++, tmp,
                                                sock);
                                   /* alloc more space if we're full */
                                   if (nfds == nalloc) {
                                           nalloc *= 2;
                                           if ((pfd = realloc(pfd,
                                               sizeof(*pfd) * nalloc)) == NULL)
                                                   err(1, "realloc");
                                           if ((psc = realloc(psc,
                                               sizeof(*psc) * nalloc)) == NULL)
                                                   err(1, "realloc");
                                   }
                                   pfd[nfds].fd = sock;
                                   pfd[nfds].events = POLLIN;
                                   stats_prepare(&psc[nfds], sock, kvmh, ktcbtab);
                                   nfds++;
                                   if (!mainstats.nconns++)
                                           set_timer(1);
                                   continue;
                         }                          }
                         saddr_ntop((struct sockaddr *)&ss, sslen,                          /* event in fd */
                             tmp, sizeof(tmp));                          if (vflag >= 3)
                         if (vflag)                                  fprintf(stderr,
                                 fprintf(stderr, "Accepted connection %d from "                                      "fd %d active", pfd[i].fd);
                                     "%s, fd = %d\n", client_id++, tmp, sock);                          while (handle_connection(&psc[i], pfd[i].fd,
                         switch (fork()) {                              buf, Bflag) == -1) {
                         case -1:                                  pfd[i] = pfd[nfds - 1];
                                 warn("fork");                                  pfd[nfds - 1].fd = -1;
                                 done = -1;                                  psc[i] = psc[nfds - 1];
                                 break;                                  mainstats.nconns--;
                         case 0:                                  nfds--;
                                 for (j = 0; j < nfds; j++)                                  /* stop display if no clients */
                                         if (j != i)                                  if (!mainstats.nconns) {
                                                 close(pfd[j].fd);                                          proc_slice = 1;
                                 handle_connection(kvmh, ktcbtab, sock,                                          set_timer(0);
                                     vflag, rflag, kflag, Bflag);                                  }
                                 /* NOTREACHED */                                  /* if we were full */
                                 _exit(1);                                  set_listening(pfd, lfds, 1);
                         default:  
                                 close(sock);                                  /* is there an event pending on the last fd? */
                                 break;                                  if (pfd[i].fd == -1 ||
                                       (pfd[i].revents & POLLIN) == 0)
                                           break;
                         }                          }
                         if (done == -1)  
                                 break;  
                 }                  }
         }          }
         for (i = 0; i < nfds; i++)  
                 close(pfd[i].fd);  
         if (done > 0)  
                 warnx("Terminated by signal %d", done);  
         signal(SIGTERM, SIG_IGN);  
         killpg(0, SIGTERM);  
         exit(1);          exit(1);
 }  }
   
 static void __dead  static void __dead
 clientloop(kvm_t *kvmh, u_long ktcbtab, const char *host, const char *port,  clientloop(kvm_t *kvmh, u_long ktcbtab, const char *host, const char *port, int nconn)
     int vflag, int rflag, char **kflag, int Sflag, int Bflag, int nconn)  
 {  {
         char tmp[128];  
         char *buf;  
         int r, sock, herr;  
         struct addrinfo *aitop, *ai, hints;          struct addrinfo *aitop, *ai, hints;
           struct statctx *psc;
         struct pollfd *pfd;          struct pollfd *pfd;
           char tmp[128], *buf;
           int i, r, herr, sock = -1;
           u_int scnt = 0;
         ssize_t n;          ssize_t n;
         struct statctx sc;  
         u_int i, scnt = 0;  
   
         if ((buf = malloc(Bflag)) == NULL)          if ((buf = malloc(Bflag)) == NULL)
                 err(1, "malloc");                  err(1, "malloc");
   
         if ((pfd = calloc(nconn, sizeof(struct pollfd))) == NULL)          if ((pfd = calloc(nconn, sizeof(*pfd))) == NULL)
                 err(1, "clientloop pfd calloc");                  err(1, "clientloop pfd calloc");
           if ((psc = calloc(nconn, sizeof(*psc))) == NULL)
                   err(1, "clientloop psc calloc");
   
         for (i = 0; i < nconn; i++) {          for (i = 0; i < nconn; i++) {
                 bzero(&hints, sizeof(hints));                  bzero(&hints, sizeof(hints));
                 hints.ai_socktype = SOCK_STREAM;                  hints.ai_socktype = SOCK_STREAM;
Line 688 
Line 814 
   
                 pfd[i].fd = sock;                  pfd[i].fd = sock;
                 pfd[i].events = POLLOUT;                  pfd[i].events = POLLOUT;
                   stats_prepare(psc + i, sock, kvmh, ktcbtab);
                   mainstats.nconns++;
                 scnt++;                  scnt++;
         }          }
   
Line 695 
Line 823 
                 fprintf(stderr, "%u connections established\n", scnt);                  fprintf(stderr, "%u connections established\n", scnt);
         arc4random_buf(buf, Bflag);          arc4random_buf(buf, Bflag);
   
         signal(SIGINT, exitsighand);          print_header();
         signal(SIGTERM, exitsighand);          set_timer(1);
         signal(SIGHUP, exitsighand);  
         signal(SIGPIPE, SIG_IGN);  
   
         stats_prepare(&sc, sock, kvmh, ktcbtab, rflag, vflag, kflag);  
   
         while (!done) {          while (!done) {
                 if (print_stats) {                  if (proc_slice) {
                         stats_display(&sc);                          process_slice(psc, scnt);
                         print_stats = 0;                          stats_cleanslice();
                           proc_slice = 0;
                 }                  }
                 if (poll(pfd, nconn, INFTIM) == -1) {                  if (poll(pfd, nconn, INFTIM) == -1) {
                         if (errno == EINTR)                          if (errno == EINTR)
Line 727 
Line 852 
                                 if (vflag >= 3)                                  if (vflag >= 3)
                                         fprintf(stderr, "write: %zd bytes\n",                                          fprintf(stderr, "write: %zd bytes\n",
                                             n);                                              n);
                                 stats_update(&sc, n);                                  stats_update(psc + i, n);
                         }                          }
                 }                  }
         }          }
         stats_finish(&sc);  
   
         if (done > 0)          if (done > 0)
                 warnx("Terminated by signal %d", done);                  warnx("Terminated by signal %d", done);
   
Line 758 
Line 882 
         extern char *optarg;          extern char *optarg;
   
         char kerr[_POSIX2_LINE_MAX], *tmp;          char kerr[_POSIX2_LINE_MAX], *tmp;
         const char *errstr;  
         int ch, herr;  
         struct addrinfo *aitop, hints;          struct addrinfo *aitop, hints;
           const char *errstr;
         kvm_t *kvmh = NULL;          kvm_t *kvmh = NULL;
           struct rlimit rl;
           int ch, herr;
   
         const char *host = NULL, *port = DEFAULT_PORT;          const char *host = NULL, *port = DEFAULT_PORT;
         char **kflag = NULL;  
         int sflag = 0, vflag = 0, rflag = DEFAULT_STATS_INTERVAL, Sflag = 0;  
         int Bflag = DEFAULT_BUF;  
         int nconn = 1;          int nconn = 1;
   
           Bflag = DEFAULT_BUF;
           Sflag = sflag = vflag = rdomain = 0;
           kflag = NULL;
           rflag = DEFAULT_STATS_INTERVAL;
   
         struct nlist nl[] = { { "_tcbtable" }, { "" } };          struct nlist nl[] = { { "_tcbtable" }, { "" } };
   
         while ((ch = getopt(argc, argv, "B:hlk:n:p:r:sS:vV:")) != -1) {          while ((ch = getopt(argc, argv, "B:hlk:n:p:r:sS:vV:")) != -1) {
Line 836 
Line 963 
         if (argc != (sflag ? 0 : 1))          if (argc != (sflag ? 0 : 1))
                 usage();                  usage();
   
         if (kflag != NULL && nconn > 1)  
                 errx(1, "-k currently only works with a single tcp connection");  
   
         if (!sflag)          if (!sflag)
                 host = argv[0];                  host = argv[0];
   
Line 864 
Line 988 
         } else          } else
                 drop_gid();                  drop_gid();
   
           signal(SIGINT, exitsighand);
           signal(SIGTERM, exitsighand);
           signal(SIGHUP, exitsighand);
           signal(SIGPIPE, SIG_IGN);
           signal(SIGALRM, alarmhandler);
   
           if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
                   err(1, "getrlimit");
           if (rl.rlim_cur < MAX_FD)
                   rl.rlim_cur = MAX_FD;
           if (setrlimit(RLIMIT_NOFILE, &rl))
                   err(1, "setrlimit");
           if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
                   err(1, "getrlimit");
   
         if (sflag)          if (sflag)
                 serverloop(kvmh, nl[0].n_value, aitop, vflag, rflag, kflag,                  serverloop(kvmh, nl[0].n_value, aitop);
                     Sflag, Bflag);  
         else          else
                 clientloop(kvmh, nl[0].n_value, host, port, vflag, rflag, kflag,                  clientloop(kvmh, nl[0].n_value, host, port, nconn);
                     Sflag, Bflag, nconn);  
   
         return 0;          return 0;
 }  }

Legend:
Removed from v.1.10  
changed lines
  Added in v.1.11