xref: /dragonfly/sbin/vinum/list.c (revision 28c7b939)
1 /*      list.c: vinum interface program, list routines
2  */
3 /*-
4  * Copyright (c) 1997, 1998
5  *	Nan Yang Computer Services Limited.  All rights reserved.
6  *
7  *  Parts copyright (c) 1997, 1998 Cybernet Corporation, NetMAX project.
8  *
9  *  Written by Greg Lehey
10  *
11  *  This software is distributed under the so-called ``Berkeley
12  *  License'':
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  * 3. All advertising materials mentioning features or use of this software
23  *    must display the following acknowledgement:
24  *	This product includes software developed by Nan Yang Computer
25  *      Services Limited.
26  * 4. Neither the name of the Company nor the names of its contributors
27  *    may be used to endorse or promote products derived from this software
28  *    without specific prior written permission.
29  *
30  * This software is provided ``as is'', and any express or implied
31  * warranties, including, but not limited to, the implied warranties of
32  * merchantability and fitness for a particular purpose are disclaimed.
33  * In no event shall the company or contributors be liable for any
34  * direct, indirect, incidental, special, exemplary, or consequential
35  * damages (including, but not limited to, procurement of substitute
36  * goods or services; loss of use, data, or profits; or business
37  * interruption) however caused and on any theory of liability, whether
38  * in contract, strict liability, or tort (including negligence or
39  * otherwise) arising in any way out of the use of this software, even if
40  * advised of the possibility of such damage.
41  *
42  * $Id: list.c,v 1.25 2000/12/20 03:38:43 grog Exp grog $
43  * $FreeBSD: src/sbin/vinum/list.c,v 1.25.2.4 2001/05/28 05:58:04 grog Exp $
44  * $DragonFly: src/sbin/vinum/list.c,v 1.4 2003/11/21 22:46:13 dillon Exp $
45  */
46 
47 #define _KERNEL_STRUCTURES
48 
49 #include <ctype.h>
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <sys/mman.h>
53 #include <netdb.h>
54 #include <setjmp.h>
55 #include <signal.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60 #include <sys/ioctl.h>
61 #include <sys/utsname.h>
62 #include <dev/raid/vinum/vinumhdr.h>
63 #include "vext.h"
64 #include <dev/raid/vinum/request.h>
65 #include <devstat.h>
66 
67 /*
68  * When a subdisk is reviving or initializing, we
69  * check to see whether it is still progressing
70  * and print a warning if not.  We check every 50
71  * ms, up to a maximum of 5 seconds.  This is the
72  * counter value.
73  */
74 #define STALLCOUNT	100
75 
76 /*
77  * Take a size in sectors and return a pointer to
78  * a string which represents the size best.  If lj
79  * is != 0, return left justified, otherwise in a
80  * fixed 10 character field suitable for columnar
81  * printing.
82  *
83  * Note this uses a static string: it's only
84  * intended to be used immediately for printing.
85  */
86 char *
87 roughlength(int64_t bytes, int lj)
88 {
89     static char description[16];
90 
91     if (bytes > (int64_t) MEGABYTE * 10000)		    /* gigabytes */
92 	sprintf(description, lj ? "%lld GB" : "%10d GB", bytes / GIGABYTE);
93     else if (bytes > KILOBYTE * 10000)			    /* megabytes */
94 	sprintf(description, lj ? "%lld MB" : "%10d MB", bytes / MEGABYTE);
95     else if (bytes > 10000)				    /* kilobytes */
96 	sprintf(description, lj ? "%lld kB" : "%10d kB", bytes / KILOBYTE);
97     else						    /* bytes */
98 	sprintf(description, lj ? "%lld  B" : "%10d  B", bytes);
99     return description;
100 }
101 
102 void
103 vinum_list(int argc, char *argv[], char *argv0[])
104 {
105     int object;
106     int i;
107     enum objecttype type;
108 
109     if (sflag & (!vflag))				    /* just summary stats, */
110 	printf("Object\t\t  Reads\t\tBytes\tAverage\tRecover\t Writes"
111 	    "\t\tBytes\tAverage\t  Mblock  Mstripe\n\n");
112     if (argc == 0)
113 	listconfig();					    /* list everything */
114     else {
115 	for (i = 0; i < argc; i++) {
116 	    object = find_object(argv[i], &type);	    /* look for it */
117 	    if (vinum_li(object, type))
118 		fprintf(stderr, "Can't find object: %s\n", argv[i]);
119 	}
120     }
121 }
122 
123 /* List an object */
124 int
125 vinum_li(int object, enum objecttype type)
126 {
127     switch (type) {
128     case drive_object:
129 	vinum_ldi(object, recurse);
130 	break;
131 
132     case sd_object:
133 	vinum_lsi(object, recurse);
134 	break;
135 
136     case plex_object:
137 	vinum_lpi(object, recurse);
138 	break;
139 
140     case volume_object:
141 	vinum_lvi(object, recurse);
142 	break;
143 
144     default:
145 	return -1;
146     }
147     return 0;
148 }
149 
150 void
151 vinum_ldi(int driveno, int recurse)
152 {
153     time_t t;						    /* because Bruce says so */
154     int sdno;						    /* for recursion */
155 
156     get_drive_info(&drive, driveno);
157     if (drive.state != drive_unallocated) {
158 	if (vflag) {
159 	    printf("Drive %s:\tDevice %s\n",
160 		drive.label.name,
161 		drive.devicename);
162 	    t = drive.label.date_of_birth.tv_sec;
163 	    printf("\t\tCreated on %s at %s",
164 		drive.label.sysname,
165 		ctime(&t));
166 	    t = drive.label.last_update.tv_sec;
167 	    printf("\t\tConfig last updated %s",	    /* care: \n at end */
168 		ctime(&t));
169 	    printf("\t\tSize: %16lld bytes (%lld MB)\n\t\tUsed: %16lld bytes (%lld MB)\n"
170 		"\t\tAvailable: %11qd bytes (%d MB)\n",
171 		(long long) drive.label.drive_size,	    /* bytes used */
172 		(long long) (drive.label.drive_size / MEGABYTE),
173 		(long long) (drive.label.drive_size - drive.sectors_available
174 		    * DEV_BSIZE),
175 		(long long) (drive.label.drive_size - drive.sectors_available
176 		    * DEV_BSIZE) / MEGABYTE,
177 		(long long) drive.sectors_available * DEV_BSIZE,
178 		(int) (drive.sectors_available * DEV_BSIZE / MEGABYTE));
179 	    printf("\t\tState: %s\n", drive_state(drive.state));
180 	    if (drive.lasterror != 0)
181 		printf("\t\tLast error: %s\n", strerror(drive.lasterror));
182 	    else
183 		printf("\t\tLast error: none\n");
184 	    printf("\t\tActive requests:\t%d\n\t\tMaximum active:\t\t%d\n",
185 		drive.active,
186 		drive.maxactive);
187 	    if (Verbose) {				    /* print the free list */
188 		int fe;					    /* freelist entry */
189 		struct drive_freelist freelist;
190 		struct ferq {				    /* request to pass to ioctl */
191 		    int driveno;
192 		    int fe;
193 		} *ferq = (struct ferq *) &freelist;
194 
195 		printf("\t\tFree list contains %d entries:\n\t\t   Offset\t     Size\n",
196 		    drive.freelist_entries);
197 		for (fe = 0; fe < drive.freelist_entries; fe++) {
198 		    ferq->driveno = drive.driveno;
199 		    ferq->fe = fe;
200 		    if (ioctl(superdev, VINUM_GETFREELIST, &freelist) < 0) {
201 			fprintf(stderr,
202 			    "Can't get free list element %d: %s\n",
203 			    fe,
204 			    strerror(errno));
205 			longjmp(command_fail, -1);
206 		    }
207 		    printf("\t\t%9lld\t%9lld\n",
208 			(long long) freelist.offset,
209 			(long long) freelist.sectors);
210 		}
211 	    }
212 	} else if (!sflag) {
213 	    printf("D %-21s State: %s\tDevice %s\tAvail: %lld/%lld MB",
214 		drive.label.name,
215 		drive_state(drive.state),
216 		drive.devicename,
217 		(long long) drive.sectors_available * DEV_BSIZE / MEGABYTE,
218 		(long long) (drive.label.drive_size / MEGABYTE));
219 	    if (drive.label.drive_size != 0)
220 		printf(" (%d%%)",
221 		    (int) ((drive.sectors_available * 100 * DEV_BSIZE)
222 			/ (drive.label.drive_size - (DATASTART * DEV_BSIZE))));
223 	}
224 	if (sflag) {
225 	    if (vflag || Verbose) {
226 		printf("\t\tReads:  \t%16lld\n\t\tBytes read:\t%16lld (%s)\n",
227 		    (long long) drive.reads,
228 		    (long long) drive.bytes_read,
229 		    roughlength(drive.bytes_read, 1));
230 		if (drive.reads != 0)
231 		    printf("\t\tAverage read:\t%16lld bytes\n",
232 			(long long) drive.bytes_read / drive.reads);
233 		printf("\t\tWrites: \t%16lld\n\t\tBytes written:\t%16lld (%s)\n",
234 		    (long long) drive.writes,
235 		    (long long) drive.bytes_written,
236 		    roughlength(drive.bytes_written, 1));
237 		if (drive.writes != 0)
238 		    printf("\t\tAverage write:\t%16lld bytes\n",
239 			(long long) (drive.bytes_written / drive.writes));
240 	    } else {					    /* non-verbose stats */
241 		printf("%-15s\t%7lld\t%15lld\t",
242 		    drive.label.name,
243 		    (long long) drive.reads,
244 		    (long long) drive.bytes_read);
245 		if (drive.reads != 0)
246 		    printf("%7lld\t\t",
247 			(long long) (drive.bytes_read / drive.reads));
248 		else
249 		    printf("\t\t");
250 		printf("%7lld\t%15lld\t",
251 		    (long long) drive.writes,
252 		    (long long) drive.bytes_written);
253 		if (drive.writes != 0)
254 		    printf("%7lld",
255 			(long long) (drive.bytes_written / drive.writes));
256 	    }
257 	}
258 	if (recurse) {
259 	    printf("\n");
260 	    for (sdno = 0; sdno < vinum_conf.subdisks_allocated; sdno++) {
261 		get_sd_info(&sd, sdno);
262 		if ((sd.state != sd_unallocated)
263 		    && (sd.driveno == drive.driveno))
264 		    vinum_lsi(sd.sdno, 0);
265 	    }
266 	}
267 	printf("\n");
268     }
269 }
270 
271 void
272 vinum_ld(int argc, char *argv[], char *argv0[])
273 {
274     int i;
275     int driveno;
276     enum objecttype type;
277 
278     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
279 	perror("Can't get vinum config");
280 	return;
281     }
282     if (argc == 0) {
283 	for (driveno = 0; driveno < vinum_conf.drives_allocated; driveno++)
284 	    vinum_ldi(driveno, recurse);
285     } else {
286 	for (i = 0; i < argc; i++) {
287 	    driveno = find_object(argv[i], &type);
288 	    if (type == drive_object)
289 		vinum_ldi(driveno, recurse);
290 	    else
291 		fprintf(stderr, "%s is not a drive\n", argv[i]);
292 	}
293     }
294 }
295 
296 void
297 vinum_lvi(int volno, int recurse)
298 {
299     get_volume_info(&vol, volno);
300     if (vol.state != volume_unallocated) {
301 	if (vflag) {
302 	    printf("Volume %s:\tSize: %lld bytes (%lld MB)\n"
303 		"\t\tState: %s\n\t\tFlags: %s%s%s\n",
304 		vol.name,
305 		((long long) vol.size) * DEV_BSIZE,
306 		((long long) vol.size) * DEV_BSIZE / MEGABYTE,
307 		volume_state(vol.state),
308 		vol.flags & VF_OPEN ? "open " : "",
309 		(vol.flags & VF_WRITETHROUGH ? "writethrough " : ""),
310 		(vol.flags & VF_RAW ? "raw" : ""));
311 	    printf("\t\t%d plexes\n\t\tRead policy: ", vol.plexes);
312 	    if (vol.preferred_plex < 0)			    /* round robin */
313 		printf("round robin\n");
314 	    else {
315 		get_plex_info(&plex, vol.plex[vol.preferred_plex]);
316 		printf("plex %d (%s)\n", vol.preferred_plex, plex.name);
317 	    }
318 	} else if (!sflag)				    /* brief */
319 	    printf("V %-21s State: %s\tPlexes: %7d\tSize: %s\n",
320 		vol.name,
321 		volume_state(vol.state),
322 		vol.plexes,
323 		roughlength(vol.size << DEV_BSHIFT, 0));
324 	if (sflag) {
325 	    if (vflag || Verbose) {
326 		printf("\t\tReads:  \t%16lld\n\t\tRecovered:\t%16lld\n\t\tBytes read:\t%16lld (%s)\n",
327 		    (long long) vol.reads,
328 		    (long long) vol.recovered_reads,
329 		    (long long) vol.bytes_read,
330 		    roughlength(vol.bytes_read, 1));
331 		if (vol.reads != 0)
332 		    printf("\t\tAverage read:\t%16lld bytes\n",
333 			(long long) (vol.bytes_read / vol.reads));
334 		printf("\t\tWrites: \t%16lld\n\t\tBytes written:\t%16lld (%s)\n",
335 		    (long long) vol.writes,
336 		    (long long) vol.bytes_written,
337 		    roughlength(vol.bytes_written, 1));
338 		if (vol.writes != 0)
339 		    printf("\t\tAverage write:\t%16lld bytes\n",
340 			(long long) (vol.bytes_written / vol.writes));
341 		printf("\t\tActive requests:\t%8d\n", vol.active);
342 	    } else {					    /* brief stats listing */
343 		printf("%-15s\t%7lld\t%15lld\t",
344 		    vol.name,
345 		    (long long) vol.reads,
346 		    (long long) vol.bytes_read);
347 		if (vol.reads != 0)
348 		    printf("%7lld\t",
349 			(long long) (vol.bytes_read / vol.reads));
350 		else
351 		    printf("\t");
352 		printf("%7lld\t", (long long) vol.recovered_reads);
353 		printf("%7lld\t%15lld\t",
354 		    (long long) vol.writes,
355 		    vol.bytes_written);
356 		if (vol.writes != 0)
357 		    printf("%7lld\n",
358 			(long long) (vol.bytes_written / vol.writes));
359 		else
360 		    printf("\n");
361 	    }
362 	}
363 	if (vol.plexes > 0) {
364 	    int plexno;
365 	    if (Verbose) {				    /* brief list */
366 		for (plexno = 0; plexno < vol.plexes; plexno++) {
367 		    get_plex_info(&plex, vol.plex[plexno]);
368 							    /* Just a brief summary here */
369 		    printf("\t\tPlex %2d:\t%s\t(%s), %s\n",
370 			plexno,
371 			plex.name,
372 			plex_org(plex.organization),
373 			roughlength(plex.length << DEV_BSHIFT, 0));
374 		}
375 	    }
376 	    if (recurse) {
377 		for (plexno = 0; plexno < vol.plexes; plexno++)
378 		    vinum_lpi(vol.plex[plexno], 0);	    /* first show the plexes */
379 		for (plexno = 0; plexno < vol.plexes; plexno++) { /* then the subdisks */
380 		    get_plex_info(&plex, vol.plex[plexno]);
381 		    if (plex.subdisks > 0) {
382 			int sdno;
383 
384 			for (sdno = 0; sdno < plex.subdisks; sdno++) {
385 			    get_plex_sd_info(&sd, vol.plex[plexno], sdno);
386 			    vinum_lsi(sd.sdno, 0);
387 			}
388 		    }
389 		}
390 		printf("\n");
391 	    }
392 	}
393     }
394 }
395 
396 void
397 vinum_lv(int argc, char *argv[], char *argv0[])
398 {
399     int i;
400     int volno;
401     enum objecttype type;
402 
403     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
404 	perror("Can't get vinum config");
405 	return;
406     }
407     if (argc == 0)
408 	for (volno = 0; volno < vinum_conf.volumes_allocated; volno++)
409 	    vinum_lvi(volno, recurse);
410     else {
411 	for (i = 0; i < argc; i++) {
412 	    volno = find_object(argv[i], &type);
413 	    if (type == volume_object)
414 		vinum_lvi(volno, recurse);
415 	    else
416 		fprintf(stderr, "%s is not a volume\n", argv[i]);
417 	}
418     }
419 }
420 
421 void
422 vinum_lpi(int plexno, int recurse)
423 {
424     get_plex_info(&plex, plexno);
425     if (plex.state != plex_unallocated) {
426 	if (vflag) {
427 	    printf("Plex %s:\tSize:\t%9lld bytes (%lld MB)\n\t\tSubdisks: %8d\n",
428 		plex.name,
429 		(long long) plex.length * DEV_BSIZE,
430 		(long long) plex.length * DEV_BSIZE / MEGABYTE,
431 		plex.subdisks);
432 	    printf("\t\tState: %s\n\t\tOrganization: %s",
433 		plex_state(plex.state),
434 		plex_org(plex.organization));
435 	    if (isstriped((&plex)))
436 		printf("\tStripe size: %s\n", roughlength(plex.stripesize * DEV_BSIZE, 1));
437 	    else
438 		printf("\n");
439 	    if ((isparity((&plex)))
440 		&& (plex.checkblock != 0))
441 		printf("\t\tCheck block pointer:\t\t%s (%d%%)\n",
442 		    roughlength((plex.checkblock << DEV_BSHIFT) * (plex.subdisks - 1), 0),
443 		    (int) (((u_int64_t) (plex.checkblock * 100)) * (plex.subdisks - 1) / plex.length));
444 	    if (plex.volno >= 0) {
445 		get_volume_info(&vol, plex.volno);
446 		printf("\t\tPart of volume %s\n", vol.name);
447 	    }
448 	} else if (!sflag) {				    /* non-verbose list */
449 	    char *org = "";				    /* organization */
450 
451 	    switch (plex.organization) {
452 	    case plex_disorg:				    /* disorganized */
453 		org = "??";
454 		break;
455 	    case plex_concat:				    /* concatenated plex */
456 		org = "C";
457 		break;
458 	    case plex_striped:				    /* striped plex */
459 		org = "S";
460 		break;
461 	    case plex_raid4:				    /* RAID4 plex */
462 		org = "R4";
463 		break;
464 	    case plex_raid5:				    /* RAID5 plex */
465 		org = "R5";
466 		break;
467 	    }
468 	    printf("P %-18s %2s State: %s\tSubdisks: %5d\tSize: %s",
469 		plex.name,
470 		org,
471 		plex_state(plex.state),
472 		plex.subdisks,
473 		roughlength(plex.length << DEV_BSHIFT, 0));
474 	}
475 	if (sflag) {
476 	    if (vflag || Verbose) {
477 		printf("\t\tReads:  \t%16lld\n\t\tBytes read:\t%16lld (%s)\n",
478 		    (long long) plex.reads,
479 		    (long long) plex.bytes_read,
480 		    roughlength(plex.bytes_read, 1));
481 		if (plex.reads != 0)
482 		    printf("\t\tAverage read:\t%16lld bytes\n",
483 			(long long) (plex.bytes_read / plex.reads));
484 		printf("\t\tWrites: \t%16lld\n\t\tBytes written:\t%16lld (%s)\n",
485 		    (long long) plex.writes,
486 		    (long long) plex.bytes_written,
487 		    roughlength(plex.bytes_written, 1));
488 		if (plex.writes != 0)
489 		    printf("\t\tAverage write:\t%16lld bytes\n",
490 			(long long) (plex.bytes_written / plex.writes));
491 		if (((plex.reads + plex.writes) > 0)
492 		    && isstriped((&plex)))
493 		    printf("\t\tMultiblock:\t%16lld (%d%%)\n"
494 			"\t\tMultistripe:\t%16lld (%d%%)\n",
495 			(long long) plex.multiblock,
496 			(int) (plex.multiblock * 100 / (plex.reads + plex.writes)),
497 			(long long) plex.multistripe,
498 			(int) (plex.multistripe * 100 / (plex.reads + plex.writes)));
499 		if (plex.recovered_reads)
500 		    printf("\t\tRecovered reads:%16lld\n",
501 			(long long) plex.recovered_reads);
502 		if (plex.degraded_writes)
503 		    printf("\t\tDegraded writes:%16lld\n",
504 			(long long) plex.degraded_writes);
505 		if (plex.parityless_writes)
506 		    printf("\t\tParityless writes:%14lld\n",
507 			(long long) plex.parityless_writes);
508 	    } else {
509 		printf("%-15s\t%7lld\t%15lld\t",
510 		    plex.name,
511 		    (long long) plex.reads,
512 		    (long long) plex.bytes_read);
513 		if (plex.reads != 0)
514 		    printf("%7lld\t",
515 			(long long) (plex.bytes_read / plex.reads));
516 		else
517 		    printf("\t");
518 		printf("%7lld\t", (long long) plex.recovered_reads);
519 		printf("%7lld\t%15lld\t",
520 		    (long long) plex.writes,
521 		    (long long) plex.bytes_written);
522 		if (plex.writes != 0)
523 		    printf("%7lld\t",
524 			(long long) (plex.bytes_written / plex.writes));
525 		else
526 		    printf("\t");
527 		printf("%7lld\t%7lld\n",
528 		    (long long) plex.multiblock,
529 		    (long long) plex.multistripe);
530 	    }
531 	}
532 	if (plex.subdisks > 0) {
533 	    int sdno;
534 
535 	    if (Verbose) {
536 		printf("\n");
537 		for (sdno = 0; sdno < plex.subdisks; sdno++) {
538 		    get_plex_sd_info(&sd, plexno, sdno);
539 		    printf("\t\tSubdisk %d:\t%s\n\t\t  state: %s\tsize %11lld (%lld MB)\n",
540 			sdno,
541 			sd.name,
542 			sd_state(sd.state),
543 			(long long) sd.sectors * DEV_BSIZE,
544 			(long long) sd.sectors * DEV_BSIZE / MEGABYTE);
545 		    if (plex.organization == plex_concat)
546 			printf("\t\t\toffset %9ld (0x%lx)\n",
547 			    (long) sd.plexoffset,
548 			    (long) sd.plexoffset);
549 		}
550 	    }
551 	    if (recurse) {
552 		printf("\n");
553 		for (sdno = 0; sdno < plex.subdisks; sdno++) {
554 		    get_plex_sd_info(&sd, plexno, sdno);
555 		    vinum_lsi(sd.sdno, 0);
556 		}
557 	    }
558 	}
559 	printf("\n");
560     }
561 }
562 
563 void
564 vinum_lp(int argc, char *argv[], char *argv0[])
565 {
566     int i;
567     int plexno;
568     enum objecttype type;
569 
570     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
571 	perror("Can't get vinum config");
572 	return;
573     }
574     if (argc == 0) {
575 	for (plexno = 0; plexno < vinum_conf.plexes_allocated; plexno++)
576 	    vinum_lpi(plexno, recurse);
577     } else {
578 	for (i = 0; i < argc; i++) {
579 	    plexno = find_object(argv[i], &type);
580 	    if (type == plex_object)
581 		vinum_lpi(plexno, recurse);
582 	    else
583 		fprintf(stderr, "%s is not a plex\n", argv[i]);
584 	}
585     }
586 }
587 
588 void
589 vinum_lsi(int sdno, int recurse)
590 {
591     long long revived;					    /* keep an eye on revive progress */
592     int times;
593 
594     get_sd_info(&sd, sdno);
595     if (sd.state != sd_unallocated) {
596 	if (vflag) {
597 	    printf("Subdisk %s:\n\t\tSize: %16lld bytes (%lld MB)\n\t\tState: %s\n",
598 		sd.name,
599 		(long long) sd.sectors * DEV_BSIZE,
600 		(long long) sd.sectors / (MEGABYTE / DEV_BSIZE),
601 		sd_state(sd.state));
602 	    if (sd.flags & VF_RETRYERRORS)
603 	        printf("\t\tretryerrors\n");
604 	    if (sd.plexno >= 0) {
605 		get_plex_info(&plex, sd.plexno);
606 		printf("\t\tPlex %s", plex.name);
607 		printf(" at offset %lld (%s)\n",
608 		    (long long) sd.plexoffset * DEV_BSIZE,
609 		    roughlength((long long) sd.plexoffset * DEV_BSIZE, 1));
610 	    }
611 	    if (sd.state == sd_reviving) {
612 		if (sd.reviver == 0)
613 		    printf("\t\t*** Start subdisk with 'start' command ***\n");
614 		else {
615 		    printf("\t\tReviver PID:\t%d\n", sd.reviver);
616 		    if (kill(sd.reviver, 0) == -1) {
617 			if (errno == ESRCH)		    /* no process */
618 			    printf("\t\t*** Revive process has died ***\n");
619 							    /* Don't report a problem that "can't happen" */
620 		    } else {
621 			revived = sd.revived;		    /* note how far we were */
622 
623 			/*
624 			 * Wait for up to a second until we
625 			 * see some progress with the revive.
626 			 * Do it like this so we don't have
627 			 * annoying delays in the listing.
628 			 */
629 			for (times = 0; times < STALLCOUNT; times++) {
630 			    get_sd_info(&sd, sdno);
631 			    if (sd.revived != revived)	    /* progress? */
632 				break;
633 			    usleep(50000);
634 			}
635 			if (times == STALLCOUNT)
636 			    printf("\t\t*** Revive has stalled ***\n");
637 		    }
638 		}
639 		printf("\t\tRevive pointer:\t\t%s (%d%%)\n",
640 		    roughlength(sd.revived << DEV_BSHIFT, 0),
641 		    (int) (((u_int64_t) (sd.revived * 100)) / sd.sectors));
642 		printf("\t\tRevive blocksize:\t%s\n"
643 		    "\t\tRevive interval:\t%10d seconds\n",
644 		    roughlength(sd.revive_blocksize, 0),
645 		    sd.revive_interval);
646 	    }
647 	    if (sd.state == sd_initializing) {
648 		printf("\t\tInitialize pointer:\t%s (%d%%)\n",
649 		    roughlength(sd.initialized << DEV_BSHIFT, 0),
650 		    (int) (((u_int64_t) (sd.initialized * 100)) / sd.sectors));
651 		printf("\t\tInitialize blocksize:\t%s\n"
652 		    "\t\tInitialize interval:\t%10d seconds\n",
653 		    roughlength(sd.init_blocksize, 0),
654 		    sd.init_interval);
655 	    }
656 	    get_drive_info(&drive, sd.driveno);
657 	    if (sd.driveoffset < 0)
658 		printf("\t\tDrive %s (%s), no offset\n",
659 		    drive.label.name,
660 		    drive.devicename);
661 	    else if (drive.devicename[0] != '\0')	    /* has a name */
662 		printf("\t\tDrive %s (%s) at offset %lld (%s)\n",
663 		    drive.label.name,
664 		    drive.devicename,
665 		    (long long) (sd.driveoffset * DEV_BSIZE),
666 		    roughlength(sd.driveoffset * DEV_BSIZE, 1));
667 	    else
668 		printf("\t\tDrive %s (*missing*) at offset %lld (%s)\n",
669 		    drive.label.name,
670 		    (long long) (sd.driveoffset * DEV_BSIZE),
671 		    roughlength(sd.driveoffset * DEV_BSIZE, 1));
672 	} else if (!sflag) {				    /* brief listing, no stats */
673 	    if (sd.state == sd_reviving)
674 		printf("S %-21s State: R %d%%\t",
675 		    sd.name,
676 		    (int) (((u_int64_t) (sd.revived * 100)) / sd.sectors));
677 	    else if (sd.state == sd_initializing)
678 		printf("S %-21s State: I %d%%\t",
679 		    sd.name,
680 		    (int) (((u_int64_t) (sd.initialized * 100)) / sd.sectors));
681 	    else
682 		printf("S %-21s State: %s\t",
683 		    sd.name,
684 		    sd_state(sd.state));
685 	    if (sd.plexno == -1)
686 		printf("(detached)\t");
687 	    else
688 		printf("PO: %s ",
689 		    &(roughlength(sd.plexoffset << DEV_BSHIFT, 0))[2]);	/* what a kludge! */
690 	    printf("Size: %s\n",
691 		roughlength(sd.sectors << DEV_BSHIFT, 0));
692 	    if (sd.state == sd_reviving) {
693 		if (sd.reviver == 0)
694 		    printf("\t\t\t*** Start %s with 'start' command ***\n",
695 			sd.name);
696 		else if (kill(sd.reviver, 0) == -1) {
697 		    if (errno == ESRCH)			    /* no process */
698 			printf("\t\t\t*** Revive process for %s has died ***\n",
699 			    sd.name);
700 							    /* Don't report a problem that "can't happen" */
701 		} else {
702 		    revived = sd.revived;		    /* note how far we were */
703 
704 		    /*
705 		     * Wait for up to a second until we
706 		     * see some progress with the revive.
707 		     * Do it like this so we don't have
708 		     * annoying delays in the listing.
709 		     */
710 		    for (times = 0; times < STALLCOUNT; times++) {
711 			get_sd_info(&sd, sdno);
712 			if (sd.revived != revived)	    /* progress? */
713 			    break;
714 			usleep(50000);
715 		    }
716 		    if (times == STALLCOUNT)
717 			printf("\t\t\t*** Revive of %s has stalled ***\n",
718 			    sd.name);
719 		}
720 	    }
721 	}
722 	if (sflag) {
723 	    if (vflag || Verbose) {
724 		printf("\t\tReads:  \t%16lld\n\t\tBytes read:\t%16lld (%s)\n",
725 		    (long long) sd.reads,
726 		    (long long) sd.bytes_read,
727 		    roughlength(sd.bytes_read, 1));
728 		if (sd.reads != 0)
729 		    printf("\t\tAverage read:\t%16lld bytes\n",
730 			(long long) (sd.bytes_read / sd.reads));
731 		printf("\t\tWrites: \t%16lld\n\t\tBytes written:\t%16lld (%s)\n",
732 		    (long long) sd.writes,
733 		    (long long) sd.bytes_written,
734 		    roughlength(sd.bytes_written, 1));
735 		if (sd.writes != 0)
736 		    printf("\t\tAverage write:\t%16lld bytes\n",
737 			(long long) (sd.bytes_written / sd.writes));
738 	    } else {
739 		printf("%-15s\t%7lld\t%15lld\t",
740 		    sd.name,
741 		    (long long) sd.reads,
742 		    (long long) sd.bytes_read);
743 		if (sd.reads != 0)
744 		    printf("%7lld\t\t",
745 			(long long) (sd.bytes_read / sd.reads));
746 		else
747 		    printf("\t\t");
748 		printf("%7lld\t%15lld\t",
749 		    (long long) sd.writes,
750 		    (long long) sd.bytes_written);
751 		if (sd.writes != 0)
752 		    printf("%7lld\n",
753 			(long long) (sd.bytes_written / sd.writes));
754 		else
755 		    printf("\n");
756 	    }
757 	}
758 	if (recurse)
759 	    vinum_ldi(sd.driveno, 0);
760 	if (vflag)
761 	    printf("\n");				    /* make it more readable */
762     }
763 }
764 
765 void
766 vinum_ls(int argc, char *argv[], char *argv0[])
767 {
768     int i;
769     int sdno;
770 
771     /* Structures to read kernel data into */
772     struct _vinum_conf vinum_conf;
773     enum objecttype type;
774 
775     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
776 	perror("Can't get vinum config");
777 	return;
778     }
779     if (argc == 0) {
780 	for (sdno = 0; sdno < vinum_conf.subdisks_allocated; sdno++)
781 	    vinum_lsi(sdno, recurse);
782     } else {						    /* specific subdisks */
783 	for (i = 0; i < argc; i++) {
784 	    sdno = find_object(argv[i], &type);
785 	    if (type == sd_object)
786 		vinum_lsi(sdno, recurse);
787 	    else
788 		fprintf(stderr, "%s is not a subdisk\n", argv[i]);
789 	}
790     }
791 }
792 
793 
794 /* List the complete configuration.
795 
796  * XXX Change this to specific lists */
797 void
798 listconfig()
799 {
800     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
801 	perror("Can't get vinum config");
802 	return;
803     }
804     printf("%d drives:\n", vinum_conf.drives_used);
805     if (vinum_conf.drives_used > 0) {
806 	vinum_ld(0, NULL, NULL);
807 	printf("\n");
808     }
809     printf("%d volumes:\n", vinum_conf.volumes_used);
810     if (vinum_conf.volumes_used > 0) {
811 	vinum_lv(0, NULL, NULL);
812 	printf("\n");
813     }
814     printf("%d plexes:\n", vinum_conf.plexes_used);
815     if (vinum_conf.plexes_used > 0) {
816 	vinum_lp(0, NULL, NULL);
817 	printf("\n");
818     }
819     printf("%d subdisks:\n", vinum_conf.subdisks_used);
820     if (vinum_conf.subdisks_used > 0)
821 	vinum_ls(0, NULL, NULL);
822 }
823 
824 /* Convert a timeval to Tue Oct 13 13:54:14.0434324
825  * Return pointer to text */
826 char *
827 timetext(struct timeval *time)
828 {
829     static char text[30];
830     time_t t;						    /* to keep Bruce happy */
831 
832     t = time->tv_sec;
833     strcpy(text, ctime(&t));				    /* to the second */
834     sprintf(&text[19], ".%06ld", time->tv_usec);	    /* and the microseconds */
835     return &text[11];
836 }
837 
838 void
839 vinum_info(int argc, char *argv[], char *argv0[])
840 {
841     struct meminfo meminfo;
842     struct mc malloced;
843     int i;
844 #if VINUMDEBUG
845     struct rqinfo rq;
846 #endif
847 
848     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
849 	perror("Can't get vinum config");
850 	return;
851     }
852     printf("Flags: 0x%x\n", vinum_conf.flags);
853     if (ioctl(superdev, VINUM_MEMINFO, &meminfo) < 0) {
854 	perror("Can't get information");
855 	return;
856     }
857     printf("Total of %d blocks malloced, total memory: %d\nMaximum allocs: %8d, malloc table at 0x%08x\n",
858 	meminfo.mallocs,
859 	meminfo.total_malloced,
860 	meminfo.highwater,
861 	(int) meminfo.malloced);
862 
863     printf("%d requests active, maximum %d active\n",
864 	vinum_conf.active,
865 	vinum_conf.maxactive);
866     if (vflag && (!Verbose))
867 	for (i = 0; i < meminfo.mallocs; i++) {
868 	    malloced.seq = i;
869 	    if (ioctl(superdev, VINUM_MALLOCINFO, &malloced) < 0) {
870 		perror("Can't get information");
871 		return;
872 	    }
873 	    if (!(i & 63))
874 		printf("Block\tSequence\t  size\t  address\t  line\t\tfile\n\n");
875 	    printf("%6d\t%6d\t\t%6d\t0x%08x\t%6d\t\t%s\n",
876 		i,
877 		malloced.seq,
878 		malloced.size,
879 		(int) malloced.address,
880 		malloced.line,
881 		(char *) &malloced.file);
882 	}
883 #if VINUMDEBUG
884     if (Verbose) {
885 	printf("\nTime\t\t Event\t     Buf\tDev\t  Offset\tBytes\tSD\tSDoff\tDoffset\tGoffset\n\n");
886 	for (i = RQINFO_SIZE - 1; i >= 0; i--) {	    /* go through the request list in order */
887 	    *((int *) &rq) = i;
888 	    if (ioctl(superdev, VINUM_RQINFO, &rq) < 0) {
889 		perror("Can't get information");
890 		return;
891 	    }
892 	    /* Compress devminor into something printable. */
893 	    rq.devminor = (rq.devminor & 0xff)
894 		| ((rq.devminor & 0xfff0000) >> 8);
895 	    switch (rq.type) {
896 	    case loginfo_unused:			    /* never been used */
897 		break;
898 
899 	    case loginfo_user_bp:			    /* this is the bp when strategy is called */
900 		printf("%s %dVS %s %p\t%d.%-6d 0x%-9x\t%ld\n",
901 		    timetext(&rq.timestamp),
902 		    rq.type,
903 		    rq.info.b.b_flags & B_READ ? "Read " : "Write",
904 		    rq.bp,
905 		    rq.devmajor,
906 		    rq.devminor,
907 		    rq.info.b.b_blkno,
908 		    rq.info.b.b_bcount);
909 		break;
910 
911 	    case loginfo_sdiol:				    /* subdisk I/O launch */
912 	    case loginfo_user_bpl:			    /* and this is the bp at launch time */
913 		printf("%s %dLR %s %p\t%d.%-6d 0x%-9x\t%ld\n",
914 		    timetext(&rq.timestamp),
915 		    rq.type,
916 		    rq.info.b.b_flags & B_READ ? "Read " : "Write",
917 		    rq.bp,
918 		    rq.devmajor,
919 		    rq.devminor,
920 		    rq.info.b.b_blkno,
921 		    rq.info.b.b_bcount);
922 		break;
923 
924 	    case loginfo_rqe:				    /* user RQE */
925 		printf("%s 3RQ %s %p\t%d.%-6d 0x%-9x\t%ld\t%d\t%x\t%x\t%x\n",
926 		    timetext(&rq.timestamp),
927 		    rq.info.rqe.b.b_flags & B_READ ? "Read " : "Write",
928 		    rq.bp,
929 		    rq.devmajor,
930 		    rq.devminor,
931 		    rq.info.rqe.b.b_blkno,
932 		    rq.info.rqe.b.b_bcount,
933 		    rq.info.rqe.sdno,
934 		    rq.info.rqe.sdoffset,
935 		    rq.info.rqe.dataoffset,
936 		    rq.info.rqe.groupoffset);
937 		break;
938 
939 	    case loginfo_iodone:			    /* iodone called */
940 		printf("%s 4DN %s %p\t%d.%-6d 0x%-9x\t%ld\t%d\t%x\t%x\t%x\n",
941 		    timetext(&rq.timestamp),
942 		    rq.info.rqe.b.b_flags & B_READ ? "Read " : "Write",
943 		    rq.bp,
944 		    rq.devmajor,
945 		    rq.devminor,
946 		    rq.info.rqe.b.b_blkno,
947 		    rq.info.rqe.b.b_bcount,
948 		    rq.info.rqe.sdno,
949 		    rq.info.rqe.sdoffset,
950 		    rq.info.rqe.dataoffset,
951 		    rq.info.rqe.groupoffset);
952 		break;
953 
954 	    case loginfo_raid5_data:			    /* RAID-5 write data block */
955 		printf("%s 5RD %s %p\t%d.%-6d 0x%-9x\t%ld\t%d\t%x\t%x\t%x\n",
956 		    timetext(&rq.timestamp),
957 		    rq.info.rqe.b.b_flags & B_READ ? "Read " : "Write",
958 		    rq.bp,
959 		    rq.devmajor,
960 		    rq.devminor,
961 		    rq.info.rqe.b.b_blkno,
962 		    rq.info.rqe.b.b_bcount,
963 		    rq.info.rqe.sdno,
964 		    rq.info.rqe.sdoffset,
965 		    rq.info.rqe.dataoffset,
966 		    rq.info.rqe.groupoffset);
967 		break;
968 
969 	    case loginfo_raid5_parity:			    /* RAID-5 write parity block */
970 		printf("%s 6RP %s %p\t%d.%-6d 0x%-9x\t%ld\t%d\t%x\t%x\t%x\n",
971 		    timetext(&rq.timestamp),
972 		    rq.info.rqe.b.b_flags & B_READ ? "Read " : "Write",
973 		    rq.bp,
974 		    rq.devmajor,
975 		    rq.devminor,
976 		    rq.info.rqe.b.b_blkno,
977 		    rq.info.rqe.b.b_bcount,
978 		    rq.info.rqe.sdno,
979 		    rq.info.rqe.sdoffset,
980 		    rq.info.rqe.dataoffset,
981 		    rq.info.rqe.groupoffset);
982 		break;
983 
984 	    case loginfo_sdio:				    /* subdisk I/O */
985 		printf("%s %dVS %s %p\t\t  0x%-9x\t%ld\t%d\n",
986 		    timetext(&rq.timestamp),
987 		    rq.type,
988 		    rq.info.b.b_flags & B_READ ? "Read " : "Write",
989 		    rq.bp,
990 		    rq.info.b.b_blkno,
991 		    rq.info.b.b_bcount,
992 		    rq.devminor);
993 		break;
994 
995 	    case loginfo_sdiodone:			    /* subdisk I/O done */
996 		printf("%s %dSD %s %p\t\t  0x%-9x\t%ld\t%d\n",
997 		    timetext(&rq.timestamp),
998 		    rq.type,
999 		    rq.info.b.b_flags & B_READ ? "Read " : "Write",
1000 		    rq.bp,
1001 		    rq.info.b.b_blkno,
1002 		    rq.info.b.b_bcount,
1003 		    rq.devminor);
1004 		break;
1005 
1006 	    case loginfo_lockwait:
1007 		printf("%s Lockwait  %p\t  0x%x\n",
1008 		    timetext(&rq.timestamp),
1009 		    rq.bp,
1010 		    rq.info.lockinfo.stripe);
1011 		break;
1012 
1013 	    case loginfo_lock:
1014 		printf("%s Lock      %p\t  0x%x\n",
1015 		    timetext(&rq.timestamp),
1016 		    rq.bp,
1017 		    rq.info.lockinfo.stripe);
1018 		break;
1019 
1020 	    case loginfo_unlock:
1021 		printf("%s Unlock\t  %p\t  0x%x\n",
1022 		    timetext(&rq.timestamp),
1023 		    rq.bp,
1024 		    rq.info.lockinfo.stripe);
1025 		break;
1026 	    }
1027 	}
1028     }
1029 #endif
1030 }
1031 
1032 /*
1033  * Print config file to a file.  This is a userland version
1034  * of kernel format_config
1035  */
1036 void
1037 vinum_printconfig(int argc, char *argv[], char *argv0[])
1038 {
1039     FILE *of;
1040 
1041     if (argc > 1) {
1042 	fprintf(stderr, "Usage: \tprintconfig [<outfile>]\n");
1043 	return;
1044     } else if (argc == 1)
1045 	of = fopen(argv[0], "w");
1046     else
1047 	of = stdout;
1048     if (of == NULL) {
1049 	fprintf(stderr, "Can't open %s: %s\n", argv[0], strerror(errno));
1050 	return;
1051     }
1052     printconfig(of, "");
1053     if (argc == 1)
1054 	fclose(of);
1055 }
1056 
1057 /*
1058  * The guts of printconfig.  This is called from
1059  * vinum_printconfig and from vinum_create when
1060  * called without an argument, in order to give
1061  * the user something to edit.
1062  */
1063 void
1064 printconfig(FILE * of, char *comment)
1065 {
1066     struct utsname uname_s;
1067     time_t now;
1068     int i;
1069     struct volume vol;
1070     struct plex plex;
1071     struct sd sd;
1072     struct drive drive;
1073 
1074     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
1075 	perror("Can't get vinum config");
1076 	return;
1077     }
1078     uname(&uname_s);					    /* get our system name */
1079     time(&now);						    /* and the current time */
1080     fprintf(of,
1081 	"# Vinum configuration of %s, saved at %s",
1082 	uname_s.nodename,
1083 	ctime(&now));					    /* say who did it */
1084 
1085     if (comment[0] != 0)				    /* abuse this for commented version */
1086 	fprintf(of, "# Current configuration:\n");
1087     for (i = 0; i < vinum_conf.drives_allocated; i++) {
1088 	get_drive_info(&drive, i);
1089 	if (drive.state != drive_unallocated) {
1090 	    fprintf(of,
1091 		"%sdrive %s device %s\n",
1092 		comment,
1093 		drive.label.name,
1094 		drive.devicename);
1095 	}
1096     }
1097 
1098     for (i = 0; i < vinum_conf.volumes_allocated; i++) {
1099 	get_volume_info(&vol, i);
1100 	if (vol.state != volume_unallocated) {
1101 	    if (vol.preferred_plex >= 0)		    /* preferences, */
1102 		fprintf(of,
1103 		    "%svolume %s readpol prefer %s\n",
1104 		    comment,
1105 		    vol.name,
1106 		    vinum_conf.plex[vol.preferred_plex].name);
1107 	    else					    /* default round-robin */
1108 		fprintf(of, "%svolume %s\n", comment, vol.name);
1109 	}
1110     }
1111 
1112     /* Then the plex configuration */
1113     for (i = 0; i < vinum_conf.plexes_allocated; i++) {
1114 	get_plex_info(&plex, i);
1115 	if (plex.state != plex_unallocated) {
1116 	    fprintf(of, "%splex name %s org %s ",
1117 		comment,
1118 		plex.name,
1119 		plex_org(plex.organization));
1120 	    if (isstriped((&plex)))
1121 		fprintf(of, "%ds ", (int) plex.stripesize);
1122 	    if (plex.volno >= 0) {			    /* we have a volume */
1123 		get_volume_info(&vol, plex.volno);
1124 		fprintf(of, "vol %s ", vol.name);
1125 	    } else
1126 		fprintf(of, "detached ");
1127 	    fprintf(of, "\n");
1128 	}
1129     }
1130 
1131     /* And finally the subdisk configuration */
1132     for (i = 0; i < vinum_conf.subdisks_allocated; i++) {
1133 	get_sd_info(&sd, i);
1134 	if (sd.state != sd_unallocated) {
1135 	    get_drive_info(&drive, sd.driveno);
1136 	    if (sd.plexno >= 0) {
1137 		get_plex_info(&plex, sd.plexno);
1138 		fprintf(of,
1139 		    "%ssd name %s drive %s plex %s len %llds driveoffset %llds plexoffset %llds\n",
1140 		    comment,
1141 		    sd.name,
1142 		    drive.label.name,
1143 		    plex.name,
1144 		    (long long) sd.sectors,
1145 		    (long long) sd.driveoffset,
1146 		    (long long) sd.plexoffset);
1147 	    } else
1148 		fprintf(of,
1149 		    "%ssd name %s drive %s detached len %llds driveoffset %llds\n",
1150 		    comment,
1151 		    sd.name,
1152 		    drive.label.name,
1153 		    (long long) sd.sectors,
1154 		    (long long) sd.driveoffset);
1155 	}
1156     }
1157 }
1158 
1159 void
1160 list_defective_objects()
1161 {
1162     int o;						    /* object */
1163     int heading_needed = 1;
1164 
1165     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
1166 	perror("Can't get vinum config");
1167 	return;
1168     }
1169     for (o = 0; o < vinum_conf.drives_allocated; o++) {
1170 	get_drive_info(&drive, o);
1171 	if ((drive.state != drive_unallocated)		    /* drive exists */
1172 	&&(drive.state != drive_up)) {			    /* but it's not up */
1173 	    if (heading_needed) {
1174 		printf("Warning: defective objects\n\n");
1175 		heading_needed = 0;
1176 	    }
1177 	    vinum_ldi(o, 0);				    /* print info */
1178 	}
1179     }
1180 
1181     for (o = 0; o < vinum_conf.volumes_allocated; o++) {
1182 	get_volume_info(&vol, o);
1183 	if ((vol.state != volume_unallocated)		    /* volume exists */
1184 	&&(vol.state != volume_up)) {			    /* but it's not up */
1185 	    if (heading_needed) {
1186 		printf("Warning: defective objects\n\n");
1187 		heading_needed = 0;
1188 	    }
1189 	    vinum_lvi(o, 0);				    /* print info */
1190 	}
1191     }
1192 
1193     for (o = 0; o < vinum_conf.plexes_allocated; o++) {
1194 	get_plex_info(&plex, o);
1195 	if ((plex.state != plex_unallocated)		    /* plex exists */
1196 	&&(plex.state != plex_up)) {			    /* but it's not up */
1197 	    if (heading_needed) {
1198 		printf("Warning: defective objects\n\n");
1199 		heading_needed = 0;
1200 	    }
1201 	    vinum_lpi(o, 0);				    /* print info */
1202 	}
1203     }
1204 
1205     for (o = 0; o < vinum_conf.subdisks_allocated; o++) {
1206 	get_sd_info(&sd, o);
1207 	if ((sd.state != sd_unallocated)		    /* sd exists */
1208 	&&(sd.state != sd_up)) {			    /* but it's not up */
1209 	    if (heading_needed) {
1210 		printf("Warning: defective objects\n\n");
1211 		heading_needed = 0;
1212 	    }
1213 	    vinum_lsi(o, 0);				    /* print info */
1214 	}
1215     }
1216 }
1217 
1218 /* Dump config from specified disk drives */
1219 void
1220 vinum_dumpconfig(int argc, char *argv[], char *argv0[])
1221 {
1222     int i;
1223 
1224     if (argc == 0) {					    /* start everything */
1225 	int devs = getnumdevs();
1226 	struct statinfo statinfo;
1227 	char *namelist;
1228 	char *enamelist;				    /* end of name list */
1229 	int i;
1230 	char **token;					    /* list of tokens */
1231 	int tokens;					    /* and their number */
1232 
1233 	bzero(&statinfo, sizeof(struct statinfo));
1234 	statinfo.dinfo = malloc(devs * sizeof(struct statinfo));
1235 	namelist = malloc(devs * (DEVSTAT_NAME_LEN + 8));
1236 	token = malloc((devs + 1) * sizeof(char *));
1237 	if ((statinfo.dinfo == NULL) || (namelist == NULL) || (token == NULL)) {
1238 	    fprintf(stderr, "Can't allocate memory for drive list\n");
1239 	    return;
1240 	}
1241 	bzero(statinfo.dinfo, sizeof(struct devinfo));
1242 
1243 	tokens = 0;					    /* no tokens yet */
1244 	if (getdevs(&statinfo) < 0) {			    /* find out what devices we have */
1245 	    perror("Can't get device list");
1246 	    return;
1247 	}
1248 	namelist[0] = '\0';				    /* start with empty namelist */
1249 	enamelist = namelist;				    /* point to the end of the list */
1250 
1251 	for (i = 0; i < devs; i++) {
1252 	    struct devstat *stat = &statinfo.dinfo->devices[i];
1253 
1254 	    if (((stat->device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT) /* disk device */
1255 	    &&((stat->device_type & DEVSTAT_TYPE_PASS) == 0) /* and not passthrough */
1256 	    &&((stat->device_name[0] != '\0'))) {	    /* and it has a name */
1257 		sprintf(enamelist, "/dev/%s%d", stat->device_name, stat->unit_number);
1258 		token[tokens] = enamelist;		    /* point to it */
1259 		tokens++;				    /* one more token */
1260 		enamelist = &enamelist[strlen(enamelist) + 1]; /* and start beyond the end */
1261 	    }
1262 	}
1263 	free(statinfo.dinfo);				    /* don't need the list any more */
1264 	for (i = 0; i < tokens; i++)
1265 	    dumpconfig(token[i]);
1266 	free(namelist);
1267 	free(token);
1268     } else {						    /* list specified drives */
1269 	for (i = 0; i < argc; i++)
1270 	    dumpconfig(argv[i]);
1271     }
1272 }
1273 
1274 #define DEVLEN 5
1275 void
1276 dumpconfig(char *part)
1277 {
1278     char partname[MAXPATHLEN];
1279     char *partid;
1280     char partition;					    /* UNIX partition */
1281     int slice;
1282     int founddrive;					    /* flag when we find a vinum drive */
1283     struct disklabel label;				    /* label of this drive */
1284     int driveno;					    /* fd of drive */
1285     int found;
1286     u_int64_t drivelength;
1287 
1288     if (memcmp(part, "/dev/", DEVLEN) == 0)		    /* starts with /dev */
1289 	memcpy(partname, part, MAXPATHLEN);
1290     else {						    /* prepend */
1291 	strcpy(partname, "/dev/");
1292 	strncat(&partname[DEVLEN], part, MAXPATHLEN - DEVLEN);
1293     }
1294     partid = &partname[strlen(partname)];
1295     founddrive = 0;					    /* no vinum drive found yet on this spindle */
1296     /* first try the partition table */
1297     for (slice = 1; slice < 5; slice++) {
1298 	sprintf(partid, "s%dc", slice);			    /* c partition */
1299 	driveno = open(partname, O_RDONLY);
1300 	if (driveno < 0) {
1301 	    if (errno != ENOENT)
1302 		fprintf(stderr, "Can't open %s: %s (%d)\n", partname, strerror(errno), errno);
1303 	    continue;
1304 	}
1305 	if (ioctl(driveno, DIOCGDINFO, &label) < 0) {
1306 	    fprintf(stderr, "Can't get label from %s: %s (%d)\n", partname, strerror(errno), errno);
1307 	    continue;
1308 	}
1309 	for (partition = 'a'; partition < 'i'; partition++) {
1310 	    if ((partition != 'c')			    /* it's not the c partition */
1311 	    &&((label.d_partitions[partition - 'a'].p_fstype == FS_VINUM) /* and it's a Vinum partition */
1312 	    ||Verbose)) {				    /* or we're just plain curious */
1313 		sprintf(partid, "s%d%c", slice, partition);
1314 		found = check_drive(partname);		    /* try to open it */
1315 		founddrive |= found;			    /* and note if we were successful at all */
1316 		if (label.d_partitions[partition - 'a'].p_fstype == FS_VINUM) {	/* it's a Vinum partition */
1317 		    drivelength = ((u_int64_t) label.d_partitions[partition - 'a'].p_size) * DEV_BSIZE;
1318 		    printf("Drive %s: %s (%lld bytes)\n",
1319 			partname,
1320 			roughlength(drivelength, 1),
1321 			drivelength);
1322 		    if ((!found) && vflag)		    /* we're talkative */
1323 			printf("*** no configuration found ***\n");
1324 		}
1325 	    }
1326 	}
1327     }
1328     if (founddrive == 0) {				    /* didn't find anything, */
1329 	sprintf(partid, "c");				    /* c partition */
1330 	driveno = open(partname, O_RDONLY);
1331 	if (driveno < 0) {
1332 	    if (errno != ENOENT)
1333 		fprintf(stderr, "Can't open %s: %s (%d)\n", partname, strerror(errno), errno);
1334 	    return;
1335 	}
1336 	if (ioctl(driveno, DIOCGDINFO, &label) < 0) {
1337 	    fprintf(stderr, "Can't get label from %s: %s (%d)\n", partname, strerror(errno), errno);
1338 	    return;
1339 	}
1340 	for (partition = 'a'; partition < 'i'; partition++) { /* try the compatibility partition */
1341 	    if ((partition != 'c')			    /* it's not the c partition */
1342 	    &&((label.d_partitions[partition - 'a'].p_fstype == FS_VINUM) /* and it's a Vinum partition */
1343 	    ||Verbose)) {				    /* or we're just plain curious */
1344 		sprintf(partid, "%c", partition);
1345 		found = check_drive(partname);		    /* try to open it */
1346 		founddrive |= found;			    /* and note if we were successful at all */
1347 		if (label.d_partitions[partition - 'a'].p_fstype == FS_VINUM) {	/* it's a Vinum partition */
1348 		    drivelength = ((u_int64_t) label.d_partitions[partition - 'a'].p_size) * DEV_BSIZE;
1349 		    printf("Drive %s: %s (%lld bytes)\n",
1350 			partname,
1351 			roughlength(drivelength, 1),
1352 			drivelength);
1353 		    if ((!found) && vflag)		    /* we're talkative */
1354 			printf("*** no configuration found ***\n");
1355 		}
1356 	    }
1357 	}
1358     }
1359 }
1360 
1361 /*
1362  * Check a drive for a Vinum header.  If found,
1363  * print configuration information from the drive.
1364  *
1365  * Return 1 if Vinum config found.
1366  */
1367 int
1368 check_drive(char *devicename)
1369 {
1370     int fd;
1371     char vinumlabel[DEV_BSIZE];				    /* one sector for label */
1372     struct vinum_hdr *hdr = (struct vinum_hdr *) vinumlabel; /* with this structure */
1373     char *config_text;					    /* read the config info from disk into here */
1374     time_t t;
1375 
1376     fd = open(devicename, O_RDONLY);
1377     if (fd >= 0) {
1378 	if (lseek(fd, VINUM_LABEL_OFFSET, SEEK_SET) < 0) {
1379 	    fprintf(stderr,
1380 		"Can't seek label for %s: %s (%d)\n",
1381 		devicename,
1382 		strerror(errno),
1383 		errno);
1384 	    close(fd);
1385 	    return 0;
1386 	}
1387 	if (read(fd, vinumlabel, DEV_BSIZE) != DEV_BSIZE) {
1388 	    if (errno != EINVAL)
1389 		fprintf(stderr,
1390 		    "Can't read label from %s: %s (%d)\n",
1391 		    devicename,
1392 		    strerror(errno),
1393 		    errno);
1394 	    close(fd);
1395 	    return 0;
1396 	}
1397 	if ((hdr->magic == VINUM_MAGIC)
1398 	    || (vflag && (hdr->magic == VINUM_NOMAGIC))) {
1399 	    printf("Drive %s:\tDevice %s\n",
1400 		hdr->label.name,
1401 		devicename);
1402 	    if (hdr->magic == VINUM_NOMAGIC)
1403 		printf("*** Drive has been obliterated ***\n");
1404 	    t = hdr->label.date_of_birth.tv_sec;
1405 	    printf("\t\tCreated on %s at %s",
1406 		hdr->label.sysname,
1407 		ctime(&t));
1408 	    t = hdr->label.last_update.tv_sec;
1409 	    printf("\t\tConfig last updated %s",	    /* care: \n at end */
1410 		ctime(&t));
1411 	    printf("\t\tSize: %16lld bytes (%lld MB)\n",
1412 		(long long) hdr->label.drive_size,	    /* bytes used */
1413 		(long long) (hdr->label.drive_size / MEGABYTE));
1414 	    config_text = (char *) malloc(MAXCONFIG);
1415 	    if (config_text == NULL)
1416 		fprintf(stderr, "Can't allocate memory\n");
1417 	    else {
1418 		if (read(fd, config_text, MAXCONFIG) != MAXCONFIG)
1419 		    fprintf(stderr,
1420 			"Can't read config from %s: %s (%d)\n",
1421 			devicename,
1422 			strerror(errno),
1423 			errno);
1424 		else
1425 		    puts(config_text);
1426 		free(config_text);
1427 	    }
1428 	}
1429 	close(fd);
1430 	return 1;
1431     }
1432     return 0;
1433 }
1434 
1435 /* Local Variables: */
1436 /* fill-column: 50 */
1437 /* End: */
1438