ccdconfig.c revision 109417
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 36static const char rcsid[] = 37 "$FreeBSD: head/sbin/ccdconfig/ccdconfig.c 109417 2003-01-17 13:23:41Z phk $"; 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 61static int lineno = 0; 62static int verbose = 0; 63static const char *ccdconf = _PATH_CCDCONF; 64 65struct 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 80static int checkdev(char *); 81static int do_io(int, u_long, struct ccd_ioctl *); 82static int do_single(int, char **, int); 83static int do_all(int); 84static int dump_ccd(int, char **); 85static int flags_to_val(char *); 86static void print_ccd_info(struct ccd_s *); 87static int resolve_ccdname(char *); 88static void usage(void); 89 90int 91main(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 164static int 165do_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)) == NULL) { 181 warnx("invalid ccd name: %s", cp); 182 i = 1; 183 continue; 184 } 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)) == NULL) { 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 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 283static int 284do_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 347static int 348checkdev(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 361static int 362resolve_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 375static int 376do_io(int unit, u_long cmd, struct ccd_ioctl *cciop) 377{ 378 int fd; 379 char *cp; 380 char *path; 381 382 asprintf(&path, "%sccd%dc", _PATH_DEV, unit); 383 384 if ((fd = open(path, O_RDWR, 0640)) < 0) { 385 warn("open: %s", path); 386 return (1); 387 } 388 389 if (ioctl(fd, cmd, cciop) < 0) { 390 switch (cmd) { 391 case CCDIOCSET: 392 cp = "CCDIOCSET"; 393 break; 394 395 case CCDIOCCLR: 396 cp = "CCDIOCCLR"; 397 break; 398 399 case CCDCONFINFO: 400 cp = "CCDCONFINFO"; 401 break; 402 403 case CCDCPPINFO: 404 cp = "CCDCPPINFO"; 405 break; 406 407 default: 408 cp = "unknown"; 409 } 410 warn("ioctl (%s): %s", cp, path); 411 return (1); 412 } 413 414 return (0); 415} 416 417static int 418dump_ccd(int argc, char **argv) 419{ 420 char *cp; 421 int i, error, numccd, numconfiged = 0; 422 struct ccdconf conf; 423 int ccd; 424 425 /* 426 * Read the ccd configuration data from the kernel and dump 427 * it to stdout. 428 */ 429 if ((ccd = resolve_ccdname("ccd0")) < 0) { /* XXX */ 430 warnx("invalid ccd name: %s", cp); 431 return (1); 432 } 433 conf.size = 0; 434 if (do_io(ccd, CCDCONFINFO, (struct ccd_ioctl *) &conf)) 435 return (1); 436 if (conf.size == 0) { 437 printf("no concatenated disks configured\n"); 438 return (0); 439 } 440 /* Allocate space for the configuration data. */ 441 conf.buffer = alloca(conf.size); 442 if (conf.buffer == NULL) { 443 warnx("no memory for configuration data"); 444 return (1); 445 } 446 if (do_io(ccd, CCDCONFINFO, (struct ccd_ioctl *) &conf)) 447 return (1); 448 449 numconfiged = conf.size / sizeof(struct ccd_s); 450 451 if (argc == 0) { 452 for (i = 0; i < numconfiged; i++) 453 print_ccd_info(&(conf.buffer[i])); 454 } else { 455 while (argc) { 456 cp = *argv++; --argc; 457 if ((ccd = resolve_ccdname(cp)) < 0) { 458 warnx("invalid ccd name: %s", cp); 459 continue; 460 } 461 error = 1; 462 for (i = 0; i < numconfiged; i++) { 463 if (conf.buffer[i].sc_unit == ccd) { 464 print_ccd_info(&(conf.buffer[i])); 465 error = 0; 466 break; 467 } 468 } 469 if (error) { 470 warnx("ccd%d not configured", numccd); 471 continue; 472 } 473 } 474 } 475 476 return (0); 477} 478 479static void 480print_ccd_info(struct ccd_s *cs) 481{ 482 char *cp; 483 static int header_printed = 0; 484 struct ccdcpps cpps; 485 int ccd; 486 487 /* Print out header if necessary*/ 488 if (header_printed == 0 && verbose) { 489 printf("# ccd\t\tileave\tflags\tcompnent devices\n"); 490 header_printed = 1; 491 } 492 493 /* Dump out softc information. */ 494 printf("ccd%d\t\t%d\t%d\t", cs->sc_unit, cs->sc_ileave, 495 cs->sc_cflags & CCDF_USERMASK); 496 fflush(stdout); 497 498 /* Read in the component info. */ 499 asprintf(&cp, "%s%d", cs->device_stats.device_name, 500 cs->device_stats.unit_number); 501 if (cp == NULL) { 502 printf("\n"); 503 warn("ccd%d: can't allocate memory", 504 cs->sc_unit); 505 return; 506 } 507 508 if ((ccd = resolve_ccdname(cp)) < 0) { 509 printf("\n"); 510 warnx("can't read component info: invalid ccd name: %s", cp); 511 return; 512 } 513 cpps.size = 0; 514 if (do_io(ccd, CCDCPPINFO, (struct ccd_ioctl *) &cpps)) { 515 printf("\n"); 516 warnx("can't read component info"); 517 return; 518 } 519 cpps.buffer = alloca(cpps.size); 520 if (cpps.buffer == NULL) { 521 printf("\n"); 522 warn("ccd%d: can't allocate memory for component info", 523 cs->sc_unit); 524 return; 525 } 526 if (do_io(ccd, CCDCPPINFO, (struct ccd_ioctl *) &cpps)) { 527 printf("\n"); 528 warnx("can't read component info"); 529 return; 530 } 531 532 /* Display component info. */ 533 for (cp = cpps.buffer; cp - cpps.buffer < cpps.size; cp += strlen(cp) + 1) { 534 printf((cp + strlen(cp) + 1) < (cpps.buffer + cpps.size) ? 535 "%s " : "%s\n", cp); 536 fflush(stdout); 537 } 538 return; 539} 540 541static int 542flags_to_val(char *flags) 543{ 544 char *cp, *tok; 545 int i, tmp, val = ~CCDF_USERMASK; 546 size_t flagslen; 547 548 /* 549 * The most common case is that of NIL flags, so check for 550 * those first. 551 */ 552 if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 || 553 strcmp("0", flags) == 0) 554 return (0); 555 556 flagslen = strlen(flags); 557 558 /* Check for values represented by strings. */ 559 if ((cp = strdup(flags)) == NULL) 560 err(1, "no memory to parse flags"); 561 tmp = 0; 562 for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) { 563 for (i = 0; flagvaltab[i].fv_flag != NULL; ++i) 564 if (strcmp(tok, flagvaltab[i].fv_flag) == 0) 565 break; 566 if (flagvaltab[i].fv_flag == NULL) { 567 free(cp); 568 goto bad_string; 569 } 570 tmp |= flagvaltab[i].fv_val; 571 } 572 573 /* If we get here, the string was ok. */ 574 free(cp); 575 val = tmp; 576 goto out; 577 578 bad_string: 579 580 /* Check for values represented in hex. */ 581 if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') { 582 errno = 0; /* to check for ERANGE */ 583 val = (int)strtol(&flags[2], &cp, 16); 584 if ((errno == ERANGE) || (*cp != '\0')) 585 return (-1); 586 goto out; 587 } 588 589 /* Check for values represented in decimal. */ 590 errno = 0; /* to check for ERANGE */ 591 val = (int)strtol(flags, &cp, 10); 592 if ((errno == ERANGE) || (*cp != '\0')) 593 return (-1); 594 595 out: 596 return (((val & ~CCDF_USERMASK) == 0) ? val : -1); 597} 598 599static void 600usage(void) 601{ 602 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n", 603 "usage: ccdconfig [-cv] ccd ileave [flags] dev [...]", 604 " ccdconfig -C [-v] [-f config_file]", 605 " ccdconfig -u [-v] ccd [...]", 606 " ccdconfig -U [-v] [-f config_file]", 607 " ccdconfig -g [ccd [...]]"); 608 exit(1); 609} 610 611/* Local Variables: */ 612/* c-argdecl-indent: 8 */ 613/* c-indent-level: 8 */ 614/* End: */ 615