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