1 /* $NetBSD: ccdconfig.c,v 1.2.2.1 1995/11/11 02:43:35 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 1995 Jason R. Thorpe. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed for the NetBSD Project 18 * by Jason R. Thorpe. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 #include <sys/param.h> 39 #include <sys/linker.h> 40 #include <sys/disklabel.h> 41 #include <sys/stat.h> 42 #include <sys/module.h> 43 #include <ctype.h> 44 #include <err.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <limits.h> 48 #include <paths.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 54 #include <sys/devicestat.h> 55 #include <sys/ccdvar.h> 56 57 #include "pathnames.h" 58 59 static int lineno = 0; 60 static int verbose = 0; 61 static const char *ccdconf = _PATH_CCDCONF; 62 63 struct flagval { 64 const char *fv_flag; 65 int fv_val; 66 } flagvaltab[] = { 67 { "CCDF_UNIFORM", CCDF_UNIFORM }, 68 { "CCDF_MIRROR", CCDF_MIRROR }, 69 { NULL, 0 }, 70 }; 71 72 #define CCD_CONFIG 0 /* configure a device */ 73 #define CCD_CONFIGALL 1 /* configure all devices */ 74 #define CCD_UNCONFIG 2 /* unconfigure a device */ 75 #define CCD_UNCONFIGALL 3 /* unconfigure all devices */ 76 #define CCD_DUMP 4 /* dump a ccd's configuration */ 77 78 static int checkdev(char *); 79 static int do_io(int, u_long, struct ccd_ioctl *); 80 static int do_single(int, char **, int); 81 static int do_all(int); 82 static int dump_ccd(int, char **); 83 static int flags_to_val(char *); 84 static void print_ccd_info(struct ccd_s *); 85 static int resolve_ccdname(char *); 86 static void usage(void); 87 88 int 89 main(int argc, char *argv[]) 90 { 91 int ch, options = 0, action = CCD_CONFIG; 92 93 while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) { 94 switch (ch) { 95 case 'c': 96 action = CCD_CONFIG; 97 ++options; 98 break; 99 100 case 'C': 101 action = CCD_CONFIGALL; 102 ++options; 103 break; 104 105 case 'f': 106 ccdconf = optarg; 107 break; 108 109 case 'g': 110 action = CCD_DUMP; 111 break; 112 113 case 'u': 114 action = CCD_UNCONFIG; 115 ++options; 116 break; 117 118 case 'U': 119 action = CCD_UNCONFIGALL; 120 ++options; 121 break; 122 123 case 'v': 124 verbose = 1; 125 break; 126 127 default: 128 usage(); 129 } 130 } 131 argc -= optind; 132 argv += optind; 133 134 if (options > 1) 135 usage(); 136 137 if (modfind("ccd") < 0) { 138 /* Not present in kernel, try loading it */ 139 if (kldload("ccd") < 0 || modfind("ccd") < 0) 140 warn("ccd module not available!"); 141 } 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 exit(dump_ccd(argc, argv)); 156 /* NOTREACHED */ 157 } 158 /* NOTREACHED */ 159 return (0); 160 } 161 162 static int 163 do_single(int argc, char **argv, int action) 164 { 165 struct ccd_ioctl ccio; 166 char *cp, *cp2, **disks; 167 int ccd, noflags = 0, i, ileave, flags = 0, j; 168 u_int u; 169 170 bzero(&ccio, sizeof(ccio)); 171 172 /* 173 * If unconfiguring, all arguments are treated as ccds. 174 */ 175 if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) { 176 for (i = 0; argc != 0; ) { 177 cp = *argv++; --argc; 178 if ((ccd = resolve_ccdname(cp)) < 0) { 179 warnx("invalid ccd name: %s", cp); 180 i = 1; 181 continue; 182 } 183 ccio.ccio_size = ccd; 184 if (do_io(ccd, CCDIOCCLR, &ccio)) 185 i = 1; 186 else 187 if (verbose) 188 printf("%s unconfigured\n", cp); 189 } 190 return (i); 191 } 192 193 /* Make sure there are enough arguments. */ 194 if (argc < 4) { 195 if (argc == 3) { 196 /* Assume that no flags are specified. */ 197 noflags = 1; 198 } else { 199 if (action == CCD_CONFIGALL) { 200 warnx("%s: bad line: %d", ccdconf, lineno); 201 return (1); 202 } else 203 usage(); 204 } 205 } 206 207 /* First argument is the ccd to configure. */ 208 cp = *argv++; --argc; 209 if ((ccd = resolve_ccdname(cp)) < 0) { 210 warnx("invalid ccd name: %s", cp); 211 return (1); 212 } 213 214 /* Next argument is the interleave factor. */ 215 cp = *argv++; --argc; 216 errno = 0; /* to check for ERANGE */ 217 ileave = (int)strtol(cp, &cp2, 10); 218 if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) { 219 warnx("invalid interleave factor: %s", cp); 220 return (1); 221 } 222 223 if (noflags == 0) { 224 /* Next argument is the ccd configuration flags. */ 225 cp = *argv++; --argc; 226 if ((flags = flags_to_val(cp)) < 0) { 227 warnx("invalid flags argument: %s", cp); 228 return (1); 229 } 230 } 231 232 /* Next is the list of disks to make the ccd from. */ 233 disks = malloc(argc * sizeof(char *)); 234 if (disks == NULL) { 235 warnx("no memory to configure ccd"); 236 return (1); 237 } 238 for (i = 0; argc != 0; ) { 239 cp = *argv++; --argc; 240 if ((j = checkdev(cp)) == 0) 241 disks[i++] = cp; 242 else { 243 warnx("%s: %s", cp, strerror(j)); 244 return (1); 245 } 246 } 247 248 /* Fill in the ccio. */ 249 ccio.ccio_disks = disks; 250 ccio.ccio_ndisks = i; 251 ccio.ccio_ileave = ileave; 252 ccio.ccio_flags = flags; 253 ccio.ccio_size = ccd; 254 255 if (do_io(ccd, CCDIOCSET, &ccio)) { 256 free(disks); 257 return (1); 258 } 259 260 if (verbose) { 261 printf("ccd%d: %d components ", ccio.ccio_unit, 262 ccio.ccio_ndisks); 263 for (u = 0; u < ccio.ccio_ndisks; ++u) { 264 if ((cp2 = strrchr(disks[u], '/')) != NULL) 265 ++cp2; 266 else 267 cp2 = disks[u]; 268 printf("%c%s%c", 269 u == 0 ? '(' : ' ', cp2, 270 u == ccio.ccio_ndisks - 1 ? ')' : ','); 271 } 272 printf(", %lu blocks ", (u_long)ccio.ccio_size); 273 if (ccio.ccio_ileave != 0) 274 printf("interleaved at %d blocks\n", ccio.ccio_ileave); 275 else 276 printf("concatenated\n"); 277 } 278 279 free(disks); 280 return (0); 281 } 282 283 static int 284 do_all(int action) 285 { 286 FILE *f; 287 char line[_POSIX2_LINE_MAX]; 288 char *cp, **argv; 289 int argc, rval; 290 gid_t egid; 291 292 rval = 0; 293 egid = getegid(); 294 setegid(getgid()); 295 if ((f = fopen(ccdconf, "r")) == NULL) { 296 setegid(egid); 297 warn("fopen: %s", ccdconf); 298 return (1); 299 } 300 setegid(egid); 301 302 while (fgets(line, sizeof(line), f) != NULL) { 303 argc = 0; 304 argv = NULL; 305 ++lineno; 306 if ((cp = strrchr(line, '\n')) != NULL) 307 *cp = '\0'; 308 309 /* Break up the line and pass it's contents to do_single(). */ 310 if (line[0] == '\0') 311 goto end_of_line; 312 for (cp = line; (cp = strtok(cp, " \t")) != NULL; cp = NULL) { 313 if (*cp == '#') 314 break; 315 if ((argv = realloc(argv, 316 sizeof(char *) * ++argc)) == NULL) { 317 warnx("no memory to configure ccds"); 318 return (1); 319 } 320 argv[argc - 1] = cp; 321 /* 322 * If our action is to unconfigure all, then pass 323 * just the first token to do_single() and ignore 324 * the rest. Since this will be encountered on 325 * our first pass through the line, the Right 326 * Thing will happen. 327 */ 328 if (action == CCD_UNCONFIGALL) { 329 if (do_single(argc, argv, action)) 330 rval = 1; 331 goto end_of_line; 332 } 333 } 334 if (argc != 0) 335 if (do_single(argc, argv, action)) 336 rval = 1; 337 338 end_of_line: 339 if (argv != NULL) 340 free(argv); 341 } 342 343 (void)fclose(f); 344 return (rval); 345 } 346 347 static int 348 checkdev(char *path) 349 { 350 struct stat st; 351 352 if (stat(path, &st) != 0) 353 return (errno); 354 355 if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) 356 return (EINVAL); 357 358 return (0); 359 } 360 361 static int 362 resolve_ccdname(char *name) 363 { 364 365 if (!strncmp(name, _PATH_DEV, strlen(_PATH_DEV))) 366 name += strlen(_PATH_DEV); 367 if (strncmp(name, "ccd", 3)) 368 return -1; 369 name += 3; 370 if (!isdigit(*name)) 371 return -1; 372 return (strtoul(name, NULL, 10)); 373 } 374 375 static int 376 do_io(int unit, u_long cmd, struct ccd_ioctl *cciop) 377 { 378 int fd; 379 char *cp; 380 char *path; 381 382 asprintf(&path, "%s%s", _PATH_DEV, _PATH_CCDCTL); 383 384 if ((fd = open(path, O_RDWR, 0640)) < 0) { 385 asprintf(&path, "%sccd%dc", _PATH_DEV, unit); 386 if ((fd = open(path, O_RDWR, 0640)) < 0) { 387 warn("open: %s", path); 388 return (1); 389 } 390 fprintf(stderr, 391 "***WARNING***: Kernel older than ccdconfig(8), please upgrade it.\n"); 392 fprintf(stderr, 393 "***WARNING***: Continuing in 30 seconds\n"); 394 sleep(30); 395 } 396 397 if (ioctl(fd, cmd, cciop) < 0) { 398 switch (cmd) { 399 case CCDIOCSET: 400 cp = "CCDIOCSET"; 401 break; 402 403 case CCDIOCCLR: 404 cp = "CCDIOCCLR"; 405 break; 406 407 case CCDCONFINFO: 408 cp = "CCDCONFINFO"; 409 break; 410 411 case CCDCPPINFO: 412 cp = "CCDCPPINFO"; 413 break; 414 415 default: 416 cp = "unknown"; 417 } 418 warn("ioctl (%s): %s", cp, path); 419 return (1); 420 } 421 422 return (0); 423 } 424 425 static int 426 dump_ccd(int argc, char **argv) 427 { 428 char *cp; 429 int i, error, numccd, numconfiged = 0; 430 struct ccdconf conf; 431 int ccd; 432 433 /* 434 * Read the ccd configuration data from the kernel and dump 435 * it to stdout. 436 */ 437 if ((ccd = resolve_ccdname("ccd0")) < 0) { /* XXX */ 438 warnx("invalid ccd name: %s", cp); 439 return (1); 440 } 441 conf.size = 0; 442 if (do_io(ccd, CCDCONFINFO, (struct ccd_ioctl *) &conf)) 443 return (1); 444 if (conf.size == 0) { 445 printf("no concatenated disks configured\n"); 446 return (0); 447 } 448 /* Allocate space for the configuration data. */ 449 conf.buffer = alloca(conf.size); 450 if (conf.buffer == NULL) { 451 warnx("no memory for configuration data"); 452 return (1); 453 } 454 if (do_io(ccd, CCDCONFINFO, (struct ccd_ioctl *) &conf)) 455 return (1); 456 457 numconfiged = conf.size / sizeof(struct ccd_s); 458 459 if (argc == 0) { 460 for (i = 0; i < numconfiged; i++) 461 print_ccd_info(&(conf.buffer[i])); 462 } else { 463 while (argc) { 464 cp = *argv++; --argc; 465 if ((ccd = resolve_ccdname(cp)) < 0) { 466 warnx("invalid ccd name: %s", cp); 467 continue; 468 } 469 error = 1; 470 for (i = 0; i < numconfiged; i++) { 471 if (conf.buffer[i].sc_unit == ccd) { 472 print_ccd_info(&(conf.buffer[i])); 473 error = 0; 474 break; 475 } 476 } 477 if (error) { 478 warnx("ccd%d not configured", numccd); 479 continue; 480 } 481 } 482 } 483 484 return (0); 485 } 486 487 static void 488 print_ccd_info(struct ccd_s *cs) 489 { 490 char *cp; 491 static int header_printed = 0; 492 struct ccdcpps cpps; 493 int ccd; 494 495 /* Print out header if necessary*/ 496 if (header_printed == 0 && verbose) { 497 printf("# ccd\t\tileave\tflags\tcompnent devices\n"); 498 header_printed = 1; 499 } 500 501 /* Dump out softc information. */ 502 printf("ccd%d\t\t%d\t%d\t", cs->sc_unit, cs->sc_ileave, 503 cs->sc_cflags & CCDF_USERMASK); 504 fflush(stdout); 505 506 /* Read in the component info. */ 507 asprintf(&cp, "ccd%d", cs->sc_unit); 508 if (cp == NULL) { 509 printf("\n"); 510 warn("ccd%d: can't allocate memory", 511 cs->sc_unit); 512 return; 513 } 514 515 if ((ccd = resolve_ccdname(cp)) < 0) { 516 printf("\n"); 517 warnx("can't read component info: invalid ccd name: %s", cp); 518 return; 519 } 520 cpps.size = 1024; 521 cpps.buffer = alloca(cpps.size); 522 memcpy(cpps.buffer, &ccd, sizeof ccd); 523 if (do_io(ccd, CCDCPPINFO, (struct ccd_ioctl *) &cpps)) { 524 printf("\n"); 525 warnx("can't read component info"); 526 return; 527 } 528 529 /* Display component info. */ 530 for (cp = cpps.buffer; *cp && cp - cpps.buffer < cpps.size; cp += strlen(cp) + 1) { 531 printf((cp + strlen(cp) + 1) < (cpps.buffer + cpps.size) ? 532 "%s " : "%s", cp); 533 } 534 printf("\n"); 535 return; 536 } 537 538 static int 539 flags_to_val(char *flags) 540 { 541 char *cp, *tok; 542 int i, tmp, val = ~CCDF_USERMASK; 543 size_t flagslen; 544 545 /* 546 * The most common case is that of NIL flags, so check for 547 * those first. 548 */ 549 if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 || 550 strcmp("0", flags) == 0) 551 return (0); 552 553 flagslen = strlen(flags); 554 555 /* Check for values represented by strings. */ 556 if ((cp = strdup(flags)) == NULL) 557 err(1, "no memory to parse flags"); 558 tmp = 0; 559 for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) { 560 for (i = 0; flagvaltab[i].fv_flag != NULL; ++i) 561 if (strcmp(tok, flagvaltab[i].fv_flag) == 0) 562 break; 563 if (flagvaltab[i].fv_flag == NULL) { 564 free(cp); 565 goto bad_string; 566 } 567 tmp |= flagvaltab[i].fv_val; 568 } 569 570 /* If we get here, the string was ok. */ 571 free(cp); 572 val = tmp; 573 goto out; 574 575 bad_string: 576 577 /* Check for values represented in hex. */ 578 if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') { 579 errno = 0; /* to check for ERANGE */ 580 val = (int)strtol(&flags[2], &cp, 16); 581 if ((errno == ERANGE) || (*cp != '\0')) 582 return (-1); 583 goto out; 584 } 585 586 /* Check for values represented in decimal. */ 587 errno = 0; /* to check for ERANGE */ 588 val = (int)strtol(flags, &cp, 10); 589 if ((errno == ERANGE) || (*cp != '\0')) 590 return (-1); 591 592 out: 593 return (((val & ~CCDF_USERMASK) == 0) ? val : -1); 594 } 595 596 static void 597 usage(void) 598 { 599 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n", 600 "usage: ccdconfig [-cv] ccd ileave [flags] dev [...]", 601 " ccdconfig -C [-v] [-f config_file]", 602 " ccdconfig -u [-v] ccd [...]", 603 " ccdconfig -U [-v] [-f config_file]", 604 " ccdconfig -g [ccd [...]]"); 605 exit(1); 606 } 607 608 /* Local Variables: */ 609 /* c-argdecl-indent: 8 */ 610 /* c-indent-level: 8 */ 611 /* End: */ 612