1/* $NetBSD: ccdconfig.c,v 1.50 2011/01/04 23:31:29 wiz 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33#ifndef lint 34__COPYRIGHT("@(#) Copyright (c) 1996, 1997\ 35 The NetBSD Foundation, Inc. All rights reserved."); 36__RCSID("$NetBSD: ccdconfig.c,v 1.50 2011/01/04 23:31:29 wiz Exp $"); 37#endif 38 39#include <sys/param.h> 40#include <sys/ioctl.h> 41#include <sys/disklabel.h> 42#include <sys/disk.h> 43#include <sys/stat.h> 44#include <sys/sysctl.h> 45#include <ctype.h> 46#include <err.h> 47#include <errno.h> 48#include <fcntl.h> 49#include <kvm.h> 50#include <limits.h> 51#include <nlist.h> 52#include <stdio.h> 53#include <stdlib.h> 54#include <string.h> 55#include <unistd.h> 56#include <util.h> 57 58#include <dev/ccdvar.h> 59 60#include "pathnames.h" 61 62 63static size_t lineno; 64static gid_t egid; 65static int verbose; 66static const char *ccdconf = _PATH_CCDCONF; 67 68static char *core; 69static char *kernel; 70 71static struct flagval { 72 const char *fv_flag; 73 int fv_val; 74} flagvaltab[] = { 75 { "CCDF_UNIFORM", CCDF_UNIFORM }, 76 { "CCDF_NOLABEL", CCDF_NOLABEL }, 77 { NULL, 0 }, 78}; 79 80static struct nlist nl[] = { 81 { .n_name = "_ccd_softc" }, 82#define SYM_CCDSOFTC 0 83 { .n_name = "_numccd" }, 84#define SYM_NUMCCD 1 85 { .n_name = "_ccd_softc_elemsize" }, 86#define SYM_CCDSOFTCELEMSIZE 2 87 { .n_name = NULL }, 88}; 89 90#define CCD_CONFIG 0 /* configure a device */ 91#define CCD_CONFIGALL 1 /* configure all devices */ 92#define CCD_UNCONFIG 2 /* unconfigure a device */ 93#define CCD_UNCONFIGALL 3 /* unconfigure all devices */ 94#define CCD_DUMP 4 /* dump a ccd's configuration */ 95 96static int checkdev(char *); 97static int do_io(char *, u_long, struct ccd_ioctl *); 98static int do_single(int, char **, int); 99static int do_all(int); 100static int dump_ccd(int, char **, int); 101static int flags_to_val(char *); 102static int pathtounit(char *, int *); 103static void print_ccd_info(struct ccd_softc *, kvm_t *); 104static char *resolve_ccdname(char *); 105__dead static void usage(void); 106 107int 108main(int argc, char *argv[]) 109{ 110 int ch, options = 0, action = CCD_CONFIG; 111 112 egid = getegid(); 113 setegid(getgid()); 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 /* 167 * Discard setgid privileges. If not the running kernel, we toss 168 * them away totally so that bad guys can't print interesting stuff 169 * from kernel memory, otherwise switch back to kmem for the 170 * duration of the kvm_openfiles() call. 171 * 172 * We also do this if we aren't just looking... 173 */ 174 if (core != NULL || kernel != NULL || action != CCD_DUMP) 175 setgid(getgid()); 176 177 switch (action) { 178 case CCD_CONFIG: 179 case CCD_UNCONFIG: 180 exit(do_single(argc, argv, action)); 181 /* NOTREACHED */ 182 183 case CCD_CONFIGALL: 184 case CCD_UNCONFIGALL: 185 exit(do_all(action)); 186 /* NOTREACHED */ 187 188 case CCD_DUMP: 189 default: 190 exit(dump_ccd(argc, argv, action)); 191 /* NOTREACHED */ 192 } 193 /* NOTREACHED */ 194} 195 196static int 197do_single(int argc, char **argv, int action) 198{ 199 struct ccd_ioctl ccio; 200 char *ccd, *cp, *cp2, **disks; 201 int noflags = 0, i, ileave, flags, j; 202 unsigned int ui; 203 204 flags = 0; 205 memset(&ccio, 0, sizeof(ccio)); 206 207 /* 208 * If unconfiguring, all arguments are treated as ccds. 209 */ 210 if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) { 211 for (i = 0; argc != 0; ) { 212 cp = *argv++; --argc; 213 if ((ccd = resolve_ccdname(cp)) == NULL) { 214 warnx("invalid ccd name: %s", cp); 215 i = 1; 216 continue; 217 } 218 if (do_io(ccd, CCDIOCCLR, &ccio)) 219 i = 1; 220 else 221 if (verbose) 222 printf("%s unconfigured\n", cp); 223 free(ccd); 224 } 225 return (i); 226 } 227 228 /* Make sure there are enough arguments. */ 229 if (argc < 4) { 230 if (argc == 3) { 231 /* Assume that no flags are specified. */ 232 noflags = 1; 233 } else { 234 if (action == CCD_CONFIGALL) { 235 warnx("%s: bad line: %lu", ccdconf, 236 (u_long)lineno); 237 return (1); 238 } else 239 usage(); 240 } 241 } 242 243 /* First argument is the ccd to configure. */ 244 cp = *argv++; --argc; 245 if ((ccd = resolve_ccdname(cp)) == NULL) { 246 warnx("invalid ccd name: %s", cp); 247 return (1); 248 } 249 250 /* Next argument is the interleave factor. */ 251 cp = *argv++; --argc; 252 errno = 0; /* to check for ERANGE */ 253 ileave = (int)strtol(cp, &cp2, 10); 254 if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) { 255 warnx("invalid interleave factor: %s", cp); 256 free(ccd); 257 return (1); 258 } 259 260 if (noflags == 0) { 261 /* Next argument is the ccd configuration flags. */ 262 cp = *argv++; --argc; 263 if ((flags = flags_to_val(cp)) < 0) { 264 warnx("invalid flags argument: %s", cp); 265 free(ccd); 266 return (1); 267 } 268 } 269 270 /* Next is the list of disks to make the ccd from. */ 271 disks = malloc(argc * sizeof(char *)); 272 if (disks == NULL) { 273 warnx("no memory to configure ccd"); 274 free(ccd); 275 return (1); 276 } 277 for (ui = 0; argc != 0; ) { 278 cp = *argv++; --argc; 279 if ((j = checkdev(cp)) == 0) 280 disks[ui++] = cp; 281 else { 282 warnx("%s: %s", cp, strerror(j)); 283 free(ccd); 284 free(disks); 285 return (1); 286 } 287 } 288 289 /* Fill in the ccio. */ 290 ccio.ccio_disks = disks; 291 ccio.ccio_ndisks = ui; 292 ccio.ccio_ileave = ileave; 293 ccio.ccio_flags = flags; 294 295 if (do_io(ccd, CCDIOCSET, &ccio)) { 296 free(ccd); 297 free(disks); 298 return (1); 299 } 300 301 if (verbose) { 302 printf("ccd%d: %d components ", ccio.ccio_unit, 303 ccio.ccio_ndisks); 304 for (ui = 0; ui < ccio.ccio_ndisks; ++ui) { 305 if ((cp2 = strrchr(disks[ui], '/')) != NULL) 306 ++cp2; 307 else 308 cp2 = disks[ui]; 309 printf("%c%s%c", 310 ui == 0 ? '(' : ' ', cp2, 311 ui == ccio.ccio_ndisks - 1 ? ')' : ','); 312 } 313 printf(", %ld blocks ", (long)ccio.ccio_size); 314 if (ccio.ccio_ileave != 0) 315 printf("interleaved at %d blocks\n", ccio.ccio_ileave); 316 else 317 printf("concatenated\n"); 318 } 319 320 free(ccd); 321 free(disks); 322 return (0); 323} 324 325static int 326do_all(int action) 327{ 328 FILE *f; 329 char *line, *cp, *vp, **argv, **nargv; 330 int argc, rval; 331 size_t len; 332 333 rval = 0; 334 335 (void)setegid(getgid()); 336 if ((f = fopen(ccdconf, "r")) == NULL) { 337 (void)setegid(egid); 338 warn("fopen: %s", ccdconf); 339 return (1); 340 } 341 (void)setegid(egid); 342 343 while ((line = fparseln(f, &len, &lineno, "\\\\#", FPARSELN_UNESCALL)) 344 != NULL) { 345 argc = 0; 346 argv = NULL; 347 if (len == 0) 348 goto end_of_line; 349 350 for (cp = line; cp != NULL; ) { 351 while ((vp = strsep(&cp, "\t ")) != NULL && *vp == '\0') 352 ; 353 if (vp == NULL) 354 continue; 355 356 if ((nargv = realloc(argv, 357 sizeof(char *) * (argc + 1))) == NULL) { 358 warnx("no memory to configure ccds"); 359 return (1); 360 } 361 argv = nargv; 362 argc++; 363 argv[argc - 1] = vp; 364 365 /* 366 * If our action is to unconfigure all, then pass 367 * just the first token to do_single() and ignore 368 * the rest. Since this will be encountered on 369 * our first pass through the line, the Right 370 * Thing will happen. 371 */ 372 if (action == CCD_UNCONFIGALL) { 373 if (do_single(argc, argv, action)) 374 rval = 1; 375 goto end_of_line; 376 } 377 } 378 if (argc != 0) 379 if (do_single(argc, argv, action)) 380 rval = 1; 381 382 end_of_line: 383 if (argv != NULL) 384 free(argv); 385 free(line); 386 } 387 388 (void)fclose(f); 389 return (rval); 390} 391 392static int 393checkdev(char *path) 394{ 395 struct stat st; 396 397 if (stat(path, &st) != 0) 398 return (errno); 399 400 if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) 401 return (EINVAL); 402 403 return (0); 404} 405 406static int 407pathtounit(char *path, int *unitp) 408{ 409 struct stat st; 410 411 if (stat(path, &st) != 0) 412 return (errno); 413 414 if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) 415 return (EINVAL); 416 417 *unitp = DISKUNIT(st.st_rdev); 418 419 return (0); 420} 421 422static char * 423resolve_ccdname(char *name) 424{ 425 char c, *path; 426 size_t len; 427 int rawpart; 428 429 if (name[0] == '/' || name[0] == '.') { 430 /* Assume they gave the correct pathname. */ 431 return (strdup(name)); 432 } 433 434 len = strlen(name); 435 c = name[len - 1]; 436 437 if (isdigit((unsigned char)c)) { 438 if ((rawpart = getrawpartition()) < 0) 439 return (NULL); 440 if (asprintf(&path, "/dev/%s%c", name, 'a' + rawpart) < 0) 441 return (NULL); 442 } else 443 if (asprintf(&path, "/dev/%s", name) < 0) 444 return (NULL); 445 446 return (path); 447} 448 449static int 450do_io(char *path, u_long cmd, struct ccd_ioctl *cciop) 451{ 452 int fd; 453 const char *cp; 454 455 if ((fd = open(path, O_RDWR, 0640)) < 0) { 456 warn("open: %s", path); 457 return (1); 458 } 459 460 if (ioctl(fd, cmd, cciop) < 0) { 461 switch (cmd) { 462 case CCDIOCSET: 463 cp = "CCDIOCSET"; 464 break; 465 466 case CCDIOCCLR: 467 cp = "CCDIOCCLR"; 468 break; 469 470 default: 471 cp = "unknown"; 472 } 473 warn("ioctl (%s): %s", cp, path); 474 (void)close(fd); 475 return (1); 476 } 477 478 (void)close(fd); 479 return (0); 480} 481 482#define KVM_ABORT(kd, str) { \ 483 (void)kvm_close((kd)); \ 484 warnx("%s", (str)); \ 485 warnx("%s", kvm_geterr((kd))); \ 486 return (1); \ 487} 488 489static int 490dump_ccd(int argc, char **argv, int action) 491{ 492 char errbuf[_POSIX2_LINE_MAX], *ccd, *cp; 493 struct ccd_softc *cs, *kcs; 494 void *vcs; 495 size_t readsize; 496 int i, error, numccd, ccd_softc_elemsize, numconfiged = 0; 497 kvm_t *kd; 498 499 memset(errbuf, 0, sizeof(errbuf)); 500 501 vcs = NULL; 502 (void)setegid(egid); 503 if ((kd = kvm_openfiles(kernel, core, NULL, O_RDONLY, 504 errbuf)) == NULL) { 505 warnx("can't open kvm: %s", errbuf); 506 return (1); 507 } 508 (void)setgid(getgid()); 509 510 if (kvm_nlist(kd, nl)) 511 KVM_ABORT(kd, "ccd-related symbols not available"); 512 513 /* Check to see how many ccds are currently configured. */ 514 if (kvm_read(kd, nl[SYM_NUMCCD].n_value, (void *)&numccd, 515 sizeof(numccd)) != sizeof(numccd)) 516 KVM_ABORT(kd, "can't determine number of configured ccds"); 517 518 if (numccd == 0) { 519 printf("ccd driver in kernel, but is uninitialized\n"); 520 goto done; 521 } 522 523 if (kvm_read(kd, nl[SYM_CCDSOFTCELEMSIZE].n_value, 524 (void *)&ccd_softc_elemsize, sizeof(ccd_softc_elemsize)) 525 != sizeof(ccd_softc_elemsize)) 526 KVM_ABORT(kd, "can't determine size of ccd_softc"); 527 528 /* Allocate space for the kernel's configuration data. */ 529 readsize = numccd * ccd_softc_elemsize; 530 if ((vcs = malloc(readsize)) == NULL) { 531 warnx("no memory for configuration data"); 532 goto bad; 533 } 534 memset(vcs, 0, readsize); 535 536 /* 537 * Read the ccd configuration data from the kernel. 538 * The kernel's ccd_softc is larger than userland's 539 * (the former contains extra structure members at 540 * the end of the structure), so read the kernel 541 * ccd_softcs into a temporary buffer and convert 542 * into userland ccd_softcs. 543 */ 544 if (kvm_read(kd, nl[SYM_CCDSOFTC].n_value, (void *)&kcs, 545 sizeof(kcs)) != sizeof(kcs)) { 546 free(vcs); 547 KVM_ABORT(kd, "can't find pointer to configuration data"); 548 } 549 if ((size_t)kvm_read(kd, (u_long)kcs, vcs, readsize) != readsize) { 550 free(vcs); 551 KVM_ABORT(kd, "can't read configuration data"); 552 } 553 554 if ((cs = calloc(numccd, sizeof(struct ccd_softc))) == NULL) { 555 warnx("no memory for configuration data"); 556 free(vcs); 557 goto bad; 558 } 559 for (i = 0; i < numccd; i++) { 560 memcpy(&cs[i], (char *)vcs + i * ccd_softc_elemsize, 561 sizeof(struct ccd_softc)); 562 } 563 free(vcs); 564 565 /* Dump ccd configuration to stdout. */ 566 if (argc == 0) { 567 for (i = 0; i < numccd; ++i) 568 if (cs[i].sc_flags & CCDF_INITED) { 569 ++numconfiged; 570 print_ccd_info(&cs[i], kd); 571 } 572 573 if (numconfiged == 0) 574 printf("# no concatenated disks configured\n"); 575 } else { 576 while (argc) { 577 cp = *argv++; --argc; 578 if ((ccd = resolve_ccdname(cp)) == NULL) { 579 warnx("invalid ccd name: %s", cp); 580 continue; 581 } 582 if ((error = pathtounit(ccd, &i)) != 0) { 583 warn("%s", ccd); 584 free(ccd); 585 continue; 586 } 587 if (i >= numccd) { 588 warnx("ccd%d not configured", i); 589 free(ccd); 590 continue; 591 } 592 if (cs[i].sc_flags & CCDF_INITED) 593 print_ccd_info(&cs[i], kd); 594 else 595 printf("# ccd%d not configured\n", i); 596 free(ccd); 597 } 598 } 599 600 free(cs); 601 602 done: 603 (void)kvm_close(kd); 604 return (0); 605 606 bad: 607 (void)kvm_close(kd); 608 return (1); 609} 610 611static void 612print_ccd_info(struct ccd_softc *cs, kvm_t *kd) 613{ 614 static int header_printed = 0; 615 struct ccdcinfo *cip; 616 size_t readsize; 617 char path[MAXPATHLEN]; 618 unsigned int i; 619 620 if (header_printed == 0 && verbose) { 621 printf("# ccd\t\tileave\tflags\tcompnent devices\n"); 622 header_printed = 1; 623 } 624 625 readsize = cs->sc_nccdisks * sizeof(struct ccdcinfo); 626 if ((cip = malloc(readsize)) == NULL) { 627 warn("%s: can't allocate memory for component info", 628 cs->sc_xname); 629 return; 630 } 631 memset(cip, 0, readsize); 632 633 /* Dump out softc information. */ 634 printf("%s\t\t%d\t%d\t", cs->sc_xname, cs->sc_ileave, 635 cs->sc_flags & CCDF_USERMASK); 636 fflush(stdout); 637 638 /* Read in the component info. */ 639 if ((size_t)kvm_read(kd, (u_long)cs->sc_cinfo, (void *)cip, 640 readsize) != readsize) { 641 printf("\n"); 642 warnx("can't read component info"); 643 warnx("%s", kvm_geterr(kd)); 644 goto done; 645 } 646 647 /* Read component pathname and display component info. */ 648 for (i = 0; i < cs->sc_nccdisks; ++i) { 649 if ((size_t)kvm_read(kd, (u_long)cip[i].ci_path, (void *)path, 650 cip[i].ci_pathlen) != cip[i].ci_pathlen) { 651 printf("\n"); 652 warnx("can't read component pathname"); 653 warnx("%s", kvm_geterr(kd)); 654 goto done; 655 } 656 fputs(path, stdout); 657 fputc((i + 1 < cs->sc_nccdisks) ? ' ' : '\n', stdout); 658 fflush(stdout); 659 } 660 661 done: 662 free(cip); 663} 664 665static int 666flags_to_val(char *flags) 667{ 668 char *cp, *tok; 669 int i, tmp, val = ~CCDF_USERMASK; 670 size_t flagslen; 671 672 /* 673 * The most common case is that of NIL flags, so check for 674 * those first. 675 */ 676 if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 || 677 strcmp("0", flags) == 0) 678 return (0); 679 680 flagslen = strlen(flags); 681 682 /* Check for values represented by strings. */ 683 if ((cp = strdup(flags)) == NULL) 684 err(1, "no memory to parse flags"); 685 tmp = 0; 686 for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) { 687 for (i = 0; flagvaltab[i].fv_flag != NULL; ++i) 688 if (strcmp(tok, flagvaltab[i].fv_flag) == 0) 689 break; 690 if (flagvaltab[i].fv_flag == NULL) { 691 free(cp); 692 goto bad_string; 693 } 694 tmp |= flagvaltab[i].fv_val; 695 } 696 697 /* If we get here, the string was ok. */ 698 free(cp); 699 val = tmp; 700 goto out; 701 702 bad_string: 703 704 /* Check for values represented in hex. */ 705 if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') { 706 errno = 0; /* to check for ERANGE */ 707 val = (int)strtol(&flags[2], &cp, 16); 708 if ((errno == ERANGE) || (*cp != '\0')) 709 return (-1); 710 goto out; 711 } 712 713 /* Check for values represented in decimal. */ 714 errno = 0; /* to check for ERANGE */ 715 val = (int)strtol(flags, &cp, 10); 716 if ((errno == ERANGE) || (*cp != '\0')) 717 return (-1); 718 719 out: 720 return (((val & ~CCDF_USERMASK) == 0) ? val : -1); 721} 722 723static void 724usage(void) 725{ 726 const char *progname = getprogname(); 727 728 fprintf(stderr, "usage: %s [-cv] ccd ileave [flags] dev [...]\n", 729 progname); 730 fprintf(stderr, " %s -C [-v] [-f config_file]\n", progname); 731 fprintf(stderr, " %s -u [-v] ccd [...]\n", progname); 732 fprintf(stderr, " %s -U [-v] [-f config_file]\n", progname); 733 fprintf(stderr, " %s -g [-M core] [-N system] [ccd [...]]\n", 734 progname); 735 exit(1); 736} 737