xref: /freebsd/sbin/ccdconfig/ccdconfig.c (revision 3ff369fed2a08f32dda232c10470b949bef9489f)
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(char *);
83 static	int do_io(char *, u_long, struct ccd_ioctl *);
84 static	int do_single(int, char **, int);
85 static	int do_all(int);
86 static	int dump_ccd(int, char **);
87 static	int getmaxpartitions(void);
88 static	int getrawpartition(void);
89 static	int flags_to_val(char *);
90 static	void print_ccd_info(struct ccd_s *);
91 static	char *resolve_ccdname(char *);
92 static	void usage(void);
93 
94 int
95 main(int argc, char *argv[])
96 {
97 	int ch, options = 0, action = CCD_CONFIG;
98 
99 	while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) {
100 		switch (ch) {
101 		case 'c':
102 			action = CCD_CONFIG;
103 			++options;
104 			break;
105 
106 		case 'C':
107 			action = CCD_CONFIGALL;
108 			++options;
109 			break;
110 
111 		case 'f':
112 			ccdconf = optarg;
113 			break;
114 
115 		case 'g':
116 			action = CCD_DUMP;
117 			break;
118 
119 		case 'u':
120 			action = CCD_UNCONFIG;
121 			++options;
122 			break;
123 
124 		case 'U':
125 			action = CCD_UNCONFIGALL;
126 			++options;
127 			break;
128 
129 		case 'v':
130 			verbose = 1;
131 			break;
132 
133 		default:
134 			usage();
135 		}
136 	}
137 	argc -= optind;
138 	argv += optind;
139 
140 	if (options > 1)
141 		usage();
142 
143 	if (modfind("ccd") < 0) {
144 		/* Not present in kernel, try loading it */
145 		if (kldload("ccd") < 0 || modfind("ccd") < 0)
146 			warn("ccd module not available!");
147 	}
148 
149 	switch (action) {
150 		case CCD_CONFIG:
151 		case CCD_UNCONFIG:
152 			exit(do_single(argc, argv, action));
153 			/* NOTREACHED */
154 
155 		case CCD_CONFIGALL:
156 		case CCD_UNCONFIGALL:
157 			exit(do_all(action));
158 			/* NOTREACHED */
159 
160 		case CCD_DUMP:
161 			exit(dump_ccd(argc, argv));
162 			/* NOTREACHED */
163 	}
164 	/* NOTREACHED */
165 	return (0);
166 }
167 
168 static int
169 do_single(int argc, char **argv, int action)
170 {
171 	struct ccd_ioctl ccio;
172 	char *ccd, *cp, *cp2, **disks;
173 	int noflags = 0, i, ileave, flags = 0, j;
174 
175 	bzero(&ccio, sizeof(ccio));
176 
177 	/*
178 	 * If unconfiguring, all arguments are treated as ccds.
179 	 */
180 	if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) {
181 		for (i = 0; argc != 0; ) {
182 			cp = *argv++; --argc;
183 			if ((ccd = resolve_ccdname(cp)) == NULL) {
184 				warnx("invalid ccd name: %s", cp);
185 				i = 1;
186 				continue;
187 			}
188 			if (do_io(ccd, CCDIOCCLR, &ccio))
189 				i = 1;
190 			else
191 				if (verbose)
192 					printf("%s unconfigured\n", cp);
193 		}
194 		return (i);
195 	}
196 
197 	/* Make sure there are enough arguments. */
198 	if (argc < 4) {
199 		if (argc == 3) {
200 			/* Assume that no flags are specified. */
201 			noflags = 1;
202 		} else {
203 			if (action == CCD_CONFIGALL) {
204 				warnx("%s: bad line: %d", ccdconf, lineno);
205 				return (1);
206 			} else
207 				usage();
208 		}
209 	}
210 
211 	/* First argument is the ccd to configure. */
212 	cp = *argv++; --argc;
213 	if ((ccd = resolve_ccdname(cp)) == NULL) {
214 		warnx("invalid ccd name: %s", cp);
215 		return (1);
216 	}
217 
218 	/* Next argument is the interleave factor. */
219 	cp = *argv++; --argc;
220 	errno = 0;	/* to check for ERANGE */
221 	ileave = (int)strtol(cp, &cp2, 10);
222 	if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) {
223 		warnx("invalid interleave factor: %s", cp);
224 		return (1);
225 	}
226 
227 	if (noflags == 0) {
228 		/* Next argument is the ccd configuration flags. */
229 		cp = *argv++; --argc;
230 		if ((flags = flags_to_val(cp)) < 0) {
231 			warnx("invalid flags argument: %s", cp);
232 			return (1);
233 		}
234 	}
235 
236 	/* Next is the list of disks to make the ccd from. */
237 	disks = malloc(argc * sizeof(char *));
238 	if (disks == NULL) {
239 		warnx("no memory to configure ccd");
240 		return (1);
241 	}
242 	for (i = 0; argc != 0; ) {
243 		cp = *argv++; --argc;
244 		if ((j = checkdev(cp)) == 0)
245 			disks[i++] = cp;
246 		else {
247 			warnx("%s: %s", cp, strerror(j));
248 			return (1);
249 		}
250 	}
251 
252 	/* Fill in the ccio. */
253 	ccio.ccio_disks = disks;
254 	ccio.ccio_ndisks = i;
255 	ccio.ccio_ileave = ileave;
256 	ccio.ccio_flags = flags;
257 
258 	if (do_io(ccd, CCDIOCSET, &ccio)) {
259 		free(disks);
260 		return (1);
261 	}
262 
263 	if (verbose) {
264 		printf("ccd%d: %d components ", ccio.ccio_unit,
265 		    ccio.ccio_ndisks);
266 		for (i = 0; i < ccio.ccio_ndisks; ++i) {
267 			if ((cp2 = strrchr(disks[i], '/')) != NULL)
268 				++cp2;
269 			else
270 				cp2 = disks[i];
271 			printf("%c%s%c",
272 			    i == 0 ? '(' : ' ', cp2,
273 			    i == ccio.ccio_ndisks - 1 ? ')' : ',');
274 		}
275 		printf(", %lu blocks ", (u_long)ccio.ccio_size);
276 		if (ccio.ccio_ileave != 0)
277 			printf("interleaved at %d blocks\n", ccio.ccio_ileave);
278 		else
279 			printf("concatenated\n");
280 	}
281 
282 	free(disks);
283 	return (0);
284 }
285 
286 static int
287 do_all(int action)
288 {
289 	FILE *f;
290 	char line[_POSIX2_LINE_MAX];
291 	char *cp, **argv;
292 	int argc, rval;
293 	gid_t egid;
294 
295 	rval = 0;
296 	egid = getegid();
297 	setegid(getgid());
298 	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 
350 static int
351 checkdev(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 
364 static int
365 pathtounit(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 
384 static char *
385 resolve_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 
416 static int
417 do_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 
455 static int
456 dump_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 
520 static void
521 print_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 
581 static int
582 getmaxpartitions(void)
583 {
584     return (MAXPARTITIONS);
585 }
586 
587 static int
588 getrawpartition(void)
589 {
590 	return (RAW_PART);
591 }
592 
593 static int
594 flags_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 
651 static void
652 usage(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