ccdconfig.c revision 115731
113044Sasami/* $NetBSD: ccdconfig.c,v 1.2.2.1 1995/11/11 02:43:35 thorpej Exp $ */ 213044Sasami 313044Sasami/* 413044Sasami * Copyright (c) 1995 Jason R. Thorpe. 513044Sasami * All rights reserved. 613044Sasami * 713044Sasami * Redistribution and use in source and binary forms, with or without 813044Sasami * modification, are permitted provided that the following conditions 913044Sasami * are met: 1013044Sasami * 1. Redistributions of source code must retain the above copyright 1113044Sasami * notice, this list of conditions and the following disclaimer. 1213044Sasami * 2. Redistributions in binary form must reproduce the above copyright 1313044Sasami * notice, this list of conditions and the following disclaimer in the 1413044Sasami * documentation and/or other materials provided with the distribution. 1513044Sasami * 3. All advertising materials mentioning features or use of this software 1613044Sasami * must display the following acknowledgement: 1713044Sasami * This product includes software developed for the NetBSD Project 1813044Sasami * by Jason R. Thorpe. 1913044Sasami * 4. The name of the author may not be used to endorse or promote products 2013044Sasami * derived from this software without specific prior written permission. 2113044Sasami * 2213044Sasami * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2313044Sasami * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2413044Sasami * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2513044Sasami * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2613044Sasami * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 2713044Sasami * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2813044Sasami * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2913044Sasami * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 3013044Sasami * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3113044Sasami * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3213044Sasami * SUCH DAMAGE. 3313044Sasami */ 3413044Sasami 35114589Sobrien#include <sys/cdefs.h> 36114589Sobrien__FBSDID("$FreeBSD: head/sbin/ccdconfig/ccdconfig.c 115731 2003-06-02 21:29:04Z phk $"); 3736628Scharnier 3813044Sasami#include <sys/param.h> 3948568Sbillf#include <sys/linker.h> 4013044Sasami#include <sys/disklabel.h> 4113044Sasami#include <sys/stat.h> 4245329Speter#include <sys/module.h> 4313044Sasami#include <ctype.h> 4413044Sasami#include <err.h> 4513044Sasami#include <errno.h> 4613044Sasami#include <fcntl.h> 4713044Sasami#include <limits.h> 4869793Sobrien#include <paths.h> 4913044Sasami#include <stdio.h> 5013044Sasami#include <stdlib.h> 5113044Sasami#include <string.h> 5213044Sasami#include <unistd.h> 53115730Sphk#include <libgeom.h> 5413044Sasami 5539228Sgibbs#include <sys/devicestat.h> 5613052Sasami#include <sys/ccdvar.h> 5713044Sasami 5813044Sasami#include "pathnames.h" 5913044Sasami 6013044Sasamistatic int lineno = 0; 6113044Sasamistatic int verbose = 0; 62109417Sphkstatic const char *ccdconf = _PATH_CCDCONF; 6313044Sasami 6413044Sasamistruct flagval { 65109417Sphk const char *fv_flag; 6613044Sasami int fv_val; 6713044Sasami} flagvaltab[] = { 6813044Sasami { "CCDF_UNIFORM", CCDF_UNIFORM }, 6913762Sasami { "CCDF_MIRROR", CCDF_MIRROR }, 7013044Sasami { NULL, 0 }, 7113044Sasami}; 7213044Sasami 7313044Sasami#define CCD_CONFIG 0 /* configure a device */ 7413044Sasami#define CCD_CONFIGALL 1 /* configure all devices */ 7513044Sasami#define CCD_UNCONFIG 2 /* unconfigure a device */ 7613044Sasami#define CCD_UNCONFIGALL 3 /* unconfigure all devices */ 7713044Sasami#define CCD_DUMP 4 /* dump a ccd's configuration */ 7813044Sasami 7992539Simpstatic int checkdev(char *); 80109417Sphkstatic int do_io(int, u_long, struct ccd_ioctl *); 8192539Simpstatic int do_single(int, char **, int); 8292539Simpstatic int do_all(int); 8392539Simpstatic int dump_ccd(int, char **); 8492539Simpstatic int flags_to_val(char *); 85109417Sphkstatic int resolve_ccdname(char *); 8692539Simpstatic void usage(void); 8713044Sasami 8813044Sasamiint 8992539Simpmain(int argc, char *argv[]) 9013044Sasami{ 9113044Sasami int ch, options = 0, action = CCD_CONFIG; 9213044Sasami 9383329Sru while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) { 9413044Sasami switch (ch) { 9513044Sasami case 'c': 9613044Sasami action = CCD_CONFIG; 9713044Sasami ++options; 9813044Sasami break; 9913044Sasami 10013044Sasami case 'C': 10113044Sasami action = CCD_CONFIGALL; 10213044Sasami ++options; 10313044Sasami break; 10413044Sasami 10513044Sasami case 'f': 10613044Sasami ccdconf = optarg; 10713044Sasami break; 10813044Sasami 10913044Sasami case 'g': 11013044Sasami action = CCD_DUMP; 11113044Sasami break; 11213044Sasami 11313044Sasami case 'u': 11413044Sasami action = CCD_UNCONFIG; 11513044Sasami ++options; 11613044Sasami break; 11713044Sasami 11813044Sasami case 'U': 11913044Sasami action = CCD_UNCONFIGALL; 12013044Sasami ++options; 12113044Sasami break; 12213044Sasami 12313044Sasami case 'v': 12413044Sasami verbose = 1; 12513044Sasami break; 12613044Sasami 12713044Sasami default: 12813044Sasami usage(); 12913044Sasami } 13013044Sasami } 13113044Sasami argc -= optind; 13213044Sasami argv += optind; 13313044Sasami 13413044Sasami if (options > 1) 13513044Sasami usage(); 13613044Sasami 13745329Speter if (modfind("ccd") < 0) { 13845329Speter /* Not present in kernel, try loading it */ 13945329Speter if (kldload("ccd") < 0 || modfind("ccd") < 0) 14045329Speter warn("ccd module not available!"); 14145329Speter } 14245329Speter 14313044Sasami switch (action) { 14413044Sasami case CCD_CONFIG: 14513044Sasami case CCD_UNCONFIG: 14613044Sasami exit(do_single(argc, argv, action)); 14713044Sasami /* NOTREACHED */ 14813044Sasami 14913044Sasami case CCD_CONFIGALL: 15013044Sasami case CCD_UNCONFIGALL: 15113044Sasami exit(do_all(action)); 15213044Sasami /* NOTREACHED */ 15313044Sasami 15413044Sasami case CCD_DUMP: 15513044Sasami exit(dump_ccd(argc, argv)); 15613044Sasami /* NOTREACHED */ 15713044Sasami } 15813044Sasami /* NOTREACHED */ 15936628Scharnier return (0); 16013044Sasami} 16113044Sasami 16213044Sasamistatic int 16392539Simpdo_single(int argc, char **argv, int action) 16413044Sasami{ 16513044Sasami struct ccd_ioctl ccio; 166109417Sphk char *cp, *cp2, **disks; 167109417Sphk int ccd, noflags = 0, i, ileave, flags = 0, j; 168109417Sphk u_int u; 16913044Sasami 17013044Sasami bzero(&ccio, sizeof(ccio)); 17113044Sasami 17213044Sasami /* 17313044Sasami * If unconfiguring, all arguments are treated as ccds. 17413044Sasami */ 17513044Sasami if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) { 17613044Sasami for (i = 0; argc != 0; ) { 17713044Sasami cp = *argv++; --argc; 178109472Sphk if ((ccd = resolve_ccdname(cp)) < 0) { 17913044Sasami warnx("invalid ccd name: %s", cp); 18013044Sasami i = 1; 18113044Sasami continue; 18213044Sasami } 183109421Sphk ccio.ccio_size = ccd; 18413044Sasami if (do_io(ccd, CCDIOCCLR, &ccio)) 18513044Sasami i = 1; 18613044Sasami else 18713044Sasami if (verbose) 18813044Sasami printf("%s unconfigured\n", cp); 18913044Sasami } 19013044Sasami return (i); 19113044Sasami } 19213044Sasami 19313044Sasami /* Make sure there are enough arguments. */ 19448568Sbillf if (argc < 4) { 19513044Sasami if (argc == 3) { 19613044Sasami /* Assume that no flags are specified. */ 19713044Sasami noflags = 1; 19813044Sasami } else { 19913044Sasami if (action == CCD_CONFIGALL) { 20013044Sasami warnx("%s: bad line: %d", ccdconf, lineno); 20113044Sasami return (1); 20213044Sasami } else 20313044Sasami usage(); 20413044Sasami } 20548568Sbillf } 20613044Sasami 20713044Sasami /* First argument is the ccd to configure. */ 20813044Sasami cp = *argv++; --argc; 209109472Sphk if ((ccd = resolve_ccdname(cp)) < 0) { 21013044Sasami warnx("invalid ccd name: %s", cp); 21113044Sasami return (1); 21213044Sasami } 21313044Sasami 21413044Sasami /* Next argument is the interleave factor. */ 21513044Sasami cp = *argv++; --argc; 21613044Sasami errno = 0; /* to check for ERANGE */ 21713044Sasami ileave = (int)strtol(cp, &cp2, 10); 21813044Sasami if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) { 21913044Sasami warnx("invalid interleave factor: %s", cp); 22013044Sasami return (1); 22113044Sasami } 22213044Sasami 22313044Sasami if (noflags == 0) { 22413044Sasami /* Next argument is the ccd configuration flags. */ 22513044Sasami cp = *argv++; --argc; 22613044Sasami if ((flags = flags_to_val(cp)) < 0) { 22713044Sasami warnx("invalid flags argument: %s", cp); 22813044Sasami return (1); 22913044Sasami } 23013044Sasami } 23113044Sasami 23213044Sasami /* Next is the list of disks to make the ccd from. */ 23313044Sasami disks = malloc(argc * sizeof(char *)); 23413044Sasami if (disks == NULL) { 23513044Sasami warnx("no memory to configure ccd"); 23613044Sasami return (1); 23713044Sasami } 23813044Sasami for (i = 0; argc != 0; ) { 23913044Sasami cp = *argv++; --argc; 24013044Sasami if ((j = checkdev(cp)) == 0) 24113044Sasami disks[i++] = cp; 24213044Sasami else { 24313044Sasami warnx("%s: %s", cp, strerror(j)); 24413044Sasami return (1); 24513044Sasami } 24613044Sasami } 24713044Sasami 24813044Sasami /* Fill in the ccio. */ 24913044Sasami ccio.ccio_disks = disks; 25013044Sasami ccio.ccio_ndisks = i; 25113044Sasami ccio.ccio_ileave = ileave; 25213044Sasami ccio.ccio_flags = flags; 253109421Sphk ccio.ccio_size = ccd; 25413044Sasami 25513044Sasami if (do_io(ccd, CCDIOCSET, &ccio)) { 25613044Sasami free(disks); 25713044Sasami return (1); 25813044Sasami } 25913044Sasami 26013044Sasami if (verbose) { 26113044Sasami printf("ccd%d: %d components ", ccio.ccio_unit, 26213044Sasami ccio.ccio_ndisks); 263109417Sphk for (u = 0; u < ccio.ccio_ndisks; ++u) { 264109417Sphk if ((cp2 = strrchr(disks[u], '/')) != NULL) 26513044Sasami ++cp2; 26613044Sasami else 267109417Sphk cp2 = disks[u]; 26813044Sasami printf("%c%s%c", 269109417Sphk u == 0 ? '(' : ' ', cp2, 270109417Sphk u == ccio.ccio_ndisks - 1 ? ')' : ','); 27113044Sasami } 27248568Sbillf printf(", %lu blocks ", (u_long)ccio.ccio_size); 27313044Sasami if (ccio.ccio_ileave != 0) 27413044Sasami printf("interleaved at %d blocks\n", ccio.ccio_ileave); 27513044Sasami else 27613044Sasami printf("concatenated\n"); 27713044Sasami } 27813044Sasami 27913044Sasami free(disks); 28013044Sasami return (0); 28113044Sasami} 28213044Sasami 28313044Sasamistatic int 28492539Simpdo_all(int action) 28513044Sasami{ 28613044Sasami FILE *f; 28713044Sasami char line[_POSIX2_LINE_MAX]; 28813044Sasami char *cp, **argv; 28913044Sasami int argc, rval; 29032116Simp gid_t egid; 29113044Sasami 29251690Sbillf rval = 0; 29332116Simp egid = getegid(); 29432116Simp setegid(getgid()); 29513044Sasami if ((f = fopen(ccdconf, "r")) == NULL) { 29632116Simp setegid(egid); 29713044Sasami warn("fopen: %s", ccdconf); 29813044Sasami return (1); 29913044Sasami } 30032116Simp setegid(egid); 30113044Sasami 30213044Sasami while (fgets(line, sizeof(line), f) != NULL) { 30313044Sasami argc = 0; 30413044Sasami argv = NULL; 30513044Sasami ++lineno; 30613044Sasami if ((cp = strrchr(line, '\n')) != NULL) 30713044Sasami *cp = '\0'; 30813044Sasami 30913044Sasami /* Break up the line and pass it's contents to do_single(). */ 31013044Sasami if (line[0] == '\0') 31113044Sasami goto end_of_line; 31213044Sasami for (cp = line; (cp = strtok(cp, " \t")) != NULL; cp = NULL) { 31313044Sasami if (*cp == '#') 31413044Sasami break; 31513044Sasami if ((argv = realloc(argv, 31613044Sasami sizeof(char *) * ++argc)) == NULL) { 31713044Sasami warnx("no memory to configure ccds"); 31813044Sasami return (1); 31913044Sasami } 32013044Sasami argv[argc - 1] = cp; 32113044Sasami /* 32213044Sasami * If our action is to unconfigure all, then pass 32313044Sasami * just the first token to do_single() and ignore 32413044Sasami * the rest. Since this will be encountered on 32513044Sasami * our first pass through the line, the Right 32613044Sasami * Thing will happen. 32713044Sasami */ 32813044Sasami if (action == CCD_UNCONFIGALL) { 32913044Sasami if (do_single(argc, argv, action)) 33013044Sasami rval = 1; 33113044Sasami goto end_of_line; 33213044Sasami } 33313044Sasami } 33413044Sasami if (argc != 0) 33513044Sasami if (do_single(argc, argv, action)) 33613044Sasami rval = 1; 33713044Sasami 33813044Sasami end_of_line: 33913044Sasami if (argv != NULL) 34013044Sasami free(argv); 34113044Sasami } 34213044Sasami 34313044Sasami (void)fclose(f); 34413044Sasami return (rval); 34513044Sasami} 34613044Sasami 34713044Sasamistatic int 34892539Simpcheckdev(char *path) 34913044Sasami{ 35013044Sasami struct stat st; 35113044Sasami 35213044Sasami if (stat(path, &st) != 0) 35313044Sasami return (errno); 35413044Sasami 35513044Sasami if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) 35613044Sasami return (EINVAL); 35713044Sasami 35813044Sasami return (0); 35913044Sasami} 36013044Sasami 36113044Sasamistatic int 36292539Simpresolve_ccdname(char *name) 36313044Sasami{ 36413044Sasami 365109417Sphk if (!strncmp(name, _PATH_DEV, strlen(_PATH_DEV))) 366109417Sphk name += strlen(_PATH_DEV); 367109417Sphk if (strncmp(name, "ccd", 3)) 368109417Sphk return -1; 369109417Sphk name += 3; 370109417Sphk if (!isdigit(*name)) 371109417Sphk return -1; 372109417Sphk return (strtoul(name, NULL, 10)); 37313044Sasami} 37413044Sasami 37513044Sasamistatic int 376109417Sphkdo_io(int unit, u_long cmd, struct ccd_ioctl *cciop) 37713044Sasami{ 37813044Sasami int fd; 37913044Sasami char *cp; 380109417Sphk char *path; 38113044Sasami 382109421Sphk asprintf(&path, "%s%s", _PATH_DEV, _PATH_CCDCTL); 383109417Sphk 38413044Sasami if ((fd = open(path, O_RDWR, 0640)) < 0) { 385109421Sphk asprintf(&path, "%sccd%dc", _PATH_DEV, unit); 386109421Sphk if ((fd = open(path, O_RDWR, 0640)) < 0) { 387109421Sphk warn("open: %s", path); 388109421Sphk return (1); 389109421Sphk } 390109421Sphk fprintf(stderr, 391109421Sphk "***WARNING***: Kernel older than ccdconfig(8), please upgrade it.\n"); 392109421Sphk fprintf(stderr, 393109421Sphk "***WARNING***: Continuing in 30 seconds\n"); 394109421Sphk sleep(30); 39513044Sasami } 39613044Sasami 39713044Sasami if (ioctl(fd, cmd, cciop) < 0) { 39813044Sasami switch (cmd) { 39913044Sasami case CCDIOCSET: 40013044Sasami cp = "CCDIOCSET"; 40113044Sasami break; 40213044Sasami 40313044Sasami case CCDIOCCLR: 40413044Sasami cp = "CCDIOCCLR"; 40513044Sasami break; 40613044Sasami 40713044Sasami default: 40813044Sasami cp = "unknown"; 40913044Sasami } 41013044Sasami warn("ioctl (%s): %s", cp, path); 41113044Sasami return (1); 41213044Sasami } 41313044Sasami 41413044Sasami return (0); 41513044Sasami} 41613044Sasami 41713044Sasamistatic int 418115731Sphkdumpout(int unit) 41913044Sasami{ 420115731Sphk static int v; 421115731Sphk struct gctl_req *grq; 422115731Sphk int ncp; 423109417Sphk char *cp; 424115730Sphk char const *errstr; 425115731Sphk 42613044Sasami 427115730Sphk grq = gctl_get_handle(); 428115731Sphk ncp = 65536; 429115731Sphk cp = malloc(ncp); 430115730Sphk gctl_ro_param(grq, "verb", -1, "list"); 431115730Sphk gctl_ro_param(grq, "class", -1, "CCD"); 432115731Sphk gctl_ro_param(grq, "unit", sizeof(unit), &unit); 433115731Sphk gctl_rw_param(grq, "output", ncp, cp); 434115730Sphk errstr = gctl_issue(grq); 435115731Sphk if (errstr != NULL) 436115731Sphk errx(1, "%s\nor possibly kernel and ccdconfig out of sync", 437115731Sphk errstr); 438115731Sphk if (strlen(cp) == 0) 439115731Sphk errx(1, "ccd%d not configured", unit); 440115731Sphk if (verbose && !v) { 441115731Sphk printf("# ccd\t\tileave\tflags\tcomponent devices\n"); 442115731Sphk v = 1; 443115730Sphk } 444115731Sphk printf("%s", cp); 445115731Sphk free(cp); 44613044Sasami return (0); 44713044Sasami} 44813044Sasami 449115731Sphkstatic int 450115731Sphkdump_ccd(int argc, char **argv) 45113044Sasami{ 452115731Sphk int i, err; 45313044Sasami 454115731Sphk if (argc == 0) { 455115731Sphk err = dumpout(-1); 456115731Sphk } else { 457115731Sphk err = 0; 458115731Sphk for (i = 0; err == 0 && i < argc; i++) 459115731Sphk err = dumpout(resolve_ccdname(argv[i])); 46013044Sasami } 461115731Sphk return (err); 46213044Sasami} 46313044Sasami 46413044Sasamistatic int 46592539Simpflags_to_val(char *flags) 46613044Sasami{ 46713044Sasami char *cp, *tok; 46813044Sasami int i, tmp, val = ~CCDF_USERMASK; 46913044Sasami size_t flagslen; 47013044Sasami 47113044Sasami /* 47213044Sasami * The most common case is that of NIL flags, so check for 47313044Sasami * those first. 47413044Sasami */ 47513044Sasami if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 || 47613044Sasami strcmp("0", flags) == 0) 47713044Sasami return (0); 47813044Sasami 47913044Sasami flagslen = strlen(flags); 48013044Sasami 48113044Sasami /* Check for values represented by strings. */ 48213044Sasami if ((cp = strdup(flags)) == NULL) 48313044Sasami err(1, "no memory to parse flags"); 48413044Sasami tmp = 0; 48513044Sasami for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) { 48613044Sasami for (i = 0; flagvaltab[i].fv_flag != NULL; ++i) 48713044Sasami if (strcmp(tok, flagvaltab[i].fv_flag) == 0) 48813044Sasami break; 48913044Sasami if (flagvaltab[i].fv_flag == NULL) { 49013044Sasami free(cp); 49113044Sasami goto bad_string; 49213044Sasami } 49313044Sasami tmp |= flagvaltab[i].fv_val; 49413044Sasami } 49513044Sasami 49613044Sasami /* If we get here, the string was ok. */ 49713044Sasami free(cp); 49813044Sasami val = tmp; 49913044Sasami goto out; 50013044Sasami 50113044Sasami bad_string: 50213044Sasami 50313044Sasami /* Check for values represented in hex. */ 50413044Sasami if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') { 50513044Sasami errno = 0; /* to check for ERANGE */ 50613044Sasami val = (int)strtol(&flags[2], &cp, 16); 50713044Sasami if ((errno == ERANGE) || (*cp != '\0')) 50813044Sasami return (-1); 50913044Sasami goto out; 51013044Sasami } 51113044Sasami 51213044Sasami /* Check for values represented in decimal. */ 51313044Sasami errno = 0; /* to check for ERANGE */ 51413044Sasami val = (int)strtol(flags, &cp, 10); 51513044Sasami if ((errno == ERANGE) || (*cp != '\0')) 51613044Sasami return (-1); 51713044Sasami 51813044Sasami out: 51913044Sasami return (((val & ~CCDF_USERMASK) == 0) ? val : -1); 52013044Sasami} 52113044Sasami 52213044Sasamistatic void 52392539Simpusage(void) 52413044Sasami{ 52526541Scharnier fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n", 52626541Scharnier "usage: ccdconfig [-cv] ccd ileave [flags] dev [...]", 52726541Scharnier " ccdconfig -C [-v] [-f config_file]", 52826541Scharnier " ccdconfig -u [-v] ccd [...]", 52926541Scharnier " ccdconfig -U [-v] [-f config_file]", 53082943Sphk " ccdconfig -g [ccd [...]]"); 53113044Sasami exit(1); 53213044Sasami} 53326541Scharnier 53413052Sasami/* Local Variables: */ 53513052Sasami/* c-argdecl-indent: 8 */ 53613052Sasami/* c-indent-level: 8 */ 53713052Sasami/* End: */ 538