iostat.c revision 1.46 1 /* $NetBSD: iostat.c,v 1.46 2006/01/08 08:47:16 yamt 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\n\
67 The Regents of the University of California. All rights reserved.\n");
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.46 2006/01/08 08:47:16 yamt 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
91 #include "dkstats.h"
92 #include "tpstats.h"
93
94 /* Namelist and memory files. */
95 char *nlistf, *memf;
96
97 int hz, reps, interval;
98 static int todo = 0;
99 static int defdrives;
100 static int winlines = 20;
101 static int wincols = 80;
102
103 #define ISSET(x, a) ((x) & (a))
104 #define SHOW_CPU 1<<0
105 #define SHOW_TTY 1<<1
106 #define SHOW_STATS_1 1<<2
107 #define SHOW_STATS_2 1<<3
108 #define SHOW_STATS_X 1<<4
109 #define SHOW_TOTALS 1<<7
110 #define SHOW_STATS_ALL (SHOW_STATS_1 | SHOW_STATS_2 | SHOW_STATS_X)
111
112 static void cpustats(void);
113 static void disk_stats(double);
114 static void disk_stats2(double);
115 static void disk_statsx(double);
116 static void sig_header(int);
117 static volatile int do_header;
118 static void header(void);
119 static void usage(void);
120 static void display(void);
121 static int selectdrives(int, char *[]);
122
123 int main(int, char *[]);
124
125 int
126 main(int argc, char *argv[])
127 {
128 int ch, hdrcnt, ndrives, lines;
129 struct timespec tv;
130 struct ttysize ts;
131
132 while ((ch = getopt(argc, argv, "Cc:dDIM:N:Tw:x")) != -1)
133 switch (ch) {
134 case 'c':
135 if ((reps = atoi(optarg)) <= 0)
136 errx(1, "repetition count <= 0.");
137 break;
138 case 'C':
139 todo |= SHOW_CPU;
140 break;
141 case 'd':
142 todo &= ~SHOW_STATS_ALL;
143 todo |= SHOW_STATS_1;
144 break;
145 case 'D':
146 todo &= ~SHOW_STATS_ALL;
147 todo |= SHOW_STATS_2;
148 break;
149 case 'I':
150 todo |= SHOW_TOTALS;
151 break;
152 case 'M':
153 memf = optarg;
154 break;
155 case 'N':
156 nlistf = optarg;
157 break;
158 case 'T':
159 todo |= SHOW_TTY;
160 break;
161 case 'w':
162 if ((interval = atoi(optarg)) <= 0)
163 errx(1, "interval <= 0.");
164 break;
165 case 'x':
166 todo &= ~SHOW_STATS_ALL;
167 todo |= SHOW_STATS_X;
168 break;
169 case '?':
170 default:
171 usage();
172 }
173 argc -= optind;
174 argv += optind;
175
176 if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL))
177 todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1;
178 if (ISSET(todo, SHOW_STATS_X)) {
179 todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL);
180 todo |= SHOW_STATS_X;
181 }
182
183 if (ioctl(STDOUT_FILENO, TIOCGSIZE, &ts) != -1) {
184 if (ts.ts_lines)
185 winlines = ts.ts_lines;
186 if (ts.ts_cols)
187 wincols = ts.ts_cols;
188 }
189
190 defdrives = wincols;
191 if (ISSET(todo, SHOW_CPU))
192 defdrives -= 16; /* XXX magic number */
193 if (ISSET(todo, SHOW_TTY))
194 defdrives -= 9; /* XXX magic number */
195 defdrives /= 18; /* XXX magic number */
196
197 dkinit(0);
198 tpinit(0);
199 dkreadstats();
200 tpreadstats();
201 ndrives = selectdrives(argc, argv);
202 if (ndrives == 0) {
203 /* No drives are selected. No need to show disk stats. */
204 todo &= ~SHOW_STATS_ALL;
205 if (todo == 0)
206 errx(1, "no drives");
207 }
208 if (ISSET(todo, SHOW_STATS_X))
209 lines = ndrives;
210 else
211 lines = 1;
212
213 tv.tv_sec = interval;
214 tv.tv_nsec = 0;
215
216 /* print a new header on sigcont */
217 (void)signal(SIGCONT, sig_header);
218
219 for (hdrcnt = 1;;) {
220 if (do_header ||
221 ((hdrcnt -= lines) <= 0) ||
222 ISSET(todo, SHOW_STATS_X)) {
223 do_header = 0;
224 header();
225 hdrcnt = winlines - 4;
226 }
227
228 if (!ISSET(todo, SHOW_TOTALS)) {
229 dkswap();
230 tpswap();
231 }
232
233 display();
234
235 if (reps >= 0 && --reps <= 0)
236 break;
237 nanosleep(&tv, NULL);
238 dkreadstats();
239 tpreadstats();
240 }
241 exit(0);
242 }
243
244 static void
245 sig_header(int signo)
246 {
247 do_header = 1;
248 }
249
250 static void
251 header()
252 {
253 int i;
254
255 /* Main Headers. */
256 if (ISSET(todo, SHOW_STATS_X)) {
257 if (ISSET(todo, SHOW_TOTALS)) {
258 (void)printf(
259 "device read KB/t xfr time MB ");
260 (void)printf(" write KB/t xfr time MB\n");
261 } else {
262 (void)printf(
263 "device read KB/t r/s time MB/s");
264 (void)printf(" write KB/t w/s time MB/s\n");
265 }
266 return;
267 }
268
269 if (ISSET(todo, SHOW_TTY))
270 (void)printf(" tty");
271
272 if (ISSET(todo, SHOW_STATS_1)) {
273 for (i = 0; i < dk_ndrive; i++)
274 if (cur.dk_select[i])
275 (void)printf(" %9.9s ", cur.dk_name[i]);
276 for (i = 0; i < tp_ndrive; i++)
277 if (cur_tape.select[i])
278 (void)printf(" %9.9s ",
279 cur_tape.name[i]);
280 }
281
282 if (ISSET(todo, SHOW_STATS_2)) {
283 for (i = 0; i < dk_ndrive; i++)
284 if (cur.dk_select[i])
285 (void)printf(" %9.9s ", cur.dk_name[i]);
286 for (i = 0; i < tp_ndrive; i++)
287 if (cur_tape.select[i])
288 (void)printf(" %9.9s ",
289 cur_tape.name[i]);
290 }
291
292 if (ISSET(todo, SHOW_CPU))
293 (void)printf(" CPU");
294
295 printf("\n");
296
297 /* Sub-Headers. */
298 if (ISSET(todo, SHOW_TTY))
299 printf(" tin tout");
300
301 if (ISSET(todo, SHOW_STATS_1)) {
302 for (i = 0; i < dk_ndrive; i++)
303 if (cur.dk_select[i]) {
304 if (ISSET(todo, SHOW_TOTALS))
305 (void)printf(" KB/t xfr MB ");
306 else
307 (void)printf(" KB/t t/s MB/s ");
308 }
309 for (i = 0; i < tp_ndrive; i++)
310 if (cur_tape.select[i]) {
311 if (ISSET(todo, SHOW_TOTALS))
312 (void)printf(" KB/t xfr MB ");
313 else
314 (void)printf(" KB/t t/s MB/s ");
315 }
316 }
317
318 if (ISSET(todo, SHOW_STATS_2)) {
319 for (i = 0; i < dk_ndrive; i++)
320 if (cur.dk_select[i])
321 (void)printf(" KB xfr time ");
322 for (i = 0; i < tp_ndrive; i++)
323 if (cur_tape.select[i])
324 (void)printf(" KB xfr time ");
325 }
326
327 if (ISSET(todo, SHOW_CPU))
328 (void)printf(" us ni sy in id");
329 printf("\n");
330 }
331
332 static void
333 disk_stats(double etime)
334 {
335 int dn;
336 double atime, mbps;
337
338 for (dn = 0; dn < dk_ndrive; ++dn) {
339 if (!cur.dk_select[dn])
340 continue;
341 /* average Kbytes per transfer. */
342 if (cur.dk_rxfer[dn] + cur.dk_wxfer[dn])
343 mbps = ((cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) /
344 1024.0) / (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]);
345 else
346 mbps = 0.0;
347 (void)printf(" %5.2f", mbps);
348
349 /* average transfers per second. */
350 (void)printf(" %4.0f",
351 (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime);
352
353 /* time busy in disk activity */
354 atime = (double)cur.dk_time[dn].tv_sec +
355 ((double)cur.dk_time[dn].tv_usec / (double)1000000);
356
357 /* Megabytes per second. */
358 if (atime != 0.0)
359 mbps = (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) /
360 (double)(1024 * 1024);
361 else
362 mbps = 0;
363 (void)printf(" %5.2f ", mbps / etime);
364 }
365 }
366
367 static void
368 disk_stats2(double etime)
369 {
370 int dn;
371 double atime;
372
373 for (dn = 0; dn < dk_ndrive; ++dn) {
374 if (!cur.dk_select[dn])
375 continue;
376
377 /* average kbytes per second. */
378 (void)printf(" %5.0f",
379 (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) / 1024.0 / etime);
380
381 /* average transfers per second. */
382 (void)printf(" %5.0f",
383 (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime);
384
385 /* average time busy in disk activity */
386 atime = (double)cur.dk_time[dn].tv_sec +
387 ((double)cur.dk_time[dn].tv_usec / (double)1000000);
388 (void)printf(" %4.2f ", atime / etime);
389 }
390 }
391
392 static void
393 disk_statsx(double etime)
394 {
395 int dn;
396 double atime, kbps;
397
398 for (dn = 0; dn < dk_ndrive; ++dn) {
399 if (!cur.dk_select[dn])
400 continue;
401
402 (void)printf("%-8.8s", cur.dk_name[dn]);
403
404 /* average read Kbytes per transfer */
405 if (cur.dk_rxfer[dn])
406 kbps = (cur.dk_rbytes[dn] / 1024.0) / cur.dk_rxfer[dn];
407 else
408 kbps = 0.0;
409 (void)printf(" %8.2f", kbps);
410
411 /* average read transfers
412 (per second) */
413 (void)printf(" %6.0f", cur.dk_rxfer[dn] / etime);
414
415 /* time read busy in disk activity */
416 atime = (double)cur.dk_time[dn].tv_sec +
417 ((double)cur.dk_time[dn].tv_usec / (double)1000000);
418 (void)printf(" %6.2f", atime / etime);
419
420 /* average read megabytes
421 (per second) */
422 (void)printf(" %8.2f",
423 cur.dk_rbytes[dn] / (1024.0 * 1024) / etime);
424
425
426 /* average write Kbytes per transfer */
427 if (cur.dk_wxfer[dn])
428 kbps = (cur.dk_wbytes[dn] / 1024.0) / cur.dk_wxfer[dn];
429 else
430 kbps = 0.0;
431 (void)printf(" %8.2f", kbps);
432
433 /* average write transfers
434 (per second) */
435 (void)printf(" %6.0f", cur.dk_wxfer[dn] / etime);
436
437 /* time write busy in disk activity */
438 atime = (double)cur.dk_time[dn].tv_sec +
439 ((double)cur.dk_time[dn].tv_usec / (double)1000000);
440 (void)printf(" %6.2f", atime / etime);
441
442 /* average write megabytes
443 (per second) */
444 (void)printf(" %8.2f\n",
445 cur.dk_wbytes[dn] / (1024.0 * 1024) / etime);
446 }
447 }
448
449 static void
450 tape_stats(double etime)
451 {
452 int dn;
453 double atime, mbps;
454
455 for (dn = 0; dn < tp_ndrive; ++dn) {
456 if (!cur_tape.select[dn])
457 continue;
458 /* average Kbytes per transfer. */
459 if (cur_tape.rxfer[dn] + cur_tape.wxfer[dn])
460 mbps = ((cur_tape.rbytes[dn] + cur_tape.wbytes[dn]) /
461 1024.0) / (cur_tape.rxfer[dn] + cur_tape.wxfer[dn]);
462 else
463 mbps = 0.0;
464 (void)printf(" %5.2f", mbps);
465
466 /* average transfers per second. */
467 (void)printf(" %4.0f",
468 (cur_tape.rxfer[dn] + cur_tape.wxfer[dn]) / etime);
469
470 /* time busy in disk activity */
471 atime = (double)cur_tape.time[dn].tv_sec +
472 ((double)cur_tape.time[dn].tv_usec / (double)1000000);
473
474 /* Megabytes per second. */
475 if (atime != 0.0)
476 mbps = (cur_tape.rbytes[dn] + cur_tape.wbytes[dn]) /
477 (double)(1024 * 1024);
478 else
479 mbps = 0;
480 (void)printf(" %5.2f ", mbps / etime);
481 }
482 }
483
484 static void
485 tape_stats2(double etime)
486 {
487 int dn;
488 double atime;
489
490 for (dn = 0; dn < tp_ndrive; ++dn) {
491 if (!cur_tape.select[dn])
492 continue;
493
494 /* average kbytes per second. */
495 (void)printf(" %5.0f",
496 (cur_tape.rbytes[dn] + cur_tape.wbytes[dn]) / 1024.0 / etime);
497
498 /* average transfers per second. */
499 (void)printf(" %5.0f",
500 (cur_tape.rxfer[dn] + cur_tape.wxfer[dn]) / etime);
501
502 /* average time busy in disk activity */
503 atime = (double)cur_tape.time[dn].tv_sec +
504 ((double)cur_tape.time[dn].tv_usec / (double)1000000);
505 (void)printf(" %4.2f ", atime / etime);
506 }
507 }
508
509 static void
510 tape_statsx(double etime)
511 {
512 int dn;
513 double atime, kbps;
514
515 for (dn = 0; dn < tp_ndrive; ++dn) {
516 if (!cur_tape.select[dn])
517 continue;
518
519 (void)printf("%-8.8s", cur_tape.name[dn]);
520
521 /* average read Kbytes per transfer */
522 if (cur.dk_rxfer[dn])
523 kbps = (cur_tape.rbytes[dn] / 1024.0) / cur_tape.rxfer[dn];
524 else
525 kbps = 0.0;
526 (void)printf(" %8.2f", kbps);
527
528 /* average read transfers
529 (per second) */
530 (void)printf(" %6.0f", cur_tape.rxfer[dn] / etime);
531
532 /* time read busy in disk activity */
533 atime = (double)cur_tape.time[dn].tv_sec +
534 ((double)cur_tape.time[dn].tv_usec / (double)1000000);
535 (void)printf(" %6.2f", atime / etime);
536
537 /* average read megabytes
538 (per second) */
539 (void)printf(" %8.2f",
540 cur_tape.rbytes[dn] / (1024.0 * 1024) / etime);
541
542
543 /* average write Kbytes per transfer */
544 if (cur_tape.wxfer[dn])
545 kbps = (cur_tape.wbytes[dn] / 1024.0) / cur_tape.wxfer[dn];
546 else
547 kbps = 0.0;
548 (void)printf(" %8.2f", kbps);
549
550 /* average write transfers
551 (per second) */
552 (void)printf(" %6.0f", cur_tape.wxfer[dn] / etime);
553
554 /* time write busy in disk activity */
555 atime = (double)cur_tape.time[dn].tv_sec +
556 ((double)cur_tape.time[dn].tv_usec / (double)1000000);
557 (void)printf(" %6.2f", atime / etime);
558
559 /* average write megabytes
560 (per second) */
561 (void)printf(" %8.2f\n",
562 cur_tape.wbytes[dn] / (1024.0 * 1024) / etime);
563 }
564 }
565
566 static void
567 cpustats(void)
568 {
569 int state;
570 double time;
571
572 time = 0;
573 for (state = 0; state < CPUSTATES; ++state)
574 time += cur.cp_time[state];
575 if (!time)
576 time = 1.0;
577 /* States are generally never 100% and can use %3.0f. */
578 for (state = 0; state < CPUSTATES; ++state)
579 printf(" %2.0f", 100. * cur.cp_time[state] / time);
580 }
581
582 static void
583 usage(void)
584 {
585
586 (void)fprintf(stderr, "usage: iostat [-CdDITx] [-c count] [-M core] "
587 "[-N system] [-w wait] [drives]\n");
588 exit(1);
589 }
590
591 static void
592 display(void)
593 {
594 double etime;
595
596 /* Sum up the elapsed ticks. */
597 etime = cur.cp_etime;
598
599 /*
600 * If we're showing totals only, then don't divide by the
601 * system time.
602 */
603 if (ISSET(todo, SHOW_TOTALS))
604 etime = 1.0;
605
606 if (ISSET(todo, SHOW_STATS_X)) {
607 disk_statsx(etime);
608 tape_statsx(etime);
609 goto out;
610 }
611
612 if (ISSET(todo, SHOW_TTY))
613 printf("%4.0f %4.0f", cur.tk_nin / etime, cur.tk_nout / etime);
614
615 if (ISSET(todo, SHOW_STATS_1)) {
616 disk_stats(etime);
617 tape_stats(etime);
618 }
619
620
621 if (ISSET(todo, SHOW_STATS_2)) {
622 disk_stats2(etime);
623 tape_stats2(etime);
624 }
625
626
627 if (ISSET(todo, SHOW_CPU))
628 cpustats();
629
630 (void)printf("\n");
631
632 out:
633 (void)fflush(stdout);
634 }
635
636 static int
637 selectdrives(int argc, char *argv[])
638 {
639 int i, maxdrives, ndrives, tried;
640
641 /*
642 * Choose drives to be displayed. Priority goes to (in order) drives
643 * supplied as arguments and default drives. If everything isn't
644 * filled in and there are drives not taken care of, display the first
645 * few that fit.
646 *
647 * The backward compatibility #ifdefs permit the syntax:
648 * iostat [ drives ] [ interval [ count ] ]
649 */
650
651 #define BACKWARD_COMPATIBILITY
652 for (tried = ndrives = 0; *argv; ++argv) {
653 #ifdef BACKWARD_COMPATIBILITY
654 if (isdigit((unsigned char)**argv))
655 break;
656 #endif
657 tried++;
658 for (i = 0; i < dk_ndrive; i++) {
659 if (strcmp(cur.dk_name[i], *argv))
660 continue;
661 cur.dk_select[i] = 1;
662 ++ndrives;
663 }
664
665 for (i = 0; i < tp_ndrive; i++) {
666 if (strcmp(cur_tape.name[i], *argv))
667 continue;
668 cur_tape.select[i] = 1;
669 ++ndrives;
670 }
671 }
672
673 if (ndrives == 0 && tried == 0) {
674 /*
675 * Pick up to defdrives (or all if -x is given) drives
676 * if none specified.
677 */
678 maxdrives = (ISSET(todo, SHOW_STATS_X) ||
679 (dk_ndrive + tp_ndrive) < defdrives)
680 ? (dk_ndrive + tp_ndrive) : defdrives;
681 for (i = 0; i < maxdrives; i++) {
682 if (i >= dk_ndrive) {
683 cur_tape.select[i - dk_ndrive] = 1;
684 } else {
685 cur.dk_select[i] = 1;
686 }
687
688 ++ndrives;
689 if (!ISSET(todo, SHOW_STATS_X) && ndrives == defdrives)
690 break;
691 }
692 }
693
694 #ifdef BACKWARD_COMPATIBILITY
695 if (*argv) {
696 interval = atoi(*argv);
697 if (*++argv)
698 reps = atoi(*argv);
699 }
700 #endif
701
702 if (interval) {
703 if (!reps)
704 reps = -1;
705 } else
706 if (reps)
707 interval = 1;
708
709 return (ndrives);
710 }
711