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 #ifndef lint 36 static const char rcsid[] = 37 "$FreeBSD$"; 38 #endif /* not lint */ 39 40 #include <sys/param.h> 41 #include <sys/linker.h> 42 #include <sys/disklabel.h> 43 #include <sys/stat.h> 44 #include <sys/module.h> 45 #include <ctype.h> 46 #include <err.h> 47 #include <errno.h> 48 #include <fcntl.h> 49 #include <limits.h> 50 #include <paths.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <unistd.h> 55 56 #include <sys/devicestat.h> 57 #include <sys/ccdvar.h> 58 59 #include "pathnames.h" 60 61 static int lineno = 0; 62 static int verbose = 0; 63 static const char *ccdconf = _PATH_CCDCONF; 64 65 struct flagval { 66 const char *fv_flag; 67 int fv_val; 68 } flagvaltab[] = { 69 { "CCDF_UNIFORM", CCDF_UNIFORM }, 70 { "CCDF_MIRROR", CCDF_MIRROR }, 71 { NULL, 0 }, 72 }; 73 74 #define CCD_CONFIG 0 /* configure a device */ 75 #define CCD_CONFIGALL 1 /* configure all devices */ 76 #define CCD_UNCONFIG 2 /* unconfigure a device */ 77 #define CCD_UNCONFIGALL 3 /* unconfigure all devices */ 78 #define CCD_DUMP 4 /* dump a ccd's configuration */ 79 80 static int checkdev(char *); 81 static int do_io(int, u_long, struct ccd_ioctl *); 82 static int do_single(int, char **, int); 83 static int do_all(int); 84 static int dump_ccd(int, char **); 85 static int flags_to_val(char *); 86 static void print_ccd_info(struct ccd_s *); 87 static int resolve_ccdname(char *); 88 static void usage(void); 89 90 int 91 main(int argc, char *argv[]) 92 { 93 int ch, options = 0, action = CCD_CONFIG; 94 95 while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) { 96 switch (ch) { 97 case 'c': 98 action = CCD_CONFIG; 99 ++options; 100 break; 101 102 case 'C': 103 action = CCD_CONFIGALL; 104 ++options; 105 break; 106 107 case 'f': 108 ccdconf = optarg; 109 break; 110 111 case 'g': 112 action = CCD_DUMP; 113 break; 114 115 case 'u': 116 action = CCD_UNCONFIG; 117 ++options; 118 break; 119 120 case 'U': 121 action = CCD_UNCONFIGALL; 122 ++options; 123 break; 124 125 case 'v': 126 verbose = 1; 127 break; 128 129 default: 130 usage(); 131 } 132 } 133 argc -= optind; 134 argv += optind; 135 136 if (options > 1) 137 usage(); 138 139 if (modfind("ccd") < 0) { 140 /* Not present in kernel, try loading it */ 141 if (kldload("ccd") < 0 || modfind("ccd") < 0) 142 warn("ccd module not available!"); 143 } 144 145 switch (action) { 146 case CCD_CONFIG: 147 case CCD_UNCONFIG: 148 exit(do_single(argc, argv, action)); 149 /* NOTREACHED */ 150 151 case CCD_CONFIGALL: 152 case CCD_UNCONFIGALL: 153 exit(do_all(action)); 154 /* NOTREACHED */ 155 156 case CCD_DUMP: 157 exit(dump_ccd(argc, argv)); 158 /* NOTREACHED */ 159 } 160 /* NOTREACHED */ 161 return (0); 162 } 163 164 static int 165 do_single(int argc, char **argv, int action) 166 { 167 struct ccd_ioctl ccio; 168 char *cp, *cp2, **disks; 169 int ccd, noflags = 0, i, ileave, flags = 0, j; 170 u_int u; 171 172 bzero(&ccio, sizeof(ccio)); 173 174 /* 175 * If unconfiguring, all arguments are treated as ccds. 176 */ 177 if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) { 178 for (i = 0; argc != 0; ) { 179 cp = *argv++; --argc; 180 if ((ccd = resolve_ccdname(cp)) < 0) { 181 warnx("invalid ccd name: %s", cp); 182 i = 1; 183 continue; 184 } 185 ccio.ccio_size = ccd; 186 if (do_io(ccd, CCDIOCCLR, &ccio)) 187 i = 1; 188 else 189 if (verbose) 190 printf("%s unconfigured\n", cp); 191 } 192 return (i); 193 } 194 195 /* Make sure there are enough arguments. */ 196 if (argc < 4) { 197 if (argc == 3) { 198 /* Assume that no flags are specified. */ 199 noflags = 1; 200 } else { 201 if (action == CCD_CONFIGALL) { 202 warnx("%s: bad line: %d", ccdconf, 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)) < 0) { 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 return (1); 223 } 224 225 if (noflags == 0) { 226 /* Next argument is the ccd configuration flags. */ 227 cp = *argv++; --argc; 228 if ((flags = flags_to_val(cp)) < 0) { 229 warnx("invalid flags argument: %s", cp); 230 return (1); 231 } 232 } 233 234 /* Next is the list of disks to make the ccd from. */ 235 disks = malloc(argc * sizeof(char *)); 236 if (disks == NULL) { 237 warnx("no memory to configure ccd"); 238 return (1); 239 } 240 for (i = 0; argc != 0; ) { 241 cp = *argv++; --argc; 242 if ((j = checkdev(cp)) == 0) 243 disks[i++] = cp; 244 else { 245 warnx("%s: %s", cp, strerror(j)); 246 return (1); 247 } 248 } 249 250 /* Fill in the ccio. */ 251 ccio.ccio_disks = disks; 252 ccio.ccio_ndisks = i; 253 ccio.ccio_ileave = ileave; 254 ccio.ccio_flags = flags; 255 ccio.ccio_size = ccd; 256 257 if (do_io(ccd, CCDIOCSET, &ccio)) { 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 (u = 0; u < ccio.ccio_ndisks; ++u) { 266 if ((cp2 = strrchr(disks[u], '/')) != NULL) 267 ++cp2; 268 else 269 cp2 = disks[u]; 270 printf("%c%s%c", 271 u == 0 ? '(' : ' ', cp2, 272 u == ccio.ccio_ndisks - 1 ? ')' : ','); 273 } 274 printf(", %lu blocks ", (u_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(disks); 282 return (0); 283 } 284 285 static int 286 do_all(int action) 287 { 288 FILE *f; 289 char line[_POSIX2_LINE_MAX]; 290 char *cp, **argv; 291 int argc, rval; 292 gid_t egid; 293 294 rval = 0; 295 egid = getegid(); 296 setegid(getgid()); 297 if ((f = fopen(ccdconf, "r")) == NULL) { 298 setegid(egid); 299 warn("fopen: %s", ccdconf); 300 return (1); 301 } 302 setegid(egid); 303 304 while (fgets(line, sizeof(line), f) != NULL) { 305 argc = 0; 306 argv = NULL; 307 ++lineno; 308 if ((cp = strrchr(line, '\n')) != NULL) 309 *cp = '\0'; 310 311 /* Break up the line and pass it's contents to do_single(). */ 312 if (line[0] == '\0') 313 goto end_of_line; 314 for (cp = line; (cp = strtok(cp, " \t")) != NULL; cp = NULL) { 315 if (*cp == '#') 316 break; 317 if ((argv = realloc(argv, 318 sizeof(char *) * ++argc)) == NULL) { 319 warnx("no memory to configure ccds"); 320 return (1); 321 } 322 argv[argc - 1] = cp; 323 /* 324 * If our action is to unconfigure all, then pass 325 * just the first token to do_single() and ignore 326 * the rest. Since this will be encountered on 327 * our first pass through the line, the Right 328 * Thing will happen. 329 */ 330 if (action == CCD_UNCONFIGALL) { 331 if (do_single(argc, argv, action)) 332 rval = 1; 333 goto end_of_line; 334 } 335 } 336 if (argc != 0) 337 if (do_single(argc, argv, action)) 338 rval = 1; 339 340 end_of_line: 341 if (argv != NULL) 342 free(argv); 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 resolve_ccdname(char *name) 365 { 366 367 if (!strncmp(name, _PATH_DEV, strlen(_PATH_DEV))) 368 name += strlen(_PATH_DEV); 369 if (strncmp(name, "ccd", 3)) 370 return -1; 371 name += 3; 372 if (!isdigit(*name)) 373 return -1; 374 return (strtoul(name, NULL, 10)); 375 } 376 377 static int 378 do_io(int unit, u_long cmd, struct ccd_ioctl *cciop) 379 { 380 int fd; 381 char *cp; 382 char *path; 383 384 asprintf(&path, "%s%s", _PATH_DEV, _PATH_CCDCTL); 385 386 if ((fd = open(path, O_RDWR, 0640)) < 0) { 387 asprintf(&path, "%sccd%dc", _PATH_DEV, unit); 388 if ((fd = open(path, O_RDWR, 0640)) < 0) { 389 warn("open: %s", path); 390 return (1); 391 } 392 fprintf(stderr, 393 "***WARNING***: Kernel older than ccdconfig(8), please upgrade it.\n"); 394 fprintf(stderr, 395 "***WARNING***: Continuing in 30 seconds\n"); 396 sleep(30); 397 } 398 399 if (ioctl(fd, cmd, cciop) < 0) { 400 switch (cmd) { 401 case CCDIOCSET: 402 cp = "CCDIOCSET"; 403 break; 404 405 case CCDIOCCLR: 406 cp = "CCDIOCCLR"; 407 break; 408 409 case CCDCONFINFO: 410 cp = "CCDCONFINFO"; 411 break; 412 413 case CCDCPPINFO: 414 cp = "CCDCPPINFO"; 415 break; 416 417 default: 418 cp = "unknown"; 419 } 420 warn("ioctl (%s): %s", cp, path); 421 return (1); 422 } 423 424 return (0); 425 } 426 427 static int 428 dump_ccd(int argc, char **argv) 429 { 430 char *cp; 431 int i, error, numccd, numconfiged = 0; 432 struct ccdconf conf; 433 int ccd; 434 435 /* 436 * Read the ccd configuration data from the kernel and dump 437 * it to stdout. 438 */ 439 if ((ccd = resolve_ccdname("ccd0")) < 0) { /* XXX */ 440 warnx("invalid ccd name: %s", cp); 441 return (1); 442 } 443 conf.size = 0; 444 if (do_io(ccd, CCDCONFINFO, (struct ccd_ioctl *) &conf)) 445 return (1); 446 if (conf.size == 0) { 447 printf("no concatenated disks configured\n"); 448 return (0); 449 } 450 /* Allocate space for the configuration data. */ 451 conf.buffer = alloca(conf.size); 452 if (conf.buffer == NULL) { 453 warnx("no memory for configuration data"); 454 return (1); 455 } 456 if (do_io(ccd, CCDCONFINFO, (struct ccd_ioctl *) &conf)) 457 return (1); 458 459 numconfiged = conf.size / sizeof(struct ccd_s); 460 461 if (argc == 0) { 462 for (i = 0; i < numconfiged; i++) 463 print_ccd_info(&(conf.buffer[i])); 464 } else { 465 while (argc) { 466 cp = *argv++; --argc; 467 if ((ccd = resolve_ccdname(cp)) < 0) { 468 warnx("invalid ccd name: %s", cp); 469 continue; 470 } 471 error = 1; 472 for (i = 0; i < numconfiged; i++) { 473 if (conf.buffer[i].sc_unit == ccd) { 474 print_ccd_info(&(conf.buffer[i])); 475 error = 0; 476 break; 477 } 478 } 479 if (error) { 480 warnx("ccd%d not configured", numccd); 481 continue; 482 } 483 } 484 } 485 486 return (0); 487 } 488 489 static void 490 print_ccd_info(struct ccd_s *cs) 491 { 492 char *cp; 493 static int header_printed = 0; 494 struct ccdcpps cpps; 495 int ccd; 496 497 /* Print out header if necessary*/ 498 if (header_printed == 0 && verbose) { 499 printf("# ccd\t\tileave\tflags\tcompnent devices\n"); 500 header_printed = 1; 501 } 502 503 /* Dump out softc information. */ 504 printf("ccd%d\t\t%d\t%d\t", cs->sc_unit, cs->sc_ileave, 505 cs->sc_cflags & CCDF_USERMASK); 506 fflush(stdout); 507 508 /* Read in the component info. */ 509 asprintf(&cp, "ccd%d", cs->sc_unit); 510 if (cp == NULL) { 511 printf("\n"); 512 warn("ccd%d: can't allocate memory", 513 cs->sc_unit); 514 return; 515 } 516 517 if ((ccd = resolve_ccdname(cp)) < 0) { 518 printf("\n"); 519 warnx("can't read component info: invalid ccd name: %s", cp); 520 return; 521 } 522 cpps.size = 1024; 523 cpps.buffer = alloca(cpps.size); 524 memcpy(cpps.buffer, &ccd, sizeof ccd); 525 if (do_io(ccd, CCDCPPINFO, (struct ccd_ioctl *) &cpps)) { 526 printf("\n"); 527 warnx("can't read component info"); 528 return; 529 } 530 531 /* Display component info. */ 532 for (cp = cpps.buffer; *cp && cp - cpps.buffer < cpps.size; cp += strlen(cp) + 1) { 533 printf((cp + strlen(cp) + 1) < (cpps.buffer + cpps.size) ? 534 "%s " : "%s", cp); 535 } 536 printf("\n"); 537 return; 538 } 539 540 static int 541 flags_to_val(char *flags) 542 { 543 char *cp, *tok; 544 int i, tmp, val = ~CCDF_USERMASK; 545 size_t flagslen; 546 547 /* 548 * The most common case is that of NIL flags, so check for 549 * those first. 550 */ 551 if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 || 552 strcmp("0", flags) == 0) 553 return (0); 554 555 flagslen = strlen(flags); 556 557 /* Check for values represented by strings. */ 558 if ((cp = strdup(flags)) == NULL) 559 err(1, "no memory to parse flags"); 560 tmp = 0; 561 for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) { 562 for (i = 0; flagvaltab[i].fv_flag != NULL; ++i) 563 if (strcmp(tok, flagvaltab[i].fv_flag) == 0) 564 break; 565 if (flagvaltab[i].fv_flag == NULL) { 566 free(cp); 567 goto bad_string; 568 } 569 tmp |= flagvaltab[i].fv_val; 570 } 571 572 /* If we get here, the string was ok. */ 573 free(cp); 574 val = tmp; 575 goto out; 576 577 bad_string: 578 579 /* Check for values represented in hex. */ 580 if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') { 581 errno = 0; /* to check for ERANGE */ 582 val = (int)strtol(&flags[2], &cp, 16); 583 if ((errno == ERANGE) || (*cp != '\0')) 584 return (-1); 585 goto out; 586 } 587 588 /* Check for values represented in decimal. */ 589 errno = 0; /* to check for ERANGE */ 590 val = (int)strtol(flags, &cp, 10); 591 if ((errno == ERANGE) || (*cp != '\0')) 592 return (-1); 593 594 out: 595 return (((val & ~CCDF_USERMASK) == 0) ? val : -1); 596 } 597 598 static void 599 usage(void) 600 { 601 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n", 602 "usage: ccdconfig [-cv] ccd ileave [flags] dev [...]", 603 " ccdconfig -C [-v] [-f config_file]", 604 " ccdconfig -u [-v] ccd [...]", 605 " ccdconfig -U [-v] [-f config_file]", 606 " ccdconfig -g [ccd [...]]"); 607 exit(1); 608 } 609 610 /* Local Variables: */ 611 /* c-argdecl-indent: 8 */ 612 /* c-indent-level: 8 */ 613 /* End: */ 614