1 /* $NetBSD: bioctl.c,v 1.6 2007/12/07 11:51:21 xtraeme Exp $ */ 2 /* $OpenBSD: bioctl.c,v 1.52 2007/03/20 15:26:06 jmc Exp $ */ 3 4 /* 5 * Copyright (c) 2004, 2005 Marco Peereboom 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 */ 30 #include <sys/cdefs.h> 31 32 #ifndef lint 33 __RCSID("$NetBSD: bioctl.c,v 1.6 2007/12/07 11:51:21 xtraeme Exp $"); 34 #endif 35 36 #include <sys/ioctl.h> 37 #include <sys/param.h> 38 #include <sys/queue.h> 39 #include <dev/biovar.h> 40 41 #include <errno.h> 42 #include <err.h> 43 #include <fcntl.h> 44 #include <util.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 #include <ctype.h> 50 #include <util.h> 51 #include "strtonum.h" 52 53 struct locator { 54 int channel; 55 int target; 56 int lun; 57 }; 58 59 static void usage(void); 60 static const char *str2locator(const char *, struct locator *); 61 62 static void bio_inq(int, char *); 63 static void bio_alarm(int, char *); 64 static void bio_setstate(int, char *); 65 static void bio_setblink(int, char *, char *, int); 66 static void bio_blink(int, char *, int, int); 67 68 static int human; 69 static int verbose; 70 71 static struct bio_locate bl; 72 73 int 74 main(int argc, char *argv[]) 75 { 76 uint64_t func = 0; 77 char *bioc_dev, *al_arg, *bl_arg; 78 int fd, ch, rv, blink; 79 80 bioc_dev = al_arg = bl_arg = NULL; 81 fd = ch = rv = blink = 0; 82 83 if (argc < 2) 84 usage(); 85 86 setprogname(*argv); 87 88 while ((ch = getopt(argc, argv, "b:c:l:u:H:ha:Dv")) != -1) { 89 switch (ch) { 90 case 'a': /* alarm */ 91 func |= BIOC_ALARM; 92 al_arg = optarg; 93 break; 94 case 'b': /* blink */ 95 func |= BIOC_BLINK; 96 blink = BIOC_SBBLINK; 97 bl_arg = optarg; 98 break; 99 case 'u': /* unblink */ 100 func |= BIOC_BLINK; 101 blink = BIOC_SBUNBLINK; 102 bl_arg = optarg; 103 break; 104 case 'H': /* set hotspare */ 105 func |= BIOC_SETSTATE; 106 al_arg = optarg; 107 break; 108 case 'h': 109 human = 1; 110 break; 111 case 'i': /* inquiry */ 112 func |= BIOC_INQ; 113 break; 114 case 'v': 115 verbose = 1; 116 break; 117 default: 118 usage(); 119 /* NOTREACHED */ 120 } 121 } 122 argc -= optind; 123 argv += optind; 124 125 if (argc != 1) 126 usage(); 127 128 if (func == 0) 129 func |= BIOC_INQ; 130 131 bioc_dev = argv[0]; 132 133 if (bioc_dev) { 134 fd = open("/dev/bio", O_RDWR); 135 if (fd == -1) 136 err(EXIT_FAILURE, "Can't open %s", "/dev/bio"); 137 138 bl.bl_name = bioc_dev; 139 rv = ioctl(fd, BIOCLOCATE, &bl); 140 if (rv == -1) 141 errx(EXIT_FAILURE, "Can't locate %s device via %s", 142 bl.bl_name, "/dev/bio"); 143 } 144 145 if (func & BIOC_INQ) { 146 bio_inq(fd, bioc_dev); 147 } else if (func == BIOC_ALARM) { 148 bio_alarm(fd, al_arg); 149 } else if (func == BIOC_BLINK) { 150 bio_setblink(fd, bioc_dev, bl_arg, blink); 151 } else if (func == BIOC_SETSTATE) { 152 bio_setstate(fd, al_arg); 153 } 154 155 exit(EXIT_SUCCESS); 156 } 157 158 static void 159 usage(void) 160 { 161 (void)fprintf(stderr, 162 "usage: %s [-hv] [-a alarm-function] " 163 "[-b channel:target[.lun]]\n" 164 "\t[-H channel:target[.lun]]\n" 165 "\t[-u channel:target[.lun]] device\n", getprogname()); 166 exit(EXIT_FAILURE); 167 /* NOTREACHED */ 168 } 169 170 static const char * 171 str2locator(const char *string, struct locator *location) 172 { 173 const char *errstr; 174 char parse[80], *targ, *lun; 175 176 strlcpy(parse, string, sizeof parse); 177 targ = strchr(parse, ':'); 178 if (targ == NULL) 179 return ("target not specified"); 180 *targ++ = '\0'; 181 182 lun = strchr(targ, '.'); 183 if (lun != NULL) { 184 *lun++ = '\0'; 185 location->lun = strtonum(lun, 0, 256, &errstr); 186 if (errstr) 187 return errstr; 188 } else 189 location->lun = 0; 190 191 location->target = strtonum(targ, 0, 256, &errstr); 192 if (errstr) 193 return errstr; 194 location->channel = strtonum(parse, 0, 256, &errstr); 195 if (errstr) 196 return errstr; 197 return NULL; 198 } 199 200 static void 201 bio_inq(int fd, char *name) 202 { 203 const char *status; 204 char size[64], scsiname[16], volname[32]; 205 char percent[10], seconds[20]; 206 int rv, i, d, volheader, hotspare, unused; 207 char encname[16], serial[32]; 208 struct bioc_disk bd; 209 struct bioc_inq bi; 210 struct bioc_vol bv; 211 212 memset(&bi, 0, sizeof(bi)); 213 214 bi.bi_cookie = bl.bl_cookie; 215 216 rv = ioctl(fd, BIOCINQ, &bi); 217 if (rv) 218 errx(EXIT_FAILURE, "BIOCINQ %s", strerror(errno)); 219 220 volheader = 0; 221 for (i = 0; i < bi.bi_novol; i++) { 222 memset(&bv, 0, sizeof(bv)); 223 bv.bv_cookie = bl.bl_cookie; 224 bv.bv_volid = i; 225 bv.bv_percent = -1; 226 bv.bv_seconds = 0; 227 228 rv = ioctl(fd, BIOCVOL, &bv); 229 if (rv) 230 errx(EXIT_FAILURE, "BIOCVOL %s", strerror(errno)); 231 232 if (!volheader) { 233 volheader = 1; 234 if (human) 235 printf("%10s %-10s %4s %-8s\n", 236 "Volume", "Status", "Size", "Device"); 237 else 238 printf("%10s %-10s %14s %-8s\n", 239 "Volume", "Status", "Size", "Device"); 240 } 241 242 percent[0] = '\0'; 243 seconds[0] = '\0'; 244 if (bv.bv_percent != -1) 245 snprintf(percent, sizeof percent, 246 " %d%% done", bv.bv_percent); 247 if (bv.bv_seconds) 248 snprintf(seconds, sizeof seconds, 249 " %u seconds", bv.bv_seconds); 250 switch (bv.bv_status) { 251 case BIOC_SVONLINE: 252 status = BIOC_SVONLINE_S; 253 break; 254 case BIOC_SVOFFLINE: 255 status = BIOC_SVOFFLINE_S; 256 break; 257 case BIOC_SVDEGRADED: 258 status = BIOC_SVDEGRADED_S; 259 break; 260 case BIOC_SVBUILDING: 261 status = BIOC_SVBUILDING_S; 262 break; 263 case BIOC_SVREBUILD: 264 status = BIOC_SVREBUILD_S; 265 break; 266 case BIOC_SVMIGRATING: 267 status = BIOC_SVMIGRATING_S; 268 break; 269 case BIOC_SVSCRUB: 270 status = BIOC_SVSCRUB_S; 271 break; 272 case BIOC_SVINVALID: 273 default: 274 status = BIOC_SVINVALID_S; 275 } 276 277 snprintf(volname, sizeof volname, "%s %u", 278 bi.bi_dev, bv.bv_volid); 279 280 if (bv.bv_level == -1 && bv.bv_nodisk == 1) { 281 hotspare = 1; 282 unused = 0; 283 } else if (bv.bv_level == -2 && bv.bv_nodisk == 1) { 284 unused = 1; 285 hotspare = 0; 286 } else { 287 unused = 0; 288 hotspare = 0; 289 290 if (human) { 291 humanize_number(size, 5, 292 (int64_t)bv.bv_size, "", HN_AUTOSCALE, 293 HN_B | HN_NOSPACE | HN_DECIMAL); 294 printf("%10s %-10s %4s %-7s RAID %u%s%s\n", 295 volname, status, size, bv.bv_dev, 296 bv.bv_level, percent, seconds); 297 } else { 298 snprintf(size, sizeof size, "%14llu", 299 (long long unsigned int)bv.bv_size); 300 printf("%10s %-10s %14s %-7s RAID %u%s%s\n", 301 volname, status, size, bv.bv_dev, 302 bv.bv_level, percent, seconds); 303 } 304 } 305 306 for (d = 0; d < bv.bv_nodisk; d++) { 307 memset(&bd, 0, sizeof(bd)); 308 bd.bd_cookie = bl.bl_cookie; 309 bd.bd_diskid = d; 310 bd.bd_volid = i; 311 312 rv = ioctl(fd, BIOCDISK, &bd); 313 if (rv) 314 errx(EXIT_FAILURE, "BIOCDISK %s", 315 strerror(errno)); 316 317 switch (bd.bd_status) { 318 case BIOC_SDONLINE: 319 status = BIOC_SDONLINE_S; 320 break; 321 case BIOC_SDOFFLINE: 322 status = BIOC_SDOFFLINE_S; 323 break; 324 case BIOC_SDFAILED: 325 status = BIOC_SDFAILED_S; 326 break; 327 case BIOC_SDREBUILD: 328 status = BIOC_SDREBUILD_S; 329 break; 330 case BIOC_SDHOTSPARE: 331 status = BIOC_SDHOTSPARE_S; 332 break; 333 case BIOC_SDUNUSED: 334 status = BIOC_SDUNUSED_S; 335 break; 336 case BIOC_SDSCRUB: 337 status = BIOC_SDSCRUB_S; 338 break; 339 case BIOC_SDINVALID: 340 default: 341 status = BIOC_SDINVALID_S; 342 } 343 344 if (hotspare || unused) 345 ; /* use volname from parent volume */ 346 else 347 snprintf(volname, sizeof volname, " %3u", 348 bd.bd_diskid); 349 350 if (human) 351 humanize_number(size, 5, 352 bd.bd_size, "", HN_AUTOSCALE, 353 HN_B | HN_NOSPACE | HN_DECIMAL); 354 else 355 snprintf(size, sizeof size, "%14llu", 356 (long long unsigned int)bd.bd_size); 357 snprintf(scsiname, sizeof scsiname, 358 "%u:%u.%u", 359 bd.bd_channel, bd.bd_target, bd.bd_lun); 360 if (bd.bd_procdev[0]) 361 strlcpy(encname, bd.bd_procdev, sizeof encname); 362 else 363 strlcpy(encname, "noencl", sizeof encname); 364 if (bd.bd_serial[0]) 365 strlcpy(serial, bd.bd_serial, sizeof serial); 366 else 367 strlcpy(serial, "unknown serial", sizeof serial); 368 369 if (human) 370 printf("%10s %-10s %4s %-7s %-6s <%s>\n", 371 volname, status, size, scsiname, encname, 372 bd.bd_vendor); 373 else 374 printf("%10s %-10s %14s %-7s %-6s <%s>\n", 375 volname, status, size, scsiname, encname, 376 bd.bd_vendor); 377 if (verbose) 378 printf("%7s %-10s %14s %-7s %-6s '%s'\n", 379 "", "", "", "", "", serial); 380 } 381 } 382 } 383 384 static void 385 bio_alarm(int fd, char *arg) 386 { 387 int rv; 388 struct bioc_alarm ba; 389 390 ba.ba_cookie = bl.bl_cookie; 391 392 switch (arg[0]) { 393 case 'q': /* silence alarm */ 394 /* FALLTHROUGH */ 395 case 's': 396 ba.ba_opcode = BIOC_SASILENCE; 397 break; 398 399 case 'e': /* enable alarm */ 400 ba.ba_opcode = BIOC_SAENABLE; 401 break; 402 403 case 'd': /* disable alarm */ 404 ba.ba_opcode = BIOC_SADISABLE; 405 break; 406 407 case 't': /* test alarm */ 408 ba.ba_opcode = BIOC_SATEST; 409 break; 410 411 case 'g': /* get alarm state */ 412 ba.ba_opcode = BIOC_GASTATUS; 413 break; 414 415 default: 416 warnx("invalid alarm function: %s", arg); 417 return; 418 } 419 420 rv = ioctl(fd, BIOCALARM, &ba); 421 if (rv) 422 errx(EXIT_FAILURE, "BIOCALARM %s", strerror(errno)); 423 424 if (arg[0] == 'g') { 425 printf("alarm is currently %s\n", 426 ba.ba_status ? "enabled" : "disabled"); 427 428 } 429 } 430 431 static void 432 bio_setstate(int fd, char *arg) 433 { 434 struct bioc_setstate bs; 435 struct locator location; 436 const char *errstr; 437 int rv; 438 439 errstr = str2locator(arg, &location); 440 if (errstr) 441 errx(1, "Target %s: %s", arg, errstr); 442 443 bs.bs_cookie = bl.bl_cookie; 444 bs.bs_status = BIOC_SSHOTSPARE; 445 bs.bs_channel = location.channel; 446 bs.bs_target = location.target; 447 bs.bs_lun = location.lun; 448 449 rv = ioctl(fd, BIOCSETSTATE, &bs); 450 if (rv) 451 errx(EXIT_FAILURE, "BIOCSETSTATE %s", strerror(errno)); 452 } 453 454 static void 455 bio_setblink(int fd, char *name, char *arg, int blink) 456 { 457 struct locator location; 458 struct bioc_inq bi; 459 struct bioc_vol bv; 460 struct bioc_disk bd; 461 struct bioc_blink bb; 462 const char *errstr; 463 int v, d, rv; 464 465 errstr = str2locator(arg, &location); 466 if (errstr) 467 errx(EXIT_FAILURE, "Target %s: %s", arg, errstr); 468 469 /* try setting blink on the device directly */ 470 memset(&bb, 0, sizeof(bb)); 471 bb.bb_cookie = bl.bl_cookie; 472 bb.bb_status = blink; 473 bb.bb_target = location.target; 474 bb.bb_channel = location.channel; 475 rv = ioctl(fd, BIOCBLINK, &bb); 476 if (rv == 0) 477 return; 478 479 /* if the blink didnt work, try to find something that will */ 480 481 memset(&bi, 0, sizeof(bi)); 482 bi.bi_cookie = bl.bl_cookie; 483 rv = ioctl(fd, BIOCINQ, &bi); 484 if (rv) 485 errx(EXIT_FAILURE, "BIOCINQ %s", strerror(errno)); 486 487 for (v = 0; v < bi.bi_novol; v++) { 488 memset(&bv, 0, sizeof(bv)); 489 bv.bv_cookie = bl.bl_cookie; 490 bv.bv_volid = v; 491 rv = ioctl(fd, BIOCVOL, &bv); 492 if (rv == -1) 493 errx(EXIT_FAILURE, "BIOCVOL %s", strerror(errno)); 494 495 for (d = 0; d < bv.bv_nodisk; d++) { 496 memset(&bd, 0, sizeof(bd)); 497 bd.bd_cookie = bl.bl_cookie; 498 bd.bd_volid = v; 499 bd.bd_diskid = d; 500 501 rv = ioctl(fd, BIOCDISK, &bd); 502 if (rv == -1) 503 errx(EXIT_FAILURE, "BIOCDISK %s", 504 strerror(errno)); 505 506 if (bd.bd_channel == location.channel && 507 bd.bd_target == location.target && 508 bd.bd_lun == location.lun) { 509 if (bd.bd_procdev[0] != '\0') { 510 bio_blink(fd, bd.bd_procdev, 511 location.target, blink); 512 } else 513 warnx("Disk %s is not in an enclosure", arg); 514 return; 515 } 516 } 517 } 518 519 warnx("Disk %s does not exist", arg); 520 return; 521 } 522 523 static void 524 bio_blink(int fd, char *enclosure, int target, int blinktype) 525 { 526 struct bio_locate bio; 527 struct bioc_blink blink; 528 int rv; 529 530 bio.bl_name = enclosure; 531 rv = ioctl(fd, BIOCLOCATE, &bio); 532 if (rv == -1) 533 errx(EXIT_FAILURE, 534 "Can't locate %s device via %s", enclosure, "/dev/bio"); 535 536 memset(&blink, 0, sizeof(blink)); 537 blink.bb_cookie = bio.bl_cookie; 538 blink.bb_status = blinktype; 539 blink.bb_target = target; 540 541 rv = ioctl(fd, BIOCBLINK, &blink); 542 if (rv == -1) 543 errx(EXIT_FAILURE, "BIOCBLINK %s", strerror(errno)); 544 } 545