ccdconfig.c revision 115730
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: head/sbin/ccdconfig/ccdconfig.c 115730 2003-06-02 20:50:59Z phk $"); 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#include <libgeom.h> 54 55#include <sys/devicestat.h> 56#include <sys/ccdvar.h> 57 58#include "pathnames.h" 59 60static int lineno = 0; 61static int verbose = 0; 62static const char *ccdconf = _PATH_CCDCONF; 63 64struct flagval { 65 const char *fv_flag; 66 int fv_val; 67} flagvaltab[] = { 68 { "CCDF_UNIFORM", CCDF_UNIFORM }, 69 { "CCDF_MIRROR", CCDF_MIRROR }, 70 { NULL, 0 }, 71}; 72 73#define CCD_CONFIG 0 /* configure a device */ 74#define CCD_CONFIGALL 1 /* configure all devices */ 75#define CCD_UNCONFIG 2 /* unconfigure a device */ 76#define CCD_UNCONFIGALL 3 /* unconfigure all devices */ 77#define CCD_DUMP 4 /* dump a ccd's configuration */ 78 79static int checkdev(char *); 80static int do_io(int, u_long, struct ccd_ioctl *); 81static int do_single(int, char **, int); 82static int do_all(int); 83static int dump_ccd(int, char **); 84static int flags_to_val(char *); 85static void print_ccd_info(struct ccd_s *); 86static int resolve_ccdname(char *); 87static void usage(void); 88 89int 90main(int argc, char *argv[]) 91{ 92 int ch, options = 0, action = CCD_CONFIG; 93 94 while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) { 95 switch (ch) { 96 case 'c': 97 action = CCD_CONFIG; 98 ++options; 99 break; 100 101 case 'C': 102 action = CCD_CONFIGALL; 103 ++options; 104 break; 105 106 case 'f': 107 ccdconf = optarg; 108 break; 109 110 case 'g': 111 action = CCD_DUMP; 112 break; 113 114 case 'u': 115 action = CCD_UNCONFIG; 116 ++options; 117 break; 118 119 case 'U': 120 action = CCD_UNCONFIGALL; 121 ++options; 122 break; 123 124 case 'v': 125 verbose = 1; 126 break; 127 128 default: 129 usage(); 130 } 131 } 132 argc -= optind; 133 argv += optind; 134 135 if (options > 1) 136 usage(); 137 138 if (modfind("ccd") < 0) { 139 /* Not present in kernel, try loading it */ 140 if (kldload("ccd") < 0 || modfind("ccd") < 0) 141 warn("ccd module not available!"); 142 } 143 144 switch (action) { 145 case CCD_CONFIG: 146 case CCD_UNCONFIG: 147 exit(do_single(argc, argv, action)); 148 /* NOTREACHED */ 149 150 case CCD_CONFIGALL: 151 case CCD_UNCONFIGALL: 152 exit(do_all(action)); 153 /* NOTREACHED */ 154 155 case CCD_DUMP: 156 exit(dump_ccd(argc, argv)); 157 /* NOTREACHED */ 158 } 159 /* NOTREACHED */ 160 return (0); 161} 162 163static int 164do_single(int argc, char **argv, int action) 165{ 166 struct ccd_ioctl ccio; 167 char *cp, *cp2, **disks; 168 int ccd, noflags = 0, i, ileave, flags = 0, j; 169 u_int u; 170 171 bzero(&ccio, 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)) < 0) { 180 warnx("invalid ccd name: %s", cp); 181 i = 1; 182 continue; 183 } 184 ccio.ccio_size = ccd; 185 if (do_io(ccd, CCDIOCCLR, &ccio)) 186 i = 1; 187 else 188 if (verbose) 189 printf("%s unconfigured\n", cp); 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: %d", ccdconf, lineno); 202 return (1); 203 } else 204 usage(); 205 } 206 } 207 208 /* First argument is the ccd to configure. */ 209 cp = *argv++; --argc; 210 if ((ccd = resolve_ccdname(cp)) < 0) { 211 warnx("invalid ccd name: %s", cp); 212 return (1); 213 } 214 215 /* Next argument is the interleave factor. */ 216 cp = *argv++; --argc; 217 errno = 0; /* to check for ERANGE */ 218 ileave = (int)strtol(cp, &cp2, 10); 219 if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) { 220 warnx("invalid interleave factor: %s", cp); 221 return (1); 222 } 223 224 if (noflags == 0) { 225 /* Next argument is the ccd configuration flags. */ 226 cp = *argv++; --argc; 227 if ((flags = flags_to_val(cp)) < 0) { 228 warnx("invalid flags argument: %s", cp); 229 return (1); 230 } 231 } 232 233 /* Next is the list of disks to make the ccd from. */ 234 disks = malloc(argc * sizeof(char *)); 235 if (disks == NULL) { 236 warnx("no memory to configure ccd"); 237 return (1); 238 } 239 for (i = 0; argc != 0; ) { 240 cp = *argv++; --argc; 241 if ((j = checkdev(cp)) == 0) 242 disks[i++] = cp; 243 else { 244 warnx("%s: %s", cp, strerror(j)); 245 return (1); 246 } 247 } 248 249 /* Fill in the ccio. */ 250 ccio.ccio_disks = disks; 251 ccio.ccio_ndisks = i; 252 ccio.ccio_ileave = ileave; 253 ccio.ccio_flags = flags; 254 ccio.ccio_size = ccd; 255 256 if (do_io(ccd, CCDIOCSET, &ccio)) { 257 free(disks); 258 return (1); 259 } 260 261 if (verbose) { 262 printf("ccd%d: %d components ", ccio.ccio_unit, 263 ccio.ccio_ndisks); 264 for (u = 0; u < ccio.ccio_ndisks; ++u) { 265 if ((cp2 = strrchr(disks[u], '/')) != NULL) 266 ++cp2; 267 else 268 cp2 = disks[u]; 269 printf("%c%s%c", 270 u == 0 ? '(' : ' ', cp2, 271 u == ccio.ccio_ndisks - 1 ? ')' : ','); 272 } 273 printf(", %lu blocks ", (u_long)ccio.ccio_size); 274 if (ccio.ccio_ileave != 0) 275 printf("interleaved at %d blocks\n", ccio.ccio_ileave); 276 else 277 printf("concatenated\n"); 278 } 279 280 free(disks); 281 return (0); 282} 283 284static int 285do_all(int action) 286{ 287 FILE *f; 288 char line[_POSIX2_LINE_MAX]; 289 char *cp, **argv; 290 int argc, rval; 291 gid_t egid; 292 293 rval = 0; 294 egid = getegid(); 295 setegid(getgid()); 296 if ((f = fopen(ccdconf, "r")) == NULL) { 297 setegid(egid); 298 warn("fopen: %s", ccdconf); 299 return (1); 300 } 301 setegid(egid); 302 303 while (fgets(line, sizeof(line), f) != NULL) { 304 argc = 0; 305 argv = NULL; 306 ++lineno; 307 if ((cp = strrchr(line, '\n')) != NULL) 308 *cp = '\0'; 309 310 /* Break up the line and pass it's contents to do_single(). */ 311 if (line[0] == '\0') 312 goto end_of_line; 313 for (cp = line; (cp = strtok(cp, " \t")) != NULL; cp = NULL) { 314 if (*cp == '#') 315 break; 316 if ((argv = realloc(argv, 317 sizeof(char *) * ++argc)) == NULL) { 318 warnx("no memory to configure ccds"); 319 return (1); 320 } 321 argv[argc - 1] = cp; 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 } 343 344 (void)fclose(f); 345 return (rval); 346} 347 348static int 349checkdev(char *path) 350{ 351 struct stat st; 352 353 if (stat(path, &st) != 0) 354 return (errno); 355 356 if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) 357 return (EINVAL); 358 359 return (0); 360} 361 362static int 363resolve_ccdname(char *name) 364{ 365 366 if (!strncmp(name, _PATH_DEV, strlen(_PATH_DEV))) 367 name += strlen(_PATH_DEV); 368 if (strncmp(name, "ccd", 3)) 369 return -1; 370 name += 3; 371 if (!isdigit(*name)) 372 return -1; 373 return (strtoul(name, NULL, 10)); 374} 375 376static int 377do_io(int unit, u_long cmd, struct ccd_ioctl *cciop) 378{ 379 int fd; 380 char *cp; 381 char *path; 382 383 asprintf(&path, "%s%s", _PATH_DEV, _PATH_CCDCTL); 384 385 if ((fd = open(path, O_RDWR, 0640)) < 0) { 386 asprintf(&path, "%sccd%dc", _PATH_DEV, unit); 387 if ((fd = open(path, O_RDWR, 0640)) < 0) { 388 warn("open: %s", path); 389 return (1); 390 } 391 fprintf(stderr, 392 "***WARNING***: Kernel older than ccdconfig(8), please upgrade it.\n"); 393 fprintf(stderr, 394 "***WARNING***: Continuing in 30 seconds\n"); 395 sleep(30); 396 } 397 398 if (ioctl(fd, cmd, cciop) < 0) { 399 switch (cmd) { 400 case CCDIOCSET: 401 cp = "CCDIOCSET"; 402 break; 403 404 case CCDIOCCLR: 405 cp = "CCDIOCCLR"; 406 break; 407 408 case CCDCONFINFO: 409 cp = "CCDCONFINFO"; 410 break; 411 412 case CCDCPPINFO: 413 cp = "CCDCPPINFO"; 414 break; 415 416 default: 417 cp = "unknown"; 418 } 419 warn("ioctl (%s): %s", cp, path); 420 return (1); 421 } 422 423 return (0); 424} 425 426static int 427dump_ccd(int argc, char **argv) 428{ 429 char *cp; 430 char const *errstr; 431 int i, error, numccd, numconfiged = 0; 432 struct ccdconf conf; 433 int ccd; 434 struct gctl_req *grq; 435 436 grq = gctl_get_handle(); 437 gctl_ro_param(grq, "verb", -1, "list"); 438 gctl_ro_param(grq, "class", -1, "CCD"); 439 cp = malloc(65536); 440 gctl_rw_param(grq, "output", 65536, cp); 441 if (verbose) 442 gctl_ro_param(grq, "verbose", -1, "yes"); 443 errstr = gctl_issue(grq); 444 if (errstr == NULL) { 445 printf("%s", cp); 446 return (0); 447 } else { 448 warnx("%s\nor possibly kernel and ccdconfig out of sync", 449 errstr); 450 } 451 452 /* 453 * Read the ccd configuration data from the kernel and dump 454 * it to stdout. 455 */ 456 if ((ccd = resolve_ccdname("ccd0")) < 0) { /* XXX */ 457 warnx("invalid ccd name: %s", cp); 458 return (1); 459 } 460 conf.size = 0; 461 if (do_io(ccd, CCDCONFINFO, (struct ccd_ioctl *) &conf)) 462 return (1); 463 if (conf.size == 0) { 464 printf("no concatenated disks configured\n"); 465 return (0); 466 } 467 /* Allocate space for the configuration data. */ 468 conf.buffer = alloca(conf.size); 469 if (conf.buffer == NULL) { 470 warnx("no memory for configuration data"); 471 return (1); 472 } 473 if (do_io(ccd, CCDCONFINFO, (struct ccd_ioctl *) &conf)) 474 return (1); 475 476 numconfiged = conf.size / sizeof(struct ccd_s); 477 478 if (argc == 0) { 479 for (i = 0; i < numconfiged; i++) 480 print_ccd_info(&(conf.buffer[i])); 481 } else { 482 while (argc) { 483 cp = *argv++; --argc; 484 if ((ccd = resolve_ccdname(cp)) < 0) { 485 warnx("invalid ccd name: %s", cp); 486 continue; 487 } 488 error = 1; 489 for (i = 0; i < numconfiged; i++) { 490 if (conf.buffer[i].sc_unit == ccd) { 491 print_ccd_info(&(conf.buffer[i])); 492 error = 0; 493 break; 494 } 495 } 496 if (error) { 497 warnx("ccd%d not configured", numccd); 498 continue; 499 } 500 } 501 } 502 503 return (0); 504} 505 506static void 507print_ccd_info(struct ccd_s *cs) 508{ 509 char *cp; 510 static int header_printed = 0; 511 struct ccdcpps cpps; 512 int ccd; 513 514 /* Print out header if necessary*/ 515 if (header_printed == 0 && verbose) { 516 printf("# ccd\t\tileave\tflags\tcompnent devices\n"); 517 header_printed = 1; 518 } 519 520 /* Dump out softc information. */ 521 printf("ccd%d\t\t%d\t%d\t", cs->sc_unit, cs->sc_ileave, 522 cs->sc_cflags & CCDF_USERMASK); 523 fflush(stdout); 524 525 /* Read in the component info. */ 526 asprintf(&cp, "ccd%d", cs->sc_unit); 527 if (cp == NULL) { 528 printf("\n"); 529 warn("ccd%d: can't allocate memory", 530 cs->sc_unit); 531 return; 532 } 533 534 if ((ccd = resolve_ccdname(cp)) < 0) { 535 printf("\n"); 536 warnx("can't read component info: invalid ccd name: %s", cp); 537 return; 538 } 539 cpps.size = 1024; 540 cpps.buffer = alloca(cpps.size); 541 memcpy(cpps.buffer, &ccd, sizeof ccd); 542 if (do_io(ccd, CCDCPPINFO, (struct ccd_ioctl *) &cpps)) { 543 printf("\n"); 544 warnx("can't read component info"); 545 return; 546 } 547 548 /* Display component info. */ 549 for (cp = cpps.buffer; *cp && cp - cpps.buffer < cpps.size; cp += strlen(cp) + 1) { 550 printf((cp + strlen(cp) + 1) < (cpps.buffer + cpps.size) ? 551 "%s " : "%s", cp); 552 } 553 printf("\n"); 554 return; 555} 556 557static int 558flags_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 if ((cp = strdup(flags)) == NULL) 576 err(1, "no memory to parse flags"); 577 tmp = 0; 578 for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) { 579 for (i = 0; flagvaltab[i].fv_flag != NULL; ++i) 580 if (strcmp(tok, flagvaltab[i].fv_flag) == 0) 581 break; 582 if (flagvaltab[i].fv_flag == NULL) { 583 free(cp); 584 goto bad_string; 585 } 586 tmp |= flagvaltab[i].fv_val; 587 } 588 589 /* If we get here, the string was ok. */ 590 free(cp); 591 val = tmp; 592 goto out; 593 594 bad_string: 595 596 /* Check for values represented in hex. */ 597 if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') { 598 errno = 0; /* to check for ERANGE */ 599 val = (int)strtol(&flags[2], &cp, 16); 600 if ((errno == ERANGE) || (*cp != '\0')) 601 return (-1); 602 goto out; 603 } 604 605 /* Check for values represented in decimal. */ 606 errno = 0; /* to check for ERANGE */ 607 val = (int)strtol(flags, &cp, 10); 608 if ((errno == ERANGE) || (*cp != '\0')) 609 return (-1); 610 611 out: 612 return (((val & ~CCDF_USERMASK) == 0) ? val : -1); 613} 614 615static void 616usage(void) 617{ 618 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n", 619 "usage: ccdconfig [-cv] ccd ileave [flags] dev [...]", 620 " ccdconfig -C [-v] [-f config_file]", 621 " ccdconfig -u [-v] ccd [...]", 622 " ccdconfig -U [-v] [-f config_file]", 623 " ccdconfig -g [ccd [...]]"); 624 exit(1); 625} 626 627/* Local Variables: */ 628/* c-argdecl-indent: 8 */ 629/* c-indent-level: 8 */ 630/* End: */ 631