1 /* $NetBSD: bioctl.c,v 1.20 2023/08/04 03:45:07 rin 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.20 2023/08/04 03:45:07 rin 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 *, struct bioc_vol *); 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, struct bioc_vol *bv) 236 { 237 const char *status, *rtypestr, *stripestr; 238 char size[64], percent[16], seconds[20]; 239 char rtype[16], stripe[16], tmp[48]; 240 241 rtypestr = stripestr = NULL; 242 243 memset(bv, 0, sizeof(*bv)); 244 bv->bv_cookie = bl.bl_cookie; 245 bv->bv_volid = bt->volid; 246 bv->bv_percent = -1; 247 bv->bv_seconds = 0; 248 249 if (ioctl(bt->fd, BIOCVOL, bv) == -1) 250 err(EXIT_FAILURE, "BIOCVOL"); 251 252 percent[0] = '\0'; 253 seconds[0] = '\0'; 254 if (bv->bv_percent != -1) 255 snprintf(percent, sizeof(percent), 256 " %3.2f%% done", bv->bv_percent / 10.0); 257 if (bv->bv_seconds) 258 snprintf(seconds, sizeof(seconds), 259 " %u seconds", bv->bv_seconds); 260 261 switch (bv->bv_status) { 262 case BIOC_SVONLINE: 263 status = BIOC_SVONLINE_S; 264 break; 265 case BIOC_SVOFFLINE: 266 status = BIOC_SVOFFLINE_S; 267 break; 268 case BIOC_SVDEGRADED: 269 status = BIOC_SVDEGRADED_S; 270 break; 271 case BIOC_SVBUILDING: 272 status = BIOC_SVBUILDING_S; 273 break; 274 case BIOC_SVREBUILD: 275 status = BIOC_SVREBUILD_S; 276 break; 277 case BIOC_SVMIGRATING: 278 status = BIOC_SVMIGRATING_S; 279 break; 280 case BIOC_SVSCRUB: 281 status = BIOC_SVSCRUB_S; 282 break; 283 case BIOC_SVCHECKING: 284 status = BIOC_SVCHECKING_S; 285 break; 286 case BIOC_SVINVALID: 287 default: 288 status = BIOC_SVINVALID_S; 289 break; 290 } 291 292 snprintf(bt->volname, sizeof(bt->volname), "%u", bv->bv_volid); 293 if (bv->bv_vendor[0]) 294 snprintf(tmp, sizeof(tmp), "%s %s", bv->bv_dev, bv->bv_vendor); 295 else 296 snprintf(tmp, sizeof(tmp), "%s", bv->bv_dev); 297 298 switch (bv->bv_level) { 299 case BIOC_SVOL_HOTSPARE: 300 rtypestr = "Hot spare"; 301 stripestr = "N/A"; 302 break; 303 case BIOC_SVOL_PASSTHRU: 304 rtypestr = "Pass through"; 305 stripestr = "N/A"; 306 break; 307 case BIOC_SVOL_RAID01: 308 rtypestr = "RAID 0+1"; 309 break; 310 case BIOC_SVOL_RAID10: 311 rtypestr = "RAID 1+0"; 312 break; 313 default: 314 snprintf(rtype, sizeof(rtype), "RAID %u", bv->bv_level); 315 if (bv->bv_level == 1 || bv->bv_stripe_size == 0) 316 stripestr = "N/A"; 317 break; 318 } 319 320 if (rtypestr) 321 strlcpy(rtype, rtypestr, sizeof(rtype)); 322 if (stripestr) 323 strlcpy(stripe, stripestr, sizeof(stripe)); 324 else 325 snprintf(stripe, sizeof(stripe), "%uK", bv->bv_stripe_size); 326 327 humanize_number(size, 5, (int64_t)bv->bv_size, "", HN_AUTOSCALE, 328 HN_B | HN_NOSPACE | HN_DECIMAL); 329 330 printf("%6s %-12s %4s %20s %8s %6s %s%s\n", 331 bt->volname, status, size, tmp, 332 rtype, stripe, percent, seconds); 333 334 bt->bv = bv; 335 336 return bv->bv_nodisk; 337 } 338 339 /* 340 * Shows info about physical disks. 341 */ 342 static void 343 bio_show_disks(struct biotmp *bt) 344 { 345 struct bioc_disk bd; 346 const char *status; 347 char size[64], serial[32], scsiname[34]; 348 349 memset(&bd, 0, sizeof(bd)); 350 bd.bd_cookie = bl.bl_cookie; 351 bd.bd_diskid = bt->diskid; 352 bd.bd_volid = bt->volid; 353 354 if (bt->show_disknovol) { 355 if (ioctl(bt->fd, BIOCDISK_NOVOL, &bd) == -1) 356 err(EXIT_FAILURE, "BIOCDISK_NOVOL"); 357 if (!bd.bd_disknovol) 358 return; 359 } else { 360 if (ioctl(bt->fd, BIOCDISK, &bd) == -1) 361 err(EXIT_FAILURE, "BIOCDISK"); 362 } 363 364 switch (bd.bd_status) { 365 case BIOC_SDONLINE: 366 status = BIOC_SDONLINE_S; 367 break; 368 case BIOC_SDOFFLINE: 369 status = BIOC_SDOFFLINE_S; 370 break; 371 case BIOC_SDFAILED: 372 status = BIOC_SDFAILED_S; 373 break; 374 case BIOC_SDREBUILD: 375 status = BIOC_SDREBUILD_S; 376 break; 377 case BIOC_SDHOTSPARE: 378 status = BIOC_SDHOTSPARE_S; 379 break; 380 case BIOC_SDUNUSED: 381 status = BIOC_SDUNUSED_S; 382 break; 383 case BIOC_SDSCRUB: 384 status = BIOC_SDSCRUB_S; 385 break; 386 case BIOC_SDPASSTHRU: 387 status = BIOC_SDPASSTHRU_S; 388 break; 389 case BIOC_SDINVALID: 390 default: 391 status = BIOC_SDINVALID_S; 392 break; 393 } 394 395 if (bt->format) 396 snprintf(bt->volname, sizeof(bt->volname), 397 "%u:%u", bt->bv->bv_volid, bd.bd_diskid); 398 399 humanize_number(size, 5, bd.bd_size, "", HN_AUTOSCALE, 400 HN_B | HN_NOSPACE | HN_DECIMAL); 401 402 if (bd.bd_procdev[0]) 403 snprintf(scsiname, sizeof(scsiname), "%u:%u.%u %s", 404 bd.bd_channel, bd.bd_target, bd.bd_lun, 405 bd.bd_procdev); 406 else 407 snprintf(scsiname, sizeof(scsiname), "%u:%u.%u noencl", 408 bd.bd_channel, bd.bd_target, bd.bd_lun); 409 410 if (bd.bd_serial[0]) 411 strlcpy(serial, bd.bd_serial, sizeof(serial)); 412 else 413 strlcpy(serial, "unknown serial", sizeof(serial)); 414 415 if (bt->format) 416 printf("%6s %-12s %4s %20s <%s>\n", 417 bt->volname, status, size, scsiname, 418 bd.bd_vendor); 419 else 420 printf("%5d [%-28s] %-12s %-6s %12s\n", 421 bt->diskid, bd.bd_vendor, status, size, scsiname); 422 423 } 424 425 /* 426 * Shows info about volumes/disks. 427 */ 428 static void 429 bio_show_common(int fd, int argc, char **argv) 430 { 431 struct biotmp *biot; 432 struct bioc_inq bi; 433 struct bioc_vol bv; 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, &bv); 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