xref: /freebsd/sbin/ccdconfig/ccdconfig.c (revision 6af83ee0d2941d18880b6aaa2b4facd1d30c6106)
1 /*
2  * Copyright (c) 2003 Poul-Henning Kamp
3  * Copyright (c) 1995 Jason R. Thorpe.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *	This product includes software developed for the NetBSD Project
17  *	by Jason R. Thorpe.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36 
37 #include <sys/param.h>
38 #include <sys/linker.h>
39 #include <sys/module.h>
40 #include <ctype.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <limits.h>
44 #include <paths.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <libgeom.h>
50 
51 #define CCDF_UNIFORM    0x02    /* use LCCD of sizes for uniform interleave */
52 #define CCDF_MIRROR     0x04    /* use mirroring */
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 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 	{ "none",		0 },
69 	{ NULL,			0 },
70 };
71 
72 #define CCD_CONFIG		0	/* configure a device */
73 #define CCD_CONFIGALL		1	/* configure all devices */
74 #define CCD_UNCONFIG		2	/* unconfigure a device */
75 #define CCD_UNCONFIGALL		3	/* unconfigure all devices */
76 #define CCD_DUMP		4	/* dump a ccd's configuration */
77 
78 static	int do_single(int, char **, int);
79 static	int do_all(int);
80 static	int dump_ccd(int, char **);
81 static	int flags_to_val(char *);
82 static	int resolve_ccdname(char *);
83 static	void usage(void);
84 
85 int
86 main(int argc, char *argv[])
87 {
88 	int ch, options = 0, action = CCD_CONFIG;
89 
90 	while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) {
91 		switch (ch) {
92 		case 'c':
93 			action = CCD_CONFIG;
94 			++options;
95 			break;
96 
97 		case 'C':
98 			action = CCD_CONFIGALL;
99 			++options;
100 			break;
101 
102 		case 'f':
103 			ccdconf = optarg;
104 			break;
105 
106 		case 'g':
107 			action = CCD_DUMP;
108 			break;
109 
110 		case 'u':
111 			action = CCD_UNCONFIG;
112 			++options;
113 			break;
114 
115 		case 'U':
116 			action = CCD_UNCONFIGALL;
117 			++options;
118 			break;
119 
120 		case 'v':
121 			verbose = 1;
122 			break;
123 
124 		default:
125 			usage();
126 		}
127 	}
128 	argc -= optind;
129 	argv += optind;
130 
131 	if (options > 1)
132 		usage();
133 
134 	if (modfind("g_ccd") < 0) {
135 		/* Not present in kernel, try loading it */
136 		if (kldload("geom_ccd") < 0 || modfind("g_ccd") < 0)
137 			warn("geom_ccd module not available!");
138 	}
139 
140 	switch (action) {
141 		case CCD_CONFIG:
142 		case CCD_UNCONFIG:
143 			exit(do_single(argc, argv, action));
144 			/* NOTREACHED */
145 
146 		case CCD_CONFIGALL:
147 		case CCD_UNCONFIGALL:
148 			exit(do_all(action));
149 			/* NOTREACHED */
150 
151 		case CCD_DUMP:
152 			exit(dump_ccd(argc, argv));
153 			/* NOTREACHED */
154 	}
155 	/* NOTREACHED */
156 	return (0);
157 }
158 
159 static int
160 do_single(int argc, char **argv, int action)
161 {
162 	char *cp, *cp2;
163 	int ccd, noflags = 0, i, ileave, flags = 0;
164 	struct gctl_req *grq;
165 	char const *errstr;
166 	char buf1[BUFSIZ];
167 	int ex;
168 
169 	/*
170 	 * If unconfiguring, all arguments are treated as ccds.
171 	 */
172 	if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) {
173 		ex = 0;
174 		for (i = 0; argc != 0; ) {
175 			cp = *argv++; --argc;
176 			if ((ccd = resolve_ccdname(cp)) < 0) {
177 				warnx("invalid ccd name: %s", cp);
178 				i = 1;
179 				continue;
180 			}
181 			grq = gctl_get_handle();
182 			gctl_ro_param(grq, "verb", -1, "destroy geom");
183 			gctl_ro_param(grq, "class", -1, "CCD");
184 			sprintf(buf1, "ccd%d", ccd);
185 			gctl_ro_param(grq, "geom", -1, buf1);
186 			errstr = gctl_issue(grq);
187 			if (errstr == NULL) {
188 				if (verbose)
189 					printf("%s unconfigured\n", cp);
190 				gctl_free(grq);
191 				continue;
192 			}
193 			warnx(
194 			    "%s\nor possibly kernel and ccdconfig out of sync",
195 			    errstr);
196 			ex = 1;
197 		}
198 		return (ex);
199 	}
200 
201 	/* Make sure there are enough arguments. */
202 	if (argc < 4) {
203 		if (argc == 3) {
204 			/* Assume that no flags are specified. */
205 			noflags = 1;
206 		} else {
207 			if (action == CCD_CONFIGALL) {
208 				warnx("%s: bad line: %d", ccdconf, lineno);
209 				return (1);
210 			} else
211 				usage();
212 		}
213 	}
214 
215 	/* First argument is the ccd to configure. */
216 	cp = *argv++; --argc;
217 	if ((ccd = resolve_ccdname(cp)) < 0) {
218 		warnx("invalid ccd name: %s", cp);
219 		return (1);
220 	}
221 
222 	/* Next argument is the interleave factor. */
223 	cp = *argv++; --argc;
224 	errno = 0;	/* to check for ERANGE */
225 	ileave = (int)strtol(cp, &cp2, 10);
226 	if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) {
227 		warnx("invalid interleave factor: %s", cp);
228 		return (1);
229 	}
230 
231 	if (noflags == 0) {
232 		/* Next argument is the ccd configuration flags. */
233 		cp = *argv++; --argc;
234 		if ((flags = flags_to_val(cp)) < 0) {
235 			warnx("invalid flags argument: %s", cp);
236 			return (1);
237 		}
238 	}
239 	grq = gctl_get_handle();
240 	gctl_ro_param(grq, "verb", -1, "create geom");
241 	gctl_ro_param(grq, "class", -1, "CCD");
242 	gctl_ro_param(grq, "unit", sizeof(ccd), &ccd);
243 	gctl_ro_param(grq, "ileave", sizeof(ileave), &ileave);
244 	if (flags & CCDF_UNIFORM)
245 		gctl_ro_param(grq, "uniform", -1, "");
246 	if (flags & CCDF_MIRROR)
247 		gctl_ro_param(grq, "mirror", -1, "");
248 	gctl_ro_param(grq, "nprovider", sizeof(argc), &argc);
249 	for (i = 0; i < argc; i++) {
250 		sprintf(buf1, "provider%d", i);
251 		cp = argv[i];
252 		if (!strncmp(cp, _PATH_DEV, strlen(_PATH_DEV)))
253 			cp += strlen(_PATH_DEV);
254 		gctl_ro_param(grq, buf1, -1, cp);
255 	}
256 	gctl_rw_param(grq, "output", sizeof(buf1), buf1);
257 	errstr = gctl_issue(grq);
258 	if (errstr == NULL) {
259 		if (verbose) {
260 			printf("%s", buf1);
261 		}
262 		gctl_free(grq);
263 		return (0);
264 	}
265 	warnx(
266 	    "%s\nor possibly kernel and ccdconfig out of sync",
267 	    errstr);
268 	return (1);
269 }
270 
271 static int
272 do_all(int action)
273 {
274 	FILE *f;
275 	char line[_POSIX2_LINE_MAX];
276 	char *cp, **argv;
277 	int argc, rval;
278 	gid_t egid;
279 
280 	rval = 0;
281 	egid = getegid();
282 	setegid(getgid());
283 	if ((f = fopen(ccdconf, "r")) == NULL) {
284 		setegid(egid);
285 		warn("fopen: %s", ccdconf);
286 		return (1);
287 	}
288 	setegid(egid);
289 
290 	while (fgets(line, sizeof(line), f) != NULL) {
291 		argc = 0;
292 		argv = NULL;
293 		++lineno;
294 		if ((cp = strrchr(line, '\n')) != NULL)
295 			*cp = '\0';
296 
297 		/* Break up the line and pass it's contents to do_single(). */
298 		if (line[0] == '\0')
299 			goto end_of_line;
300 		for (cp = line; (cp = strtok(cp, " \t")) != NULL; cp = NULL) {
301 			if (*cp == '#')
302 				break;
303 			if ((argv = realloc(argv,
304 			    sizeof(char *) * ++argc)) == NULL) {
305 				warnx("no memory to configure ccds");
306 				return (1);
307 			}
308 			argv[argc - 1] = cp;
309 			/*
310 			 * If our action is to unconfigure all, then pass
311 			 * just the first token to do_single() and ignore
312 			 * the rest.  Since this will be encountered on
313 			 * our first pass through the line, the Right
314 			 * Thing will happen.
315 			 */
316 			if (action == CCD_UNCONFIGALL) {
317 				if (do_single(argc, argv, action))
318 					rval = 1;
319 				goto end_of_line;
320 			}
321 		}
322 		if (argc != 0)
323 			if (do_single(argc, argv, action))
324 				rval = 1;
325 
326  end_of_line:
327 		if (argv != NULL)
328 			free(argv);
329 	}
330 
331 	(void)fclose(f);
332 	return (rval);
333 }
334 
335 static int
336 resolve_ccdname(char *name)
337 {
338 
339 	if (!strncmp(name, _PATH_DEV, strlen(_PATH_DEV)))
340 		name += strlen(_PATH_DEV);
341 	if (strncmp(name, "ccd", 3))
342 		return -1;
343 	name += 3;
344 	if (!isdigit(*name))
345 		return -1;
346 	return (strtoul(name, NULL, 10));
347 }
348 
349 static int
350 dumpout(int unit)
351 {
352 	static int v;
353 	struct gctl_req *grq;
354 	int ncp;
355 	char *cp;
356 	char const *errstr;
357 
358 	grq = gctl_get_handle();
359 	ncp = 65536;
360 	cp = malloc(ncp);
361 	gctl_ro_param(grq, "verb", -1, "list");
362 	gctl_ro_param(grq, "class", -1, "CCD");
363 	gctl_ro_param(grq, "unit", sizeof(unit), &unit);
364 	gctl_rw_param(grq, "output", ncp, cp);
365 	errstr = gctl_issue(grq);
366 	if (errstr != NULL)
367 		errx(1, "%s\nor possibly kernel and ccdconfig out of sync",
368 			errstr);
369 	if (strlen(cp) == 0)
370 		errx(1, "ccd%d not configured", unit);
371 	if (verbose && !v) {
372 		printf("# ccd\t\tileave\tflags\tcomponent devices\n");
373 		v = 1;
374 	}
375 	printf("%s", cp);
376 	free(cp);
377 	return (0);
378 }
379 
380 static int
381 dump_ccd(int argc, char **argv)
382 {
383 	int i, error;
384 
385 	if (argc == 0) {
386 		error = dumpout(-1);
387 	} else {
388 		error = 0;
389 		for (i = 0; error == 0 && i < argc; i++)
390 			error = dumpout(resolve_ccdname(argv[i]));
391 	}
392 	return (error);
393 }
394 
395 static int
396 flags_to_val(char *flags)
397 {
398 	char *cp, *tok;
399 	int i, tmp, val;
400 	size_t flagslen;
401 
402 	errno = 0;	/* to check for ERANGE */
403 	val = (int)strtol(flags, &cp, 0);
404 	if ((errno != ERANGE) && (*cp == '\0')) {
405 		if (val & ~(CCDF_UNIFORM|CCDF_MIRROR))
406 			return (-1);
407 		return (val);
408 	}
409 
410 	flagslen = strlen(flags);
411 	/* Check for values represented by strings. */
412 	if ((cp = strdup(flags)) == NULL)
413 		err(1, "no memory to parse flags");
414 	tmp = 0;
415 	for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) {
416 		for (i = 0; flagvaltab[i].fv_flag != NULL; ++i)
417 			if (strcmp(tok, flagvaltab[i].fv_flag) == 0)
418 				break;
419 		if (flagvaltab[i].fv_flag == NULL) {
420 			free(cp);
421 			return (-1);
422 		}
423 		tmp |= flagvaltab[i].fv_val;
424 	}
425 
426 	/* If we get here, the string was ok. */
427 	free(cp);
428 	return (tmp);
429 }
430 
431 static void
432 usage(void)
433 {
434 	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
435 		"usage: ccdconfig [-cv] ccd ileave [flags] dev ...",
436 		"       ccdconfig -C [-v] [-f config_file]",
437 		"       ccdconfig -u [-v] ccd ...",
438 		"       ccdconfig -U [-v] [-f config_file]",
439 		"       ccdconfig -g [ccd ...]");
440 	exit(1);
441 }
442