Home | History | Annotate | Line # | Download | only in bioctl
bioctl.c revision 1.1.8.2
      1 /* $NetBSD: bioctl.c,v 1.1.8.2 2007/10/15 05:09:51 riz 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.8.2 2007/10/15 05:09:51 riz 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 /* $NetBSD: bioctl.c,v 1.1.8.2 2007/10/15 05:09:51 riz Exp $ */
    663 /* $OpenBSD: bioctl.c,v 1.52 2007/03/20 15:26:06 jmc Exp $       */
    664 
    665 /*
    666  * Copyright (c) 2004, 2005 Marco Peereboom
    667  * All rights reserved.
    668  *
    669  * Redistribution and use in source and binary forms, with or without
    670  * modification, are permitted provided that the following conditions
    671  * are met:
    672  * 1. Redistributions of source code must retain the above copyright
    673  *    notice, this list of conditions and the following disclaimer.
    674  * 2. Redistributions in binary form must reproduce the above copyright
    675  *    notice, this list of conditions and the following disclaimer in the
    676  *    documentation and/or other materials provided with the distribution.
    677  *
    678  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
    679  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    680  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    681  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
    682  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    683  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
    684  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    685  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    686  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    687  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    688  * SUCH DAMAGE.
    689  *
    690  */
    691 #include <sys/cdefs.h>
    692 
    693 #ifndef lint
    694 __RCSID("$NetBSD: bioctl.c,v 1.1.8.2 2007/10/15 05:09:51 riz Exp $");
    695 #endif
    696 
    697 #include <sys/ioctl.h>
    698 #include <sys/param.h>
    699 #include <sys/queue.h>
    700 // #include <scsi/scsipi_disk.h>
    701 // #include <scsi/scsipi_all.h>
    702 #include <dev/biovar.h>
    703 
    704 #include <errno.h>
    705 #include <err.h>
    706 #include <fcntl.h>
    707 #include <util.h>
    708 #include <stdio.h>
    709 #include <stdlib.h>
    710 #include <string.h>
    711 #include <unistd.h>
    712 #include <ctype.h>
    713 #include <util.h>
    714 #include "strtonum.h"
    715 
    716 struct locator {
    717 	int channel;
    718 	int target;
    719 	int lun;
    720 };
    721 
    722 void usage(void);
    723 const char *str2locator(const char *, struct locator *);
    724 void cleanup(void);
    725 
    726 void bio_inq(char *);
    727 void bio_alarm(char *);
    728 void bio_setstate(char *);
    729 void bio_setblink(char *, char *, int);
    730 void bio_blink(char *, int, int);
    731 void bio_createraid(u_int16_t, char *);
    732 
    733 int devh = -1;
    734 int debug;
    735 int human;
    736 int verbose;
    737 
    738 struct bio_locate bl;
    739 
    740 int
    741 main(int argc, char *argv[])
    742 {
    743 	extern char *optarg;
    744 	u_int64_t func = 0;
    745 	/* u_int64_t subfunc = 0; XXX */
    746 	char *bioc_dev = NULL, *sd_dev = NULL;
    747 	char /* *realname = NULL, XXX */ *al_arg = NULL;
    748 	char *bl_arg = NULL, *dev_list = NULL;
    749 	int ch, rv, blink = 0; /* XXX GCC */
    750 	u_int16_t cr_level = 0; /* XXX GCC */
    751 
    752 	if (argc < 2)
    753 		usage();
    754 
    755 	while ((ch = getopt(argc, argv, "b:c:l:u:H:ha:Div")) != -1) {
    756 		switch (ch) {
    757 		case 'a': /* alarm */
    758 			func |= BIOC_ALARM;
    759 			al_arg = optarg;
    760 			break;
    761 		case 'b': /* blink */
    762 			func |= BIOC_BLINK;
    763 			blink = BIOC_SBBLINK;
    764 			bl_arg = optarg;
    765 			break;
    766 		case 'c': /* create */
    767 			func |= BIOC_CREATERAID;
    768 			cr_level = atoi(optarg);
    769 			break;
    770 		case 'u': /* unblink */
    771 			func |= BIOC_BLINK;
    772 			blink = BIOC_SBUNBLINK;
    773 			bl_arg = optarg;
    774 			break;
    775 		case 'D': /* debug */
    776 			debug = 1;
    777 			break;
    778 		case 'H': /* set hotspare */
    779 			func |= BIOC_SETSTATE;
    780 			al_arg = optarg;
    781 			break;
    782 		case 'h':
    783 			human = 1;
    784 			break;
    785 		case 'i': /* inquiry */
    786 			func |= BIOC_INQ;
    787 			break;
    788 		case 'l': /* device list */
    789 			func |= BIOC_DEVLIST;
    790 			dev_list = optarg;
    791 			break;
    792 		case 'v':
    793 			verbose = 1;
    794 			break;
    795 		default:
    796 			usage();
    797 			/* NOTREACHED */
    798 		}
    799 	}
    800 	argc -= optind;
    801 	argv += optind;
    802 
    803 	if (argc != 1)
    804 		usage();
    805 
    806 	if (func == 0)
    807 		func |= BIOC_INQ;
    808 #if 0
    809 	/* if at least glob sd[0-9]*, it is a drive identifier */
    810 	if (strncmp(argv[0], "sd", 2) == 0 && strlen(argv[0]) > 2 &&
    811 	    isdigit((int)argv[0][2]))
    812 		sd_dev = argv[0];
    813 	else
    814 #endif
    815 		bioc_dev = argv[0];
    816 
    817 	if (bioc_dev) {
    818 		devh = open("/dev/bio", O_RDWR);
    819 		if (devh == -1)
    820 			err(1, "Can't open %s", "/dev/bio");
    821 
    822 		bl.bl_name = bioc_dev;
    823 		rv = ioctl(devh, BIOCLOCATE, &bl);
    824 		if (rv == -1)
    825 			errx(1, "Can't locate %s device via %s",
    826 			    bl.bl_name, "/dev/bio");
    827 	}
    828 #if 0
    829 	else if (sd_dev) {
    830 		devh = opendev(sd_dev, O_RDWR, OPENDEV_PART, &realname);
    831 		if (devh == -1)
    832 			err(1, "Can't open %s", sd_dev);
    833 	}
    834 #endif
    835 	else
    836 		errx(1, "need -d or -f parameter");
    837 
    838 	if (debug)
    839 		warnx("cookie = %p", bl.bl_cookie);
    840 
    841 	if (func & BIOC_INQ) {
    842 		bio_inq(sd_dev);
    843 	} else if (func == BIOC_ALARM) {
    844 		bio_alarm(al_arg);
    845 	} else if (func == BIOC_BLINK) {
    846 		bio_setblink(sd_dev, bl_arg, blink);
    847 	} else if (func == BIOC_SETSTATE) {
    848 		bio_setstate(al_arg);
    849 	} else if (func & BIOC_CREATERAID || func & BIOC_DEVLIST) {
    850 		if (!(func & BIOC_CREATERAID))
    851 			errx(1, "need -c parameter");
    852 		if (!(func & BIOC_DEVLIST))
    853 			errx(1, "need -l parameter");
    854 		if (sd_dev)
    855 			errx(1, "can't use sd device");
    856 		bio_createraid(cr_level, dev_list);
    857 	}
    858 
    859 	return (0);
    860 }
    861 
    862 void
    863 usage(void)
    864 {
    865 	extern char *__progname;
    866 
    867 	fprintf(stderr,
    868 		"usage: %s [-Dhiv] [-a alarm-function] "
    869 		"[-b channel:target[.lun]]\n"
    870 		"\t[-c raidlevel] [-H channel:target[.lun]]\n"
    871 		"\t[-l special[,special[,...]]] "
    872 		"[-u channel:target[.lun]] device\n", __progname);
    873 
    874 	exit(1);
    875 }
    876 
    877 const char *
    878 str2locator(const char *string, struct locator *location)
    879 {
    880 	const char *errstr;
    881 	char parse[80], *targ, *lun;
    882 
    883 	strlcpy(parse, string, sizeof parse);
    884 	targ = strchr(parse, ':');
    885 	if (targ == NULL)
    886 		return ("target not specified");
    887 	*targ++ = '\0';
    888 
    889 	lun = strchr(targ, '.');
    890 	if (lun != NULL) {
    891 		*lun++ = '\0';
    892 		location->lun = strtonum(lun, 0, 256, &errstr);
    893 		if (errstr)
    894 			return (errstr);
    895 	} else
    896 		location->lun = 0;
    897 
    898 	location->target = strtonum(targ, 0, 256, &errstr);
    899 	if (errstr)
    900 		return (errstr);
    901 	location->channel = strtonum(parse, 0, 256, &errstr);
    902 	if (errstr)
    903 		return (errstr);
    904 	return (NULL);
    905 }
    906 
    907 void
    908 bio_inq(char *name)
    909 {
    910 	const char *status;
    911 	char size[64], scsiname[16], volname[32];
    912 	char percent[10], seconds[20];
    913 	int rv, i, d, volheader, hotspare, unused;
    914 	char encname[16], serial[32];
    915 	struct bioc_disk bd;
    916 	struct bioc_inq bi;
    917 	struct bioc_vol bv;
    918 
    919 	memset(&bi, 0, sizeof(bi));
    920 
    921 	if (debug)
    922 		printf("bio_inq\n");
    923 
    924 	bi.bi_cookie = bl.bl_cookie;
    925 
    926 	rv = ioctl(devh, BIOCINQ, &bi);
    927 	if (rv == -1) {
    928 		warn("BIOCINQ");
    929 		return;
    930 	}
    931 
    932 	if (debug)
    933 		printf("bio_inq { %p, %s, %d, %d }\n",
    934 		    bi.bi_cookie,
    935 		    bi.bi_dev,
    936 		    bi.bi_novol,
    937 		    bi.bi_nodisk);
    938 
    939 	volheader = 0;
    940 	for (i = 0; i < bi.bi_novol; i++) {
    941 		memset(&bv, 0, sizeof(bv));
    942 		bv.bv_cookie = bl.bl_cookie;
    943 		bv.bv_volid = i;
    944 		bv.bv_percent = -1;
    945 		bv.bv_seconds = 0;
    946 
    947 		rv = ioctl(devh, BIOCVOL, &bv);
    948 		if (rv == -1) {
    949 			warn("BIOCVOL");
    950 			return;
    951 		}
    952 
    953 		if (name && strcmp(name, bv.bv_dev) != 0)
    954 			continue;
    955 
    956 		if (!volheader) {
    957 			volheader = 1;
    958 			printf("%-7s %-10s %14s %-8s\n",
    959 			    "Volume", "Status", "Size", "Device");
    960 		}
    961 
    962 		percent[0] = '\0';
    963 		seconds[0] = '\0';
    964 		if (bv.bv_percent != -1)
    965 			snprintf(percent, sizeof percent,
    966 			    " %d%% done", bv.bv_percent);
    967 		if (bv.bv_seconds)
    968 			snprintf(seconds, sizeof seconds,
    969 			    " %u seconds", bv.bv_seconds);
    970 		switch (bv.bv_status) {
    971 		case BIOC_SVONLINE:
    972 			status = BIOC_SVONLINE_S;
    973 			break;
    974 		case BIOC_SVOFFLINE:
    975 			status = BIOC_SVOFFLINE_S;
    976 			break;
    977 		case BIOC_SVDEGRADED:
    978 			status = BIOC_SVDEGRADED_S;
    979 			break;
    980 		case BIOC_SVBUILDING:
    981 			status = BIOC_SVBUILDING_S;
    982 			break;
    983 		case BIOC_SVREBUILD:
    984 			status = BIOC_SVREBUILD_S;
    985 			break;
    986 		case BIOC_SVSCRUB:
    987 			status = BIOC_SVSCRUB_S;
    988 			break;
    989 		case BIOC_SVINVALID:
    990 		default:
    991 			status = BIOC_SVINVALID_S;
    992 		}
    993 
    994 		snprintf(volname, sizeof volname, "%s %u",
    995 		    bi.bi_dev, bv.bv_volid);
    996 
    997 		if (bv.bv_level == -1 && bv.bv_nodisk == 1) {
    998 			hotspare = 1;
    999 			unused = 0;
   1000 		} else if (bv.bv_level == -2 && bv.bv_nodisk == 1) {
   1001 			unused = 1;
   1002 			hotspare = 0;
   1003 		} else {
   1004 			unused = 0;
   1005 			hotspare = 0;
   1006 
   1007 			if (human)
   1008 				humanize_number(size, 5,
   1009 				    (int64_t)bv.bv_size, "", HN_AUTOSCALE,
   1010 				    HN_B | HN_NOSPACE | HN_DECIMAL);
   1011 			else
   1012 				snprintf(size, sizeof size, "%14llu",
   1013 				    (long long unsigned int)bv.bv_size);
   1014 			printf("%7s %-10s %14s %-7s RAID%u%s%s\n",
   1015 			    volname, status, size, bv.bv_dev,
   1016 			    bv.bv_level, percent, seconds);
   1017 		}
   1018 
   1019 		for (d = 0; d < bv.bv_nodisk; d++) {
   1020 			memset(&bd, 0, sizeof(bd));
   1021 			bd.bd_cookie = bl.bl_cookie;
   1022 			bd.bd_diskid = d;
   1023 			bd.bd_volid = i;
   1024 
   1025 			rv = ioctl(devh, BIOCDISK, &bd);
   1026 			if (rv == -1) {
   1027 				warn("BIOCDISK");
   1028 				return;
   1029 			}
   1030 
   1031 			switch (bd.bd_status) {
   1032 			case BIOC_SDONLINE:
   1033 				status = BIOC_SDONLINE_S;
   1034 				break;
   1035 			case BIOC_SDOFFLINE:
   1036 				status = BIOC_SDOFFLINE_S;
   1037 				break;
   1038 			case BIOC_SDFAILED:
   1039 				status = BIOC_SDFAILED_S;
   1040 				break;
   1041 			case BIOC_SDREBUILD:
   1042 				status = BIOC_SDREBUILD_S;
   1043 				break;
   1044 			case BIOC_SDHOTSPARE:
   1045 				status = BIOC_SDHOTSPARE_S;
   1046 				break;
   1047 			case BIOC_SDUNUSED:
   1048 				status = BIOC_SDUNUSED_S;
   1049 				break;
   1050 			case BIOC_SDSCRUB:
   1051 				status = BIOC_SDSCRUB_S;
   1052 				break;
   1053 			case BIOC_SDINVALID:
   1054 			default:
   1055 				status = BIOC_SDINVALID_S;
   1056 			}
   1057 
   1058 			if (hotspare || unused)
   1059 				;	/* use volname from parent volume */
   1060 			else
   1061 				snprintf(volname, sizeof volname, "    %3u",
   1062 				    bd.bd_diskid);
   1063 
   1064 			if (human)
   1065 				humanize_number(size, 5,
   1066 				    bd.bd_size, "", HN_AUTOSCALE,
   1067 				    HN_B | HN_NOSPACE | HN_DECIMAL);
   1068 			else
   1069 				snprintf(size, sizeof size, "%14llu",
   1070 				    (long long unsigned int)bd.bd_size);
   1071 			snprintf(scsiname, sizeof scsiname,
   1072 			    "%u:%u.%u",
   1073 			    bd.bd_channel, bd.bd_target, bd.bd_lun);
   1074 			if (bd.bd_procdev[0])
   1075 				strlcpy(encname, bd.bd_procdev, sizeof encname);
   1076 			else
   1077 				strlcpy(encname, "noencl", sizeof encname);
   1078 			if (bd.bd_serial[0])
   1079 				strlcpy(serial, bd.bd_serial, sizeof serial);
   1080 			else
   1081 				strlcpy(serial, "unknown serial", sizeof serial);
   1082 
   1083 			printf("%7s %-10s %14s %-7s %-6s <%s>\n",
   1084 			    volname, status, size, scsiname, encname,
   1085 			    bd.bd_vendor);
   1086 			if (verbose)
   1087 				printf("%7s %-10s %14s %-7s %-6s '%s'\n",
   1088 				    "", "", "", "", "", serial);
   1089 		}
   1090 	}
   1091 }
   1092 
   1093 void
   1094 bio_alarm(char *arg)
   1095 {
   1096 	int rv;
   1097 	struct bioc_alarm ba;
   1098 
   1099 	ba.ba_cookie = bl.bl_cookie;
   1100 
   1101 	switch (arg[0]) {
   1102 	case 'q': /* silence alarm */
   1103 		/* FALLTHROUGH */
   1104 	case 's':
   1105 		ba.ba_opcode = BIOC_SASILENCE;
   1106 		break;
   1107 
   1108 	case 'e': /* enable alarm */
   1109 		ba.ba_opcode = BIOC_SAENABLE;
   1110 		break;
   1111 
   1112 	case 'd': /* disable alarm */
   1113 		ba.ba_opcode = BIOC_SADISABLE;
   1114 		break;
   1115 
   1116 	case 't': /* test alarm */
   1117 		ba.ba_opcode = BIOC_SATEST;
   1118 		break;
   1119 
   1120 	case 'g': /* get alarm state */
   1121 		ba.ba_opcode = BIOC_GASTATUS;
   1122 		break;
   1123 
   1124 	default:
   1125 		warnx("invalid alarm function: %s", arg);
   1126 		return;
   1127 	}
   1128 
   1129 	rv = ioctl(devh, BIOCALARM, &ba);
   1130 	if (rv == -1) {
   1131 		warn("BIOCALARM");
   1132 		return;
   1133 	}
   1134 
   1135 	if (arg[0] == 'g') {
   1136 		printf("alarm is currently %s\n",
   1137 		    ba.ba_status ? "enabled" : "disabled");
   1138 
   1139 	}
   1140 }
   1141 
   1142 void
   1143 bio_setstate(char *arg)
   1144 {
   1145 	struct bioc_setstate	bs;
   1146 	struct locator		location;
   1147 	const char		*errstr;
   1148 	int			rv;
   1149 
   1150 	errstr = str2locator(arg, &location);
   1151 	if (errstr)
   1152 		errx(1, "Target %s: %s", arg, errstr);
   1153 
   1154 	bs.bs_cookie = bl.bl_cookie;
   1155 	bs.bs_status = BIOC_SSHOTSPARE;
   1156 	bs.bs_channel = location.channel;
   1157 	bs.bs_target = location.target;
   1158 	bs.bs_lun = location.lun;
   1159 
   1160 	rv = ioctl(devh, BIOCSETSTATE, &bs);
   1161 	if (rv == -1) {
   1162 		warn("BIOCSETSTATE");
   1163 		return;
   1164 	}
   1165 }
   1166 
   1167 void
   1168 bio_setblink(char *name, char *arg, int blink)
   1169 {
   1170 	struct locator		location;
   1171 	struct bioc_inq		bi;
   1172 	struct bioc_vol		bv;
   1173 	struct bioc_disk	bd;
   1174 	struct bioc_blink	bb;
   1175 	const char		*errstr;
   1176 	int			v, d, rv;
   1177 
   1178 	errstr = str2locator(arg, &location);
   1179 	if (errstr)
   1180 		errx(1, "Target %s: %s", arg, errstr);
   1181 
   1182 	/* try setting blink on the device directly */
   1183 	memset(&bb, 0, sizeof(bb));
   1184 	bb.bb_cookie = bl.bl_cookie;
   1185 	bb.bb_status = blink;
   1186 	bb.bb_target = location.target;
   1187 	bb.bb_channel = location.channel;
   1188 	rv = ioctl(devh, BIOCBLINK, &bb);
   1189 	if (rv == 0)
   1190 		return;
   1191 
   1192 	/* if the blink didnt work, try to find something that will */
   1193 
   1194 	memset(&bi, 0, sizeof(bi));
   1195 	bi.bi_cookie = bl.bl_cookie;
   1196 	rv = ioctl(devh, BIOCINQ, &bi);
   1197 	if (rv == -1) {
   1198 		warn("BIOCINQ");
   1199 		return;
   1200 	}
   1201 
   1202 	for (v = 0; v < bi.bi_novol; v++) {
   1203 		memset(&bv, 0, sizeof(bv));
   1204 		bv.bv_cookie = bl.bl_cookie;
   1205 		bv.bv_volid = v;
   1206 		rv = ioctl(devh, BIOCVOL, &bv);
   1207 		if (rv == -1) {
   1208 			warn("BIOCVOL");
   1209 			return;
   1210 		}
   1211 
   1212 		if (name && strcmp(name, bv.bv_dev) != 0)
   1213 			continue;
   1214 
   1215 		for (d = 0; d < bv.bv_nodisk; d++) {
   1216 			memset(&bd, 0, sizeof(bd));
   1217 			bd.bd_cookie = bl.bl_cookie;
   1218 			bd.bd_volid = v;
   1219 			bd.bd_diskid = d;
   1220 
   1221 			rv = ioctl(devh, BIOCDISK, &bd);
   1222 			if (rv == -1) {
   1223 				warn("BIOCDISK");
   1224 				return;
   1225 			}
   1226 
   1227 			if (bd.bd_channel == location.channel &&
   1228 			    bd.bd_target == location.target &&
   1229 			    bd.bd_lun == location.lun) {
   1230 				if (bd.bd_procdev[0] != '\0') {
   1231 					bio_blink(bd.bd_procdev,
   1232 					    location.target, blink);
   1233 				} else
   1234 					warnx("Disk %s is not in an enclosure", arg);
   1235 				return;
   1236 			}
   1237 		}
   1238 	}
   1239 
   1240 	warnx("Disk %s does not exist", arg);
   1241 	return;
   1242 }
   1243 
   1244 void
   1245 bio_blink(char *enclosure, int target, int blinktype)
   1246 {
   1247 	int			bioh;
   1248 	struct bio_locate	bio;
   1249 	struct bioc_blink	blink;
   1250 	int			rv;
   1251 
   1252 	bioh = open("/dev/bio", O_RDWR);
   1253 	if (bioh == -1)
   1254 		err(1, "Can't open %s", "/dev/bio");
   1255 
   1256 	bio.bl_name = enclosure;
   1257 	rv = ioctl(bioh, BIOCLOCATE, &bio);
   1258 	if (rv == -1)
   1259 		errx(1, "Can't locate %s device via %s", enclosure, "/dev/bio");
   1260 
   1261 	memset(&blink, 0, sizeof(blink));
   1262 	blink.bb_cookie = bio.bl_cookie;
   1263 	blink.bb_status = blinktype;
   1264 	blink.bb_target = target;
   1265 
   1266 	rv = ioctl(bioh, BIOCBLINK, &blink);
   1267 	if (rv == -1)
   1268 		warn("BIOCBLINK");
   1269 
   1270 	close(bioh);
   1271 }
   1272 
   1273 void
   1274 bio_createraid(u_int16_t level, char *dev_list)
   1275 {
   1276 	struct bioc_createraid	create;
   1277 	int			rv;
   1278 	u_int16_t		min_disks = 0;
   1279 
   1280 	if (debug)
   1281 		printf("bio_createraid\n");
   1282 
   1283 	if (!dev_list)
   1284 		errx(1, "no devices specified");
   1285 
   1286 	switch (level) {
   1287 	case 0:
   1288 		min_disks = 1;
   1289 		break;
   1290 	case 1:
   1291 		min_disks = 2;
   1292 		break;
   1293 	default:
   1294 		errx(1, "unsuported raid level");
   1295 	}
   1296 
   1297 	/* XXX validate device list for real */
   1298 #if 0
   1299 	if (strncmp(dev_list, "sd", 2) == 0 && strlen(dev_list) > 2 &&
   1300 	    isdigit(dev_list[2])) {
   1301 	    	if (strlen(dev_list) != 3)
   1302 			errx(1, "only one device supported");
   1303 
   1304 		if (debug)
   1305 			printf("bio_createraid: dev_list: %s\n", dev_list);
   1306 	}
   1307 	else
   1308 		errx(1, "no sd device specified");
   1309 #endif
   1310 
   1311 	memset(&create, 0, sizeof(create));
   1312 	create.bc_cookie = bl.bl_cookie;
   1313 	create.bc_level = level;
   1314 	create.bc_dev_list_len = strlen(dev_list);
   1315 	create.bc_dev_list = dev_list;
   1316 
   1317 	rv = ioctl(devh, BIOCCREATERAID, &create);
   1318 	if (rv == -1) {
   1319 		warn("BIOCCREATERAID");
   1320 		return;
   1321 	}
   1322 }
   1323 /* $NetBSD: bioctl.c,v 1.1.8.2 2007/10/15 05:09:51 riz Exp $ */
   1324 /* $OpenBSD: bioctl.c,v 1.52 2007/03/20 15:26:06 jmc Exp $       */
   1325 
   1326 /*
   1327  * Copyright (c) 2004, 2005 Marco Peereboom
   1328  * All rights reserved.
   1329  *
   1330  * Redistribution and use in source and binary forms, with or without
   1331  * modification, are permitted provided that the following conditions
   1332  * are met:
   1333  * 1. Redistributions of source code must retain the above copyright
   1334  *    notice, this list of conditions and the following disclaimer.
   1335  * 2. Redistributions in binary form must reproduce the above copyright
   1336  *    notice, this list of conditions and the following disclaimer in the
   1337  *    documentation and/or other materials provided with the distribution.
   1338  *
   1339  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
   1340  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   1341  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   1342  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
   1343  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   1344  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   1345  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   1346  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   1347  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   1348  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   1349  * SUCH DAMAGE.
   1350  *
   1351  */
   1352 #include <sys/cdefs.h>
   1353 
   1354 #ifndef lint
   1355 __RCSID("$NetBSD: bioctl.c,v 1.1.8.2 2007/10/15 05:09:51 riz Exp $");
   1356 #endif
   1357 
   1358 #include <sys/ioctl.h>
   1359 #include <sys/param.h>
   1360 #include <sys/queue.h>
   1361 // #include <scsi/scsipi_disk.h>
   1362 // #include <scsi/scsipi_all.h>
   1363 #include <dev/biovar.h>
   1364 
   1365 #include <errno.h>
   1366 #include <err.h>
   1367 #include <fcntl.h>
   1368 #include <util.h>
   1369 #include <stdio.h>
   1370 #include <stdlib.h>
   1371 #include <string.h>
   1372 #include <unistd.h>
   1373 #include <ctype.h>
   1374 #include <util.h>
   1375 #include "strtonum.h"
   1376 
   1377 struct locator {
   1378 	int channel;
   1379 	int target;
   1380 	int lun;
   1381 };
   1382 
   1383 void usage(void);
   1384 const char *str2locator(const char *, struct locator *);
   1385 void cleanup(void);
   1386 
   1387 void bio_inq(char *);
   1388 void bio_alarm(char *);
   1389 void bio_setstate(char *);
   1390 void bio_setblink(char *, char *, int);
   1391 void bio_blink(char *, int, int);
   1392 void bio_createraid(u_int16_t, char *);
   1393 
   1394 int devh = -1;
   1395 int debug;
   1396 int human;
   1397 int verbose;
   1398 
   1399 struct bio_locate bl;
   1400 
   1401 int
   1402 main(int argc, char *argv[])
   1403 {
   1404 	extern char *optarg;
   1405 	u_int64_t func = 0;
   1406 	/* u_int64_t subfunc = 0; XXX */
   1407 	char *bioc_dev = NULL, *sd_dev = NULL;
   1408 	char /* *realname = NULL, XXX */ *al_arg = NULL;
   1409 	char *bl_arg = NULL, *dev_list = NULL;
   1410 	int ch, rv, blink = 0; /* XXX GCC */
   1411 	u_int16_t cr_level = 0; /* XXX GCC */
   1412 
   1413 	if (argc < 2)
   1414 		usage();
   1415 
   1416 	while ((ch = getopt(argc, argv, "b:c:l:u:H:ha:Div")) != -1) {
   1417 		switch (ch) {
   1418 		case 'a': /* alarm */
   1419 			func |= BIOC_ALARM;
   1420 			al_arg = optarg;
   1421 			break;
   1422 		case 'b': /* blink */
   1423 			func |= BIOC_BLINK;
   1424 			blink = BIOC_SBBLINK;
   1425 			bl_arg = optarg;
   1426 			break;
   1427 		case 'c': /* create */
   1428 			func |= BIOC_CREATERAID;
   1429 			cr_level = atoi(optarg);
   1430 			break;
   1431 		case 'u': /* unblink */
   1432 			func |= BIOC_BLINK;
   1433 			blink = BIOC_SBUNBLINK;
   1434 			bl_arg = optarg;
   1435 			break;
   1436 		case 'D': /* debug */
   1437 			debug = 1;
   1438 			break;
   1439 		case 'H': /* set hotspare */
   1440 			func |= BIOC_SETSTATE;
   1441 			al_arg = optarg;
   1442 			break;
   1443 		case 'h':
   1444 			human = 1;
   1445 			break;
   1446 		case 'i': /* inquiry */
   1447 			func |= BIOC_INQ;
   1448 			break;
   1449 		case 'l': /* device list */
   1450 			func |= BIOC_DEVLIST;
   1451 			dev_list = optarg;
   1452 			break;
   1453 		case 'v':
   1454 			verbose = 1;
   1455 			break;
   1456 		default:
   1457 			usage();
   1458 			/* NOTREACHED */
   1459 		}
   1460 	}
   1461 	argc -= optind;
   1462 	argv += optind;
   1463 
   1464 	if (argc != 1)
   1465 		usage();
   1466 
   1467 	if (func == 0)
   1468 		func |= BIOC_INQ;
   1469 #if 0
   1470 	/* if at least glob sd[0-9]*, it is a drive identifier */
   1471 	if (strncmp(argv[0], "sd", 2) == 0 && strlen(argv[0]) > 2 &&
   1472 	    isdigit((int)argv[0][2]))
   1473 		sd_dev = argv[0];
   1474 	else
   1475 #endif
   1476 		bioc_dev = argv[0];
   1477 
   1478 	if (bioc_dev) {
   1479 		devh = open("/dev/bio", O_RDWR);
   1480 		if (devh == -1)
   1481 			err(1, "Can't open %s", "/dev/bio");
   1482 
   1483 		bl.bl_name = bioc_dev;
   1484 		rv = ioctl(devh, BIOCLOCATE, &bl);
   1485 		if (rv == -1)
   1486 			errx(1, "Can't locate %s device via %s",
   1487 			    bl.bl_name, "/dev/bio");
   1488 	}
   1489 #if 0
   1490 	else if (sd_dev) {
   1491 		devh = opendev(sd_dev, O_RDWR, OPENDEV_PART, &realname);
   1492 		if (devh == -1)
   1493 			err(1, "Can't open %s", sd_dev);
   1494 	}
   1495 #endif
   1496 	else
   1497 		errx(1, "need -d or -f parameter");
   1498 
   1499 	if (debug)
   1500 		warnx("cookie = %p", bl.bl_cookie);
   1501 
   1502 	if (func & BIOC_INQ) {
   1503 		bio_inq(sd_dev);
   1504 	} else if (func == BIOC_ALARM) {
   1505 		bio_alarm(al_arg);
   1506 	} else if (func == BIOC_BLINK) {
   1507 		bio_setblink(sd_dev, bl_arg, blink);
   1508 	} else if (func == BIOC_SETSTATE) {
   1509 		bio_setstate(al_arg);
   1510 	} else if (func & BIOC_CREATERAID || func & BIOC_DEVLIST) {
   1511 		if (!(func & BIOC_CREATERAID))
   1512 			errx(1, "need -c parameter");
   1513 		if (!(func & BIOC_DEVLIST))
   1514 			errx(1, "need -l parameter");
   1515 		if (sd_dev)
   1516 			errx(1, "can't use sd device");
   1517 		bio_createraid(cr_level, dev_list);
   1518 	}
   1519 
   1520 	return (0);
   1521 }
   1522 
   1523 void
   1524 usage(void)
   1525 {
   1526 	extern char *__progname;
   1527 
   1528 	fprintf(stderr,
   1529 		"usage: %s [-Dhiv] [-a alarm-function] "
   1530 		"[-b channel:target[.lun]]\n"
   1531 		"\t[-c raidlevel] [-H channel:target[.lun]]\n"
   1532 		"\t[-l special[,special[,...]]] "
   1533 		"[-u channel:target[.lun]] device\n", __progname);
   1534 
   1535 	exit(1);
   1536 }
   1537 
   1538 const char *
   1539 str2locator(const char *string, struct locator *location)
   1540 {
   1541 	const char *errstr;
   1542 	char parse[80], *targ, *lun;
   1543 
   1544 	strlcpy(parse, string, sizeof parse);
   1545 	targ = strchr(parse, ':');
   1546 	if (targ == NULL)
   1547 		return ("target not specified");
   1548 	*targ++ = '\0';
   1549 
   1550 	lun = strchr(targ, '.');
   1551 	if (lun != NULL) {
   1552 		*lun++ = '\0';
   1553 		location->lun = strtonum(lun, 0, 256, &errstr);
   1554 		if (errstr)
   1555 			return (errstr);
   1556 	} else
   1557 		location->lun = 0;
   1558 
   1559 	location->target = strtonum(targ, 0, 256, &errstr);
   1560 	if (errstr)
   1561 		return (errstr);
   1562 	location->channel = strtonum(parse, 0, 256, &errstr);
   1563 	if (errstr)
   1564 		return (errstr);
   1565 	return (NULL);
   1566 }
   1567 
   1568 void
   1569 bio_inq(char *name)
   1570 {
   1571 	const char *status;
   1572 	char size[64], scsiname[16], volname[32];
   1573 	char percent[10], seconds[20];
   1574 	int rv, i, d, volheader, hotspare, unused;
   1575 	char encname[16], serial[32];
   1576 	struct bioc_disk bd;
   1577 	struct bioc_inq bi;
   1578 	struct bioc_vol bv;
   1579 
   1580 	memset(&bi, 0, sizeof(bi));
   1581 
   1582 	if (debug)
   1583 		printf("bio_inq\n");
   1584 
   1585 	bi.bi_cookie = bl.bl_cookie;
   1586 
   1587 	rv = ioctl(devh, BIOCINQ, &bi);
   1588 	if (rv == -1) {
   1589 		warn("BIOCINQ");
   1590 		return;
   1591 	}
   1592 
   1593 	if (debug)
   1594 		printf("bio_inq { %p, %s, %d, %d }\n",
   1595 		    bi.bi_cookie,
   1596 		    bi.bi_dev,
   1597 		    bi.bi_novol,
   1598 		    bi.bi_nodisk);
   1599 
   1600 	volheader = 0;
   1601 	for (i = 0; i < bi.bi_novol; i++) {
   1602 		memset(&bv, 0, sizeof(bv));
   1603 		bv.bv_cookie = bl.bl_cookie;
   1604 		bv.bv_volid = i;
   1605 		bv.bv_percent = -1;
   1606 		bv.bv_seconds = 0;
   1607 
   1608 		rv = ioctl(devh, BIOCVOL, &bv);
   1609 		if (rv == -1) {
   1610 			warn("BIOCVOL");
   1611 			return;
   1612 		}
   1613 
   1614 		if (name && strcmp(name, bv.bv_dev) != 0)
   1615 			continue;
   1616 
   1617 		if (!volheader) {
   1618 			volheader = 1;
   1619 			printf("%-7s %-10s %14s %-8s\n",
   1620 			    "Volume", "Status", "Size", "Device");
   1621 		}
   1622 
   1623 		percent[0] = '\0';
   1624 		seconds[0] = '\0';
   1625 		if (bv.bv_percent != -1)
   1626 			snprintf(percent, sizeof percent,
   1627 			    " %d%% done", bv.bv_percent);
   1628 		if (bv.bv_seconds)
   1629 			snprintf(seconds, sizeof seconds,
   1630 			    " %u seconds", bv.bv_seconds);
   1631 		switch (bv.bv_status) {
   1632 		case BIOC_SVONLINE:
   1633 			status = BIOC_SVONLINE_S;
   1634 			break;
   1635 		case BIOC_SVOFFLINE:
   1636 			status = BIOC_SVOFFLINE_S;
   1637 			break;
   1638 		case BIOC_SVDEGRADED:
   1639 			status = BIOC_SVDEGRADED_S;
   1640 			break;
   1641 		case BIOC_SVBUILDING:
   1642 			status = BIOC_SVBUILDING_S;
   1643 			break;
   1644 		case BIOC_SVREBUILD:
   1645 			status = BIOC_SVREBUILD_S;
   1646 			break;
   1647 		case BIOC_SVSCRUB:
   1648 			status = BIOC_SVSCRUB_S;
   1649 			break;
   1650 		case BIOC_SVINVALID:
   1651 		default:
   1652 			status = BIOC_SVINVALID_S;
   1653 		}
   1654 
   1655 		snprintf(volname, sizeof volname, "%s %u",
   1656 		    bi.bi_dev, bv.bv_volid);
   1657 
   1658 		if (bv.bv_level == -1 && bv.bv_nodisk == 1) {
   1659 			hotspare = 1;
   1660 			unused = 0;
   1661 		} else if (bv.bv_level == -2 && bv.bv_nodisk == 1) {
   1662 			unused = 1;
   1663 			hotspare = 0;
   1664 		} else {
   1665 			unused = 0;
   1666 			hotspare = 0;
   1667 
   1668 			if (human)
   1669 				humanize_number(size, 5,
   1670 				    (int64_t)bv.bv_size, "", HN_AUTOSCALE,
   1671 				    HN_B | HN_NOSPACE | HN_DECIMAL);
   1672 			else
   1673 				snprintf(size, sizeof size, "%14llu",
   1674 				    (long long unsigned int)bv.bv_size);
   1675 			printf("%7s %-10s %14s %-7s RAID%u%s%s\n",
   1676 			    volname, status, size, bv.bv_dev,
   1677 			    bv.bv_level, percent, seconds);
   1678 		}
   1679 
   1680 		for (d = 0; d < bv.bv_nodisk; d++) {
   1681 			memset(&bd, 0, sizeof(bd));
   1682 			bd.bd_cookie = bl.bl_cookie;
   1683 			bd.bd_diskid = d;
   1684 			bd.bd_volid = i;
   1685 
   1686 			rv = ioctl(devh, BIOCDISK, &bd);
   1687 			if (rv == -1) {
   1688 				warn("BIOCDISK");
   1689 				return;
   1690 			}
   1691 
   1692 			switch (bd.bd_status) {
   1693 			case BIOC_SDONLINE:
   1694 				status = BIOC_SDONLINE_S;
   1695 				break;
   1696 			case BIOC_SDOFFLINE:
   1697 				status = BIOC_SDOFFLINE_S;
   1698 				break;
   1699 			case BIOC_SDFAILED:
   1700 				status = BIOC_SDFAILED_S;
   1701 				break;
   1702 			case BIOC_SDREBUILD:
   1703 				status = BIOC_SDREBUILD_S;
   1704 				break;
   1705 			case BIOC_SDHOTSPARE:
   1706 				status = BIOC_SDHOTSPARE_S;
   1707 				break;
   1708 			case BIOC_SDUNUSED:
   1709 				status = BIOC_SDUNUSED_S;
   1710 				break;
   1711 			case BIOC_SDSCRUB:
   1712 				status = BIOC_SDSCRUB_S;
   1713 				break;
   1714 			case BIOC_SDINVALID:
   1715 			default:
   1716 				status = BIOC_SDINVALID_S;
   1717 			}
   1718 
   1719 			if (hotspare || unused)
   1720 				;	/* use volname from parent volume */
   1721 			else
   1722 				snprintf(volname, sizeof volname, "    %3u",
   1723 				    bd.bd_diskid);
   1724 
   1725 			if (human)
   1726 				humanize_number(size, 5,
   1727 				    bd.bd_size, "", HN_AUTOSCALE,
   1728 				    HN_B | HN_NOSPACE | HN_DECIMAL);
   1729 			else
   1730 				snprintf(size, sizeof size, "%14llu",
   1731 				    (long long unsigned int)bd.bd_size);
   1732 			snprintf(scsiname, sizeof scsiname,
   1733 			    "%u:%u.%u",
   1734 			    bd.bd_channel, bd.bd_target, bd.bd_lun);
   1735 			if (bd.bd_procdev[0])
   1736 				strlcpy(encname, bd.bd_procdev, sizeof encname);
   1737 			else
   1738 				strlcpy(encname, "noencl", sizeof encname);
   1739 			if (bd.bd_serial[0])
   1740 				strlcpy(serial, bd.bd_serial, sizeof serial);
   1741 			else
   1742 				strlcpy(serial, "unknown serial", sizeof serial);
   1743 
   1744 			printf("%7s %-10s %14s %-7s %-6s <%s>\n",
   1745 			    volname, status, size, scsiname, encname,
   1746 			    bd.bd_vendor);
   1747 			if (verbose)
   1748 				printf("%7s %-10s %14s %-7s %-6s '%s'\n",
   1749 				    "", "", "", "", "", serial);
   1750 		}
   1751 	}
   1752 }
   1753 
   1754 void
   1755 bio_alarm(char *arg)
   1756 {
   1757 	int rv;
   1758 	struct bioc_alarm ba;
   1759 
   1760 	ba.ba_cookie = bl.bl_cookie;
   1761 
   1762 	switch (arg[0]) {
   1763 	case 'q': /* silence alarm */
   1764 		/* FALLTHROUGH */
   1765 	case 's':
   1766 		ba.ba_opcode = BIOC_SASILENCE;
   1767 		break;
   1768 
   1769 	case 'e': /* enable alarm */
   1770 		ba.ba_opcode = BIOC_SAENABLE;
   1771 		break;
   1772 
   1773 	case 'd': /* disable alarm */
   1774 		ba.ba_opcode = BIOC_SADISABLE;
   1775 		break;
   1776 
   1777 	case 't': /* test alarm */
   1778 		ba.ba_opcode = BIOC_SATEST;
   1779 		break;
   1780 
   1781 	case 'g': /* get alarm state */
   1782 		ba.ba_opcode = BIOC_GASTATUS;
   1783 		break;
   1784 
   1785 	default:
   1786 		warnx("invalid alarm function: %s", arg);
   1787 		return;
   1788 	}
   1789 
   1790 	rv = ioctl(devh, BIOCALARM, &ba);
   1791 	if (rv == -1) {
   1792 		warn("BIOCALARM");
   1793 		return;
   1794 	}
   1795 
   1796 	if (arg[0] == 'g') {
   1797 		printf("alarm is currently %s\n",
   1798 		    ba.ba_status ? "enabled" : "disabled");
   1799 
   1800 	}
   1801 }
   1802 
   1803 void
   1804 bio_setstate(char *arg)
   1805 {
   1806 	struct bioc_setstate	bs;
   1807 	struct locator		location;
   1808 	const char		*errstr;
   1809 	int			rv;
   1810 
   1811 	errstr = str2locator(arg, &location);
   1812 	if (errstr)
   1813 		errx(1, "Target %s: %s", arg, errstr);
   1814 
   1815 	bs.bs_cookie = bl.bl_cookie;
   1816 	bs.bs_status = BIOC_SSHOTSPARE;
   1817 	bs.bs_channel = location.channel;
   1818 	bs.bs_target = location.target;
   1819 	bs.bs_lun = location.lun;
   1820 
   1821 	rv = ioctl(devh, BIOCSETSTATE, &bs);
   1822 	if (rv == -1) {
   1823 		warn("BIOCSETSTATE");
   1824 		return;
   1825 	}
   1826 }
   1827 
   1828 void
   1829 bio_setblink(char *name, char *arg, int blink)
   1830 {
   1831 	struct locator		location;
   1832 	struct bioc_inq		bi;
   1833 	struct bioc_vol		bv;
   1834 	struct bioc_disk	bd;
   1835 	struct bioc_blink	bb;
   1836 	const char		*errstr;
   1837 	int			v, d, rv;
   1838 
   1839 	errstr = str2locator(arg, &location);
   1840 	if (errstr)
   1841 		errx(1, "Target %s: %s", arg, errstr);
   1842 
   1843 	/* try setting blink on the device directly */
   1844 	memset(&bb, 0, sizeof(bb));
   1845 	bb.bb_cookie = bl.bl_cookie;
   1846 	bb.bb_status = blink;
   1847 	bb.bb_target = location.target;
   1848 	bb.bb_channel = location.channel;
   1849 	rv = ioctl(devh, BIOCBLINK, &bb);
   1850 	if (rv == 0)
   1851 		return;
   1852 
   1853 	/* if the blink didnt work, try to find something that will */
   1854 
   1855 	memset(&bi, 0, sizeof(bi));
   1856 	bi.bi_cookie = bl.bl_cookie;
   1857 	rv = ioctl(devh, BIOCINQ, &bi);
   1858 	if (rv == -1) {
   1859 		warn("BIOCINQ");
   1860 		return;
   1861 	}
   1862 
   1863 	for (v = 0; v < bi.bi_novol; v++) {
   1864 		memset(&bv, 0, sizeof(bv));
   1865 		bv.bv_cookie = bl.bl_cookie;
   1866 		bv.bv_volid = v;
   1867 		rv = ioctl(devh, BIOCVOL, &bv);
   1868 		if (rv == -1) {
   1869 			warn("BIOCVOL");
   1870 			return;
   1871 		}
   1872 
   1873 		if (name && strcmp(name, bv.bv_dev) != 0)
   1874 			continue;
   1875 
   1876 		for (d = 0; d < bv.bv_nodisk; d++) {
   1877 			memset(&bd, 0, sizeof(bd));
   1878 			bd.bd_cookie = bl.bl_cookie;
   1879 			bd.bd_volid = v;
   1880 			bd.bd_diskid = d;
   1881 
   1882 			rv = ioctl(devh, BIOCDISK, &bd);
   1883 			if (rv == -1) {
   1884 				warn("BIOCDISK");
   1885 				return;
   1886 			}
   1887 
   1888 			if (bd.bd_channel == location.channel &&
   1889 			    bd.bd_target == location.target &&
   1890 			    bd.bd_lun == location.lun) {
   1891 				if (bd.bd_procdev[0] != '\0') {
   1892 					bio_blink(bd.bd_procdev,
   1893 					    location.target, blink);
   1894 				} else
   1895 					warnx("Disk %s is not in an enclosure", arg);
   1896 				return;
   1897 			}
   1898 		}
   1899 	}
   1900 
   1901 	warnx("Disk %s does not exist", arg);
   1902 	return;
   1903 }
   1904 
   1905 void
   1906 bio_blink(char *enclosure, int target, int blinktype)
   1907 {
   1908 	int			bioh;
   1909 	struct bio_locate	bio;
   1910 	struct bioc_blink	blink;
   1911 	int			rv;
   1912 
   1913 	bioh = open("/dev/bio", O_RDWR);
   1914 	if (bioh == -1)
   1915 		err(1, "Can't open %s", "/dev/bio");
   1916 
   1917 	bio.bl_name = enclosure;
   1918 	rv = ioctl(bioh, BIOCLOCATE, &bio);
   1919 	if (rv == -1)
   1920 		errx(1, "Can't locate %s device via %s", enclosure, "/dev/bio");
   1921 
   1922 	memset(&blink, 0, sizeof(blink));
   1923 	blink.bb_cookie = bio.bl_cookie;
   1924 	blink.bb_status = blinktype;
   1925 	blink.bb_target = target;
   1926 
   1927 	rv = ioctl(bioh, BIOCBLINK, &blink);
   1928 	if (rv == -1)
   1929 		warn("BIOCBLINK");
   1930 
   1931 	close(bioh);
   1932 }
   1933 
   1934 void
   1935 bio_createraid(u_int16_t level, char *dev_list)
   1936 {
   1937 	struct bioc_createraid	create;
   1938 	int			rv;
   1939 	u_int16_t		min_disks = 0;
   1940 
   1941 	if (debug)
   1942 		printf("bio_createraid\n");
   1943 
   1944 	if (!dev_list)
   1945 		errx(1, "no devices specified");
   1946 
   1947 	switch (level) {
   1948 	case 0:
   1949 		min_disks = 1;
   1950 		break;
   1951 	case 1:
   1952 		min_disks = 2;
   1953 		break;
   1954 	default:
   1955 		errx(1, "unsuported raid level");
   1956 	}
   1957 
   1958 	/* XXX validate device list for real */
   1959 #if 0
   1960 	if (strncmp(dev_list, "sd", 2) == 0 && strlen(dev_list) > 2 &&
   1961 	    isdigit(dev_list[2])) {
   1962 	    	if (strlen(dev_list) != 3)
   1963 			errx(1, "only one device supported");
   1964 
   1965 		if (debug)
   1966 			printf("bio_createraid: dev_list: %s\n", dev_list);
   1967 	}
   1968 	else
   1969 		errx(1, "no sd device specified");
   1970 #endif
   1971 
   1972 	memset(&create, 0, sizeof(create));
   1973 	create.bc_cookie = bl.bl_cookie;
   1974 	create.bc_level = level;
   1975 	create.bc_dev_list_len = strlen(dev_list);
   1976 	create.bc_dev_list = dev_list;
   1977 
   1978 	rv = ioctl(devh, BIOCCREATERAID, &create);
   1979 	if (rv == -1) {
   1980 		warn("BIOCCREATERAID");
   1981 		return;
   1982 	}
   1983 }
   1984