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