xref: /freebsd/sbin/ccdconfig/ccdconfig.c (revision 17d6c636720d00f77e5d098daf4c278f89d84f7b)
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
36 static const char rcsid[] =
37   "$FreeBSD$";
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 
61 static	int lineno = 0;
62 static	int verbose = 0;
63 static	char *ccdconf = _PATH_CCDCONF;
64 
65 struct	flagval {
66 	char	*fv_flag;
67 	int	fv_val;
68 } flagvaltab[] = {
69 	{ "CCDF_SWAP",		CCDF_SWAP },
70 	{ "CCDF_UNIFORM",	CCDF_UNIFORM },
71 	{ "CCDF_MIRROR",	CCDF_MIRROR },
72 	{ "CCDF_PARITY",	CCDF_PARITY },
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 checkdev __P((char *));
83 static	int do_io __P((char *, u_long, struct ccd_ioctl *));
84 static	int do_single __P((int, char **, int));
85 static	int do_all __P((int));
86 static	int dump_ccd __P((int, char **));
87 static	int getmaxpartitions __P((void));
88 static	int getrawpartition __P((void));
89 static	int flags_to_val __P((char *));
90 static	void print_ccd_info __P((struct ccd_s *));
91 static	char *resolve_ccdname __P((char *));
92 static	void usage __P((void));
93 
94 int
95 main(argc, argv)
96 	int argc;
97 	char **argv;
98 {
99 	int ch, options = 0, action = CCD_CONFIG;
100 
101 	while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) {
102 		switch (ch) {
103 		case 'c':
104 			action = CCD_CONFIG;
105 			++options;
106 			break;
107 
108 		case 'C':
109 			action = CCD_CONFIGALL;
110 			++options;
111 			break;
112 
113 		case 'f':
114 			ccdconf = optarg;
115 			break;
116 
117 		case 'g':
118 			action = CCD_DUMP;
119 			break;
120 
121 		case 'u':
122 			action = CCD_UNCONFIG;
123 			++options;
124 			break;
125 
126 		case 'U':
127 			action = CCD_UNCONFIGALL;
128 			++options;
129 			break;
130 
131 		case 'v':
132 			verbose = 1;
133 			break;
134 
135 		default:
136 			usage();
137 		}
138 	}
139 	argc -= optind;
140 	argv += optind;
141 
142 	if (options > 1)
143 		usage();
144 
145 	if (modfind("ccd") < 0) {
146 		/* Not present in kernel, try loading it */
147 		if (kldload("ccd") < 0 || modfind("ccd") < 0)
148 			warn("ccd module not available!");
149 	}
150 
151 	switch (action) {
152 		case CCD_CONFIG:
153 		case CCD_UNCONFIG:
154 			exit(do_single(argc, argv, action));
155 			/* NOTREACHED */
156 
157 		case CCD_CONFIGALL:
158 		case CCD_UNCONFIGALL:
159 			exit(do_all(action));
160 			/* NOTREACHED */
161 
162 		case CCD_DUMP:
163 			exit(dump_ccd(argc, argv));
164 			/* NOTREACHED */
165 	}
166 	/* NOTREACHED */
167 	return (0);
168 }
169 
170 static int
171 do_single(argc, argv, action)
172 	int argc;
173 	char **argv;
174 	int action;
175 {
176 	struct ccd_ioctl ccio;
177 	char *ccd, *cp, *cp2, **disks;
178 	int noflags = 0, i, ileave, flags = 0, j;
179 
180 	bzero(&ccio, sizeof(ccio));
181 
182 	/*
183 	 * If unconfiguring, all arguments are treated as ccds.
184 	 */
185 	if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) {
186 		for (i = 0; argc != 0; ) {
187 			cp = *argv++; --argc;
188 			if ((ccd = resolve_ccdname(cp)) == NULL) {
189 				warnx("invalid ccd name: %s", cp);
190 				i = 1;
191 				continue;
192 			}
193 			if (do_io(ccd, CCDIOCCLR, &ccio))
194 				i = 1;
195 			else
196 				if (verbose)
197 					printf("%s unconfigured\n", cp);
198 		}
199 		return (i);
200 	}
201 
202 	/* Make sure there are enough arguments. */
203 	if (argc < 4) {
204 		if (argc == 3) {
205 			/* Assume that no flags are specified. */
206 			noflags = 1;
207 		} else {
208 			if (action == CCD_CONFIGALL) {
209 				warnx("%s: bad line: %d", ccdconf, lineno);
210 				return (1);
211 			} else
212 				usage();
213 		}
214 	}
215 
216 	/* First argument is the ccd to configure. */
217 	cp = *argv++; --argc;
218 	if ((ccd = resolve_ccdname(cp)) == NULL) {
219 		warnx("invalid ccd name: %s", cp);
220 		return (1);
221 	}
222 
223 	/* Next argument is the interleave factor. */
224 	cp = *argv++; --argc;
225 	errno = 0;	/* to check for ERANGE */
226 	ileave = (int)strtol(cp, &cp2, 10);
227 	if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) {
228 		warnx("invalid interleave factor: %s", cp);
229 		return (1);
230 	}
231 
232 	if (noflags == 0) {
233 		/* Next argument is the ccd configuration flags. */
234 		cp = *argv++; --argc;
235 		if ((flags = flags_to_val(cp)) < 0) {
236 			warnx("invalid flags argument: %s", cp);
237 			return (1);
238 		}
239 	}
240 
241 	/* Next is the list of disks to make the ccd from. */
242 	disks = malloc(argc * sizeof(char *));
243 	if (disks == NULL) {
244 		warnx("no memory to configure ccd");
245 		return (1);
246 	}
247 	for (i = 0; argc != 0; ) {
248 		cp = *argv++; --argc;
249 		if ((j = checkdev(cp)) == 0)
250 			disks[i++] = cp;
251 		else {
252 			warnx("%s: %s", cp, strerror(j));
253 			return (1);
254 		}
255 	}
256 
257 	/* Fill in the ccio. */
258 	ccio.ccio_disks = disks;
259 	ccio.ccio_ndisks = i;
260 	ccio.ccio_ileave = ileave;
261 	ccio.ccio_flags = flags;
262 
263 	if (do_io(ccd, CCDIOCSET, &ccio)) {
264 		free(disks);
265 		return (1);
266 	}
267 
268 	if (verbose) {
269 		printf("ccd%d: %d components ", ccio.ccio_unit,
270 		    ccio.ccio_ndisks);
271 		for (i = 0; i < ccio.ccio_ndisks; ++i) {
272 			if ((cp2 = strrchr(disks[i], '/')) != NULL)
273 				++cp2;
274 			else
275 				cp2 = disks[i];
276 			printf("%c%s%c",
277 			    i == 0 ? '(' : ' ', cp2,
278 			    i == ccio.ccio_ndisks - 1 ? ')' : ',');
279 		}
280 		printf(", %lu blocks ", (u_long)ccio.ccio_size);
281 		if (ccio.ccio_ileave != 0)
282 			printf("interleaved at %d blocks\n", ccio.ccio_ileave);
283 		else
284 			printf("concatenated\n");
285 	}
286 
287 	free(disks);
288 	return (0);
289 }
290 
291 static int
292 do_all(action)
293 	int action;
294 {
295 	FILE *f;
296 	char line[_POSIX2_LINE_MAX];
297 	char *cp, **argv;
298 	int argc, rval;
299 	gid_t egid;
300 
301 	rval = 0;
302 	egid = getegid();
303 	setegid(getgid());
304 	if ((f = fopen(ccdconf, "r")) == NULL) {
305 		setegid(egid);
306 		warn("fopen: %s", ccdconf);
307 		return (1);
308 	}
309 	setegid(egid);
310 
311 	while (fgets(line, sizeof(line), f) != NULL) {
312 		argc = 0;
313 		argv = NULL;
314 		++lineno;
315 		if ((cp = strrchr(line, '\n')) != NULL)
316 			*cp = '\0';
317 
318 		/* Break up the line and pass it's contents to do_single(). */
319 		if (line[0] == '\0')
320 			goto end_of_line;
321 		for (cp = line; (cp = strtok(cp, " \t")) != NULL; cp = NULL) {
322 			if (*cp == '#')
323 				break;
324 			if ((argv = realloc(argv,
325 			    sizeof(char *) * ++argc)) == NULL) {
326 				warnx("no memory to configure ccds");
327 				return (1);
328 			}
329 			argv[argc - 1] = cp;
330 			/*
331 			 * If our action is to unconfigure all, then pass
332 			 * just the first token to do_single() and ignore
333 			 * the rest.  Since this will be encountered on
334 			 * our first pass through the line, the Right
335 			 * Thing will happen.
336 			 */
337 			if (action == CCD_UNCONFIGALL) {
338 				if (do_single(argc, argv, action))
339 					rval = 1;
340 				goto end_of_line;
341 			}
342 		}
343 		if (argc != 0)
344 			if (do_single(argc, argv, action))
345 				rval = 1;
346 
347  end_of_line:
348 		if (argv != NULL)
349 			free(argv);
350 	}
351 
352 	(void)fclose(f);
353 	return (rval);
354 }
355 
356 static int
357 checkdev(path)
358 	char *path;
359 {
360 	struct stat st;
361 
362 	if (stat(path, &st) != 0)
363 		return (errno);
364 
365 	if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
366 		return (EINVAL);
367 
368 	return (0);
369 }
370 
371 static int
372 pathtounit(path, unitp)
373 	char *path;
374 	int *unitp;
375 {
376 	struct stat st;
377 	int maxpartitions;
378 
379 	if (stat(path, &st) != 0)
380 		return (errno);
381 
382 	if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
383 		return (EINVAL);
384 
385 	if ((maxpartitions = getmaxpartitions()) < 0)
386 		return (errno);
387 
388 	*unitp = minor(st.st_rdev) / maxpartitions;
389 
390 	return (0);
391 }
392 
393 static char *
394 resolve_ccdname(name)
395 	char *name;
396 {
397 	char c, *path;
398 	size_t len, newlen;
399 	int rawpart;
400 
401 	if (name[0] == '/' || name[0] == '.') {
402 		/* Assume they gave the correct pathname. */
403 		return (strdup(name));
404 	}
405 
406 	len = strlen(name);
407 	c = name[len - 1];
408 
409 	newlen = len + 8;
410 	if ((path = malloc(newlen)) == NULL)
411 		return (NULL);
412 	bzero(path, newlen);
413 
414 	if (isdigit(c)) {
415 		if ((rawpart = getrawpartition()) < 0) {
416 			free(path);
417 			return (NULL);
418 		}
419 		(void)sprintf(path, "%s%s%c", _PATH_DEV, name, 'a' + rawpart);
420 	} else
421 		(void)sprintf(path, "%s%s", _PATH_DEV, name);
422 
423 	return (path);
424 }
425 
426 static int
427 do_io(path, cmd, cciop)
428 	char *path;
429 	u_long cmd;
430 	struct ccd_ioctl *cciop;
431 {
432 	int fd;
433 	char *cp;
434 
435 	if ((fd = open(path, O_RDWR, 0640)) < 0) {
436 		warn("open: %s", path);
437 		return (1);
438 	}
439 
440 	if (ioctl(fd, cmd, cciop) < 0) {
441 		switch (cmd) {
442 		case CCDIOCSET:
443 			cp = "CCDIOCSET";
444 			break;
445 
446 		case CCDIOCCLR:
447 			cp = "CCDIOCCLR";
448 			break;
449 
450 		case CCDCONFINFO:
451 			cp = "CCDCONFINFO";
452 			break;
453 
454 		case CCDCPPINFO:
455 			cp = "CCDCPPINFO";
456 			break;
457 
458 		default:
459 			cp = "unknown";
460 		}
461 		warn("ioctl (%s): %s", cp, path);
462 		return (1);
463 	}
464 
465 	return (0);
466 }
467 
468 static int
469 dump_ccd(argc, argv)
470 	int argc;
471 	char **argv;
472 {
473 	char *ccd, *cp;
474 	int i, error, numccd, numconfiged = 0;
475 	struct ccdconf conf;
476 
477 	/*
478 	 * Read the ccd configuration data from the kernel and dump
479 	 * it to stdout.
480 	 */
481 	if ((ccd = resolve_ccdname("ccd0")) == NULL) {		/* XXX */
482 		warnx("invalid ccd name: %s", cp);
483 		return (1);
484 	}
485 	conf.size = 0;
486 	if (do_io(ccd, CCDCONFINFO, (struct ccd_ioctl *) &conf))
487 		return (1);
488 	if (conf.size == 0) {
489 		printf("no concatenated disks configured\n");
490 		return (0);
491 	}
492 	/* Allocate space for the configuration data. */
493 	conf.buffer = alloca(conf.size);
494 	if (conf.buffer == NULL) {
495 		warnx("no memory for configuration data");
496 		return (1);
497 	}
498 	if (do_io(ccd, CCDCONFINFO, (struct ccd_ioctl *) &conf))
499 		return (1);
500 
501 	numconfiged = conf.size / sizeof(struct ccd_s);
502 
503 	if (argc == 0) {
504 		for (i = 0; i < numconfiged; i++)
505 			print_ccd_info(&(conf.buffer[i]));
506 	} else {
507 		while (argc) {
508 			cp = *argv++; --argc;
509 			if ((ccd = resolve_ccdname(cp)) == NULL) {
510 				warnx("invalid ccd name: %s", cp);
511 				continue;
512 			}
513 			if ((error = pathtounit(ccd, &numccd)) != 0) {
514 				warnx("%s: %s", ccd, strerror(error));
515 				continue;
516 			}
517 			error = 1;
518 			for (i = 0; i < numconfiged; i++) {
519 				if (conf.buffer[i].sc_unit == numccd) {
520 					print_ccd_info(&(conf.buffer[i]));
521 					error = 0;
522 					break;
523 				}
524 			}
525 			if (error) {
526 				warnx("ccd%d not configured", numccd);
527 				continue;
528 			}
529 		}
530 	}
531 
532 	return (0);
533 }
534 
535 static void
536 print_ccd_info(cs)
537 	struct ccd_s *cs;
538 {
539 	char *cp, *ccd;
540 	static int header_printed = 0;
541 	struct ccdcpps cpps;
542 
543 	/* Print out header if necessary*/
544 	if (header_printed == 0 && verbose) {
545 		printf("# ccd\t\tileave\tflags\tcompnent devices\n");
546 		header_printed = 1;
547 	}
548 
549 	/* Dump out softc information. */
550 	printf("ccd%d\t\t%d\t%d\t", cs->sc_unit, cs->sc_ileave,
551 	    cs->sc_cflags & CCDF_USERMASK);
552 	fflush(stdout);
553 
554 	/* Read in the component info. */
555 	asprintf(&cp, "%s%d", cs->device_stats.device_name,
556 	    cs->device_stats.unit_number);
557 	if (cp == NULL) {
558 		printf("\n");
559 		warn("ccd%d: can't allocate memory",
560 		    cs->sc_unit);
561 		return;
562 	}
563 
564 	if ((ccd = resolve_ccdname(cp)) == NULL) {
565 		printf("\n");
566 		warnx("can't read component info: invalid ccd name: %s", cp);
567 		return;
568 	}
569 	cpps.size = 0;
570 	if (do_io(ccd, CCDCPPINFO, (struct ccd_ioctl *) &cpps)) {
571 		printf("\n");
572 		warnx("can't read component info");
573 		return;
574 	}
575 	cpps.buffer = alloca(cpps.size);
576 	if (cpps.buffer == NULL) {
577 		printf("\n");
578 		warn("ccd%d: can't allocate memory for component info",
579 		    cs->sc_unit);
580 		return;
581 	}
582 	if (do_io(ccd, CCDCPPINFO, (struct ccd_ioctl *) &cpps)) {
583 		printf("\n");
584 		warnx("can't read component info");
585 		return;
586 	}
587 
588 	/* Display component info. */
589 	for (cp = cpps.buffer; cp - cpps.buffer < cpps.size; cp += strlen(cp) + 1) {
590 		printf((cp + strlen(cp) + 1) < (cpps.buffer + cpps.size) ?
591 		    "%s " : "%s\n", cp);
592 		fflush(stdout);
593 	}
594 	return;
595 }
596 
597 static int
598 getmaxpartitions()
599 {
600     return (MAXPARTITIONS);
601 }
602 
603 static int
604 getrawpartition()
605 {
606 	return (RAW_PART);
607 }
608 
609 static int
610 flags_to_val(flags)
611 	char *flags;
612 {
613 	char *cp, *tok;
614 	int i, tmp, val = ~CCDF_USERMASK;
615 	size_t flagslen;
616 
617 	/*
618 	 * The most common case is that of NIL flags, so check for
619 	 * those first.
620 	 */
621 	if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 ||
622 	    strcmp("0", flags) == 0)
623 		return (0);
624 
625 	flagslen = strlen(flags);
626 
627 	/* Check for values represented by strings. */
628 	if ((cp = strdup(flags)) == NULL)
629 		err(1, "no memory to parse flags");
630 	tmp = 0;
631 	for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) {
632 		for (i = 0; flagvaltab[i].fv_flag != NULL; ++i)
633 			if (strcmp(tok, flagvaltab[i].fv_flag) == 0)
634 				break;
635 		if (flagvaltab[i].fv_flag == NULL) {
636 			free(cp);
637 			goto bad_string;
638 		}
639 		tmp |= flagvaltab[i].fv_val;
640 	}
641 
642 	/* If we get here, the string was ok. */
643 	free(cp);
644 	val = tmp;
645 	goto out;
646 
647  bad_string:
648 
649 	/* Check for values represented in hex. */
650 	if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') {
651 		errno = 0;	/* to check for ERANGE */
652 		val = (int)strtol(&flags[2], &cp, 16);
653 		if ((errno == ERANGE) || (*cp != '\0'))
654 			return (-1);
655 		goto out;
656 	}
657 
658 	/* Check for values represented in decimal. */
659 	errno = 0;	/* to check for ERANGE */
660 	val = (int)strtol(flags, &cp, 10);
661 	if ((errno == ERANGE) || (*cp != '\0'))
662 		return (-1);
663 
664  out:
665 	return (((val & ~CCDF_USERMASK) == 0) ? val : -1);
666 }
667 
668 static void
669 usage()
670 {
671 	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
672 		"usage: ccdconfig [-cv] ccd ileave [flags] dev [...]",
673 		"       ccdconfig -C [-v] [-f config_file]",
674 		"       ccdconfig -u [-v] ccd [...]",
675 		"       ccdconfig -U [-v] [-f config_file]",
676 		"       ccdconfig -g [ccd [...]]");
677 	exit(1);
678 }
679 
680 /* Local Variables: */
681 /* c-argdecl-indent: 8 */
682 /* c-indent-level: 8 */
683 /* End: */
684