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