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