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