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