Home | History | Annotate | Line # | Download | only in bioctl
bioctl.c revision 1.1
      1 /* $NetBSD: bioctl.c,v 1.1 2007/05/01 17:18:53 bouyer 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.1 2007/05/01 17:18:53 bouyer Exp $");
     34 #endif
     35 
     36 #include <sys/ioctl.h>
     37 #include <sys/param.h>
     38 #include <sys/queue.h>
     39 // #include <scsi/scsipi_disk.h>
     40 // #include <scsi/scsipi_all.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 <stdio.h>
     48 #include <stdlib.h>
     49 #include <string.h>
     50 #include <unistd.h>
     51 #include <ctype.h>
     52 #include <util.h>
     53 #include "strtonum.h"
     54 
     55 struct locator {
     56 	int channel;
     57 	int target;
     58 	int lun;
     59 };
     60 
     61 void usage(void);
     62 const char *str2locator(const char *, struct locator *);
     63 void cleanup(void);
     64 
     65 void bio_inq(char *);
     66 void bio_alarm(char *);
     67 void bio_setstate(char *);
     68 void bio_setblink(char *, char *, int);
     69 void bio_blink(char *, int, int);
     70 void bio_createraid(u_int16_t, char *);
     71 
     72 int devh = -1;
     73 int debug;
     74 int human;
     75 int verbose;
     76 
     77 struct bio_locate bl;
     78 
     79 int
     80 main(int argc, char *argv[])
     81 {
     82 	extern char *optarg;
     83 	u_int64_t func = 0;
     84 	/* u_int64_t subfunc = 0; XXX */
     85 	char *bioc_dev = NULL, *sd_dev = NULL;
     86 	char /* *realname = NULL, XXX */ *al_arg = NULL;
     87 	char *bl_arg = NULL, *dev_list = NULL;
     88 	int ch, rv, blink = 0; /* XXX GCC */
     89 	u_int16_t cr_level = 0; /* XXX GCC */
     90 
     91 	if (argc < 2)
     92 		usage();
     93 
     94 	while ((ch = getopt(argc, argv, "b:c:l:u:H:ha:Div")) != -1) {
     95 		switch (ch) {
     96 		case 'a': /* alarm */
     97 			func |= BIOC_ALARM;
     98 			al_arg = optarg;
     99 			break;
    100 		case 'b': /* blink */
    101 			func |= BIOC_BLINK;
    102 			blink = BIOC_SBBLINK;
    103 			bl_arg = optarg;
    104 			break;
    105 		case 'c': /* create */
    106 			func |= BIOC_CREATERAID;
    107 			cr_level = atoi(optarg);
    108 			break;
    109 		case 'u': /* unblink */
    110 			func |= BIOC_BLINK;
    111 			blink = BIOC_SBUNBLINK;
    112 			bl_arg = optarg;
    113 			break;
    114 		case 'D': /* debug */
    115 			debug = 1;
    116 			break;
    117 		case 'H': /* set hotspare */
    118 			func |= BIOC_SETSTATE;
    119 			al_arg = optarg;
    120 			break;
    121 		case 'h':
    122 			human = 1;
    123 			break;
    124 		case 'i': /* inquiry */
    125 			func |= BIOC_INQ;
    126 			break;
    127 		case 'l': /* device list */
    128 			func |= BIOC_DEVLIST;
    129 			dev_list = optarg;
    130 			break;
    131 		case 'v':
    132 			verbose = 1;
    133 			break;
    134 		default:
    135 			usage();
    136 			/* NOTREACHED */
    137 		}
    138 	}
    139 	argc -= optind;
    140 	argv += optind;
    141 
    142 	if (argc != 1)
    143 		usage();
    144 
    145 	if (func == 0)
    146 		func |= BIOC_INQ;
    147 #if 0
    148 	/* if at least glob sd[0-9]*, it is a drive identifier */
    149 	if (strncmp(argv[0], "sd", 2) == 0 && strlen(argv[0]) > 2 &&
    150 	    isdigit((int)argv[0][2]))
    151 		sd_dev = argv[0];
    152 	else
    153 #endif
    154 		bioc_dev = argv[0];
    155 
    156 	if (bioc_dev) {
    157 		devh = open("/dev/bio", O_RDWR);
    158 		if (devh == -1)
    159 			err(1, "Can't open %s", "/dev/bio");
    160 
    161 		bl.bl_name = bioc_dev;
    162 		rv = ioctl(devh, BIOCLOCATE, &bl);
    163 		if (rv == -1)
    164 			errx(1, "Can't locate %s device via %s",
    165 			    bl.bl_name, "/dev/bio");
    166 	}
    167 #if 0
    168 	else if (sd_dev) {
    169 		devh = opendev(sd_dev, O_RDWR, OPENDEV_PART, &realname);
    170 		if (devh == -1)
    171 			err(1, "Can't open %s", sd_dev);
    172 	}
    173 #endif
    174 	else
    175 		errx(1, "need -d or -f parameter");
    176 
    177 	if (debug)
    178 		warnx("cookie = %p", bl.bl_cookie);
    179 
    180 	if (func & BIOC_INQ) {
    181 		bio_inq(sd_dev);
    182 	} else if (func == BIOC_ALARM) {
    183 		bio_alarm(al_arg);
    184 	} else if (func == BIOC_BLINK) {
    185 		bio_setblink(sd_dev, bl_arg, blink);
    186 	} else if (func == BIOC_SETSTATE) {
    187 		bio_setstate(al_arg);
    188 	} else if (func & BIOC_CREATERAID || func & BIOC_DEVLIST) {
    189 		if (!(func & BIOC_CREATERAID))
    190 			errx(1, "need -c parameter");
    191 		if (!(func & BIOC_DEVLIST))
    192 			errx(1, "need -l parameter");
    193 		if (sd_dev)
    194 			errx(1, "can't use sd device");
    195 		bio_createraid(cr_level, dev_list);
    196 	}
    197 
    198 	return (0);
    199 }
    200 
    201 void
    202 usage(void)
    203 {
    204 	extern char *__progname;
    205 
    206 	fprintf(stderr,
    207 		"usage: %s [-Dhiv] [-a alarm-function] "
    208 		"[-b channel:target[.lun]]\n"
    209 		"\t[-c raidlevel] [-H channel:target[.lun]]\n"
    210 		"\t[-l special[,special[,...]]] "
    211 		"[-u channel:target[.lun]] device\n", __progname);
    212 
    213 	exit(1);
    214 }
    215 
    216 const char *
    217 str2locator(const char *string, struct locator *location)
    218 {
    219 	const char *errstr;
    220 	char parse[80], *targ, *lun;
    221 
    222 	strlcpy(parse, string, sizeof parse);
    223 	targ = strchr(parse, ':');
    224 	if (targ == NULL)
    225 		return ("target not specified");
    226 	*targ++ = '\0';
    227 
    228 	lun = strchr(targ, '.');
    229 	if (lun != NULL) {
    230 		*lun++ = '\0';
    231 		location->lun = strtonum(lun, 0, 256, &errstr);
    232 		if (errstr)
    233 			return (errstr);
    234 	} else
    235 		location->lun = 0;
    236 
    237 	location->target = strtonum(targ, 0, 256, &errstr);
    238 	if (errstr)
    239 		return (errstr);
    240 	location->channel = strtonum(parse, 0, 256, &errstr);
    241 	if (errstr)
    242 		return (errstr);
    243 	return (NULL);
    244 }
    245 
    246 void
    247 bio_inq(char *name)
    248 {
    249 	const char *status;
    250 	char size[64], scsiname[16], volname[32];
    251 	char percent[10], seconds[20];
    252 	int rv, i, d, volheader, hotspare, unused;
    253 	char encname[16], serial[32];
    254 	struct bioc_disk bd;
    255 	struct bioc_inq bi;
    256 	struct bioc_vol bv;
    257 
    258 	memset(&bi, 0, sizeof(bi));
    259 
    260 	if (debug)
    261 		printf("bio_inq\n");
    262 
    263 	bi.bi_cookie = bl.bl_cookie;
    264 
    265 	rv = ioctl(devh, BIOCINQ, &bi);
    266 	if (rv == -1) {
    267 		warn("BIOCINQ");
    268 		return;
    269 	}
    270 
    271 	if (debug)
    272 		printf("bio_inq { %p, %s, %d, %d }\n",
    273 		    bi.bi_cookie,
    274 		    bi.bi_dev,
    275 		    bi.bi_novol,
    276 		    bi.bi_nodisk);
    277 
    278 	volheader = 0;
    279 	for (i = 0; i < bi.bi_novol; i++) {
    280 		memset(&bv, 0, sizeof(bv));
    281 		bv.bv_cookie = bl.bl_cookie;
    282 		bv.bv_volid = i;
    283 		bv.bv_percent = -1;
    284 		bv.bv_seconds = 0;
    285 
    286 		rv = ioctl(devh, BIOCVOL, &bv);
    287 		if (rv == -1) {
    288 			warn("BIOCVOL");
    289 			return;
    290 		}
    291 
    292 		if (name && strcmp(name, bv.bv_dev) != 0)
    293 			continue;
    294 
    295 		if (!volheader) {
    296 			volheader = 1;
    297 			printf("%-7s %-10s %14s %-8s\n",
    298 			    "Volume", "Status", "Size", "Device");
    299 		}
    300 
    301 		percent[0] = '\0';
    302 		seconds[0] = '\0';
    303 		if (bv.bv_percent != -1)
    304 			snprintf(percent, sizeof percent,
    305 			    " %d%% done", bv.bv_percent);
    306 		if (bv.bv_seconds)
    307 			snprintf(seconds, sizeof seconds,
    308 			    " %u seconds", bv.bv_seconds);
    309 		switch (bv.bv_status) {
    310 		case BIOC_SVONLINE:
    311 			status = BIOC_SVONLINE_S;
    312 			break;
    313 		case BIOC_SVOFFLINE:
    314 			status = BIOC_SVOFFLINE_S;
    315 			break;
    316 		case BIOC_SVDEGRADED:
    317 			status = BIOC_SVDEGRADED_S;
    318 			break;
    319 		case BIOC_SVBUILDING:
    320 			status = BIOC_SVBUILDING_S;
    321 			break;
    322 		case BIOC_SVREBUILD:
    323 			status = BIOC_SVREBUILD_S;
    324 			break;
    325 		case BIOC_SVSCRUB:
    326 			status = BIOC_SVSCRUB_S;
    327 			break;
    328 		case BIOC_SVINVALID:
    329 		default:
    330 			status = BIOC_SVINVALID_S;
    331 		}
    332 
    333 		snprintf(volname, sizeof volname, "%s %u",
    334 		    bi.bi_dev, bv.bv_volid);
    335 
    336 		if (bv.bv_level == -1 && bv.bv_nodisk == 1) {
    337 			hotspare = 1;
    338 			unused = 0;
    339 		} else if (bv.bv_level == -2 && bv.bv_nodisk == 1) {
    340 			unused = 1;
    341 			hotspare = 0;
    342 		} else {
    343 			unused = 0;
    344 			hotspare = 0;
    345 
    346 			if (human)
    347 				humanize_number(size, 5,
    348 				    (int64_t)bv.bv_size, "", HN_AUTOSCALE,
    349 				    HN_B | HN_NOSPACE | HN_DECIMAL);
    350 			else
    351 				snprintf(size, sizeof size, "%14llu",
    352 				    (long long unsigned int)bv.bv_size);
    353 			printf("%7s %-10s %14s %-7s RAID%u%s%s\n",
    354 			    volname, status, size, bv.bv_dev,
    355 			    bv.bv_level, percent, seconds);
    356 		}
    357 
    358 		for (d = 0; d < bv.bv_nodisk; d++) {
    359 			memset(&bd, 0, sizeof(bd));
    360 			bd.bd_cookie = bl.bl_cookie;
    361 			bd.bd_diskid = d;
    362 			bd.bd_volid = i;
    363 
    364 			rv = ioctl(devh, BIOCDISK, &bd);
    365 			if (rv == -1) {
    366 				warn("BIOCDISK");
    367 				return;
    368 			}
    369 
    370 			switch (bd.bd_status) {
    371 			case BIOC_SDONLINE:
    372 				status = BIOC_SDONLINE_S;
    373 				break;
    374 			case BIOC_SDOFFLINE:
    375 				status = BIOC_SDOFFLINE_S;
    376 				break;
    377 			case BIOC_SDFAILED:
    378 				status = BIOC_SDFAILED_S;
    379 				break;
    380 			case BIOC_SDREBUILD:
    381 				status = BIOC_SDREBUILD_S;
    382 				break;
    383 			case BIOC_SDHOTSPARE:
    384 				status = BIOC_SDHOTSPARE_S;
    385 				break;
    386 			case BIOC_SDUNUSED:
    387 				status = BIOC_SDUNUSED_S;
    388 				break;
    389 			case BIOC_SDSCRUB:
    390 				status = BIOC_SDSCRUB_S;
    391 				break;
    392 			case BIOC_SDINVALID:
    393 			default:
    394 				status = BIOC_SDINVALID_S;
    395 			}
    396 
    397 			if (hotspare || unused)
    398 				;	/* use volname from parent volume */
    399 			else
    400 				snprintf(volname, sizeof volname, "    %3u",
    401 				    bd.bd_diskid);
    402 
    403 			if (human)
    404 				humanize_number(size, 5,
    405 				    bd.bd_size, "", HN_AUTOSCALE,
    406 				    HN_B | HN_NOSPACE | HN_DECIMAL);
    407 			else
    408 				snprintf(size, sizeof size, "%14llu",
    409 				    (long long unsigned int)bd.bd_size);
    410 			snprintf(scsiname, sizeof scsiname,
    411 			    "%u:%u.%u",
    412 			    bd.bd_channel, bd.bd_target, bd.bd_lun);
    413 			if (bd.bd_procdev[0])
    414 				strlcpy(encname, bd.bd_procdev, sizeof encname);
    415 			else
    416 				strlcpy(encname, "noencl", sizeof encname);
    417 			if (bd.bd_serial[0])
    418 				strlcpy(serial, bd.bd_serial, sizeof serial);
    419 			else
    420 				strlcpy(serial, "unknown serial", sizeof serial);
    421 
    422 			printf("%7s %-10s %14s %-7s %-6s <%s>\n",
    423 			    volname, status, size, scsiname, encname,
    424 			    bd.bd_vendor);
    425 			if (verbose)
    426 				printf("%7s %-10s %14s %-7s %-6s '%s'\n",
    427 				    "", "", "", "", "", serial);
    428 		}
    429 	}
    430 }
    431 
    432 void
    433 bio_alarm(char *arg)
    434 {
    435 	int rv;
    436 	struct bioc_alarm ba;
    437 
    438 	ba.ba_cookie = bl.bl_cookie;
    439 
    440 	switch (arg[0]) {
    441 	case 'q': /* silence alarm */
    442 		/* FALLTHROUGH */
    443 	case 's':
    444 		ba.ba_opcode = BIOC_SASILENCE;
    445 		break;
    446 
    447 	case 'e': /* enable alarm */
    448 		ba.ba_opcode = BIOC_SAENABLE;
    449 		break;
    450 
    451 	case 'd': /* disable alarm */
    452 		ba.ba_opcode = BIOC_SADISABLE;
    453 		break;
    454 
    455 	case 't': /* test alarm */
    456 		ba.ba_opcode = BIOC_SATEST;
    457 		break;
    458 
    459 	case 'g': /* get alarm state */
    460 		ba.ba_opcode = BIOC_GASTATUS;
    461 		break;
    462 
    463 	default:
    464 		warnx("invalid alarm function: %s", arg);
    465 		return;
    466 	}
    467 
    468 	rv = ioctl(devh, BIOCALARM, &ba);
    469 	if (rv == -1) {
    470 		warn("BIOCALARM");
    471 		return;
    472 	}
    473 
    474 	if (arg[0] == 'g') {
    475 		printf("alarm is currently %s\n",
    476 		    ba.ba_status ? "enabled" : "disabled");
    477 
    478 	}
    479 }
    480 
    481 void
    482 bio_setstate(char *arg)
    483 {
    484 	struct bioc_setstate	bs;
    485 	struct locator		location;
    486 	const char		*errstr;
    487 	int			rv;
    488 
    489 	errstr = str2locator(arg, &location);
    490 	if (errstr)
    491 		errx(1, "Target %s: %s", arg, errstr);
    492 
    493 	bs.bs_cookie = bl.bl_cookie;
    494 	bs.bs_status = BIOC_SSHOTSPARE;
    495 	bs.bs_channel = location.channel;
    496 	bs.bs_target = location.target;
    497 	bs.bs_lun = location.lun;
    498 
    499 	rv = ioctl(devh, BIOCSETSTATE, &bs);
    500 	if (rv == -1) {
    501 		warn("BIOCSETSTATE");
    502 		return;
    503 	}
    504 }
    505 
    506 void
    507 bio_setblink(char *name, char *arg, int blink)
    508 {
    509 	struct locator		location;
    510 	struct bioc_inq		bi;
    511 	struct bioc_vol		bv;
    512 	struct bioc_disk	bd;
    513 	struct bioc_blink	bb;
    514 	const char		*errstr;
    515 	int			v, d, rv;
    516 
    517 	errstr = str2locator(arg, &location);
    518 	if (errstr)
    519 		errx(1, "Target %s: %s", arg, errstr);
    520 
    521 	/* try setting blink on the device directly */
    522 	memset(&bb, 0, sizeof(bb));
    523 	bb.bb_cookie = bl.bl_cookie;
    524 	bb.bb_status = blink;
    525 	bb.bb_target = location.target;
    526 	bb.bb_channel = location.channel;
    527 	rv = ioctl(devh, BIOCBLINK, &bb);
    528 	if (rv == 0)
    529 		return;
    530 
    531 	/* if the blink didnt work, try to find something that will */
    532 
    533 	memset(&bi, 0, sizeof(bi));
    534 	bi.bi_cookie = bl.bl_cookie;
    535 	rv = ioctl(devh, BIOCINQ, &bi);
    536 	if (rv == -1) {
    537 		warn("BIOCINQ");
    538 		return;
    539 	}
    540 
    541 	for (v = 0; v < bi.bi_novol; v++) {
    542 		memset(&bv, 0, sizeof(bv));
    543 		bv.bv_cookie = bl.bl_cookie;
    544 		bv.bv_volid = v;
    545 		rv = ioctl(devh, BIOCVOL, &bv);
    546 		if (rv == -1) {
    547 			warn("BIOCVOL");
    548 			return;
    549 		}
    550 
    551 		if (name && strcmp(name, bv.bv_dev) != 0)
    552 			continue;
    553 
    554 		for (d = 0; d < bv.bv_nodisk; d++) {
    555 			memset(&bd, 0, sizeof(bd));
    556 			bd.bd_cookie = bl.bl_cookie;
    557 			bd.bd_volid = v;
    558 			bd.bd_diskid = d;
    559 
    560 			rv = ioctl(devh, BIOCDISK, &bd);
    561 			if (rv == -1) {
    562 				warn("BIOCDISK");
    563 				return;
    564 			}
    565 
    566 			if (bd.bd_channel == location.channel &&
    567 			    bd.bd_target == location.target &&
    568 			    bd.bd_lun == location.lun) {
    569 				if (bd.bd_procdev[0] != '\0') {
    570 					bio_blink(bd.bd_procdev,
    571 					    location.target, blink);
    572 				} else
    573 					warnx("Disk %s is not in an enclosure", arg);
    574 				return;
    575 			}
    576 		}
    577 	}
    578 
    579 	warnx("Disk %s does not exist", arg);
    580 	return;
    581 }
    582 
    583 void
    584 bio_blink(char *enclosure, int target, int blinktype)
    585 {
    586 	int			bioh;
    587 	struct bio_locate	bio;
    588 	struct bioc_blink	blink;
    589 	int			rv;
    590 
    591 	bioh = open("/dev/bio", O_RDWR);
    592 	if (bioh == -1)
    593 		err(1, "Can't open %s", "/dev/bio");
    594 
    595 	bio.bl_name = enclosure;
    596 	rv = ioctl(bioh, BIOCLOCATE, &bio);
    597 	if (rv == -1)
    598 		errx(1, "Can't locate %s device via %s", enclosure, "/dev/bio");
    599 
    600 	memset(&blink, 0, sizeof(blink));
    601 	blink.bb_cookie = bio.bl_cookie;
    602 	blink.bb_status = blinktype;
    603 	blink.bb_target = target;
    604 
    605 	rv = ioctl(bioh, BIOCBLINK, &blink);
    606 	if (rv == -1)
    607 		warn("BIOCBLINK");
    608 
    609 	close(bioh);
    610 }
    611 
    612 void
    613 bio_createraid(u_int16_t level, char *dev_list)
    614 {
    615 	struct bioc_createraid	create;
    616 	int			rv;
    617 	u_int16_t		min_disks = 0;
    618 
    619 	if (debug)
    620 		printf("bio_createraid\n");
    621 
    622 	if (!dev_list)
    623 		errx(1, "no devices specified");
    624 
    625 	switch (level) {
    626 	case 0:
    627 		min_disks = 1;
    628 		break;
    629 	case 1:
    630 		min_disks = 2;
    631 		break;
    632 	default:
    633 		errx(1, "unsuported raid level");
    634 	}
    635 
    636 	/* XXX validate device list for real */
    637 #if 0
    638 	if (strncmp(dev_list, "sd", 2) == 0 && strlen(dev_list) > 2 &&
    639 	    isdigit(dev_list[2])) {
    640 	    	if (strlen(dev_list) != 3)
    641 			errx(1, "only one device supported");
    642 
    643 		if (debug)
    644 			printf("bio_createraid: dev_list: %s\n", dev_list);
    645 	}
    646 	else
    647 		errx(1, "no sd device specified");
    648 #endif
    649 
    650 	memset(&create, 0, sizeof(create));
    651 	create.bc_cookie = bl.bl_cookie;
    652 	create.bc_level = level;
    653 	create.bc_dev_list_len = strlen(dev_list);
    654 	create.bc_dev_list = dev_list;
    655 
    656 	rv = ioctl(devh, BIOCCREATERAID, &create);
    657 	if (rv == -1) {
    658 		warn("BIOCCREATERAID");
    659 		return;
    660 	}
    661 }
    662