ccdconfig.c revision 141611
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: head/sbin/ccdconfig/ccdconfig.c 141611 2005-02-10 09:19:34Z ru $");
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
56static	int lineno = 0;
57static	int verbose = 0;
58static	const char *ccdconf = _PATH_CCDCONF;
59
60struct	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
78static	int do_single(int, char **, int);
79static	int do_all(int);
80static	int dump_ccd(int, char **);
81static	int flags_to_val(char *);
82static	int resolve_ccdname(char *);
83static	void usage(void);
84
85int
86main(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
159static int
160do_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
271static int
272do_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
335static int
336resolve_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
349static int
350dumpout(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
380static int
381dump_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
395static int
396flags_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
431static void
432usage(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