ccdconfig.c revision 115731
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 115731 2003-06-02 21:29:04Z 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 int resolve_ccdname(char *); 86static void usage(void); 87 88int 89main(int argc, char *argv[]) 90{ 91 int ch, options = 0, action = CCD_CONFIG; 92 93 while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) { 94 switch (ch) { 95 case 'c': 96 action = CCD_CONFIG; 97 ++options; 98 break; 99 100 case 'C': 101 action = CCD_CONFIGALL; 102 ++options; 103 break; 104 105 case 'f': 106 ccdconf = optarg; 107 break; 108 109 case 'g': 110 action = CCD_DUMP; 111 break; 112 113 case 'u': 114 action = CCD_UNCONFIG; 115 ++options; 116 break; 117 118 case 'U': 119 action = CCD_UNCONFIGALL; 120 ++options; 121 break; 122 123 case 'v': 124 verbose = 1; 125 break; 126 127 default: 128 usage(); 129 } 130 } 131 argc -= optind; 132 argv += optind; 133 134 if (options > 1) 135 usage(); 136 137 if (modfind("ccd") < 0) { 138 /* Not present in kernel, try loading it */ 139 if (kldload("ccd") < 0 || modfind("ccd") < 0) 140 warn("ccd module not available!"); 141 } 142 143 switch (action) { 144 case CCD_CONFIG: 145 case CCD_UNCONFIG: 146 exit(do_single(argc, argv, action)); 147 /* NOTREACHED */ 148 149 case CCD_CONFIGALL: 150 case CCD_UNCONFIGALL: 151 exit(do_all(action)); 152 /* NOTREACHED */ 153 154 case CCD_DUMP: 155 exit(dump_ccd(argc, argv)); 156 /* NOTREACHED */ 157 } 158 /* NOTREACHED */ 159 return (0); 160} 161 162static int 163do_single(int argc, char **argv, int action) 164{ 165 struct ccd_ioctl ccio; 166 char *cp, *cp2, **disks; 167 int ccd, noflags = 0, i, ileave, flags = 0, j; 168 u_int u; 169 170 bzero(&ccio, sizeof(ccio)); 171 172 /* 173 * If unconfiguring, all arguments are treated as ccds. 174 */ 175 if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) { 176 for (i = 0; argc != 0; ) { 177 cp = *argv++; --argc; 178 if ((ccd = resolve_ccdname(cp)) < 0) { 179 warnx("invalid ccd name: %s", cp); 180 i = 1; 181 continue; 182 } 183 ccio.ccio_size = ccd; 184 if (do_io(ccd, CCDIOCCLR, &ccio)) 185 i = 1; 186 else 187 if (verbose) 188 printf("%s unconfigured\n", cp); 189 } 190 return (i); 191 } 192 193 /* Make sure there are enough arguments. */ 194 if (argc < 4) { 195 if (argc == 3) { 196 /* Assume that no flags are specified. */ 197 noflags = 1; 198 } else { 199 if (action == CCD_CONFIGALL) { 200 warnx("%s: bad line: %d", ccdconf, lineno); 201 return (1); 202 } else 203 usage(); 204 } 205 } 206 207 /* First argument is the ccd to configure. */ 208 cp = *argv++; --argc; 209 if ((ccd = resolve_ccdname(cp)) < 0) { 210 warnx("invalid ccd name: %s", cp); 211 return (1); 212 } 213 214 /* Next argument is the interleave factor. */ 215 cp = *argv++; --argc; 216 errno = 0; /* to check for ERANGE */ 217 ileave = (int)strtol(cp, &cp2, 10); 218 if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) { 219 warnx("invalid interleave factor: %s", cp); 220 return (1); 221 } 222 223 if (noflags == 0) { 224 /* Next argument is the ccd configuration flags. */ 225 cp = *argv++; --argc; 226 if ((flags = flags_to_val(cp)) < 0) { 227 warnx("invalid flags argument: %s", cp); 228 return (1); 229 } 230 } 231 232 /* Next is the list of disks to make the ccd from. */ 233 disks = malloc(argc * sizeof(char *)); 234 if (disks == NULL) { 235 warnx("no memory to configure ccd"); 236 return (1); 237 } 238 for (i = 0; argc != 0; ) { 239 cp = *argv++; --argc; 240 if ((j = checkdev(cp)) == 0) 241 disks[i++] = cp; 242 else { 243 warnx("%s: %s", cp, strerror(j)); 244 return (1); 245 } 246 } 247 248 /* Fill in the ccio. */ 249 ccio.ccio_disks = disks; 250 ccio.ccio_ndisks = i; 251 ccio.ccio_ileave = ileave; 252 ccio.ccio_flags = flags; 253 ccio.ccio_size = ccd; 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, "%s%s", _PATH_DEV, _PATH_CCDCTL); 383 384 if ((fd = open(path, O_RDWR, 0640)) < 0) { 385 asprintf(&path, "%sccd%dc", _PATH_DEV, unit); 386 if ((fd = open(path, O_RDWR, 0640)) < 0) { 387 warn("open: %s", path); 388 return (1); 389 } 390 fprintf(stderr, 391 "***WARNING***: Kernel older than ccdconfig(8), please upgrade it.\n"); 392 fprintf(stderr, 393 "***WARNING***: Continuing in 30 seconds\n"); 394 sleep(30); 395 } 396 397 if (ioctl(fd, cmd, cciop) < 0) { 398 switch (cmd) { 399 case CCDIOCSET: 400 cp = "CCDIOCSET"; 401 break; 402 403 case CCDIOCCLR: 404 cp = "CCDIOCCLR"; 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 418dumpout(int unit) 419{ 420 static int v; 421 struct gctl_req *grq; 422 int ncp; 423 char *cp; 424 char const *errstr; 425 426 427 grq = gctl_get_handle(); 428 ncp = 65536; 429 cp = malloc(ncp); 430 gctl_ro_param(grq, "verb", -1, "list"); 431 gctl_ro_param(grq, "class", -1, "CCD"); 432 gctl_ro_param(grq, "unit", sizeof(unit), &unit); 433 gctl_rw_param(grq, "output", ncp, cp); 434 errstr = gctl_issue(grq); 435 if (errstr != NULL) 436 errx(1, "%s\nor possibly kernel and ccdconfig out of sync", 437 errstr); 438 if (strlen(cp) == 0) 439 errx(1, "ccd%d not configured", unit); 440 if (verbose && !v) { 441 printf("# ccd\t\tileave\tflags\tcomponent devices\n"); 442 v = 1; 443 } 444 printf("%s", cp); 445 free(cp); 446 return (0); 447} 448 449static int 450dump_ccd(int argc, char **argv) 451{ 452 int i, err; 453 454 if (argc == 0) { 455 err = dumpout(-1); 456 } else { 457 err = 0; 458 for (i = 0; err == 0 && i < argc; i++) 459 err = dumpout(resolve_ccdname(argv[i])); 460 } 461 return (err); 462} 463 464static int 465flags_to_val(char *flags) 466{ 467 char *cp, *tok; 468 int i, tmp, val = ~CCDF_USERMASK; 469 size_t flagslen; 470 471 /* 472 * The most common case is that of NIL flags, so check for 473 * those first. 474 */ 475 if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 || 476 strcmp("0", flags) == 0) 477 return (0); 478 479 flagslen = strlen(flags); 480 481 /* Check for values represented by strings. */ 482 if ((cp = strdup(flags)) == NULL) 483 err(1, "no memory to parse flags"); 484 tmp = 0; 485 for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) { 486 for (i = 0; flagvaltab[i].fv_flag != NULL; ++i) 487 if (strcmp(tok, flagvaltab[i].fv_flag) == 0) 488 break; 489 if (flagvaltab[i].fv_flag == NULL) { 490 free(cp); 491 goto bad_string; 492 } 493 tmp |= flagvaltab[i].fv_val; 494 } 495 496 /* If we get here, the string was ok. */ 497 free(cp); 498 val = tmp; 499 goto out; 500 501 bad_string: 502 503 /* Check for values represented in hex. */ 504 if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') { 505 errno = 0; /* to check for ERANGE */ 506 val = (int)strtol(&flags[2], &cp, 16); 507 if ((errno == ERANGE) || (*cp != '\0')) 508 return (-1); 509 goto out; 510 } 511 512 /* Check for values represented in decimal. */ 513 errno = 0; /* to check for ERANGE */ 514 val = (int)strtol(flags, &cp, 10); 515 if ((errno == ERANGE) || (*cp != '\0')) 516 return (-1); 517 518 out: 519 return (((val & ~CCDF_USERMASK) == 0) ? val : -1); 520} 521 522static void 523usage(void) 524{ 525 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n", 526 "usage: ccdconfig [-cv] ccd ileave [flags] dev [...]", 527 " ccdconfig -C [-v] [-f config_file]", 528 " ccdconfig -u [-v] ccd [...]", 529 " ccdconfig -U [-v] [-f config_file]", 530 " ccdconfig -g [ccd [...]]"); 531 exit(1); 532} 533 534/* Local Variables: */ 535/* c-argdecl-indent: 8 */ 536/* c-indent-level: 8 */ 537/* End: */ 538