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