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