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