ccdconfig.c revision 92539
1139804Simp/* $NetBSD: ccdconfig.c,v 1.2.2.1 1995/11/11 02:43:35 thorpej Exp $ */ 265534Salfred 3143427Srwatson/* 465534Salfred * Copyright (c) 1995 Jason R. Thorpe. 561837Salfred * All rights reserved. 661837Salfred * 761837Salfred * Redistribution and use in source and binary forms, with or without 861837Salfred * modification, are permitted provided that the following conditions 961837Salfred * are met: 1061837Salfred * 1. Redistributions of source code must retain the above copyright 1161837Salfred * notice, this list of conditions and the following disclaimer. 1261837Salfred * 2. Redistributions in binary form must reproduce the above copyright 1361837Salfred * notice, this list of conditions and the following disclaimer in the 1461837Salfred * documentation and/or other materials provided with the distribution. 1561837Salfred * 3. All advertising materials mentioning features or use of this software 1661837Salfred * must display the following acknowledgement: 1761837Salfred * This product includes software developed for the NetBSD Project 1861837Salfred * by Jason R. Thorpe. 1961837Salfred * 4. The name of the author may not be used to endorse or promote products 2061837Salfred * derived from this software without specific prior written permission. 2161837Salfred * 2261837Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2361837Salfred * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2461837Salfred * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2561837Salfred * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2661837Salfred * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 2761837Salfred * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2861837Salfred * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29116182Sobrien * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30116182Sobrien * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31116182Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3261837Salfred * SUCH DAMAGE. 3361837Salfred */ 3477598Sjesper 3561837Salfred#ifndef lint 3661837Salfredstatic const char rcsid[] = 3761837Salfred "$FreeBSD: head/sbin/ccdconfig/ccdconfig.c 92539 2002-03-18 05:00:52Z imp $"; 3861837Salfred#endif /* not lint */ 39129920Srwatson 4061837Salfred#include <sys/param.h> 4161837Salfred#include <sys/linker.h> 42129876Sphk#include <sys/disklabel.h> 43129920Srwatson#include <sys/stat.h> 4461837Salfred#include <sys/module.h> 4565534Salfred#include <ctype.h> 4661837Salfred#include <err.h> 4761837Salfred#include <errno.h> 4861837Salfred#include <fcntl.h> 4961837Salfred#include <limits.h> 50129920Srwatson#include <paths.h> 51129920Srwatson#include <stdio.h> 52129920Srwatson#include <stdlib.h> 53129920Srwatson#include <string.h> 54129920Srwatson#include <unistd.h> 55129920Srwatson 5661837Salfred#include <sys/devicestat.h> 5761837Salfred#include <sys/ccdvar.h> 5861837Salfred 5961837Salfred#include "pathnames.h" 6061837Salfred 6165534Salfredstatic int lineno = 0; 6265534Salfredstatic int verbose = 0; 6365534Salfredstatic char *ccdconf = _PATH_CCDCONF; 6465534Salfred 6565534Salfredstruct flagval { 6665534Salfred char *fv_flag; 6765534Salfred int fv_val; 6861837Salfred} flagvaltab[] = { 69142056Srwatson { "CCDF_SWAP", CCDF_SWAP }, 70142056Srwatson { "CCDF_UNIFORM", CCDF_UNIFORM }, 71142056Srwatson { "CCDF_MIRROR", CCDF_MIRROR }, 7261837Salfred { "CCDF_PARITY", CCDF_PARITY }, 7361837Salfred { NULL, 0 }, 7461837Salfred}; 7561837Salfred 7661837Salfred#define CCD_CONFIG 0 /* configure a device */ 7761837Salfred#define CCD_CONFIGALL 1 /* configure all devices */ 78129920Srwatson#define CCD_UNCONFIG 2 /* unconfigure a device */ 7961837Salfred#define CCD_UNCONFIGALL 3 /* unconfigure all devices */ 8061837Salfred#define CCD_DUMP 4 /* dump a ccd's configuration */ 8161837Salfred 82129920Srwatsonstatic int checkdev(char *); 8361837Salfredstatic int do_io(char *, u_long, struct ccd_ioctl *); 8461837Salfredstatic int do_single(int, char **, int); 8561837Salfredstatic int do_all(int); 86129920Srwatsonstatic int dump_ccd(int, char **); 8761837Salfredstatic int getmaxpartitions(void); 8861837Salfredstatic int getrawpartition(void); 8961837Salfredstatic int flags_to_val(char *); 9061837Salfredstatic void print_ccd_info(struct ccd_s *); 9161837Salfredstatic char *resolve_ccdname(char *); 9261837Salfredstatic void usage(void); 9361837Salfred 94129920Srwatsonint 9561837Salfredmain(int argc, char *argv[]) 9661837Salfred{ 9761837Salfred int ch, options = 0, action = CCD_CONFIG; 9861837Salfred 9961837Salfred while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) { 10061837Salfred switch (ch) { 10161837Salfred case 'c': 10261837Salfred action = CCD_CONFIG; 10361837Salfred ++options; 10461837Salfred break; 10561837Salfred 10661837Salfred case 'C': 10761837Salfred action = CCD_CONFIGALL; 10861837Salfred ++options; 10961837Salfred break; 11061837Salfred 11161837Salfred case 'f': 11261837Salfred ccdconf = optarg; 11361837Salfred break; 11461837Salfred 11561837Salfred case 'g': 116129920Srwatson action = CCD_DUMP; 11761837Salfred break; 11861837Salfred 119129920Srwatson case 'u': 120129920Srwatson action = CCD_UNCONFIG; 12161837Salfred ++options; 122129920Srwatson break; 12361837Salfred 12461837Salfred case 'U': 12561837Salfred action = CCD_UNCONFIGALL; 12661837Salfred ++options; 12761837Salfred break; 12861837Salfred 12961837Salfred case 'v': 130142060Srwatson verbose = 1; 13161837Salfred break; 13261837Salfred 13361837Salfred default: 134142056Srwatson usage(); 135142056Srwatson } 13661837Salfred } 13761837Salfred argc -= optind; 13861837Salfred argv += optind; 13961837Salfred 14061837Salfred if (options > 1) 14163645Salfred usage(); 142142056Srwatson 143142056Srwatson if (modfind("ccd") < 0) { 144142056Srwatson /* Not present in kernel, try loading it */ 145142056Srwatson if (kldload("ccd") < 0 || modfind("ccd") < 0) 14663645Salfred warn("ccd module not available!"); 14765534Salfred } 14865534Salfred 14965534Salfred switch (action) { 15065534Salfred case CCD_CONFIG: 15161837Salfred case CCD_UNCONFIG: 15261837Salfred exit(do_single(argc, argv, action)); 15361837Salfred /* NOTREACHED */ 15461837Salfred 15561837Salfred case CCD_CONFIGALL: 15661837Salfred case CCD_UNCONFIGALL: 15761837Salfred exit(do_all(action)); 15861837Salfred /* NOTREACHED */ 15961837Salfred 16061837Salfred case CCD_DUMP: 16161837Salfred exit(dump_ccd(argc, argv)); 16261837Salfred /* NOTREACHED */ 16361837Salfred } 164142058Srwatson /* NOTREACHED */ 165142058Srwatson return (0); 166143463Srwatson} 167143463Srwatson 168143463Srwatsonstatic int 169143463Srwatsondo_single(int argc, char **argv, int action) 170143463Srwatson{ 171143463Srwatson struct ccd_ioctl ccio; 172143463Srwatson char *ccd, *cp, *cp2, **disks; 173143463Srwatson int noflags = 0, i, ileave, flags = 0, j; 174143463Srwatson 175143463Srwatson bzero(&ccio, sizeof(ccio)); 176143463Srwatson 177143463Srwatson /* 178143463Srwatson * If unconfiguring, all arguments are treated as ccds. 179147300Smaxim */ 180147300Smaxim if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) { 181143463Srwatson for (i = 0; argc != 0; ) { 182147300Smaxim cp = *argv++; --argc; 183143463Srwatson if ((ccd = resolve_ccdname(cp)) == NULL) { 184143463Srwatson warnx("invalid ccd name: %s", cp); 185143463Srwatson i = 1; 186143463Srwatson continue; 187143463Srwatson } 188143463Srwatson if (do_io(ccd, CCDIOCCLR, &ccio)) 189143463Srwatson i = 1; 190143463Srwatson else 191143463Srwatson if (verbose) 192143463Srwatson printf("%s unconfigured\n", cp); 193143463Srwatson } 194143463Srwatson return (i); 195142060Srwatson } 196142058Srwatson 197142060Srwatson /* Make sure there are enough arguments. */ 198142060Srwatson if (argc < 4) { 199142060Srwatson if (argc == 3) { 200142060Srwatson /* Assume that no flags are specified. */ 201142058Srwatson noflags = 1; 202142058Srwatson } else { 203143427Srwatson if (action == CCD_CONFIGALL) { 204142058Srwatson warnx("%s: bad line: %d", ccdconf, lineno); 205147300Smaxim return (1); 206143427Srwatson } else 207143427Srwatson usage(); 208143427Srwatson } 209143427Srwatson } 210143427Srwatson 211142058Srwatson /* First argument is the ccd to configure. */ 212142058Srwatson cp = *argv++; --argc; 213142058Srwatson if ((ccd = resolve_ccdname(cp)) == NULL) { 214142058Srwatson warnx("invalid ccd name: %s", cp); 215142058Srwatson return (1); 216142058Srwatson } 217143427Srwatson 218142058Srwatson /* Next argument is the interleave factor. */ 219142058Srwatson cp = *argv++; --argc; 220142058Srwatson errno = 0; /* to check for ERANGE */ 221142058Srwatson ileave = (int)strtol(cp, &cp2, 10); 222142058Srwatson if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) { 223142058Srwatson warnx("invalid interleave factor: %s", cp); 224142058Srwatson return (1); 225142058Srwatson } 226143427Srwatson 227143427Srwatson if (noflags == 0) { 228143461Srwatson /* Next argument is the ccd configuration flags. */ 229143461Srwatson cp = *argv++; --argc; 230143427Srwatson if ((flags = flags_to_val(cp)) < 0) { 231142058Srwatson warnx("invalid flags argument: %s", cp); 232142058Srwatson return (1); 233142058Srwatson } 234142058Srwatson } 235142058Srwatson 236142058Srwatson /* Next is the list of disks to make the ccd from. */ 237142058Srwatson disks = malloc(argc * sizeof(char *)); 238142058Srwatson if (disks == NULL) { 239142058Srwatson warnx("no memory to configure ccd"); 240142058Srwatson return (1); 241142058Srwatson } 242142058Srwatson for (i = 0; argc != 0; ) { 243142058Srwatson cp = *argv++; --argc; 244142058Srwatson if ((j = checkdev(cp)) == 0) 245142058Srwatson disks[i++] = cp; 246143461Srwatson else { 247143461Srwatson warnx("%s: %s", cp, strerror(j)); 248143461Srwatson return (1); 249143461Srwatson } 250142058Srwatson } 251142058Srwatson 252142058Srwatson /* Fill in the ccio. */ 253142058Srwatson ccio.ccio_disks = disks; 254142058Srwatson ccio.ccio_ndisks = i; 255142058Srwatson ccio.ccio_ileave = ileave; 256142058Srwatson ccio.ccio_flags = flags; 257142058Srwatson 258142058Srwatson if (do_io(ccd, CCDIOCSET, &ccio)) { 259142058Srwatson free(disks); 260143461Srwatson return (1); 261143461Srwatson } 262143461Srwatson 263143461Srwatson if (verbose) { 264142058Srwatson printf("ccd%d: %d components ", ccio.ccio_unit, 265143461Srwatson ccio.ccio_ndisks); 266143461Srwatson for (i = 0; i < ccio.ccio_ndisks; ++i) { 267142058Srwatson if ((cp2 = strrchr(disks[i], '/')) != NULL) 268142058Srwatson ++cp2; 269142058Srwatson else 270143461Srwatson cp2 = disks[i]; 271142058Srwatson printf("%c%s%c", 272143461Srwatson i == 0 ? '(' : ' ', cp2, 273143461Srwatson i == ccio.ccio_ndisks - 1 ? ')' : ','); 274143461Srwatson } 275142058Srwatson printf(", %lu blocks ", (u_long)ccio.ccio_size); 276142058Srwatson if (ccio.ccio_ileave != 0) 277142058Srwatson printf("interleaved at %d blocks\n", ccio.ccio_ileave); 278142058Srwatson else 279142058Srwatson printf("concatenated\n"); 280142058Srwatson } 281142058Srwatson 282142058Srwatson free(disks); 283142058Srwatson return (0); 284142058Srwatson} 285142058Srwatson 286142058Srwatsonstatic int 287142058Srwatsondo_all(int action) 288142058Srwatson{ 289142058Srwatson FILE *f; 290142058Srwatson char line[_POSIX2_LINE_MAX]; 291142058Srwatson char *cp, **argv; 292142058Srwatson int argc, rval; 293142058Srwatson gid_t egid; 294142058Srwatson 295142058Srwatson rval = 0; 296142058Srwatson egid = getegid(); 297142058Srwatson setegid(getgid()); 298142058Srwatson if ((f = fopen(ccdconf, "r")) == NULL) { 299 setegid(egid); 300 warn("fopen: %s", ccdconf); 301 return (1); 302 } 303 setegid(egid); 304 305 while (fgets(line, sizeof(line), f) != NULL) { 306 argc = 0; 307 argv = NULL; 308 ++lineno; 309 if ((cp = strrchr(line, '\n')) != NULL) 310 *cp = '\0'; 311 312 /* Break up the line and pass it's contents to do_single(). */ 313 if (line[0] == '\0') 314 goto end_of_line; 315 for (cp = line; (cp = strtok(cp, " \t")) != NULL; cp = NULL) { 316 if (*cp == '#') 317 break; 318 if ((argv = realloc(argv, 319 sizeof(char *) * ++argc)) == NULL) { 320 warnx("no memory to configure ccds"); 321 return (1); 322 } 323 argv[argc - 1] = cp; 324 /* 325 * If our action is to unconfigure all, then pass 326 * just the first token to do_single() and ignore 327 * the rest. Since this will be encountered on 328 * our first pass through the line, the Right 329 * Thing will happen. 330 */ 331 if (action == CCD_UNCONFIGALL) { 332 if (do_single(argc, argv, action)) 333 rval = 1; 334 goto end_of_line; 335 } 336 } 337 if (argc != 0) 338 if (do_single(argc, argv, action)) 339 rval = 1; 340 341 end_of_line: 342 if (argv != NULL) 343 free(argv); 344 } 345 346 (void)fclose(f); 347 return (rval); 348} 349 350static int 351checkdev(char *path) 352{ 353 struct stat st; 354 355 if (stat(path, &st) != 0) 356 return (errno); 357 358 if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) 359 return (EINVAL); 360 361 return (0); 362} 363 364static int 365pathtounit(char *path, int *unitp) 366{ 367 struct stat st; 368 int maxpartitions; 369 370 if (stat(path, &st) != 0) 371 return (errno); 372 373 if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) 374 return (EINVAL); 375 376 if ((maxpartitions = getmaxpartitions()) < 0) 377 return (errno); 378 379 *unitp = minor(st.st_rdev) / maxpartitions; 380 381 return (0); 382} 383 384static char * 385resolve_ccdname(char *name) 386{ 387 char c, *path; 388 size_t len, newlen; 389 int rawpart; 390 391 if (name[0] == '/' || name[0] == '.') { 392 /* Assume they gave the correct pathname. */ 393 return (strdup(name)); 394 } 395 396 len = strlen(name); 397 c = name[len - 1]; 398 399 newlen = len + 8; 400 if ((path = malloc(newlen)) == NULL) 401 return (NULL); 402 bzero(path, newlen); 403 404 if (isdigit(c)) { 405 if ((rawpart = getrawpartition()) < 0) { 406 free(path); 407 return (NULL); 408 } 409 (void)sprintf(path, "%s%s%c", _PATH_DEV, name, 'a' + rawpart); 410 } else 411 (void)sprintf(path, "%s%s", _PATH_DEV, name); 412 413 return (path); 414} 415 416static int 417do_io(char *path, u_long cmd, struct ccd_ioctl *cciop) 418{ 419 int fd; 420 char *cp; 421 422 if ((fd = open(path, O_RDWR, 0640)) < 0) { 423 warn("open: %s", path); 424 return (1); 425 } 426 427 if (ioctl(fd, cmd, cciop) < 0) { 428 switch (cmd) { 429 case CCDIOCSET: 430 cp = "CCDIOCSET"; 431 break; 432 433 case CCDIOCCLR: 434 cp = "CCDIOCCLR"; 435 break; 436 437 case CCDCONFINFO: 438 cp = "CCDCONFINFO"; 439 break; 440 441 case CCDCPPINFO: 442 cp = "CCDCPPINFO"; 443 break; 444 445 default: 446 cp = "unknown"; 447 } 448 warn("ioctl (%s): %s", cp, path); 449 return (1); 450 } 451 452 return (0); 453} 454 455static int 456dump_ccd(int argc, char **argv) 457{ 458 char *ccd, *cp; 459 int i, error, numccd, numconfiged = 0; 460 struct ccdconf conf; 461 462 /* 463 * Read the ccd configuration data from the kernel and dump 464 * it to stdout. 465 */ 466 if ((ccd = resolve_ccdname("ccd0")) == NULL) { /* XXX */ 467 warnx("invalid ccd name: %s", cp); 468 return (1); 469 } 470 conf.size = 0; 471 if (do_io(ccd, CCDCONFINFO, (struct ccd_ioctl *) &conf)) 472 return (1); 473 if (conf.size == 0) { 474 printf("no concatenated disks configured\n"); 475 return (0); 476 } 477 /* Allocate space for the configuration data. */ 478 conf.buffer = alloca(conf.size); 479 if (conf.buffer == NULL) { 480 warnx("no memory for configuration data"); 481 return (1); 482 } 483 if (do_io(ccd, CCDCONFINFO, (struct ccd_ioctl *) &conf)) 484 return (1); 485 486 numconfiged = conf.size / sizeof(struct ccd_s); 487 488 if (argc == 0) { 489 for (i = 0; i < numconfiged; i++) 490 print_ccd_info(&(conf.buffer[i])); 491 } else { 492 while (argc) { 493 cp = *argv++; --argc; 494 if ((ccd = resolve_ccdname(cp)) == NULL) { 495 warnx("invalid ccd name: %s", cp); 496 continue; 497 } 498 if ((error = pathtounit(ccd, &numccd)) != 0) { 499 warnx("%s: %s", ccd, strerror(error)); 500 continue; 501 } 502 error = 1; 503 for (i = 0; i < numconfiged; i++) { 504 if (conf.buffer[i].sc_unit == numccd) { 505 print_ccd_info(&(conf.buffer[i])); 506 error = 0; 507 break; 508 } 509 } 510 if (error) { 511 warnx("ccd%d not configured", numccd); 512 continue; 513 } 514 } 515 } 516 517 return (0); 518} 519 520static void 521print_ccd_info(struct ccd_s *cs) 522{ 523 char *cp, *ccd; 524 static int header_printed = 0; 525 struct ccdcpps cpps; 526 527 /* Print out header if necessary*/ 528 if (header_printed == 0 && verbose) { 529 printf("# ccd\t\tileave\tflags\tcompnent devices\n"); 530 header_printed = 1; 531 } 532 533 /* Dump out softc information. */ 534 printf("ccd%d\t\t%d\t%d\t", cs->sc_unit, cs->sc_ileave, 535 cs->sc_cflags & CCDF_USERMASK); 536 fflush(stdout); 537 538 /* Read in the component info. */ 539 asprintf(&cp, "%s%d", cs->device_stats.device_name, 540 cs->device_stats.unit_number); 541 if (cp == NULL) { 542 printf("\n"); 543 warn("ccd%d: can't allocate memory", 544 cs->sc_unit); 545 return; 546 } 547 548 if ((ccd = resolve_ccdname(cp)) == NULL) { 549 printf("\n"); 550 warnx("can't read component info: invalid ccd name: %s", cp); 551 return; 552 } 553 cpps.size = 0; 554 if (do_io(ccd, CCDCPPINFO, (struct ccd_ioctl *) &cpps)) { 555 printf("\n"); 556 warnx("can't read component info"); 557 return; 558 } 559 cpps.buffer = alloca(cpps.size); 560 if (cpps.buffer == NULL) { 561 printf("\n"); 562 warn("ccd%d: can't allocate memory for component info", 563 cs->sc_unit); 564 return; 565 } 566 if (do_io(ccd, CCDCPPINFO, (struct ccd_ioctl *) &cpps)) { 567 printf("\n"); 568 warnx("can't read component info"); 569 return; 570 } 571 572 /* Display component info. */ 573 for (cp = cpps.buffer; cp - cpps.buffer < cpps.size; cp += strlen(cp) + 1) { 574 printf((cp + strlen(cp) + 1) < (cpps.buffer + cpps.size) ? 575 "%s " : "%s\n", cp); 576 fflush(stdout); 577 } 578 return; 579} 580 581static int 582getmaxpartitions(void) 583{ 584 return (MAXPARTITIONS); 585} 586 587static int 588getrawpartition(void) 589{ 590 return (RAW_PART); 591} 592 593static int 594flags_to_val(char *flags) 595{ 596 char *cp, *tok; 597 int i, tmp, val = ~CCDF_USERMASK; 598 size_t flagslen; 599 600 /* 601 * The most common case is that of NIL flags, so check for 602 * those first. 603 */ 604 if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 || 605 strcmp("0", flags) == 0) 606 return (0); 607 608 flagslen = strlen(flags); 609 610 /* Check for values represented by strings. */ 611 if ((cp = strdup(flags)) == NULL) 612 err(1, "no memory to parse flags"); 613 tmp = 0; 614 for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) { 615 for (i = 0; flagvaltab[i].fv_flag != NULL; ++i) 616 if (strcmp(tok, flagvaltab[i].fv_flag) == 0) 617 break; 618 if (flagvaltab[i].fv_flag == NULL) { 619 free(cp); 620 goto bad_string; 621 } 622 tmp |= flagvaltab[i].fv_val; 623 } 624 625 /* If we get here, the string was ok. */ 626 free(cp); 627 val = tmp; 628 goto out; 629 630 bad_string: 631 632 /* Check for values represented in hex. */ 633 if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') { 634 errno = 0; /* to check for ERANGE */ 635 val = (int)strtol(&flags[2], &cp, 16); 636 if ((errno == ERANGE) || (*cp != '\0')) 637 return (-1); 638 goto out; 639 } 640 641 /* Check for values represented in decimal. */ 642 errno = 0; /* to check for ERANGE */ 643 val = (int)strtol(flags, &cp, 10); 644 if ((errno == ERANGE) || (*cp != '\0')) 645 return (-1); 646 647 out: 648 return (((val & ~CCDF_USERMASK) == 0) ? val : -1); 649} 650 651static void 652usage(void) 653{ 654 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n", 655 "usage: ccdconfig [-cv] ccd ileave [flags] dev [...]", 656 " ccdconfig -C [-v] [-f config_file]", 657 " ccdconfig -u [-v] ccd [...]", 658 " ccdconfig -U [-v] [-f config_file]", 659 " ccdconfig -g [ccd [...]]"); 660 exit(1); 661} 662 663/* Local Variables: */ 664/* c-argdecl-indent: 8 */ 665/* c-indent-level: 8 */ 666/* End: */ 667