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