Home | History | Annotate | Line # | Download | only in bioctl
bioctl.c revision 1.5
      1 /* $NetBSD: bioctl.c,v 1.5 2007/12/05 16:29:48 xtraeme Exp $ */
      2 /* $OpenBSD: bioctl.c,v 1.52 2007/03/20 15:26:06 jmc Exp $       */
      3 
      4 /*
      5  * Copyright (c) 2004, 2005 Marco Peereboom
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
     18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     20  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
     21  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     27  * SUCH DAMAGE.
     28  *
     29  */
     30 #include <sys/cdefs.h>
     31 
     32 #ifndef lint
     33 __RCSID("$NetBSD: bioctl.c,v 1.5 2007/12/05 16:29:48 xtraeme Exp $");
     34 #endif
     35 
     36 #include <sys/ioctl.h>
     37 #include <sys/param.h>
     38 #include <sys/queue.h>
     39 #include <dev/biovar.h>
     40 
     41 #include <errno.h>
     42 #include <err.h>
     43 #include <fcntl.h>
     44 #include <util.h>
     45 #include <stdio.h>
     46 #include <stdlib.h>
     47 #include <string.h>
     48 #include <unistd.h>
     49 #include <ctype.h>
     50 #include <util.h>
     51 #include "strtonum.h"
     52 
     53 struct locator {
     54 	int channel;
     55 	int target;
     56 	int lun;
     57 };
     58 
     59 static void usage(void);
     60 static const char *str2locator(const char *, struct locator *);
     61 
     62 static void bio_inq(int, char *);
     63 static void bio_alarm(int, char *);
     64 static void bio_setstate(int, char *);
     65 static void bio_setblink(int, char *, char *, int);
     66 static void bio_blink(int, char *, int, int);
     67 
     68 static int human;
     69 static int verbose;
     70 
     71 static struct bio_locate bl;
     72 
     73 int
     74 main(int argc, char *argv[])
     75 {
     76 	uint64_t func = 0;
     77 	char *bioc_dev, *al_arg, *bl_arg;
     78 	int fd, ch, rv, blink;
     79 
     80 	bioc_dev = al_arg = bl_arg = NULL;
     81 	fd = ch = rv = blink = 0;
     82 
     83 	if (argc < 2)
     84 		usage();
     85 
     86 	setprogname(*argv);
     87 
     88 	while ((ch = getopt(argc, argv, "b:c:l:u:H:ha:Dv")) != -1) {
     89 		switch (ch) {
     90 		case 'a': /* alarm */
     91 			func |= BIOC_ALARM;
     92 			al_arg = optarg;
     93 			break;
     94 		case 'b': /* blink */
     95 			func |= BIOC_BLINK;
     96 			blink = BIOC_SBBLINK;
     97 			bl_arg = optarg;
     98 			break;
     99 		case 'u': /* unblink */
    100 			func |= BIOC_BLINK;
    101 			blink = BIOC_SBUNBLINK;
    102 			bl_arg = optarg;
    103 			break;
    104 		case 'H': /* set hotspare */
    105 			func |= BIOC_SETSTATE;
    106 			al_arg = optarg;
    107 			break;
    108 		case 'h':
    109 			human = 1;
    110 			break;
    111 		case 'i': /* inquiry */
    112 			func |= BIOC_INQ;
    113 			break;
    114 		case 'v':
    115 			verbose = 1;
    116 			break;
    117 		default:
    118 			usage();
    119 			/* NOTREACHED */
    120 		}
    121 	}
    122 	argc -= optind;
    123 	argv += optind;
    124 
    125 	if (argc != 1)
    126 		usage();
    127 
    128 	if (func == 0)
    129 		func |= BIOC_INQ;
    130 
    131 	bioc_dev = argv[0];
    132 
    133 	if (bioc_dev) {
    134 		fd = open("/dev/bio", O_RDWR);
    135 		if (fd == -1)
    136 			err(EXIT_FAILURE, "Can't open %s", "/dev/bio");
    137 
    138 		bl.bl_name = bioc_dev;
    139 		rv = ioctl(fd, BIOCLOCATE, &bl);
    140 		if (rv == -1)
    141 			errx(EXIT_FAILURE, "Can't locate %s device via %s",
    142 			    bl.bl_name, "/dev/bio");
    143 	}
    144 
    145 	if (func & BIOC_INQ) {
    146 		bio_inq(fd, bioc_dev);
    147 	} else if (func == BIOC_ALARM) {
    148 		bio_alarm(fd, al_arg);
    149 	} else if (func == BIOC_BLINK) {
    150 		bio_setblink(fd, bioc_dev, bl_arg, blink);
    151 	} else if (func == BIOC_SETSTATE) {
    152 		bio_setstate(fd, al_arg);
    153 	}
    154 
    155 	exit(EXIT_SUCCESS);
    156 }
    157 
    158 static void
    159 usage(void)
    160 {
    161 	(void)fprintf(stderr,
    162 		"usage: %s [-hv] [-a alarm-function] "
    163 		"[-b channel:target[.lun]]\n"
    164 		"\t[-H channel:target[.lun]]\n"
    165 		"\t[-u channel:target[.lun]] device\n", getprogname());
    166 	exit(EXIT_FAILURE);
    167 	/* NOTREACHED */
    168 }
    169 
    170 static const char *
    171 str2locator(const char *string, struct locator *location)
    172 {
    173 	const char *errstr;
    174 	char parse[80], *targ, *lun;
    175 
    176 	strlcpy(parse, string, sizeof parse);
    177 	targ = strchr(parse, ':');
    178 	if (targ == NULL)
    179 		return ("target not specified");
    180 	*targ++ = '\0';
    181 
    182 	lun = strchr(targ, '.');
    183 	if (lun != NULL) {
    184 		*lun++ = '\0';
    185 		location->lun = strtonum(lun, 0, 256, &errstr);
    186 		if (errstr)
    187 			return errstr;
    188 	} else
    189 		location->lun = 0;
    190 
    191 	location->target = strtonum(targ, 0, 256, &errstr);
    192 	if (errstr)
    193 		return errstr;
    194 	location->channel = strtonum(parse, 0, 256, &errstr);
    195 	if (errstr)
    196 		return errstr;
    197 	return NULL;
    198 }
    199 
    200 static void
    201 bio_inq(int fd, char *name)
    202 {
    203 	const char *status;
    204 	char size[64], scsiname[16], volname[32];
    205 	char percent[10], seconds[20];
    206 	int rv, i, d, volheader, hotspare, unused;
    207 	char encname[16], serial[32];
    208 	struct bioc_disk bd;
    209 	struct bioc_inq bi;
    210 	struct bioc_vol bv;
    211 
    212 	memset(&bi, 0, sizeof(bi));
    213 
    214 	bi.bi_cookie = bl.bl_cookie;
    215 
    216 	rv = ioctl(fd, BIOCINQ, &bi);
    217 	if (rv)
    218 		errx(EXIT_FAILURE, "BIOCINQ %s", strerror(errno));
    219 
    220 	volheader = 0;
    221 	for (i = 0; i < bi.bi_novol; i++) {
    222 		memset(&bv, 0, sizeof(bv));
    223 		bv.bv_cookie = bl.bl_cookie;
    224 		bv.bv_volid = i;
    225 		bv.bv_percent = -1;
    226 		bv.bv_seconds = 0;
    227 
    228 		rv = ioctl(fd, BIOCVOL, &bv);
    229 		if (rv)
    230 			errx(EXIT_FAILURE, "BIOCVOL %s", strerror(errno));
    231 
    232 		if (!volheader) {
    233 			volheader = 1;
    234 			if (human)
    235 				printf("%10s %-10s %4s %-8s\n",
    236 				    "Volume", "Status", "Size", "Device");
    237 			else
    238 				printf("%10s %-10s %14s %-8s\n",
    239 			    	    "Volume", "Status", "Size", "Device");
    240 		}
    241 
    242 		percent[0] = '\0';
    243 		seconds[0] = '\0';
    244 		if (bv.bv_percent != -1)
    245 			snprintf(percent, sizeof percent,
    246 			    " %d%% done", bv.bv_percent);
    247 		if (bv.bv_seconds)
    248 			snprintf(seconds, sizeof seconds,
    249 			    " %u seconds", bv.bv_seconds);
    250 		switch (bv.bv_status) {
    251 		case BIOC_SVONLINE:
    252 			status = BIOC_SVONLINE_S;
    253 			break;
    254 		case BIOC_SVOFFLINE:
    255 			status = BIOC_SVOFFLINE_S;
    256 			break;
    257 		case BIOC_SVDEGRADED:
    258 			status = BIOC_SVDEGRADED_S;
    259 			break;
    260 		case BIOC_SVBUILDING:
    261 			status = BIOC_SVBUILDING_S;
    262 			break;
    263 		case BIOC_SVREBUILD:
    264 			status = BIOC_SVREBUILD_S;
    265 			break;
    266 		case BIOC_SVSCRUB:
    267 			status = BIOC_SVSCRUB_S;
    268 			break;
    269 		case BIOC_SVINVALID:
    270 		default:
    271 			status = BIOC_SVINVALID_S;
    272 		}
    273 
    274 		snprintf(volname, sizeof volname, "%s %u",
    275 		    bi.bi_dev, bv.bv_volid);
    276 
    277 		if (bv.bv_level == -1 && bv.bv_nodisk == 1) {
    278 			hotspare = 1;
    279 			unused = 0;
    280 		} else if (bv.bv_level == -2 && bv.bv_nodisk == 1) {
    281 			unused = 1;
    282 			hotspare = 0;
    283 		} else {
    284 			unused = 0;
    285 			hotspare = 0;
    286 
    287 			if (human) {
    288 				humanize_number(size, 5,
    289 				    (int64_t)bv.bv_size, "", HN_AUTOSCALE,
    290 				    HN_B | HN_NOSPACE | HN_DECIMAL);
    291 				printf("%10s %-10s %4s %-7s RAID %u%s%s\n",
    292 				    volname, status, size, bv.bv_dev,
    293 				    bv.bv_level, percent, seconds);
    294 			} else {
    295 				snprintf(size, sizeof size, "%14llu",
    296 				    (long long unsigned int)bv.bv_size);
    297 				printf("%10s %-10s %14s %-7s RAID %u%s%s\n",
    298 			    	    volname, status, size, bv.bv_dev,
    299 			    	    bv.bv_level, percent, seconds);
    300 			}
    301 		}
    302 
    303 		for (d = 0; d < bv.bv_nodisk; d++) {
    304 			memset(&bd, 0, sizeof(bd));
    305 			bd.bd_cookie = bl.bl_cookie;
    306 			bd.bd_diskid = d;
    307 			bd.bd_volid = i;
    308 
    309 			rv = ioctl(fd, BIOCDISK, &bd);
    310 			if (rv)
    311 				errx(EXIT_FAILURE, "BIOCDISK %s",
    312 				    strerror(errno));
    313 
    314 			switch (bd.bd_status) {
    315 			case BIOC_SDONLINE:
    316 				status = BIOC_SDONLINE_S;
    317 				break;
    318 			case BIOC_SDOFFLINE:
    319 				status = BIOC_SDOFFLINE_S;
    320 				break;
    321 			case BIOC_SDFAILED:
    322 				status = BIOC_SDFAILED_S;
    323 				break;
    324 			case BIOC_SDREBUILD:
    325 				status = BIOC_SDREBUILD_S;
    326 				break;
    327 			case BIOC_SDHOTSPARE:
    328 				status = BIOC_SDHOTSPARE_S;
    329 				break;
    330 			case BIOC_SDUNUSED:
    331 				status = BIOC_SDUNUSED_S;
    332 				break;
    333 			case BIOC_SDSCRUB:
    334 				status = BIOC_SDSCRUB_S;
    335 				break;
    336 			case BIOC_SDINVALID:
    337 			default:
    338 				status = BIOC_SDINVALID_S;
    339 			}
    340 
    341 			if (hotspare || unused)
    342 				;	/* use volname from parent volume */
    343 			else
    344 				snprintf(volname, sizeof volname, "    %3u",
    345 				    bd.bd_diskid);
    346 
    347 			if (human)
    348 				humanize_number(size, 5,
    349 				    bd.bd_size, "", HN_AUTOSCALE,
    350 				    HN_B | HN_NOSPACE | HN_DECIMAL);
    351 			else
    352 				snprintf(size, sizeof size, "%14llu",
    353 				    (long long unsigned int)bd.bd_size);
    354 			snprintf(scsiname, sizeof scsiname,
    355 			    "%u:%u.%u",
    356 			    bd.bd_channel, bd.bd_target, bd.bd_lun);
    357 			if (bd.bd_procdev[0])
    358 				strlcpy(encname, bd.bd_procdev, sizeof encname);
    359 			else
    360 				strlcpy(encname, "noencl", sizeof encname);
    361 			if (bd.bd_serial[0])
    362 				strlcpy(serial, bd.bd_serial, sizeof serial);
    363 			else
    364 				strlcpy(serial, "unknown serial", sizeof serial);
    365 
    366 			if (human)
    367 				printf("%10s %-10s %4s %-7s %-6s <%s>\n",
    368 				    volname, status, size, scsiname, encname,
    369 				    bd.bd_vendor);
    370 			else
    371 				printf("%10s %-10s %14s %-7s %-6s <%s>\n",
    372 			    	    volname, status, size, scsiname, encname,
    373 			    	    bd.bd_vendor);
    374 			if (verbose)
    375 				printf("%7s %-10s %14s %-7s %-6s '%s'\n",
    376 				    "", "", "", "", "", serial);
    377 		}
    378 	}
    379 }
    380 
    381 static void
    382 bio_alarm(int fd, char *arg)
    383 {
    384 	int rv;
    385 	struct bioc_alarm ba;
    386 
    387 	ba.ba_cookie = bl.bl_cookie;
    388 
    389 	switch (arg[0]) {
    390 	case 'q': /* silence alarm */
    391 		/* FALLTHROUGH */
    392 	case 's':
    393 		ba.ba_opcode = BIOC_SASILENCE;
    394 		break;
    395 
    396 	case 'e': /* enable alarm */
    397 		ba.ba_opcode = BIOC_SAENABLE;
    398 		break;
    399 
    400 	case 'd': /* disable alarm */
    401 		ba.ba_opcode = BIOC_SADISABLE;
    402 		break;
    403 
    404 	case 't': /* test alarm */
    405 		ba.ba_opcode = BIOC_SATEST;
    406 		break;
    407 
    408 	case 'g': /* get alarm state */
    409 		ba.ba_opcode = BIOC_GASTATUS;
    410 		break;
    411 
    412 	default:
    413 		warnx("invalid alarm function: %s", arg);
    414 		return;
    415 	}
    416 
    417 	rv = ioctl(fd, BIOCALARM, &ba);
    418 	if (rv)
    419 		errx(EXIT_FAILURE, "BIOCALARM %s", strerror(errno));
    420 
    421 	if (arg[0] == 'g') {
    422 		printf("alarm is currently %s\n",
    423 		    ba.ba_status ? "enabled" : "disabled");
    424 
    425 	}
    426 }
    427 
    428 static void
    429 bio_setstate(int fd, char *arg)
    430 {
    431 	struct bioc_setstate	bs;
    432 	struct locator		location;
    433 	const char		*errstr;
    434 	int			rv;
    435 
    436 	errstr = str2locator(arg, &location);
    437 	if (errstr)
    438 		errx(1, "Target %s: %s", arg, errstr);
    439 
    440 	bs.bs_cookie = bl.bl_cookie;
    441 	bs.bs_status = BIOC_SSHOTSPARE;
    442 	bs.bs_channel = location.channel;
    443 	bs.bs_target = location.target;
    444 	bs.bs_lun = location.lun;
    445 
    446 	rv = ioctl(fd, BIOCSETSTATE, &bs);
    447 	if (rv)
    448 		errx(EXIT_FAILURE, "BIOCSETSTATE %s", strerror(errno));
    449 }
    450 
    451 static void
    452 bio_setblink(int fd, char *name, char *arg, int blink)
    453 {
    454 	struct locator		location;
    455 	struct bioc_inq		bi;
    456 	struct bioc_vol		bv;
    457 	struct bioc_disk	bd;
    458 	struct bioc_blink	bb;
    459 	const char		*errstr;
    460 	int			v, d, rv;
    461 
    462 	errstr = str2locator(arg, &location);
    463 	if (errstr)
    464 		errx(EXIT_FAILURE, "Target %s: %s", arg, errstr);
    465 
    466 	/* try setting blink on the device directly */
    467 	memset(&bb, 0, sizeof(bb));
    468 	bb.bb_cookie = bl.bl_cookie;
    469 	bb.bb_status = blink;
    470 	bb.bb_target = location.target;
    471 	bb.bb_channel = location.channel;
    472 	rv = ioctl(fd, BIOCBLINK, &bb);
    473 	if (rv == 0)
    474 		return;
    475 
    476 	/* if the blink didnt work, try to find something that will */
    477 
    478 	memset(&bi, 0, sizeof(bi));
    479 	bi.bi_cookie = bl.bl_cookie;
    480 	rv = ioctl(fd, BIOCINQ, &bi);
    481 	if (rv)
    482 		errx(EXIT_FAILURE, "BIOCINQ %s", strerror(errno));
    483 
    484 	for (v = 0; v < bi.bi_novol; v++) {
    485 		memset(&bv, 0, sizeof(bv));
    486 		bv.bv_cookie = bl.bl_cookie;
    487 		bv.bv_volid = v;
    488 		rv = ioctl(fd, BIOCVOL, &bv);
    489 		if (rv == -1)
    490 			errx(EXIT_FAILURE, "BIOCVOL %s", strerror(errno));
    491 
    492 		for (d = 0; d < bv.bv_nodisk; d++) {
    493 			memset(&bd, 0, sizeof(bd));
    494 			bd.bd_cookie = bl.bl_cookie;
    495 			bd.bd_volid = v;
    496 			bd.bd_diskid = d;
    497 
    498 			rv = ioctl(fd, BIOCDISK, &bd);
    499 			if (rv == -1)
    500 				errx(EXIT_FAILURE, "BIOCDISK %s",
    501 				    strerror(errno));
    502 
    503 			if (bd.bd_channel == location.channel &&
    504 			    bd.bd_target == location.target &&
    505 			    bd.bd_lun == location.lun) {
    506 				if (bd.bd_procdev[0] != '\0') {
    507 					bio_blink(fd, bd.bd_procdev,
    508 					    location.target, blink);
    509 				} else
    510 					warnx("Disk %s is not in an enclosure", arg);
    511 				return;
    512 			}
    513 		}
    514 	}
    515 
    516 	warnx("Disk %s does not exist", arg);
    517 	return;
    518 }
    519 
    520 static void
    521 bio_blink(int fd, char *enclosure, int target, int blinktype)
    522 {
    523 	struct bio_locate	bio;
    524 	struct bioc_blink	blink;
    525 	int			rv;
    526 
    527 	bio.bl_name = enclosure;
    528 	rv = ioctl(fd, BIOCLOCATE, &bio);
    529 	if (rv == -1)
    530 		errx(EXIT_FAILURE,
    531 		    "Can't locate %s device via %s", enclosure, "/dev/bio");
    532 
    533 	memset(&blink, 0, sizeof(blink));
    534 	blink.bb_cookie = bio.bl_cookie;
    535 	blink.bb_status = blinktype;
    536 	blink.bb_target = target;
    537 
    538 	rv = ioctl(fd, BIOCBLINK, &blink);
    539 	if (rv == -1)
    540 		errx(EXIT_FAILURE, "BIOCBLINK %s", strerror(errno));
    541 }
    542