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