bioctl.c revision 1.1.2.3 1 /* $NetBSD: bioctl.c,v 1.1.2.3 2008/08/29 21:24:18 bouyer Exp $ */
2 /* $OpenBSD: bioctl.c,v 1.52 2007/03/20 15:26:06 jmc Exp $ */
3
4 /*
5 * Copyright (c) 2007, 2008 Juan Romero Pardines
6 * Copyright (c) 2004, 2005 Marco Peereboom
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
22 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 */
31 #include <sys/cdefs.h>
32
33 #ifndef lint
34 __RCSID("$NetBSD: bioctl.c,v 1.1.2.3 2008/08/29 21:24:18 bouyer Exp $");
35 #endif
36
37 #include <sys/types.h>
38 #include <sys/ioctl.h>
39 #include <sys/param.h>
40 #include <sys/queue.h>
41 #include <dev/biovar.h>
42
43 #include <errno.h>
44 #include <err.h>
45 #include <fcntl.h>
46 #include <util.h>
47 #include <stdbool.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 #include <ctype.h>
53 #include <util.h>
54 #include "strtonum.h"
55
56 struct command {
57 const char *cmd_name;
58 const char *arg_names;
59 void (*cmd_func)(int, int, char **);
60 };
61
62 struct biotmp {
63 struct bioc_inq *bi;
64 struct bioc_vol *bv;
65 char volname[64];
66 int fd;
67 int volid;
68 int diskid;
69 bool format;
70 bool show_disknovol;
71 };
72
73 struct locator {
74 int channel;
75 int target;
76 int lun;
77 };
78
79 int dehumanize_number(const char *, int64_t *);
80
81
82 static void usage(void);
83 static void bio_alarm(int, int, char **);
84 static void bio_show_common(int, int, char **);
85 static int bio_show_volumes(struct biotmp *);
86 static void bio_show_disks(struct biotmp *);
87 static void bio_setblink(int, int, char **);
88 static void bio_blink(int, char *, int, int);
89 static void bio_setstate_hotspare(int, int, char **);
90 static void bio_setstate_passthru(int, int, char **);
91 static void bio_setstate_common(int, char *, struct bioc_setstate *,
92 struct locator *);
93 static void bio_setstate_consistency(int, int, char **);
94 static void bio_volops_create(int, int, char **);
95 #ifdef notyet
96 static void bio_volops_modify(int, int, char **);
97 #endif
98 static void bio_volops_remove(int, int, char **);
99
100 static const char *str2locator(const char *, struct locator *);
101
102 static struct bio_locate bl;
103 static struct command commands[] = {
104 {
105 "show",
106 "[disks] | [volumes]",
107 bio_show_common },
108 {
109 "alarm",
110 "[enable] | [disable] | [silence] | [test]",
111 bio_alarm },
112 {
113 "blink",
114 "start [channel:target[.lun]] | stop [channel:target[.lun]]",
115 bio_setblink },
116 {
117 "hotspare",
118 "add channel:target.lun | remove channel:target.lun",
119 bio_setstate_hotspare },
120 {
121 "passthru",
122 "add DISKID channel:target.lun | remove channel:target.lun",
123 bio_setstate_passthru },
124 {
125 "check",
126 "start VOLID | stop VOLID",
127 bio_setstate_consistency },
128 {
129 "create",
130 "volume VOLID DISKIDs [SIZE] STRIPE RAID_LEVEL channel:target.lun",
131 bio_volops_create },
132 #ifdef notyet
133 {
134 "modify",
135 "volume VOLID STRIPE RAID_LEVEL channel:target.lun",
136 bio_volops_modify },
137 #endif
138 {
139 "remove",
140 "volume VOLID channel:target.lun",
141 bio_volops_remove },
142
143 { NULL, NULL, NULL }
144 };
145
146 int
147 main(int argc, char **argv)
148 {
149 char *dvname;
150 const char *cmdname;
151 int fd = 0, i;
152
153 /* Must have at least: device command */
154 if (argc < 3)
155 usage();
156
157 /* Skip program name, get and skip device name and command */
158 setprogname(*argv);
159 dvname = argv[1];
160 cmdname = argv[2];
161 argv += 3;
162 argc -= 3;
163
164 /* Look up and call the command */
165 for (i = 0; commands[i].cmd_name != NULL; i++)
166 if (strcmp(cmdname, commands[i].cmd_name) == 0)
167 break;
168 if (commands[i].cmd_name == NULL)
169 errx(EXIT_FAILURE, "unknown command: %s", cmdname);
170
171 /* Locate the device by issuing the BIOCLOCATE ioctl */
172 fd = open("/dev/bio", O_RDWR);
173 if (fd == -1)
174 err(EXIT_FAILURE, "Can't open /dev/bio");
175
176 bl.bl_name = dvname;
177 if (ioctl(fd, BIOCLOCATE, &bl) == -1)
178 errx(EXIT_FAILURE, "Can't locate %s device via /dev/bio",
179 bl.bl_name);
180
181 /* and execute the command */
182 (*commands[i].cmd_func)(fd, argc, argv);
183
184 (void)close(fd);
185 exit(EXIT_SUCCESS);
186 }
187
188 static void
189 usage(void)
190 {
191 int i;
192
193 (void)fprintf(stderr, "usage: %s device command [arg [...]]\n",
194 getprogname());
195
196 (void)fprintf(stderr, "Available commands:\n");
197 for (i = 0; commands[i].cmd_name != NULL; i++)
198 (void)fprintf(stderr, " %s %s\n", commands[i].cmd_name,
199 commands[i].arg_names);
200
201 exit(EXIT_FAILURE);
202 /* NOTREACHED */
203 }
204
205 static const char *
206 str2locator(const char *string, struct locator *location)
207 {
208 const char *errstr;
209 char parse[80], *targ, *lun;
210
211 strlcpy(parse, string, sizeof parse);
212 targ = strchr(parse, ':');
213 if (targ == NULL)
214 return "target not specified";
215
216 *targ++ = '\0';
217 lun = strchr(targ, '.');
218 if (lun != NULL) {
219 *lun++ = '\0';
220 location->lun = strtonum(lun, 0, 256, &errstr);
221 if (errstr)
222 return errstr;
223 } else
224 location->lun = 0;
225
226 location->target = strtonum(targ, 0, 256, &errstr);
227 if (errstr)
228 return errstr;
229 location->channel = strtonum(parse, 0, 256, &errstr);
230 if (errstr)
231 return errstr;
232 return NULL;
233 }
234
235 /*
236 * Shows info about available RAID volumes.
237 */
238 static int
239 bio_show_volumes(struct biotmp *bt)
240 {
241 struct bioc_vol bv;
242 const char *status, *rtypestr, *stripestr;
243 char size[64], percent[16], seconds[20];
244 char rtype[16], stripe[16], tmp[32];
245
246 rtypestr = stripestr = NULL;
247
248 memset(&bv, 0, sizeof(bv));
249 bv.bv_cookie = bl.bl_cookie;
250 bv.bv_volid = bt->volid;
251 bv.bv_percent = -1;
252 bv.bv_seconds = -1;
253
254 if (ioctl(bt->fd, BIOCVOL, &bv) == -1)
255 err(EXIT_FAILURE, "BIOCVOL");
256
257 percent[0] = '\0';
258 seconds[0] = '\0';
259 if (bv.bv_percent != -1)
260 snprintf(percent, sizeof(percent),
261 " %3.2f%% done", bv.bv_percent / 10.0);
262 if (bv.bv_seconds)
263 snprintf(seconds, sizeof(seconds),
264 " %u seconds", bv.bv_seconds);
265
266 switch (bv.bv_status) {
267 case BIOC_SVONLINE:
268 status = BIOC_SVONLINE_S;
269 break;
270 case BIOC_SVOFFLINE:
271 status = BIOC_SVOFFLINE_S;
272 break;
273 case BIOC_SVDEGRADED:
274 status = BIOC_SVDEGRADED_S;
275 break;
276 case BIOC_SVBUILDING:
277 status = BIOC_SVBUILDING_S;
278 break;
279 case BIOC_SVREBUILD:
280 status = BIOC_SVREBUILD_S;
281 break;
282 case BIOC_SVMIGRATING:
283 status = BIOC_SVMIGRATING_S;
284 break;
285 case BIOC_SVSCRUB:
286 status = BIOC_SVSCRUB_S;
287 break;
288 case BIOC_SVCHECKING:
289 status = BIOC_SVCHECKING_S;
290 break;
291 case BIOC_SVINVALID:
292 default:
293 status = BIOC_SVINVALID_S;
294 break;
295 }
296
297 snprintf(bt->volname, sizeof(bt->volname), "%u", bv.bv_volid);
298 if (bv.bv_vendor)
299 snprintf(tmp, sizeof(tmp), "%s %s", bv.bv_dev, bv.bv_vendor);
300 else
301 snprintf(tmp, sizeof(tmp), "%s", bv.bv_dev);
302
303 switch (bv.bv_level) {
304 case BIOC_SVOL_HOTSPARE:
305 rtypestr = "Hot spare";
306 stripestr = "N/A";
307 break;
308 case BIOC_SVOL_PASSTHRU:
309 rtypestr = "Pass through";
310 stripestr = "N/A";
311 break;
312 case BIOC_SVOL_RAID01:
313 rtypestr = "RAID 0+1";
314 break;
315 case BIOC_SVOL_RAID10:
316 rtypestr = "RAID 1+0";
317 break;
318 default:
319 snprintf(rtype, sizeof(rtype), "RAID %u", bv.bv_level);
320 if (bv.bv_level == 1 || bv.bv_stripe_size == 0)
321 stripestr = "N/A";
322 break;
323 }
324
325 if (rtypestr)
326 snprintf(rtype, sizeof(rtype), rtypestr);
327 if (stripestr)
328 snprintf(stripe, sizeof(stripe), stripestr);
329 else
330 snprintf(stripe, sizeof(stripe), "%uK", bv.bv_stripe_size);
331
332 humanize_number(size, 5, (int64_t)bv.bv_size, "", HN_AUTOSCALE,
333 HN_B | HN_NOSPACE | HN_DECIMAL);
334
335 printf("%6s %-12s %4s %20s %8s %6s %s%s\n",
336 bt->volname, status, size, tmp,
337 rtype, stripe, percent, seconds);
338
339 bt->bv = &bv;
340
341 return bv.bv_nodisk;
342 }
343
344 /*
345 * Shows info about physical disks.
346 */
347 static void
348 bio_show_disks(struct biotmp *bt)
349 {
350 struct bioc_disk bd;
351 const char *status;
352 char size[64], serial[32], scsiname[16];
353
354 memset(&bd, 0, sizeof(bd));
355 bd.bd_cookie = bl.bl_cookie;
356 bd.bd_diskid = bt->diskid;
357 bd.bd_volid = bt->volid;
358
359 if (bt->show_disknovol) {
360 if (ioctl(bt->fd, BIOCDISK_NOVOL, &bd) == -1)
361 err(EXIT_FAILURE, "BIOCDISK_NOVOL");
362 if (!bd.bd_disknovol)
363 return;
364 } else {
365 if (ioctl(bt->fd, BIOCDISK, &bd) == -1)
366 err(EXIT_FAILURE, "BIOCDISK");
367 }
368
369 switch (bd.bd_status) {
370 case BIOC_SDONLINE:
371 status = BIOC_SDONLINE_S;
372 break;
373 case BIOC_SDOFFLINE:
374 status = BIOC_SDOFFLINE_S;
375 break;
376 case BIOC_SDFAILED:
377 status = BIOC_SDFAILED_S;
378 break;
379 case BIOC_SDREBUILD:
380 status = BIOC_SDREBUILD_S;
381 break;
382 case BIOC_SDHOTSPARE:
383 status = BIOC_SDHOTSPARE_S;
384 break;
385 case BIOC_SDUNUSED:
386 status = BIOC_SDUNUSED_S;
387 break;
388 case BIOC_SDSCRUB:
389 status = BIOC_SDSCRUB_S;
390 break;
391 case BIOC_SDPASSTHRU:
392 status = BIOC_SDPASSTHRU_S;
393 break;
394 case BIOC_SDINVALID:
395 default:
396 status = BIOC_SDINVALID_S;
397 break;
398 }
399
400 if (bt->format)
401 snprintf(bt->volname, sizeof(bt->volname),
402 "%u:%u", bt->bv->bv_volid, bd.bd_diskid);
403
404 humanize_number(size, 5, bd.bd_size, "", HN_AUTOSCALE,
405 HN_B | HN_NOSPACE | HN_DECIMAL);
406
407 if (bd.bd_procdev[0])
408 snprintf(scsiname, sizeof(scsiname), "%u:%u.%u %s",
409 bd.bd_channel, bd.bd_target, bd.bd_lun,
410 bd.bd_procdev);
411 else
412 snprintf(scsiname, sizeof(scsiname), "%u:%u.%u noencl",
413 bd.bd_channel, bd.bd_target, bd.bd_lun);
414
415 if (bd.bd_serial[0])
416 strlcpy(serial, bd.bd_serial, sizeof(serial));
417 else
418 strlcpy(serial, "unknown serial", sizeof(serial));
419
420 if (bt->format)
421 printf("%6s %-12s %4s %20s <%s>\n",
422 bt->volname, status, size, scsiname,
423 bd.bd_vendor);
424 else
425 printf("%5d [%-28s] %-12s %-6s %12s\n",
426 bt->diskid, bd.bd_vendor, status, size, scsiname);
427
428 }
429
430 /*
431 * Shows info about volumes/disks.
432 */
433 static void
434 bio_show_common(int fd, int argc, char **argv)
435 {
436 struct biotmp *biot;
437 struct bioc_inq bi;
438 int i, d, ndisks;
439 bool show_all, show_disks;
440 bool show_vols, show_caps;
441
442 show_all = show_disks = show_vols = show_caps = false;
443
444 if (argc > 1)
445 usage();
446
447 if (argv[0]) {
448 if (strcmp(argv[0], "disks") == 0)
449 show_disks = true;
450 else if (strcmp(argv[0], "volumes") == 0)
451 show_vols = true;
452 else
453 usage();
454 } else
455 show_all = true;
456
457 memset(&bi, 0, sizeof(bi));
458 bi.bi_cookie = bl.bl_cookie;
459
460 if (ioctl(fd, BIOCINQ, &bi) == -1)
461 err(EXIT_FAILURE, "BIOCINQ");
462
463 /*
464 * If there are volumes there's no point to continue.
465 */
466 if (show_all || show_vols) {
467 if (!bi.bi_novol) {
468 warnx("no volumes available");
469 return;
470 }
471 }
472
473 biot = calloc(1, sizeof(*biot));
474 if (!biot)
475 err(EXIT_FAILURE, "biotemp calloc");
476
477 biot->fd = fd;
478 biot->bi = &bi;
479 /*
480 * Go to the disks section if that was specified.
481 */
482 if (show_disks)
483 goto disks;
484
485 /*
486 * Common code to show only info about volumes and disks
487 * associated to them.
488 */
489 printf("%6s %-12s %4s %20s %8s %6s\n",
490 "Volume", "Status", "Size", "Device/Label",
491 "Level", "Stripe");
492 printf("=============================================="
493 "===============\n");
494
495 for (i = 0; i < bi.bi_novol; i++) {
496 biot->format = true;
497 biot->volid = i;
498 ndisks = bio_show_volumes(biot);
499 if (show_vols)
500 continue;
501
502 for (d = 0; d < ndisks; d++) {
503 biot->diskid = d;
504 bio_show_disks(biot);
505 }
506
507 }
508 goto out;
509
510 disks:
511 /*
512 * show info about all disks connected to the raid controller,
513 * even if they aren't associated with a volume or raid set.
514 */
515 if (show_disks) {
516 printf("%5s %-30s %-12s %-6s %12s\n",
517 "Disk", "Model/Serial", "Status", "Size", "Location");
518 printf("==============================================="
519 "======================\n");
520 for (d = 0; d < bi.bi_nodisk; d++) {
521 biot->show_disknovol = true;
522 biot->diskid = d;
523 bio_show_disks(biot);
524 }
525 }
526 out:
527 free(biot);
528 }
529
530 /*
531 * To handle the alarm feature.
532 */
533 static void
534 bio_alarm(int fd, int argc, char **argv)
535 {
536 struct bioc_alarm ba;
537 bool show = false;
538
539 memset(&ba, 0, sizeof(ba));
540 ba.ba_cookie = bl.bl_cookie;
541
542 if (argc > 1)
543 usage();
544
545 if (argc == 0) {
546 /* show alarm status */
547 ba.ba_opcode = BIOC_GASTATUS;
548 show = true;
549 } else if (strcmp(argv[0], "silence") == 0) {
550 /* silence alarm */
551 ba.ba_opcode = BIOC_SASILENCE;
552 } else if (strcmp(argv[0], "enable") == 0) {
553 /* enable alarm */
554 ba.ba_opcode = BIOC_SAENABLE;
555 } else if (strcmp(argv[0], "disable") == 0) {
556 /* disable alarm */
557 ba.ba_opcode = BIOC_SADISABLE;
558 } else if (strcmp(argv[0], "test") == 0) {
559 /* test alarm */
560 ba.ba_opcode = BIOC_SATEST;
561 } else
562 usage();
563
564 if (ioctl(fd, BIOCALARM, &ba) == -1)
565 err(EXIT_FAILURE, "BIOCALARM");
566
567 if (show)
568 printf("alarm is currently %s\n",
569 ba.ba_status ? "enabled" : "disabled");
570 }
571
572 /*
573 * To add/remove a hotspare disk.
574 */
575 static void
576 bio_setstate_hotspare(int fd, int argc, char **argv)
577 {
578 struct bioc_setstate bs;
579 struct locator location;
580
581 memset(&bs, 0, sizeof(bs));
582
583 if (argc != 2)
584 usage();
585
586 if (strcmp(argv[0], "add") == 0)
587 bs.bs_status = BIOC_SSHOTSPARE;
588 else if (strcmp(argv[0], "remove") == 0)
589 bs.bs_status = BIOC_SSDELHOTSPARE;
590 else
591 usage();
592
593 bio_setstate_common(fd, argv[1], &bs, &location);
594 }
595
596 /*
597 * To add/remove a pass through disk.
598 */
599 static void
600 bio_setstate_passthru(int fd, int argc, char **argv)
601 {
602 struct bioc_setstate bs;
603 struct locator location;
604 char *endptr;
605 bool rem = false;
606
607 if (argc > 3)
608 usage();
609
610 memset(&bs, 0, sizeof(bs));
611
612 if (strcmp(argv[0], "add") == 0) {
613 if (argv[1] == NULL || argv[2] == NULL)
614 usage();
615
616 bs.bs_status = BIOC_SSPASSTHRU;
617 } else if (strcmp(argv[0], "remove") == 0) {
618 if (argv[1] == NULL)
619 usage();
620
621 bs.bs_status = BIOC_SSDELPASSTHRU;
622 rem = true;
623 } else
624 usage();
625
626 if (rem)
627 bio_setstate_common(fd, argv[1], &bs, &location);
628 else {
629 bs.bs_other_id = (unsigned int)strtoul(argv[1], &endptr, 10);
630 if (*endptr != '\0')
631 errx(EXIT_FAILURE, "Invalid Volume ID value");
632
633 bio_setstate_common(fd, argv[2], &bs, &location);
634 }
635 }
636
637 /*
638 * To start/stop a consistency check in a RAID volume.
639 */
640 static void
641 bio_setstate_consistency(int fd, int argc, char **argv)
642 {
643 struct bioc_setstate bs;
644 char *endptr;
645
646 if (argc > 2)
647 usage();
648
649 memset(&bs, 0, sizeof(bs));
650
651 if (strcmp(argv[0], "start") == 0)
652 bs.bs_status = BIOC_SSCHECKSTART_VOL;
653 else if (strcmp(argv[0], "stop") == 0)
654 bs.bs_status = BIOC_SSCHECKSTOP_VOL;
655 else
656 usage();
657
658 bs.bs_volid = (unsigned int)strtoul(argv[1], &endptr, 10);
659 if (*endptr != '\0')
660 errx(EXIT_FAILURE, "Invalid Volume ID value");
661
662 bio_setstate_common(fd, NULL, &bs, NULL);
663 }
664
665 static void
666 bio_setstate_common(int fd, char *arg, struct bioc_setstate *bs,
667 struct locator *location)
668 {
669 const char *errstr;
670
671 if (!arg || !location)
672 goto send;
673
674 errstr = str2locator(arg, location);
675 if (errstr)
676 errx(EXIT_FAILURE, "Target %s: %s", arg, errstr);
677
678 bs->bs_channel = location->channel;
679 bs->bs_target = location->target;
680 bs->bs_lun = location->lun;
681
682 send:
683 bs->bs_cookie = bl.bl_cookie;
684
685 if (ioctl(fd, BIOCSETSTATE, bs) == -1)
686 err(EXIT_FAILURE, "BIOCSETSTATE");
687 }
688
689 /*
690 * To create a RAID volume.
691 */
692 static void
693 bio_volops_create(int fd, int argc, char **argv)
694 {
695 struct bioc_volops bc;
696 struct bioc_inq bi;
697 struct bioc_disk bd;
698 struct locator location;
699 uint64_t total_size = 0, disksize = 0;
700 int64_t volsize = 0;
701 const char *errstr;
702 char *endptr, *stripe, levelstr[32];
703 char *scsiname, *raid_level, size[64];
704 int disk_first = 0, disk_end = 0;
705 int i, nfreedisks = 0;
706 int user_disks = 0;
707
708 if (argc < 6 || argc > 7)
709 usage();
710
711 if (strcmp(argv[0], "volume") != 0)
712 usage();
713
714 /*
715 * No size requested, use max size depending on RAID level.
716 */
717 if (argc == 6) {
718 stripe = argv[3];
719 raid_level = argv[4];
720 scsiname = argv[5];
721 } else {
722 stripe = argv[4];
723 raid_level = argv[5];
724 scsiname = argv[6];
725 }
726
727 memset(&bd, 0, sizeof(bd));
728 memset(&bc, 0, sizeof(bc));
729 memset(&bi, 0, sizeof(bi));
730
731 bc.bc_cookie = bd.bd_cookie = bi.bi_cookie = bl.bl_cookie;
732 bc.bc_opcode = BIOC_VCREATE_VOLUME;
733
734 bc.bc_volid = (unsigned int)strtoul(argv[1], &endptr, 10);
735 if (*endptr != '\0')
736 errx(EXIT_FAILURE, "Invalid Volume ID value");
737
738 if (argc == 7)
739 if (dehumanize_number(argv[3], &volsize) == -1)
740 errx(EXIT_FAILURE, "Invalid SIZE value");
741
742 bc.bc_stripe = (unsigned int)strtoul(stripe, &endptr, 10);
743 if (*endptr != '\0')
744 errx(EXIT_FAILURE, "Invalid STRIPE size value");
745
746 bc.bc_level = (unsigned int)strtoul(raid_level, &endptr, 10);
747 if (*endptr != '\0')
748 errx(EXIT_FAILURE, "Invalid RAID_LEVEL value");
749
750 errstr = str2locator(scsiname, &location);
751 if (errstr)
752 errx(EXIT_FAILURE, "Target %s: %s", scsiname, errstr);
753
754 /*
755 * Parse the device list that will be used for the volume,
756 * by using a bit field for the disks.
757 */
758 if ((isdigit((unsigned char)argv[2][0]) == 0) || argv[2][1] != '-' ||
759 (isdigit((unsigned char)argv[2][2]) == 0))
760 errx(EXIT_FAILURE, "Invalid DISKIDs value");
761
762 disk_first = atoi(&argv[2][0]);
763 disk_end = atoi(&argv[2][2]);
764
765 for (i = disk_first; i < disk_end + 1; i++) {
766 bc.bc_devmask |= (1 << i);
767 user_disks++;
768 }
769
770 /*
771 * Find out how many disks are free and how much size we
772 * have available for the new volume.
773 */
774 if (ioctl(fd, BIOCINQ, &bi) == -1)
775 err(EXIT_FAILURE, "BIOCINQ");
776
777 for (i = 0; i < bi.bi_nodisk; i++) {
778 bd.bd_diskid = i;
779 if (ioctl(fd, BIOCDISK_NOVOL, &bd) == -1)
780 err(EXIT_FAILURE, "BIOCDISK_NOVOL");
781
782 if (bd.bd_status == BIOC_SDUNUSED) {
783 if (i == 0)
784 disksize = bd.bd_size;
785
786 total_size += bd.bd_size;
787 nfreedisks++;
788 }
789 }
790
791 if (user_disks > nfreedisks)
792 errx(EXIT_FAILURE, "specified disks number is higher than "
793 "available free disks");
794
795 /*
796 * Basic checks to be sure we don't do something stupid.
797 */
798 if (nfreedisks == 0)
799 errx(EXIT_FAILURE, "No free disks available");
800
801 switch (bc.bc_level) {
802 case 0: /* RAID 0 requires at least one disk */
803 if (argc == 7) {
804 if (volsize > (disksize * user_disks))
805 errx(EXIT_FAILURE, "volume size specified "
806 "is larger than available on free disks");
807 bc.bc_size = (uint64_t)volsize;
808 } else
809 bc.bc_size = disksize * user_disks;
810
811 break;
812 case 1: /* RAID 1 requires two disks and size is total / 2 */
813 if (nfreedisks < 2 || user_disks < 2)
814 errx(EXIT_FAILURE, "2 disks are required at least for "
815 "this RAID level");
816
817 /* RAID 1+0 requires three disks at least */
818 if (nfreedisks > 2 && user_disks > 2)
819 bc.bc_level = BIOC_SVOL_RAID10;
820
821 if (argc == 7) {
822 if (volsize > ((disksize * user_disks) / 2))
823 errx(EXIT_FAILURE, "volume size specified "
824 "is larger than available on free disks");
825 bc.bc_size = (uint64_t)volsize;
826 } else
827 bc.bc_size = ((disksize * user_disks) / 2);
828
829 break;
830 case 3: /* RAID 3/5 requires three disks and size is total - 1 disk */
831 case 5:
832 if (nfreedisks < 3 || user_disks < 3)
833 errx(EXIT_FAILURE, "3 disks are required at least for "
834 "this RAID level");
835
836 if (argc == 7) {
837 if (volsize > (disksize * (user_disks - 1)))
838 errx(EXIT_FAILURE, "volume size specified "
839 "is larger than available on free disks");
840 bc.bc_size = (uint64_t)volsize;
841 } else
842 bc.bc_size = (disksize * (user_disks - 1));
843
844 break;
845 case 6: /* RAID 6 requires four disks and size is total - 2 disks */
846 if (nfreedisks < 4 || user_disks < 4)
847 errx(EXIT_FAILURE, "4 disks are required at least for "
848 "this RAID level");
849
850 if (argc == 7) {
851 if (volsize >
852 ((disksize * user_disks) - (disksize * 2)))
853 err(EXIT_FAILURE, "volume size specified "
854 "is larger than available on free disks");
855 bc.bc_size = (uint64_t)volsize;
856 } else
857 bc.bc_size =
858 (((disksize * user_disks) - (disksize * 2)));
859
860 break;
861 default:
862 errx(EXIT_FAILURE, "Unsupported RAID level");
863 }
864
865 bc.bc_channel = location.channel;
866 bc.bc_target = location.target;
867 bc.bc_lun = location.lun;
868
869 if (ioctl(fd, BIOCVOLOPS, &bc) == -1)
870 err(EXIT_FAILURE, "BIOCVOLOPS");
871
872 humanize_number(size, 5, bc.bc_size, "", HN_AUTOSCALE,
873 HN_B | HN_NOSPACE | HN_DECIMAL);
874
875 if (bc.bc_level == BIOC_SVOL_RAID10)
876 snprintf(levelstr, sizeof(levelstr), "1+0");
877 else
878 snprintf(levelstr, sizeof(levelstr), "%u", bc.bc_level);
879
880 printf("Created volume %u size: %s stripe: %uK level: %s "
881 "SCSI location: %u:%u.%u\n", bc.bc_volid, size, bc.bc_stripe,
882 levelstr, bc.bc_channel, bc.bc_target, bc.bc_lun);
883 }
884
885 #ifdef notyet
886 /*
887 * To modify a RAID volume.
888 */
889 static void
890 bio_volops_modify(int fd, int argc, char **argv)
891 {
892 /* XTRAEME: TODO */
893 }
894 #endif
895
896 /*
897 * To remove a RAID volume.
898 */
899 static void
900 bio_volops_remove(int fd, int argc, char **argv)
901 {
902 struct bioc_volops bc;
903 struct locator location;
904 const char *errstr;
905 char *endptr;
906
907 if (argc != 3 || strcmp(argv[0], "volume") != 0)
908 usage();
909
910 memset(&bc, 0, sizeof(bc));
911 bc.bc_cookie = bl.bl_cookie;
912 bc.bc_opcode = BIOC_VREMOVE_VOLUME;
913
914 bc.bc_volid = (unsigned int)strtoul(argv[1], &endptr, 10);
915 if (*endptr != '\0')
916 errx(EXIT_FAILURE, "Invalid Volume ID value");
917
918 errstr = str2locator(argv[2], &location);
919 if (errstr)
920 errx(EXIT_FAILURE, "Target %s: %s", argv[2], errstr);
921
922 bc.bc_channel = location.channel;
923 bc.bc_target = location.target;
924 bc.bc_lun = location.lun;
925
926 if (ioctl(fd, BIOCVOLOPS, &bc) == -1)
927 err(EXIT_FAILURE, "BIOCVOLOPS");
928
929 printf("Removed volume %u at SCSI location %u:%u.%u\n",
930 bc.bc_volid, bc.bc_channel, bc.bc_target, bc.bc_lun);
931 }
932
933 /*
934 * To blink/unblink a disk in enclosures.
935 */
936 static void
937 bio_setblink(int fd, int argc, char **argv)
938 {
939 struct locator location;
940 struct bioc_inq bi;
941 struct bioc_vol bv;
942 struct bioc_disk bd;
943 struct bioc_blink bb;
944 const char *errstr;
945 int v, d, rv, blink = 0;
946
947 if (argc != 2)
948 usage();
949
950 if (strcmp(argv[0], "start") == 0)
951 blink = BIOC_SBBLINK;
952 else if (strcmp(argv[0], "stop") == 0)
953 blink = BIOC_SBUNBLINK;
954 else
955 usage();
956
957 errstr = str2locator(argv[1], &location);
958 if (errstr)
959 errx(EXIT_FAILURE, "Target %s: %s", argv[1], errstr);
960
961 /* try setting blink on the device directly */
962 memset(&bb, 0, sizeof(bb));
963 bb.bb_cookie = bl.bl_cookie;
964 bb.bb_status = blink;
965 bb.bb_target = location.target;
966 bb.bb_channel = location.channel;
967 rv = ioctl(fd, BIOCBLINK, &bb);
968 if (rv == 0)
969 return;
970
971 /* if the blink didnt work, try to find something that will */
972 memset(&bi, 0, sizeof(bi));
973 bi.bi_cookie = bl.bl_cookie;
974 rv = ioctl(fd, BIOCINQ, &bi);
975 if (rv == -1)
976 err(EXIT_FAILURE, "BIOCINQ");
977
978 for (v = 0; v < bi.bi_novol; v++) {
979 memset(&bv, 0, sizeof(bv));
980 bv.bv_cookie = bl.bl_cookie;
981 bv.bv_volid = v;
982 rv = ioctl(fd, BIOCVOL, &bv);
983 if (rv == -1)
984 err(EXIT_FAILURE, "BIOCVOL");
985
986 for (d = 0; d < bv.bv_nodisk; d++) {
987 memset(&bd, 0, sizeof(bd));
988 bd.bd_cookie = bl.bl_cookie;
989 bd.bd_volid = v;
990 bd.bd_diskid = d;
991
992 rv = ioctl(fd, BIOCDISK, &bd);
993 if (rv == -1)
994 err(EXIT_FAILURE, "BIOCDISK");
995
996 if (bd.bd_channel == location.channel &&
997 bd.bd_target == location.target &&
998 bd.bd_lun == location.lun) {
999 if (bd.bd_procdev[0] != '\0') {
1000 bio_blink(fd, bd.bd_procdev,
1001 location.target, blink);
1002 } else
1003 warnx("Disk %s is not in an enclosure",
1004 argv[1]);
1005 return;
1006 }
1007 }
1008 }
1009
1010 warnx("Disk %s does not exist", argv[1]);
1011 }
1012
1013 static void
1014 bio_blink(int fd, char *enclosure, int target, int blinktype)
1015 {
1016 struct bio_locate bio;
1017 struct bioc_blink blink;
1018
1019 bio.bl_name = enclosure;
1020 if (ioctl(fd, BIOCLOCATE, &bio) == -1)
1021 errx(EXIT_FAILURE,
1022 "Can't locate %s device via /dev/bio", enclosure);
1023
1024 memset(&blink, 0, sizeof(blink));
1025 blink.bb_cookie = bio.bl_cookie;
1026 blink.bb_status = blinktype;
1027 blink.bb_target = target;
1028
1029 if (ioctl(fd, BIOCBLINK, &blink) == -1)
1030 err(EXIT_FAILURE, "BIOCBLINK");
1031 }
1032