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