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