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