ccdconfig.c revision 1.4 1 /* $NetBSD: ccdconfig.c,v 1.4 1996/02/01 20:47:40 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 1995 Jason R. Thorpe.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed for the NetBSD Project
18 * by Jason R. Thorpe.
19 * 4. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <sys/param.h>
36 #include <sys/ioctl.h>
37 #include <sys/disklabel.h>
38 #include <sys/device.h>
39 #include <sys/disk.h>
40 #include <sys/stat.h>
41 #include <sys/sysctl.h>
42 #include <ctype.h>
43 #include <err.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <kvm.h>
47 #include <limits.h>
48 #include <nlist.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53
54 #include <dev/ccdvar.h>
55
56 #include "pathnames.h"
57
58 extern char *__progname;
59
60 static int lineno = 0;
61 static int verbose = 0;
62 static char *ccdconf = _PATH_CCDCONF;
63
64 static char *core = NULL;
65 static char *kernel = NULL;
66
67 struct flagval {
68 char *fv_flag;
69 int fv_val;
70 } flagvaltab[] = {
71 { "CCDF_SWAP", CCDF_SWAP },
72 { "CCDF_UNIFORM", CCDF_UNIFORM },
73 { "CCDF_MIRROR", CCDF_MIRROR },
74 { NULL, 0 },
75 };
76
77 static struct nlist nl[] = {
78 { "_ccd_softc" },
79 #define SYM_CCDSOFTC 0
80 { "_numccd" },
81 #define SYM_NUMCCD 1
82 { NULL },
83 };
84
85 #define CCD_CONFIG 0 /* configure a device */
86 #define CCD_CONFIGALL 1 /* configure all devices */
87 #define CCD_UNCONFIG 2 /* unconfigure a device */
88 #define CCD_UNCONFIGALL 3 /* unconfigure all devices */
89 #define CCD_DUMP 4 /* dump a ccd's configuration */
90
91 static int checkdev __P((char *));
92 static int do_io __P((char *, u_long, struct ccd_ioctl *));
93 static int do_single __P((int, char **, int));
94 static int do_all __P((int));
95 static int dump_ccd __P((int, char **));
96 static int getmaxpartitions __P((void));
97 static int getrawpartition __P((void));
98 static int flags_to_val __P((char *));
99 static int pathtodevt __P((char *, dev_t *));
100 static void print_ccd_info __P((struct ccd_softc *, kvm_t *));
101 static char *resolve_ccdname __P((char *));
102 static void usage __P((void));
103
104 int
105 main(argc, argv)
106 int argc;
107 char **argv;
108 {
109 int ch, options = 0, action = CCD_CONFIG;
110
111 while ((ch = getopt(argc, argv, "cCf:gM:N:uUv")) != -1) {
112 switch (ch) {
113 case 'c':
114 action = CCD_CONFIG;
115 ++options;
116 break;
117
118 case 'C':
119 action = CCD_CONFIGALL;
120 ++options;
121 break;
122
123 case 'f':
124 ccdconf = optarg;
125 break;
126
127 case 'g':
128 action = CCD_DUMP;
129 break;
130
131 case 'M':
132 core = optarg;
133 break;
134
135 case 'N':
136 kernel = optarg;
137 break;
138
139 case 'u':
140 action = CCD_UNCONFIG;
141 ++options;
142 break;
143
144 case 'U':
145 action = CCD_UNCONFIGALL;
146 ++options;
147 break;
148
149 case 'v':
150 verbose = 1;
151 break;
152
153 default:
154 usage();
155 }
156 }
157 argc -= optind;
158 argv += optind;
159
160 if (options > 1)
161 usage();
162
163 switch (action) {
164 case CCD_CONFIG:
165 case CCD_UNCONFIG:
166 exit(do_single(argc, argv, action));
167 /* NOTREACHED */
168
169 case CCD_CONFIGALL:
170 case CCD_UNCONFIGALL:
171 exit(do_all(action));
172 /* NOTREACHED */
173
174 case CCD_DUMP:
175 exit(dump_ccd(argc, argv));
176 /* NOTREACHED */
177 }
178 /* NOTREACHED */
179 }
180
181 static int
182 do_single(argc, argv, action)
183 int argc;
184 char **argv;
185 int action;
186 {
187 struct ccd_ioctl ccio;
188 char *ccd, *cp, *cp2, **disks;
189 int noflags = 0, i, ileave, flags, j, error;
190
191 bzero(&ccio, sizeof(ccio));
192
193 /*
194 * If unconfiguring, all arguments are treated as ccds.
195 */
196 if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) {
197 for (i = 0; argc != 0; ) {
198 cp = *argv++; --argc;
199 if ((ccd = resolve_ccdname(cp)) == NULL) {
200 warnx("invalid ccd name: %s", cp);
201 i = 1;
202 continue;
203 }
204 if (do_io(ccd, CCDIOCCLR, &ccio))
205 i = 1;
206 else
207 if (verbose)
208 printf("%s unconfigured\n", cp);
209 }
210 return (i);
211 }
212
213 /* Make sure there are enough arguments. */
214 if (argc < 4)
215 if (argc == 3) {
216 /* Assume that no flags are specified. */
217 noflags = 1;
218 } else {
219 if (action == CCD_CONFIGALL) {
220 warnx("%s: bad line: %d", ccdconf, lineno);
221 return (1);
222 } else
223 usage();
224 }
225
226 /* First argument is the ccd to configure. */
227 cp = *argv++; --argc;
228 if ((ccd = resolve_ccdname(cp)) == NULL) {
229 warnx("invalid ccd name: %s", cp);
230 return (1);
231 }
232
233 /* Next argument is the interleave factor. */
234 cp = *argv++; --argc;
235 errno = 0; /* to check for ERANGE */
236 ileave = (int)strtol(cp, &cp2, 10);
237 if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) {
238 warnx("invalid interleave factor: %s", cp);
239 return (1);
240 }
241
242 if (noflags == 0) {
243 /* Next argument is the ccd configuration flags. */
244 cp = *argv++; --argc;
245 if ((flags = flags_to_val(cp)) < 0) {
246 warnx("invalid flags argument: %s", cp);
247 return (1);
248 }
249 }
250
251 /* Next is the list of disks to make the ccd from. */
252 disks = malloc(argc * sizeof(char *));
253 if (disks == NULL) {
254 warnx("no memory to configure ccd");
255 return (1);
256 }
257 for (i = 0; argc != 0; ) {
258 cp = *argv++; --argc;
259 if ((j = checkdev(cp)) == 0)
260 disks[i++] = cp;
261 else {
262 warnx("%s: %s", cp, strerror(j));
263 return (1);
264 }
265 }
266
267 /* Fill in the ccio. */
268 ccio.ccio_disks = disks;
269 ccio.ccio_ndisks = i;
270 ccio.ccio_ileave = ileave;
271 ccio.ccio_flags = flags;
272
273 if (do_io(ccd, CCDIOCSET, &ccio)) {
274 free(disks);
275 return (1);
276 }
277
278 if (verbose) {
279 printf("ccd%d: %d components ", ccio.ccio_unit,
280 ccio.ccio_ndisks);
281 for (i = 0; i < ccio.ccio_ndisks; ++i) {
282 if ((cp2 = strrchr(disks[i], '/')) != NULL)
283 ++cp2;
284 else
285 cp2 = disks[i];
286 printf("%c%s%c",
287 i == 0 ? '(' : ' ', cp2,
288 i == ccio.ccio_ndisks - 1 ? ')' : ',');
289 }
290 printf(", %d blocks ", ccio.ccio_size);
291 if (ccio.ccio_ileave != 0)
292 printf("interleaved at %d blocks\n", ccio.ccio_ileave);
293 else
294 printf("concatenated\n");
295 }
296
297 free(disks);
298 return (0);
299 }
300
301 static int
302 do_all(action)
303 int action;
304 {
305 FILE *f;
306 char line[_POSIX2_LINE_MAX];
307 char *cp, **argv;
308 int argc, rval;
309
310 if ((f = fopen(ccdconf, "r")) == NULL) {
311 warn("fopen: %s", ccdconf);
312 return (1);
313 }
314
315 while (fgets(line, sizeof(line), f) != NULL) {
316 argc = 0;
317 argv = NULL;
318 ++lineno;
319 if ((cp = strrchr(line, '\n')) != NULL)
320 *cp = '\0';
321
322 /* Break up the line and pass it's contents to do_single(). */
323 if (line[0] == '\0')
324 goto end_of_line;
325 for (cp = line; (cp = strtok(cp, " \t")) != NULL; cp = NULL) {
326 if (*cp == '#')
327 break;
328 if ((argv = realloc(argv,
329 sizeof(char *) * ++argc)) == NULL) {
330 warnx("no memory to configure ccds");
331 return (1);
332 }
333 argv[argc - 1] = cp;
334 /*
335 * If our action is to unconfigure all, then pass
336 * just the first token to do_single() and ignore
337 * the rest. Since this will be encountered on
338 * our first pass through the line, the Right
339 * Thing will happen.
340 */
341 if (action == CCD_UNCONFIGALL) {
342 if (do_single(argc, argv, action))
343 rval = 1;
344 goto end_of_line;
345 }
346 }
347 if (argc != 0)
348 if (do_single(argc, argv, action))
349 rval = 1;
350
351 end_of_line:
352 if (argv != NULL)
353 free(argv);
354 }
355
356 (void)fclose(f);
357 return (rval);
358 }
359
360 static int
361 checkdev(path)
362 char *path;
363 {
364 struct stat st;
365
366 if (stat(path, &st) != 0)
367 return (errno);
368
369 if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
370 return (EINVAL);
371
372 return (0);
373 }
374
375 static int
376 pathtounit(path, unitp)
377 char *path;
378 int *unitp;
379 {
380 struct stat st;
381 dev_t dev;
382 int maxpartitions;
383
384 if (stat(path, &st) != 0)
385 return (errno);
386
387 if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
388 return (EINVAL);
389
390 if ((maxpartitions = getmaxpartitions()) < 0)
391 return (errno);
392
393 *unitp = minor(st.st_rdev) / maxpartitions;
394
395 return (0);
396 }
397
398 static char *
399 resolve_ccdname(name)
400 char *name;
401 {
402 char c, *cp, *path;
403 size_t len, newlen;
404 int rawpart;
405
406 if (name[0] == '/' || name[0] == '.') {
407 /* Assume they gave the correct pathname. */
408 return (strdup(name));
409 }
410
411 len = strlen(name);
412 c = name[len - 1];
413
414 newlen = len + 8;
415 if ((path = malloc(newlen)) == NULL)
416 return (NULL);
417 bzero(path, newlen);
418
419 if (isdigit(c)) {
420 if ((rawpart = getrawpartition()) < 0) {
421 free(path);
422 return (NULL);
423 }
424 (void)sprintf(path, "/dev/%s%c", name, 'a' + rawpart);
425 } else
426 (void)sprintf(path, "/dev/%s", name);
427
428 return (path);
429 }
430
431 static int
432 do_io(path, cmd, cciop)
433 char *path;
434 u_long cmd;
435 struct ccd_ioctl *cciop;
436 {
437 int fd;
438 char *cp;
439
440 if ((fd = open(path, O_RDWR, 0640)) < 0) {
441 warn("open: %s", path);
442 return (1);
443 }
444
445 if (ioctl(fd, cmd, cciop) < 0) {
446 switch (cmd) {
447 case CCDIOCSET:
448 cp = "CCDIOCSET";
449 break;
450
451 case CCDIOCCLR:
452 cp = "CCDIOCCLR";
453 break;
454
455 default:
456 cp = "unknown";
457 }
458 warn("ioctl (%s): %s", cp, path);
459 return (1);
460 }
461
462 return (0);
463 }
464
465 #define KVM_ABORT(kd, str) { \
466 (void)kvm_close((kd)); \
467 warnx((str)); \
468 warnx(kvm_geterr((kd))); \
469 return (1); \
470 }
471
472 static int
473 dump_ccd(argc, argv)
474 int argc;
475 char **argv;
476 {
477 char errbuf[_POSIX2_LINE_MAX], *ccd, *cp;
478 struct ccd_softc *cs, *kcs;
479 size_t readsize;
480 int i, error, numccd, numconfiged = 0;
481 kvm_t *kd;
482
483 bzero(errbuf, sizeof(errbuf));
484
485 if ((kd = kvm_openfiles(kernel, core, NULL, O_RDONLY,
486 errbuf)) == NULL) {
487 warnx("can't open kvm: %s", errbuf);
488 return (1);
489 }
490
491 if (kvm_nlist(kd, nl))
492 KVM_ABORT(kd, "ccd-related symbols not available");
493
494 /* Check to see how many ccds are currently configured. */
495 if (kvm_read(kd, nl[SYM_NUMCCD].n_value, (char *)&numccd,
496 sizeof(numccd)) != sizeof(numccd))
497 KVM_ABORT(kd, "can't determine number of configured ccds");
498
499 if (numccd == 0) {
500 printf("ccd driver in kernel, but is uninitialized\n");
501 goto done;
502 }
503
504 /* Allocate space for the configuration data. */
505 readsize = numccd * sizeof(struct ccd_softc);
506 if ((cs = malloc(readsize)) == NULL) {
507 warnx("no memory for configuration data");
508 goto bad;
509 }
510 bzero(cs, readsize);
511
512 /*
513 * Read the ccd configuration data from the kernel and dump
514 * it to stdout.
515 */
516 if (kvm_read(kd, nl[SYM_CCDSOFTC].n_value, (char *)&kcs,
517 sizeof(kcs)) != sizeof(kcs)) {
518 free(cs);
519 KVM_ABORT(kd, "can't find pointer to configuration data");
520 }
521 if (kvm_read(kd, (u_long)kcs, (char *)cs, readsize) != readsize) {
522 free(cs);
523 KVM_ABORT(kd, "can't read configuration data");
524 }
525
526 if (argc == 0) {
527 for (i = 0; i < numccd; ++i)
528 if (cs[i].sc_flags & CCDF_INITED) {
529 ++numconfiged;
530 print_ccd_info(&cs[i], kd);
531 }
532
533 if (numconfiged == 0)
534 printf("no concatenated disks configured\n");
535 } else {
536 while (argc) {
537 cp = *argv++; --argc;
538 if ((ccd = resolve_ccdname(cp)) == NULL) {
539 warnx("invalid ccd name: %s", cp);
540 continue;
541 }
542 if ((error = pathtounit(ccd, &i)) != 0) {
543 warnx("%s: %s", ccd, strerror(error));
544 continue;
545 }
546 if (i >= numccd) {
547 warnx("ccd%d not configured", i);
548 continue;
549 }
550 if (cs[i].sc_flags & CCDF_INITED)
551 print_ccd_info(&cs[i], kd);
552 else
553 printf("ccd%d not configured\n", i);
554 }
555 }
556
557 free(cs);
558
559 done:
560 (void)kvm_close(kd);
561 return (0);
562
563 bad:
564 (void)kvm_close(kd);
565 return (1);
566 }
567
568 static void
569 print_ccd_info(cs, kd)
570 struct ccd_softc *cs;
571 kvm_t *kd;
572 {
573 static int header_printed = 0;
574 struct ccdcinfo *cip;
575 size_t readsize;
576 char path[MAXPATHLEN];
577 int i;
578
579 if (header_printed == 0 && verbose) {
580 printf("# ccd\t\tileave\tflags\tcompnent devices\n");
581 header_printed = 1;
582 }
583
584 readsize = cs->sc_nccdisks * sizeof(struct ccdcinfo);
585 if ((cip = malloc(readsize)) == NULL) {
586 warn("ccd%d: can't allocate memory for component info",
587 cs->sc_unit);
588 return;
589 }
590 bzero(cip, readsize);
591
592 /* Dump out softc information. */
593 printf("ccd%d\t\t%d\t%d\t", cs->sc_unit, cs->sc_ileave,
594 cs->sc_cflags & CCDF_USERMASK);
595 fflush(stdout);
596
597 /* Read in the component info. */
598 if (kvm_read(kd, (u_long)cs->sc_cinfo, (char *)cip,
599 readsize) != readsize) {
600 printf("\n");
601 warnx("can't read component info");
602 warnx(kvm_geterr(kd));
603 goto done;
604 }
605
606 /* Read component pathname and display component info. */
607 for (i = 0; i < cs->sc_nccdisks; ++i) {
608 if (kvm_read(kd, (u_long)cip[i].ci_path, (char *)path,
609 cip[i].ci_pathlen) != cip[i].ci_pathlen) {
610 printf("\n");
611 warnx("can't read component pathname");
612 warnx(kvm_geterr(kd));
613 goto done;
614 }
615 printf((i + 1 < cs->sc_nccdisks) ? "%s " : "%s\n", path);
616 fflush(stdout);
617 }
618
619 done:
620 free(cip);
621 }
622
623 static int
624 getmaxpartitions()
625 {
626 int maxpart, mib[2];
627 size_t varlen;
628
629 mib[0] = CTL_KERN;
630 mib[1] = KERN_MAXPARTITIONS;
631 varlen = sizeof(maxpart);
632 if (sysctl(mib, 2, &maxpart, &varlen, NULL, 0) < 0)
633 return (-1);
634
635 return (maxpart);
636 }
637
638 static int
639 getrawpartition()
640 {
641 int rawpart, mib[2];
642 size_t varlen;
643
644 mib[0] = CTL_KERN;
645 mib[1] = KERN_RAWPARTITION;
646 varlen = sizeof(rawpart);
647 if (sysctl(mib, 2, &rawpart, &varlen, NULL, 0) < 0)
648 return (-1);
649
650 return (rawpart);
651 }
652
653 static int
654 flags_to_val(flags)
655 char *flags;
656 {
657 char *cp, *tok;
658 int i, tmp, val = ~CCDF_USERMASK;
659 size_t flagslen;
660
661 /*
662 * The most common case is that of NIL flags, so check for
663 * those first.
664 */
665 if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 ||
666 strcmp("0", flags) == 0)
667 return (0);
668
669 flagslen = strlen(flags);
670
671 /* Check for values represented by strings. */
672 if ((cp = strdup(flags)) == NULL)
673 err(1, "no memory to parse flags");
674 tmp = 0;
675 for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) {
676 for (i = 0; flagvaltab[i].fv_flag != NULL; ++i)
677 if (strcmp(tok, flagvaltab[i].fv_flag) == 0)
678 break;
679 if (flagvaltab[i].fv_flag == NULL) {
680 free(cp);
681 goto bad_string;
682 }
683 tmp |= flagvaltab[i].fv_val;
684 }
685
686 /* If we get here, the string was ok. */
687 free(cp);
688 val = tmp;
689 goto out;
690
691 bad_string:
692
693 /* Check for values represented in hex. */
694 if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') {
695 errno = 0; /* to check for ERANGE */
696 val = (int)strtol(&flags[2], &cp, 16);
697 if ((errno == ERANGE) || (*cp != '\0'))
698 return (-1);
699 goto out;
700 }
701
702 /* Check for values represented in decimal. */
703 errno = 0; /* to check for ERANGE */
704 val = (int)strtol(flags, &cp, 10);
705 if ((errno == ERANGE) || (*cp != '\0'))
706 return (-1);
707
708 out:
709 return (((val & ~CCDF_USERMASK) == 0) ? val : -1);
710 }
711
712 static void
713 usage()
714 {
715
716 fprintf(stderr, "usage: %s [-cv] ccd ileave [flags] %s\n", __progname,
717 "dev [...]");
718 fprintf(stderr, " %s -C [-v] [-f config_file]\n", __progname);
719 fprintf(stderr, " %s -u [-v] ccd [...]\n", __progname);
720 fprintf(stderr, " %s -U [-v] [-f config_file]\n", __progname);
721 fprintf(stderr, " %s -g [-M core] [-N system] %s\n", __progname,
722 "[ccd [...]]");
723 exit(1);
724 }
725