xref: /dragonfly/sbin/ccdconfig/ccdconfig.c (revision 4d0c54c1)
1 /*
2  * Copyright (c) 1995 Jason R. Thorpe.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed for the NetBSD Project
16  *	by Jason R. Thorpe.
17  * 4. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $NetBSD: ccdconfig.c,v 1.2.2.1 1995/11/11 02:43:35 thorpej Exp $
33  * $FreeBSD: src/sbin/ccdconfig/ccdconfig.c,v 1.16.2.2 2000/12/11 01:03:25 obrien Exp $
34  */
35 
36 #define _KERNEL_STRUCTURES
37 #include <sys/param.h>
38 #include <sys/linker.h>
39 #include <sys/stat.h>
40 #include <sys/module.h>
41 #include <ctype.h>
42 #include <err.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <kvm.h>
46 #include <limits.h>
47 #include <paths.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 
53 #include <sys/devicestat.h>
54 #include <sys/ccdvar.h>
55 
56 #include "pathnames.h"
57 
58 static	int lineno = 0;
59 static	int verbose = 0;
60 static	const char *ccdconf = _PATH_CCDCONF;
61 
62 static	char *core = NULL;
63 static	char *kernel = NULL;
64 
65 struct	flagval {
66 	const 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 static	struct nlist nl[] = {
77 	{ "_ccd_softc", 0, 0, 0, 0 },
78 #define SYM_CCDSOFTC		0
79 	{ "_numccd", 0, 0, 0, 0 },
80 #define SYM_NUMCCD		1
81 	{ NULL , 0, 0, 0, 0 },
82 };
83 
84 #define CCD_CONFIG		0	/* configure a device */
85 #define CCD_CONFIGALL		1	/* configure all devices */
86 #define CCD_UNCONFIG		2	/* unconfigure a device */
87 #define CCD_UNCONFIGALL		3	/* unconfigure all devices */
88 #define CCD_DUMP		4	/* dump a ccd's configuration */
89 
90 static	int checkdev(char *);
91 static	int do_io(char *, u_long, struct ccd_ioctl *);
92 static	int do_single(int, char **, int);
93 static	int do_all(int);
94 static	int dump_ccd(int, char **);
95 static	int getmaxpartitions(void);
96 static	int flags_to_val(char *);
97 static	void print_ccd_info(struct ccd_softc *, kvm_t *);
98 static	char *resolve_ccdname(char *);
99 static	void usage(void);
100 
101 int
102 main(int argc, char **argv)
103 {
104 	int ch, options = 0, action = CCD_CONFIG;
105 
106 	while ((ch = getopt(argc, argv, "cCf:gM:N:uUv")) != -1) {
107 		switch (ch) {
108 		case 'c':
109 			action = CCD_CONFIG;
110 			++options;
111 			break;
112 
113 		case 'C':
114 			action = CCD_CONFIGALL;
115 			++options;
116 			break;
117 
118 		case 'f':
119 			ccdconf = optarg;
120 			break;
121 
122 		case 'g':
123 			action = CCD_DUMP;
124 			break;
125 
126 		case 'M':
127 			core = optarg;
128 			break;
129 
130 		case 'N':
131 			kernel = optarg;
132 			break;
133 
134 		case 'u':
135 			action = CCD_UNCONFIG;
136 			++options;
137 			break;
138 
139 		case 'U':
140 			action = CCD_UNCONFIGALL;
141 			++options;
142 			break;
143 
144 		case 'v':
145 			verbose = 1;
146 			break;
147 
148 		default:
149 			usage();
150 		}
151 	}
152 	argc -= optind;
153 	argv += optind;
154 
155 	if (options > 1)
156 		usage();
157 
158 	/*
159 	 * Discard setgid privileges if not the running kernel so that bad
160 	 * guys can't print interesting stuff from kernel memory.
161 	 */
162 	if (core != NULL || kernel != NULL || action != CCD_DUMP) {
163 		setegid(getgid());
164 		setgid(getgid());
165 	}
166 
167 	if (modfind("ccd") < 0) {
168 		/* Not present in kernel, try loading it */
169 		if (kldload("ccd") < 0 || modfind("ccd") < 0)
170 			warn("ccd module not available!");
171 	}
172 
173 	switch (action) {
174 		case CCD_CONFIG:
175 		case CCD_UNCONFIG:
176 			exit(do_single(argc, argv, action));
177 			/* NOTREACHED */
178 
179 		case CCD_CONFIGALL:
180 		case CCD_UNCONFIGALL:
181 			exit(do_all(action));
182 			/* NOTREACHED */
183 
184 		case CCD_DUMP:
185 			exit(dump_ccd(argc, argv));
186 			/* NOTREACHED */
187 	}
188 	/* NOTREACHED */
189 	return (0);
190 }
191 
192 static int
193 do_single(int argc, char **argv, int action)
194 {
195 	struct ccd_ioctl ccio;
196 	char *ccd, *cp, *cp2, **disks;
197 	int noflags = 0, ileave, flags = 0, j;
198 	unsigned int i;
199 
200 	bzero(&ccio, sizeof(ccio));
201 
202 	/*
203 	 * If unconfiguring, all arguments are treated as ccds.
204 	 */
205 	if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) {
206 		for (i = 0; argc != 0; ) {
207 			cp = *argv++; --argc;
208 			if ((ccd = resolve_ccdname(cp)) == NULL) {
209 				warnx("invalid ccd name: %s", cp);
210 				i = 1;
211 				continue;
212 			}
213 			if (do_io(ccd, CCDIOCCLR, &ccio))
214 				i = 1;
215 			else
216 				if (verbose)
217 					printf("%s unconfigured\n", cp);
218 		}
219 		return (i);
220 	}
221 
222 	/* Make sure there are enough arguments. */
223 	if (argc < 4) {
224 		if (argc == 3) {
225 			/* Assume that no flags are specified. */
226 			noflags = 1;
227 		} else {
228 			if (action == CCD_CONFIGALL) {
229 				warnx("%s: bad line: %d", ccdconf, lineno);
230 				return (1);
231 			} else
232 				usage();
233 		}
234 	}
235 
236 	/* First argument is the ccd to configure. */
237 	cp = *argv++; --argc;
238 	if ((ccd = resolve_ccdname(cp)) == NULL) {
239 		warnx("invalid ccd name: %s", cp);
240 		return (1);
241 	}
242 
243 	/* Next argument is the interleave factor. */
244 	cp = *argv++; --argc;
245 	errno = 0;	/* to check for ERANGE */
246 	ileave = (int)strtol(cp, &cp2, 10);
247 	if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) {
248 		warnx("invalid interleave factor: %s", cp);
249 		return (1);
250 	}
251 
252 	if (noflags == 0) {
253 		/* Next argument is the ccd configuration flags. */
254 		cp = *argv++; --argc;
255 		if ((flags = flags_to_val(cp)) < 0) {
256 			warnx("invalid flags argument: %s", cp);
257 			return (1);
258 		}
259 	}
260 
261 	/* Next is the list of disks to make the ccd from. */
262 	disks = malloc(argc * sizeof(char *));
263 	if (disks == NULL) {
264 		warnx("no memory to configure ccd");
265 		return (1);
266 	}
267 	for (i = 0; argc != 0; ) {
268 		cp = *argv++; --argc;
269 		if ((j = checkdev(cp)) == 0)
270 			disks[i++] = cp;
271 		else {
272 			warnx("%s: %s", cp, strerror(j));
273 			return (1);
274 		}
275 	}
276 
277 	/* Fill in the ccio. */
278 	ccio.ccio_disks = disks;
279 	ccio.ccio_ndisks = i;
280 	ccio.ccio_ileave = ileave;
281 	ccio.ccio_flags = flags;
282 
283 	if (do_io(ccd, CCDIOCSET, &ccio)) {
284 		free(disks);
285 		return (1);
286 	}
287 
288 	if (verbose) {
289 		printf("ccd%d: %d components ", ccio.ccio_unit,
290 		    ccio.ccio_ndisks);
291 		for (i = 0; i < ccio.ccio_ndisks; ++i) {
292 			if ((cp2 = strrchr(disks[i], '/')) != NULL)
293 				++cp2;
294 			else
295 				cp2 = disks[i];
296 			printf("%c%s%c",
297 			    i == 0 ? '(' : ' ', cp2,
298 			    i == ccio.ccio_ndisks - 1 ? ')' : ',');
299 		}
300 		printf(", %ju blocks ", (uintmax_t)ccio.ccio_size);
301 		if (ccio.ccio_ileave != 0)
302 			printf("interleaved at %d blocks\n", ccio.ccio_ileave);
303 		else
304 			printf("concatenated\n");
305 	}
306 
307 	free(disks);
308 	return (0);
309 }
310 
311 static int
312 do_all(int action)
313 {
314 	FILE *f;
315 	char line[_POSIX2_LINE_MAX];
316 	char *cp, **argv;
317 	int argc, rval;
318 	gid_t egid;
319 
320 	rval = 0;
321 	egid = getegid();
322 	setegid(getgid());
323 	if ((f = fopen(ccdconf, "r")) == NULL) {
324 		setegid(egid);
325 		warn("fopen: %s", ccdconf);
326 		return (1);
327 	}
328 	setegid(egid);
329 
330 	while (fgets(line, sizeof(line), f) != NULL) {
331 		argc = 0;
332 		argv = NULL;
333 		++lineno;
334 		if ((cp = strrchr(line, '\n')) != NULL)
335 			*cp = '\0';
336 
337 		/* Break up the line and pass it's contents to do_single(). */
338 		if (line[0] == '\0')
339 			goto end_of_line;
340 		for (cp = line; (cp = strtok(cp, " \t")) != NULL; cp = NULL) {
341 			if (*cp == '#')
342 				break;
343 			if ((argv = realloc(argv,
344 			    sizeof(char *) * ++argc)) == NULL) {
345 				warnx("no memory to configure ccds");
346 				return (1);
347 			}
348 			argv[argc - 1] = cp;
349 			/*
350 			 * If our action is to unconfigure all, then pass
351 			 * just the first token to do_single() and ignore
352 			 * the rest.  Since this will be encountered on
353 			 * our first pass through the line, the Right
354 			 * Thing will happen.
355 			 */
356 			if (action == CCD_UNCONFIGALL) {
357 				if (do_single(argc, argv, action))
358 					rval = 1;
359 				goto end_of_line;
360 			}
361 		}
362 		if (argc != 0)
363 			if (do_single(argc, argv, action))
364 				rval = 1;
365 
366  end_of_line:
367 		if (argv != NULL)
368 			free(argv);
369 	}
370 
371 	fclose(f);
372 	return (rval);
373 }
374 
375 static int
376 checkdev(char *path)
377 {
378 	struct stat st;
379 
380 	if (stat(path, &st) != 0)
381 		return (errno);
382 
383 	if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
384 		return (EINVAL);
385 
386 	return (0);
387 }
388 
389 static int
390 pathtounit(char *path, int *unitp)
391 {
392 	struct stat st;
393 	int maxpartitions;
394 
395 	if (stat(path, &st) != 0)
396 		return (errno);
397 
398 	if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
399 		return (EINVAL);
400 
401 	if ((maxpartitions = getmaxpartitions()) < 0)
402 		return (errno);
403 
404 	*unitp = minor(st.st_rdev) / maxpartitions;
405 
406 	return (0);
407 }
408 
409 static char *
410 resolve_ccdname(char *name)
411 {
412 	char *path;
413 
414 	if (name[0] == '/' || name[0] == '.') {
415 		/* Assume they gave the correct pathname. */
416 		return (strdup(name));
417 	}
418 
419 	asprintf(&path, "%s%s", _PATH_DEV, name);
420 
421 	return (path);
422 }
423 
424 static int
425 do_io(char *path, u_long cmd, struct ccd_ioctl *cciop)
426 {
427 	int fd;
428 	const char *cp;
429 
430 	if ((fd = open(path, O_RDWR, 0640)) < 0) {
431 		warn("open: %s", path);
432 		return (1);
433 	}
434 
435 	if (ioctl(fd, cmd, cciop) < 0) {
436 		switch (cmd) {
437 		case CCDIOCSET:
438 			cp = "CCDIOCSET";
439 			break;
440 
441 		case CCDIOCCLR:
442 			cp = "CCDIOCCLR";
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 #define KVM_ABORT(kd, str) {						\
456 	kvm_close((kd));						\
457 	warnx("%s", (str));							\
458 	warnx("%s", kvm_geterr((kd)));					\
459 	return (1);							\
460 }
461 
462 static int
463 dump_ccd(int argc, char **argv)
464 {
465 	char errbuf[_POSIX2_LINE_MAX], *ccd, *cp;
466 	struct ccd_softc *cs, *kcs;
467 	ssize_t readsize;
468 	int i = 0, error, numccd, numconfiged = 0;
469 	kvm_t *kd;
470 
471 	bzero(errbuf, sizeof(errbuf));
472 
473 	if ((kd = kvm_openfiles(kernel, core, NULL, O_RDONLY,
474 	    errbuf)) == NULL) {
475 		warnx("can't open kvm: %s", errbuf);
476 		return (1);
477 	}
478 
479 	if (kvm_nlist(kd, nl))
480 		KVM_ABORT(kd, "ccd-related symbols not available");
481 
482 	/* Check to see how many ccds are currently configured. */
483 	if (kvm_read(kd, nl[SYM_NUMCCD].n_value, (char *)&numccd,
484 	    sizeof(numccd)) != sizeof(numccd))
485 		KVM_ABORT(kd, "can't determine number of configured ccds");
486 
487 	if (numccd == 0) {
488 		printf("ccd driver in kernel, but is uninitialized\n");
489 		goto done;
490 	}
491 
492 	/* Allocate space for the configuration data. */
493 	readsize = numccd * sizeof(struct ccd_softc);
494 	if ((cs = malloc(readsize)) == NULL) {
495 		warnx("no memory for configuration data");
496 		goto bad;
497 	}
498 	bzero(cs, readsize);
499 
500 	/*
501 	 * Read the ccd configuration data from the kernel and dump
502 	 * it to stdout.
503 	 */
504 	if (kvm_read(kd, nl[SYM_CCDSOFTC].n_value, (char *)&kcs,
505 	    sizeof(kcs)) != sizeof(kcs)) {
506 		free(cs);
507 		KVM_ABORT(kd, "can't find pointer to configuration data");
508 	}
509 	if (kvm_read(kd, (u_long)kcs, (char *)cs, readsize) != readsize) {
510 		free(cs);
511 		KVM_ABORT(kd, "can't read configuration data");
512 	}
513 
514 	if (argc == 0) {
515 		for (i = 0; i < numccd; ++i)
516 			if (cs[i].sc_flags & CCDF_INITED) {
517 				++numconfiged;
518 				print_ccd_info(&cs[i], kd);
519 			}
520 
521 		if (numconfiged == 0)
522 			printf("no concatenated disks configured\n");
523 	} else {
524 		while (argc) {
525 			cp = *argv++; --argc;
526 			if ((ccd = resolve_ccdname(cp)) == NULL) {
527 				warnx("invalid ccd name: %s", cp);
528 				continue;
529 			}
530 			if ((error = pathtounit(ccd, &i)) != 0) {
531 				warnx("%s: %s", ccd, strerror(error));
532 				continue;
533 			}
534 			if (i >= numccd) {
535 				warnx("ccd%d not configured", i);
536 				continue;
537 			}
538 			if (cs[i].sc_flags & CCDF_INITED)
539 				print_ccd_info(&cs[i], kd);
540 			else
541 				printf("ccd%d not configured\n", i);
542 		}
543 	}
544 
545 	free(cs);
546 
547  done:
548 	kvm_close(kd);
549 	return (0);
550 
551  bad:
552 	kvm_close(kd);
553 	return (1);
554 }
555 
556 static void
557 print_ccd_info(struct ccd_softc *cs, kvm_t *kd)
558 {
559 	static int header_printed = 0;
560 	struct ccdcinfo *cip;
561 	ssize_t readsize;
562 	char path[MAXPATHLEN];
563 	unsigned int i;
564 
565 	if (header_printed == 0 && verbose) {
566 		printf("# ccd\t\tileave\tflags\tcompnent devices\n");
567 		header_printed = 1;
568 	}
569 
570 	readsize = cs->sc_nccdisks * sizeof(struct ccdcinfo);
571 	if ((cip = malloc(readsize)) == NULL) {
572 		warn("ccd%d: can't allocate memory for component info",
573 		    cs->sc_unit);
574 		return;
575 	}
576 	bzero(cip, readsize);
577 
578 	/* Dump out softc information. */
579 	printf("ccd%d\t\t%d\t%d\t", cs->sc_unit, cs->sc_ileave,
580 	    cs->sc_cflags & CCDF_USERMASK);
581 	fflush(stdout);
582 
583 	/* Read in the component info. */
584 	if (kvm_read(kd, (u_long)cs->sc_cinfo, (char *)cip,
585 	    readsize) != readsize) {
586 		printf("\n");
587 		warnx("can't read component info");
588 		warnx("%s", kvm_geterr(kd));
589 		goto done;
590 	}
591 
592 	/* Read component pathname and display component info. */
593 	for (i = 0; i < cs->sc_nccdisks; ++i) {
594 		if ((unsigned)kvm_read(kd, (u_long)cip[i].ci_path, (char *)path,
595 		    cip[i].ci_pathlen) != cip[i].ci_pathlen) {
596 			printf("\n");
597 			warnx("can't read component pathname");
598 			warnx("%s", kvm_geterr(kd));
599 			goto done;
600 		}
601 		printf((i + 1 < cs->sc_nccdisks) ? "%s " : "%s\n", path);
602 		fflush(stdout);
603 	}
604 
605  done:
606 	free(cip);
607 }
608 
609 static int
610 getmaxpartitions(void)
611 {
612     return (MAXPARTITIONS);
613 }
614 
615 static int
616 flags_to_val(char *flags)
617 {
618 	char *cp, *tok;
619 	int i, tmp, val = ~CCDF_USERMASK;
620 	size_t flagslen;
621 
622 	/*
623 	 * The most common case is that of NIL flags, so check for
624 	 * those first.
625 	 */
626 	if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 ||
627 	    strcmp("0", flags) == 0)
628 		return (0);
629 
630 	flagslen = strlen(flags);
631 
632 	/* Check for values represented by strings. */
633 	if ((cp = strdup(flags)) == NULL)
634 		err(1, "no memory to parse flags");
635 	tmp = 0;
636 	for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) {
637 		for (i = 0; flagvaltab[i].fv_flag != NULL; ++i)
638 			if (strcmp(tok, flagvaltab[i].fv_flag) == 0)
639 				break;
640 		if (flagvaltab[i].fv_flag == NULL) {
641 			free(cp);
642 			goto bad_string;
643 		}
644 		tmp |= flagvaltab[i].fv_val;
645 	}
646 
647 	/* If we get here, the string was ok. */
648 	free(cp);
649 	val = tmp;
650 	goto out;
651 
652  bad_string:
653 
654 	/* Check for values represented in hex. */
655 	if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') {
656 		errno = 0;	/* to check for ERANGE */
657 		val = (int)strtol(&flags[2], &cp, 16);
658 		if ((errno == ERANGE) || (*cp != '\0'))
659 			return (-1);
660 		goto out;
661 	}
662 
663 	/* Check for values represented in decimal. */
664 	errno = 0;	/* to check for ERANGE */
665 	val = (int)strtol(flags, &cp, 10);
666 	if ((errno == ERANGE) || (*cp != '\0'))
667 		return (-1);
668 
669  out:
670 	return (((val & ~CCDF_USERMASK) == 0) ? val : -1);
671 }
672 
673 static void
674 usage(void)
675 {
676 	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
677 		"usage: ccdconfig [-cv] ccd ileave [flags] dev ...",
678 		"       ccdconfig -C [-v] [-f config_file]",
679 		"       ccdconfig -u [-v] ccd ...",
680 		"       ccdconfig -U [-v] [-f config_file]",
681 		"       ccdconfig -g [-M core] [-N system] [ccd ...]");
682 	exit(1);
683 }
684 
685 /* Local Variables: */
686 /* c-argdecl-indent: 8 */
687 /* c-indent-level: 8 */
688 /* End: */
689