xref: /netbsd-src/sbin/bioctl/bioctl.c (revision 7fa608457b817eca6e0977b37f758ae064f3c99c)
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