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