1 /* $NetBSD: ccdconfig.c,v 1.58 2020/10/06 18:47:07 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.58 2020/10/06 18:47:07 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("strdup failed"); 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 *path, *buf; 395 const char *p; 396 char c; 397 size_t len; 398 int rawpart; 399 400 if (name[0] == '/' || name[0] == '.') { 401 /* Assume they gave the correct pathname. */ 402 path = estrdup(name); 403 } else { 404 405 len = strlen(name); 406 c = name[len - 1]; 407 408 if (isdigit((unsigned char)c)) { 409 if ((rawpart = getrawpartition()) < 0) 410 return NULL; 411 easprintf(&path, "/dev/%s%c", name, 'a' + rawpart); 412 } else 413 easprintf(&path, "/dev/%s", name); 414 } 415 416 /* 417 * Convert to raw device if possible. 418 */ 419 buf = emalloc(MAXPATHLEN); 420 p = getdiskrawname(buf, MAXPATHLEN, path); 421 if (p) { 422 free(path); 423 path = estrdup(p); 424 } 425 free(buf); 426 427 return path; 428 } 429 430 static int 431 do_io(char *path, u_long cmd, struct ccd_ioctl *cciop) 432 { 433 int fd; 434 const char *cp; 435 436 if ((fd = open(path, O_RDWR, 0640)) < 0) { 437 warn("open: %s", path); 438 return (1); 439 } 440 441 if (ioctl(fd, cmd, cciop) < 0) { 442 switch (cmd) { 443 case CCDIOCSET: 444 cp = "CCDIOCSET"; 445 break; 446 447 case CCDIOCCLR: 448 cp = "CCDIOCCLR"; 449 break; 450 451 default: 452 cp = "unknown"; 453 } 454 warn("ioctl (%s): %s", cp, path); 455 (void)close(fd); 456 return (1); 457 } 458 459 (void)close(fd); 460 return (0); 461 } 462 463 static void 464 print_ccd_info(int u, struct ccddiskinfo *ccd, char *str) 465 { 466 static int header_printed = 0; 467 468 if (header_printed == 0 && verbose) { 469 printf("# ccd\t\tileave\tflags\t\tsize\tcomponent devices\n"); 470 header_printed = 1; 471 } 472 473 /* Dump out softc information. */ 474 printf("ccd%d\t\t%d\t0x%x\t%ju\t", u, ccd->ccd_ileave, 475 ccd->ccd_flags & CCDF_USERMASK, 476 (uintmax_t)ccd->ccd_size * DEV_BSIZE); 477 478 /* Read component pathname and display component info. */ 479 for (size_t i = 0; i < ccd->ccd_ndisks; ++i) { 480 fputs(str, stdout); 481 fputc((i + 1 < ccd->ccd_ndisks) ? ' ' : '\n', stdout); 482 str += strlen(str) + 1; 483 } 484 fflush(stdout); 485 } 486 487 static int 488 printccdinfo(int u) 489 { 490 struct ccddiskinfo ccd; 491 size_t s = sizeof(ccd); 492 size_t len; 493 const char *str; 494 495 if (sysctlbyname(str = "kern.ccd.info", &ccd, &s, &u, sizeof(u)) 496 == -1) { 497 if (errno == ENOENT) 498 warnx("ccd unit %d not configured", u); 499 else 500 warn("error getting %s for ccd%d", str, u); 501 return 1; 502 } 503 504 if (sysctlbyname(str = "kern.ccd.components", NULL, &len, &u, sizeof(u)) 505 == -1) { 506 warn("Error getting %s for ccd%d", str, u); 507 return 1; 508 } 509 510 char *names; 511 names = emalloc(len); 512 if (sysctlbyname(str = "kern.ccd.components", names, &len, &u, 513 sizeof(u)) == -1) { 514 warn("error getting %s for ccd%d", str, u); 515 free(names); 516 return 1; 517 } 518 print_ccd_info(u, &ccd, names); 519 free(names); 520 return 0; 521 } 522 523 static int 524 dump_ccd(int argc, char **argv, int action) 525 { 526 const char *sys; 527 int errs = 0; 528 if (argc == 0) { 529 int *units; 530 size_t nunits = 0; 531 if (sysctlbyname(sys = "kern.ccd.units", NULL, &nunits, NULL, 0) 532 == -1) { 533 switch (errno) { 534 case ENOENT: 535 warnx("no ccd driver in the kernel"); 536 return 1; 537 case ENOMEM: 538 break; 539 default: 540 err(EXIT_FAILURE, "1 error getting %s", sys); 541 } 542 } 543 544 if (nunits == 0) { 545 warnx("no concatenated disks configured"); 546 return 1; 547 } 548 549 units = emalloc(nunits); 550 551 if (sysctlbyname(sys, units, &nunits, NULL, 0) == -1) 552 err(EXIT_FAILURE, "2 error getting %s", sys); 553 nunits /= sizeof(*units); 554 for (size_t i = 0; i < nunits; i++) 555 errs += printccdinfo(units[i]); 556 free(units); 557 return errs; 558 } 559 560 /* Dump ccd configuration to stdout. */ 561 while (argc) { 562 int i = 0; /* XXX: vax gcc */ 563 int error; 564 char *cp = *argv++; --argc; 565 char *ccd; 566 567 if ((ccd = resolve_ccdname(cp)) == NULL) { 568 warnx("invalid ccd name: %s", cp); 569 errs++; 570 continue; 571 } 572 if ((error = pathtounit(ccd, &i)) != 0) { 573 warn("%s", ccd); 574 free(ccd); 575 errs++; 576 continue; 577 } 578 errs += printccdinfo(i); 579 free(ccd); 580 } 581 return errs; 582 } 583 584 585 static int 586 flags_to_val(char *flags) 587 { 588 char *cp, *tok; 589 int i, tmp, val = ~CCDF_USERMASK; 590 size_t flagslen; 591 592 /* 593 * The most common case is that of NIL flags, so check for 594 * those first. 595 */ 596 if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 || 597 strcmp("0", flags) == 0) 598 return (0); 599 600 flagslen = strlen(flags); 601 602 /* Check for values represented by strings. */ 603 cp = estrdup(flags); 604 tmp = 0; 605 for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) { 606 for (i = 0; flagvaltab[i].fv_flag != NULL; ++i) 607 if (strcmp(tok, flagvaltab[i].fv_flag) == 0) 608 break; 609 if (flagvaltab[i].fv_flag == NULL) { 610 free(cp); 611 goto bad_string; 612 } 613 tmp |= flagvaltab[i].fv_val; 614 } 615 616 /* If we get here, the string was ok. */ 617 free(cp); 618 val = tmp; 619 goto out; 620 621 bad_string: 622 623 /* Check for values represented in hex. */ 624 if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') { 625 errno = 0; /* to check for ERANGE */ 626 val = (int)strtol(&flags[2], &cp, 16); 627 if ((errno == ERANGE) || (*cp != '\0')) 628 return (-1); 629 goto out; 630 } 631 632 /* Check for values represented in decimal. */ 633 errno = 0; /* to check for ERANGE */ 634 val = (int)strtol(flags, &cp, 10); 635 if ((errno == ERANGE) || (*cp != '\0')) 636 return (-1); 637 638 out: 639 return (((val & ~CCDF_USERMASK) == 0) ? val : -1); 640 } 641 642 static void 643 usage(void) 644 { 645 const char *progname = getprogname(); 646 647 fprintf(stderr, "usage: %s [-cv] ccd ileave [flags] dev [...]\n", 648 progname); 649 fprintf(stderr, " %s -C [-v] [-f config_file]\n", progname); 650 fprintf(stderr, " %s -u [-v] ccd [...]\n", progname); 651 fprintf(stderr, " %s -U [-v] [-f config_file]\n", progname); 652 fprintf(stderr, " %s -g [ccd [...]]\n", 653 progname); 654 exit(1); 655 } 656