Home | History | Annotate | Line # | Download | only in bioctl
bioctl.c revision 1.2
      1 /* $NetBSD: bioctl.c,v 1.2 2007/11/04 08:25:05 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.2 2007/11/04 08:25:05 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 debug;
     69 static int human;
     70 static int verbose;
     71 
     72 static struct bio_locate bl;
     73 
     74 int
     75 main(int argc, char *argv[])
     76 {
     77 	uint64_t func = 0;
     78 	char *bioc_dev, *al_arg, *bl_arg;
     79 	int fd, ch, rv, blink;
     80 
     81 	bioc_dev = al_arg = bl_arg = NULL;
     82 	fd = ch = rv = blink = 0;
     83 
     84 	if (argc < 2)
     85 		usage();
     86 
     87 	setprogname(*argv);
     88 
     89 	while ((ch = getopt(argc, argv, "b:c:l:u:H:ha:Dv")) != -1) {
     90 		switch (ch) {
     91 		case 'a': /* alarm */
     92 			func |= BIOC_ALARM;
     93 			al_arg = optarg;
     94 			break;
     95 		case 'b': /* blink */
     96 			func |= BIOC_BLINK;
     97 			blink = BIOC_SBBLINK;
     98 			bl_arg = optarg;
     99 			break;
    100 		case 'u': /* unblink */
    101 			func |= BIOC_BLINK;
    102 			blink = BIOC_SBUNBLINK;
    103 			bl_arg = optarg;
    104 			break;
    105 		case 'D': /* debug */
    106 			debug = 1;
    107 			break;
    108 		case 'H': /* set hotspare */
    109 			func |= BIOC_SETSTATE;
    110 			al_arg = optarg;
    111 			break;
    112 		case 'h':
    113 			human = 1;
    114 			break;
    115 		case 'i': /* inquiry */
    116 			func |= BIOC_INQ;
    117 			break;
    118 		case 'v':
    119 			verbose = 1;
    120 			break;
    121 		default:
    122 			usage();
    123 			/* NOTREACHED */
    124 		}
    125 	}
    126 	argc -= optind;
    127 	argv += optind;
    128 
    129 	if (argc != 1)
    130 		usage();
    131 
    132 	if (func == 0)
    133 		func |= BIOC_INQ;
    134 
    135 	bioc_dev = argv[0];
    136 
    137 	if (bioc_dev) {
    138 		fd = open("/dev/bio", O_RDWR);
    139 		if (fd == -1)
    140 			err(EXIT_FAILURE, "Can't open %s", "/dev/bio");
    141 
    142 		bl.bl_name = bioc_dev;
    143 		rv = ioctl(fd, BIOCLOCATE, &bl);
    144 		if (rv == -1)
    145 			errx(EXIT_FAILURE, "Can't locate %s device via %s",
    146 			    bl.bl_name, "/dev/bio");
    147 	}
    148 
    149 	if (debug)
    150 		warnx("cookie = %p", bl.bl_cookie);
    151 
    152 	if (func & BIOC_INQ) {
    153 		bio_inq(fd, bioc_dev);
    154 	} else if (func == BIOC_ALARM) {
    155 		bio_alarm(fd, al_arg);
    156 	} else if (func == BIOC_BLINK) {
    157 		bio_setblink(fd, bioc_dev, bl_arg, blink);
    158 	} else if (func == BIOC_SETSTATE) {
    159 		bio_setstate(fd, al_arg);
    160 	}
    161 
    162 	exit(EXIT_SUCCESS);
    163 }
    164 
    165 static void
    166 usage(void)
    167 {
    168 	(void)fprintf(stderr,
    169 		"usage: %s [-Dhv] [-a alarm-function] "
    170 		"[-b channel:target[.lun]]\n"
    171 		"\t[-H channel:target[.lun]]\n"
    172 		"\t[-u channel:target[.lun]] device\n", getprogname());
    173 	exit(EXIT_FAILURE);
    174 	/* NOTREACHED */
    175 }
    176 
    177 static const char *
    178 str2locator(const char *string, struct locator *location)
    179 {
    180 	const char *errstr;
    181 	char parse[80], *targ, *lun;
    182 
    183 	strlcpy(parse, string, sizeof parse);
    184 	targ = strchr(parse, ':');
    185 	if (targ == NULL)
    186 		return ("target not specified");
    187 	*targ++ = '\0';
    188 
    189 	lun = strchr(targ, '.');
    190 	if (lun != NULL) {
    191 		*lun++ = '\0';
    192 		location->lun = strtonum(lun, 0, 256, &errstr);
    193 		if (errstr)
    194 			return errstr;
    195 	} else
    196 		location->lun = 0;
    197 
    198 	location->target = strtonum(targ, 0, 256, &errstr);
    199 	if (errstr)
    200 		return errstr;
    201 	location->channel = strtonum(parse, 0, 256, &errstr);
    202 	if (errstr)
    203 		return errstr;
    204 	return NULL;
    205 }
    206 
    207 static void
    208 bio_inq(int fd, char *name)
    209 {
    210 	const char *status;
    211 	char size[64], scsiname[16], volname[32];
    212 	char percent[10], seconds[20];
    213 	int rv, i, d, volheader, hotspare, unused;
    214 	char encname[16], serial[32];
    215 	struct bioc_disk bd;
    216 	struct bioc_inq bi;
    217 	struct bioc_vol bv;
    218 
    219 	memset(&bi, 0, sizeof(bi));
    220 
    221 	if (debug)
    222 		printf("bio_inq\n");
    223 
    224 	bi.bi_cookie = bl.bl_cookie;
    225 
    226 	rv = ioctl(fd, BIOCINQ, &bi);
    227 	if (rv == -1) {
    228 		warn("BIOCINQ");
    229 		return;
    230 	}
    231 
    232 	if (debug)
    233 		printf("bio_inq { %p, %s, %d, %d }\n",
    234 		    bi.bi_cookie,
    235 		    bi.bi_dev,
    236 		    bi.bi_novol,
    237 		    bi.bi_nodisk);
    238 
    239 	volheader = 0;
    240 	for (i = 0; i < bi.bi_novol; i++) {
    241 		memset(&bv, 0, sizeof(bv));
    242 		bv.bv_cookie = bl.bl_cookie;
    243 		bv.bv_volid = i;
    244 		bv.bv_percent = -1;
    245 		bv.bv_seconds = 0;
    246 
    247 		rv = ioctl(fd, BIOCVOL, &bv);
    248 		if (rv == -1) {
    249 			warn("BIOCVOL");
    250 			return;
    251 		}
    252 
    253 		if (name && strcmp(name, bv.bv_dev) != 0)
    254 			continue;
    255 
    256 		if (!volheader) {
    257 			volheader = 1;
    258 			printf("%-7s %-10s %14s %-8s\n",
    259 			    "Volume", "Status", "Size", "Device");
    260 		}
    261 
    262 		percent[0] = '\0';
    263 		seconds[0] = '\0';
    264 		if (bv.bv_percent != -1)
    265 			snprintf(percent, sizeof percent,
    266 			    " %d%% done", bv.bv_percent);
    267 		if (bv.bv_seconds)
    268 			snprintf(seconds, sizeof seconds,
    269 			    " %u seconds", bv.bv_seconds);
    270 		switch (bv.bv_status) {
    271 		case BIOC_SVONLINE:
    272 			status = BIOC_SVONLINE_S;
    273 			break;
    274 		case BIOC_SVOFFLINE:
    275 			status = BIOC_SVOFFLINE_S;
    276 			break;
    277 		case BIOC_SVDEGRADED:
    278 			status = BIOC_SVDEGRADED_S;
    279 			break;
    280 		case BIOC_SVBUILDING:
    281 			status = BIOC_SVBUILDING_S;
    282 			break;
    283 		case BIOC_SVREBUILD:
    284 			status = BIOC_SVREBUILD_S;
    285 			break;
    286 		case BIOC_SVSCRUB:
    287 			status = BIOC_SVSCRUB_S;
    288 			break;
    289 		case BIOC_SVINVALID:
    290 		default:
    291 			status = BIOC_SVINVALID_S;
    292 		}
    293 
    294 		snprintf(volname, sizeof volname, "%s %u",
    295 		    bi.bi_dev, bv.bv_volid);
    296 
    297 		if (bv.bv_level == -1 && bv.bv_nodisk == 1) {
    298 			hotspare = 1;
    299 			unused = 0;
    300 		} else if (bv.bv_level == -2 && bv.bv_nodisk == 1) {
    301 			unused = 1;
    302 			hotspare = 0;
    303 		} else {
    304 			unused = 0;
    305 			hotspare = 0;
    306 
    307 			if (human)
    308 				humanize_number(size, 5,
    309 				    (int64_t)bv.bv_size, "", HN_AUTOSCALE,
    310 				    HN_B | HN_NOSPACE | HN_DECIMAL);
    311 			else
    312 				snprintf(size, sizeof size, "%14llu",
    313 				    (long long unsigned int)bv.bv_size);
    314 			printf("%7s %-10s %14s %-7s RAID%u%s%s\n",
    315 			    volname, status, size, bv.bv_dev,
    316 			    bv.bv_level, percent, seconds);
    317 		}
    318 
    319 		for (d = 0; d < bv.bv_nodisk; d++) {
    320 			memset(&bd, 0, sizeof(bd));
    321 			bd.bd_cookie = bl.bl_cookie;
    322 			bd.bd_diskid = d;
    323 			bd.bd_volid = i;
    324 
    325 			rv = ioctl(fd, BIOCDISK, &bd);
    326 			if (rv == -1) {
    327 				warn("BIOCDISK");
    328 				return;
    329 			}
    330 
    331 			switch (bd.bd_status) {
    332 			case BIOC_SDONLINE:
    333 				status = BIOC_SDONLINE_S;
    334 				break;
    335 			case BIOC_SDOFFLINE:
    336 				status = BIOC_SDOFFLINE_S;
    337 				break;
    338 			case BIOC_SDFAILED:
    339 				status = BIOC_SDFAILED_S;
    340 				break;
    341 			case BIOC_SDREBUILD:
    342 				status = BIOC_SDREBUILD_S;
    343 				break;
    344 			case BIOC_SDHOTSPARE:
    345 				status = BIOC_SDHOTSPARE_S;
    346 				break;
    347 			case BIOC_SDUNUSED:
    348 				status = BIOC_SDUNUSED_S;
    349 				break;
    350 			case BIOC_SDSCRUB:
    351 				status = BIOC_SDSCRUB_S;
    352 				break;
    353 			case BIOC_SDINVALID:
    354 			default:
    355 				status = BIOC_SDINVALID_S;
    356 			}
    357 
    358 			if (hotspare || unused)
    359 				;	/* use volname from parent volume */
    360 			else
    361 				snprintf(volname, sizeof volname, "    %3u",
    362 				    bd.bd_diskid);
    363 
    364 			if (human)
    365 				humanize_number(size, 5,
    366 				    bd.bd_size, "", HN_AUTOSCALE,
    367 				    HN_B | HN_NOSPACE | HN_DECIMAL);
    368 			else
    369 				snprintf(size, sizeof size, "%14llu",
    370 				    (long long unsigned int)bd.bd_size);
    371 			snprintf(scsiname, sizeof scsiname,
    372 			    "%u:%u.%u",
    373 			    bd.bd_channel, bd.bd_target, bd.bd_lun);
    374 			if (bd.bd_procdev[0])
    375 				strlcpy(encname, bd.bd_procdev, sizeof encname);
    376 			else
    377 				strlcpy(encname, "noencl", sizeof encname);
    378 			if (bd.bd_serial[0])
    379 				strlcpy(serial, bd.bd_serial, sizeof serial);
    380 			else
    381 				strlcpy(serial, "unknown serial", sizeof serial);
    382 
    383 			printf("%7s %-10s %14s %-7s %-6s <%s>\n",
    384 			    volname, status, size, scsiname, encname,
    385 			    bd.bd_vendor);
    386 			if (verbose)
    387 				printf("%7s %-10s %14s %-7s %-6s '%s'\n",
    388 				    "", "", "", "", "", serial);
    389 		}
    390 	}
    391 }
    392 
    393 static void
    394 bio_alarm(int fd, char *arg)
    395 {
    396 	int rv;
    397 	struct bioc_alarm ba;
    398 
    399 	ba.ba_cookie = bl.bl_cookie;
    400 
    401 	switch (arg[0]) {
    402 	case 'q': /* silence alarm */
    403 		/* FALLTHROUGH */
    404 	case 's':
    405 		ba.ba_opcode = BIOC_SASILENCE;
    406 		break;
    407 
    408 	case 'e': /* enable alarm */
    409 		ba.ba_opcode = BIOC_SAENABLE;
    410 		break;
    411 
    412 	case 'd': /* disable alarm */
    413 		ba.ba_opcode = BIOC_SADISABLE;
    414 		break;
    415 
    416 	case 't': /* test alarm */
    417 		ba.ba_opcode = BIOC_SATEST;
    418 		break;
    419 
    420 	case 'g': /* get alarm state */
    421 		ba.ba_opcode = BIOC_GASTATUS;
    422 		break;
    423 
    424 	default:
    425 		warnx("invalid alarm function: %s", arg);
    426 		return;
    427 	}
    428 
    429 	rv = ioctl(fd, BIOCALARM, &ba);
    430 	if (rv == -1) {
    431 		warn("BIOCALARM");
    432 		return;
    433 	}
    434 
    435 	if (arg[0] == 'g') {
    436 		printf("alarm is currently %s\n",
    437 		    ba.ba_status ? "enabled" : "disabled");
    438 
    439 	}
    440 }
    441 
    442 static void
    443 bio_setstate(int fd, char *arg)
    444 {
    445 	struct bioc_setstate	bs;
    446 	struct locator		location;
    447 	const char		*errstr;
    448 	int			rv;
    449 
    450 	errstr = str2locator(arg, &location);
    451 	if (errstr)
    452 		errx(1, "Target %s: %s", arg, errstr);
    453 
    454 	bs.bs_cookie = bl.bl_cookie;
    455 	bs.bs_status = BIOC_SSHOTSPARE;
    456 	bs.bs_channel = location.channel;
    457 	bs.bs_target = location.target;
    458 	bs.bs_lun = location.lun;
    459 
    460 	rv = ioctl(fd, BIOCSETSTATE, &bs);
    461 	if (rv == -1) {
    462 		warn("BIOCSETSTATE");
    463 		return;
    464 	}
    465 }
    466 
    467 static void
    468 bio_setblink(int fd, char *name, char *arg, int blink)
    469 {
    470 	struct locator		location;
    471 	struct bioc_inq		bi;
    472 	struct bioc_vol		bv;
    473 	struct bioc_disk	bd;
    474 	struct bioc_blink	bb;
    475 	const char		*errstr;
    476 	int			v, d, rv;
    477 
    478 	errstr = str2locator(arg, &location);
    479 	if (errstr)
    480 		errx(1, "Target %s: %s", arg, errstr);
    481 
    482 	/* try setting blink on the device directly */
    483 	memset(&bb, 0, sizeof(bb));
    484 	bb.bb_cookie = bl.bl_cookie;
    485 	bb.bb_status = blink;
    486 	bb.bb_target = location.target;
    487 	bb.bb_channel = location.channel;
    488 	rv = ioctl(fd, BIOCBLINK, &bb);
    489 	if (rv == 0)
    490 		return;
    491 
    492 	/* if the blink didnt work, try to find something that will */
    493 
    494 	memset(&bi, 0, sizeof(bi));
    495 	bi.bi_cookie = bl.bl_cookie;
    496 	rv = ioctl(fd, BIOCINQ, &bi);
    497 	if (rv == -1) {
    498 		warn("BIOCINQ");
    499 		return;
    500 	}
    501 
    502 	for (v = 0; v < bi.bi_novol; v++) {
    503 		memset(&bv, 0, sizeof(bv));
    504 		bv.bv_cookie = bl.bl_cookie;
    505 		bv.bv_volid = v;
    506 		rv = ioctl(fd, BIOCVOL, &bv);
    507 		if (rv == -1) {
    508 			warn("BIOCVOL");
    509 			return;
    510 		}
    511 
    512 		if (name && strcmp(name, bv.bv_dev) != 0)
    513 			continue;
    514 
    515 		for (d = 0; d < bv.bv_nodisk; d++) {
    516 			memset(&bd, 0, sizeof(bd));
    517 			bd.bd_cookie = bl.bl_cookie;
    518 			bd.bd_volid = v;
    519 			bd.bd_diskid = d;
    520 
    521 			rv = ioctl(fd, BIOCDISK, &bd);
    522 			if (rv == -1) {
    523 				warn("BIOCDISK");
    524 				return;
    525 			}
    526 
    527 			if (bd.bd_channel == location.channel &&
    528 			    bd.bd_target == location.target &&
    529 			    bd.bd_lun == location.lun) {
    530 				if (bd.bd_procdev[0] != '\0') {
    531 					bio_blink(fd, bd.bd_procdev,
    532 					    location.target, blink);
    533 				} else
    534 					warnx("Disk %s is not in an enclosure", arg);
    535 				return;
    536 			}
    537 		}
    538 	}
    539 
    540 	warnx("Disk %s does not exist", arg);
    541 	return;
    542 }
    543 
    544 static void
    545 bio_blink(int fd, char *enclosure, int target, int blinktype)
    546 {
    547 	struct bio_locate	bio;
    548 	struct bioc_blink	blink;
    549 	int			rv;
    550 
    551 	bio.bl_name = enclosure;
    552 	rv = ioctl(fd, BIOCLOCATE, &bio);
    553 	if (rv == -1)
    554 		errx(1, "Can't locate %s device via %s", enclosure, "/dev/bio");
    555 
    556 	memset(&blink, 0, sizeof(blink));
    557 	blink.bb_cookie = bio.bl_cookie;
    558 	blink.bb_status = blinktype;
    559 	blink.bb_target = target;
    560 
    561 	rv = ioctl(fd, BIOCBLINK, &blink);
    562 	if (rv == -1)
    563 		warn("BIOCBLINK");
    564 }
    565