1 /* $NetBSD: bioctl.c,v 1.2 2007/11/04 08:25:05 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.2 2007/11/04 08:25:05 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 debug; 69 static int human; 70 static int verbose; 71 72 static struct bio_locate bl; 73 74 int 75 main(int argc, char *argv[]) 76 { 77 uint64_t func = 0; 78 char *bioc_dev, *al_arg, *bl_arg; 79 int fd, ch, rv, blink; 80 81 bioc_dev = al_arg = bl_arg = NULL; 82 fd = ch = rv = blink = 0; 83 84 if (argc < 2) 85 usage(); 86 87 setprogname(*argv); 88 89 while ((ch = getopt(argc, argv, "b:c:l:u:H:ha:Dv")) != -1) { 90 switch (ch) { 91 case 'a': /* alarm */ 92 func |= BIOC_ALARM; 93 al_arg = optarg; 94 break; 95 case 'b': /* blink */ 96 func |= BIOC_BLINK; 97 blink = BIOC_SBBLINK; 98 bl_arg = optarg; 99 break; 100 case 'u': /* unblink */ 101 func |= BIOC_BLINK; 102 blink = BIOC_SBUNBLINK; 103 bl_arg = optarg; 104 break; 105 case 'D': /* debug */ 106 debug = 1; 107 break; 108 case 'H': /* set hotspare */ 109 func |= BIOC_SETSTATE; 110 al_arg = optarg; 111 break; 112 case 'h': 113 human = 1; 114 break; 115 case 'i': /* inquiry */ 116 func |= BIOC_INQ; 117 break; 118 case 'v': 119 verbose = 1; 120 break; 121 default: 122 usage(); 123 /* NOTREACHED */ 124 } 125 } 126 argc -= optind; 127 argv += optind; 128 129 if (argc != 1) 130 usage(); 131 132 if (func == 0) 133 func |= BIOC_INQ; 134 135 bioc_dev = argv[0]; 136 137 if (bioc_dev) { 138 fd = open("/dev/bio", O_RDWR); 139 if (fd == -1) 140 err(EXIT_FAILURE, "Can't open %s", "/dev/bio"); 141 142 bl.bl_name = bioc_dev; 143 rv = ioctl(fd, BIOCLOCATE, &bl); 144 if (rv == -1) 145 errx(EXIT_FAILURE, "Can't locate %s device via %s", 146 bl.bl_name, "/dev/bio"); 147 } 148 149 if (debug) 150 warnx("cookie = %p", bl.bl_cookie); 151 152 if (func & BIOC_INQ) { 153 bio_inq(fd, bioc_dev); 154 } else if (func == BIOC_ALARM) { 155 bio_alarm(fd, al_arg); 156 } else if (func == BIOC_BLINK) { 157 bio_setblink(fd, bioc_dev, bl_arg, blink); 158 } else if (func == BIOC_SETSTATE) { 159 bio_setstate(fd, al_arg); 160 } 161 162 exit(EXIT_SUCCESS); 163 } 164 165 static void 166 usage(void) 167 { 168 (void)fprintf(stderr, 169 "usage: %s [-Dhv] [-a alarm-function] " 170 "[-b channel:target[.lun]]\n" 171 "\t[-H channel:target[.lun]]\n" 172 "\t[-u channel:target[.lun]] device\n", getprogname()); 173 exit(EXIT_FAILURE); 174 /* NOTREACHED */ 175 } 176 177 static const char * 178 str2locator(const char *string, struct locator *location) 179 { 180 const char *errstr; 181 char parse[80], *targ, *lun; 182 183 strlcpy(parse, string, sizeof parse); 184 targ = strchr(parse, ':'); 185 if (targ == NULL) 186 return ("target not specified"); 187 *targ++ = '\0'; 188 189 lun = strchr(targ, '.'); 190 if (lun != NULL) { 191 *lun++ = '\0'; 192 location->lun = strtonum(lun, 0, 256, &errstr); 193 if (errstr) 194 return errstr; 195 } else 196 location->lun = 0; 197 198 location->target = strtonum(targ, 0, 256, &errstr); 199 if (errstr) 200 return errstr; 201 location->channel = strtonum(parse, 0, 256, &errstr); 202 if (errstr) 203 return errstr; 204 return NULL; 205 } 206 207 static void 208 bio_inq(int fd, char *name) 209 { 210 const char *status; 211 char size[64], scsiname[16], volname[32]; 212 char percent[10], seconds[20]; 213 int rv, i, d, volheader, hotspare, unused; 214 char encname[16], serial[32]; 215 struct bioc_disk bd; 216 struct bioc_inq bi; 217 struct bioc_vol bv; 218 219 memset(&bi, 0, sizeof(bi)); 220 221 if (debug) 222 printf("bio_inq\n"); 223 224 bi.bi_cookie = bl.bl_cookie; 225 226 rv = ioctl(fd, BIOCINQ, &bi); 227 if (rv == -1) { 228 warn("BIOCINQ"); 229 return; 230 } 231 232 if (debug) 233 printf("bio_inq { %p, %s, %d, %d }\n", 234 bi.bi_cookie, 235 bi.bi_dev, 236 bi.bi_novol, 237 bi.bi_nodisk); 238 239 volheader = 0; 240 for (i = 0; i < bi.bi_novol; i++) { 241 memset(&bv, 0, sizeof(bv)); 242 bv.bv_cookie = bl.bl_cookie; 243 bv.bv_volid = i; 244 bv.bv_percent = -1; 245 bv.bv_seconds = 0; 246 247 rv = ioctl(fd, BIOCVOL, &bv); 248 if (rv == -1) { 249 warn("BIOCVOL"); 250 return; 251 } 252 253 if (name && strcmp(name, bv.bv_dev) != 0) 254 continue; 255 256 if (!volheader) { 257 volheader = 1; 258 printf("%-7s %-10s %14s %-8s\n", 259 "Volume", "Status", "Size", "Device"); 260 } 261 262 percent[0] = '\0'; 263 seconds[0] = '\0'; 264 if (bv.bv_percent != -1) 265 snprintf(percent, sizeof percent, 266 " %d%% done", bv.bv_percent); 267 if (bv.bv_seconds) 268 snprintf(seconds, sizeof seconds, 269 " %u seconds", bv.bv_seconds); 270 switch (bv.bv_status) { 271 case BIOC_SVONLINE: 272 status = BIOC_SVONLINE_S; 273 break; 274 case BIOC_SVOFFLINE: 275 status = BIOC_SVOFFLINE_S; 276 break; 277 case BIOC_SVDEGRADED: 278 status = BIOC_SVDEGRADED_S; 279 break; 280 case BIOC_SVBUILDING: 281 status = BIOC_SVBUILDING_S; 282 break; 283 case BIOC_SVREBUILD: 284 status = BIOC_SVREBUILD_S; 285 break; 286 case BIOC_SVSCRUB: 287 status = BIOC_SVSCRUB_S; 288 break; 289 case BIOC_SVINVALID: 290 default: 291 status = BIOC_SVINVALID_S; 292 } 293 294 snprintf(volname, sizeof volname, "%s %u", 295 bi.bi_dev, bv.bv_volid); 296 297 if (bv.bv_level == -1 && bv.bv_nodisk == 1) { 298 hotspare = 1; 299 unused = 0; 300 } else if (bv.bv_level == -2 && bv.bv_nodisk == 1) { 301 unused = 1; 302 hotspare = 0; 303 } else { 304 unused = 0; 305 hotspare = 0; 306 307 if (human) 308 humanize_number(size, 5, 309 (int64_t)bv.bv_size, "", HN_AUTOSCALE, 310 HN_B | HN_NOSPACE | HN_DECIMAL); 311 else 312 snprintf(size, sizeof size, "%14llu", 313 (long long unsigned int)bv.bv_size); 314 printf("%7s %-10s %14s %-7s RAID%u%s%s\n", 315 volname, status, size, bv.bv_dev, 316 bv.bv_level, percent, seconds); 317 } 318 319 for (d = 0; d < bv.bv_nodisk; d++) { 320 memset(&bd, 0, sizeof(bd)); 321 bd.bd_cookie = bl.bl_cookie; 322 bd.bd_diskid = d; 323 bd.bd_volid = i; 324 325 rv = ioctl(fd, BIOCDISK, &bd); 326 if (rv == -1) { 327 warn("BIOCDISK"); 328 return; 329 } 330 331 switch (bd.bd_status) { 332 case BIOC_SDONLINE: 333 status = BIOC_SDONLINE_S; 334 break; 335 case BIOC_SDOFFLINE: 336 status = BIOC_SDOFFLINE_S; 337 break; 338 case BIOC_SDFAILED: 339 status = BIOC_SDFAILED_S; 340 break; 341 case BIOC_SDREBUILD: 342 status = BIOC_SDREBUILD_S; 343 break; 344 case BIOC_SDHOTSPARE: 345 status = BIOC_SDHOTSPARE_S; 346 break; 347 case BIOC_SDUNUSED: 348 status = BIOC_SDUNUSED_S; 349 break; 350 case BIOC_SDSCRUB: 351 status = BIOC_SDSCRUB_S; 352 break; 353 case BIOC_SDINVALID: 354 default: 355 status = BIOC_SDINVALID_S; 356 } 357 358 if (hotspare || unused) 359 ; /* use volname from parent volume */ 360 else 361 snprintf(volname, sizeof volname, " %3u", 362 bd.bd_diskid); 363 364 if (human) 365 humanize_number(size, 5, 366 bd.bd_size, "", HN_AUTOSCALE, 367 HN_B | HN_NOSPACE | HN_DECIMAL); 368 else 369 snprintf(size, sizeof size, "%14llu", 370 (long long unsigned int)bd.bd_size); 371 snprintf(scsiname, sizeof scsiname, 372 "%u:%u.%u", 373 bd.bd_channel, bd.bd_target, bd.bd_lun); 374 if (bd.bd_procdev[0]) 375 strlcpy(encname, bd.bd_procdev, sizeof encname); 376 else 377 strlcpy(encname, "noencl", sizeof encname); 378 if (bd.bd_serial[0]) 379 strlcpy(serial, bd.bd_serial, sizeof serial); 380 else 381 strlcpy(serial, "unknown serial", sizeof serial); 382 383 printf("%7s %-10s %14s %-7s %-6s <%s>\n", 384 volname, status, size, scsiname, encname, 385 bd.bd_vendor); 386 if (verbose) 387 printf("%7s %-10s %14s %-7s %-6s '%s'\n", 388 "", "", "", "", "", serial); 389 } 390 } 391 } 392 393 static void 394 bio_alarm(int fd, char *arg) 395 { 396 int rv; 397 struct bioc_alarm ba; 398 399 ba.ba_cookie = bl.bl_cookie; 400 401 switch (arg[0]) { 402 case 'q': /* silence alarm */ 403 /* FALLTHROUGH */ 404 case 's': 405 ba.ba_opcode = BIOC_SASILENCE; 406 break; 407 408 case 'e': /* enable alarm */ 409 ba.ba_opcode = BIOC_SAENABLE; 410 break; 411 412 case 'd': /* disable alarm */ 413 ba.ba_opcode = BIOC_SADISABLE; 414 break; 415 416 case 't': /* test alarm */ 417 ba.ba_opcode = BIOC_SATEST; 418 break; 419 420 case 'g': /* get alarm state */ 421 ba.ba_opcode = BIOC_GASTATUS; 422 break; 423 424 default: 425 warnx("invalid alarm function: %s", arg); 426 return; 427 } 428 429 rv = ioctl(fd, BIOCALARM, &ba); 430 if (rv == -1) { 431 warn("BIOCALARM"); 432 return; 433 } 434 435 if (arg[0] == 'g') { 436 printf("alarm is currently %s\n", 437 ba.ba_status ? "enabled" : "disabled"); 438 439 } 440 } 441 442 static void 443 bio_setstate(int fd, char *arg) 444 { 445 struct bioc_setstate bs; 446 struct locator location; 447 const char *errstr; 448 int rv; 449 450 errstr = str2locator(arg, &location); 451 if (errstr) 452 errx(1, "Target %s: %s", arg, errstr); 453 454 bs.bs_cookie = bl.bl_cookie; 455 bs.bs_status = BIOC_SSHOTSPARE; 456 bs.bs_channel = location.channel; 457 bs.bs_target = location.target; 458 bs.bs_lun = location.lun; 459 460 rv = ioctl(fd, BIOCSETSTATE, &bs); 461 if (rv == -1) { 462 warn("BIOCSETSTATE"); 463 return; 464 } 465 } 466 467 static void 468 bio_setblink(int fd, char *name, char *arg, int blink) 469 { 470 struct locator location; 471 struct bioc_inq bi; 472 struct bioc_vol bv; 473 struct bioc_disk bd; 474 struct bioc_blink bb; 475 const char *errstr; 476 int v, d, rv; 477 478 errstr = str2locator(arg, &location); 479 if (errstr) 480 errx(1, "Target %s: %s", arg, errstr); 481 482 /* try setting blink on the device directly */ 483 memset(&bb, 0, sizeof(bb)); 484 bb.bb_cookie = bl.bl_cookie; 485 bb.bb_status = blink; 486 bb.bb_target = location.target; 487 bb.bb_channel = location.channel; 488 rv = ioctl(fd, BIOCBLINK, &bb); 489 if (rv == 0) 490 return; 491 492 /* if the blink didnt work, try to find something that will */ 493 494 memset(&bi, 0, sizeof(bi)); 495 bi.bi_cookie = bl.bl_cookie; 496 rv = ioctl(fd, BIOCINQ, &bi); 497 if (rv == -1) { 498 warn("BIOCINQ"); 499 return; 500 } 501 502 for (v = 0; v < bi.bi_novol; v++) { 503 memset(&bv, 0, sizeof(bv)); 504 bv.bv_cookie = bl.bl_cookie; 505 bv.bv_volid = v; 506 rv = ioctl(fd, BIOCVOL, &bv); 507 if (rv == -1) { 508 warn("BIOCVOL"); 509 return; 510 } 511 512 if (name && strcmp(name, bv.bv_dev) != 0) 513 continue; 514 515 for (d = 0; d < bv.bv_nodisk; d++) { 516 memset(&bd, 0, sizeof(bd)); 517 bd.bd_cookie = bl.bl_cookie; 518 bd.bd_volid = v; 519 bd.bd_diskid = d; 520 521 rv = ioctl(fd, BIOCDISK, &bd); 522 if (rv == -1) { 523 warn("BIOCDISK"); 524 return; 525 } 526 527 if (bd.bd_channel == location.channel && 528 bd.bd_target == location.target && 529 bd.bd_lun == location.lun) { 530 if (bd.bd_procdev[0] != '\0') { 531 bio_blink(fd, bd.bd_procdev, 532 location.target, blink); 533 } else 534 warnx("Disk %s is not in an enclosure", arg); 535 return; 536 } 537 } 538 } 539 540 warnx("Disk %s does not exist", arg); 541 return; 542 } 543 544 static void 545 bio_blink(int fd, char *enclosure, int target, int blinktype) 546 { 547 struct bio_locate bio; 548 struct bioc_blink blink; 549 int rv; 550 551 bio.bl_name = enclosure; 552 rv = ioctl(fd, BIOCLOCATE, &bio); 553 if (rv == -1) 554 errx(1, "Can't locate %s device via %s", enclosure, "/dev/bio"); 555 556 memset(&blink, 0, sizeof(blink)); 557 blink.bb_cookie = bio.bl_cookie; 558 blink.bb_status = blinktype; 559 blink.bb_target = target; 560 561 rv = ioctl(fd, BIOCBLINK, &blink); 562 if (rv == -1) 563 warn("BIOCBLINK"); 564 } 565