Home | History | Annotate | Line # | Download | only in iostat
      1 /*	$NetBSD: iostat.c,v 1.72 2023/10/30 19:43:33 mrg Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1996 John M. Vinopal
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *      This product includes software developed for the NetBSD Project
     18  *      by John M. Vinopal.
     19  * 4. The name of the author may not be used to endorse or promote products
     20  *    derived from this software without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  * SUCH DAMAGE.
     33  */
     34 
     35 /*-
     36  * Copyright (c) 1986, 1991, 1993
     37  *      The Regents of the University of California.  All rights reserved.
     38  *
     39  * Redistribution and use in source and binary forms, with or without
     40  * modification, are permitted provided that the following conditions
     41  * are met:
     42  * 1. Redistributions of source code must retain the above copyright
     43  *    notice, this list of conditions and the following disclaimer.
     44  * 2. Redistributions in binary form must reproduce the above copyright
     45  *    notice, this list of conditions and the following disclaimer in the
     46  *    documentation and/or other materials provided with the distribution.
     47  * 3. Neither the name of the University nor the names of its contributors
     48  *    may be used to endorse or promote products derived from this software
     49  *    without specific prior written permission.
     50  *
     51  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     52  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     53  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     54  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     55  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     56  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     57  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     58  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     59  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     60  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     61  * SUCH DAMAGE.
     62  */
     63 
     64 #include <sys/cdefs.h>
     65 #ifndef lint
     66 __COPYRIGHT("@(#) Copyright (c) 1986, 1991, 1993\
     67  The Regents of the University of California.  All rights reserved.");
     68 #endif /* not lint */
     69 
     70 #ifndef lint
     71 #if 0
     72 static char sccsid[] = "@(#)iostat.c	8.3 (Berkeley) 4/28/95";
     73 #else
     74 __RCSID("$NetBSD: iostat.c,v 1.72 2023/10/30 19:43:33 mrg Exp $");
     75 #endif
     76 #endif /* not lint */
     77 
     78 #include <sys/types.h>
     79 #include <sys/ioctl.h>
     80 #include <sys/sched.h>
     81 #include <sys/time.h>
     82 
     83 #include <err.h>
     84 #include <ctype.h>
     85 #include <signal.h>
     86 #include <stdio.h>
     87 #include <stdlib.h>
     88 #include <string.h>
     89 #include <unistd.h>
     90 #include <math.h>
     91 #include <fnmatch.h>
     92 
     93 #include "drvstats.h"
     94 
     95 int		hz;
     96 static int	reps, interval;
     97 static int	todo = 0;
     98 static int	defdrives;
     99 static int	winlines = 20;
    100 static int	wincols = 80;
    101 
    102 static int *order, ordersize;
    103 
    104 static char Line_Marker[] = "________________________________________________";
    105 
    106 #define	MAX(a,b)	(((a)>(b))?(a):(b))
    107 #define	MIN(a,b)	(((a)<(b))?(a):(b))
    108 
    109 #define	ISSET(x, a)	((x) & (a))
    110 #define	SHOW_CPU	(1u<<0)
    111 #define	SHOW_TTY	(1u<<1)
    112 #define	SHOW_STATS_1	(1u<<2)
    113 #define	SHOW_STATS_2	(1u<<3)
    114 #define	SHOW_STATS_3	(1u<<4)
    115 #define	SHOW_STATS_X	(1u<<5)
    116 #define	SHOW_STATS_Y	(1u<<6)
    117 #define	SHOW_UPDATES	(1u<<7)
    118 #define	SHOW_TOTALS	(1u<<8)
    119 #define	SHOW_NEW_TOTALS	(1u<<9)
    120 #define	SUPPRESS_ZERO	(1u<<10)
    121 
    122 #define	SHOW_STATS_ALL	(SHOW_STATS_1 | SHOW_STATS_2 |	\
    123 			 SHOW_STATS_3 | SHOW_STATS_X | SHOW_STATS_Y)
    124 
    125 /*
    126  * Decide how many screen columns each output statistic is given
    127  * (these are determined empirically ("looks good to me") and likely
    128  * will require changes from time to time as technology advances).
    129  *
    130  * The odd "+ N" at the end of the summary (total width of stat) definition
    131  * allows for the gaps between the columns, and is (#data cols - 1).
    132  * So, tty stats have "in" and "out", 2 columns, so there is 1 extra space,
    133  * whereas the cpu stats have 5 columns, so 4 extra spaces (etc).
    134  */
    135 #define	LAYOUT_TTY_IN	4	/* tty input in last interval */
    136 #define	LAYOUT_TTY_TIN	7	/* tty input forever */
    137 #define	LAYOUT_TTY_OUT	5	/* tty output in last interval */
    138 #define	LAYOUT_TTY_TOUT	10	/* tty output forever */
    139 #define	LAYOUT_TTY	(((todo & SHOW_TOTALS)				     \
    140 				? (LAYOUT_TTY_TIN + LAYOUT_TTY_TOUT)	     \
    141 				: (LAYOUT_TTY_IN + LAYOUT_TTY_OUT)) + 1)
    142 #define	LAYOUT_TTY_GAP	0		/* always starts at left margin */
    143 
    144 #define	LAYOUT_CPU_USER	2
    145 #define	LAYOUT_CPU_NICE	2
    146 #define	LAYOUT_CPU_SYS	2
    147 #define	LAYOUT_CPU_INT	2
    148 #define	LAYOUT_CPU_IDLE	3
    149 #define	LAYOUT_CPU	(LAYOUT_CPU_USER + LAYOUT_CPU_NICE + LAYOUT_CPU_SYS + \
    150 			    LAYOUT_CPU_INT + LAYOUT_CPU_IDLE + 4)
    151 #define	LAYOUT_CPU_GAP	2
    152 
    153 			/* used for:       w/o TOTALS  w TOTALS	*/
    154 #define	LAYOUT_DRIVE_1_XSIZE	5	/*	KB/t	KB/t	*/
    155 #define	LAYOUT_DRIVE_1_RATE	6	/*	t/s		*/
    156 #define	LAYOUT_DRIVE_1_XFER	10	/*		xfr	*/
    157 #define	LAYOUT_DRIVE_1_SPEED	5	/*	MB/s		*/
    158 #define	LAYOUT_DRIVE_1_VOLUME	8	/*		MB	*/
    159 #define	LAYOUT_DRIVE_1_INCR	5	/*		(inc)	*/
    160 
    161 #define	LAYOUT_DRIVE_2_XSIZE	7	/*	KB		*/
    162 #define	LAYOUT_DRIVE_2_VOLUME	11	/*		KB	*/
    163 #define	LAYOUT_DRIVE_2_XFR	7	/*	xfr		*/
    164 #define	LAYOUT_DRIVE_2_TXFR	10	/*		xfr	*/
    165 #define	LAYOUT_DRIVE_2_INCR	5	/*		(inc)	*/
    166 #define	LAYOUT_DRIVE_2_TBUSY	9	/*		time	*/
    167 #define	LAYOUT_DRIVE_2_BUSY	5	/*	time		*/
    168 
    169 /* Layout 3 uses same sizes as 2, but with MB. */
    170 
    171 #define	LAYOUT_DRIVE_1	(LAYOUT_DRIVE_1_XSIZE + ((todo & SHOW_TOTALS) ?	       \
    172 			    (LAYOUT_DRIVE_1_XFER + LAYOUT_DRIVE_1_VOLUME +     \
    173 			    ((todo&SHOW_UPDATES)? 2*LAYOUT_DRIVE_1_INCR+2 :0)) \
    174 			  : (LAYOUT_DRIVE_1_RATE + LAYOUT_DRIVE_1_SPEED)) + 3)
    175 #define	LAYOUT_DRIVE_2	(((todo & SHOW_TOTALS) ? (LAYOUT_DRIVE_2_VOLUME +      \
    176 			    LAYOUT_DRIVE_2_TXFR + LAYOUT_DRIVE_2_TBUSY +       \
    177 			    ((todo&SHOW_UPDATES)? 2*LAYOUT_DRIVE_2_INCR+2 : 0))\
    178 			  : (LAYOUT_DRIVE_2_XSIZE + LAYOUT_DRIVE_2_XFR +       \
    179 			     LAYOUT_DRIVE_2_BUSY)) + 3)
    180 #define	LAYOUT_DRIVE_3	(((todo & SHOW_TOTALS) ? (LAYOUT_DRIVE_2_VOLUME +      \
    181 			    LAYOUT_DRIVE_2_TBUSY +			       \
    182 			    ((todo&SHOW_UPDATES)? 2*LAYOUT_DRIVE_2_INCR+1 : 0))\
    183 			  : (LAYOUT_DRIVE_2_XSIZE + LAYOUT_DRIVE_2_BUSY)) + 2)
    184 
    185 #define	LAYOUT_DRIVE_GAP 0	/* Gap included in column, always present */
    186 
    187 /* TODO: X & Y stats layouts */
    188 
    189 static void cpustats(void);
    190 static double drive_time(double, int);
    191 static void drive_stats(int, double);
    192 static void drive_stats2(int, double);
    193 static void drive_statsx(int, double);
    194 static void drive_statsy(int, double);
    195 static void drive_statsy_io(double, double, double);
    196 static void drive_statsy_q(double, double, double, double, double, double);
    197 static void sig_header(int);
    198 static volatile int do_header;
    199 static void header(int);
    200 __dead static void usage(void);
    201 static void display(int);
    202 static int selectdrives(int, char *[], int);
    203 
    204 int
    205 main(int argc, char *argv[])
    206 {
    207 	int ch, hdrcnt, hdroffset, ndrives, lines;
    208 	struct timespec	tv;
    209 	struct ttysize ts;
    210 	long width = -1, height = -1;
    211 	char *ep;
    212 
    213 #if 0		/* -i and -u are not currently (sanely) implementable */
    214 	while ((ch = getopt(argc, argv, "Cc:dDH:iITuw:W:xXyz")) != -1)
    215 #else
    216 	while ((ch = getopt(argc, argv, "Cc:dDH:ITw:W:xXyz")) != -1)
    217 #endif
    218 		switch (ch) {
    219 		case 'c':
    220 			if ((reps = atoi(optarg)) <= 0)
    221 				errx(1, "repetition count <= 0.");
    222 			break;
    223 		case 'C':
    224 			todo |= SHOW_CPU;
    225 			break;
    226 		case 'd':
    227 			todo &= ~SHOW_STATS_ALL;
    228 			todo |= SHOW_STATS_1;
    229 			break;
    230 		case 'D':
    231 			todo &= ~SHOW_STATS_ALL;
    232 			todo |= SHOW_STATS_2;
    233 			break;
    234 		case 'H':
    235 			height = strtol(optarg, &ep, 10);
    236 			if (height < 0 || *ep != '\0')
    237 				errx(1, "bad height (-H) value.");
    238 			height += 2;	/* magic, but needed to be sane */
    239 			break;
    240 #if 0
    241 		case 'i':
    242 			todo |= SHOW_TOTALS | SHOW_NEW_TOTALS;
    243 			break;
    244 #endif
    245 		case 'I':
    246 			todo |= SHOW_TOTALS;
    247 			break;
    248 		case 'T':
    249 			todo |= SHOW_TTY;
    250 			break;
    251 #if 0
    252 		case 'u':
    253 			todo |= SHOW_UPDATES;
    254 			break;
    255 #endif
    256 		case 'w':
    257 			if ((interval = atoi(optarg)) <= 0)
    258 				errx(1, "interval <= 0.");
    259 			break;
    260 		case 'W':
    261 			width = strtol(optarg, &ep, 10);
    262 			if (width < 0 || *ep != '\0')
    263 				errx(1, "bad width (-W) value.");
    264 			break;
    265 		case 'x':
    266 			todo &= ~SHOW_STATS_ALL;
    267 			todo |= SHOW_STATS_X;
    268 			break;
    269 		case 'X':
    270 			todo &= ~SHOW_STATS_ALL;
    271 			todo |= SHOW_STATS_3;
    272 			break;
    273 		case 'y':
    274 			todo &= ~SHOW_STATS_ALL;
    275 			todo |= SHOW_STATS_Y;
    276 			break;
    277 		case 'z':
    278 			todo |= SUPPRESS_ZERO;
    279 			break;
    280 		case '?':
    281 		default:
    282 			usage();
    283 		}
    284 	argc -= optind;
    285 	argv += optind;
    286 
    287 	if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL))
    288 		todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1;
    289 	if (ISSET(todo, SHOW_STATS_X)) {
    290 		todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL);
    291 		todo |= SHOW_STATS_X;
    292 	}
    293 	if (ISSET(todo, SHOW_STATS_3)) {
    294 		todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL);
    295 		todo |= SHOW_STATS_3;
    296 	}
    297 	if (ISSET(todo, SHOW_STATS_Y)) {
    298 		todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL | SHOW_TOTALS);
    299 		todo |= SHOW_STATS_Y;
    300 	}
    301 
    302 	if (ioctl(STDOUT_FILENO, TIOCGSIZE, &ts) != -1) {
    303 		if (ts.ts_lines)
    304 			winlines = ts.ts_lines;
    305 		if (ts.ts_cols)
    306 			wincols = ts.ts_cols;
    307 	}
    308 
    309 	if (height == -1) {
    310 		char *lns = getenv("LINES");
    311 
    312 		if (lns == NULL || (height = strtol(lns, &ep, 10)) < 0 ||
    313 		    *ep != '\0')
    314 			height = winlines;
    315 	}
    316 	winlines = height;
    317 
    318 	if (width == -1) {
    319 		char *cols = getenv("COLUMNS");
    320 
    321 		if (cols == NULL || (width = strtol(cols, &ep, 10)) < 0 ||
    322 		    *ep != '\0')
    323 			width = wincols;
    324 	}
    325 	defdrives = width;
    326 	if (defdrives == 0) {
    327 		defdrives = 5000;	/* anything absurdly big */
    328 	} else {
    329 		if (ISSET(todo, SHOW_CPU))
    330 			defdrives -= LAYOUT_CPU + LAYOUT_CPU_GAP;
    331 		if (ISSET(todo, SHOW_TTY))
    332 			defdrives -= LAYOUT_TTY + LAYOUT_TTY_GAP;
    333 		if (ISSET(todo, SHOW_STATS_2))
    334 			defdrives /= LAYOUT_DRIVE_2 + LAYOUT_DRIVE_GAP;
    335 		if (ISSET(todo, SHOW_STATS_3))
    336 			defdrives /= LAYOUT_DRIVE_3 + LAYOUT_DRIVE_GAP;
    337 		else
    338 			defdrives /= LAYOUT_DRIVE_1 + LAYOUT_DRIVE_GAP;
    339 	}
    340 
    341 	drvinit(0);
    342 	cpureadstats();
    343 	drvreadstats();
    344 	ordersize = 0;
    345 	ndrives = selectdrives(argc, argv, 1);
    346 	if (ndrives == 0) {
    347 		/* No drives are selected.  No need to show drive stats. */
    348 		todo &= ~SHOW_STATS_ALL;
    349 		if (todo == 0)
    350 			errx(1, "no drives");
    351 	}
    352 	tv.tv_sec = interval;
    353 	tv.tv_nsec = 0;
    354 
    355 	/* print a new header on sigcont */
    356 	(void)signal(SIGCONT, sig_header);
    357 	do_header = 1;
    358 
    359 	for (hdrcnt = 1;;) {
    360 		if (ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y)) {
    361 			lines = ndrives;
    362 			hdroffset = 3;
    363 		} else if (ISSET(todo, SHOW_STATS_3)) {
    364 			lines = 1;
    365 			hdroffset = 3;
    366 		} else {
    367 			lines = 1;
    368 			hdroffset = 4;
    369 		}
    370 
    371 		if (do_header || (winlines != 0 && (hdrcnt -= lines) <= 0)) {
    372 			do_header = 0;
    373 			header(ndrives);
    374 			hdrcnt = winlines - hdroffset;
    375 		}
    376 
    377 		if (!ISSET(todo, SHOW_TOTALS) || ISSET(todo, SHOW_NEW_TOTALS)) {
    378 			cpuswap();
    379 			drvswap();
    380 			tkswap();
    381 			todo &= ~SHOW_NEW_TOTALS;
    382 		}
    383 
    384 		display(ndrives);
    385 
    386 		if (reps >= 0 && --reps <= 0)
    387 			break;
    388 		nanosleep(&tv, NULL);
    389 		cpureadstats();
    390 		drvreadstats();
    391 
    392 		ndrives = selectdrives(argc, argv, 0);
    393 	}
    394 	exit(0);
    395 }
    396 
    397 static void
    398 sig_header(int signo)
    399 {
    400 	do_header = 1;
    401 }
    402 
    403 static void
    404 header(int ndrives)
    405 {
    406 	int i;
    407 
    408 					/* Main Headers. */
    409 	if (ISSET(todo, SHOW_STATS_X)) {
    410 		if (ISSET(todo, SHOW_TOTALS)) {
    411 			(void)printf(
    412 			    "device  read KB/t    xfr   time     MB  ");
    413 			(void)printf(" write KB/t    xfr   time     MB\n");
    414 		} else {
    415 			(void)printf(
    416 			    "device  read KB/t    r/s   time     MB/s");
    417 			(void)printf(" write KB/t    w/s   time     MB/s\n");
    418 		}
    419 		return;
    420 	}
    421 
    422 	if (ISSET(todo, SHOW_STATS_Y)) {
    423 		(void)printf("device  read KB/t    r/s     MB/s write KB/t    w/s     MB/s");
    424 		(void)printf("   wait   actv  wsvc_t  asvc_t  wtime   time");
    425 		(void)printf("\n");
    426 		return;
    427 	}
    428 
    429 	if (ISSET(todo, SHOW_TTY))
    430 		(void)printf("%*s", LAYOUT_TTY_GAP + LAYOUT_TTY, "tty");
    431 
    432 	if (ISSET(todo, SHOW_STATS_1)) {
    433 		for (i = 0; i < ndrives; i++) {
    434 			char *dname = cur.name[order[i]];
    435 			int dnlen = (int)strlen(dname);
    436 
    437 			printf(" ");	/* always a 1 column gap */
    438 			if (dnlen < LAYOUT_DRIVE_1 - 6)
    439 				printf("|%-*.*s ",
    440 				    (LAYOUT_DRIVE_1 - 1 - dnlen - 1) / 2 - 1,
    441 				    (LAYOUT_DRIVE_1 - 1 - dnlen - 1) / 2 - 1,
    442 				    Line_Marker);
    443 			printf("%*.*s", ((dnlen >= LAYOUT_DRIVE_1 - 6) ?
    444 			    MIN(MAX((LAYOUT_DRIVE_1 - dnlen) / 2, 0),
    445 				LAYOUT_DRIVE_1) : 0),
    446 			    LAYOUT_DRIVE_1, dname);
    447 			if (dnlen < LAYOUT_DRIVE_1 - 6)
    448 				printf(" %*.*s|",
    449 				    (LAYOUT_DRIVE_1 - 1 - dnlen - 2) / 2 - 1,
    450 				    (LAYOUT_DRIVE_1 - 1 - dnlen - 2) / 2 - 1,
    451 				    Line_Marker);
    452 		}
    453 	}
    454 
    455 	if (ISSET(todo, SHOW_STATS_2)) {
    456 		for (i = 0; i < ndrives; i++) {
    457 			char *dname = cur.name[order[i]];
    458 			int dnlen = (int)strlen(dname);
    459 
    460 			printf(" ");	/* always a 1 column gap */
    461 			if (dnlen < LAYOUT_DRIVE_2 - 6)
    462 				printf("|%-*.*s ",
    463 				    (LAYOUT_DRIVE_2 - 1 - dnlen - 1) / 2 - 1,
    464 				    (LAYOUT_DRIVE_2 - 1 - dnlen - 1) / 2 - 1,
    465 				    Line_Marker);
    466 			printf("%*.*s", ((dnlen >= LAYOUT_DRIVE_1 - 6) ?
    467 			    MIN(MAX((LAYOUT_DRIVE_2 - dnlen) / 2, 0),
    468 				LAYOUT_DRIVE_2) : 0),
    469 			    LAYOUT_DRIVE_1, dname);
    470 			if (dnlen < LAYOUT_DRIVE_2 - 6)
    471 				printf(" %*.*s|",
    472 				    (LAYOUT_DRIVE_2 - 1 - dnlen - 2) / 2 - 1,
    473 				    (LAYOUT_DRIVE_2 - 1 - dnlen - 2) / 2 - 1,
    474 				    Line_Marker);
    475 		}
    476 	}
    477 
    478 	if (ISSET(todo, SHOW_STATS_3)) {
    479 		for (i = 0; i < ndrives; i++) {
    480 			char *dname = cur.name[order[i]];
    481 			int dnlen = (int)strlen(dname);
    482 
    483 			printf(" ");	/* always a 1 column gap */
    484 			if (dnlen < LAYOUT_DRIVE_3 - 6)
    485 				printf("|%-*.*s ",
    486 				    (LAYOUT_DRIVE_3 - 1 - dnlen - 1) / 2 - 1,
    487 				    (LAYOUT_DRIVE_3 - 1 - dnlen - 1) / 2 - 1,
    488 				    Line_Marker);
    489 			printf("%*.*s", ((dnlen >= LAYOUT_DRIVE_1 - 6) ?
    490 			    MIN(MAX((LAYOUT_DRIVE_3 - dnlen) / 2, 0),
    491 				LAYOUT_DRIVE_3) : 0),
    492 			    LAYOUT_DRIVE_1, dname);
    493 			if (dnlen < LAYOUT_DRIVE_3 - 6)
    494 				printf(" %*.*s|",
    495 				    (LAYOUT_DRIVE_3 - 1 - dnlen - 2) / 2 - 1,
    496 				    (LAYOUT_DRIVE_3 - 1 - dnlen - 2) / 2 - 1,
    497 				    Line_Marker);
    498 		}
    499 	}
    500 
    501 	if (ISSET(todo, SHOW_CPU))
    502 		(void)printf("%*s", LAYOUT_CPU + LAYOUT_CPU_GAP, "CPU");
    503 
    504 	printf("\n");
    505 
    506 					/* Sub-Headers. */
    507 	if (ISSET(todo, SHOW_TTY)) {
    508 		printf("%*s %*s",
    509 		   ((todo&SHOW_TOTALS)?LAYOUT_TTY_TIN:LAYOUT_TTY_IN), "tin",
    510 		   ((todo&SHOW_TOTALS)?LAYOUT_TTY_TOUT:LAYOUT_TTY_OUT), "tout");
    511 	}
    512 
    513 	if (ISSET(todo, SHOW_STATS_1)) {
    514 		for (i = 0; i < ndrives; i++) {
    515 			if (ISSET(todo, SHOW_TOTALS)) {
    516 				(void)printf(" %*s %*s %*s",
    517 				    LAYOUT_DRIVE_1_XFER, "xfr",
    518 				    LAYOUT_DRIVE_1_XSIZE, "KB/t",
    519 				    LAYOUT_DRIVE_1_VOLUME, "MB");
    520 			} else {
    521 				(void)printf(" %*s %*s %*s",
    522 				    LAYOUT_DRIVE_1_RATE, "t/s",
    523 				    LAYOUT_DRIVE_1_XSIZE, "KB/t",
    524 				    LAYOUT_DRIVE_1_SPEED, "MB/s");
    525 			}
    526 		}
    527 	}
    528 
    529 	if (ISSET(todo, SHOW_STATS_2)) {
    530 		for (i = 0; i < ndrives; i++) {
    531 			if (ISSET(todo, SHOW_TOTALS)) {
    532 				(void)printf(" %*s %*s %*s",
    533 				    LAYOUT_DRIVE_2_TXFR, "xfr",
    534 				    LAYOUT_DRIVE_2_VOLUME, "KB",
    535 				    LAYOUT_DRIVE_2_TBUSY, "time");
    536 			} else {
    537 				(void)printf(" %*s %*s %*s",
    538 				    LAYOUT_DRIVE_2_XFR, "xfr",
    539 				    LAYOUT_DRIVE_2_XSIZE, "KB",
    540 				    LAYOUT_DRIVE_2_BUSY, "time");
    541 			}
    542 		}
    543 	}
    544 
    545 	if (ISSET(todo, SHOW_STATS_3)) {
    546 		for (i = 0; i < ndrives; i++) {
    547 			if (ISSET(todo, SHOW_TOTALS)) {
    548 				(void)printf(" %*s %*s",
    549 				    LAYOUT_DRIVE_2_VOLUME, "MB/s",
    550 				    LAYOUT_DRIVE_2_TBUSY, "time");
    551 			} else {
    552 				(void)printf(" %*s %*s",
    553 				    LAYOUT_DRIVE_2_XSIZE, "MB/s",
    554 				    LAYOUT_DRIVE_2_BUSY, "time");
    555 			}
    556 		}
    557 	}
    558 
    559 	/* should do this properly, but it is such a simple case... */
    560 	if (ISSET(todo, SHOW_CPU))
    561 		(void)printf("  us ni sy in  id");
    562 	printf("\n");
    563 }
    564 
    565 static double
    566 drive_time(double etime, int dn)
    567 {
    568 	if (ISSET(todo, SHOW_TOTALS))
    569 		return etime;
    570 
    571 	if (cur.timestamp[dn].tv_sec || cur.timestamp[dn].tv_usec) {
    572 		etime = (double)cur.timestamp[dn].tv_sec +
    573 		    ((double)cur.timestamp[dn].tv_usec / (double)1000000);
    574 	}
    575 
    576 	return etime;
    577 }
    578 
    579 static void
    580 drive_stats(int ndrives, double etime)
    581 {
    582 	int drive;
    583 	double atime, dtime, mbps;
    584 	int c1, c2, c3;
    585 
    586 	if (ISSET(todo, SHOW_TOTALS)) {
    587 		c1 = LAYOUT_DRIVE_1_XFER;
    588 		c2 = LAYOUT_DRIVE_1_XSIZE;
    589 		c3 = LAYOUT_DRIVE_1_VOLUME;
    590 	} else {
    591 		c1 = LAYOUT_DRIVE_1_RATE;
    592 		c2 = LAYOUT_DRIVE_1_XSIZE;
    593 		c3 = LAYOUT_DRIVE_1_SPEED;
    594 	}
    595 
    596 	for (drive = 0; drive < ndrives; ++drive) {
    597 		int dn = order[drive];
    598 
    599 		if (!cur.select[dn])	/* should be impossible */
    600 			continue;
    601 
    602 		if (todo & SUPPRESS_ZERO) {
    603 			if (cur.rxfer[dn] == 0 &&
    604 			    cur.wxfer[dn] == 0 &&
    605 			    cur.rbytes[dn] == 0 &&
    606 			    cur.wbytes[dn] == 0) {
    607 				printf("%*s", c1 + 1 + c2 + 1 + c3 + 1, "");
    608 				continue;
    609 			}
    610 		}
    611 
    612 		dtime = drive_time(etime, dn);
    613 
    614 					/* average transfers per second. */
    615 		(void)printf(" %*.0f", c1,
    616 		    (cur.rxfer[dn] + cur.wxfer[dn]) / dtime);
    617 
    618 					/* average Kbytes per transfer. */
    619 		if (cur.rxfer[dn] + cur.wxfer[dn])
    620 			mbps = ((cur.rbytes[dn] + cur.wbytes[dn]) /
    621 			    1024.0) / (cur.rxfer[dn] + cur.wxfer[dn]);
    622 		else
    623 			mbps = 0.0;
    624 		(void)printf(" %*.*f", c2,
    625 		    MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps);
    626 
    627 					/* time busy in drive activity */
    628 		atime = (double)cur.time[dn].tv_sec +
    629 		    ((double)cur.time[dn].tv_usec / (double)1000000);
    630 
    631 					/* Megabytes per second. */
    632 		if (atime != 0.0)
    633 			mbps = (cur.rbytes[dn] + cur.wbytes[dn]) /
    634 			    (double)(1024 * 1024);
    635 		else
    636 			mbps = 0;
    637 		mbps /= dtime;
    638 		(void)printf(" %*.*f", c3,
    639 		    MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps);
    640 	}
    641 }
    642 
    643 static void
    644 drive_stats2(int ndrives, double etime)
    645 {
    646 	int drive;
    647 	double atime, dtime;
    648 	int c1, c2, c3;
    649 
    650 	if (ISSET(todo, SHOW_TOTALS)) {
    651 		c1 = LAYOUT_DRIVE_2_TXFR;
    652 		c2 = LAYOUT_DRIVE_2_VOLUME;
    653 		c3 = LAYOUT_DRIVE_2_TBUSY;
    654 	} else {
    655 		c1 = LAYOUT_DRIVE_2_XFR;
    656 		c2 = LAYOUT_DRIVE_2_XSIZE;
    657 		c3 = LAYOUT_DRIVE_2_BUSY;
    658 	}
    659 
    660 	for (drive = 0; drive < ndrives; ++drive) {
    661 		int dn = order[drive];
    662 
    663 		if (!cur.select[dn])		/* should be impossible */
    664 			continue;
    665 
    666 		if (todo & SUPPRESS_ZERO) {
    667 			if (cur.rxfer[dn] == 0 &&
    668 			    cur.wxfer[dn] == 0 &&
    669 			    cur.rbytes[dn] == 0 &&
    670 			    cur.wbytes[dn] == 0) {
    671 				printf("%*s", c1 + 1 + c2 + 1 + c3 + 1, "");
    672 				continue;
    673 			}
    674 		}
    675 
    676 		dtime = drive_time(etime, dn);
    677 
    678 					/* average transfers per second. */
    679 		if (ISSET(todo, SHOW_STATS_2)) {
    680 			(void)printf(" %*.0f", c1,
    681 			    (cur.rxfer[dn] + cur.wxfer[dn]) / dtime);
    682 		}
    683 
    684 					/* average mbytes per second. */
    685 		(void)printf(" %*.0f", c2,
    686 		    (cur.rbytes[dn] + cur.wbytes[dn]) /
    687 		    (double)(1024 * 1024) / dtime);
    688 
    689 					/* average time busy in dn activity */
    690 		atime = (double)cur.time[dn].tv_sec +
    691 		    ((double)cur.time[dn].tv_usec / (double)1000000);
    692 		(void)printf(" %*.2f", c3, atime / dtime);
    693 	}
    694 }
    695 
    696 static void
    697 drive_statsx(int ndrives, double etime)
    698 {
    699 	int dn, drive;
    700 	double atime, dtime, kbps;
    701 
    702 	for (drive = 0; drive < ndrives; ++drive) {
    703 		dn = order[drive];
    704 
    705 		if (!cur.select[dn])	/* impossible */
    706 			continue;
    707 
    708 		(void)printf("%-8.8s", cur.name[dn]);
    709 
    710 		if (todo & SUPPRESS_ZERO) {
    711 			if (cur.rbytes[dn] == 0 && cur.rxfer[dn] == 0 &&
    712 			    cur.wbytes[dn] == 0 && cur.wxfer[dn] == 0) {
    713 				printf("\n");
    714 				continue;
    715 			}
    716 		}
    717 
    718 		dtime = drive_time(etime, dn);
    719 
    720 					/* average read Kbytes per transfer */
    721 		if (cur.rxfer[dn])
    722 			kbps = (cur.rbytes[dn] / 1024.0) / cur.rxfer[dn];
    723 		else
    724 			kbps = 0.0;
    725 		(void)printf(" %8.2f", kbps);
    726 
    727 					/* average read transfers
    728 					   (per second) */
    729 		(void)printf(" %6.0f", cur.rxfer[dn] / dtime);
    730 
    731 					/* time read busy in drive activity */
    732 		atime = (double)cur.time[dn].tv_sec +
    733 		    ((double)cur.time[dn].tv_usec / (double)1000000);
    734 		(void)printf(" %6.2f", atime / dtime);
    735 
    736 					/* average read megabytes
    737 					   (per second) */
    738 		(void)printf(" %8.2f",
    739 		    cur.rbytes[dn] / (1024.0 * 1024) / dtime);
    740 
    741 
    742 					/* average write Kbytes per transfer */
    743 		if (cur.wxfer[dn])
    744 			kbps = (cur.wbytes[dn] / 1024.0) / cur.wxfer[dn];
    745 		else
    746 			kbps = 0.0;
    747 		(void)printf("   %8.2f", kbps);
    748 
    749 					/* average write transfers
    750 					   (per second) */
    751 		(void)printf(" %6.0f", cur.wxfer[dn] / dtime);
    752 
    753 					/* time write busy in drive activity */
    754 		atime = (double)cur.time[dn].tv_sec +
    755 		    ((double)cur.time[dn].tv_usec / (double)1000000);
    756 		(void)printf(" %6.2f", atime / dtime);
    757 
    758 					/* average write megabytes
    759 					   (per second) */
    760 		(void)printf(" %8.2f\n",
    761 		    cur.wbytes[dn] / (1024.0 * 1024) / dtime);
    762 	}
    763 }
    764 
    765 static void
    766 drive_statsy_io(double elapsed, double count, double volume)
    767 {
    768 	double kbps;
    769 
    770 	/* average Kbytes per transfer */
    771 	if (count)
    772 		kbps = (volume / 1024.0) / count;
    773 	else
    774 		kbps = 0.0;
    775 	(void)printf(" %8.2f", kbps);
    776 
    777 	/* average transfers (per second) */
    778 	(void)printf(" %6.0f", count / elapsed);
    779 
    780 	/* average megabytes (per second) */
    781 	(void)printf(" %8.2f", volume / (1024.0 * 1024) / elapsed);
    782 }
    783 
    784 static void
    785 drive_statsy_q(double elapsed, double busy, double wait, double busysum, double waitsum, double count)
    786 {
    787 	/* average wait queue length */
    788 	(void)printf(" %6.1f", waitsum / elapsed);
    789 
    790 	/* average busy queue length */
    791 	(void)printf(" %6.1f", busysum / elapsed);
    792 
    793 	/* average wait time */
    794 	(void)printf(" %7.2f", count > 0 ? waitsum / count * 1000.0 : 0.0);
    795 
    796 	/* average service time */
    797 	(void)printf(" %7.2f", count > 0 ? busysum / count * 1000.0 : 0.0);
    798 
    799 	/* time waiting for drive activity */
    800 	(void)printf(" %6.2f", wait / elapsed);
    801 
    802 	/* time busy in drive activity */
    803 	(void)printf(" %6.2f", busy / elapsed);
    804 }
    805 
    806 static void
    807 drive_statsy(int ndrives, double etime)
    808 {
    809 	int drive, dn;
    810 	double atime, await, abusysum, awaitsum, dtime;
    811 
    812 	for (drive = 0; drive < ndrives; ++drive) {
    813 		dn = order[drive];
    814 		if (!cur.select[dn])	/* impossible */
    815 			continue;
    816 
    817 		(void)printf("%-8.8s", cur.name[dn]);
    818 
    819 		if (todo & SUPPRESS_ZERO) {
    820 			if (cur.rbytes[dn] == 0 && cur.rxfer[dn] == 0 &&
    821 			    cur.wbytes[dn] == 0 && cur.wxfer[dn] == 0) {
    822 				printf("\n");
    823 				continue;
    824 			}
    825 		}
    826 
    827 		dtime = drive_time(etime, dn);
    828 
    829 		atime = (double)cur.time[dn].tv_sec +
    830 		    ((double)cur.time[dn].tv_usec / (double)1000000);
    831 		await = (double)cur.wait[dn].tv_sec +
    832 		    ((double)cur.wait[dn].tv_usec / (double)1000000);
    833 		abusysum = (double)cur.busysum[dn].tv_sec +
    834 		    ((double)cur.busysum[dn].tv_usec / (double)1000000);
    835 		awaitsum = (double)cur.waitsum[dn].tv_sec +
    836 		    ((double)cur.waitsum[dn].tv_usec / (double)1000000);
    837 
    838 		drive_statsy_io(dtime, cur.rxfer[dn], cur.rbytes[dn]);
    839 		(void)printf("  ");
    840 		drive_statsy_io(dtime, cur.wxfer[dn], cur.wbytes[dn]);
    841 		drive_statsy_q(dtime, atime, await, abusysum, awaitsum, cur.rxfer[dn]+cur.wxfer[dn]);
    842 
    843 		(void)printf("\n");
    844 	}
    845 }
    846 
    847 static void
    848 cpustats(void)
    849 {
    850 	int state;
    851 	double ttime;
    852 
    853 	static int cwidth[CPUSTATES] = {
    854 		LAYOUT_CPU_USER,
    855 		LAYOUT_CPU_NICE,
    856 		LAYOUT_CPU_SYS,
    857 		LAYOUT_CPU_INT,
    858 		LAYOUT_CPU_IDLE
    859 	};
    860 
    861 	ttime = 0;
    862 	for (state = 0; state < CPUSTATES; ++state)
    863 		ttime += cur.cp_time[state];
    864 	if (!ttime)
    865 		ttime = 1.0;
    866 
    867 	printf("%*s", LAYOUT_CPU_GAP - 1, "");	/* the 1 is the next space */
    868 	for (state = 0; state < CPUSTATES; ++state) {
    869 		if ((todo & SUPPRESS_ZERO) && cur.cp_time[state] == 0) {
    870 			printf(" %*s", cwidth[state], "");
    871 			continue;
    872 		}
    873 		printf(" %*.0f", cwidth[state],
    874 		    100. * cur.cp_time[state] / ttime);
    875 	}
    876 }
    877 
    878 static void
    879 usage(void)
    880 {
    881 
    882 	(void)fprintf(stderr, "usage: iostat [-CDdITXxyz] [-c count] "
    883 	    "[-H height] [-W width] [-w wait] [drives]\n");
    884 	exit(1);
    885 }
    886 
    887 static void
    888 display(int ndrives)
    889 {
    890 	double	etime;
    891 
    892 	/* Sum up the elapsed ticks. */
    893 	etime = cur.cp_etime;
    894 
    895 	/*
    896 	 * If we're showing totals only, then don't divide by the
    897 	 * system time.
    898 	 */
    899 	if (ISSET(todo, SHOW_TOTALS))
    900 		etime = 1.0;
    901 
    902 	if (ISSET(todo, SHOW_STATS_X)) {
    903 		drive_statsx(ndrives, etime);
    904 		goto out;
    905 	}
    906 
    907 	if (ISSET(todo, SHOW_STATS_Y)) {
    908 		drive_statsy(ndrives, etime);
    909 		goto out;
    910 	}
    911 
    912 	if (ISSET(todo, SHOW_TTY))
    913 		printf("%*.0f %*.0f",
    914 		    ((todo & SHOW_TOTALS) ? LAYOUT_TTY_TIN : LAYOUT_TTY_IN),
    915 		    cur.tk_nin / etime,
    916 		    ((todo & SHOW_TOTALS) ? LAYOUT_TTY_TOUT : LAYOUT_TTY_OUT),
    917 		    cur.tk_nout / etime);
    918 
    919 	if (ISSET(todo, SHOW_STATS_1)) {
    920 		drive_stats(ndrives, etime);
    921 	}
    922 
    923 	if (ISSET(todo, SHOW_STATS_2) || ISSET(todo, SHOW_STATS_3)) {
    924 		drive_stats2(ndrives, etime);
    925 	}
    926 
    927 	if (ISSET(todo, SHOW_CPU))
    928 		cpustats();
    929 
    930 	(void)printf("\n");
    931 
    932 out:
    933 	(void)fflush(stdout);
    934 }
    935 
    936 static int
    937 selectdrives(int argc, char *argv[], int first)
    938 {
    939 	int	i, maxdrives, ndrives, tried;
    940 
    941 	/*
    942 	 * Choose drives to be displayed.  Priority goes to (in order) drives
    943 	 * supplied as arguments and default drives.  If everything isn't
    944 	 * filled in and there are drives not taken care of, display the first
    945 	 * few that fit.
    946 	 *
    947 	 * The backward compatibility #ifdefs permit the syntax:
    948 	 *	iostat [ drives ] [ interval [ count ] ]
    949 	 */
    950 
    951 #define	BACKWARD_COMPATIBILITY
    952 	for (tried = ndrives = 0; *argv; ++argv) {
    953 #ifdef BACKWARD_COMPATIBILITY
    954 		if (isdigit((unsigned char)**argv))
    955 			break;
    956 #endif
    957 		tried++;
    958 		for (i = 0; i < (int)ndrive; i++) {
    959 			if (fnmatch(*argv, cur.name[i], 0))
    960 				continue;
    961 			cur.select[i] = 1;
    962 			if (ordersize <= ndrives) {
    963 				int *new = realloc(order,
    964 				    (ordersize + 8) * sizeof *order);
    965 				if (new == NULL)
    966 					break;
    967 				ordersize += 8;
    968 				order = new;
    969 			}
    970 			order[ndrives++] = i;
    971 		}
    972 
    973 	}
    974 
    975 	if (ndrives == 0 && tried == 0) {
    976 		/*
    977 		 * Pick up to defdrives (or all if -x is given) drives
    978 		 * if none specified.
    979 		 */
    980 		maxdrives = (ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y) ||
    981 			     (int)ndrive < defdrives)
    982 			? (int)(ndrive) : defdrives;
    983 		ordersize = maxdrives;
    984 		free(order);
    985 		order = calloc(ordersize, sizeof *order);
    986 		if (order == NULL)
    987 			errx(1, "Insufficient memory");
    988 		for (i = 0; i < maxdrives; i++) {
    989 			cur.select[i] = 1;
    990 			order[i] = i;
    991 
    992 			++ndrives;
    993 			if (!ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y) &&
    994 			    ndrives == defdrives)
    995 				break;
    996 		}
    997 	}
    998 
    999 #ifdef BACKWARD_COMPATIBILITY
   1000 	if (first && *argv) {
   1001 		interval = atoi(*argv);
   1002 		if (*++argv)
   1003 			reps = atoi(*argv);
   1004 	}
   1005 #endif
   1006 
   1007 	if (interval) {
   1008 		if (!reps)
   1009 			reps = -1;
   1010 	} else
   1011 		if (reps)
   1012 			interval = 1;
   1013 
   1014 	return (ndrives);
   1015 }
   1016