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