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