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