diff --git a/bash-completion/tc b/bash-completion/tc index 80d12972..29bca5d9 100644 --- a/bash-completion/tc +++ b/bash-completion/tc @@ -450,7 +450,7 @@ _tc_filter_options() return 0 ;; matchall) - _tc_once_attr 'action skip_sw skip_hw' + _tc_once_attr 'action classid skip_sw skip_hw' return 0 ;; flower) diff --git a/misc/ss.c b/misc/ss.c index da52d5ed..9d21ed7a 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "utils.h" #include "rt_names.h" @@ -46,6 +47,8 @@ #include #define MAGIC_SEQ 123456 +#define BUF_CHUNK (1024 * 1024) +#define LEN_ALIGN(x) (((x) + 1) & ~1) #define DIAG_REQUEST(_req, _r) \ struct { \ @@ -102,11 +105,71 @@ int show_header = 1; int follow_events; int sctp_ino; -int netid_width; -int state_width; -int addr_width; -int serv_width; -char *odd_width_pad = ""; +enum col_id { + COL_NETID, + COL_STATE, + COL_RECVQ, + COL_SENDQ, + COL_ADDR, + COL_SERV, + COL_RADDR, + COL_RSERV, + COL_EXT, + COL_MAX +}; + +enum col_align { + ALIGN_LEFT, + ALIGN_CENTER, + ALIGN_RIGHT +}; + +struct column { + const enum col_align align; + const char *header; + const char *ldelim; + int disabled; + int width; /* Calculated, including additional layout spacing */ + int max_len; /* Measured maximum field length in this column */ +}; + +static struct column columns[] = { + { ALIGN_LEFT, "Netid", "", 0, 0, 0 }, + { ALIGN_LEFT, "State", " ", 0, 0, 0 }, + { ALIGN_LEFT, "Recv-Q", " ", 0, 0, 0 }, + { ALIGN_LEFT, "Send-Q", " ", 0, 0, 0 }, + { ALIGN_RIGHT, "Local Address:", " ", 0, 0, 0 }, + { ALIGN_LEFT, "Port", "", 0, 0, 0 }, + { ALIGN_RIGHT, "Peer Address:", " ", 0, 0, 0 }, + { ALIGN_LEFT, "Port", "", 0, 0, 0 }, + { ALIGN_LEFT, "", "", 0, 0, 0 }, +}; + +static struct column *current_field = columns; + +/* Output buffer: chained chunks of BUF_CHUNK bytes. Each field is written to + * the buffer as a variable size token. A token consists of a 16 bits length + * field, followed by a string which is not NULL-terminated. + * + * A new chunk is allocated and linked when the current chunk doesn't have + * enough room to store the current token as a whole. + */ +struct buf_chunk { + struct buf_chunk *next; /* Next chained chunk */ + char *end; /* Current end of content */ + char data[0]; +}; + +struct buf_token { + uint16_t len; /* Data length, excluding length descriptor */ + char data[0]; +}; + +static struct { + struct buf_token *cur; /* Position of current token in chunk */ + struct buf_chunk *head; /* First chunk */ + struct buf_chunk *tail; /* Current chunk */ +} buffer; static const char *TCP_PROTO = "tcp"; static const char *SCTP_PROTO = "sctp"; @@ -823,6 +886,357 @@ static const char *vsock_netid_name(int type) } } +/* Allocate and initialize a new buffer chunk */ +static struct buf_chunk *buf_chunk_new(void) +{ + struct buf_chunk *new = malloc(BUF_CHUNK); + + if (!new) + abort(); + + new->next = NULL; + + /* This is also the last block */ + buffer.tail = new; + + /* Next token will be stored at the beginning of chunk data area, and + * its initial length is zero. + */ + buffer.cur = (struct buf_token *)new->data; + buffer.cur->len = 0; + + new->end = buffer.cur->data; + + return new; +} + +/* Return available tail room in given chunk */ +static int buf_chunk_avail(struct buf_chunk *chunk) +{ + return BUF_CHUNK - offsetof(struct buf_chunk, data) - + (chunk->end - chunk->data); +} + +/* Update end pointer and token length, link new chunk if we hit the end of the + * current one. Return -EAGAIN if we got a new chunk, caller has to print again. + */ +static int buf_update(int len) +{ + struct buf_chunk *chunk = buffer.tail; + struct buf_token *t = buffer.cur; + + /* Claim success if new content fits in the current chunk, and anyway + * if this is the first token in the chunk: in the latter case, + * allocating a new chunk won't help, so we'll just cut the output. + */ + if ((len < buf_chunk_avail(chunk) && len != -1 /* glibc < 2.0.6 */) || + t == (struct buf_token *)chunk->data) { + len = min(len, buf_chunk_avail(chunk)); + + /* Total field length can't exceed 2^16 bytes, cut as needed */ + len = min(len, USHRT_MAX - t->len); + + chunk->end += len; + t->len += len; + return 0; + } + + /* Content truncated, time to allocate more */ + chunk->next = buf_chunk_new(); + + /* Copy current token over to new chunk, including length descriptor */ + memcpy(chunk->next->data, t, sizeof(t->len) + t->len); + chunk->next->end += t->len; + + /* Discard partially written field in old chunk */ + chunk->end -= t->len + sizeof(t->len); + + return -EAGAIN; +} + +/* Append content to buffer as part of the current field */ +static void out(const char *fmt, ...) +{ + struct column *f = current_field; + va_list args; + char *pos; + int len; + + if (f->disabled) + return; + + if (!buffer.head) + buffer.head = buf_chunk_new(); + +again: /* Append to buffer: if we have a new chunk, print again */ + + pos = buffer.cur->data + buffer.cur->len; + va_start(args, fmt); + + /* Limit to tail room. If we hit the limit, buf_update() will tell us */ + len = vsnprintf(pos, buf_chunk_avail(buffer.tail), fmt, args); + va_end(args); + + if (buf_update(len)) + goto again; +} + +static int print_left_spacing(struct column *f, int stored, int printed) +{ + int s; + + if (!f->width || f->align == ALIGN_LEFT) + return 0; + + s = f->width - stored - printed; + if (f->align == ALIGN_CENTER) + /* If count of total spacing is odd, shift right by one */ + s = (s + 1) / 2; + + if (s > 0) + return printf("%*c", s, ' '); + + return 0; +} + +static void print_right_spacing(struct column *f, int printed) +{ + int s; + + if (!f->width || f->align == ALIGN_RIGHT) + return; + + s = f->width - printed; + if (f->align == ALIGN_CENTER) + s /= 2; + + if (s > 0) + printf("%*c", s, ' '); +} + +/* Done with field: update buffer pointer, start new token after current one */ +static void field_flush(struct column *f) +{ + struct buf_chunk *chunk = buffer.tail; + unsigned int pad = buffer.cur->len % 2; + + if (f->disabled) + return; + + if (buffer.cur->len > f->max_len) + f->max_len = buffer.cur->len; + + /* We need a new chunk if we can't store the next length descriptor. + * Mind the gap between end of previous token and next aligned position + * for length descriptor. + */ + if (buf_chunk_avail(chunk) - pad < sizeof(buffer.cur->len)) { + chunk->end += pad; + chunk->next = buf_chunk_new(); + return; + } + + buffer.cur = (struct buf_token *)(buffer.cur->data + + LEN_ALIGN(buffer.cur->len)); + buffer.cur->len = 0; + buffer.tail->end = buffer.cur->data; +} + +static int field_is_last(struct column *f) +{ + return f - columns == COL_MAX - 1; +} + +static void field_next(void) +{ + field_flush(current_field); + + if (field_is_last(current_field)) + current_field = columns; + else + current_field++; +} + +/* Walk through fields and flush them until we reach the desired one */ +static void field_set(enum col_id id) +{ + while (id != current_field - columns) + field_next(); +} + +/* Print header for all non-empty columns */ +static void print_header(void) +{ + while (!field_is_last(current_field)) { + if (!current_field->disabled) + out(current_field->header); + field_next(); + } +} + +/* Get the next available token in the buffer starting from the current token */ +static struct buf_token *buf_token_next(struct buf_token *cur) +{ + struct buf_chunk *chunk = buffer.tail; + + /* If we reached the end of chunk contents, get token from next chunk */ + if (cur->data + LEN_ALIGN(cur->len) == chunk->end) { + buffer.tail = chunk = chunk->next; + return chunk ? (struct buf_token *)chunk->data : NULL; + } + + return (struct buf_token *)(cur->data + LEN_ALIGN(cur->len)); +} + +/* Free up all allocated buffer chunks */ +static void buf_free_all(void) +{ + struct buf_chunk *tmp; + + for (buffer.tail = buffer.head; buffer.tail; ) { + tmp = buffer.tail; + buffer.tail = buffer.tail->next; + free(tmp); + } + buffer.head = NULL; +} + +/* Calculate column width from contents length. If columns don't fit on one + * line, break them into the least possible amount of lines and keep them + * aligned across lines. Available screen space is equally spread between fields + * as additional spacing. + */ +static void render_calc_width(int screen_width) +{ + int first, len = 0, linecols = 0; + struct column *c, *eol = columns - 1; + + /* First pass: set width for each column to measured content length */ + for (first = 1, c = columns; c - columns < COL_MAX; c++) { + if (c->disabled) + continue; + + if (!first && c->max_len) + c->width = c->max_len + strlen(c->ldelim); + else + c->width = c->max_len; + + /* But don't exceed screen size. If we exceed the screen size + * for even a single field, it will just start on a line of its + * own and then naturally wrap. + */ + c->width = min(c->width, screen_width); + + if (c->width) + first = 0; + } + + /* Second pass: find out newlines and distribute available spacing */ + for (c = columns; c - columns < COL_MAX; c++) { + int pad, spacing, rem, last; + struct column *tmp; + + if (!c->width) + continue; + + linecols++; + len += c->width; + + for (last = 1, tmp = c + 1; tmp - columns < COL_MAX; tmp++) { + if (tmp->width) { + last = 0; + break; + } + } + + if (!last && len < screen_width) { + /* Columns fit on screen so far, nothing to do yet */ + continue; + } + + if (len == screen_width) { + /* Exact fit, just start with new line */ + goto newline; + } + + if (len > screen_width) { + /* Screen width exceeded: go back one column */ + len -= c->width; + c--; + linecols--; + } + + /* Distribute remaining space to columns on this line */ + pad = screen_width - len; + spacing = pad / linecols; + rem = pad % linecols; + for (tmp = c; tmp > eol; tmp--) { + if (!tmp->width) + continue; + + tmp->width += spacing; + if (rem) { + tmp->width++; + rem--; + } + } + +newline: + /* Line break: reset line counters, mark end-of-line */ + eol = c; + len = 0; + linecols = 0; + } +} + +/* Render buffered output with spacing and delimiters, then free up buffers */ +static void render(int screen_width) +{ + struct buf_token *token = (struct buf_token *)buffer.head->data; + int printed, line_started = 0; + struct column *f; + + /* Ensure end alignment of last token, it wasn't necessarily flushed */ + buffer.tail->end += buffer.cur->len % 2; + + render_calc_width(screen_width); + + /* Rewind and replay */ + buffer.tail = buffer.head; + + f = columns; + while (!f->width) + f++; + + while (token) { + /* Print left delimiter only if we already started a line */ + if (line_started++) + printed = printf("%s", current_field->ldelim); + else + printed = 0; + + /* Print field content from token data with spacing */ + printed += print_left_spacing(f, token->len, printed); + printed += fwrite(token->data, 1, token->len, stdout); + print_right_spacing(f, printed); + + /* Go to next non-empty field, deal with end-of-line */ + do { + if (field_is_last(f)) { + printf("\n"); + f = columns; + line_started = 0; + } else { + f++; + } + } while (f->disabled); + + token = buf_token_next(token); + } + + buf_free_all(); +} + static void sock_state_print(struct sockstat *s) { const char *sock_name; @@ -862,47 +1276,46 @@ static void sock_state_print(struct sockstat *s) sock_name = "unknown"; } - if (netid_width) - printf("%-*s ", netid_width, - is_sctp_assoc(s, sock_name) ? "" : sock_name); - if (state_width) { - if (is_sctp_assoc(s, sock_name)) - printf("`- %-*s ", state_width - 3, - sctp_sstate_name[s->state]); - else - printf("%-*s ", state_width, sstate_name[s->state]); + if (is_sctp_assoc(s, sock_name)) { + field_set(COL_STATE); /* Empty Netid field */ + out("`- %s", sctp_sstate_name[s->state]); + } else { + field_set(COL_NETID); + out("%s", sock_name); + field_set(COL_STATE); + out("%s", sstate_name[s->state]); } - printf("%-6d %-6d %s", s->rq, s->wq, odd_width_pad); + field_set(COL_RECVQ); + out("%-6d", s->rq); + field_set(COL_SENDQ); + out("%-6d", s->wq); + field_set(COL_ADDR); } static void sock_details_print(struct sockstat *s) { if (s->uid) - printf(" uid:%u", s->uid); + out(" uid:%u", s->uid); - printf(" ino:%u", s->ino); - printf(" sk:%llx", s->sk); + out(" ino:%u", s->ino); + out(" sk:%llx", s->sk); if (s->mark) - printf(" fwmark:0x%x", s->mark); -} - -static void sock_addr_print_width(int addr_len, const char *addr, char *delim, - int port_len, const char *port, const char *ifname) -{ - if (ifname) { - printf("%*s%%%s%s%-*s ", addr_len, addr, ifname, delim, - port_len, port); - } else { - printf("%*s%s%-*s ", addr_len, addr, delim, port_len, port); - } + out(" fwmark:0x%x", s->mark); } static void sock_addr_print(const char *addr, char *delim, const char *port, const char *ifname) { - sock_addr_print_width(addr_width, addr, delim, serv_width, port, ifname); + if (ifname) + out("%s" "%%" "%s%s", addr, ifname, delim); + else + out("%s%s", addr, delim); + + field_next(); + out("%s", port); + field_next(); } static const char *print_ms_timer(unsigned int timeout) @@ -1083,7 +1496,6 @@ static void inet_addr_print(const inet_prefix *a, int port, { char buf[1024]; const char *ap = buf; - int est_len = addr_width; const char *ifname = NULL; if (a->family == AF_INET) { @@ -1102,24 +1514,13 @@ static void inet_addr_print(const inet_prefix *a, int port, "[%s]", ap); ap = buf; } - - est_len = strlen(ap); - if (est_len <= addr_width) - est_len = addr_width; - else - est_len = addr_width + ((est_len-addr_width+3)/4)*4; } } - if (ifindex) { - ifname = ll_index_to_name(ifindex); - est_len -= strlen(ifname) + 1; /* +1 for percent char */ - if (est_len < 0) - est_len = 0; - } + if (ifindex) + ifname = ll_index_to_name(ifindex); - sock_addr_print_width(est_len, ap, ":", serv_width, resolve_service(port), - ifname); + sock_addr_print(ap, ":", resolve_service(port), ifname); } struct aafilter { @@ -1793,12 +2194,12 @@ static void proc_ctx_print(struct sockstat *s) if (find_entry(s->ino, &buf, (show_proc_ctx & show_sock_ctx) ? PROC_SOCK_CTX : PROC_CTX) > 0) { - printf(" users:(%s)", buf); + out(" users:(%s)", buf); free(buf); } } else if (show_users) { if (find_entry(s->ino, &buf, USERS) > 0) { - printf(" users:(%s)", buf); + out(" users:(%s)", buf); free(buf); } } @@ -1878,51 +2279,51 @@ static char *sprint_bw(char *buf, double bw) static void sctp_stats_print(struct sctp_info *s) { if (s->sctpi_tag) - printf(" tag:%x", s->sctpi_tag); + out(" tag:%x", s->sctpi_tag); if (s->sctpi_state) - printf(" state:%s", sctp_sstate_name[s->sctpi_state]); + out(" state:%s", sctp_sstate_name[s->sctpi_state]); if (s->sctpi_rwnd) - printf(" rwnd:%d", s->sctpi_rwnd); + out(" rwnd:%d", s->sctpi_rwnd); if (s->sctpi_unackdata) - printf(" unackdata:%d", s->sctpi_unackdata); + out(" unackdata:%d", s->sctpi_unackdata); if (s->sctpi_penddata) - printf(" penddata:%d", s->sctpi_penddata); + out(" penddata:%d", s->sctpi_penddata); if (s->sctpi_instrms) - printf(" instrms:%d", s->sctpi_instrms); + out(" instrms:%d", s->sctpi_instrms); if (s->sctpi_outstrms) - printf(" outstrms:%d", s->sctpi_outstrms); + out(" outstrms:%d", s->sctpi_outstrms); if (s->sctpi_inqueue) - printf(" inqueue:%d", s->sctpi_inqueue); + out(" inqueue:%d", s->sctpi_inqueue); if (s->sctpi_outqueue) - printf(" outqueue:%d", s->sctpi_outqueue); + out(" outqueue:%d", s->sctpi_outqueue); if (s->sctpi_overall_error) - printf(" overerr:%d", s->sctpi_overall_error); + out(" overerr:%d", s->sctpi_overall_error); if (s->sctpi_max_burst) - printf(" maxburst:%d", s->sctpi_max_burst); + out(" maxburst:%d", s->sctpi_max_burst); if (s->sctpi_maxseg) - printf(" maxseg:%d", s->sctpi_maxseg); + out(" maxseg:%d", s->sctpi_maxseg); if (s->sctpi_peer_rwnd) - printf(" prwnd:%d", s->sctpi_peer_rwnd); + out(" prwnd:%d", s->sctpi_peer_rwnd); if (s->sctpi_peer_tag) - printf(" ptag:%x", s->sctpi_peer_tag); + out(" ptag:%x", s->sctpi_peer_tag); if (s->sctpi_peer_capable) - printf(" pcapable:%d", s->sctpi_peer_capable); + out(" pcapable:%d", s->sctpi_peer_capable); if (s->sctpi_peer_sack) - printf(" psack:%d", s->sctpi_peer_sack); + out(" psack:%d", s->sctpi_peer_sack); if (s->sctpi_s_autoclose) - printf(" autoclose:%d", s->sctpi_s_autoclose); + out(" autoclose:%d", s->sctpi_s_autoclose); if (s->sctpi_s_adaptation_ind) - printf(" adapind:%d", s->sctpi_s_adaptation_ind); + out(" adapind:%d", s->sctpi_s_adaptation_ind); if (s->sctpi_s_pd_point) - printf(" pdpoint:%d", s->sctpi_s_pd_point); + out(" pdpoint:%d", s->sctpi_s_pd_point); if (s->sctpi_s_nodelay) - printf(" nodealy:%d", s->sctpi_s_nodelay); + out(" nodealy:%d", s->sctpi_s_nodelay); if (s->sctpi_s_disable_fragments) - printf(" nofrag:%d", s->sctpi_s_disable_fragments); + out(" nofrag:%d", s->sctpi_s_disable_fragments); if (s->sctpi_s_v4mapped) - printf(" v4mapped:%d", s->sctpi_s_v4mapped); + out(" v4mapped:%d", s->sctpi_s_v4mapped); if (s->sctpi_s_frag_interleave) - printf(" fraginl:%d", s->sctpi_s_frag_interleave); + out(" fraginl:%d", s->sctpi_s_frag_interleave); } static void tcp_stats_print(struct tcpstat *s) @@ -1930,65 +2331,65 @@ static void tcp_stats_print(struct tcpstat *s) char b1[64]; if (s->has_ts_opt) - printf(" ts"); + out(" ts"); if (s->has_sack_opt) - printf(" sack"); + out(" sack"); if (s->has_ecn_opt) - printf(" ecn"); + out(" ecn"); if (s->has_ecnseen_opt) - printf(" ecnseen"); + out(" ecnseen"); if (s->has_fastopen_opt) - printf(" fastopen"); + out(" fastopen"); if (s->cong_alg[0]) - printf(" %s", s->cong_alg); + out(" %s", s->cong_alg); if (s->has_wscale_opt) - printf(" wscale:%d,%d", s->snd_wscale, s->rcv_wscale); + out(" wscale:%d,%d", s->snd_wscale, s->rcv_wscale); if (s->rto) - printf(" rto:%g", s->rto); + out(" rto:%g", s->rto); if (s->backoff) - printf(" backoff:%u", s->backoff); + out(" backoff:%u", s->backoff); if (s->rtt) - printf(" rtt:%g/%g", s->rtt, s->rttvar); + out(" rtt:%g/%g", s->rtt, s->rttvar); if (s->ato) - printf(" ato:%g", s->ato); + out(" ato:%g", s->ato); if (s->qack) - printf(" qack:%d", s->qack); + out(" qack:%d", s->qack); if (s->qack & 1) - printf(" bidir"); + out(" bidir"); if (s->mss) - printf(" mss:%d", s->mss); + out(" mss:%d", s->mss); if (s->rcv_mss) - printf(" rcvmss:%d", s->rcv_mss); + out(" rcvmss:%d", s->rcv_mss); if (s->advmss) - printf(" advmss:%d", s->advmss); + out(" advmss:%d", s->advmss); if (s->cwnd) - printf(" cwnd:%u", s->cwnd); + out(" cwnd:%u", s->cwnd); if (s->ssthresh) - printf(" ssthresh:%d", s->ssthresh); + out(" ssthresh:%d", s->ssthresh); if (s->bytes_acked) - printf(" bytes_acked:%llu", s->bytes_acked); + out(" bytes_acked:%llu", s->bytes_acked); if (s->bytes_received) - printf(" bytes_received:%llu", s->bytes_received); + out(" bytes_received:%llu", s->bytes_received); if (s->segs_out) - printf(" segs_out:%u", s->segs_out); + out(" segs_out:%u", s->segs_out); if (s->segs_in) - printf(" segs_in:%u", s->segs_in); + out(" segs_in:%u", s->segs_in); if (s->data_segs_out) - printf(" data_segs_out:%u", s->data_segs_out); + out(" data_segs_out:%u", s->data_segs_out); if (s->data_segs_in) - printf(" data_segs_in:%u", s->data_segs_in); + out(" data_segs_in:%u", s->data_segs_in); if (s->dctcp && s->dctcp->enabled) { struct dctcpstat *dctcp = s->dctcp; - printf(" dctcp:(ce_state:%u,alpha:%u,ab_ecn:%u,ab_tot:%u)", - dctcp->ce_state, dctcp->alpha, dctcp->ab_ecn, - dctcp->ab_tot); + out(" dctcp:(ce_state:%u,alpha:%u,ab_ecn:%u,ab_tot:%u)", + dctcp->ce_state, dctcp->alpha, dctcp->ab_ecn, + dctcp->ab_tot); } else if (s->dctcp) { - printf(" dctcp:fallback_mode"); + out(" dctcp:fallback_mode"); } if (s->bbr_info) { @@ -1998,73 +2399,72 @@ static void tcp_stats_print(struct tcpstat *s) bw <<= 32; bw |= s->bbr_info->bbr_bw_lo; - printf(" bbr:(bw:%sbps,mrtt:%g", - sprint_bw(b1, bw * 8.0), - (double)s->bbr_info->bbr_min_rtt / 1000.0); + out(" bbr:(bw:%sbps,mrtt:%g", + sprint_bw(b1, bw * 8.0), + (double)s->bbr_info->bbr_min_rtt / 1000.0); if (s->bbr_info->bbr_pacing_gain) - printf(",pacing_gain:%g", - (double)s->bbr_info->bbr_pacing_gain / 256.0); + out(",pacing_gain:%g", + (double)s->bbr_info->bbr_pacing_gain / 256.0); if (s->bbr_info->bbr_cwnd_gain) - printf(",cwnd_gain:%g", - (double)s->bbr_info->bbr_cwnd_gain / 256.0); - printf(")"); + out(",cwnd_gain:%g", + (double)s->bbr_info->bbr_cwnd_gain / 256.0); + out(")"); } if (s->send_bps) - printf(" send %sbps", sprint_bw(b1, s->send_bps)); + out(" send %sbps", sprint_bw(b1, s->send_bps)); if (s->lastsnd) - printf(" lastsnd:%u", s->lastsnd); + out(" lastsnd:%u", s->lastsnd); if (s->lastrcv) - printf(" lastrcv:%u", s->lastrcv); + out(" lastrcv:%u", s->lastrcv); if (s->lastack) - printf(" lastack:%u", s->lastack); + out(" lastack:%u", s->lastack); if (s->pacing_rate) { - printf(" pacing_rate %sbps", sprint_bw(b1, s->pacing_rate)); + out(" pacing_rate %sbps", sprint_bw(b1, s->pacing_rate)); if (s->pacing_rate_max) - printf("/%sbps", sprint_bw(b1, - s->pacing_rate_max)); + out("/%sbps", sprint_bw(b1, s->pacing_rate_max)); } if (s->delivery_rate) - printf(" delivery_rate %sbps", sprint_bw(b1, s->delivery_rate)); + out(" delivery_rate %sbps", sprint_bw(b1, s->delivery_rate)); if (s->app_limited) - printf(" app_limited"); + out(" app_limited"); if (s->busy_time) { - printf(" busy:%llums", s->busy_time / 1000); + out(" busy:%llums", s->busy_time / 1000); if (s->rwnd_limited) - printf(" rwnd_limited:%llums(%.1f%%)", - s->rwnd_limited / 1000, - 100.0 * s->rwnd_limited / s->busy_time); + out(" rwnd_limited:%llums(%.1f%%)", + s->rwnd_limited / 1000, + 100.0 * s->rwnd_limited / s->busy_time); if (s->sndbuf_limited) - printf(" sndbuf_limited:%llums(%.1f%%)", - s->sndbuf_limited / 1000, - 100.0 * s->sndbuf_limited / s->busy_time); + out(" sndbuf_limited:%llums(%.1f%%)", + s->sndbuf_limited / 1000, + 100.0 * s->sndbuf_limited / s->busy_time); } if (s->unacked) - printf(" unacked:%u", s->unacked); + out(" unacked:%u", s->unacked); if (s->retrans || s->retrans_total) - printf(" retrans:%u/%u", s->retrans, s->retrans_total); + out(" retrans:%u/%u", s->retrans, s->retrans_total); if (s->lost) - printf(" lost:%u", s->lost); + out(" lost:%u", s->lost); if (s->sacked && s->ss.state != SS_LISTEN) - printf(" sacked:%u", s->sacked); + out(" sacked:%u", s->sacked); if (s->fackets) - printf(" fackets:%u", s->fackets); + out(" fackets:%u", s->fackets); if (s->reordering != 3) - printf(" reordering:%d", s->reordering); + out(" reordering:%d", s->reordering); if (s->rcv_rtt) - printf(" rcv_rtt:%g", s->rcv_rtt); + out(" rcv_rtt:%g", s->rcv_rtt); if (s->rcv_space) - printf(" rcv_space:%d", s->rcv_space); + out(" rcv_space:%d", s->rcv_space); if (s->rcv_ssthresh) - printf(" rcv_ssthresh:%u", s->rcv_ssthresh); + out(" rcv_ssthresh:%u", s->rcv_ssthresh); if (s->not_sent) - printf(" notsent:%u", s->not_sent); + out(" notsent:%u", s->not_sent); if (s->min_rtt) - printf(" minrtt:%g", s->min_rtt); + out(" minrtt:%g", s->min_rtt); } static void tcp_timer_print(struct tcpstat *s) @@ -2081,18 +2481,18 @@ static void tcp_timer_print(struct tcpstat *s) if (s->timer) { if (s->timer > 4) s->timer = 5; - printf(" timer:(%s,%s,%d)", - tmr_name[s->timer], - print_ms_timer(s->timeout), - s->retrans); + out(" timer:(%s,%s,%d)", + tmr_name[s->timer], + print_ms_timer(s->timeout), + s->retrans); } } static void sctp_timer_print(struct tcpstat *s) { if (s->timer) - printf(" timer:(T3_RTX,%s,%d)", - print_ms_timer(s->timeout), s->retrans); + out(" timer:(T3_RTX,%s,%d)", + print_ms_timer(s->timeout), s->retrans); } static int tcp_show_line(char *line, const struct filter *f, int family) @@ -2151,13 +2551,12 @@ static int tcp_show_line(char *line, const struct filter *f, int family) if (show_details) { sock_details_print(&s.ss); if (opt[0]) - printf(" opt:\"%s\"", opt); + out(" opt:\"%s\"", opt); } if (show_tcpinfo) tcp_stats_print(&s); - printf("\n"); return 0; } @@ -2200,44 +2599,44 @@ static void print_skmeminfo(struct rtattr *tb[], int attrtype) const struct inet_diag_meminfo *minfo = RTA_DATA(tb[INET_DIAG_MEMINFO]); - printf(" mem:(r%u,w%u,f%u,t%u)", - minfo->idiag_rmem, - minfo->idiag_wmem, - minfo->idiag_fmem, - minfo->idiag_tmem); + out(" mem:(r%u,w%u,f%u,t%u)", + minfo->idiag_rmem, + minfo->idiag_wmem, + minfo->idiag_fmem, + minfo->idiag_tmem); } return; } skmeminfo = RTA_DATA(tb[attrtype]); - printf(" skmem:(r%u,rb%u,t%u,tb%u,f%u,w%u,o%u", - skmeminfo[SK_MEMINFO_RMEM_ALLOC], - skmeminfo[SK_MEMINFO_RCVBUF], - skmeminfo[SK_MEMINFO_WMEM_ALLOC], - skmeminfo[SK_MEMINFO_SNDBUF], - skmeminfo[SK_MEMINFO_FWD_ALLOC], - skmeminfo[SK_MEMINFO_WMEM_QUEUED], - skmeminfo[SK_MEMINFO_OPTMEM]); + out(" skmem:(r%u,rb%u,t%u,tb%u,f%u,w%u,o%u", + skmeminfo[SK_MEMINFO_RMEM_ALLOC], + skmeminfo[SK_MEMINFO_RCVBUF], + skmeminfo[SK_MEMINFO_WMEM_ALLOC], + skmeminfo[SK_MEMINFO_SNDBUF], + skmeminfo[SK_MEMINFO_FWD_ALLOC], + skmeminfo[SK_MEMINFO_WMEM_QUEUED], + skmeminfo[SK_MEMINFO_OPTMEM]); if (RTA_PAYLOAD(tb[attrtype]) >= (SK_MEMINFO_BACKLOG + 1) * sizeof(__u32)) - printf(",bl%u", skmeminfo[SK_MEMINFO_BACKLOG]); + out(",bl%u", skmeminfo[SK_MEMINFO_BACKLOG]); if (RTA_PAYLOAD(tb[attrtype]) >= (SK_MEMINFO_DROPS + 1) * sizeof(__u32)) - printf(",d%u", skmeminfo[SK_MEMINFO_DROPS]); + out(",d%u", skmeminfo[SK_MEMINFO_DROPS]); - printf(")"); + out(")"); } static void print_md5sig(struct tcp_diag_md5sig *sig) { - printf("%s/%d=", - format_host(sig->tcpm_family, - sig->tcpm_family == AF_INET6 ? 16 : 4, - &sig->tcpm_addr), - sig->tcpm_prefixlen); + out("%s/%d=", + format_host(sig->tcpm_family, + sig->tcpm_family == AF_INET6 ? 16 : 4, + &sig->tcpm_addr), + sig->tcpm_prefixlen); print_escape_buf(sig->tcpm_key, sig->tcpm_keylen, " ,"); } @@ -2382,10 +2781,10 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r, struct tcp_diag_md5sig *sig = RTA_DATA(tb[INET_DIAG_MD5SIG]); int len = RTA_PAYLOAD(tb[INET_DIAG_MD5SIG]); - printf(" md5keys:"); + out(" md5keys:"); print_md5sig(sig++); for (len -= sizeof(*sig); len > 0; len -= sizeof(*sig)) { - printf(","); + out(","); print_md5sig(sig++); } } @@ -2420,18 +2819,18 @@ static void sctp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r, len = RTA_PAYLOAD(tb[INET_DIAG_LOCALS]); sa = RTA_DATA(tb[INET_DIAG_LOCALS]); - printf("locals:%s", format_host_sa(sa)); + out("locals:%s", format_host_sa(sa)); for (sa++, len -= sizeof(*sa); len > 0; sa++, len -= sizeof(*sa)) - printf(",%s", format_host_sa(sa)); + out(",%s", format_host_sa(sa)); } if (tb[INET_DIAG_PEERS]) { len = RTA_PAYLOAD(tb[INET_DIAG_PEERS]); sa = RTA_DATA(tb[INET_DIAG_PEERS]); - printf(" peers:%s", format_host_sa(sa)); + out(" peers:%s", format_host_sa(sa)); for (sa++, len -= sizeof(*sa); len > 0; sa++, len -= sizeof(*sa)) - printf(",%s", format_host_sa(sa)); + out(",%s", format_host_sa(sa)); } if (tb[INET_DIAG_INFO]) { struct sctp_info *info; @@ -2518,18 +2917,19 @@ static int inet_show_sock(struct nlmsghdr *nlh, if (show_details) { sock_details_print(s); if (s->local.family == AF_INET6 && tb[INET_DIAG_SKV6ONLY]) - printf(" v6only:%u", v6only); + out(" v6only:%u", v6only); if (tb[INET_DIAG_SHUTDOWN]) { unsigned char mask; mask = rta_getattr_u8(tb[INET_DIAG_SHUTDOWN]); - printf(" %c-%c", mask & 1 ? '-' : '<', mask & 2 ? '-' : '>'); + out(" %c-%c", + mask & 1 ? '-' : '<', mask & 2 ? '-' : '>'); } } if (show_mem || (show_tcpinfo && s->type != IPPROTO_UDP)) { - printf("\n\t"); + out("\n\t"); if (s->type == IPPROTO_SCTP) sctp_show_info(nlh, r, tb); else @@ -2537,7 +2937,6 @@ static int inet_show_sock(struct nlmsghdr *nlh, } sctp_ino = s->ino; - printf("\n"); return 0; } @@ -2993,9 +3392,8 @@ static int dgram_show_line(char *line, const struct filter *f, int family) inet_stats_print(&s, false); if (show_details && opt[0]) - printf(" opt:\"%s\"", opt); + out(" opt:\"%s\"", opt); - printf("\n"); return 0; } @@ -3170,10 +3568,10 @@ static int unix_show_sock(const struct sockaddr_nl *addr, struct nlmsghdr *nlh, unsigned char mask; mask = rta_getattr_u8(tb[UNIX_DIAG_SHUTDOWN]); - printf(" %c-%c", mask & 1 ? '-' : '<', mask & 2 ? '-' : '>'); + out(" %c-%c", + mask & 1 ? '-' : '<', mask & 2 ? '-' : '>'); } } - printf("\n"); return 0; } @@ -3330,8 +3728,6 @@ static int unix_show(struct filter *f) if (++cnt > MAX_UNIX_REMEMBER) { while (list) { unix_stats_print(list, f); - printf("\n"); - unix_list_drop_first(&list); } cnt = 0; @@ -3340,8 +3736,6 @@ static int unix_show(struct filter *f) fclose(fp); while (list) { unix_stats_print(list, f); - printf("\n"); - unix_list_drop_first(&list); } @@ -3386,12 +3780,12 @@ static int packet_stats_print(struct sockstat *s, const struct filter *f) static void packet_show_ring(struct packet_diag_ring *ring) { - printf("blk_size:%d", ring->pdr_block_size); - printf(",blk_nr:%d", ring->pdr_block_nr); - printf(",frm_size:%d", ring->pdr_frame_size); - printf(",frm_nr:%d", ring->pdr_frame_nr); - printf(",tmo:%d", ring->pdr_retire_tmo); - printf(",features:0x%x", ring->pdr_features); + out("blk_size:%d", ring->pdr_block_size); + out(",blk_nr:%d", ring->pdr_block_nr); + out(",frm_size:%d", ring->pdr_frame_size); + out(",frm_nr:%d", ring->pdr_frame_nr); + out(",tmo:%d", ring->pdr_retire_tmo); + out(",features:0x%x", ring->pdr_features); } static int packet_show_sock(const struct sockaddr_nl *addr, @@ -3449,56 +3843,56 @@ static int packet_show_sock(const struct sockaddr_nl *addr, if (show_details) { if (pinfo) { - printf("\n\tver:%d", pinfo->pdi_version); - printf(" cpy_thresh:%d", pinfo->pdi_copy_thresh); - printf(" flags( "); + out("\n\tver:%d", pinfo->pdi_version); + out(" cpy_thresh:%d", pinfo->pdi_copy_thresh); + out(" flags( "); if (pinfo->pdi_flags & PDI_RUNNING) - printf("running"); + out("running"); if (pinfo->pdi_flags & PDI_AUXDATA) - printf(" auxdata"); + out(" auxdata"); if (pinfo->pdi_flags & PDI_ORIGDEV) - printf(" origdev"); + out(" origdev"); if (pinfo->pdi_flags & PDI_VNETHDR) - printf(" vnethdr"); + out(" vnethdr"); if (pinfo->pdi_flags & PDI_LOSS) - printf(" loss"); + out(" loss"); if (!pinfo->pdi_flags) - printf("0"); - printf(" )"); + out("0"); + out(" )"); } if (ring_rx) { - printf("\n\tring_rx("); + out("\n\tring_rx("); packet_show_ring(ring_rx); - printf(")"); + out(")"); } if (ring_tx) { - printf("\n\tring_tx("); + out("\n\tring_tx("); packet_show_ring(ring_tx); - printf(")"); + out(")"); } if (has_fanout) { uint16_t type = (fanout >> 16) & 0xffff; - printf("\n\tfanout("); - printf("id:%d,", fanout & 0xffff); - printf("type:"); + out("\n\tfanout("); + out("id:%d,", fanout & 0xffff); + out("type:"); if (type == 0) - printf("hash"); + out("hash"); else if (type == 1) - printf("lb"); + out("lb"); else if (type == 2) - printf("cpu"); + out("cpu"); else if (type == 3) - printf("roll"); + out("roll"); else if (type == 4) - printf("random"); + out("random"); else if (type == 5) - printf("qm"); + out("qm"); else - printf("0x%x", type); + out("0x%x", type); - printf(")"); + out(")"); } } @@ -3508,15 +3902,14 @@ static int packet_show_sock(const struct sockaddr_nl *addr, int num = RTA_PAYLOAD(tb[PACKET_DIAG_FILTER]) / sizeof(struct sock_filter); - printf("\n\tbpf filter (%d): ", num); + out("\n\tbpf filter (%d): ", num); while (num) { - printf(" 0x%02x %u %u %u,", - fil->code, fil->jt, fil->jf, fil->k); + out(" 0x%02x %u %u %u,", + fil->code, fil->jt, fil->jf, fil->k); num--; fil++; } } - printf("\n"); return 0; } @@ -3559,7 +3952,6 @@ static int packet_show_line(char *buf, const struct filter *f, int fam) if (packet_stats_print(&stat, f)) return 0; - printf("\n"); return 0; } @@ -3672,14 +4064,13 @@ static int netlink_show_one(struct filter *f, else if (pid > 0) getpidcon(pid, &pid_context); - printf(" proc_ctx=%s", pid_context ? : "unavailable"); + out(" proc_ctx=%s", pid_context ? : "unavailable"); free(pid_context); } if (show_details) { - printf(" sk=%llx cb=%llx groups=0x%08x", sk, cb, groups); + out(" sk=%llx cb=%llx groups=0x%08x", sk, cb, groups); } - printf("\n"); return 0; } @@ -3715,9 +4106,8 @@ static int netlink_show_sock(const struct sockaddr_nl *addr, } if (show_mem) { - printf("\t"); + out("\t"); print_skmeminfo(tb, NETLINK_DIAG_MEMINFO); - printf("\n"); } return 0; @@ -3807,8 +4197,6 @@ static void vsock_stats_print(struct sockstat *s, struct filter *f) vsock_addr_print(&s->remote, s->rport); proc_ctx_print(s); - - printf("\n"); } static int vsock_show_sock(const struct sockaddr_nl *addr, @@ -4232,7 +4620,7 @@ int main(int argc, char *argv[]) FILE *filter_fp = NULL; int ch; int state_filter = 0; - int addrp_width, screen_width = 80; + int screen_width = 80; while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spbEf:miA:D:F:vVzZN:KHS", @@ -4528,13 +4916,11 @@ int main(int argc, char *argv[]) if (ssfilter_parse(¤t_filter.f, argc, argv, filter_fp)) usage(); - netid_width = 0; - if (current_filter.dbs&(current_filter.dbs-1)) - netid_width = 5; + if (!(current_filter.dbs & (current_filter.dbs - 1))) + columns[COL_NETID].disabled = 1; - state_width = 0; - if (current_filter.states&(current_filter.states-1)) - state_width = 10; + if (!(current_filter.states & (current_filter.states - 1))) + columns[COL_STATE].disabled = 1; if (isatty(STDOUT_FILENO)) { struct winsize w; @@ -4545,49 +4931,8 @@ int main(int argc, char *argv[]) } } - addrp_width = screen_width; - if (netid_width) - addrp_width -= netid_width + 1; - if (state_width) - addrp_width -= state_width + 1; - addrp_width -= 14; - - if (addrp_width&1) { - if (netid_width) - netid_width++; - else if (state_width) - state_width++; - else - odd_width_pad = " "; - } - - addrp_width /= 2; - addrp_width--; - - serv_width = resolve_services ? 7 : 5; - - if (addrp_width < 15+serv_width+1) - addrp_width = 15+serv_width+1; - - addr_width = addrp_width - serv_width - 1; - - if (show_header) { - if (netid_width) - printf("%-*s ", netid_width, "Netid"); - if (state_width) - printf("%-*s ", state_width, "State"); - printf("%-6s %-6s %s", "Recv-Q", "Send-Q", odd_width_pad); - } - - /* Make enough space for the local/remote port field */ - addr_width -= 13; - serv_width += 13; - - if (show_header) { - printf("%*s:%-*s %*s:%-*s\n", - addr_width, "Local Address", serv_width, "Port", - addr_width, "Peer Address", serv_width, "Port"); - } + if (show_header) + print_header(); fflush(stdout); @@ -4616,5 +4961,7 @@ int main(int argc, char *argv[]) if (show_users || show_proc_ctx || show_sock_ctx) user_ent_destroy(); + render(screen_width); + return 0; }