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