bin/124863: [PATCH] make vmstat(8) a bit smarter about the number of tty rows

Giorgos Keramidas keramida at ceid.upatras.gr
Sun Jun 22 01:20:02 UTC 2008


>Number:         124863
>Category:       bin
>Synopsis:       [PATCH] make vmstat(8) a bit smarter about the number of tty rows
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Sun Jun 22 01:20:01 UTC 2008
>Closed-Date:
>Last-Modified:
>Originator:     Giorgos Keramidas
>Release:        FreeBSD 8.0-CURRENT i386
>Organization:
<organization of PR author (multiple lines)>
>Environment:

System: FreeBSD kobe 8.0-CURRENT FreeBSD 8.0-CURRENT #1: \
Mon Jun 16 08:37:10 EEST 2008 build at kobe:/home/build/obj/home/build/src/sys/KOBE i386

>Description:

The current vmstat(8) utility repeats the header line every 20 lines,
and this number is hardcoded in the source of vmstat.  This means that
with very short xterm windows, vmstat 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 vmstat() 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 vmstat
for a while, using:

        % vmstat 1
>Fix:

The following patch is a direct `port' of the similar change I did to
iostat(8) (see Subversion change r175562 for the iostat patch).

--- vmstat.patch begins here ---
diff -ruN old/vmstat.c new/vmstat.c
--- old/vmstat.c	2008-06-22 04:06:14.000000000 +0300
+++ new/vmstat.c	2008-06-22 04:06:36.000000000 +0300
@@ -135,7 +135,10 @@
 
 static struct	vmmeter sum, osum;
 
-static int	winlines = 20;
+#define	VMSTAT_DEFAULT_LINES	20	/* Default number of `winlines'. */
+volatile sig_atomic_t wresized;		/* Tty resized, when non-zero. */
+static int winlines = VMSTAT_DEFAULT_LINES; /* Current number of tty rows. */
+
 static int	aflag;
 static int	nflag;
 static int	Pflag;
@@ -164,6 +167,8 @@
 static void	kreado(int, void *, size_t, size_t);
 static char    *kgetstr(const char *);
 static void	needhdr(int);
+static void	needresize(int);
+static void	doresize(void);
 static void	printhdr(int, u_long);
 static void	usage(void);
 
@@ -580,8 +585,27 @@
 
 	uptime = getuptime();
 	halfuptime = uptime / 2;
+
+	/*
+	 * If the user stops the program (control-Z) and then resumes it,
+	 * print out the header again.
+	 */
 	(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 {
+		wresized = 0;
+		winlines = VMSTAT_DEFAULT_LINES;
+	}
+
 	if (kd != NULL) {
 		if (namelist[X_STATHZ].n_type != 0 &&
 		    namelist[X_STATHZ].n_value != 0)
@@ -607,8 +631,11 @@
 		bzero(last_cp_times, size_cp_times);
 	}
 	for (hdrcnt = 1;;) {
-		if (!--hdrcnt)
+		if (!--hdrcnt) {
+			if (wresized != 0)
+				doresize();
 			printhdr(ncpus, cpumask);
+		}
 		if (kd != NULL) {
 			kread(X_CPTIME, cur.cp_time, sizeof(cur.cp_time));
 		} else {
@@ -658,6 +685,8 @@
 				errx(1, "%s", devstat_errbuf);
 				break;
 			case 1:
+				if (wresized != 0)
+					doresize();
 				printhdr(ncpus, cpumask);
 				break;
 			default:
@@ -772,6 +801,47 @@
 	hdrcnt = 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;
+	hdrcnt = 1;
+}
+
+/*
+ * Update the global `wrows' count of terminal rows.
+ */
+void
+doresize(void)
+{
+	int status;
+	struct winsize w;
+
+	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)
+			winlines = w.ws_row;
+		else
+			winlines = VMSTAT_DEFAULT_LINES;
+		break;
+	}
+
+	/*
+	 * Inhibit doresize() calls until we are rescheduled by SIGWINCH.
+	 */
+	wresized = 0;
+}
+
 #ifdef notyet
 static void
 dotimes(void)
--- vmstat.patch ends here ---
>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list