1*c55617f1Sdlg /* $OpenBSD: bioctl.c,v 1.32 2005/08/18 12:19:08 dlg Exp $ */ 2cf6503d7Sderaadt 33af9de98Smarco /* 42f39728eSdlg * Copyright (c) 2004, 2005 Marco Peereboom 53af9de98Smarco * All rights reserved. 63af9de98Smarco * 73af9de98Smarco * Redistribution and use in source and binary forms, with or without 83af9de98Smarco * modification, are permitted provided that the following conditions 93af9de98Smarco * are met: 103af9de98Smarco * 1. Redistributions of source code must retain the above copyright 113af9de98Smarco * notice, this list of conditions and the following disclaimer. 123af9de98Smarco * 2. Redistributions in binary form must reproduce the above copyright 133af9de98Smarco * notice, this list of conditions and the following disclaimer in the 143af9de98Smarco * documentation and/or other materials provided with the distribution. 153af9de98Smarco * 163af9de98Smarco * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 173af9de98Smarco * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 183af9de98Smarco * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 193af9de98Smarco * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 203af9de98Smarco * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 213af9de98Smarco * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 223af9de98Smarco * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 233af9de98Smarco * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 243af9de98Smarco * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 253af9de98Smarco * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 263af9de98Smarco * SUCH DAMAGE. 273af9de98Smarco * 283af9de98Smarco */ 293af9de98Smarco 30c2126c9aSmarco #include <sys/ioctl.h> 31c2126c9aSmarco #include <sys/param.h> 32c2126c9aSmarco #include <sys/queue.h> 33c2126c9aSmarco #include <scsi/scsi_disk.h> 342b69df39Smarco #include <scsi/scsi_all.h> 353af9de98Smarco #include <dev/biovar.h> 363af9de98Smarco 37db2730c1Smarco #include <errno.h> 38db2730c1Smarco #include <err.h> 39db2730c1Smarco #include <fcntl.h> 408ccdd032Sderaadt #include <util.h> 41db2730c1Smarco #include <stdio.h> 42db2730c1Smarco #include <stdlib.h> 43db2730c1Smarco #include <string.h> 44db2730c1Smarco #include <unistd.h> 45e4e14ad7Sderaadt #include <ctype.h> 46db2730c1Smarco #include <util.h> 47db2730c1Smarco 486de960dcSmarco #define CT_SEP ':' 496de960dcSmarco #define TL_SEP '.' 506de960dcSmarco 516de960dcSmarco struct locator { 526de960dcSmarco int channel; 536de960dcSmarco int target; 546de960dcSmarco int lun; 556de960dcSmarco }; 566de960dcSmarco 578ccdd032Sderaadt void usage(void); 586de960dcSmarco int str2locator(const char *, struct locator *); 598ccdd032Sderaadt void cleanup(void); 608ccdd032Sderaadt 618ccdd032Sderaadt void bio_inq(char *); 628ccdd032Sderaadt void bio_alarm(char *); 636de960dcSmarco void bio_setstate(char *); 64*c55617f1Sdlg void bio_blink(char *); 653af9de98Smarco 66c2126c9aSmarco /* globals */ 67c2126c9aSmarco const char *bio_device = "/dev/bio"; 683af9de98Smarco 693af9de98Smarco int devh = -1; 70abe9d68eSderaadt int debug; 71abe9d68eSderaadt int human; 72abe9d68eSderaadt int verbose; 733af9de98Smarco 743af9de98Smarco struct bio_locate bl; 753af9de98Smarco 763af9de98Smarco int 773af9de98Smarco main(int argc, char *argv[]) 783af9de98Smarco { 793af9de98Smarco extern char *optarg; 80db2730c1Smarco u_int64_t func = 0; 81db2730c1Smarco /* u_int64_t subfunc = 0; */ 82cf6503d7Sderaadt char *bioc_dev = NULL, *sd_dev = NULL; 83cf6503d7Sderaadt char *realname = NULL, *al_arg = NULL; 84*c55617f1Sdlg char *bl_arg = NULL; 85cf6503d7Sderaadt int ch, rv; 863af9de98Smarco 873af9de98Smarco if (argc < 2) 883af9de98Smarco usage(); 893af9de98Smarco 90*c55617f1Sdlg while ((ch = getopt(argc, argv, "b:H:ha:Div")) != -1) { 913af9de98Smarco switch (ch) { 92edfd9792Smarco case 'a': /* alarm */ 93edfd9792Smarco func |= BIOC_ALARM; 94edfd9792Smarco al_arg = optarg; 95edfd9792Smarco break; 96*c55617f1Sdlg case 'b': /* blink */ 97*c55617f1Sdlg func |= BIOC_BLINK; 98*c55617f1Sdlg bl_arg = optarg; 99*c55617f1Sdlg break; 100db2730c1Smarco case 'D': /* debug */ 1013af9de98Smarco debug = 1; 1023af9de98Smarco break; 1036de960dcSmarco case 'H': /* set hotspare */ 1046de960dcSmarco func |= BIOC_SETSTATE; 1056de960dcSmarco al_arg = optarg; 1066de960dcSmarco break; 1078ccdd032Sderaadt case 'h': 1088ccdd032Sderaadt human = 1; 1098ccdd032Sderaadt break; 110db2730c1Smarco case 'i': /* inquiry */ 111db2730c1Smarco func |= BIOC_INQ; 1123af9de98Smarco break; 113abe9d68eSderaadt case 'v': 114abe9d68eSderaadt verbose = 1; 115abe9d68eSderaadt break; 1163af9de98Smarco default: 1173af9de98Smarco usage(); 1183af9de98Smarco /* NOTREACHED */ 1193af9de98Smarco } 1203af9de98Smarco } 121cf6503d7Sderaadt argc -= optind; 122cf6503d7Sderaadt argv += optind; 1233af9de98Smarco 1248ccdd032Sderaadt if (argc != 1) 125cf6503d7Sderaadt usage(); 126cf6503d7Sderaadt 127dcbaf4c8Sderaadt if (func == 0) 128dcbaf4c8Sderaadt func |= BIOC_INQ; 129dcbaf4c8Sderaadt 130e4e14ad7Sderaadt /* if at least glob sd[0-9]*, it is a drive identifier */ 131e4e14ad7Sderaadt if (strncmp(argv[0], "sd", 2) == 0 && strlen(argv[0]) > 2 && 132e4e14ad7Sderaadt isdigit(argv[0][2])) 133cf6503d7Sderaadt sd_dev = argv[0]; 134cf6503d7Sderaadt else 135cf6503d7Sderaadt bioc_dev = argv[0]; 13610b411a7Smarco 13710b411a7Smarco if (bioc_dev) { 1383af9de98Smarco devh = open(bio_device, O_RDWR); 1393af9de98Smarco if (devh == -1) 1403af9de98Smarco err(1, "Can't open %s", bio_device); 1413af9de98Smarco 1428ccdd032Sderaadt bl.bl_name = bioc_dev; 1433af9de98Smarco rv = ioctl(devh, BIOCLOCATE, &bl); 1443af9de98Smarco if (rv == -1) 14510b411a7Smarco errx(1, "Can't locate %s device via %s", 1468ccdd032Sderaadt bl.bl_name, bio_device); 147db2730c1Smarco } else if (sd_dev) { 14810b411a7Smarco devh = opendev(sd_dev, O_RDWR, OPENDEV_PART, &realname); 14910b411a7Smarco if (devh == -1) 15010b411a7Smarco err(1, "Can't open %s", sd_dev); 151db2730c1Smarco } else 15210b411a7Smarco errx(1, "need -d or -f parameter"); 1533af9de98Smarco 1543af9de98Smarco if (debug) 1558ccdd032Sderaadt warnx("cookie = %p", bl.bl_cookie); 1563af9de98Smarco 157db2730c1Smarco if (func & BIOC_INQ) { 1588ccdd032Sderaadt bio_inq(sd_dev); 159edfd9792Smarco } else if (func == BIOC_ALARM) { 160edfd9792Smarco bio_alarm(al_arg); 161*c55617f1Sdlg } else if (func == BIOC_BLINK) { 162*c55617f1Sdlg bio_blink(bl_arg); 1636de960dcSmarco } else if (func == BIOC_SETSTATE) { 1646de960dcSmarco bio_setstate(al_arg); 1653af9de98Smarco } 1663af9de98Smarco 1673af9de98Smarco return (0); 1683af9de98Smarco } 1693af9de98Smarco 1703af9de98Smarco void 1713af9de98Smarco usage(void) 1723af9de98Smarco { 1733af9de98Smarco extern char *__progname; 1743af9de98Smarco 175*c55617f1Sdlg fprintf(stderr, "usage: %s [-Dhiv] [-a alarm-function] [-b targ]" 176*c55617f1Sdlg " [-H chan:targ[.lun]] device\n", __progname); 1773af9de98Smarco exit(1); 1783af9de98Smarco } 1793af9de98Smarco 1806de960dcSmarco int 1816de960dcSmarco str2locator(const char *string, struct locator *location) 1826de960dcSmarco { 1836de960dcSmarco char *targ, *lun; 1846de960dcSmarco 1856de960dcSmarco targ = strchr(string, CT_SEP); 1866de960dcSmarco if (targ == NULL) 1876de960dcSmarco return (-1); 1886de960dcSmarco 1896de960dcSmarco *targ++ = '\0'; 1906de960dcSmarco 1916de960dcSmarco lun = strchr(targ, TL_SEP); 1926de960dcSmarco if (lun != NULL) { 1936de960dcSmarco *lun++ = '\0'; 1946de960dcSmarco location->lun = strtonum(lun, 0, 256 /* XXX */, NULL); 1956de960dcSmarco if (errno) 1966de960dcSmarco return (-1); 1976de960dcSmarco } else 1986de960dcSmarco location->lun = 0; 1996de960dcSmarco 2006de960dcSmarco location->target = strtonum(targ, 0, 256 /* XXX */, NULL); 2016de960dcSmarco if (errno) 2026de960dcSmarco return (-1); 2036de960dcSmarco 2046de960dcSmarco location->channel = strtonum(string, 0, 256 /* XXX */, NULL); 2056de960dcSmarco if (errno) 2066de960dcSmarco return (-1); 2076de960dcSmarco 2086de960dcSmarco return (0); 2096de960dcSmarco } 2106de960dcSmarco 2113af9de98Smarco void 2128ccdd032Sderaadt bio_inq(char *name) 213d4546a56Sdlg { 214aa65acf1Sderaadt char *status, size[64], scsiname[16], volname[32]; 215b9950701Smarco int rv, i, d, volheader, hotspare, unused; 216aa65acf1Sderaadt char encname[16], serial[32]; 2178ccdd032Sderaadt struct bioc_disk bd; 2188ccdd032Sderaadt struct bioc_inq bi; 2198ccdd032Sderaadt struct bioc_vol bv; 220db2730c1Smarco 221db2730c1Smarco memset(&bi, 0, sizeof(bi)); 2223af9de98Smarco 2233af9de98Smarco if (debug) 224db2730c1Smarco printf("bio_inq\n"); 2253af9de98Smarco 2268ccdd032Sderaadt bi.bi_cookie = bl.bl_cookie; 2273af9de98Smarco 228db2730c1Smarco rv = ioctl(devh, BIOCINQ, &bi); 2293af9de98Smarco if (rv == -1) { 230b9950701Smarco warnx("bioc_ioctl(BIOCINQ) call failed"); 2313af9de98Smarco return; 2323af9de98Smarco } 2333af9de98Smarco 2348ccdd032Sderaadt volheader = 0; 2358ccdd032Sderaadt for (i = 0; i < bi.bi_novol; i++) { 236db2730c1Smarco memset(&bv, 0, sizeof(bv)); 2378ccdd032Sderaadt bv.bv_cookie = bl.bl_cookie; 2388ccdd032Sderaadt bv.bv_volid = i; 23970a2ae7bSmarco 240db2730c1Smarco rv = ioctl(devh, BIOCVOL, &bv); 2413af9de98Smarco if (rv == -1) { 242b9950701Smarco warnx("bioc_ioctl(BIOCVOL) call failed"); 2433af9de98Smarco return; 2443af9de98Smarco } 2453af9de98Smarco 2468ccdd032Sderaadt if (name && strcmp(name, bv.bv_dev) != 0) 2478ccdd032Sderaadt continue; 2488ccdd032Sderaadt 2498ccdd032Sderaadt if (!volheader) { 2508ccdd032Sderaadt volheader = 1; 2518ccdd032Sderaadt printf("%-7s %-10s %-14s %-8s\n", 2528ccdd032Sderaadt "Volume", "Status", "Size", "Device"); 2538ccdd032Sderaadt } 2548ccdd032Sderaadt 2558ccdd032Sderaadt switch (bv.bv_status) { 256db2730c1Smarco case BIOC_SVONLINE: 2578ccdd032Sderaadt status = BIOC_SVONLINE_S; 258db2730c1Smarco break; 259db2730c1Smarco case BIOC_SVOFFLINE: 2608ccdd032Sderaadt status = BIOC_SVOFFLINE_S; 261db2730c1Smarco break; 262db2730c1Smarco case BIOC_SVDEGRADED: 2638ccdd032Sderaadt status = BIOC_SVDEGRADED_S; 264db2730c1Smarco break; 265db2730c1Smarco case BIOC_SVINVALID: 266db2730c1Smarco default: 2678ccdd032Sderaadt status = BIOC_SVINVALID_S; 268d4546a56Sdlg } 2693af9de98Smarco 270aa65acf1Sderaadt snprintf(volname, sizeof volname, "%s %u", 2718ccdd032Sderaadt bi.bi_dev, bv.bv_volid); 272b9950701Smarco 273aa65acf1Sderaadt if (bv.bv_level == -1 && bv.bv_nodisk == 1) 274aa65acf1Sderaadt hotspare = 1; 275b9950701Smarco else if (bv.bv_level == -2 && bv.bv_nodisk == 1) 276b9950701Smarco unused = 1; 277aa65acf1Sderaadt else { 278b9950701Smarco unused = 0; 279aa65acf1Sderaadt hotspare = 0; 280aa65acf1Sderaadt 2818ccdd032Sderaadt if (human) 2828ccdd032Sderaadt fmt_scaled(bv.bv_size, size); 2838ccdd032Sderaadt else 2848ccdd032Sderaadt snprintf(size, sizeof size, "%14llu", 2858ccdd032Sderaadt bv.bv_size); 286813b6523Sderaadt printf("%7s %-10s %14s %-7s RAID%u\n", 287aa65acf1Sderaadt volname, status, size, bv.bv_dev, bv.bv_level); 288aa65acf1Sderaadt } 2898ccdd032Sderaadt 2908ccdd032Sderaadt for (d = 0; d < bv.bv_nodisk; d++) { 291db2730c1Smarco memset(&bd, 0, sizeof(bd)); 2928ccdd032Sderaadt bd.bd_cookie = bl.bl_cookie; 2938ccdd032Sderaadt bd.bd_diskid = d; 2948ccdd032Sderaadt bd.bd_volid = i; 2953af9de98Smarco 296db2730c1Smarco rv = ioctl(devh, BIOCDISK, &bd); 2973af9de98Smarco if (rv == -1) { 298b9950701Smarco warnx("bioc_ioctl(BIOCDISK) call failed"); 2993af9de98Smarco return; 3003af9de98Smarco } 3013af9de98Smarco 3028ccdd032Sderaadt switch (bd.bd_status) { 303db2730c1Smarco case BIOC_SDONLINE: 3048ccdd032Sderaadt status = BIOC_SDONLINE_S; 305d4546a56Sdlg break; 306db2730c1Smarco case BIOC_SDOFFLINE: 3078ccdd032Sderaadt status = BIOC_SDOFFLINE_S; 308d4546a56Sdlg break; 309db2730c1Smarco case BIOC_SDFAILED: 3108ccdd032Sderaadt status = BIOC_SDFAILED_S; 311db2730c1Smarco break; 312db2730c1Smarco case BIOC_SDREBUILD: 3138ccdd032Sderaadt status = BIOC_SDREBUILD_S; 314db2730c1Smarco break; 315db2730c1Smarco case BIOC_SDHOTSPARE: 3168ccdd032Sderaadt status = BIOC_SDHOTSPARE_S; 317db2730c1Smarco break; 318db2730c1Smarco case BIOC_SDUNUSED: 3198ccdd032Sderaadt status = BIOC_SDUNUSED_S; 320db2730c1Smarco break; 321db2730c1Smarco case BIOC_SDINVALID: 322d4546a56Sdlg default: 3238ccdd032Sderaadt status = BIOC_SDINVALID_S; 324d4546a56Sdlg } 325aa65acf1Sderaadt 326b9950701Smarco if (hotspare || unused) 327aa65acf1Sderaadt ; /* use volname from parent volume */ 328aa65acf1Sderaadt else 329aa65acf1Sderaadt snprintf(volname, sizeof volname, " %3u", 330aa65acf1Sderaadt bd.bd_diskid); 331aa65acf1Sderaadt 3328ccdd032Sderaadt if (human) 3338ccdd032Sderaadt fmt_scaled(bd.bd_size, size); 3348ccdd032Sderaadt else 3358ccdd032Sderaadt snprintf(size, sizeof size, "%14llu", 3368ccdd032Sderaadt bd.bd_size); 3378ccdd032Sderaadt snprintf(scsiname, sizeof scsiname, 33843d61178Sderaadt "%u:%u.%u", 33943d61178Sderaadt bd.bd_channel, bd.bd_target, bd.bd_lun); 3405978b28dSderaadt if (bd.bd_procdev[0]) 341abe9d68eSderaadt strlcpy(encname, bd.bd_procdev, sizeof encname); 3425978b28dSderaadt else 343abe9d68eSderaadt strlcpy(encname, "noencl", sizeof encname); 344abe9d68eSderaadt if (bd.bd_serial[0]) 345abe9d68eSderaadt strlcpy(serial, bd.bd_serial, sizeof serial); 346abe9d68eSderaadt else 347abe9d68eSderaadt strlcpy(serial, "unknown serial", sizeof serial); 3488ccdd032Sderaadt 349aa65acf1Sderaadt printf("%7s %-10s %14s %-7s %-6s <%s>\n", 350aa65acf1Sderaadt volname, status, size, scsiname, encname, 3518ccdd032Sderaadt bd.bd_vendor); 352abe9d68eSderaadt if (verbose) 353aa65acf1Sderaadt printf("%7s %-10s %14s %-7s %-6s '%s'\n", 354abe9d68eSderaadt "", "", "", "", "", serial); 355d4546a56Sdlg } 356d4546a56Sdlg } 357d4546a56Sdlg } 358edfd9792Smarco 359edfd9792Smarco void 360edfd9792Smarco bio_alarm(char *arg) 361edfd9792Smarco { 362edfd9792Smarco int rv; 3638ccdd032Sderaadt struct bioc_alarm ba; 364edfd9792Smarco 3658ccdd032Sderaadt ba.ba_cookie = bl.bl_cookie; 366edfd9792Smarco 367edfd9792Smarco switch (arg[0]) { 368edfd9792Smarco case 'q': /* silence alarm */ 369edfd9792Smarco /* FALLTHROUGH */ 370edfd9792Smarco case 's': 3718ccdd032Sderaadt ba.ba_opcode = BIOC_SASILENCE; 372edfd9792Smarco break; 373edfd9792Smarco 374edfd9792Smarco case 'e': /* enable alarm */ 3758ccdd032Sderaadt ba.ba_opcode = BIOC_SAENABLE; 376edfd9792Smarco break; 377edfd9792Smarco 378edfd9792Smarco case 'd': /* disable alarm */ 3798ccdd032Sderaadt ba.ba_opcode = BIOC_SADISABLE; 380edfd9792Smarco break; 381edfd9792Smarco 382edfd9792Smarco case 't': /* test alarm */ 3838ccdd032Sderaadt ba.ba_opcode = BIOC_SATEST; 384edfd9792Smarco break; 385edfd9792Smarco 386edfd9792Smarco case 'g': /* get alarm state */ 3878ccdd032Sderaadt ba.ba_opcode = BIOC_GASTATUS; 388edfd9792Smarco break; 389edfd9792Smarco 390edfd9792Smarco default: 391edfd9792Smarco warnx("invalid alarm function: %s", arg); 392edfd9792Smarco return; 393edfd9792Smarco } 394edfd9792Smarco 395edfd9792Smarco rv = ioctl(devh, BIOCALARM, &ba); 396edfd9792Smarco if (rv == -1) { 3976de960dcSmarco warnx("bioc_ioctl(ALARM) call failed"); 398edfd9792Smarco return; 399edfd9792Smarco } 400edfd9792Smarco 401edfd9792Smarco if (arg[0] == 'g') { 402edfd9792Smarco printf("alarm is currently %s\n", 4038ccdd032Sderaadt ba.ba_status ? "enabled" : "disabled"); 4048ccdd032Sderaadt 405edfd9792Smarco } 406edfd9792Smarco } 4076de960dcSmarco 4086de960dcSmarco void bio_setstate(char *arg) 4096de960dcSmarco { 4106de960dcSmarco int rv; 4116de960dcSmarco struct bioc_setstate bs; 4126de960dcSmarco struct locator location; 4136de960dcSmarco 4146de960dcSmarco if (str2locator(arg, &location) != 0) 4156de960dcSmarco errx(1, "invalid channel:target[.lun]"); 4166de960dcSmarco 4176de960dcSmarco 4186de960dcSmarco bs.bs_cookie = bl.bl_cookie; 4196de960dcSmarco bs.bs_status = BIOC_SSHOTSPARE; 4206de960dcSmarco bs.bs_channel = location.channel; 4216de960dcSmarco bs.bs_target = location.target; 4226de960dcSmarco bs.bs_lun = location.lun; 4236de960dcSmarco 4246de960dcSmarco rv = ioctl(devh, BIOCSETSTATE, &bs); 4256de960dcSmarco if (rv == -1) { 4266de960dcSmarco warnx("bioc_ioctl(BIOCSETSTATE) call failed"); 4276de960dcSmarco return; 4286de960dcSmarco } 4296de960dcSmarco } 430*c55617f1Sdlg 431*c55617f1Sdlg void 432*c55617f1Sdlg bio_blink(char *arg) 433*c55617f1Sdlg { 434*c55617f1Sdlg struct bioc_blink blink; 435*c55617f1Sdlg int target, rv; 436*c55617f1Sdlg const char *errstr; 437*c55617f1Sdlg 438*c55617f1Sdlg target = strtonum(arg, 0, 255, &errstr); 439*c55617f1Sdlg if (errstr != NULL) 440*c55617f1Sdlg errx(1, "target is %s", errstr); 441*c55617f1Sdlg 442*c55617f1Sdlg memset(&blink, 0, sizeof(blink)); 443*c55617f1Sdlg blink.bb_cookie = bl.bl_cookie; 444*c55617f1Sdlg blink.bb_status = BIOC_SBBLINK; 445*c55617f1Sdlg blink.bb_target = target; 446*c55617f1Sdlg 447*c55617f1Sdlg rv = ioctl(devh, BIOCBLINK, &blink); 448*c55617f1Sdlg if (rv == -1) 449*c55617f1Sdlg err(1, "blink unable to be set"); 450*c55617f1Sdlg } 451