1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2003 Poul-Henning Kamp 5 * Copyright (c) 1996 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Jason R. Thorpe. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE 24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 * 32 * NetBSD: ccdconfig.c,v 1.6 1996/05/16 07:11:18 thorpej Exp $ 33 */ 34 35 #include <sys/param.h> 36 #include <sys/linker.h> 37 #include <sys/module.h> 38 #include <ctype.h> 39 #include <err.h> 40 #include <errno.h> 41 #include <limits.h> 42 #include <paths.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <unistd.h> 47 #include <libgeom.h> 48 49 #define CCDF_UNIFORM 0x02 /* use LCCD of sizes for uniform interleave */ 50 #define CCDF_MIRROR 0x04 /* use mirroring */ 51 #define CCDF_NO_OFFSET 0x08 /* do not leave space in front */ 52 #define CCDF_LINUX 0x10 /* use Linux compatibility mode */ 53 54 #include "pathnames.h" 55 56 static int lineno = 0; 57 static int verbose = 0; 58 static const char *ccdconf = _PATH_CCDCONF; 59 60 static struct flagval { 61 const char *fv_flag; 62 int fv_val; 63 } flagvaltab[] = { 64 { "CCDF_UNIFORM", CCDF_UNIFORM }, 65 { "uniform", CCDF_UNIFORM }, 66 { "CCDF_MIRROR", CCDF_MIRROR }, 67 { "mirror", CCDF_MIRROR }, 68 { "CCDF_NO_OFFSET", CCDF_NO_OFFSET }, 69 { "no_offset", CCDF_NO_OFFSET }, 70 { "CCDF_LINUX", CCDF_LINUX }, 71 { "linux", CCDF_LINUX }, 72 { "none", 0 }, 73 { NULL, 0 }, 74 }; 75 76 #define CCD_CONFIG 0 /* configure a device */ 77 #define CCD_CONFIGALL 1 /* configure all devices */ 78 #define CCD_UNCONFIG 2 /* unconfigure a device */ 79 #define CCD_UNCONFIGALL 3 /* unconfigure all devices */ 80 #define CCD_DUMP 4 /* dump a ccd's configuration */ 81 82 static int do_single(int, char **, int); 83 static int do_all(int); 84 static int dump_ccd(int, char **); 85 static int flags_to_val(char *); 86 static int resolve_ccdname(char *); 87 static void usage(void); 88 89 int 90 main(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("g_ccd") < 0) { 139 /* Not present in kernel, try loading it */ 140 if (kldload("geom_ccd") < 0 || modfind("g_ccd") < 0) 141 warn("geom_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 163 static int 164 do_single(int argc, char **argv, int action) 165 { 166 char *cp, *cp2; 167 int ccd, noflags = 0, i, ileave, flags = 0; 168 struct gctl_req *grq; 169 char const *errstr; 170 char buf1[BUFSIZ]; 171 int ex; 172 173 /* 174 * If unconfiguring, all arguments are treated as ccds. 175 */ 176 if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) { 177 ex = 0; 178 for (; argc != 0;) { 179 cp = *argv++; --argc; 180 if ((ccd = resolve_ccdname(cp)) < 0) { 181 warnx("invalid ccd name: %s", cp); 182 continue; 183 } 184 grq = gctl_get_handle(); 185 gctl_ro_param(grq, "verb", -1, "destroy geom"); 186 gctl_ro_param(grq, "class", -1, "CCD"); 187 sprintf(buf1, "ccd%d", ccd); 188 gctl_ro_param(grq, "geom", -1, buf1); 189 errstr = gctl_issue(grq); 190 if (errstr == NULL) { 191 if (verbose) 192 printf("%s unconfigured\n", cp); 193 gctl_free(grq); 194 continue; 195 } 196 warnx( 197 "%s\nor possibly kernel and ccdconfig out of sync", 198 errstr); 199 ex = 1; 200 } 201 return (ex); 202 } 203 204 /* Make sure there are enough arguments. */ 205 if (argc < 4) { 206 if (argc == 3) { 207 /* Assume that no flags are specified. */ 208 noflags = 1; 209 } else { 210 if (action == CCD_CONFIGALL) { 211 warnx("%s: bad line: %d", ccdconf, lineno); 212 return (1); 213 } else 214 usage(); 215 } 216 } 217 218 /* First argument is the ccd to configure. */ 219 cp = *argv++; --argc; 220 if ((ccd = resolve_ccdname(cp)) < 0) { 221 warnx("invalid ccd name: %s", cp); 222 return (1); 223 } 224 225 /* Next argument is the interleave factor. */ 226 cp = *argv++; --argc; 227 errno = 0; /* to check for ERANGE */ 228 ileave = (int)strtol(cp, &cp2, 10); 229 if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) { 230 warnx("invalid interleave factor: %s", cp); 231 return (1); 232 } 233 234 if (noflags == 0) { 235 /* Next argument is the ccd configuration flags. */ 236 cp = *argv++; --argc; 237 if ((flags = flags_to_val(cp)) < 0) { 238 warnx("invalid flags argument: %s", cp); 239 return (1); 240 } 241 } 242 grq = gctl_get_handle(); 243 gctl_ro_param(grq, "verb", -1, "create geom"); 244 gctl_ro_param(grq, "class", -1, "CCD"); 245 gctl_ro_param(grq, "unit", sizeof(ccd), &ccd); 246 gctl_ro_param(grq, "ileave", sizeof(ileave), &ileave); 247 if (flags & CCDF_UNIFORM) 248 gctl_ro_param(grq, "uniform", -1, ""); 249 if (flags & CCDF_MIRROR) 250 gctl_ro_param(grq, "mirror", -1, ""); 251 if (flags & CCDF_NO_OFFSET) 252 gctl_ro_param(grq, "no_offset", -1, ""); 253 if (flags & CCDF_LINUX) 254 gctl_ro_param(grq, "linux", -1, ""); 255 gctl_ro_param(grq, "nprovider", sizeof(argc), &argc); 256 for (i = 0; i < argc; i++) { 257 sprintf(buf1, "provider%d", i); 258 cp = argv[i]; 259 if (!strncmp(cp, _PATH_DEV, strlen(_PATH_DEV))) 260 cp += strlen(_PATH_DEV); 261 gctl_ro_param(grq, buf1, -1, cp); 262 } 263 buf1[0] = '\0'; 264 gctl_add_param(grq, "output", sizeof(buf1), buf1, 265 GCTL_PARAM_WR | GCTL_PARAM_ASCII); 266 errstr = gctl_issue(grq); 267 if (errstr == NULL) { 268 if (verbose) { 269 printf("%s", buf1); 270 } 271 gctl_free(grq); 272 return (0); 273 } 274 warnx( 275 "%s\nor possibly kernel and ccdconfig out of sync", 276 errstr); 277 return (1); 278 } 279 280 static int 281 do_all(int action) 282 { 283 FILE *f; 284 char line[_POSIX2_LINE_MAX]; 285 char *cp, **argv; 286 int argc, rval; 287 gid_t egid; 288 289 rval = 0; 290 egid = getegid(); 291 if (setegid(getgid()) != 0) 292 err(1, "setegid failed"); 293 if ((f = fopen(ccdconf, "r")) == NULL) { 294 if (setegid(egid) != 0) 295 err(1, "setegid failed"); 296 warn("fopen: %s", ccdconf); 297 return (1); 298 } 299 if (setegid(egid) != 0) 300 err(1, "setegid failed"); 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 347 static int 348 resolve_ccdname(char *name) 349 { 350 351 if (!strncmp(name, _PATH_DEV, strlen(_PATH_DEV))) 352 name += strlen(_PATH_DEV); 353 if (strncmp(name, "ccd", 3)) 354 return -1; 355 name += 3; 356 if (!isdigit(*name)) 357 return -1; 358 return (strtoul(name, NULL, 10)); 359 } 360 361 static int 362 dumpout(int unit) 363 { 364 static int v; 365 struct gctl_req *grq; 366 int ncp; 367 char *cp; 368 char const *errstr; 369 370 grq = gctl_get_handle(); 371 ncp = 65536; 372 cp = malloc(ncp); 373 cp[0] = '\0'; 374 gctl_ro_param(grq, "verb", -1, "list"); 375 gctl_ro_param(grq, "class", -1, "CCD"); 376 gctl_ro_param(grq, "unit", sizeof(unit), &unit); 377 gctl_add_param(grq, "output", ncp, cp, 378 GCTL_PARAM_WR | GCTL_PARAM_ASCII); 379 errstr = gctl_issue(grq); 380 if (errstr != NULL) 381 errx(1, "%s\nor possibly kernel and ccdconfig out of sync", 382 errstr); 383 if (strlen(cp) == 0) 384 errx(1, "ccd%d not configured", unit); 385 if (verbose && !v) { 386 printf("# ccd\t\tileave\tflags\tcomponent devices\n"); 387 v = 1; 388 } 389 printf("%s", cp); 390 free(cp); 391 return (0); 392 } 393 394 static int 395 dump_ccd(int argc, char **argv) 396 { 397 int i, error; 398 399 if (argc == 0) { 400 error = dumpout(-1); 401 } else { 402 error = 0; 403 for (i = 0; error == 0 && i < argc; i++) 404 error = dumpout(resolve_ccdname(argv[i])); 405 } 406 return (error); 407 } 408 409 static int 410 flags_to_val(char *flags) 411 { 412 char *cp, *tok; 413 int i, tmp, val; 414 415 errno = 0; /* to check for ERANGE */ 416 val = (int)strtol(flags, &cp, 0); 417 if ((errno != ERANGE) && (*cp == '\0')) { 418 if (val & ~(CCDF_UNIFORM|CCDF_MIRROR)) 419 return (-1); 420 return (val); 421 } 422 423 /* Check for values represented by strings. */ 424 if ((cp = strdup(flags)) == NULL) 425 err(1, "no memory to parse flags"); 426 tmp = 0; 427 for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) { 428 for (i = 0; flagvaltab[i].fv_flag != NULL; ++i) 429 if (strcmp(tok, flagvaltab[i].fv_flag) == 0) 430 break; 431 if (flagvaltab[i].fv_flag == NULL) { 432 free(cp); 433 return (-1); 434 } 435 tmp |= flagvaltab[i].fv_val; 436 } 437 438 /* If we get here, the string was ok. */ 439 free(cp); 440 return (tmp); 441 } 442 443 static void 444 usage(void) 445 { 446 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n", 447 "usage: ccdconfig [-cv] ccd ileave [flags] dev ...", 448 " ccdconfig -C [-v] [-f config_file]", 449 " ccdconfig -u [-v] ccd ...", 450 " ccdconfig -U [-v] [-f config_file]", 451 " ccdconfig -g [ccd ...]"); 452 exit(1); 453 } 454