ss: Fix rendering of continuous output (-E, --events)

Roman Mashak reported that ss currently shows no output when it
should continuously report information about terminated sockets
(-E, --events switch).

This happens because I missed this case in 691bd854bf ("ss:
Buffer raw fields first, then render them as a table") and the
rendering function is simply not called.

To fix this, we need to:

- call render() every time we need to display new socket events
  from generic_show_sock(), which is only used to follow events.
  Always call it even if specific socket display functions
  return errors to ensure we clean up buffers

- get the screen width every time we have new events to display,
  thus factor out getting the screen width from main() into a
  function we'll call whenever we calculate columns width

- reset the current field pointer after rendering, more output
  might come after render() is called

Reported-by: Roman Mashak <mrv@mojatatu.com>
Fixes: 691bd854bf ("ss: Buffer raw fields first, then render them as a table")
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Tested-by: Roman Mashak <mrv@mojatatu.com>
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
This commit is contained in:
Stefano Brivio 2018-03-23 09:37:05 +01:00 committed by Stephen Hemminger
parent 79f49f58aa
commit 32ea3d54b4
1 changed files with 40 additions and 21 deletions

View File

@ -1106,15 +1106,33 @@ static void buf_free_all(void)
buffer.head = NULL; buffer.head = NULL;
} }
/* Get current screen width, default to 80 columns if TIOCGWINSZ fails */
static int render_screen_width(void)
{
int width = 80;
if (isatty(STDOUT_FILENO)) {
struct winsize w;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) {
if (w.ws_col > 0)
width = w.ws_col;
}
}
return width;
}
/* Calculate column width from contents length. If columns don't fit on one /* 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 * line, break them into the least possible amount of lines and keep them
* aligned across lines. Available screen space is equally spread between fields * aligned across lines. Available screen space is equally spread between fields
* as additional spacing. * as additional spacing.
*/ */
static void render_calc_width(int screen_width) static void render_calc_width(void)
{ {
int first, len = 0, linecols = 0; int screen_width = render_screen_width();
struct column *c, *eol = columns - 1; struct column *c, *eol = columns - 1;
int first, len = 0, linecols = 0;
/* First pass: set width for each column to measured content length */ /* First pass: set width for each column to measured content length */
for (first = 1, c = columns; c - columns < COL_MAX; c++) { for (first = 1, c = columns; c - columns < COL_MAX; c++) {
@ -1195,7 +1213,7 @@ newline:
} }
/* Render buffered output with spacing and delimiters, then free up buffers */ /* Render buffered output with spacing and delimiters, then free up buffers */
static void render(int screen_width) static void render(void)
{ {
struct buf_token *token; struct buf_token *token;
int printed, line_started = 0; int printed, line_started = 0;
@ -1209,7 +1227,7 @@ static void render(int screen_width)
/* Ensure end alignment of last token, it wasn't necessarily flushed */ /* Ensure end alignment of last token, it wasn't necessarily flushed */
buffer.tail->end += buffer.cur->len % 2; buffer.tail->end += buffer.cur->len % 2;
render_calc_width(screen_width); render_calc_width();
/* Rewind and replay */ /* Rewind and replay */
buffer.tail = buffer.head; buffer.tail = buffer.head;
@ -1245,6 +1263,7 @@ static void render(int screen_width)
} }
buf_free_all(); buf_free_all();
current_field = columns;
} }
static void sock_state_print(struct sockstat *s) static void sock_state_print(struct sockstat *s)
@ -4264,23 +4283,33 @@ static int generic_show_sock(const struct sockaddr_nl *addr,
{ {
struct sock_diag_msg *r = NLMSG_DATA(nlh); struct sock_diag_msg *r = NLMSG_DATA(nlh);
struct inet_diag_arg inet_arg = { .f = arg, .protocol = IPPROTO_MAX }; struct inet_diag_arg inet_arg = { .f = arg, .protocol = IPPROTO_MAX };
int ret;
switch (r->sdiag_family) { switch (r->sdiag_family) {
case AF_INET: case AF_INET:
case AF_INET6: case AF_INET6:
inet_arg.rth = inet_arg.f->rth_for_killing; inet_arg.rth = inet_arg.f->rth_for_killing;
return show_one_inet_sock(addr, nlh, &inet_arg); ret = show_one_inet_sock(addr, nlh, &inet_arg);
break;
case AF_UNIX: case AF_UNIX:
return unix_show_sock(addr, nlh, arg); ret = unix_show_sock(addr, nlh, arg);
break;
case AF_PACKET: case AF_PACKET:
return packet_show_sock(addr, nlh, arg); ret = packet_show_sock(addr, nlh, arg);
break;
case AF_NETLINK: case AF_NETLINK:
return netlink_show_sock(addr, nlh, arg); ret = netlink_show_sock(addr, nlh, arg);
break;
case AF_VSOCK: case AF_VSOCK:
return vsock_show_sock(addr, nlh, arg); ret = vsock_show_sock(addr, nlh, arg);
break;
default: default:
return -1; ret = -1;
} }
render();
return ret;
} }
static int handle_follow_request(struct filter *f) static int handle_follow_request(struct filter *f)
@ -4647,7 +4676,6 @@ int main(int argc, char *argv[])
FILE *filter_fp = NULL; FILE *filter_fp = NULL;
int ch; int ch;
int state_filter = 0; int state_filter = 0;
int screen_width = 80;
while ((ch = getopt_long(argc, argv, while ((ch = getopt_long(argc, argv,
"dhaletuwxnro460spbEf:miA:D:F:vVzZN:KHS", "dhaletuwxnro460spbEf:miA:D:F:vVzZN:KHS",
@ -4949,15 +4977,6 @@ int main(int argc, char *argv[])
if (!(current_filter.states & (current_filter.states - 1))) if (!(current_filter.states & (current_filter.states - 1)))
columns[COL_STATE].disabled = 1; columns[COL_STATE].disabled = 1;
if (isatty(STDOUT_FILENO)) {
struct winsize w;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) {
if (w.ws_col > 0)
screen_width = w.ws_col;
}
}
if (show_header) if (show_header)
print_header(); print_header();
@ -4988,7 +5007,7 @@ int main(int argc, char *argv[])
if (show_users || show_proc_ctx || show_sock_ctx) if (show_users || show_proc_ctx || show_sock_ctx)
user_ent_destroy(); user_ent_destroy();
render(screen_width); render();
return 0; return 0;
} }