Home | History | Annotate | Line # | Download | only in bioctl
bioctl.c revision 1.4
      1 /* $NetBSD: bioctl.c,v 1.4 2007/12/05 14:28:14 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.4 2007/12/05 14:28:14 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 			printf("%10s %-10s %14s %-8s\n",
    235 			    "Volume", "Status", "Size", "Device");
    236 		}
    237 
    238 		percent[0] = '\0';
    239 		seconds[0] = '\0';
    240 		if (bv.bv_percent != -1)
    241 			snprintf(percent, sizeof percent,
    242 			    " %d%% done", bv.bv_percent);
    243 		if (bv.bv_seconds)
    244 			snprintf(seconds, sizeof seconds,
    245 			    " %u seconds", bv.bv_seconds);
    246 		switch (bv.bv_status) {
    247 		case BIOC_SVONLINE:
    248 			status = BIOC_SVONLINE_S;
    249 			break;
    250 		case BIOC_SVOFFLINE:
    251 			status = BIOC_SVOFFLINE_S;
    252 			break;
    253 		case BIOC_SVDEGRADED:
    254 			status = BIOC_SVDEGRADED_S;
    255 			break;
    256 		case BIOC_SVBUILDING:
    257 			status = BIOC_SVBUILDING_S;
    258 			break;
    259 		case BIOC_SVREBUILD:
    260 			status = BIOC_SVREBUILD_S;
    261 			break;
    262 		case BIOC_SVSCRUB:
    263 			status = BIOC_SVSCRUB_S;
    264 			break;
    265 		case BIOC_SVINVALID:
    266 		default:
    267 			status = BIOC_SVINVALID_S;
    268 		}
    269 
    270 		snprintf(volname, sizeof volname, "%s %u",
    271 		    bi.bi_dev, bv.bv_volid);
    272 
    273 		if (bv.bv_level == -1 && bv.bv_nodisk == 1) {
    274 			hotspare = 1;
    275 			unused = 0;
    276 		} else if (bv.bv_level == -2 && bv.bv_nodisk == 1) {
    277 			unused = 1;
    278 			hotspare = 0;
    279 		} else {
    280 			unused = 0;
    281 			hotspare = 0;
    282 
    283 			if (human)
    284 				humanize_number(size, 5,
    285 				    (int64_t)bv.bv_size, "", HN_AUTOSCALE,
    286 				    HN_B | HN_NOSPACE | HN_DECIMAL);
    287 			else
    288 				snprintf(size, sizeof size, "%14llu",
    289 				    (long long unsigned int)bv.bv_size);
    290 			printf("%10s %-10s %14s %-7s RAID%u%s%s\n",
    291 			    volname, status, size, bv.bv_dev,
    292 			    bv.bv_level, percent, seconds);
    293 		}
    294 
    295 		for (d = 0; d < bv.bv_nodisk; d++) {
    296 			memset(&bd, 0, sizeof(bd));
    297 			bd.bd_cookie = bl.bl_cookie;
    298 			bd.bd_diskid = d;
    299 			bd.bd_volid = i;
    300 
    301 			rv = ioctl(fd, BIOCDISK, &bd);
    302 			if (rv)
    303 				errx(EXIT_FAILURE, "BIOCDISK %s",
    304 				    strerror(errno));
    305 
    306 			switch (bd.bd_status) {
    307 			case BIOC_SDONLINE:
    308 				status = BIOC_SDONLINE_S;
    309 				break;
    310 			case BIOC_SDOFFLINE:
    311 				status = BIOC_SDOFFLINE_S;
    312 				break;
    313 			case BIOC_SDFAILED:
    314 				status = BIOC_SDFAILED_S;
    315 				break;
    316 			case BIOC_SDREBUILD:
    317 				status = BIOC_SDREBUILD_S;
    318 				break;
    319 			case BIOC_SDHOTSPARE:
    320 				status = BIOC_SDHOTSPARE_S;
    321 				break;
    322 			case BIOC_SDUNUSED:
    323 				status = BIOC_SDUNUSED_S;
    324 				break;
    325 			case BIOC_SDSCRUB:
    326 				status = BIOC_SDSCRUB_S;
    327 				break;
    328 			case BIOC_SDINVALID:
    329 			default:
    330 				status = BIOC_SDINVALID_S;
    331 			}
    332 
    333 			if (hotspare || unused)
    334 				;	/* use volname from parent volume */
    335 			else
    336 				snprintf(volname, sizeof volname, "    %3u",
    337 				    bd.bd_diskid);
    338 
    339 			if (human)
    340 				humanize_number(size, 5,
    341 				    bd.bd_size, "", HN_AUTOSCALE,
    342 				    HN_B | HN_NOSPACE | HN_DECIMAL);
    343 			else
    344 				snprintf(size, sizeof size, "%14llu",
    345 				    (long long unsigned int)bd.bd_size);
    346 			snprintf(scsiname, sizeof scsiname,
    347 			    "%u:%u.%u",
    348 			    bd.bd_channel, bd.bd_target, bd.bd_lun);
    349 			if (bd.bd_procdev[0])
    350 				strlcpy(encname, bd.bd_procdev, sizeof encname);
    351 			else
    352 				strlcpy(encname, "noencl", sizeof encname);
    353 			if (bd.bd_serial[0])
    354 				strlcpy(serial, bd.bd_serial, sizeof serial);
    355 			else
    356 				strlcpy(serial, "unknown serial", sizeof serial);
    357 
    358 			printf("%10s %-10s %14s %-7s %-6s <%s>\n",
    359 			    volname, status, size, scsiname, encname,
    360 			    bd.bd_vendor);
    361 			if (verbose)
    362 				printf("%7s %-10s %14s %-7s %-6s '%s'\n",
    363 				    "", "", "", "", "", serial);
    364 		}
    365 	}
    366 }
    367 
    368 static void
    369 bio_alarm(int fd, char *arg)
    370 {
    371 	int rv;
    372 	struct bioc_alarm ba;
    373 
    374 	ba.ba_cookie = bl.bl_cookie;
    375 
    376 	switch (arg[0]) {
    377 	case 'q': /* silence alarm */
    378 		/* FALLTHROUGH */
    379 	case 's':
    380 		ba.ba_opcode = BIOC_SASILENCE;
    381 		break;
    382 
    383 	case 'e': /* enable alarm */
    384 		ba.ba_opcode = BIOC_SAENABLE;
    385 		break;
    386 
    387 	case 'd': /* disable alarm */
    388 		ba.ba_opcode = BIOC_SADISABLE;
    389 		break;
    390 
    391 	case 't': /* test alarm */
    392 		ba.ba_opcode = BIOC_SATEST;
    393 		break;
    394 
    395 	case 'g': /* get alarm state */
    396 		ba.ba_opcode = BIOC_GASTATUS;
    397 		break;
    398 
    399 	default:
    400 		warnx("invalid alarm function: %s", arg);
    401 		return;
    402 	}
    403 
    404 	rv = ioctl(fd, BIOCALARM, &ba);
    405 	if (rv)
    406 		errx(EXIT_FAILURE, "BIOCALARM %s", strerror(errno));
    407 
    408 	if (arg[0] == 'g') {
    409 		printf("alarm is currently %s\n",
    410 		    ba.ba_status ? "enabled" : "disabled");
    411 
    412 	}
    413 }
    414 
    415 static void
    416 bio_setstate(int fd, char *arg)
    417 {
    418 	struct bioc_setstate	bs;
    419 	struct locator		location;
    420 	const char		*errstr;
    421 	int			rv;
    422 
    423 	errstr = str2locator(arg, &location);
    424 	if (errstr)
    425 		errx(1, "Target %s: %s", arg, errstr);
    426 
    427 	bs.bs_cookie = bl.bl_cookie;
    428 	bs.bs_status = BIOC_SSHOTSPARE;
    429 	bs.bs_channel = location.channel;
    430 	bs.bs_target = location.target;
    431 	bs.bs_lun = location.lun;
    432 
    433 	rv = ioctl(fd, BIOCSETSTATE, &bs);
    434 	if (rv)
    435 		errx(EXIT_FAILURE, "BIOCSETSTATE %s", strerror(errno));
    436 }
    437 
    438 static void
    439 bio_setblink(int fd, char *name, char *arg, int blink)
    440 {
    441 	struct locator		location;
    442 	struct bioc_inq		bi;
    443 	struct bioc_vol		bv;
    444 	struct bioc_disk	bd;
    445 	struct bioc_blink	bb;
    446 	const char		*errstr;
    447 	int			v, d, rv;
    448 
    449 	errstr = str2locator(arg, &location);
    450 	if (errstr)
    451 		errx(EXIT_FAILURE, "Target %s: %s", arg, errstr);
    452 
    453 	/* try setting blink on the device directly */
    454 	memset(&bb, 0, sizeof(bb));
    455 	bb.bb_cookie = bl.bl_cookie;
    456 	bb.bb_status = blink;
    457 	bb.bb_target = location.target;
    458 	bb.bb_channel = location.channel;
    459 	rv = ioctl(fd, BIOCBLINK, &bb);
    460 	if (rv == 0)
    461 		return;
    462 
    463 	/* if the blink didnt work, try to find something that will */
    464 
    465 	memset(&bi, 0, sizeof(bi));
    466 	bi.bi_cookie = bl.bl_cookie;
    467 	rv = ioctl(fd, BIOCINQ, &bi);
    468 	if (rv)
    469 		errx(EXIT_FAILURE, "BIOCINQ %s", strerror(errno));
    470 
    471 	for (v = 0; v < bi.bi_novol; v++) {
    472 		memset(&bv, 0, sizeof(bv));
    473 		bv.bv_cookie = bl.bl_cookie;
    474 		bv.bv_volid = v;
    475 		rv = ioctl(fd, BIOCVOL, &bv);
    476 		if (rv == -1)
    477 			errx(EXIT_FAILURE, "BIOCVOL %s", strerror(errno));
    478 
    479 		for (d = 0; d < bv.bv_nodisk; d++) {
    480 			memset(&bd, 0, sizeof(bd));
    481 			bd.bd_cookie = bl.bl_cookie;
    482 			bd.bd_volid = v;
    483 			bd.bd_diskid = d;
    484 
    485 			rv = ioctl(fd, BIOCDISK, &bd);
    486 			if (rv == -1)
    487 				errx(EXIT_FAILURE, "BIOCDISK %s",
    488 				    strerror(errno));
    489 
    490 			if (bd.bd_channel == location.channel &&
    491 			    bd.bd_target == location.target &&
    492 			    bd.bd_lun == location.lun) {
    493 				if (bd.bd_procdev[0] != '\0') {
    494 					bio_blink(fd, bd.bd_procdev,
    495 					    location.target, blink);
    496 				} else
    497 					warnx("Disk %s is not in an enclosure", arg);
    498 				return;
    499 			}
    500 		}
    501 	}
    502 
    503 	warnx("Disk %s does not exist", arg);
    504 	return;
    505 }
    506 
    507 static void
    508 bio_blink(int fd, char *enclosure, int target, int blinktype)
    509 {
    510 	struct bio_locate	bio;
    511 	struct bioc_blink	blink;
    512 	int			rv;
    513 
    514 	bio.bl_name = enclosure;
    515 	rv = ioctl(fd, BIOCLOCATE, &bio);
    516 	if (rv == -1)
    517 		errx(EXIT_FAILURE,
    518 		    "Can't locate %s device via %s", enclosure, "/dev/bio");
    519 
    520 	memset(&blink, 0, sizeof(blink));
    521 	blink.bb_cookie = bio.bl_cookie;
    522 	blink.bb_status = blinktype;
    523 	blink.bb_target = target;
    524 
    525 	rv = ioctl(fd, BIOCBLINK, &blink);
    526 	if (rv == -1)
    527 		errx(EXIT_FAILURE, "BIOCBLINK %s", strerror(errno));
    528 }
    529