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