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