1 /* $NetBSD: ccdconfig.c,v 1.32 2000/10/10 20:24:50 is 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.32 2000/10/10 20:24:50 is 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_UNIFORM", CCDF_UNIFORM }, 87 { NULL, 0 }, 88 }; 89 90 static struct nlist nl[] = { 91 { "_ccd_softc" }, 92 #define SYM_CCDSOFTC 0 93 { "_numccd" }, 94 #define SYM_NUMCCD 1 95 { NULL }, 96 }; 97 98 #define CCD_CONFIG 0 /* configure a device */ 99 #define CCD_CONFIGALL 1 /* configure all devices */ 100 #define CCD_UNCONFIG 2 /* unconfigure a device */ 101 #define CCD_UNCONFIGALL 3 /* unconfigure all devices */ 102 #define CCD_DUMP 4 /* dump a ccd's configuration */ 103 104 int main __P((int, char *[])); 105 static int checkdev __P((char *)); 106 static int do_io __P((char *, u_long, struct ccd_ioctl *)); 107 static int do_single __P((int, char **, int)); 108 static int do_all __P((int)); 109 static int dump_ccd __P((int, char **, int)); 110 static int flags_to_val __P((char *)); 111 static int pathtounit __P((char *, int *)); 112 static void print_ccd_info __P((struct ccd_softc *, kvm_t *)); 113 static char *resolve_ccdname __P((char *)); 114 static void usage __P((void)); 115 116 int 117 main(argc, argv) 118 int argc; 119 char *argv[]; 120 { 121 int ch, options = 0, action = CCD_CONFIG; 122 123 egid = getegid(); 124 setegid(getgid()); 125 while ((ch = getopt(argc, argv, "cCf:gM:N:uUv")) != -1) { 126 switch (ch) { 127 case 'c': 128 action = CCD_CONFIG; 129 ++options; 130 break; 131 132 case 'C': 133 action = CCD_CONFIGALL; 134 ++options; 135 break; 136 137 case 'f': 138 ccdconf = optarg; 139 break; 140 141 case 'g': 142 action = CCD_DUMP; 143 break; 144 145 case 'M': 146 core = optarg; 147 break; 148 149 case 'N': 150 kernel = optarg; 151 break; 152 153 case 'u': 154 action = CCD_UNCONFIG; 155 ++options; 156 break; 157 158 case 'U': 159 action = CCD_UNCONFIGALL; 160 ++options; 161 break; 162 163 case 'v': 164 verbose = 1; 165 break; 166 167 default: 168 usage(); 169 } 170 } 171 argc -= optind; 172 argv += optind; 173 174 if (options > 1) 175 usage(); 176 177 /* 178 * Discard setgid privileges. If not the running kernel, we toss 179 * them away totally so that bad guys can't print interesting stuff 180 * from kernel memory, otherwise switch back to kmem for the 181 * duration of the kvm_openfiles() call. 182 * 183 * We also do this if we aren't just looking... 184 */ 185 if (core != NULL || kernel != NULL || action != CCD_DUMP) 186 setgid(getgid()); 187 188 switch (action) { 189 case CCD_CONFIG: 190 case CCD_UNCONFIG: 191 exit(do_single(argc, argv, action)); 192 /* NOTREACHED */ 193 194 case CCD_CONFIGALL: 195 case CCD_UNCONFIGALL: 196 exit(do_all(action)); 197 /* NOTREACHED */ 198 199 case CCD_DUMP: 200 default: 201 exit(dump_ccd(argc, argv, action)); 202 /* NOTREACHED */ 203 } 204 /* NOTREACHED */ 205 } 206 207 static int 208 do_single(argc, argv, action) 209 int argc; 210 char **argv; 211 int action; 212 { 213 struct ccd_ioctl ccio; 214 char *ccd, *cp, *cp2, **disks; 215 int noflags = 0, i, ileave, flags, j; 216 217 flags = 0; 218 memset(&ccio, 0, sizeof(ccio)); 219 220 /* 221 * If unconfiguring, all arguments are treated as ccds. 222 */ 223 if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) { 224 for (i = 0; argc != 0; ) { 225 cp = *argv++; --argc; 226 if ((ccd = resolve_ccdname(cp)) == NULL) { 227 warnx("invalid ccd name: %s", cp); 228 i = 1; 229 continue; 230 } 231 if (do_io(ccd, CCDIOCCLR, &ccio)) 232 i = 1; 233 else 234 if (verbose) 235 printf("%s unconfigured\n", cp); 236 } 237 return (i); 238 } 239 240 /* Make sure there are enough arguments. */ 241 if (argc < 4) { 242 if (argc == 3) { 243 /* Assume that no flags are specified. */ 244 noflags = 1; 245 } else { 246 if (action == CCD_CONFIGALL) { 247 warnx("%s: bad line: %lu", ccdconf, 248 (u_long)lineno); 249 return (1); 250 } else 251 usage(); 252 } 253 } 254 255 /* First argument is the ccd to configure. */ 256 cp = *argv++; --argc; 257 if ((ccd = resolve_ccdname(cp)) == NULL) { 258 warnx("invalid ccd name: %s", cp); 259 return (1); 260 } 261 262 /* Next argument is the interleave factor. */ 263 cp = *argv++; --argc; 264 errno = 0; /* to check for ERANGE */ 265 ileave = (int)strtol(cp, &cp2, 10); 266 if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) { 267 warnx("invalid interleave factor: %s", cp); 268 return (1); 269 } 270 271 if (noflags == 0) { 272 /* Next argument is the ccd configuration flags. */ 273 cp = *argv++; --argc; 274 if ((flags = flags_to_val(cp)) < 0) { 275 warnx("invalid flags argument: %s", cp); 276 return (1); 277 } 278 } 279 280 /* Next is the list of disks to make the ccd from. */ 281 disks = malloc(argc * sizeof(char *)); 282 if (disks == NULL) { 283 warnx("no memory to configure ccd"); 284 return (1); 285 } 286 for (i = 0; argc != 0; ) { 287 cp = *argv++; --argc; 288 if ((j = checkdev(cp)) == 0) 289 disks[i++] = cp; 290 else { 291 warnx("%s: %s", cp, strerror(j)); 292 return (1); 293 } 294 } 295 296 /* Fill in the ccio. */ 297 ccio.ccio_disks = disks; 298 ccio.ccio_ndisks = i; 299 ccio.ccio_ileave = ileave; 300 ccio.ccio_flags = flags; 301 302 if (do_io(ccd, CCDIOCSET, &ccio)) { 303 free(disks); 304 return (1); 305 } 306 307 if (verbose) { 308 printf("ccd%d: %d components ", ccio.ccio_unit, 309 ccio.ccio_ndisks); 310 for (i = 0; i < ccio.ccio_ndisks; ++i) { 311 if ((cp2 = strrchr(disks[i], '/')) != NULL) 312 ++cp2; 313 else 314 cp2 = disks[i]; 315 printf("%c%s%c", 316 i == 0 ? '(' : ' ', cp2, 317 i == ccio.ccio_ndisks - 1 ? ')' : ','); 318 } 319 printf(", %ld blocks ", (long)ccio.ccio_size); 320 if (ccio.ccio_ileave != 0) 321 printf("interleaved at %d blocks\n", ccio.ccio_ileave); 322 else 323 printf("concatenated\n"); 324 } 325 326 free(disks); 327 return (0); 328 } 329 330 static int 331 do_all(action) 332 int action; 333 { 334 FILE *f; 335 char *line, *cp, *vp, **argv; 336 int argc, rval; 337 size_t len; 338 339 rval = 0; 340 341 (void)setegid(getgid()); 342 if ((f = fopen(ccdconf, "r")) == NULL) { 343 (void)setegid(egid); 344 warn("fopen: %s", ccdconf); 345 return (1); 346 } 347 (void)setegid(egid); 348 349 while ((line = fparseln(f, &len, &lineno, "\\\\#", FPARSELN_UNESCALL)) 350 != NULL) { 351 argc = 0; 352 argv = NULL; 353 if (len == 0) 354 goto end_of_line; 355 356 for (cp = line; cp != NULL; ) { 357 while ((vp = strsep(&cp, "\t ")) != NULL && *vp == '\0') 358 ; 359 if (vp == NULL) 360 continue; 361 362 if ((argv = realloc(argv, 363 sizeof(char *) * ++argc)) == NULL) { 364 warnx("no memory to configure ccds"); 365 return (1); 366 } 367 argv[argc - 1] = vp; 368 369 /* 370 * If our action is to unconfigure all, then pass 371 * just the first token to do_single() and ignore 372 * the rest. Since this will be encountered on 373 * our first pass through the line, the Right 374 * Thing will happen. 375 */ 376 if (action == CCD_UNCONFIGALL) { 377 if (do_single(argc, argv, action)) 378 rval = 1; 379 goto end_of_line; 380 } 381 } 382 if (argc != 0) 383 if (do_single(argc, argv, action)) 384 rval = 1; 385 386 end_of_line: 387 if (argv != NULL) 388 free(argv); 389 free(line); 390 } 391 392 (void)fclose(f); 393 return (rval); 394 } 395 396 static int 397 checkdev(path) 398 char *path; 399 { 400 struct stat st; 401 402 if (stat(path, &st) != 0) 403 return (errno); 404 405 if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) 406 return (EINVAL); 407 408 return (0); 409 } 410 411 static int 412 pathtounit(path, unitp) 413 char *path; 414 int *unitp; 415 { 416 struct stat st; 417 int maxpartitions; 418 419 if (stat(path, &st) != 0) 420 return (errno); 421 422 if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) 423 return (EINVAL); 424 425 if ((maxpartitions = getmaxpartitions()) < 0) 426 return (errno); 427 428 *unitp = minor(st.st_rdev) / maxpartitions; 429 430 return (0); 431 } 432 433 static char * 434 resolve_ccdname(name) 435 char *name; 436 { 437 char c, *path; 438 size_t len, newlen; 439 int rawpart; 440 441 if (name[0] == '/' || name[0] == '.') { 442 /* Assume they gave the correct pathname. */ 443 return (strdup(name)); 444 } 445 446 len = strlen(name); 447 c = name[len - 1]; 448 449 newlen = len + 8; 450 if ((path = malloc(newlen)) == NULL) 451 return (NULL); 452 memset(path, 0, newlen); 453 454 if (isdigit(c)) { 455 if ((rawpart = getrawpartition()) < 0) { 456 free(path); 457 return (NULL); 458 } 459 (void)snprintf(path, newlen, "/dev/%s%c", name, 'a' + rawpart); 460 } else 461 (void)snprintf(path, newlen, "/dev/%s", name); 462 463 return (path); 464 } 465 466 static int 467 do_io(path, cmd, cciop) 468 char *path; 469 u_long cmd; 470 struct ccd_ioctl *cciop; 471 { 472 int fd; 473 char *cp; 474 475 if ((fd = open(path, O_RDWR, 0640)) < 0) { 476 warn("open: %s", path); 477 return (1); 478 } 479 480 if (ioctl(fd, cmd, cciop) < 0) { 481 switch (cmd) { 482 case CCDIOCSET: 483 cp = "CCDIOCSET"; 484 break; 485 486 case CCDIOCCLR: 487 cp = "CCDIOCCLR"; 488 break; 489 490 default: 491 cp = "unknown"; 492 } 493 warn("ioctl (%s): %s", cp, path); 494 return (1); 495 } 496 497 return (0); 498 } 499 500 #define KVM_ABORT(kd, str) { \ 501 (void)kvm_close((kd)); \ 502 warnx("%s", (str)); \ 503 warnx("%s", kvm_geterr((kd))); \ 504 return (1); \ 505 } 506 507 static int 508 dump_ccd(argc, argv, action) 509 int argc; 510 char **argv; 511 int action; 512 { 513 char errbuf[_POSIX2_LINE_MAX], *ccd, *cp; 514 struct ccd_softc *cs, *kcs; 515 size_t readsize; 516 int i, error, numccd, numconfiged = 0; 517 kvm_t *kd; 518 519 memset(errbuf, 0, sizeof(errbuf)); 520 521 (void)setegid(egid); 522 if ((kd = kvm_openfiles(kernel, core, NULL, O_RDONLY, 523 errbuf)) == NULL) { 524 warnx("can't open kvm: %s", errbuf); 525 return (1); 526 } 527 (void)setgid(getgid()); 528 529 if (kvm_nlist(kd, nl)) 530 KVM_ABORT(kd, "ccd-related symbols not available"); 531 532 /* Check to see how many ccds are currently configured. */ 533 if (kvm_read(kd, nl[SYM_NUMCCD].n_value, (char *)&numccd, 534 sizeof(numccd)) != sizeof(numccd)) 535 KVM_ABORT(kd, "can't determine number of configured ccds"); 536 537 if (numccd == 0) { 538 printf("ccd driver in kernel, but is uninitialized\n"); 539 goto done; 540 } 541 542 /* Allocate space for the configuration data. */ 543 readsize = numccd * sizeof(struct ccd_softc); 544 if ((cs = malloc(readsize)) == NULL) { 545 warnx("no memory for configuration data"); 546 goto bad; 547 } 548 memset(cs, 0, readsize); 549 550 /* 551 * Read the ccd configuration data from the kernel and dump 552 * it to stdout. 553 */ 554 if (kvm_read(kd, nl[SYM_CCDSOFTC].n_value, (char *)&kcs, 555 sizeof(kcs)) != sizeof(kcs)) { 556 free(cs); 557 KVM_ABORT(kd, "can't find pointer to configuration data"); 558 } 559 if (kvm_read(kd, (u_long)kcs, (char *)cs, readsize) != readsize) { 560 free(cs); 561 KVM_ABORT(kd, "can't read configuration data"); 562 } 563 564 if (argc == 0) { 565 for (i = 0; i < numccd; ++i) 566 if (cs[i].sc_flags & CCDF_INITED) { 567 ++numconfiged; 568 print_ccd_info(&cs[i], kd); 569 } 570 571 if (numconfiged == 0) 572 printf("# no concatenated disks configured\n"); 573 } else { 574 while (argc) { 575 cp = *argv++; --argc; 576 if ((ccd = resolve_ccdname(cp)) == NULL) { 577 warnx("invalid ccd name: %s", cp); 578 continue; 579 } 580 if ((error = pathtounit(ccd, &i)) != 0) { 581 warn("%s", ccd); 582 continue; 583 } 584 if (i >= numccd) { 585 warnx("ccd%d not configured", i); 586 continue; 587 } 588 if (cs[i].sc_flags & CCDF_INITED) 589 print_ccd_info(&cs[i], kd); 590 else 591 printf("# ccd%d not configured\n", i); 592 } 593 } 594 595 free(cs); 596 597 done: 598 (void)kvm_close(kd); 599 return (0); 600 601 bad: 602 (void)kvm_close(kd); 603 return (1); 604 } 605 606 static void 607 print_ccd_info(cs, kd) 608 struct ccd_softc *cs; 609 kvm_t *kd; 610 { 611 static int header_printed = 0; 612 struct ccdcinfo *cip; 613 size_t readsize; 614 char path[MAXPATHLEN]; 615 int i; 616 617 if (header_printed == 0 && verbose) { 618 printf("# ccd\t\tileave\tflags\tcompnent devices\n"); 619 header_printed = 1; 620 } 621 622 readsize = cs->sc_nccdisks * sizeof(struct ccdcinfo); 623 if ((cip = malloc(readsize)) == NULL) { 624 warn("%s: can't allocate memory for component info", 625 cs->sc_xname); 626 return; 627 } 628 memset(cip, 0, readsize); 629 630 /* Dump out softc information. */ 631 printf("%s\t\t%d\t%d\t", cs->sc_xname, cs->sc_ileave, 632 cs->sc_flags & CCDF_USERMASK); 633 fflush(stdout); 634 635 /* Read in the component info. */ 636 if (kvm_read(kd, (u_long)cs->sc_cinfo, (char *)cip, 637 readsize) != readsize) { 638 printf("\n"); 639 warnx("can't read component info"); 640 warnx("%s", kvm_geterr(kd)); 641 goto done; 642 } 643 644 /* Read component pathname and display component info. */ 645 for (i = 0; i < cs->sc_nccdisks; ++i) { 646 if (kvm_read(kd, (u_long)cip[i].ci_path, (char *)path, 647 cip[i].ci_pathlen) != cip[i].ci_pathlen) { 648 printf("\n"); 649 warnx("can't read component pathname"); 650 warnx("%s", kvm_geterr(kd)); 651 goto done; 652 } 653 fputs(path, stdout); 654 fputc((i + 1 < cs->sc_nccdisks) ? ' ' : '\n', stdout); 655 fflush(stdout); 656 } 657 658 done: 659 free(cip); 660 } 661 662 static int 663 flags_to_val(flags) 664 char *flags; 665 { 666 char *cp, *tok; 667 int i, tmp, val = ~CCDF_USERMASK; 668 size_t flagslen; 669 670 /* 671 * The most common case is that of NIL flags, so check for 672 * those first. 673 */ 674 if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 || 675 strcmp("0", flags) == 0) 676 return (0); 677 678 flagslen = strlen(flags); 679 680 /* Check for values represented by strings. */ 681 if ((cp = strdup(flags)) == NULL) 682 err(1, "no memory to parse flags"); 683 tmp = 0; 684 for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) { 685 for (i = 0; flagvaltab[i].fv_flag != NULL; ++i) 686 if (strcmp(tok, flagvaltab[i].fv_flag) == 0) 687 break; 688 if (flagvaltab[i].fv_flag == NULL) { 689 free(cp); 690 goto bad_string; 691 } 692 tmp |= flagvaltab[i].fv_val; 693 } 694 695 /* If we get here, the string was ok. */ 696 free(cp); 697 val = tmp; 698 goto out; 699 700 bad_string: 701 702 /* Check for values represented in hex. */ 703 if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') { 704 errno = 0; /* to check for ERANGE */ 705 val = (int)strtol(&flags[2], &cp, 16); 706 if ((errno == ERANGE) || (*cp != '\0')) 707 return (-1); 708 goto out; 709 } 710 711 /* Check for values represented in decimal. */ 712 errno = 0; /* to check for ERANGE */ 713 val = (int)strtol(flags, &cp, 10); 714 if ((errno == ERANGE) || (*cp != '\0')) 715 return (-1); 716 717 out: 718 return (((val & ~CCDF_USERMASK) == 0) ? val : -1); 719 } 720 721 static void 722 usage() 723 { 724 725 fprintf(stderr, "usage: %s [-cv] ccd ileave [flags] %s\n", __progname, 726 "dev [...]"); 727 fprintf(stderr, " %s -C [-v] [-f config_file]\n", __progname); 728 fprintf(stderr, " %s -u [-v] ccd [...]\n", __progname); 729 fprintf(stderr, " %s -U [-v] [-f config_file]\n", __progname); 730 fprintf(stderr, " %s -g [-M core] [-N system] %s\n", __progname, 731 "[ccd [...]]"); 732 exit(1); 733 } 734