version 1.10, 2009/08/13 14:26:38 |
version 1.11, 2009/08/28 11:59:12 |
|
|
#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> |
|
|
|
|
#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", |
|
|
static void |
static void |
alarmhandler(int signo) |
alarmhandler(int signo) |
{ |
{ |
print_stats = 1; |
proc_slice = 1; |
signal(signo, alarmhandler); |
signal(signo, alarmhandler); |
} |
} |
|
|
|
|
} |
} |
|
|
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) |
|
|
} |
} |
|
|
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; |
|
|
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"); |
|
|
} |
} |
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); |
|
|
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); |
|
|
|
|
} |
} |
|
|
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) |
|
|
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; |
|
|
|
|
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++; |
} |
} |
|
|
|
|
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) |
|
|
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); |
|
|
|
|
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) { |
|
|
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]; |
|
|
|
|
} 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; |
} |
} |