/* $NetBSD: bioctl.c,v 1.18.2.1 2022/05/14 11:40:07 martin Exp $ */ /* $OpenBSD: bioctl.c,v 1.52 2007/03/20 15:26:06 jmc Exp $ */ /* * Copyright (c) 2007, 2008 Juan Romero Pardines * Copyright (c) 2004, 2005 Marco Peereboom * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include #ifndef lint __RCSID("$NetBSD: bioctl.c,v 1.18.2.1 2022/05/14 11:40:07 martin Exp $"); #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct command { const char *cmd_name; const char *arg_names; void (*cmd_func)(int, int, char **); }; struct biotmp { struct bioc_inq *bi; struct bioc_vol *bv; char volname[64]; int fd; int volid; int diskid; bool format; bool show_disknovol; }; struct locator { int channel; int target; int lun; }; __dead static void usage(void); static void bio_alarm(int, int, char **); static void bio_show_common(int, int, char **); static int bio_show_volumes(struct biotmp *); static void bio_show_disks(struct biotmp *); static void bio_setblink(int, int, char **); static void bio_blink(int, char *, int, int); static void bio_setstate_hotspare(int, int, char **); static void bio_setstate_passthru(int, int, char **); static void bio_setstate_common(int, char *, struct bioc_setstate *, struct locator *); static void bio_setstate_consistency(int, int, char **); static void bio_volops_create(int, int, char **); #ifdef notyet static void bio_volops_modify(int, int, char **); #endif static void bio_volops_remove(int, int, char **); static const char *str2locator(const char *, struct locator *); static struct bio_locate bl; static struct command commands[] = { { "show", "[disks] | [volumes]", bio_show_common }, { "alarm", "[enable] | [disable] | [silence] | [test]", bio_alarm }, { "blink", "start [channel:target[.lun]] | stop [channel:target[.lun]]", bio_setblink }, { "hotspare", "add channel:target.lun | remove channel:target.lun", bio_setstate_hotspare }, { "passthru", "add DISKID channel:target.lun | remove channel:target.lun", bio_setstate_passthru }, { "check", "start VOLID | stop VOLID", bio_setstate_consistency }, { "create", "volume VOLID DISKIDs [SIZE] STRIPE RAID_LEVEL channel:target.lun", bio_volops_create }, #ifdef notyet { "modify", "volume VOLID STRIPE RAID_LEVEL channel:target.lun", bio_volops_modify }, #endif { "remove", "volume VOLID channel:target.lun", bio_volops_remove }, { NULL, NULL, NULL } }; int main(int argc, char **argv) { char *dvname; const char *cmdname; int fd = 0, i; /* Must have at least: device command */ if (argc < 3) usage(); /* Skip program name, get and skip device name and command */ setprogname(*argv); dvname = argv[1]; cmdname = argv[2]; argv += 3; argc -= 3; /* Look up and call the command */ for (i = 0; commands[i].cmd_name != NULL; i++) if (strcmp(cmdname, commands[i].cmd_name) == 0) break; if (commands[i].cmd_name == NULL) errx(EXIT_FAILURE, "unknown command: %s", cmdname); /* Locate the device by issuing the BIOCLOCATE ioctl */ fd = open("/dev/bio", O_RDWR); if (fd == -1) err(EXIT_FAILURE, "Can't open /dev/bio"); bl.bl_name = dvname; if (ioctl(fd, BIOCLOCATE, &bl) == -1) errx(EXIT_FAILURE, "Can't locate %s device via /dev/bio", bl.bl_name); /* and execute the command */ (*commands[i].cmd_func)(fd, argc, argv); (void)close(fd); exit(EXIT_SUCCESS); } static void usage(void) { int i; (void)fprintf(stderr, "usage: %s device command [arg [...]]\n", getprogname()); (void)fprintf(stderr, "Available commands:\n"); for (i = 0; commands[i].cmd_name != NULL; i++) (void)fprintf(stderr, " %s %s\n", commands[i].cmd_name, commands[i].arg_names); exit(EXIT_FAILURE); /* NOTREACHED */ } static const char * str2locator(const char *string, struct locator *location) { const char *errstr; char parse[80], *targ, *lun; strlcpy(parse, string, sizeof parse); targ = strchr(parse, ':'); if (targ == NULL) return "target not specified"; *targ++ = '\0'; lun = strchr(targ, '.'); if (lun != NULL) { *lun++ = '\0'; location->lun = strtonum(lun, 0, 256, &errstr); if (errstr) return errstr; } else location->lun = 0; location->target = strtonum(targ, 0, 256, &errstr); if (errstr) return errstr; location->channel = strtonum(parse, 0, 256, &errstr); if (errstr) return errstr; return NULL; } /* * Shows info about available RAID volumes. */ static int bio_show_volumes(struct biotmp *bt) { struct bioc_vol bv; const char *status, *rtypestr, *stripestr; char size[64], percent[16], seconds[20]; char rtype[16], stripe[16], tmp[48]; rtypestr = stripestr = NULL; memset(&bv, 0, sizeof(bv)); bv.bv_cookie = bl.bl_cookie; bv.bv_volid = bt->volid; bv.bv_percent = -1; bv.bv_seconds = 0; if (ioctl(bt->fd, BIOCVOL, &bv) == -1) err(EXIT_FAILURE, "BIOCVOL"); percent[0] = '\0'; seconds[0] = '\0'; if (bv.bv_percent != -1) snprintf(percent, sizeof(percent), " %3.2f%% done", bv.bv_percent / 10.0); if (bv.bv_seconds) snprintf(seconds, sizeof(seconds), " %u seconds", bv.bv_seconds); switch (bv.bv_status) { case BIOC_SVONLINE: status = BIOC_SVONLINE_S; break; case BIOC_SVOFFLINE: status = BIOC_SVOFFLINE_S; break; case BIOC_SVDEGRADED: status = BIOC_SVDEGRADED_S; break; case BIOC_SVBUILDING: status = BIOC_SVBUILDING_S; break; case BIOC_SVREBUILD: status = BIOC_SVREBUILD_S; break; case BIOC_SVMIGRATING: status = BIOC_SVMIGRATING_S; break; case BIOC_SVSCRUB: status = BIOC_SVSCRUB_S; break; case BIOC_SVCHECKING: status = BIOC_SVCHECKING_S; break; case BIOC_SVINVALID: default: status = BIOC_SVINVALID_S; break; } snprintf(bt->volname, sizeof(bt->volname), "%u", bv.bv_volid); if (bv.bv_vendor[0]) snprintf(tmp, sizeof(tmp), "%s %s", bv.bv_dev, bv.bv_vendor); else snprintf(tmp, sizeof(tmp), "%s", bv.bv_dev); switch (bv.bv_level) { case BIOC_SVOL_HOTSPARE: rtypestr = "Hot spare"; stripestr = "N/A"; break; case BIOC_SVOL_PASSTHRU: rtypestr = "Pass through"; stripestr = "N/A"; break; case BIOC_SVOL_RAID01: rtypestr = "RAID 0+1"; break; case BIOC_SVOL_RAID10: rtypestr = "RAID 1+0"; break; default: snprintf(rtype, sizeof(rtype), "RAID %u", bv.bv_level); if (bv.bv_level == 1 || bv.bv_stripe_size == 0) stripestr = "N/A"; break; } if (rtypestr) strlcpy(rtype, rtypestr, sizeof(rtype)); if (stripestr) strlcpy(stripe, stripestr, sizeof(stripe)); else snprintf(stripe, sizeof(stripe), "%uK", bv.bv_stripe_size); humanize_number(size, 5, (int64_t)bv.bv_size, "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); printf("%6s %-12s %4s %20s %8s %6s %s%s\n", bt->volname, status, size, tmp, rtype, stripe, percent, seconds); bt->bv = &bv; return bv.bv_nodisk; } /* * Shows info about physical disks. */ static void bio_show_disks(struct biotmp *bt) { struct bioc_disk bd; const char *status; char size[64], serial[32], scsiname[34]; memset(&bd, 0, sizeof(bd)); bd.bd_cookie = bl.bl_cookie; bd.bd_diskid = bt->diskid; bd.bd_volid = bt->volid; if (bt->show_disknovol) { if (ioctl(bt->fd, BIOCDISK_NOVOL, &bd) == -1) err(EXIT_FAILURE, "BIOCDISK_NOVOL"); if (!bd.bd_disknovol) return; } else { if (ioctl(bt->fd, BIOCDISK, &bd) == -1) err(EXIT_FAILURE, "BIOCDISK"); } switch (bd.bd_status) { case BIOC_SDONLINE: status = BIOC_SDONLINE_S; break; case BIOC_SDOFFLINE: status = BIOC_SDOFFLINE_S; break; case BIOC_SDFAILED: status = BIOC_SDFAILED_S; break; case BIOC_SDREBUILD: status = BIOC_SDREBUILD_S; break; case BIOC_SDHOTSPARE: status = BIOC_SDHOTSPARE_S; break; case BIOC_SDUNUSED: status = BIOC_SDUNUSED_S; break; case BIOC_SDSCRUB: status = BIOC_SDSCRUB_S; break; case BIOC_SDPASSTHRU: status = BIOC_SDPASSTHRU_S; break; case BIOC_SDINVALID: default: status = BIOC_SDINVALID_S; break; } if (bt->format) snprintf(bt->volname, sizeof(bt->volname), "%u:%u", bt->bv->bv_volid, bd.bd_diskid); humanize_number(size, 5, bd.bd_size, "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); if (bd.bd_procdev[0]) snprintf(scsiname, sizeof(scsiname), "%u:%u.%u %s", bd.bd_channel, bd.bd_target, bd.bd_lun, bd.bd_procdev); else snprintf(scsiname, sizeof(scsiname), "%u:%u.%u noencl", bd.bd_channel, bd.bd_target, bd.bd_lun); if (bd.bd_serial[0]) strlcpy(serial, bd.bd_serial, sizeof(serial)); else strlcpy(serial, "unknown serial", sizeof(serial)); if (bt->format) printf("%6s %-12s %4s %20s <%s>\n", bt->volname, status, size, scsiname, bd.bd_vendor); else printf("%5d [%-28s] %-12s %-6s %12s\n", bt->diskid, bd.bd_vendor, status, size, scsiname); } /* * Shows info about volumes/disks. */ static void bio_show_common(int fd, int argc, char **argv) { struct biotmp *biot; struct bioc_inq bi; int i, d, ndisks; bool show_all, show_disks; bool show_vols, show_caps; show_all = show_disks = show_vols = show_caps = false; if (argc > 1) usage(); if (argv[0]) { if (strcmp(argv[0], "disks") == 0) show_disks = true; else if (strcmp(argv[0], "volumes") == 0) show_vols = true; else usage(); } else show_all = true; memset(&bi, 0, sizeof(bi)); bi.bi_cookie = bl.bl_cookie; if (ioctl(fd, BIOCINQ, &bi) == -1) err(EXIT_FAILURE, "BIOCINQ"); /* * If there are volumes there's no point to continue. */ if (show_all || show_vols) { if (!bi.bi_novol) { warnx("no volumes available"); return; } } biot = calloc(1, sizeof(*biot)); if (!biot) err(EXIT_FAILURE, "biotemp calloc"); biot->fd = fd; biot->bi = &bi; /* * Go to the disks section if that was specified. */ if (show_disks) goto disks; /* * Common code to show only info about volumes and disks * associated to them. */ printf("%6s %-12s %4s %20s %8s %6s\n", "Volume", "Status", "Size", "Device/Label", "Level", "Stripe"); printf("==============================================" "===============\n"); for (i = 0; i < bi.bi_novol; i++) { biot->format = true; biot->volid = i; ndisks = bio_show_volumes(biot); if (show_vols) continue; for (d = 0; d < ndisks; d++) { biot->diskid = d; bio_show_disks(biot); } } goto out; disks: /* * show info about all disks connected to the raid controller, * even if they aren't associated with a volume or raid set. */ if (show_disks) { printf("%5s %-30s %-12s %-6s %12s\n", "Disk", "Model/Serial", "Status", "Size", "Location"); printf("===============================================" "======================\n"); for (d = 0; d < bi.bi_nodisk; d++) { biot->show_disknovol = true; biot->diskid = d; bio_show_disks(biot); } } out: free(biot); } /* * To handle the alarm feature. */ static void bio_alarm(int fd, int argc, char **argv) { struct bioc_alarm ba; bool show = false; memset(&ba, 0, sizeof(ba)); ba.ba_cookie = bl.bl_cookie; if (argc > 1) usage(); if (argc == 0) { /* show alarm status */ ba.ba_opcode = BIOC_GASTATUS; show = true; } else if (strcmp(argv[0], "silence") == 0) { /* silence alarm */ ba.ba_opcode = BIOC_SASILENCE; } else if (strcmp(argv[0], "enable") == 0) { /* enable alarm */ ba.ba_opcode = BIOC_SAENABLE; } else if (strcmp(argv[0], "disable") == 0) { /* disable alarm */ ba.ba_opcode = BIOC_SADISABLE; } else if (strcmp(argv[0], "test") == 0) { /* test alarm */ ba.ba_opcode = BIOC_SATEST; } else usage(); if (ioctl(fd, BIOCALARM, &ba) == -1) err(EXIT_FAILURE, "BIOCALARM"); if (show) printf("alarm is currently %s\n", ba.ba_status ? "enabled" : "disabled"); } /* * To add/remove a hotspare disk. */ static void bio_setstate_hotspare(int fd, int argc, char **argv) { struct bioc_setstate bs; struct locator location; memset(&bs, 0, sizeof(bs)); if (argc != 2) usage(); if (strcmp(argv[0], "add") == 0) bs.bs_status = BIOC_SSHOTSPARE; else if (strcmp(argv[0], "remove") == 0) bs.bs_status = BIOC_SSDELHOTSPARE; else usage(); bio_setstate_common(fd, argv[1], &bs, &location); } /* * To add/remove a pass through disk. */ static void bio_setstate_passthru(int fd, int argc, char **argv) { struct bioc_setstate bs; struct locator location; char *endptr; bool rem = false; if (argc < 2 || argc > 3) usage(); memset(&bs, 0, sizeof(bs)); if (strcmp(argv[0], "add") == 0) { if (argv[1] == NULL || argv[2] == NULL) usage(); bs.bs_status = BIOC_SSPASSTHRU; } else if (strcmp(argv[0], "remove") == 0) { if (argv[1] == NULL) usage(); bs.bs_status = BIOC_SSDELPASSTHRU; rem = true; } else usage(); if (rem) bio_setstate_common(fd, argv[1], &bs, &location); else { bs.bs_other_id = (unsigned int)strtoul(argv[1], &endptr, 10); if (*endptr != '\0') errx(EXIT_FAILURE, "Invalid Volume ID value"); bio_setstate_common(fd, argv[2], &bs, &location); } } /* * To start/stop a consistency check in a RAID volume. */ static void bio_setstate_consistency(int fd, int argc, char **argv) { struct bioc_setstate bs; char *endptr; if (argc != 2) usage(); memset(&bs, 0, sizeof(bs)); if (strcmp(argv[0], "start") == 0) bs.bs_status = BIOC_SSCHECKSTART_VOL; else if (strcmp(argv[0], "stop") == 0) bs.bs_status = BIOC_SSCHECKSTOP_VOL; else usage(); bs.bs_volid = (unsigned int)strtoul(argv[1], &endptr, 10); if (*endptr != '\0') errx(EXIT_FAILURE, "Invalid Volume ID value"); bio_setstate_common(fd, NULL, &bs, NULL); } static void bio_setstate_common(int fd, char *arg, struct bioc_setstate *bs, struct locator *location) { const char *errstr; if (!arg || !location) goto send; errstr = str2locator(arg, location); if (errstr) errx(EXIT_FAILURE, "Target %s: %s", arg, errstr); bs->bs_channel = location->channel; bs->bs_target = location->target; bs->bs_lun = location->lun; send: bs->bs_cookie = bl.bl_cookie; if (ioctl(fd, BIOCSETSTATE, bs) == -1) err(EXIT_FAILURE, "BIOCSETSTATE"); } /* * To create a RAID volume. */ static void bio_volops_create(int fd, int argc, char **argv) { struct bioc_volops bc; struct bioc_inq bi; struct bioc_disk bd; struct locator location; uint64_t total_size = 0, disksize = 0; int64_t volsize = 0; const char *errstr; char *endptr, *stripe, levelstr[32]; char *scsiname, *raid_level, size[64]; int disk_first = 0, disk_end = 0; int i, nfreedisks = 0; int user_disks = 0; if (argc < 6 || argc > 7) usage(); if (strcmp(argv[0], "volume") != 0) usage(); /* * No size requested, use max size depending on RAID level. */ if (argc == 6) { stripe = argv[3]; raid_level = argv[4]; scsiname = argv[5]; } else { stripe = argv[4]; raid_level = argv[5]; scsiname = argv[6]; } memset(&bd, 0, sizeof(bd)); memset(&bc, 0, sizeof(bc)); memset(&bi, 0, sizeof(bi)); bc.bc_cookie = bd.bd_cookie = bi.bi_cookie = bl.bl_cookie; bc.bc_opcode = BIOC_VCREATE_VOLUME; bc.bc_volid = (unsigned int)strtoul(argv[1], &endptr, 10); if (*endptr != '\0') errx(EXIT_FAILURE, "Invalid Volume ID value"); if (argc == 7) if (dehumanize_number(argv[3], &volsize) == -1 || volsize < 0) errx(EXIT_FAILURE, "Invalid SIZE value"); bc.bc_stripe = (unsigned int)strtoul(stripe, &endptr, 10); if (*endptr != '\0') errx(EXIT_FAILURE, "Invalid STRIPE size value"); bc.bc_level = (unsigned int)strtoul(raid_level, &endptr, 10); if (*endptr != '\0') errx(EXIT_FAILURE, "Invalid RAID_LEVEL value"); errstr = str2locator(scsiname, &location); if (errstr) errx(EXIT_FAILURE, "Target %s: %s", scsiname, errstr); /* * Parse the device list that will be used for the volume, * by using a bit field for the disks. */ if ((isdigit((unsigned char)argv[2][0]) == 0) || argv[2][1] != '-' || (isdigit((unsigned char)argv[2][2]) == 0)) errx(EXIT_FAILURE, "Invalid DISKIDs value"); disk_first = atoi(&argv[2][0]); disk_end = atoi(&argv[2][2]); for (i = disk_first; i < disk_end + 1; i++) { bc.bc_devmask |= (1 << i); user_disks++; } /* * Find out how many disks are free and how much size we * have available for the new volume. */ if (ioctl(fd, BIOCINQ, &bi) == -1) err(EXIT_FAILURE, "BIOCINQ"); for (i = 0; i < bi.bi_nodisk; i++) { bd.bd_diskid = i; if (ioctl(fd, BIOCDISK_NOVOL, &bd) == -1) err(EXIT_FAILURE, "BIOCDISK_NOVOL"); if (bd.bd_status == BIOC_SDUNUSED) { if (i == 0) disksize = bd.bd_size; total_size += bd.bd_size; nfreedisks++; } } if (user_disks > nfreedisks) errx(EXIT_FAILURE, "specified disks number is higher than " "available free disks"); /* * Basic checks to be sure we don't do something stupid. */ if (nfreedisks == 0) errx(EXIT_FAILURE, "No free disks available"); switch (bc.bc_level) { case 0: /* RAID 0 requires at least one disk */ if (argc == 7) { if ((uint64_t)volsize > (disksize * user_disks)) errx(EXIT_FAILURE, "volume size specified " "is larger than available on free disks"); bc.bc_size = (uint64_t)volsize; } else bc.bc_size = disksize * user_disks; break; case 1: /* RAID 1 requires two disks and size is total / 2 */ if (nfreedisks < 2 || user_disks < 2) errx(EXIT_FAILURE, "2 disks are required at least for " "this RAID level"); /* RAID 1+0 requires three disks at least */ if (nfreedisks > 2 && user_disks > 2) bc.bc_level = BIOC_SVOL_RAID10; if (argc == 7) { if ((uint64_t)volsize > ((disksize * user_disks) / 2)) errx(EXIT_FAILURE, "volume size specified " "is larger than available on free disks"); bc.bc_size = (uint64_t)volsize; } else bc.bc_size = ((disksize * user_disks) / 2); break; case 3: /* RAID 3/5 requires three disks and size is total - 1 disk */ case 5: if (nfreedisks < 3 || user_disks < 3) errx(EXIT_FAILURE, "3 disks are required at least for " "this RAID level"); if (argc == 7) { if ((uint64_t)volsize > (disksize * (user_disks - 1))) errx(EXIT_FAILURE, "volume size specified " "is larger than available on free disks"); bc.bc_size = (uint64_t)volsize; } else bc.bc_size = (disksize * (user_disks - 1)); break; case 6: /* RAID 6 requires four disks and size is total - 2 disks */ if (nfreedisks < 4 || user_disks < 4) errx(EXIT_FAILURE, "4 disks are required at least for " "this RAID level"); if (argc == 7) { if ((uint64_t)volsize > ((disksize * user_disks) - (disksize * 2))) err(EXIT_FAILURE, "volume size specified " "is larger than available on free disks"); bc.bc_size = (uint64_t)volsize; } else bc.bc_size = (((disksize * user_disks) - (disksize * 2))); break; default: errx(EXIT_FAILURE, "Unsupported RAID level"); } bc.bc_channel = location.channel; bc.bc_target = location.target; bc.bc_lun = location.lun; if (ioctl(fd, BIOCVOLOPS, &bc) == -1) err(EXIT_FAILURE, "BIOCVOLOPS"); humanize_number(size, 5, bc.bc_size, "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); if (bc.bc_level == BIOC_SVOL_RAID10) snprintf(levelstr, sizeof(levelstr), "1+0"); else snprintf(levelstr, sizeof(levelstr), "%u", bc.bc_level); printf("Created volume %u size: %s stripe: %uK level: %s " "SCSI location: %u:%u.%u\n", bc.bc_volid, size, bc.bc_stripe, levelstr, bc.bc_channel, bc.bc_target, bc.bc_lun); } #ifdef notyet /* * To modify a RAID volume. */ static void bio_volops_modify(int fd, int argc, char **argv) { /* XTRAEME: TODO */ } #endif /* * To remove a RAID volume. */ static void bio_volops_remove(int fd, int argc, char **argv) { struct bioc_volops bc; struct locator location; const char *errstr; char *endptr; if (argc != 3 || strcmp(argv[0], "volume") != 0) usage(); memset(&bc, 0, sizeof(bc)); bc.bc_cookie = bl.bl_cookie; bc.bc_opcode = BIOC_VREMOVE_VOLUME; bc.bc_volid = (unsigned int)strtoul(argv[1], &endptr, 10); if (*endptr != '\0') errx(EXIT_FAILURE, "Invalid Volume ID value"); errstr = str2locator(argv[2], &location); if (errstr) errx(EXIT_FAILURE, "Target %s: %s", argv[2], errstr); bc.bc_channel = location.channel; bc.bc_target = location.target; bc.bc_lun = location.lun; if (ioctl(fd, BIOCVOLOPS, &bc) == -1) err(EXIT_FAILURE, "BIOCVOLOPS"); printf("Removed volume %u at SCSI location %u:%u.%u\n", bc.bc_volid, bc.bc_channel, bc.bc_target, bc.bc_lun); } /* * To blink/unblink a disk in enclosures. */ static void bio_setblink(int fd, int argc, char **argv) { struct locator location; struct bioc_inq bi; struct bioc_vol bv; struct bioc_disk bd; struct bioc_blink bb; const char *errstr; int v, d, rv, blink = 0; if (argc != 2) usage(); if (strcmp(argv[0], "start") == 0) blink = BIOC_SBBLINK; else if (strcmp(argv[0], "stop") == 0) blink = BIOC_SBUNBLINK; else usage(); errstr = str2locator(argv[1], &location); if (errstr) errx(EXIT_FAILURE, "Target %s: %s", argv[1], errstr); /* try setting blink on the device directly */ memset(&bb, 0, sizeof(bb)); bb.bb_cookie = bl.bl_cookie; bb.bb_status = blink; bb.bb_target = location.target; bb.bb_channel = location.channel; rv = ioctl(fd, BIOCBLINK, &bb); if (rv == 0) return; /* if the blink didnt work, try to find something that will */ memset(&bi, 0, sizeof(bi)); bi.bi_cookie = bl.bl_cookie; rv = ioctl(fd, BIOCINQ, &bi); if (rv == -1) err(EXIT_FAILURE, "BIOCINQ"); for (v = 0; v < bi.bi_novol; v++) { memset(&bv, 0, sizeof(bv)); bv.bv_cookie = bl.bl_cookie; bv.bv_volid = v; rv = ioctl(fd, BIOCVOL, &bv); if (rv == -1) err(EXIT_FAILURE, "BIOCVOL"); for (d = 0; d < bv.bv_nodisk; d++) { memset(&bd, 0, sizeof(bd)); bd.bd_cookie = bl.bl_cookie; bd.bd_volid = v; bd.bd_diskid = d; rv = ioctl(fd, BIOCDISK, &bd); if (rv == -1) err(EXIT_FAILURE, "BIOCDISK"); if (bd.bd_channel == location.channel && bd.bd_target == location.target && bd.bd_lun == location.lun) { if (bd.bd_procdev[0] != '\0') { bio_blink(fd, bd.bd_procdev, location.target, blink); } else warnx("Disk %s is not in an enclosure", argv[1]); return; } } } warnx("Disk %s does not exist", argv[1]); } static void bio_blink(int fd, char *enclosure, int target, int blinktype) { struct bio_locate bio; struct bioc_blink blink; bio.bl_name = enclosure; if (ioctl(fd, BIOCLOCATE, &bio) == -1) errx(EXIT_FAILURE, "Can't locate %s device via /dev/bio", enclosure); memset(&blink, 0, sizeof(blink)); blink.bb_cookie = bio.bl_cookie; blink.bb_status = blinktype; blink.bb_target = target; if (ioctl(fd, BIOCBLINK, &blink) == -1) err(EXIT_FAILURE, "BIOCBLINK"); }