From keramida@ceid.upatras.gr  Wed Jan 16 02:56:05 2008
Return-Path: <keramida@ceid.upatras.gr>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id 8EE7516A46B
	for <FreeBSD-gnats-submit@freebsd.org>; Wed, 16 Jan 2008 02:56:05 +0000 (UTC)
	(envelope-from keramida@ceid.upatras.gr)
Received: from mx-out.forthnet.gr (mx-out.forthnet.gr [193.92.150.104])
	by mx1.freebsd.org (Postfix) with ESMTP id F376113C461
	for <FreeBSD-gnats-submit@freebsd.org>; Wed, 16 Jan 2008 02:56:04 +0000 (UTC)
	(envelope-from keramida@ceid.upatras.gr)
Received: from mx-av-03.forthnet.gr (mx-av.forthnet.gr [193.92.150.27])
	by mx-out-02.forthnet.gr (8.14.0/8.14.0) with ESMTP id m0G2u3Ip004311;
	Wed, 16 Jan 2008 04:56:03 +0200
Received: from MX-IN-04.forthnet.gr (mx-in-04.forthnet.gr [193.92.150.163])
	by mx-av-03.forthnet.gr (8.14.1/8.14.1) with ESMTP id m0G2u2bE003759;
	Wed, 16 Jan 2008 04:56:02 +0200
Received: from kobe.laptop (ppp176-5.adsl.forthnet.gr [62.1.179.5])
	by MX-IN-04.forthnet.gr (8.14.2/8.14.2) with ESMTP id m0G2u1ol010222;
	Wed, 16 Jan 2008 04:56:02 +0200
Received: from kobe.laptop (kobe.laptop [127.0.0.1])
	by kobe.laptop (8.14.2/8.14.2) with ESMTP id m0G2u0N1004215;
	Wed, 16 Jan 2008 04:56:01 +0200 (EET)
	(envelope-from keramida@kobe.laptop)
Received: (from keramida@localhost)
	by kobe.laptop (8.14.2/8.14.2/Submit) id m0G2u0US004214;
	Wed, 16 Jan 2008 04:56:00 +0200 (EET)
	(envelope-from keramida)
Message-Id: <200801160256.m0G2u0US004214@kobe.laptop>
Date: Wed, 16 Jan 2008 04:56:00 +0200 (EET)
From: Giorgos Keramidas <keramida@freebsd.org>
Reply-To: Giorgos Keramidas <keramida@freebsd.org>
To: FreeBSD-gnats-submit@freebsd.org
Cc: bde@freebsd.org (Bruce Evans)
Subject: [PATCH] make iostat a bit smarter about the number of tty rows
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         119705
>Category:       bin
>Synopsis:       [patch] make iostat(8) a bit smarter about the number of tty rows
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    keramida
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Wed Jan 16 03:00:03 UTC 2008
>Closed-Date:    Fri Jan 30 18:19:38 UTC 2009
>Last-Modified:  Fri Jan 30 18:19:38 UTC 2009
>Originator:     Giorgos Keramidas
>Release:        FreeBSD 8.0-CURRENT i386
>Organization:
>Environment:
System: FreeBSD kobe 8.0-CURRENT FreeBSD 8.0-CURRENT #0: \
Tue Jan 15 21:10:19 EET 2008 build@kobe:/home/build/obj/home/build/src/sys/KOBE i386

>Description:

The current iostat(8) utility repeats the header line every 20 lines,
and this number is hardcoded in the source of iostat.  This means that
with very short xterm windows, iostat may not display one header per
terminal, and when the window size changes, it doesn't detect this and
update its internal row count.

The attached patch modifies iostat() to trap SIGWINCH, and use it to
update its internal 'max rows' limit of output lines.  It also forces
a new header to be prepended to the output on every SIGWINCH, and it
changes the hardcoded '20' lines of output to 'wrows - 3' (two rows
are needed for the header itself, and one for the cursor below the last
output line of each screenful).

>How-To-Repeat:

Resize an xterm window to 80x10 lines, and watch the output of iostat
for a while, using:

	% iostat 1

>Fix:

--- iostat-rows.patch begins here ---
diff -r d03d5ae8b3ef -r 21be4228379f usr.sbin/iostat/iostat.c
--- a/usr.sbin/iostat/iostat.c	Tue Jan 15 15:04:40 2008 +0000
+++ b/usr.sbin/iostat/iostat.c	Wed Jan 16 04:50:48 2008 +0200
@@ -113,6 +113,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <termios.h>
 #include <unistd.h>
 #include <limits.h>
 #include <devstat.h>
@@ -136,12 +137,16 @@ struct device_selection *dev_select;
 struct device_selection *dev_select;
 int maxshowdevs;
 volatile sig_atomic_t headercount;
+volatile sig_atomic_t wresized;		/* Tty resized, when non-zero. */
+unsigned short wrows;			/* Current number of tty rows. */
 int dflag = 0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0, Kflag = 0;
 int xflag = 0, zflag = 0;
 
 /* local function declarations */
 static void usage(void);
 static void needhdr(int signo);
+static void needresize(int signo);
+static void doresize(void);
 static void phdr(void);
 static void devstats(int perf_select, long double etime, int havelast);
 static void cpustats(void);
@@ -425,8 +430,9 @@ main(int argc, char **argv)
 	 * print out the header again.
 	 */
 	(void)signal(SIGCONT, needhdr);
+	(void)signal(SIGWINCH, needresize);
 
-	for (headercount = 1;;) {
+	for (wresized = 1, headercount = 1;;) {
 		struct devinfo *tmp_dinfo;
 		long tmp;
 		long double etime;
@@ -451,7 +457,9 @@ main(int argc, char **argv)
 
 		if (!--headercount) {
 			phdr();
-			headercount = 20;
+			if (wresized != 0)
+				doresize();
+			headercount = wrows - 3;
 		}
 
 		tmp_dinfo = last.dinfo;
@@ -493,7 +501,9 @@ main(int argc, char **argv)
 				break;
 			case 1:
 				phdr();
-				headercount = 20;
+				if (wresized != 0)
+					doresize();
+				headercount = wrows - 3;
 				break;
 			default:
 				break;
@@ -528,7 +538,9 @@ main(int argc, char **argv)
 				break;
 			case 1:
 				phdr();
-				headercount = 20;
+				if (wresized != 0)
+					doresize();
+				headercount = wrows - 3;
 				break;
 			default:
 				break;
@@ -587,6 +599,45 @@ needhdr(int signo)
 {
 
 	headercount = 1;
+}
+
+/*
+ * When the terminal is resized, force an update of the maximum number of rows
+ * printed between each header repetition.  Then force a new header to be
+ * prepended to the next output.
+ */
+void
+needresize(int signo)
+{
+
+	wresized = 1;
+	headercount = 1;
+}
+
+/*
+ * Update the global `wrows' count of terminal rows.
+ */
+void
+doresize(void)
+{
+	int fd, status;
+	struct winsize w;
+
+	fd = fileno(stdout);
+	if (isatty(fd) == 0) {
+		wrows = 20;			/* Traditional default. */
+		return;
+	}
+
+	for (;;) {
+		status = ioctl(fd, TIOCGWINSZ, &w);
+		if (status == -1 && errno == EINTR)
+			continue;
+		else if (status == -1)
+			err(1, "ioctl");
+		wrows = w.ws_row;
+		break;
+	}
 }
 
 static void
--- iostat-rows.patch ends here ---


>Release-Note:
>Audit-Trail:

From: Maxim Konovalov <maxim@macomnet.ru>
To: Giorgos Keramidas <keramida@freebsd.org>
Cc: Bruce Evans <bde@freebsd.org>, bug-followup@freebsd.org
Subject: Re: bin/119705: [PATCH] make iostat a bit smarter about the number
 of tty rows
Date: Wed, 16 Jan 2008 09:29:16 +0300 (MSK)

 Hi Giorgos,
 
 It seems for me you forget to clear wresized flag -- I added
 printf("%s\n", __func__); at the beginning of doresize() to notice
 this.
 
 IMHO the isatty() check doesn't belong to a pseudo signal handler
 doresize().  Install SIGWINCH handler if and only if stdout is a tty.
 
 Do you have plans to add this feature to netstat, vmstat and all other
 similar tools? :-)
 
 -- 
 Maxim Konovalov

From: Giorgos Keramidas <keramida@freebsd.org>
To: Maxim Konovalov <maxim@macomnet.ru>
Cc: Bruce Evans <bde@freebsd.org>, bug-followup@freebsd.org
Subject: Re: bin/119705: [PATCH] make iostat a bit smarter about the number
	of tty rows
Date: Wed, 16 Jan 2008 09:13:28 +0200

 On 2008-01-16 09:29, Maxim Konovalov <maxim@macomnet.ru> wrote:
 > Hi Giorgos,
 > It seems for me you forget to clear wresized flag -- I added
 > printf("%s\n", __func__); at the beginning of doresize() to notice
 > this.
 
 Indeed.  Thanks for catching this.
 
 > IMHO the isatty() check doesn't belong to a pseudo signal handler
 > doresize().  Install SIGWINCH handler if and only if stdout is a tty.
 
 That's a good suggestion too, and it will save one isatty() call for
 each SIGWINCH delivery.  I'll update the patch and follow-up with a
 second version in the PR.  Nice...
 
 > Do you have plans to add this feature to netstat, vmstat and all other
 > similar tools? :-)
 
 That didn't occur to me, but it's probably a good idea :)
 

From: Giorgos Keramidas <keramida@freebsd.org>
To: Maxim Konovalov <maxim@macomnet.ru>
Cc: Bruce Evans <bde@freebsd.org>, bug-followup@freebsd.org
Subject: Re: bin/119705: [PATCH] make iostat a bit smarter about the number
	of tty rows
Date: Wed, 16 Jan 2008 09:39:37 +0200

 On 2008-01-16 09:13, Giorgos Keramidas <keramida@freebsd.org> wrote:
 > On 2008-01-16 09:29, Maxim Konovalov <maxim@macomnet.ru> wrote:
 > > Hi Giorgos,
 > > It seems for me you forget to clear wresized flag -- I added
 > > printf("%s\n", __func__); at the beginning of doresize() to notice
 > > this.
 >
 > Indeed.  Thanks for catching this.
 >
 > > IMHO the isatty() check doesn't belong to a pseudo signal handler
 > > doresize().  Install SIGWINCH handler if and only if stdout is a tty.
 >
 > That's a good suggestion too, and it will save one isatty() call for
 > each SIGWINCH delivery.  I'll update the patch and follow-up with a
 > second version in the PR.  Nice...
 
 Both fixed, I think.  The new version of the patch is attached below,
 after the following changes:
 
   iostat-rows: fix a couple of nits reported by maxim
 
   * Reset wresized when doresize() completes its work
   * Only call isatty(fileno(stdout)) once, instead of every time
   doresize() is called
   * Only install a SIGWINCH handler if isatty() is true
 
   While here, also fix another potential problem, when a terminal
   has 3 rows or less. Instead of blindly subtracting 3 from w.ws_row
   and ending up with a negative wrows value, default to 20 in that
   case.
 
   Since the traditional value of 20 rows is now used in two places with
   this change, #define it as IOSTAT_DEFAULT_ROWS, to avoid repeating a
   magic number multiple times.
 
 %%%
 diff --git a/usr.sbin/iostat/iostat.c b/usr.sbin/iostat/iostat.c
 --- a/usr.sbin/iostat/iostat.c
 +++ b/usr.sbin/iostat/iostat.c
 @@ -113,6 +113,7 @@
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
 +#include <termios.h>
  #include <unistd.h>
  #include <limits.h>
  #include <devstat.h>
 @@ -131,17 +132,23 @@ struct nlist namelist[] = {
  	{ NULL },
  };
 
 +#define	IOSTAT_DEFAULT_ROWS	20	/* Traditional default `wrows' */
 +
  struct statinfo cur, last;
  int num_devices;
  struct device_selection *dev_select;
  int maxshowdevs;
  volatile sig_atomic_t headercount;
 +volatile sig_atomic_t wresized;		/* Tty resized, when non-zero. */
 +unsigned short wrows;			/* Current number of tty rows. */
  int dflag = 0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0, Kflag = 0;
  int xflag = 0, zflag = 0;
 
  /* local function declarations */
  static void usage(void);
  static void needhdr(int signo);
 +static void needresize(int signo);
 +static void doresize(void);
  static void phdr(void);
  static void devstats(int perf_select, long double etime, int havelast);
  static void cpustats(void);
 @@ -426,6 +433,20 @@ main(int argc, char **argv)
  	 */
  	(void)signal(SIGCONT, needhdr);
 
 +	/*
 +	 * If our standard output is a tty, then install a SIGWINCH handler
 +	 * and set wresized so that our first iteration through the main
 +	 * iostat loop will peek at the terminal's current rows to find out
 +	 * how many lines can fit in a screenful of output.
 +	 */
 +	if (isatty(fileno(stdout)) != 0) {
 +		wresized = 1;
 +		(void)signal(SIGWINCH, needresize);
 +	} else {
 +		wrows = IOSTAT_DEFAULT_ROWS;
 +		return;
 +	}
 +
  	for (headercount = 1;;) {
  		struct devinfo *tmp_dinfo;
  		long tmp;
 @@ -451,7 +472,9 @@ main(int argc, char **argv)
 
  		if (!--headercount) {
  			phdr();
 -			headercount = 20;
 +			if (wresized != 0)
 +				doresize();
 +			headercount = wrows;
  		}
 
  		tmp_dinfo = last.dinfo;
 @@ -493,7 +516,9 @@ main(int argc, char **argv)
  				break;
  			case 1:
  				phdr();
 -				headercount = 20;
 +				if (wresized != 0)
 +					doresize();
 +				headercount = wrows;
  				break;
  			default:
  				break;
 @@ -528,7 +553,9 @@ main(int argc, char **argv)
  				break;
  			case 1:
  				phdr();
 -				headercount = 20;
 +				if (wresized != 0)
 +					doresize();
 +				headercount = wrows;
  				break;
  			default:
  				break;
 @@ -587,6 +614,48 @@ needhdr(int signo)
  {
 
  	headercount = 1;
 +}
 +
 +/*
 + * When the terminal is resized, force an update of the maximum number of rows
 + * printed between each header repetition.  Then force a new header to be
 + * prepended to the next output.
 + */
 +void
 +needresize(int signo)
 +{
 +
 +	wresized = 1;
 +	headercount = 1;
 +}
 +
 +/*
 + * Update the global `wrows' count of terminal rows.
 + */
 +void
 +doresize(void)
 +{
 +	int status;
 +	struct winsize w;
 +
 +	printf("wresized = %d\n", wresized);
 +	for (;;) {
 +		status = ioctl(fileno(stdout), TIOCGWINSZ, &w);
 +		if (status == -1 && errno == EINTR)
 +			continue;
 +		else if (status == -1)
 +			err(1, "ioctl");
 +		if (w.ws_row > 3)
 +			wrows = w.ws_row - 3;
 +		else
 +			wrows = IOSTAT_DEFAULT_ROWS;
 +		break;
 +	}
 +
 +	/*
 +	 * Inhibit doresize() calls until we are rescheduled by SIGWINCH.
 +	 */
 +	wresized = 0;
  }
 
  static void
 %%%

From: Giorgos Keramidas <keramida@freebsd.org>
To: Maxim Konovalov <maxim@macomnet.ru>
Cc: Bruce Evans <bde@freebsd.org>, bug-followup@freebsd.org
Subject: Re: bin/119705: [PATCH] make iostat a bit smarter about the number
	of tty rows
Date: Wed, 16 Jan 2008 09:41:38 +0200

 On 2008-01-16 09:39, Giorgos Keramidas <keramida@freebsd.org> wrote:
 > Both fixed, I think.  The new version of the patch is attached below,
 > after the following changes:
 >
 >   * Reset wresized when doresize() completes its work
 >   * Only call isatty(fileno(stdout)) once, instead of every time
 >   doresize() is called
 >   * Only install a SIGWINCH handler if isatty() is true
 > diff --git a/usr.sbin/iostat/iostat.c b/usr.sbin/iostat/iostat.c
 > --- a/usr.sbin/iostat/iostat.c
 > +++ b/usr.sbin/iostat/iostat.c
 [...]
 > @@ -426,6 +433,20 @@ main(int argc, char **argv)
 >  	 */
 >  	(void)signal(SIGCONT, needhdr);
 >
 > +	/*
 > +	 * If our standard output is a tty, then install a SIGWINCH handler
 > +	 * and set wresized so that our first iteration through the main
 > +	 * iostat loop will peek at the terminal's current rows to find out
 > +	 * how many lines can fit in a screenful of output.
 > +	 */
 > +	if (isatty(fileno(stdout)) != 0) {
 > +		wresized = 1;
 > +		(void)signal(SIGWINCH, needresize);
 > +	} else {
 > +		wrows = IOSTAT_DEFAULT_ROWS;
 > +		return;
 
 Oops.  `return' doesn't belong here, of course.  Running iostat | more
 caught what I missed :)

From: Maxim Konovalov <maxim@macomnet.ru>
To: Giorgos Keramidas <keramida@freebsd.org>
Cc: Bruce Evans <bde@freebsd.org>, bug-followup@freebsd.org
Subject: Re: bin/119705: [PATCH] make iostat a bit smarter about the number
 of tty rows
Date: Wed, 16 Jan 2008 20:28:46 +0300 (MSK)

 On Wed, 16 Jan 2008, 09:41+0200, Giorgos Keramidas wrote:
 
 > On 2008-01-16 09:39, Giorgos Keramidas <keramida@freebsd.org> wrote:
 > > Both fixed, I think.  The new version of the patch is attached below,
 > > after the following changes:
 > >
 > >   * Reset wresized when doresize() completes its work
 > >   * Only call isatty(fileno(stdout)) once, instead of every time
 > >   doresize() is called
 > >   * Only install a SIGWINCH handler if isatty() is true
 > > diff --git a/usr.sbin/iostat/iostat.c b/usr.sbin/iostat/iostat.c
 > > --- a/usr.sbin/iostat/iostat.c
 > > +++ b/usr.sbin/iostat/iostat.c
 > [...]
 > > @@ -426,6 +433,20 @@ main(int argc, char **argv)
 > >  	 */
 > >  	(void)signal(SIGCONT, needhdr);
 > >
 > > +	/*
 > > +	 * If our standard output is a tty, then install a SIGWINCH handler
 > > +	 * and set wresized so that our first iteration through the main
 > > +	 * iostat loop will peek at the terminal's current rows to find out
 > > +	 * how many lines can fit in a screenful of output.
 > > +	 */
 > > +	if (isatty(fileno(stdout)) != 0) {
 > > +		wresized = 1;
 > > +		(void)signal(SIGWINCH, needresize);
 > > +	} else {
 > > +		wrows = IOSTAT_DEFAULT_ROWS;
 > > +		return;
 >
 > Oops.  `return' doesn't belong here, of course.  Running iostat | more
 > caught what I missed :)
 >
 Looks OK to me.
 
 -- 
 Maxim Konovalov
State-Changed-From-To: open->patched 
State-Changed-By: keramida 
State-Changed-When: Tue Jan 22 11:19:14 UTC 2008 
State-Changed-Why:  
Committed to HEAD after Maxim's help to fix the original patch, 
and his OK for the change. 


Responsible-Changed-From-To: freebsd-bugs->keramida 
Responsible-Changed-By: keramida 
Responsible-Changed-When: Tue Jan 22 11:19:14 UTC 2008 
Responsible-Changed-Why:  
I'll take care of MFC'ing this. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=119705 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/119705: commit references a PR
Date: Tue, 22 Jan 2008 11:18:59 +0000 (UTC)

 keramida    2008-01-22 11:18:51 UTC
 
   FreeBSD src repository (doc committer)
 
   Modified files:
     usr.sbin/iostat      iostat.c 
   Log:
   Repeat iostat header after rows-3 instead of a hardcoded 20.
   
   Use ioctl() to get the window size in iostat(8), and force a new
   header to be prepended to the output every time the current
   window size changes.  Change the number of lines before each
   header to `rows - 3' when the terminal is resized, so that the
   full terminal length can be used for output lines.
   
   PR:             bin/119705
   Submitted by:   keramida
   Approved by:    maxim
   MFC after:      2 weeks
   
   Revision  Changes    Path
   1.40      +71 -3     src/usr.sbin/iostat/iostat.c
 _______________________________________________
 cvs-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/cvs-all
 To unsubscribe, send any mail to "cvs-all-unsubscribe@freebsd.org"
 
State-Changed-From-To: patched->closed 
State-Changed-By: keramida 
State-Changed-When: Fri Jan 30 18:18:33 UTC 2009 
State-Changed-Why:  
Merged to stable/7 [r187925] and stable/6 [r187271] 

http://www.freebsd.org/cgi/query-pr.cgi?pr=119705 
>Unformatted:
