Home | History | Annotate | Line # | Download | only in bioctl
bioctl.c revision 1.8
      1 /* $NetBSD: bioctl.c,v 1.8 2008/02/29 14:33:02 xtraeme 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.8 2008/02/29 14:33:02 xtraeme 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 	memset(&bs, 0, sizeof(bs));
    631 
    632 	if (strcmp(argv[0], "start") == 0)
    633 		bs.bs_status = BIOC_SSCHECKSTART_VOL;
    634 	else if (strcmp(argv[0], "stop") == 0)
    635 		bs.bs_status = BIOC_SSCHECKSTOP_VOL;
    636 	else
    637 		usage();
    638 
    639 	bs.bs_volid = (unsigned int)strtoul(argv[1], &endptr, 10);
    640 	if (*endptr != '\0')
    641 		errx(EXIT_FAILURE, "Invalid Volume ID value");
    642 
    643 	bio_setstate_common(fd, NULL, &bs, NULL);
    644 }
    645 
    646 static void
    647 bio_setstate_common(int fd, char *arg, struct bioc_setstate *bs,
    648 		    struct locator *location)
    649 {
    650 	const char		*errstr;
    651 
    652 	if (!arg || !location)
    653 		goto send;
    654 
    655 	errstr = str2locator(arg, location);
    656 	if (errstr)
    657 		errx(EXIT_FAILURE, "Target %s: %s", arg, errstr);
    658 
    659 	bs->bs_channel = location->channel;
    660 	bs->bs_target = location->target;
    661 	bs->bs_lun = location->lun;
    662 
    663 send:
    664 	bs->bs_cookie = bl.bl_cookie;
    665 
    666 	if (ioctl(fd, BIOCSETSTATE, bs) == -1)
    667 		err(EXIT_FAILURE, "BIOCSETSTATE");
    668 }
    669 
    670 /*
    671  * To create a RAID volume.
    672  */
    673 static void
    674 bio_volops_create(int fd, int argc, char **argv)
    675 {
    676 	struct bioc_volops	bc;
    677 	struct bioc_inq 	bi;
    678 	struct bioc_disk	bd;
    679 	struct locator		location;
    680 	uint64_t 		total_disksize = 0, first_disksize = 0;
    681 	int64_t 		volsize = 0;
    682 	const char 		*errstr;
    683 	char 			*endptr, *stripe;
    684 	char			*scsiname, *raid_level, size[64];
    685 	int			disk_first = 0, disk_end = 0;
    686 	int			i, nfreedisks = 0;
    687 	int			user_disks = 0;
    688 
    689 	if (argc < 6 || argc > 7)
    690 		usage();
    691 
    692 	if (strcmp(argv[0], "volume") != 0)
    693 		usage();
    694 
    695 	/*
    696 	 * No size requested, use max size depending on RAID level.
    697 	 */
    698 	if (argc == 6) {
    699 		stripe = argv[3];
    700 		raid_level = argv[4];
    701 		scsiname = argv[5];
    702 	} else {
    703 		stripe = argv[4];
    704 		raid_level = argv[5];
    705 		scsiname = argv[6];
    706 	}
    707 
    708 	memset(&bd, 0, sizeof(bd));
    709 	memset(&bc, 0, sizeof(bc));
    710 	memset(&bi, 0, sizeof(bi));
    711 
    712 	bc.bc_cookie = bd.bd_cookie = bi.bi_cookie = bl.bl_cookie;
    713 	bc.bc_opcode = BIOC_VCREATE_VOLUME;
    714 
    715 	bc.bc_volid = (unsigned int)strtoul(argv[1], &endptr, 10);
    716 	if (*endptr != '\0')
    717 		errx(EXIT_FAILURE, "Invalid Volume ID value");
    718 
    719 	if (argc == 7)
    720 		if (dehumanize_number(argv[3], &volsize) == -1)
    721 			errx(EXIT_FAILURE, "Invalid SIZE value");
    722 
    723 	bc.bc_stripe = (unsigned int)strtoul(stripe, &endptr, 10);
    724 	if (*endptr != '\0')
    725 		errx(EXIT_FAILURE, "Invalid STRIPE size value");
    726 
    727 	bc.bc_level = (unsigned int)strtoul(raid_level, &endptr, 10);
    728 	if (*endptr != '\0')
    729 		errx(EXIT_FAILURE, "Invalid RAID_LEVEL value");
    730 
    731 	errstr = str2locator(scsiname, &location);
    732 	if (errstr)
    733 		errx(EXIT_FAILURE, "Target %s: %s", scsiname, errstr);
    734 
    735 	/*
    736 	 * Parse the device list that will be used for the volume,
    737 	 * by using a bit field for the disks.
    738 	 */
    739 	if ((isdigit((unsigned char)argv[2][0]) == 0) || argv[2][1] != '-' ||
    740 	    (isdigit((unsigned char)argv[2][2]) == 0))
    741 		errx(EXIT_FAILURE, "Invalid DISKIDs value");
    742 
    743 	disk_first = atoi(&argv[2][0]);
    744 	disk_end = atoi(&argv[2][2]);
    745 
    746 	for (i = disk_first; i < disk_end + 1; i++) {
    747 		bc.bc_devmask |= (1 << i);
    748 		user_disks++;
    749 	}
    750 
    751 	/*
    752 	 * Find out how many disks are free and how much size we
    753 	 * have available for the new volume.
    754 	 */
    755 	if (ioctl(fd, BIOCINQ, &bi) == -1)
    756 		err(EXIT_FAILURE, "BIOCINQ");
    757 
    758 	for (i = 0; i < bi.bi_nodisk; i++) {
    759 		bd.bd_diskid = i;
    760 		if (ioctl(fd, BIOCDISK_NOVOL, &bd) == -1)
    761 			err(EXIT_FAILURE, "BIOCDISK_NOVOL");
    762 
    763 		if (bd.bd_status == BIOC_SDUNUSED) {
    764 			if (i == 0)
    765 				first_disksize = bd.bd_size;
    766 
    767 			total_disksize += bd.bd_size;
    768 			nfreedisks++;
    769 		}
    770 	}
    771 
    772 	/*
    773 	 * Basic checks to be sure we don't do something stupid.
    774 	 */
    775 	if (nfreedisks == 0)
    776 		errx(EXIT_FAILURE, "No free disks available");
    777 
    778 	switch (bc.bc_level) {
    779 	case 0:	/* RAID 0 requires at least one disk */
    780 		if (argc == 7) {
    781 			if (volsize > total_disksize)
    782 				errx(EXIT_FAILURE, "volume size specified "
    783 				   "is larger than available on free disks");
    784 			bc.bc_size = (uint64_t)volsize;
    785 		} else
    786 			bc.bc_size = total_disksize;
    787 
    788 		break;
    789 	case 1:	/* RAID 1 requires two disks and size is total - 1 disk */
    790 		if (nfreedisks < 2 || user_disks < 2)
    791 			errx(EXIT_FAILURE, "2 disks are required at least for "
    792 			    "this RAID level");
    793 
    794 		if (argc == 7) {
    795 			if (volsize > (total_disksize - first_disksize))
    796 				errx(EXIT_FAILURE, "volume size specified "
    797 				   "is larger than available on free disks");
    798 			bc.bc_size = (uint64_t)volsize;
    799 		} else
    800 			bc.bc_size = (total_disksize - first_disksize);
    801 
    802 		break;
    803 	case 3:	/* RAID 0+1/3/5 requires three disks and size is total - 1 disk */
    804 	case 5:
    805 		if (nfreedisks < 3 || user_disks < 3)
    806 			errx(EXIT_FAILURE, "3 disks are required at least for "
    807 			    "this RAID level");
    808 
    809 		if (argc == 7) {
    810 			if (volsize > (total_disksize - first_disksize))
    811 				errx(EXIT_FAILURE, "volume size specified "
    812 				    "is larger than available on free disks");
    813 			bc.bc_size = (uint64_t)volsize;
    814 		} else
    815 			bc.bc_size = (total_disksize - first_disksize);
    816 
    817 		break;
    818 	case 6:	/* RAID 6 requires four disks and size is total - 2 disks */
    819 		if (nfreedisks < 4 || user_disks < 4)
    820 			errx(EXIT_FAILURE, "4 disks are required at least for "
    821 			    "this RAID level");
    822 
    823 		if (argc == 7) {
    824 			if (volsize > (total_disksize - (first_disksize * 2)))
    825 				err(EXIT_FAILURE, "volume size specified "
    826 				    "is larger than available on free disks");
    827 			bc.bc_size = (uint64_t)volsize;
    828 		} else
    829 			bc.bc_size = (total_disksize - (first_disksize * 2));
    830 
    831 		break;
    832 	default:
    833 		errx(EXIT_FAILURE, "Unsupported RAID level");
    834 	}
    835 
    836 	bc.bc_channel = location.channel;
    837 	bc.bc_target = location.target;
    838 	bc.bc_lun = location.lun;
    839 
    840 	if (ioctl(fd, BIOCVOLOPS, &bc) == -1)
    841 		err(EXIT_FAILURE, "BIOCVOLOPS");
    842 
    843         humanize_number(size, 5, bc.bc_size, "", HN_AUTOSCALE,
    844 	    HN_B | HN_NOSPACE | HN_DECIMAL);
    845 
    846 	printf("Created volume %u size: %s stripe: %uK level: %u "
    847 	    "SCSI location: %u:%u.%u\n", bc.bc_volid, size, bc.bc_stripe,
    848 	    bc.bc_level, bc.bc_channel, bc.bc_target, bc.bc_lun);
    849 }
    850 
    851 #ifdef notyet
    852 /*
    853  * To modify a RAID volume.
    854  */
    855 static void
    856 bio_volops_modify(int fd, int argc, char **argv)
    857 {
    858 	/* XTRAEME: TODO */
    859 }
    860 #endif
    861 
    862 /*
    863  * To remove a RAID volume.
    864  */
    865 static void
    866 bio_volops_remove(int fd, int argc, char **argv)
    867 {
    868 	struct bioc_volops	bc;
    869 	struct locator		location;
    870 	const char		*errstr;
    871 	char			*endptr;
    872 
    873 	if (argc != 3 || strcmp(argv[0], "volume") != 0)
    874 		usage();
    875 
    876 	memset(&bc, 0, sizeof(bc));
    877 	bc.bc_cookie = bl.bl_cookie;
    878 	bc.bc_opcode = BIOC_VREMOVE_VOLUME;
    879 
    880 	bc.bc_volid = (unsigned int)strtoul(argv[1], &endptr, 10);
    881 	if (*endptr != '\0')
    882 		errx(EXIT_FAILURE, "Invalid Volume ID value");
    883 
    884 	errstr = str2locator(argv[2], &location);
    885 	if (errstr)
    886 		errx(EXIT_FAILURE, "Target %s: %s", argv[2], errstr);
    887 
    888 	bc.bc_channel = location.channel;
    889 	bc.bc_target = location.target;
    890 	bc.bc_lun = location.lun;
    891 
    892 	if (ioctl(fd, BIOCVOLOPS, &bc) == -1)
    893 		err(EXIT_FAILURE, "BIOCVOLOPS");
    894 
    895 	printf("Removed volume %u at SCSI location %u:%u.%u\n",
    896 	    bc.bc_volid, bc.bc_channel, bc.bc_target, bc.bc_lun);
    897 }
    898 
    899 /*
    900  * To blink/unblink a disk in enclosures.
    901  */
    902 static void
    903 bio_setblink(int fd, int argc, char **argv)
    904 {
    905 	struct locator		location;
    906 	struct bioc_inq		bi;
    907 	struct bioc_vol		bv;
    908 	struct bioc_disk	bd;
    909 	struct bioc_blink	bb;
    910 	const char		*errstr;
    911 	int			v, d, rv, blink = 0;
    912 
    913 	if (argc != 2)
    914 		usage();
    915 
    916 	if (strcmp(argv[0], "start") == 0)
    917 		blink = BIOC_SBBLINK;
    918 	else if (strcmp(argv[0], "stop") == 0)
    919 		blink = BIOC_SBUNBLINK;
    920 	else
    921 		usage();
    922 
    923 	errstr = str2locator(argv[1], &location);
    924 	if (errstr)
    925 		errx(EXIT_FAILURE, "Target %s: %s", argv[1], errstr);
    926 
    927 	/* try setting blink on the device directly */
    928 	memset(&bb, 0, sizeof(bb));
    929 	bb.bb_cookie = bl.bl_cookie;
    930 	bb.bb_status = blink;
    931 	bb.bb_target = location.target;
    932 	bb.bb_channel = location.channel;
    933 	rv = ioctl(fd, BIOCBLINK, &bb);
    934 	if (rv == 0)
    935 		return;
    936 
    937 	/* if the blink didnt work, try to find something that will */
    938 	memset(&bi, 0, sizeof(bi));
    939 	bi.bi_cookie = bl.bl_cookie;
    940 	rv = ioctl(fd, BIOCINQ, &bi);
    941 	if (rv == -1)
    942 		err(EXIT_FAILURE, "BIOCINQ");
    943 
    944 	for (v = 0; v < bi.bi_novol; v++) {
    945 		memset(&bv, 0, sizeof(bv));
    946 		bv.bv_cookie = bl.bl_cookie;
    947 		bv.bv_volid = v;
    948 		rv = ioctl(fd, BIOCVOL, &bv);
    949 		if (rv == -1)
    950 			err(EXIT_FAILURE, "BIOCVOL");
    951 
    952 		for (d = 0; d < bv.bv_nodisk; d++) {
    953 			memset(&bd, 0, sizeof(bd));
    954 			bd.bd_cookie = bl.bl_cookie;
    955 			bd.bd_volid = v;
    956 			bd.bd_diskid = d;
    957 
    958 			rv = ioctl(fd, BIOCDISK, &bd);
    959 			if (rv == -1)
    960 				err(EXIT_FAILURE, "BIOCDISK");
    961 
    962 			if (bd.bd_channel == location.channel &&
    963 			    bd.bd_target == location.target &&
    964 			    bd.bd_lun == location.lun) {
    965 				if (bd.bd_procdev[0] != '\0') {
    966 					bio_blink(fd, bd.bd_procdev,
    967 					    location.target, blink);
    968 				} else
    969 					warnx("Disk %s is not in an enclosure",
    970 					   argv[1]);
    971 				return;
    972 			}
    973 		}
    974 	}
    975 
    976 	warnx("Disk %s does not exist", argv[1]);
    977 }
    978 
    979 static void
    980 bio_blink(int fd, char *enclosure, int target, int blinktype)
    981 {
    982 	struct bio_locate	bio;
    983 	struct bioc_blink	blink;
    984 
    985 	bio.bl_name = enclosure;
    986 	if (ioctl(fd, BIOCLOCATE, &bio) == -1)
    987 		errx(EXIT_FAILURE,
    988 		    "Can't locate %s device via /dev/bio", enclosure);
    989 
    990 	memset(&blink, 0, sizeof(blink));
    991 	blink.bb_cookie = bio.bl_cookie;
    992 	blink.bb_status = blinktype;
    993 	blink.bb_target = target;
    994 
    995 	if (ioctl(fd, BIOCBLINK, &blink) == -1)
    996 		err(EXIT_FAILURE, "BIOCBLINK");
    997 }
    998