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