1 /* $NetBSD: ccdconfig.c,v 1.56 2014/12/07 10:44:34 mlelstv 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.56 2014/12/07 10:44:34 mlelstv 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 <limits.h> 50 #include <nlist.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <unistd.h> 55 #include <util.h> 56 57 #include <dev/ccdvar.h> 58 59 #include "pathnames.h" 60 61 62 static size_t lineno; 63 static gid_t egid; 64 static int verbose; 65 static const char *ccdconf = _PATH_CCDCONF; 66 67 static struct flagval { 68 const char *fv_flag; 69 int fv_val; 70 } flagvaltab[] = { 71 { "CCDF_UNIFORM", CCDF_UNIFORM }, 72 { "CCDF_NOLABEL", CCDF_NOLABEL }, 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(char *); 83 static int do_io(char *, u_long, struct ccd_ioctl *); 84 static int do_single(int, char **, int); 85 static int do_all(int); 86 static int dump_ccd(int, char **, int); 87 static int flags_to_val(char *); 88 static int pathtounit(char *, int *); 89 static char *resolve_ccdname(char *); 90 __dead static void usage(void); 91 92 int 93 main(int argc, char *argv[]) 94 { 95 int ch, options = 0, action = CCD_CONFIG; 96 97 egid = getegid(); 98 setegid(getgid()); 99 while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) { 100 switch (ch) { 101 case 'c': 102 action = CCD_CONFIG; 103 ++options; 104 break; 105 106 case 'C': 107 action = CCD_CONFIGALL; 108 ++options; 109 break; 110 111 case 'f': 112 ccdconf = optarg; 113 break; 114 115 case 'g': 116 action = CCD_DUMP; 117 break; 118 119 case 'u': 120 action = CCD_UNCONFIG; 121 ++options; 122 break; 123 124 case 'U': 125 action = CCD_UNCONFIGALL; 126 ++options; 127 break; 128 129 case 'v': 130 verbose = 1; 131 break; 132 133 default: 134 usage(); 135 } 136 } 137 argc -= optind; 138 argv += optind; 139 140 if (options > 1) 141 usage(); 142 143 switch (action) { 144 case CCD_CONFIG: 145 case CCD_UNCONFIG: 146 exit(do_single(argc, argv, action)); 147 /* NOTREACHED */ 148 149 case CCD_CONFIGALL: 150 case CCD_UNCONFIGALL: 151 exit(do_all(action)); 152 /* NOTREACHED */ 153 154 case CCD_DUMP: 155 default: 156 exit(dump_ccd(argc, argv, action)); 157 /* NOTREACHED */ 158 } 159 /* NOTREACHED */ 160 } 161 162 static int 163 do_single(int argc, char **argv, int action) 164 { 165 struct ccd_ioctl ccio; 166 char *ccd, *cp, *cp2, **disks; 167 char buf[MAXPATHLEN]; 168 int noflags = 0, i, ileave, flags, j; 169 unsigned int ndisks, ui; 170 int ret = 1; 171 172 flags = 0; 173 memset(&ccio, 0, sizeof(ccio)); 174 175 /* 176 * If unconfiguring, all arguments are treated as ccds. 177 */ 178 if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) { 179 for (i = 0; argc != 0; ) { 180 cp = *argv++; --argc; 181 if ((ccd = resolve_ccdname(cp)) == NULL) { 182 warnx("invalid ccd name: %s", cp); 183 i = 1; 184 continue; 185 } 186 if (do_io(ccd, CCDIOCCLR, &ccio)) 187 i = 1; 188 else 189 if (verbose) 190 printf("%s unconfigured\n", cp); 191 free(ccd); 192 } 193 return (i); 194 } 195 196 /* Make sure there are enough arguments. */ 197 if (argc < 4) { 198 if (argc == 3) { 199 /* Assume that no flags are specified. */ 200 noflags = 1; 201 } else { 202 if (action == CCD_CONFIGALL) { 203 warnx("%s: bad line: %lu", ccdconf, 204 (u_long)lineno); 205 return (1); 206 } else 207 usage(); 208 } 209 } 210 211 /* First argument is the ccd to configure. */ 212 cp = *argv++; --argc; 213 if ((ccd = resolve_ccdname(cp)) == NULL) { 214 warnx("invalid ccd name: %s", cp); 215 return (1); 216 } 217 218 /* Next argument is the interleave factor. */ 219 cp = *argv++; --argc; 220 errno = 0; /* to check for ERANGE */ 221 ileave = (int)strtol(cp, &cp2, 10); 222 if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) { 223 warnx("invalid interleave factor: %s", cp); 224 free(ccd); 225 return (1); 226 } 227 228 if (noflags == 0) { 229 /* Next argument is the ccd configuration flags. */ 230 cp = *argv++; --argc; 231 if ((flags = flags_to_val(cp)) < 0) { 232 warnx("invalid flags argument: %s", cp); 233 free(ccd); 234 return (1); 235 } 236 } 237 238 /* Next is the list of disks to make the ccd from. */ 239 disks = emalloc(argc * sizeof(char *)); 240 for (ndisks = 0; argc != 0; ++argv, --argc) { 241 if (getfsspecname(buf, sizeof(buf), *argv) == NULL) { 242 warn("%s", *argv); 243 goto error; 244 } 245 246 cp = strdup(buf); 247 if (cp == NULL) { 248 warn("%s", cp); 249 goto error; 250 } 251 252 if ((j = checkdev(cp)) == 0) 253 disks[ndisks++] = cp; 254 else { 255 warnx("%s: %s", cp, strerror(j)); 256 goto error; 257 } 258 } 259 260 /* Fill in the ccio. */ 261 ccio.ccio_disks = disks; 262 ccio.ccio_ndisks = ndisks; 263 ccio.ccio_ileave = ileave; 264 ccio.ccio_flags = flags; 265 266 if (do_io(ccd, CCDIOCSET, &ccio)) 267 goto error; 268 269 if (verbose) { 270 printf("ccd%d: %d components ", ccio.ccio_unit, 271 ccio.ccio_ndisks); 272 for (ui = 0; ui < ccio.ccio_ndisks; ++ui) { 273 if ((cp2 = strrchr(disks[ui], '/')) != NULL) 274 ++cp2; 275 else 276 cp2 = disks[ui]; 277 printf("%c%s%c", 278 ui == 0 ? '(' : ' ', cp2, 279 ui == ccio.ccio_ndisks - 1 ? ')' : ','); 280 } 281 printf(", %ju blocks ", (uintmax_t)ccio.ccio_size); 282 if (ccio.ccio_ileave != 0) 283 printf("interleaved at %d blocks\n", ccio.ccio_ileave); 284 else 285 printf("concatenated\n"); 286 } 287 288 ret = 0; 289 290 error: 291 free(ccd); 292 while (ndisks > 0) 293 free(disks[--ndisks]); 294 free(disks); 295 return (ret); 296 } 297 298 static int 299 do_all(int action) 300 { 301 FILE *f; 302 char *line, *cp, *vp, **argv, **nargv; 303 int argc, rval; 304 size_t len; 305 306 rval = 0; 307 308 (void)setegid(getgid()); 309 if ((f = fopen(ccdconf, "r")) == NULL) { 310 (void)setegid(egid); 311 warn("fopen: %s", ccdconf); 312 return (1); 313 } 314 (void)setegid(egid); 315 316 while ((line = fparseln(f, &len, &lineno, "\\\\#", FPARSELN_UNESCALL)) 317 != NULL) { 318 argc = 0; 319 argv = NULL; 320 if (len == 0) 321 goto end_of_line; 322 323 for (cp = line; cp != NULL; ) { 324 while ((vp = strsep(&cp, "\t ")) != NULL && *vp == '\0') 325 ; 326 if (vp == NULL) 327 continue; 328 329 nargv = erealloc(argv, sizeof(char *) * (argc + 1)); 330 argv = nargv; 331 argc++; 332 argv[argc - 1] = vp; 333 334 /* 335 * If our action is to unconfigure all, then pass 336 * just the first token to do_single() and ignore 337 * the rest. Since this will be encountered on 338 * our first pass through the line, the Right 339 * Thing will happen. 340 */ 341 if (action == CCD_UNCONFIGALL) { 342 if (do_single(argc, argv, action)) 343 rval = 1; 344 goto end_of_line; 345 } 346 } 347 if (argc != 0) 348 if (do_single(argc, argv, action)) 349 rval = 1; 350 351 end_of_line: 352 if (argv != NULL) 353 free(argv); 354 free(line); 355 } 356 357 (void)fclose(f); 358 return (rval); 359 } 360 361 static int 362 checkdev(char *path) 363 { 364 struct stat st; 365 366 if (stat(path, &st) != 0) 367 return (errno); 368 369 if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) 370 return (EINVAL); 371 372 return (0); 373 } 374 375 static int 376 pathtounit(char *path, int *unitp) 377 { 378 struct stat st; 379 380 if (stat(path, &st) != 0) 381 return (errno); 382 383 if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) 384 return (EINVAL); 385 386 *unitp = DISKUNIT(st.st_rdev); 387 388 return (0); 389 } 390 391 static char * 392 resolve_ccdname(char *name) 393 { 394 char c, *path; 395 size_t len; 396 int rawpart; 397 398 if (name[0] == '/' || name[0] == '.') { 399 /* Assume they gave the correct pathname. */ 400 return estrdup(name); 401 } 402 403 len = strlen(name); 404 c = name[len - 1]; 405 406 if (isdigit((unsigned char)c)) { 407 if ((rawpart = getrawpartition()) < 0) 408 return NULL; 409 easprintf(&path, "/dev/%s%c", name, 'a' + rawpart); 410 } else 411 easprintf(&path, "/dev/%s", name); 412 413 return path; 414 } 415 416 static int 417 do_io(char *path, u_long cmd, struct ccd_ioctl *cciop) 418 { 419 int fd; 420 const char *cp; 421 422 if ((fd = open(path, O_RDWR, 0640)) < 0) { 423 warn("open: %s", path); 424 return (1); 425 } 426 427 if (ioctl(fd, cmd, cciop) < 0) { 428 switch (cmd) { 429 case CCDIOCSET: 430 cp = "CCDIOCSET"; 431 break; 432 433 case CCDIOCCLR: 434 cp = "CCDIOCCLR"; 435 break; 436 437 default: 438 cp = "unknown"; 439 } 440 warn("ioctl (%s): %s", cp, path); 441 (void)close(fd); 442 return (1); 443 } 444 445 (void)close(fd); 446 return (0); 447 } 448 449 static void 450 print_ccd_info(int u, struct ccddiskinfo *ccd, char *str) 451 { 452 static int header_printed = 0; 453 454 if (header_printed == 0 && verbose) { 455 printf("# ccd\t\tileave\tflags\t\tsize\tcomponent devices\n"); 456 header_printed = 1; 457 } 458 459 /* Dump out softc information. */ 460 printf("ccd%d\t\t%d\t0x%x\t%ju\t", u, ccd->ccd_ileave, 461 ccd->ccd_flags & CCDF_USERMASK, 462 (uintmax_t)ccd->ccd_size * DEV_BSIZE); 463 464 /* Read component pathname and display component info. */ 465 for (size_t i = 0; i < ccd->ccd_ndisks; ++i) { 466 fputs(str, stdout); 467 fputc((i + 1 < ccd->ccd_ndisks) ? ' ' : '\n', stdout); 468 str += strlen(str) + 1; 469 } 470 fflush(stdout); 471 } 472 473 static int 474 printccdinfo(int u) 475 { 476 struct ccddiskinfo ccd; 477 size_t s = sizeof(ccd); 478 size_t len; 479 const char *str; 480 481 if (sysctlbyname(str = "kern.ccd.info", &ccd, &s, &u, sizeof(u)) 482 == -1) { 483 if (errno == ENOENT) 484 warnx("ccd unit %d not configured", u); 485 else 486 warn("error getting %s for ccd%d", str, u); 487 return 1; 488 } 489 490 if (sysctlbyname(str = "kern.ccd.components", NULL, &len, &u, sizeof(u)) 491 == -1) { 492 warn("Error getting %s for ccd%d", str, u); 493 return 1; 494 } 495 496 char *names; 497 names = emalloc(len); 498 if (sysctlbyname(str = "kern.ccd.components", names, &len, &u, 499 sizeof(u)) == -1) { 500 warn("error getting %s for ccd%d", str, u); 501 free(names); 502 return 1; 503 } 504 print_ccd_info(u, &ccd, names); 505 free(names); 506 return 0; 507 } 508 509 static int 510 dump_ccd(int argc, char **argv, int action) 511 { 512 const char *sys; 513 int errs = 0; 514 if (argc == 0) { 515 int *units; 516 size_t nunits = 0; 517 if (sysctlbyname(sys = "kern.ccd.units", NULL, &nunits, NULL, 0) 518 == -1) { 519 switch (errno) { 520 case ENOENT: 521 warnx("no ccd driver in the kernel"); 522 return 1; 523 case ENOMEM: 524 break; 525 default: 526 err(EXIT_FAILURE, "1 error getting %s", sys); 527 } 528 } 529 530 if (nunits == 0) { 531 warnx("no concatenated disks configured"); 532 return 1; 533 } 534 535 units = emalloc(nunits); 536 537 if (sysctlbyname(sys, units, &nunits, NULL, 0) == -1) 538 err(EXIT_FAILURE, "2 error getting %s", sys); 539 nunits /= sizeof(*units); 540 for (size_t i = 0; i < nunits; i++) 541 errs += printccdinfo(units[i]); 542 free(units); 543 return errs; 544 } 545 546 /* Dump ccd configuration to stdout. */ 547 while (argc) { 548 int i = 0; /* XXX: vax gcc */ 549 int error; 550 char *cp = *argv++; --argc; 551 char *ccd; 552 553 if ((ccd = resolve_ccdname(cp)) == NULL) { 554 warnx("invalid ccd name: %s", cp); 555 errs++; 556 continue; 557 } 558 if ((error = pathtounit(ccd, &i)) != 0) { 559 warn("%s", ccd); 560 free(ccd); 561 errs++; 562 continue; 563 } 564 errs += printccdinfo(i); 565 } 566 return errs; 567 } 568 569 570 static int 571 flags_to_val(char *flags) 572 { 573 char *cp, *tok; 574 int i, tmp, val = ~CCDF_USERMASK; 575 size_t flagslen; 576 577 /* 578 * The most common case is that of NIL flags, so check for 579 * those first. 580 */ 581 if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 || 582 strcmp("0", flags) == 0) 583 return (0); 584 585 flagslen = strlen(flags); 586 587 /* Check for values represented by strings. */ 588 cp = estrdup(flags); 589 tmp = 0; 590 for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) { 591 for (i = 0; flagvaltab[i].fv_flag != NULL; ++i) 592 if (strcmp(tok, flagvaltab[i].fv_flag) == 0) 593 break; 594 if (flagvaltab[i].fv_flag == NULL) { 595 free(cp); 596 goto bad_string; 597 } 598 tmp |= flagvaltab[i].fv_val; 599 } 600 601 /* If we get here, the string was ok. */ 602 free(cp); 603 val = tmp; 604 goto out; 605 606 bad_string: 607 608 /* Check for values represented in hex. */ 609 if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') { 610 errno = 0; /* to check for ERANGE */ 611 val = (int)strtol(&flags[2], &cp, 16); 612 if ((errno == ERANGE) || (*cp != '\0')) 613 return (-1); 614 goto out; 615 } 616 617 /* Check for values represented in decimal. */ 618 errno = 0; /* to check for ERANGE */ 619 val = (int)strtol(flags, &cp, 10); 620 if ((errno == ERANGE) || (*cp != '\0')) 621 return (-1); 622 623 out: 624 return (((val & ~CCDF_USERMASK) == 0) ? val : -1); 625 } 626 627 static void 628 usage(void) 629 { 630 const char *progname = getprogname(); 631 632 fprintf(stderr, "usage: %s [-cv] ccd ileave [flags] dev [...]\n", 633 progname); 634 fprintf(stderr, " %s -C [-v] [-f config_file]\n", progname); 635 fprintf(stderr, " %s -u [-v] ccd [...]\n", progname); 636 fprintf(stderr, " %s -U [-v] [-f config_file]\n", progname); 637 fprintf(stderr, " %s -g [ccd [...]]\n", 638 progname); 639 exit(1); 640 } 641