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