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