1 /* $NetBSD: bioctl.c,v 1.18 2019/02/04 04:36:41 mrg Exp $ */ 2 /* $OpenBSD: bioctl.c,v 1.52 2007/03/20 15:26:06 jmc Exp $ */ 3 4 /* 5 * Copyright (c) 2007, 2008 Juan Romero Pardines 6 * Copyright (c) 2004, 2005 Marco Peereboom 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 22 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 */ 31 #include <sys/cdefs.h> 32 33 #ifndef lint 34 __RCSID("$NetBSD: bioctl.c,v 1.18 2019/02/04 04:36:41 mrg Exp $"); 35 #endif 36 37 #include <sys/types.h> 38 #include <sys/ioctl.h> 39 #include <sys/param.h> 40 #include <sys/queue.h> 41 #include <dev/biovar.h> 42 43 #include <errno.h> 44 #include <err.h> 45 #include <fcntl.h> 46 #include <util.h> 47 #include <stdbool.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <unistd.h> 52 #include <ctype.h> 53 #include <util.h> 54 55 struct command { 56 const char *cmd_name; 57 const char *arg_names; 58 void (*cmd_func)(int, int, char **); 59 }; 60 61 struct biotmp { 62 struct bioc_inq *bi; 63 struct bioc_vol *bv; 64 char volname[64]; 65 int fd; 66 int volid; 67 int diskid; 68 bool format; 69 bool show_disknovol; 70 }; 71 72 struct locator { 73 int channel; 74 int target; 75 int lun; 76 }; 77 78 __dead static void usage(void); 79 static void bio_alarm(int, int, char **); 80 static void bio_show_common(int, int, char **); 81 static int bio_show_volumes(struct biotmp *); 82 static void bio_show_disks(struct biotmp *); 83 static void bio_setblink(int, int, char **); 84 static void bio_blink(int, char *, int, int); 85 static void bio_setstate_hotspare(int, int, char **); 86 static void bio_setstate_passthru(int, int, char **); 87 static void bio_setstate_common(int, char *, struct bioc_setstate *, 88 struct locator *); 89 static void bio_setstate_consistency(int, int, char **); 90 static void bio_volops_create(int, int, char **); 91 #ifdef notyet 92 static void bio_volops_modify(int, int, char **); 93 #endif 94 static void bio_volops_remove(int, int, char **); 95 96 static const char *str2locator(const char *, struct locator *); 97 98 static struct bio_locate bl; 99 static struct command commands[] = { 100 { 101 "show", 102 "[disks] | [volumes]", 103 bio_show_common }, 104 { 105 "alarm", 106 "[enable] | [disable] | [silence] | [test]", 107 bio_alarm }, 108 { 109 "blink", 110 "start [channel:target[.lun]] | stop [channel:target[.lun]]", 111 bio_setblink }, 112 { 113 "hotspare", 114 "add channel:target.lun | remove channel:target.lun", 115 bio_setstate_hotspare }, 116 { 117 "passthru", 118 "add DISKID channel:target.lun | remove channel:target.lun", 119 bio_setstate_passthru }, 120 { 121 "check", 122 "start VOLID | stop VOLID", 123 bio_setstate_consistency }, 124 { 125 "create", 126 "volume VOLID DISKIDs [SIZE] STRIPE RAID_LEVEL channel:target.lun", 127 bio_volops_create }, 128 #ifdef notyet 129 { 130 "modify", 131 "volume VOLID STRIPE RAID_LEVEL channel:target.lun", 132 bio_volops_modify }, 133 #endif 134 { 135 "remove", 136 "volume VOLID channel:target.lun", 137 bio_volops_remove }, 138 139 { NULL, NULL, NULL } 140 }; 141 142 int 143 main(int argc, char **argv) 144 { 145 char *dvname; 146 const char *cmdname; 147 int fd = 0, i; 148 149 /* Must have at least: device command */ 150 if (argc < 3) 151 usage(); 152 153 /* Skip program name, get and skip device name and command */ 154 setprogname(*argv); 155 dvname = argv[1]; 156 cmdname = argv[2]; 157 argv += 3; 158 argc -= 3; 159 160 /* Look up and call the command */ 161 for (i = 0; commands[i].cmd_name != NULL; i++) 162 if (strcmp(cmdname, commands[i].cmd_name) == 0) 163 break; 164 if (commands[i].cmd_name == NULL) 165 errx(EXIT_FAILURE, "unknown command: %s", cmdname); 166 167 /* Locate the device by issuing the BIOCLOCATE ioctl */ 168 fd = open("/dev/bio", O_RDWR); 169 if (fd == -1) 170 err(EXIT_FAILURE, "Can't open /dev/bio"); 171 172 bl.bl_name = dvname; 173 if (ioctl(fd, BIOCLOCATE, &bl) == -1) 174 errx(EXIT_FAILURE, "Can't locate %s device via /dev/bio", 175 bl.bl_name); 176 177 /* and execute the command */ 178 (*commands[i].cmd_func)(fd, argc, argv); 179 180 (void)close(fd); 181 exit(EXIT_SUCCESS); 182 } 183 184 static void 185 usage(void) 186 { 187 int i; 188 189 (void)fprintf(stderr, "usage: %s device command [arg [...]]\n", 190 getprogname()); 191 192 (void)fprintf(stderr, "Available commands:\n"); 193 for (i = 0; commands[i].cmd_name != NULL; i++) 194 (void)fprintf(stderr, " %s %s\n", commands[i].cmd_name, 195 commands[i].arg_names); 196 197 exit(EXIT_FAILURE); 198 /* NOTREACHED */ 199 } 200 201 static const char * 202 str2locator(const char *string, struct locator *location) 203 { 204 const char *errstr; 205 char parse[80], *targ, *lun; 206 207 strlcpy(parse, string, sizeof parse); 208 targ = strchr(parse, ':'); 209 if (targ == NULL) 210 return "target not specified"; 211 212 *targ++ = '\0'; 213 lun = strchr(targ, '.'); 214 if (lun != NULL) { 215 *lun++ = '\0'; 216 location->lun = strtonum(lun, 0, 256, &errstr); 217 if (errstr) 218 return errstr; 219 } else 220 location->lun = 0; 221 222 location->target = strtonum(targ, 0, 256, &errstr); 223 if (errstr) 224 return errstr; 225 location->channel = strtonum(parse, 0, 256, &errstr); 226 if (errstr) 227 return errstr; 228 return NULL; 229 } 230 231 /* 232 * Shows info about available RAID volumes. 233 */ 234 static int 235 bio_show_volumes(struct biotmp *bt) 236 { 237 struct bioc_vol bv; 238 const char *status, *rtypestr, *stripestr; 239 char size[64], percent[16], seconds[20]; 240 char rtype[16], stripe[16], tmp[48]; 241 242 rtypestr = stripestr = NULL; 243 244 memset(&bv, 0, sizeof(bv)); 245 bv.bv_cookie = bl.bl_cookie; 246 bv.bv_volid = bt->volid; 247 bv.bv_percent = -1; 248 bv.bv_seconds = -1; 249 250 if (ioctl(bt->fd, BIOCVOL, &bv) == -1) 251 err(EXIT_FAILURE, "BIOCVOL"); 252 253 percent[0] = '\0'; 254 seconds[0] = '\0'; 255 if (bv.bv_percent != -1) 256 snprintf(percent, sizeof(percent), 257 " %3.2f%% done", bv.bv_percent / 10.0); 258 if (bv.bv_seconds) 259 snprintf(seconds, sizeof(seconds), 260 " %u seconds", bv.bv_seconds); 261 262 switch (bv.bv_status) { 263 case BIOC_SVONLINE: 264 status = BIOC_SVONLINE_S; 265 break; 266 case BIOC_SVOFFLINE: 267 status = BIOC_SVOFFLINE_S; 268 break; 269 case BIOC_SVDEGRADED: 270 status = BIOC_SVDEGRADED_S; 271 break; 272 case BIOC_SVBUILDING: 273 status = BIOC_SVBUILDING_S; 274 break; 275 case BIOC_SVREBUILD: 276 status = BIOC_SVREBUILD_S; 277 break; 278 case BIOC_SVMIGRATING: 279 status = BIOC_SVMIGRATING_S; 280 break; 281 case BIOC_SVSCRUB: 282 status = BIOC_SVSCRUB_S; 283 break; 284 case BIOC_SVCHECKING: 285 status = BIOC_SVCHECKING_S; 286 break; 287 case BIOC_SVINVALID: 288 default: 289 status = BIOC_SVINVALID_S; 290 break; 291 } 292 293 snprintf(bt->volname, sizeof(bt->volname), "%u", bv.bv_volid); 294 if (bv.bv_vendor[0]) 295 snprintf(tmp, sizeof(tmp), "%s %s", bv.bv_dev, bv.bv_vendor); 296 else 297 snprintf(tmp, sizeof(tmp), "%s", bv.bv_dev); 298 299 switch (bv.bv_level) { 300 case BIOC_SVOL_HOTSPARE: 301 rtypestr = "Hot spare"; 302 stripestr = "N/A"; 303 break; 304 case BIOC_SVOL_PASSTHRU: 305 rtypestr = "Pass through"; 306 stripestr = "N/A"; 307 break; 308 case BIOC_SVOL_RAID01: 309 rtypestr = "RAID 0+1"; 310 break; 311 case BIOC_SVOL_RAID10: 312 rtypestr = "RAID 1+0"; 313 break; 314 default: 315 snprintf(rtype, sizeof(rtype), "RAID %u", bv.bv_level); 316 if (bv.bv_level == 1 || bv.bv_stripe_size == 0) 317 stripestr = "N/A"; 318 break; 319 } 320 321 if (rtypestr) 322 strlcpy(rtype, rtypestr, sizeof(rtype)); 323 if (stripestr) 324 strlcpy(stripe, stripestr, sizeof(stripe)); 325 else 326 snprintf(stripe, sizeof(stripe), "%uK", bv.bv_stripe_size); 327 328 humanize_number(size, 5, (int64_t)bv.bv_size, "", HN_AUTOSCALE, 329 HN_B | HN_NOSPACE | HN_DECIMAL); 330 331 printf("%6s %-12s %4s %20s %8s %6s %s%s\n", 332 bt->volname, status, size, tmp, 333 rtype, stripe, percent, seconds); 334 335 bt->bv = &bv; 336 337 return bv.bv_nodisk; 338 } 339 340 /* 341 * Shows info about physical disks. 342 */ 343 static void 344 bio_show_disks(struct biotmp *bt) 345 { 346 struct bioc_disk bd; 347 const char *status; 348 char size[64], serial[32], scsiname[34]; 349 350 memset(&bd, 0, sizeof(bd)); 351 bd.bd_cookie = bl.bl_cookie; 352 bd.bd_diskid = bt->diskid; 353 bd.bd_volid = bt->volid; 354 355 if (bt->show_disknovol) { 356 if (ioctl(bt->fd, BIOCDISK_NOVOL, &bd) == -1) 357 err(EXIT_FAILURE, "BIOCDISK_NOVOL"); 358 if (!bd.bd_disknovol) 359 return; 360 } else { 361 if (ioctl(bt->fd, BIOCDISK, &bd) == -1) 362 err(EXIT_FAILURE, "BIOCDISK"); 363 } 364 365 switch (bd.bd_status) { 366 case BIOC_SDONLINE: 367 status = BIOC_SDONLINE_S; 368 break; 369 case BIOC_SDOFFLINE: 370 status = BIOC_SDOFFLINE_S; 371 break; 372 case BIOC_SDFAILED: 373 status = BIOC_SDFAILED_S; 374 break; 375 case BIOC_SDREBUILD: 376 status = BIOC_SDREBUILD_S; 377 break; 378 case BIOC_SDHOTSPARE: 379 status = BIOC_SDHOTSPARE_S; 380 break; 381 case BIOC_SDUNUSED: 382 status = BIOC_SDUNUSED_S; 383 break; 384 case BIOC_SDSCRUB: 385 status = BIOC_SDSCRUB_S; 386 break; 387 case BIOC_SDPASSTHRU: 388 status = BIOC_SDPASSTHRU_S; 389 break; 390 case BIOC_SDINVALID: 391 default: 392 status = BIOC_SDINVALID_S; 393 break; 394 } 395 396 if (bt->format) 397 snprintf(bt->volname, sizeof(bt->volname), 398 "%u:%u", bt->bv->bv_volid, bd.bd_diskid); 399 400 humanize_number(size, 5, bd.bd_size, "", HN_AUTOSCALE, 401 HN_B | HN_NOSPACE | HN_DECIMAL); 402 403 if (bd.bd_procdev[0]) 404 snprintf(scsiname, sizeof(scsiname), "%u:%u.%u %s", 405 bd.bd_channel, bd.bd_target, bd.bd_lun, 406 bd.bd_procdev); 407 else 408 snprintf(scsiname, sizeof(scsiname), "%u:%u.%u noencl", 409 bd.bd_channel, bd.bd_target, bd.bd_lun); 410 411 if (bd.bd_serial[0]) 412 strlcpy(serial, bd.bd_serial, sizeof(serial)); 413 else 414 strlcpy(serial, "unknown serial", sizeof(serial)); 415 416 if (bt->format) 417 printf("%6s %-12s %4s %20s <%s>\n", 418 bt->volname, status, size, scsiname, 419 bd.bd_vendor); 420 else 421 printf("%5d [%-28s] %-12s %-6s %12s\n", 422 bt->diskid, bd.bd_vendor, status, size, scsiname); 423 424 } 425 426 /* 427 * Shows info about volumes/disks. 428 */ 429 static void 430 bio_show_common(int fd, int argc, char **argv) 431 { 432 struct biotmp *biot; 433 struct bioc_inq bi; 434 int i, d, ndisks; 435 bool show_all, show_disks; 436 bool show_vols, show_caps; 437 438 show_all = show_disks = show_vols = show_caps = false; 439 440 if (argc > 1) 441 usage(); 442 443 if (argv[0]) { 444 if (strcmp(argv[0], "disks") == 0) 445 show_disks = true; 446 else if (strcmp(argv[0], "volumes") == 0) 447 show_vols = true; 448 else 449 usage(); 450 } else 451 show_all = true; 452 453 memset(&bi, 0, sizeof(bi)); 454 bi.bi_cookie = bl.bl_cookie; 455 456 if (ioctl(fd, BIOCINQ, &bi) == -1) 457 err(EXIT_FAILURE, "BIOCINQ"); 458 459 /* 460 * If there are volumes there's no point to continue. 461 */ 462 if (show_all || show_vols) { 463 if (!bi.bi_novol) { 464 warnx("no volumes available"); 465 return; 466 } 467 } 468 469 biot = calloc(1, sizeof(*biot)); 470 if (!biot) 471 err(EXIT_FAILURE, "biotemp calloc"); 472 473 biot->fd = fd; 474 biot->bi = &bi; 475 /* 476 * Go to the disks section if that was specified. 477 */ 478 if (show_disks) 479 goto disks; 480 481 /* 482 * Common code to show only info about volumes and disks 483 * associated to them. 484 */ 485 printf("%6s %-12s %4s %20s %8s %6s\n", 486 "Volume", "Status", "Size", "Device/Label", 487 "Level", "Stripe"); 488 printf("==============================================" 489 "===============\n"); 490 491 for (i = 0; i < bi.bi_novol; i++) { 492 biot->format = true; 493 biot->volid = i; 494 ndisks = bio_show_volumes(biot); 495 if (show_vols) 496 continue; 497 498 for (d = 0; d < ndisks; d++) { 499 biot->diskid = d; 500 bio_show_disks(biot); 501 } 502 503 } 504 goto out; 505 506 disks: 507 /* 508 * show info about all disks connected to the raid controller, 509 * even if they aren't associated with a volume or raid set. 510 */ 511 if (show_disks) { 512 printf("%5s %-30s %-12s %-6s %12s\n", 513 "Disk", "Model/Serial", "Status", "Size", "Location"); 514 printf("===============================================" 515 "======================\n"); 516 for (d = 0; d < bi.bi_nodisk; d++) { 517 biot->show_disknovol = true; 518 biot->diskid = d; 519 bio_show_disks(biot); 520 } 521 } 522 out: 523 free(biot); 524 } 525 526 /* 527 * To handle the alarm feature. 528 */ 529 static void 530 bio_alarm(int fd, int argc, char **argv) 531 { 532 struct bioc_alarm ba; 533 bool show = false; 534 535 memset(&ba, 0, sizeof(ba)); 536 ba.ba_cookie = bl.bl_cookie; 537 538 if (argc > 1) 539 usage(); 540 541 if (argc == 0) { 542 /* show alarm status */ 543 ba.ba_opcode = BIOC_GASTATUS; 544 show = true; 545 } else if (strcmp(argv[0], "silence") == 0) { 546 /* silence alarm */ 547 ba.ba_opcode = BIOC_SASILENCE; 548 } else if (strcmp(argv[0], "enable") == 0) { 549 /* enable alarm */ 550 ba.ba_opcode = BIOC_SAENABLE; 551 } else if (strcmp(argv[0], "disable") == 0) { 552 /* disable alarm */ 553 ba.ba_opcode = BIOC_SADISABLE; 554 } else if (strcmp(argv[0], "test") == 0) { 555 /* test alarm */ 556 ba.ba_opcode = BIOC_SATEST; 557 } else 558 usage(); 559 560 if (ioctl(fd, BIOCALARM, &ba) == -1) 561 err(EXIT_FAILURE, "BIOCALARM"); 562 563 if (show) 564 printf("alarm is currently %s\n", 565 ba.ba_status ? "enabled" : "disabled"); 566 } 567 568 /* 569 * To add/remove a hotspare disk. 570 */ 571 static void 572 bio_setstate_hotspare(int fd, int argc, char **argv) 573 { 574 struct bioc_setstate bs; 575 struct locator location; 576 577 memset(&bs, 0, sizeof(bs)); 578 579 if (argc != 2) 580 usage(); 581 582 if (strcmp(argv[0], "add") == 0) 583 bs.bs_status = BIOC_SSHOTSPARE; 584 else if (strcmp(argv[0], "remove") == 0) 585 bs.bs_status = BIOC_SSDELHOTSPARE; 586 else 587 usage(); 588 589 bio_setstate_common(fd, argv[1], &bs, &location); 590 } 591 592 /* 593 * To add/remove a pass through disk. 594 */ 595 static void 596 bio_setstate_passthru(int fd, int argc, char **argv) 597 { 598 struct bioc_setstate bs; 599 struct locator location; 600 char *endptr; 601 bool rem = false; 602 603 if (argc < 2 || argc > 3) 604 usage(); 605 606 memset(&bs, 0, sizeof(bs)); 607 608 if (strcmp(argv[0], "add") == 0) { 609 if (argv[1] == NULL || argv[2] == NULL) 610 usage(); 611 612 bs.bs_status = BIOC_SSPASSTHRU; 613 } else if (strcmp(argv[0], "remove") == 0) { 614 if (argv[1] == NULL) 615 usage(); 616 617 bs.bs_status = BIOC_SSDELPASSTHRU; 618 rem = true; 619 } else 620 usage(); 621 622 if (rem) 623 bio_setstate_common(fd, argv[1], &bs, &location); 624 else { 625 bs.bs_other_id = (unsigned int)strtoul(argv[1], &endptr, 10); 626 if (*endptr != '\0') 627 errx(EXIT_FAILURE, "Invalid Volume ID value"); 628 629 bio_setstate_common(fd, argv[2], &bs, &location); 630 } 631 } 632 633 /* 634 * To start/stop a consistency check in a RAID volume. 635 */ 636 static void 637 bio_setstate_consistency(int fd, int argc, char **argv) 638 { 639 struct bioc_setstate bs; 640 char *endptr; 641 642 if (argc != 2) 643 usage(); 644 645 memset(&bs, 0, sizeof(bs)); 646 647 if (strcmp(argv[0], "start") == 0) 648 bs.bs_status = BIOC_SSCHECKSTART_VOL; 649 else if (strcmp(argv[0], "stop") == 0) 650 bs.bs_status = BIOC_SSCHECKSTOP_VOL; 651 else 652 usage(); 653 654 bs.bs_volid = (unsigned int)strtoul(argv[1], &endptr, 10); 655 if (*endptr != '\0') 656 errx(EXIT_FAILURE, "Invalid Volume ID value"); 657 658 bio_setstate_common(fd, NULL, &bs, NULL); 659 } 660 661 static void 662 bio_setstate_common(int fd, char *arg, struct bioc_setstate *bs, 663 struct locator *location) 664 { 665 const char *errstr; 666 667 if (!arg || !location) 668 goto send; 669 670 errstr = str2locator(arg, location); 671 if (errstr) 672 errx(EXIT_FAILURE, "Target %s: %s", arg, errstr); 673 674 bs->bs_channel = location->channel; 675 bs->bs_target = location->target; 676 bs->bs_lun = location->lun; 677 678 send: 679 bs->bs_cookie = bl.bl_cookie; 680 681 if (ioctl(fd, BIOCSETSTATE, bs) == -1) 682 err(EXIT_FAILURE, "BIOCSETSTATE"); 683 } 684 685 /* 686 * To create a RAID volume. 687 */ 688 static void 689 bio_volops_create(int fd, int argc, char **argv) 690 { 691 struct bioc_volops bc; 692 struct bioc_inq bi; 693 struct bioc_disk bd; 694 struct locator location; 695 uint64_t total_size = 0, disksize = 0; 696 int64_t volsize = 0; 697 const char *errstr; 698 char *endptr, *stripe, levelstr[32]; 699 char *scsiname, *raid_level, size[64]; 700 int disk_first = 0, disk_end = 0; 701 int i, nfreedisks = 0; 702 int user_disks = 0; 703 704 if (argc < 6 || argc > 7) 705 usage(); 706 707 if (strcmp(argv[0], "volume") != 0) 708 usage(); 709 710 /* 711 * No size requested, use max size depending on RAID level. 712 */ 713 if (argc == 6) { 714 stripe = argv[3]; 715 raid_level = argv[4]; 716 scsiname = argv[5]; 717 } else { 718 stripe = argv[4]; 719 raid_level = argv[5]; 720 scsiname = argv[6]; 721 } 722 723 memset(&bd, 0, sizeof(bd)); 724 memset(&bc, 0, sizeof(bc)); 725 memset(&bi, 0, sizeof(bi)); 726 727 bc.bc_cookie = bd.bd_cookie = bi.bi_cookie = bl.bl_cookie; 728 bc.bc_opcode = BIOC_VCREATE_VOLUME; 729 730 bc.bc_volid = (unsigned int)strtoul(argv[1], &endptr, 10); 731 if (*endptr != '\0') 732 errx(EXIT_FAILURE, "Invalid Volume ID value"); 733 734 if (argc == 7) 735 if (dehumanize_number(argv[3], &volsize) == -1 736 || volsize < 0) 737 errx(EXIT_FAILURE, "Invalid SIZE value"); 738 739 bc.bc_stripe = (unsigned int)strtoul(stripe, &endptr, 10); 740 if (*endptr != '\0') 741 errx(EXIT_FAILURE, "Invalid STRIPE size value"); 742 743 bc.bc_level = (unsigned int)strtoul(raid_level, &endptr, 10); 744 if (*endptr != '\0') 745 errx(EXIT_FAILURE, "Invalid RAID_LEVEL value"); 746 747 errstr = str2locator(scsiname, &location); 748 if (errstr) 749 errx(EXIT_FAILURE, "Target %s: %s", scsiname, errstr); 750 751 /* 752 * Parse the device list that will be used for the volume, 753 * by using a bit field for the disks. 754 */ 755 if ((isdigit((unsigned char)argv[2][0]) == 0) || argv[2][1] != '-' || 756 (isdigit((unsigned char)argv[2][2]) == 0)) 757 errx(EXIT_FAILURE, "Invalid DISKIDs value"); 758 759 disk_first = atoi(&argv[2][0]); 760 disk_end = atoi(&argv[2][2]); 761 762 for (i = disk_first; i < disk_end + 1; i++) { 763 bc.bc_devmask |= (1 << i); 764 user_disks++; 765 } 766 767 /* 768 * Find out how many disks are free and how much size we 769 * have available for the new volume. 770 */ 771 if (ioctl(fd, BIOCINQ, &bi) == -1) 772 err(EXIT_FAILURE, "BIOCINQ"); 773 774 for (i = 0; i < bi.bi_nodisk; i++) { 775 bd.bd_diskid = i; 776 if (ioctl(fd, BIOCDISK_NOVOL, &bd) == -1) 777 err(EXIT_FAILURE, "BIOCDISK_NOVOL"); 778 779 if (bd.bd_status == BIOC_SDUNUSED) { 780 if (i == 0) 781 disksize = bd.bd_size; 782 783 total_size += bd.bd_size; 784 nfreedisks++; 785 } 786 } 787 788 if (user_disks > nfreedisks) 789 errx(EXIT_FAILURE, "specified disks number is higher than " 790 "available free disks"); 791 792 /* 793 * Basic checks to be sure we don't do something stupid. 794 */ 795 if (nfreedisks == 0) 796 errx(EXIT_FAILURE, "No free disks available"); 797 798 switch (bc.bc_level) { 799 case 0: /* RAID 0 requires at least one disk */ 800 if (argc == 7) { 801 if ((uint64_t)volsize > (disksize * user_disks)) 802 errx(EXIT_FAILURE, "volume size specified " 803 "is larger than available on free disks"); 804 bc.bc_size = (uint64_t)volsize; 805 } else 806 bc.bc_size = disksize * user_disks; 807 808 break; 809 case 1: /* RAID 1 requires two disks and size is total / 2 */ 810 if (nfreedisks < 2 || user_disks < 2) 811 errx(EXIT_FAILURE, "2 disks are required at least for " 812 "this RAID level"); 813 814 /* RAID 1+0 requires three disks at least */ 815 if (nfreedisks > 2 && user_disks > 2) 816 bc.bc_level = BIOC_SVOL_RAID10; 817 818 if (argc == 7) { 819 if ((uint64_t)volsize > ((disksize * user_disks) / 2)) 820 errx(EXIT_FAILURE, "volume size specified " 821 "is larger than available on free disks"); 822 bc.bc_size = (uint64_t)volsize; 823 } else 824 bc.bc_size = ((disksize * user_disks) / 2); 825 826 break; 827 case 3: /* RAID 3/5 requires three disks and size is total - 1 disk */ 828 case 5: 829 if (nfreedisks < 3 || user_disks < 3) 830 errx(EXIT_FAILURE, "3 disks are required at least for " 831 "this RAID level"); 832 833 if (argc == 7) { 834 if ((uint64_t)volsize > (disksize * (user_disks - 1))) 835 errx(EXIT_FAILURE, "volume size specified " 836 "is larger than available on free disks"); 837 bc.bc_size = (uint64_t)volsize; 838 } else 839 bc.bc_size = (disksize * (user_disks - 1)); 840 841 break; 842 case 6: /* RAID 6 requires four disks and size is total - 2 disks */ 843 if (nfreedisks < 4 || user_disks < 4) 844 errx(EXIT_FAILURE, "4 disks are required at least for " 845 "this RAID level"); 846 847 if (argc == 7) { 848 if ((uint64_t)volsize > 849 ((disksize * user_disks) - (disksize * 2))) 850 err(EXIT_FAILURE, "volume size specified " 851 "is larger than available on free disks"); 852 bc.bc_size = (uint64_t)volsize; 853 } else 854 bc.bc_size = 855 (((disksize * user_disks) - (disksize * 2))); 856 857 break; 858 default: 859 errx(EXIT_FAILURE, "Unsupported RAID level"); 860 } 861 862 bc.bc_channel = location.channel; 863 bc.bc_target = location.target; 864 bc.bc_lun = location.lun; 865 866 if (ioctl(fd, BIOCVOLOPS, &bc) == -1) 867 err(EXIT_FAILURE, "BIOCVOLOPS"); 868 869 humanize_number(size, 5, bc.bc_size, "", HN_AUTOSCALE, 870 HN_B | HN_NOSPACE | HN_DECIMAL); 871 872 if (bc.bc_level == BIOC_SVOL_RAID10) 873 snprintf(levelstr, sizeof(levelstr), "1+0"); 874 else 875 snprintf(levelstr, sizeof(levelstr), "%u", bc.bc_level); 876 877 printf("Created volume %u size: %s stripe: %uK level: %s " 878 "SCSI location: %u:%u.%u\n", bc.bc_volid, size, bc.bc_stripe, 879 levelstr, bc.bc_channel, bc.bc_target, bc.bc_lun); 880 } 881 882 #ifdef notyet 883 /* 884 * To modify a RAID volume. 885 */ 886 static void 887 bio_volops_modify(int fd, int argc, char **argv) 888 { 889 /* XTRAEME: TODO */ 890 } 891 #endif 892 893 /* 894 * To remove a RAID volume. 895 */ 896 static void 897 bio_volops_remove(int fd, int argc, char **argv) 898 { 899 struct bioc_volops bc; 900 struct locator location; 901 const char *errstr; 902 char *endptr; 903 904 if (argc != 3 || strcmp(argv[0], "volume") != 0) 905 usage(); 906 907 memset(&bc, 0, sizeof(bc)); 908 bc.bc_cookie = bl.bl_cookie; 909 bc.bc_opcode = BIOC_VREMOVE_VOLUME; 910 911 bc.bc_volid = (unsigned int)strtoul(argv[1], &endptr, 10); 912 if (*endptr != '\0') 913 errx(EXIT_FAILURE, "Invalid Volume ID value"); 914 915 errstr = str2locator(argv[2], &location); 916 if (errstr) 917 errx(EXIT_FAILURE, "Target %s: %s", argv[2], errstr); 918 919 bc.bc_channel = location.channel; 920 bc.bc_target = location.target; 921 bc.bc_lun = location.lun; 922 923 if (ioctl(fd, BIOCVOLOPS, &bc) == -1) 924 err(EXIT_FAILURE, "BIOCVOLOPS"); 925 926 printf("Removed volume %u at SCSI location %u:%u.%u\n", 927 bc.bc_volid, bc.bc_channel, bc.bc_target, bc.bc_lun); 928 } 929 930 /* 931 * To blink/unblink a disk in enclosures. 932 */ 933 static void 934 bio_setblink(int fd, int argc, char **argv) 935 { 936 struct locator location; 937 struct bioc_inq bi; 938 struct bioc_vol bv; 939 struct bioc_disk bd; 940 struct bioc_blink bb; 941 const char *errstr; 942 int v, d, rv, blink = 0; 943 944 if (argc != 2) 945 usage(); 946 947 if (strcmp(argv[0], "start") == 0) 948 blink = BIOC_SBBLINK; 949 else if (strcmp(argv[0], "stop") == 0) 950 blink = BIOC_SBUNBLINK; 951 else 952 usage(); 953 954 errstr = str2locator(argv[1], &location); 955 if (errstr) 956 errx(EXIT_FAILURE, "Target %s: %s", argv[1], errstr); 957 958 /* try setting blink on the device directly */ 959 memset(&bb, 0, sizeof(bb)); 960 bb.bb_cookie = bl.bl_cookie; 961 bb.bb_status = blink; 962 bb.bb_target = location.target; 963 bb.bb_channel = location.channel; 964 rv = ioctl(fd, BIOCBLINK, &bb); 965 if (rv == 0) 966 return; 967 968 /* if the blink didnt work, try to find something that will */ 969 memset(&bi, 0, sizeof(bi)); 970 bi.bi_cookie = bl.bl_cookie; 971 rv = ioctl(fd, BIOCINQ, &bi); 972 if (rv == -1) 973 err(EXIT_FAILURE, "BIOCINQ"); 974 975 for (v = 0; v < bi.bi_novol; v++) { 976 memset(&bv, 0, sizeof(bv)); 977 bv.bv_cookie = bl.bl_cookie; 978 bv.bv_volid = v; 979 rv = ioctl(fd, BIOCVOL, &bv); 980 if (rv == -1) 981 err(EXIT_FAILURE, "BIOCVOL"); 982 983 for (d = 0; d < bv.bv_nodisk; d++) { 984 memset(&bd, 0, sizeof(bd)); 985 bd.bd_cookie = bl.bl_cookie; 986 bd.bd_volid = v; 987 bd.bd_diskid = d; 988 989 rv = ioctl(fd, BIOCDISK, &bd); 990 if (rv == -1) 991 err(EXIT_FAILURE, "BIOCDISK"); 992 993 if (bd.bd_channel == location.channel && 994 bd.bd_target == location.target && 995 bd.bd_lun == location.lun) { 996 if (bd.bd_procdev[0] != '\0') { 997 bio_blink(fd, bd.bd_procdev, 998 location.target, blink); 999 } else 1000 warnx("Disk %s is not in an enclosure", 1001 argv[1]); 1002 return; 1003 } 1004 } 1005 } 1006 1007 warnx("Disk %s does not exist", argv[1]); 1008 } 1009 1010 static void 1011 bio_blink(int fd, char *enclosure, int target, int blinktype) 1012 { 1013 struct bio_locate bio; 1014 struct bioc_blink blink; 1015 1016 bio.bl_name = enclosure; 1017 if (ioctl(fd, BIOCLOCATE, &bio) == -1) 1018 errx(EXIT_FAILURE, 1019 "Can't locate %s device via /dev/bio", enclosure); 1020 1021 memset(&blink, 0, sizeof(blink)); 1022 blink.bb_cookie = bio.bl_cookie; 1023 blink.bb_status = blinktype; 1024 blink.bb_target = target; 1025 1026 if (ioctl(fd, BIOCBLINK, &blink) == -1) 1027 err(EXIT_FAILURE, "BIOCBLINK"); 1028 } 1029