Home | History | Annotate | Line # | Download | only in bioctl
bioctl.c revision 1.1.6.2
      1 /* $NetBSD: bioctl.c,v 1.1.6.2 2008/01/09 01:38:02 matt Exp $ */
      2 /* $OpenBSD: bioctl.c,v 1.52 2007/03/20 15:26:06 jmc Exp $ */
      3 
      4 /*
      5  * Copyright (c) 2007, 2008 Juan Romero Pardines
      6  * Copyright (c) 2004, 2005 Marco Peereboom
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
     19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
     22  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     28  * SUCH DAMAGE.
     29  *
     30  */
     31 #include <sys/cdefs.h>
     32 
     33 #ifndef lint
     34 __RCSID("$NetBSD: bioctl.c,v 1.1.6.2 2008/01/09 01:38:02 matt Exp $");
     35 #endif
     36 
     37 #include <sys/types.h>
     38 #include <sys/ioctl.h>
     39 #include <sys/param.h>
     40 #include <sys/queue.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 <stdbool.h>
     48 #include <stdio.h>
     49 #include <stdlib.h>
     50 #include <string.h>
     51 #include <unistd.h>
     52 #include <ctype.h>
     53 #include <util.h>
     54 #include "strtonum.h"
     55 
     56 struct command {
     57 	const char *cmd_name;
     58 	const char *arg_names;
     59 	void (*cmd_func)(int, int, char **);
     60 };
     61 
     62 struct biotmp {
     63 	struct bioc_inq *bi;
     64 	struct bioc_vol *bv;
     65 	char volname[64];
     66 	int fd;
     67 	int volid;
     68 	int diskid;
     69 	bool format;
     70 	bool show_disknovol;
     71 };
     72 
     73 struct locator {
     74 	int channel;
     75 	int target;
     76 	int lun;
     77 };
     78 
     79 static void 	usage(void);
     80 static void	bio_alarm(int, int, char **);
     81 static void	bio_show_common(int, int, char **);
     82 static int	bio_show_volumes(struct biotmp *);
     83 static void	bio_show_disks(struct biotmp *);
     84 static void	bio_setblink(int, int, char **);
     85 static void 	bio_blink(int, char *, int, int);
     86 static void	bio_setstate_hotspare(int, int, char **);
     87 static void	bio_setstate_passthru(int, int, char **);
     88 static void	bio_setstate_common(int, char *, struct bioc_setstate *,
     89 				    struct locator *);
     90 static void	bio_setstate_consistency(int, int, char **);
     91 static void	bio_volops_create(int, int, char **);
     92 #ifdef notyet
     93 static void	bio_volops_modify(int, int, char **);
     94 #endif
     95 static void	bio_volops_remove(int, int, char **);
     96 
     97 static const char *str2locator(const char *, struct locator *);
     98 
     99 static struct bio_locate bl;
    100 static struct command commands[] = {
    101 	{
    102 	  "show",
    103 	  "[disks] | [volumes]",
    104 	  bio_show_common },
    105 	{
    106 	  "alarm",
    107 	  "[enable] | [disable] | [silence] | [test]",
    108 	  bio_alarm },
    109 	{
    110 	  "blink",
    111 	  "start | stop [channel:target[.lun]]",
    112 	  bio_setblink },
    113 	{
    114 	  "hotspare",
    115 	  "add | remove channel:target.lun",
    116 	  bio_setstate_hotspare },
    117 	{
    118 	  "passthru",
    119 	  "add DISKID | remove channel:target.lun",
    120 	  bio_setstate_passthru },
    121 	{
    122 	  "check",
    123 	  "start | stop VOLID",
    124 	  bio_setstate_consistency },
    125 	{
    126 	  "create",
    127 	  "volume VOLID DISKIDs [SIZE] STRIPE RAID_LEVEL channel:target.lun",
    128 	  bio_volops_create },
    129 #ifdef notyet
    130 	{
    131 	  "modify",
    132 	  "volume VOLID STRIPE RAID_LEVEL channel:target.lun",
    133 	  bio_volops_modify },
    134 #endif
    135 	{
    136 	  "remove",
    137 	  "volume VOLID channel:target.lun",
    138 	  bio_volops_remove },
    139 
    140 	{ NULL, NULL, NULL }
    141 };
    142 
    143 int
    144 main(int argc, char **argv)
    145 {
    146 	char 		*dvname;
    147 	const char 	*cmdname;
    148 	int 		fd = 0, i;
    149 
    150 	/* Must have at least: device command */
    151 	if (argc < 3)
    152 		usage();
    153 
    154 	/* Skip program name, get and skip device name and command */
    155 	setprogname(*argv);
    156 	dvname = argv[1];
    157 	cmdname = argv[2];
    158 	argv += 3;
    159 	argc -= 3;
    160 
    161 	/* Look up and call the command */
    162 	for (i = 0; commands[i].cmd_name != NULL; i++)
    163 		if (strcmp(cmdname, commands[i].cmd_name) == 0)
    164 			break;
    165 	if (commands[i].cmd_name == NULL)
    166 		errx(EXIT_FAILURE, "unknown command: %s", cmdname);
    167 
    168 	/* Locate the device by issuing the BIOCLOCATE ioctl */
    169 	fd = open("/dev/bio", O_RDWR);
    170 	if (fd == -1)
    171 		err(EXIT_FAILURE, "Can't open /dev/bio");
    172 
    173 	bl.bl_name = dvname;
    174 	if (ioctl(fd, BIOCLOCATE, &bl) == -1)
    175 		errx(EXIT_FAILURE, "Can't locate %s device via /dev/bio",
    176 		    bl.bl_name);
    177 
    178 	/* and execute the command */
    179 	(*commands[i].cmd_func)(fd, argc, argv);
    180 
    181 	(void)close(fd);
    182 	exit(EXIT_SUCCESS);
    183 }
    184 
    185 static void
    186 usage(void)
    187 {
    188 	int i;
    189 
    190 	(void)fprintf(stderr, "usage: %s device command [arg [...]]\n",
    191 	    getprogname());
    192 
    193 	(void)fprintf(stderr, "Available commands:\n");
    194 	for (i = 0; commands[i].cmd_name != NULL; i++)
    195 		(void)fprintf(stderr, "  %s %s\n", commands[i].cmd_name,
    196 		    commands[i].arg_names);
    197 
    198 	exit(EXIT_FAILURE);
    199 	/* NOTREACHED */
    200 }
    201 
    202 static const char *
    203 str2locator(const char *string, struct locator *location)
    204 {
    205 	const char 	*errstr;
    206 	char 		parse[80], *targ, *lun;
    207 
    208 	strlcpy(parse, string, sizeof parse);
    209 	targ = strchr(parse, ':');
    210 	if (targ == NULL)
    211 		return "target not specified";
    212 
    213 	*targ++ = '\0';
    214 	lun = strchr(targ, '.');
    215 	if (lun != NULL) {
    216 		*lun++ = '\0';
    217 		location->lun = strtonum(lun, 0, 256, &errstr);
    218 		if (errstr)
    219 			return errstr;
    220 	} else
    221 		location->lun = 0;
    222 
    223 	location->target = strtonum(targ, 0, 256, &errstr);
    224 	if (errstr)
    225 		return errstr;
    226 	location->channel = strtonum(parse, 0, 256, &errstr);
    227 	if (errstr)
    228 		return errstr;
    229 	return NULL;
    230 }
    231 
    232 /*
    233  * Shows info about available RAID volumes.
    234  */
    235 static int
    236 bio_show_volumes(struct biotmp *bt)
    237 {
    238 	struct bioc_vol 	bv;
    239 	const char 		*status;
    240 	char 			size[64], percent[16], seconds[20];
    241 	char 			rtype[16], stripe[16], tmp[32];
    242 
    243 	memset(&bv, 0, sizeof(bv));
    244 	bv.bv_cookie = bl.bl_cookie;
    245 	bv.bv_volid = bt->volid;
    246 	bv.bv_percent = -1;
    247 	bv.bv_seconds = -1;
    248 
    249 	if (ioctl(bt->fd, BIOCVOL, &bv) == -1)
    250 		err(EXIT_FAILURE, "BIOCVOL");
    251 
    252 	percent[0] = '\0';
    253 	seconds[0] = '\0';
    254 	if (bv.bv_percent != -1)
    255 		snprintf(percent, sizeof(percent),
    256 		    " %3.2f%% done", bv.bv_percent / 10.0);
    257 	if (bv.bv_seconds)
    258 		snprintf(seconds, sizeof(seconds),
    259 		    " %u seconds", bv.bv_seconds);
    260 
    261 	switch (bv.bv_status) {
    262 	case BIOC_SVONLINE:
    263 		status = BIOC_SVONLINE_S;
    264 		break;
    265 	case BIOC_SVOFFLINE:
    266 		status = BIOC_SVOFFLINE_S;
    267 		break;
    268 	case BIOC_SVDEGRADED:
    269 		status = BIOC_SVDEGRADED_S;
    270 		break;
    271 	case BIOC_SVBUILDING:
    272 		status = BIOC_SVBUILDING_S;
    273 		break;
    274 	case BIOC_SVREBUILD:
    275 		status = BIOC_SVREBUILD_S;
    276 		break;
    277 	case BIOC_SVMIGRATING:
    278 		status = BIOC_SVMIGRATING_S;
    279 		break;
    280 	case BIOC_SVSCRUB:
    281 		status = BIOC_SVSCRUB_S;
    282 		break;
    283 	case BIOC_SVCHECKING:
    284 		status = BIOC_SVCHECKING_S;
    285 		break;
    286 	case BIOC_SVINVALID:
    287 	default:
    288 		status = BIOC_SVINVALID_S;
    289 		break;
    290 	}
    291 
    292 	snprintf(bt->volname, sizeof(bt->volname), "%u", bv.bv_volid);
    293 	if (bv.bv_vendor)
    294 		snprintf(tmp, sizeof(tmp), "%s %s", bv.bv_dev, bv.bv_vendor);
    295 	else
    296 		snprintf(tmp, sizeof(tmp), "%s", bv.bv_dev);
    297 
    298 	switch (bv.bv_level) {
    299 	case BIOC_SVOL_HOTSPARE:
    300 		snprintf(rtype, sizeof(rtype), "Hot spare");
    301 		snprintf(stripe, sizeof(stripe), "N/A");
    302 		break;
    303 	case BIOC_SVOL_PASSTHRU:
    304 		snprintf(rtype, sizeof(rtype), "Pass through");
    305 		snprintf(stripe, sizeof(stripe), "N/A");
    306 		break;
    307 	default:
    308 		snprintf(rtype, sizeof(rtype), "RAID %u", bv.bv_level);
    309 		snprintf(stripe, sizeof(stripe), "%uK", bv.bv_stripe_size);
    310 		break;
    311 	}
    312 
    313 	humanize_number(size, 5, (int64_t)bv.bv_size, "", HN_AUTOSCALE,
    314 	    HN_B | HN_NOSPACE | HN_DECIMAL);
    315 
    316 	printf("%6s %-12s %4s %20s %12s %6s %s%s\n",
    317 	    bt->volname, status, size, tmp,
    318 	    rtype, stripe, percent, seconds);
    319 
    320 	bt->bv = &bv;
    321 
    322 	return bv.bv_nodisk;
    323 }
    324 
    325 /*
    326  * Shows info about physical disks.
    327  */
    328 static void
    329 bio_show_disks(struct biotmp *bt)
    330 {
    331 	struct bioc_disk 	bd;
    332 	const char 		*status;
    333 	char 			size[64], serial[32], scsiname[16];
    334 
    335 	memset(&bd, 0, sizeof(bd));
    336 	bd.bd_cookie = bl.bl_cookie;
    337 	bd.bd_diskid = bt->diskid;
    338 	bd.bd_volid = bt->volid;
    339 
    340 	if (bt->show_disknovol) {
    341 		if (ioctl(bt->fd, BIOCDISK_NOVOL, &bd) == -1)
    342 			err(EXIT_FAILURE, "BIOCDISK_NOVOL");
    343 		if (!bd.bd_disknovol)
    344 			return;
    345 	} else {
    346 		if (ioctl(bt->fd, BIOCDISK, &bd) == -1)
    347 			err(EXIT_FAILURE, "BIOCDISK");
    348 	}
    349 
    350 	switch (bd.bd_status) {
    351 	case BIOC_SDONLINE:
    352 		status = BIOC_SDONLINE_S;
    353 		break;
    354 	case BIOC_SDOFFLINE:
    355 		status = BIOC_SDOFFLINE_S;
    356 		break;
    357 	case BIOC_SDFAILED:
    358 		status = BIOC_SDFAILED_S;
    359 		break;
    360 	case BIOC_SDREBUILD:
    361 		status = BIOC_SDREBUILD_S;
    362 		break;
    363 	case BIOC_SDHOTSPARE:
    364 		status = BIOC_SDHOTSPARE_S;
    365 		break;
    366 	case BIOC_SDUNUSED:
    367 		status = BIOC_SDUNUSED_S;
    368 		break;
    369 	case BIOC_SDSCRUB:
    370 		status = BIOC_SDSCRUB_S;
    371 		break;
    372 	case BIOC_SDPASSTHRU:
    373 		status = BIOC_SDPASSTHRU_S;
    374 		break;
    375 	case BIOC_SDINVALID:
    376 	default:
    377 		status = BIOC_SDINVALID_S;
    378 		break;
    379 	}
    380 
    381 	if (bt->format)
    382 		snprintf(bt->volname, sizeof(bt->volname),
    383 		    "%u:%u", bt->bv->bv_volid, bd.bd_diskid);
    384 
    385 	humanize_number(size, 5, bd.bd_size, "", HN_AUTOSCALE,
    386 	    HN_B | HN_NOSPACE | HN_DECIMAL);
    387 
    388 	if (bd.bd_procdev[0])
    389 		snprintf(scsiname, sizeof(scsiname), "%u:%u.%u %s",
    390 	    	    bd.bd_channel, bd.bd_target, bd.bd_lun,
    391 		    bd.bd_procdev);
    392 	else
    393 		snprintf(scsiname, sizeof(scsiname), "%u:%u.%u noencl",
    394 		    bd.bd_channel, bd.bd_target, bd.bd_lun);
    395 
    396 	if (bd.bd_serial[0])
    397 		strlcpy(serial, bd.bd_serial, sizeof(serial));
    398 	else
    399 		strlcpy(serial, "unknown serial", sizeof(serial));
    400 
    401 	if (bt->format)
    402 		printf("%6s %-12s %4s %20s <%s>\n",
    403 		    bt->volname, status, size, scsiname,
    404 				    bd.bd_vendor);
    405 	else
    406 		printf("%5d [%-28s] %-12s %-6s %12s\n",
    407 		    bt->diskid, bd.bd_vendor, status, size, scsiname);
    408 
    409 }
    410 
    411 /*
    412  * Shows info about volumes/disks.
    413  */
    414 static void
    415 bio_show_common(int fd, int argc, char **argv)
    416 {
    417 	struct biotmp 		*biot;
    418 	struct bioc_inq 	bi;
    419 	int 			i, d, ndisks;
    420 	bool 			show_all, show_disks;
    421 	bool 			show_vols, show_caps;
    422 
    423 	show_all = show_disks = show_vols = show_caps = false;
    424 
    425 	if (argc > 1)
    426 		usage();
    427 
    428 	if (argv[0]) {
    429 		if (strcmp(argv[0], "disks") == 0)
    430 			show_disks = true;
    431 		else if (strcmp(argv[0], "volumes") == 0)
    432 			show_vols = true;
    433 		else
    434 			usage();
    435 	} else
    436 		show_all = true;
    437 
    438 	memset(&bi, 0, sizeof(bi));
    439 	bi.bi_cookie = bl.bl_cookie;
    440 
    441 	if (ioctl(fd, BIOCINQ, &bi) == -1)
    442 		err(EXIT_FAILURE, "BIOCINQ");
    443 
    444 	/*
    445 	 * If there are volumes there's no point to continue.
    446 	 */
    447 	if (show_all || show_vols) {
    448 		if (!bi.bi_novol) {
    449 			warnx("no volumes available");
    450 			return;
    451 		}
    452 	}
    453 
    454 	biot = calloc(1, sizeof(*biot));
    455 	if (!biot)
    456 		err(EXIT_FAILURE, "biotemp calloc");
    457 
    458 	biot->fd = fd;
    459 	biot->bi = &bi;
    460 	/*
    461 	 * Go to the disks section if that was specified.
    462 	 */
    463 	if (show_disks)
    464 		goto disks;
    465 
    466 	/*
    467 	 * Common code to show only info about volumes and disks
    468 	 * associated to them.
    469 	 */
    470 	printf("%6s %-12s %4s %20s %12s %6s\n",
    471 	    "Volume", "Status", "Size", "Device/Label",
    472 	    "RAID Level", "Stripe");
    473 	printf("=============================================="
    474 	       "===================\n");
    475 
    476 	for (i = 0; i < bi.bi_novol; i++) {
    477 		biot->format = true;
    478 		biot->volid = i;
    479 		ndisks = bio_show_volumes(biot);
    480 		if (show_vols)
    481 			continue;
    482 
    483 		for (d = 0; d < ndisks; d++) {
    484 			biot->diskid = d;
    485 			bio_show_disks(biot);
    486 		}
    487 
    488 	}
    489 	goto out;
    490 
    491 disks:
    492 	/*
    493 	 * show info about all disks connected to the raid controller,
    494 	 * even if they aren't associated with a volume or raid set.
    495 	 */
    496 	if (show_disks) {
    497 		printf("%5s %-30s %-12s %-6s %12s\n",
    498 		    "Disk", "Model/Serial", "Status", "Size", "Location");
    499 		printf("==============================================="
    500 		       "======================\n");
    501 		for (d = 0; d < bi.bi_nodisk; d++) {
    502 			biot->show_disknovol = true;
    503 			biot->diskid = d;
    504 			bio_show_disks(biot);
    505 		}
    506 	}
    507 out:
    508 	free(biot);
    509 }
    510 
    511 /*
    512  * To handle the alarm feature.
    513  */
    514 static void
    515 bio_alarm(int fd, int argc, char **argv)
    516 {
    517 	struct bioc_alarm 	ba;
    518 	bool 			show = false;
    519 
    520 	memset(&ba, 0, sizeof(ba));
    521 	ba.ba_cookie = bl.bl_cookie;
    522 
    523 	if (argc > 1)
    524 		usage();
    525 
    526 	if (argc == 0) {
    527 		/* show alarm status */
    528 		ba.ba_opcode = BIOC_GASTATUS;
    529 		show = true;
    530 	} else if (strcmp(argv[0], "silence") == 0) {
    531 		/* silence alarm */
    532 		ba.ba_opcode = BIOC_SASILENCE;
    533 	} else if (strcmp(argv[0], "enable") == 0) {
    534 		/* enable alarm */
    535 		ba.ba_opcode = BIOC_SAENABLE;
    536 	} else if (strcmp(argv[0], "disable") == 0) {
    537 		/* disable alarm */
    538 		ba.ba_opcode = BIOC_SADISABLE;
    539 	} else if (strcmp(argv[0], "test") == 0) {
    540 		/* test alarm */
    541 		ba.ba_opcode = BIOC_SATEST;
    542 	} else
    543 		usage();
    544 
    545 	if (ioctl(fd, BIOCALARM, &ba) == -1)
    546 		err(EXIT_FAILURE, "BIOCALARM");
    547 
    548 	if (show)
    549 		printf("alarm is currently %s\n",
    550 		    ba.ba_status ? "enabled" : "disabled");
    551 }
    552 
    553 /*
    554  * To add/remove a hotspare disk.
    555  */
    556 static void
    557 bio_setstate_hotspare(int fd, int argc, char **argv)
    558 {
    559 	struct bioc_setstate	bs;
    560 	struct locator		location;
    561 
    562 	memset(&bs, 0, sizeof(bs));
    563 
    564 	if (argc != 2)
    565 		usage();
    566 
    567 	if (strcmp(argv[0], "add") == 0)
    568 		bs.bs_status = BIOC_SSHOTSPARE;
    569 	else if (strcmp(argv[0], "remove") == 0)
    570 		bs.bs_status = BIOC_SSDELHOTSPARE;
    571 	else
    572 		usage();
    573 
    574 	bio_setstate_common(fd, argv[1], &bs, &location);
    575 }
    576 
    577 /*
    578  * To add/remove a pass through disk.
    579  */
    580 static void
    581 bio_setstate_passthru(int fd, int argc, char **argv)
    582 {
    583 	struct bioc_setstate	bs;
    584 	struct locator		location;
    585 	char			*endptr;
    586 	bool			rem = false;
    587 
    588 	if (argc > 3)
    589 		usage();
    590 
    591 	memset(&bs, 0, sizeof(bs));
    592 
    593 	if (strcmp(argv[0], "add") == 0) {
    594 		if (argv[1] == NULL || argv[2] == NULL)
    595 			usage();
    596 
    597 		bs.bs_status = BIOC_SSPASSTHRU;
    598 	} else if (strcmp(argv[0], "remove") == 0) {
    599 		if (argv[1] == NULL)
    600 			usage();
    601 
    602 		bs.bs_status = BIOC_SSDELPASSTHRU;
    603 		rem = true;
    604 	} else
    605 		usage();
    606 
    607 	if (rem)
    608 		bio_setstate_common(fd, argv[1], &bs, &location);
    609 	else {
    610 		bs.bs_other_id = (unsigned int)strtoul(argv[1], &endptr, 10);
    611 		if (*endptr != '\0')
    612 			errx(EXIT_FAILURE, "Invalid Volume ID value");
    613 
    614 		bio_setstate_common(fd, argv[2], &bs, &location);
    615 	}
    616 }
    617 
    618 /*
    619  * To start/stop a consistency check in a RAID volume.
    620  */
    621 static void
    622 bio_setstate_consistency(int fd, int argc, char **argv)
    623 {
    624 	struct bioc_setstate	bs;
    625 	char			*endptr;
    626 
    627 	if (argc > 2)
    628 		usage();
    629 
    630 	if (strcmp(argv[0], "start") == 0)
    631 		bs.bs_status = BIOC_SSCHECKSTART_VOL;
    632 	else if (strcmp(argv[0], "stop") == 0)
    633 		bs.bs_status = BIOC_SSCHECKSTOP_VOL;
    634 	else
    635 		usage();
    636 
    637 	memset(&bs, 0, sizeof(bs));
    638 	bs.bs_volid = (unsigned int)strtoul(argv[2], &endptr, 10);
    639 	if (*endptr != '\0')
    640 		errx(EXIT_FAILURE, "Invalid Volume ID value");
    641 
    642 	bio_setstate_common(fd, NULL, &bs, NULL);
    643 }
    644 
    645 static void
    646 bio_setstate_common(int fd, char *arg, struct bioc_setstate *bs,
    647 		    struct locator *location)
    648 {
    649 	const char		*errstr;
    650 
    651 	if (!arg || !location)
    652 		goto send;
    653 
    654 	errstr = str2locator(arg, location);
    655 	if (errstr)
    656 		errx(EXIT_FAILURE, "Target %s: %s", arg, errstr);
    657 
    658 	bs->bs_channel = location->channel;
    659 	bs->bs_target = location->target;
    660 	bs->bs_lun = location->lun;
    661 
    662 send:
    663 	bs->bs_cookie = bl.bl_cookie;
    664 
    665 	if (ioctl(fd, BIOCSETSTATE, bs) == -1)
    666 		err(EXIT_FAILURE, "BIOCSETSTATE");
    667 }
    668 
    669 /*
    670  * To create a RAID volume.
    671  */
    672 static void
    673 bio_volops_create(int fd, int argc, char **argv)
    674 {
    675 	struct bioc_volops	bc;
    676 	struct bioc_inq 	bi;
    677 	struct bioc_disk	bd;
    678 	struct locator		location;
    679 	uint64_t 		total_disksize = 0, first_disksize = 0;
    680 	int64_t 		volsize = 0;
    681 	const char 		*errstr;
    682 	char 			*endptr, *stripe;
    683 	char			*scsiname, *raid_level, size[64];
    684 	int			disk_first = 0, disk_end = 0;
    685 	int			i, nfreedisks = 0;
    686 	int			user_disks = 0;
    687 
    688 	if (argc < 6 || argc > 7)
    689 		usage();
    690 
    691 	if (strcmp(argv[0], "volume") != 0)
    692 		usage();
    693 
    694 	/*
    695 	 * No size requested, use max size depending on RAID level.
    696 	 */
    697 	if (argc == 6) {
    698 		stripe = argv[3];
    699 		raid_level = argv[4];
    700 		scsiname = argv[5];
    701 	} else {
    702 		stripe = argv[4];
    703 		raid_level = argv[5];
    704 		scsiname = argv[6];
    705 	}
    706 
    707 	memset(&bd, 0, sizeof(bd));
    708 	memset(&bc, 0, sizeof(bc));
    709 	memset(&bi, 0, sizeof(bi));
    710 
    711 	bc.bc_cookie = bd.bd_cookie = bi.bi_cookie = bl.bl_cookie;
    712 	bc.bc_opcode = BIOC_VCREATE_VOLUME;
    713 
    714 	bc.bc_volid = (unsigned int)strtoul(argv[1], &endptr, 10);
    715 	if (*endptr != '\0')
    716 		errx(EXIT_FAILURE, "Invalid Volume ID value");
    717 
    718 	if (argc == 7)
    719 		if (dehumanize_number(argv[3], &volsize) == -1)
    720 			errx(EXIT_FAILURE, "Invalid SIZE value");
    721 
    722 	bc.bc_stripe = (unsigned int)strtoul(stripe, &endptr, 10);
    723 	if (*endptr != '\0')
    724 		errx(EXIT_FAILURE, "Invalid STRIPE size value");
    725 
    726 	bc.bc_level = (unsigned int)strtoul(raid_level, &endptr, 10);
    727 	if (*endptr != '\0')
    728 		errx(EXIT_FAILURE, "Invalid RAID_LEVEL value");
    729 
    730 	errstr = str2locator(scsiname, &location);
    731 	if (errstr)
    732 		errx(EXIT_FAILURE, "Target %s: %s", scsiname, errstr);
    733 
    734 	/*
    735 	 * Parse the device list that will be used for the volume,
    736 	 * by using a bit field for the disks.
    737 	 */
    738 	if ((isdigit((unsigned char)argv[2][0]) == 0) || argv[2][1] != '-' ||
    739 	    (isdigit((unsigned char)argv[2][2]) == 0))
    740 		errx(EXIT_FAILURE, "Invalid DISKIDs value");
    741 
    742 	disk_first = atoi(&argv[2][0]);
    743 	disk_end = atoi(&argv[2][2]);
    744 
    745 	for (i = disk_first; i < disk_end + 1; i++) {
    746 		bc.bc_devmask |= (1 << i);
    747 		user_disks++;
    748 	}
    749 
    750 	/*
    751 	 * Find out how many disks are free and how much size we
    752 	 * have available for the new volume.
    753 	 */
    754 	if (ioctl(fd, BIOCINQ, &bi) == -1)
    755 		err(EXIT_FAILURE, "BIOCINQ");
    756 
    757 	for (i = 0; i < bi.bi_nodisk; i++) {
    758 		bd.bd_diskid = i;
    759 		if (ioctl(fd, BIOCDISK_NOVOL, &bd) == -1)
    760 			err(EXIT_FAILURE, "BIOCDISK_NOVOL");
    761 
    762 		if (bd.bd_status == BIOC_SDUNUSED) {
    763 			if (i == 0)
    764 				first_disksize = bd.bd_size;
    765 
    766 			total_disksize += bd.bd_size;
    767 			nfreedisks++;
    768 		}
    769 	}
    770 
    771 	/*
    772 	 * Basic checks to be sure we don't do something stupid.
    773 	 */
    774 	if (nfreedisks == 0)
    775 		errx(EXIT_FAILURE, "No free disks available");
    776 
    777 	switch (bc.bc_level) {
    778 	case 0:	/* RAID 0 requires at least one disk */
    779 		if (argc == 7) {
    780 			if (volsize > total_disksize)
    781 				errx(EXIT_FAILURE, "volume size specified "
    782 				   "is larger than available on free disks");
    783 			bc.bc_size = (uint64_t)volsize;
    784 		} else
    785 			bc.bc_size = total_disksize;
    786 
    787 		break;
    788 	case 1:	/* RAID 1 requires two disks and size is total - 1 disk */
    789 		if (nfreedisks < 2 || user_disks < 2)
    790 			errx(EXIT_FAILURE, "2 disks are required at least for "
    791 			    "this RAID level");
    792 
    793 		if (argc == 7) {
    794 			if (volsize > (total_disksize - first_disksize))
    795 				errx(EXIT_FAILURE, "volume size specified "
    796 				   "is larger than available on free disks");
    797 			bc.bc_size = (uint64_t)volsize;
    798 		} else
    799 			bc.bc_size = (total_disksize - first_disksize);
    800 
    801 		break;
    802 	case 3:	/* RAID 0+1/3/5 requires three disks and size is total - 1 disk */
    803 	case 5:
    804 		if (nfreedisks < 3 || user_disks < 3)
    805 			errx(EXIT_FAILURE, "3 disks are required at least for "
    806 			    "this RAID level");
    807 
    808 		if (argc == 7) {
    809 			if (volsize > (total_disksize - first_disksize))
    810 				errx(EXIT_FAILURE, "volume size specified "
    811 				    "is larger than available on free disks");
    812 			bc.bc_size = (uint64_t)volsize;
    813 		} else
    814 			bc.bc_size = (total_disksize - first_disksize);
    815 
    816 		break;
    817 	case 6:	/* RAID 6 requires four disks and size is total - 2 disks */
    818 		if (nfreedisks < 4 || user_disks < 4)
    819 			errx(EXIT_FAILURE, "4 disks are required at least for "
    820 			    "this RAID level");
    821 
    822 		if (argc == 7) {
    823 			if (volsize > (total_disksize - (first_disksize * 2)))
    824 				err(EXIT_FAILURE, "volume size specified "
    825 				    "is larger than available on free disks");
    826 			bc.bc_size = (uint64_t)volsize;
    827 		} else
    828 			bc.bc_size = (total_disksize - (first_disksize * 2));
    829 
    830 		break;
    831 	default:
    832 		errx(EXIT_FAILURE, "Unsupported RAID level");
    833 	}
    834 
    835 	bc.bc_channel = location.channel;
    836 	bc.bc_target = location.target;
    837 	bc.bc_lun = location.lun;
    838 
    839 	if (ioctl(fd, BIOCVOLOPS, &bc) == -1)
    840 		err(EXIT_FAILURE, "BIOCVOLOPS");
    841 
    842         humanize_number(size, 5, bc.bc_size, "", HN_AUTOSCALE,
    843 	    HN_B | HN_NOSPACE | HN_DECIMAL);
    844 
    845 	printf("Created volume %u size: %s stripe: %uK level: %u "
    846 	    "SCSI location: %u:%u.%u\n", bc.bc_volid, size, bc.bc_stripe,
    847 	    bc.bc_level, bc.bc_channel, bc.bc_target, bc.bc_lun);
    848 }
    849 
    850 #ifdef notyet
    851 /*
    852  * To modify a RAID volume.
    853  */
    854 static void
    855 bio_volops_modify(int fd, int argc, char **argv)
    856 {
    857 	/* XTRAEME: TODO */
    858 }
    859 #endif
    860 
    861 /*
    862  * To remove a RAID volume.
    863  */
    864 static void
    865 bio_volops_remove(int fd, int argc, char **argv)
    866 {
    867 	struct bioc_volops	bc;
    868 	struct locator		location;
    869 	const char		*errstr;
    870 	char			*endptr;
    871 
    872 	if (argc != 3 || strcmp(argv[0], "volume") != 0)
    873 		usage();
    874 
    875 	memset(&bc, 0, sizeof(bc));
    876 	bc.bc_cookie = bl.bl_cookie;
    877 	bc.bc_opcode = BIOC_VREMOVE_VOLUME;
    878 
    879 	bc.bc_volid = (unsigned int)strtoul(argv[1], &endptr, 10);
    880 	if (*endptr != '\0')
    881 		errx(EXIT_FAILURE, "Invalid Volume ID value");
    882 
    883 	errstr = str2locator(argv[2], &location);
    884 	if (errstr)
    885 		errx(EXIT_FAILURE, "Target %s: %s", argv[2], errstr);
    886 
    887 	bc.bc_channel = location.channel;
    888 	bc.bc_target = location.target;
    889 	bc.bc_lun = location.lun;
    890 
    891 	if (ioctl(fd, BIOCVOLOPS, &bc) == -1)
    892 		err(EXIT_FAILURE, "BIOCVOLOPS");
    893 
    894 	printf("Removed volume %u at SCSI location %u:%u.%u\n",
    895 	    bc.bc_volid, bc.bc_channel, bc.bc_target, bc.bc_lun);
    896 }
    897 
    898 /*
    899  * To blink/unblink a disk in enclosures.
    900  */
    901 static void
    902 bio_setblink(int fd, int argc, char **argv)
    903 {
    904 	struct locator		location;
    905 	struct bioc_inq		bi;
    906 	struct bioc_vol		bv;
    907 	struct bioc_disk	bd;
    908 	struct bioc_blink	bb;
    909 	const char		*errstr;
    910 	int			v, d, rv, blink = 0;
    911 
    912 	if (argc != 2)
    913 		usage();
    914 
    915 	if (strcmp(argv[0], "start") == 0)
    916 		blink = BIOC_SBBLINK;
    917 	else if (strcmp(argv[0], "stop") == 0)
    918 		blink = BIOC_SBUNBLINK;
    919 	else
    920 		usage();
    921 
    922 	errstr = str2locator(argv[1], &location);
    923 	if (errstr)
    924 		errx(EXIT_FAILURE, "Target %s: %s", argv[1], errstr);
    925 
    926 	/* try setting blink on the device directly */
    927 	memset(&bb, 0, sizeof(bb));
    928 	bb.bb_cookie = bl.bl_cookie;
    929 	bb.bb_status = blink;
    930 	bb.bb_target = location.target;
    931 	bb.bb_channel = location.channel;
    932 	rv = ioctl(fd, BIOCBLINK, &bb);
    933 	if (rv == 0)
    934 		return;
    935 
    936 	/* if the blink didnt work, try to find something that will */
    937 	memset(&bi, 0, sizeof(bi));
    938 	bi.bi_cookie = bl.bl_cookie;
    939 	rv = ioctl(fd, BIOCINQ, &bi);
    940 	if (rv == -1)
    941 		err(EXIT_FAILURE, "BIOCINQ");
    942 
    943 	for (v = 0; v < bi.bi_novol; v++) {
    944 		memset(&bv, 0, sizeof(bv));
    945 		bv.bv_cookie = bl.bl_cookie;
    946 		bv.bv_volid = v;
    947 		rv = ioctl(fd, BIOCVOL, &bv);
    948 		if (rv == -1)
    949 			err(EXIT_FAILURE, "BIOCVOL");
    950 
    951 		for (d = 0; d < bv.bv_nodisk; d++) {
    952 			memset(&bd, 0, sizeof(bd));
    953 			bd.bd_cookie = bl.bl_cookie;
    954 			bd.bd_volid = v;
    955 			bd.bd_diskid = d;
    956 
    957 			rv = ioctl(fd, BIOCDISK, &bd);
    958 			if (rv == -1)
    959 				err(EXIT_FAILURE, "BIOCDISK");
    960 
    961 			if (bd.bd_channel == location.channel &&
    962 			    bd.bd_target == location.target &&
    963 			    bd.bd_lun == location.lun) {
    964 				if (bd.bd_procdev[0] != '\0') {
    965 					bio_blink(fd, bd.bd_procdev,
    966 					    location.target, blink);
    967 				} else
    968 					warnx("Disk %s is not in an enclosure",
    969 					   argv[1]);
    970 				return;
    971 			}
    972 		}
    973 	}
    974 
    975 	warnx("Disk %s does not exist", argv[1]);
    976 }
    977 
    978 static void
    979 bio_blink(int fd, char *enclosure, int target, int blinktype)
    980 {
    981 	struct bio_locate	bio;
    982 	struct bioc_blink	blink;
    983 
    984 	bio.bl_name = enclosure;
    985 	if (ioctl(fd, BIOCLOCATE, &bio) == -1)
    986 		errx(EXIT_FAILURE,
    987 		    "Can't locate %s device via /dev/bio", enclosure);
    988 
    989 	memset(&blink, 0, sizeof(blink));
    990 	blink.bb_cookie = bio.bl_cookie;
    991 	blink.bb_status = blinktype;
    992 	blink.bb_target = target;
    993 
    994 	if (ioctl(fd, BIOCBLINK, &blink) == -1)
    995 		err(EXIT_FAILURE, "BIOCBLINK");
    996 }
    997