ccdconfig.c revision 157740
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 157740 2006-04-13 20:35:31Z cracauer $");
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#define CCDF_NO_OFFSET  0x08    /* do not leave space in front */
54#define CCDF_LINUX      0x10    /* use Linux compatibility mode */
55
56#include "pathnames.h"
57
58static	int lineno = 0;
59static	int verbose = 0;
60static	const char *ccdconf = _PATH_CCDCONF;
61
62struct	flagval {
63	const char	*fv_flag;
64	int		fv_val;
65} flagvaltab[] = {
66	{ "CCDF_UNIFORM",	CCDF_UNIFORM },
67	{ "uniform",		CCDF_UNIFORM },
68	{ "CCDF_MIRROR",	CCDF_MIRROR },
69	{ "mirror",		CCDF_MIRROR },
70	{ "CCDF_NO_OFFSET",	CCDF_NO_OFFSET },
71	{ "no_offset",		CCDF_NO_OFFSET },
72	{ "CCDF_LINUX",		CCDF_LINUX },
73	{ "linux",		CCDF_LINUX },
74	{ "none",		0 },
75	{ NULL,			0 },
76};
77
78#define CCD_CONFIG		0	/* configure a device */
79#define CCD_CONFIGALL		1	/* configure all devices */
80#define CCD_UNCONFIG		2	/* unconfigure a device */
81#define CCD_UNCONFIGALL		3	/* unconfigure all devices */
82#define CCD_DUMP		4	/* dump a ccd's configuration */
83
84static	int do_single(int, char **, int);
85static	int do_all(int);
86static	int dump_ccd(int, char **);
87static	int flags_to_val(char *);
88static	int resolve_ccdname(char *);
89static	void usage(void);
90
91int
92main(int argc, char *argv[])
93{
94	int ch, options = 0, action = CCD_CONFIG;
95
96	while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) {
97		switch (ch) {
98		case 'c':
99			action = CCD_CONFIG;
100			++options;
101			break;
102
103		case 'C':
104			action = CCD_CONFIGALL;
105			++options;
106			break;
107
108		case 'f':
109			ccdconf = optarg;
110			break;
111
112		case 'g':
113			action = CCD_DUMP;
114			break;
115
116		case 'u':
117			action = CCD_UNCONFIG;
118			++options;
119			break;
120
121		case 'U':
122			action = CCD_UNCONFIGALL;
123			++options;
124			break;
125
126		case 'v':
127			verbose = 1;
128			break;
129
130		default:
131			usage();
132		}
133	}
134	argc -= optind;
135	argv += optind;
136
137	if (options > 1)
138		usage();
139
140	if (modfind("g_ccd") < 0) {
141		/* Not present in kernel, try loading it */
142		if (kldload("geom_ccd") < 0 || modfind("g_ccd") < 0)
143			warn("geom_ccd module not available!");
144	}
145
146	switch (action) {
147		case CCD_CONFIG:
148		case CCD_UNCONFIG:
149			exit(do_single(argc, argv, action));
150			/* NOTREACHED */
151
152		case CCD_CONFIGALL:
153		case CCD_UNCONFIGALL:
154			exit(do_all(action));
155			/* NOTREACHED */
156
157		case CCD_DUMP:
158			exit(dump_ccd(argc, argv));
159			/* NOTREACHED */
160	}
161	/* NOTREACHED */
162	return (0);
163}
164
165static int
166do_single(int argc, char **argv, int action)
167{
168	char *cp, *cp2;
169	int ccd, noflags = 0, i, ileave, flags = 0;
170	struct gctl_req *grq;
171	char const *errstr;
172	char buf1[BUFSIZ];
173	int ex;
174
175	/*
176	 * If unconfiguring, all arguments are treated as ccds.
177	 */
178	if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) {
179		ex = 0;
180		for (i = 0; argc != 0; ) {
181			cp = *argv++; --argc;
182			if ((ccd = resolve_ccdname(cp)) < 0) {
183				warnx("invalid ccd name: %s", cp);
184				i = 1;
185				continue;
186			}
187			grq = gctl_get_handle();
188			gctl_ro_param(grq, "verb", -1, "destroy geom");
189			gctl_ro_param(grq, "class", -1, "CCD");
190			sprintf(buf1, "ccd%d", ccd);
191			gctl_ro_param(grq, "geom", -1, buf1);
192			errstr = gctl_issue(grq);
193			if (errstr == NULL) {
194				if (verbose)
195					printf("%s unconfigured\n", cp);
196				gctl_free(grq);
197				continue;
198			}
199			warnx(
200			    "%s\nor possibly kernel and ccdconfig out of sync",
201			    errstr);
202			ex = 1;
203		}
204		return (ex);
205	}
206
207	/* Make sure there are enough arguments. */
208	if (argc < 4) {
209		if (argc == 3) {
210			/* Assume that no flags are specified. */
211			noflags = 1;
212		} else {
213			if (action == CCD_CONFIGALL) {
214				warnx("%s: bad line: %d", ccdconf, lineno);
215				return (1);
216			} else
217				usage();
218		}
219	}
220
221	/* First argument is the ccd to configure. */
222	cp = *argv++; --argc;
223	if ((ccd = resolve_ccdname(cp)) < 0) {
224		warnx("invalid ccd name: %s", cp);
225		return (1);
226	}
227
228	/* Next argument is the interleave factor. */
229	cp = *argv++; --argc;
230	errno = 0;	/* to check for ERANGE */
231	ileave = (int)strtol(cp, &cp2, 10);
232	if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) {
233		warnx("invalid interleave factor: %s", cp);
234		return (1);
235	}
236
237	if (noflags == 0) {
238		/* Next argument is the ccd configuration flags. */
239		cp = *argv++; --argc;
240		if ((flags = flags_to_val(cp)) < 0) {
241			warnx("invalid flags argument: %s", cp);
242			return (1);
243		}
244	}
245	grq = gctl_get_handle();
246	gctl_ro_param(grq, "verb", -1, "create geom");
247	gctl_ro_param(grq, "class", -1, "CCD");
248	gctl_ro_param(grq, "unit", sizeof(ccd), &ccd);
249	gctl_ro_param(grq, "ileave", sizeof(ileave), &ileave);
250	if (flags & CCDF_UNIFORM)
251		gctl_ro_param(grq, "uniform", -1, "");
252	if (flags & CCDF_MIRROR)
253		gctl_ro_param(grq, "mirror", -1, "");
254	if (flags & CCDF_NO_OFFSET)
255		gctl_ro_param(grq, "no_offset", -1, "");
256	if (flags & CCDF_LINUX)
257		gctl_ro_param(grq, "linux", -1, "");
258	gctl_ro_param(grq, "nprovider", sizeof(argc), &argc);
259	for (i = 0; i < argc; i++) {
260		sprintf(buf1, "provider%d", i);
261		cp = argv[i];
262		if (!strncmp(cp, _PATH_DEV, strlen(_PATH_DEV)))
263			cp += strlen(_PATH_DEV);
264		gctl_ro_param(grq, buf1, -1, cp);
265	}
266	gctl_rw_param(grq, "output", sizeof(buf1), buf1);
267	errstr = gctl_issue(grq);
268	if (errstr == NULL) {
269		if (verbose) {
270			printf("%s", buf1);
271		}
272		gctl_free(grq);
273		return (0);
274	}
275	warnx(
276	    "%s\nor possibly kernel and ccdconfig out of sync",
277	    errstr);
278	return (1);
279}
280
281static int
282do_all(int action)
283{
284	FILE *f;
285	char line[_POSIX2_LINE_MAX];
286	char *cp, **argv;
287	int argc, rval;
288	gid_t egid;
289
290	rval = 0;
291	egid = getegid();
292	setegid(getgid());
293	if ((f = fopen(ccdconf, "r")) == NULL) {
294		setegid(egid);
295		warn("fopen: %s", ccdconf);
296		return (1);
297	}
298	setegid(egid);
299
300	while (fgets(line, sizeof(line), f) != NULL) {
301		argc = 0;
302		argv = NULL;
303		++lineno;
304		if ((cp = strrchr(line, '\n')) != NULL)
305			*cp = '\0';
306
307		/* Break up the line and pass it's contents to do_single(). */
308		if (line[0] == '\0')
309			goto end_of_line;
310		for (cp = line; (cp = strtok(cp, " \t")) != NULL; cp = NULL) {
311			if (*cp == '#')
312				break;
313			if ((argv = realloc(argv,
314			    sizeof(char *) * ++argc)) == NULL) {
315				warnx("no memory to configure ccds");
316				return (1);
317			}
318			argv[argc - 1] = cp;
319			/*
320			 * If our action is to unconfigure all, then pass
321			 * just the first token to do_single() and ignore
322			 * the rest.  Since this will be encountered on
323			 * our first pass through the line, the Right
324			 * Thing will happen.
325			 */
326			if (action == CCD_UNCONFIGALL) {
327				if (do_single(argc, argv, action))
328					rval = 1;
329				goto end_of_line;
330			}
331		}
332		if (argc != 0)
333			if (do_single(argc, argv, action))
334				rval = 1;
335
336 end_of_line:
337		if (argv != NULL)
338			free(argv);
339	}
340
341	(void)fclose(f);
342	return (rval);
343}
344
345static int
346resolve_ccdname(char *name)
347{
348
349	if (!strncmp(name, _PATH_DEV, strlen(_PATH_DEV)))
350		name += strlen(_PATH_DEV);
351	if (strncmp(name, "ccd", 3))
352		return -1;
353	name += 3;
354	if (!isdigit(*name))
355		return -1;
356	return (strtoul(name, NULL, 10));
357}
358
359static int
360dumpout(int unit)
361{
362	static int v;
363	struct gctl_req *grq;
364	int ncp;
365	char *cp;
366	char const *errstr;
367
368	grq = gctl_get_handle();
369	ncp = 65536;
370	cp = malloc(ncp);
371	gctl_ro_param(grq, "verb", -1, "list");
372	gctl_ro_param(grq, "class", -1, "CCD");
373	gctl_ro_param(grq, "unit", sizeof(unit), &unit);
374	gctl_rw_param(grq, "output", ncp, cp);
375	errstr = gctl_issue(grq);
376	if (errstr != NULL)
377		errx(1, "%s\nor possibly kernel and ccdconfig out of sync",
378			errstr);
379	if (strlen(cp) == 0)
380		errx(1, "ccd%d not configured", unit);
381	if (verbose && !v) {
382		printf("# ccd\t\tileave\tflags\tcomponent devices\n");
383		v = 1;
384	}
385	printf("%s", cp);
386	free(cp);
387	return (0);
388}
389
390static int
391dump_ccd(int argc, char **argv)
392{
393	int i, error;
394
395	if (argc == 0) {
396		error = dumpout(-1);
397	} else {
398		error = 0;
399		for (i = 0; error == 0 && i < argc; i++)
400			error = dumpout(resolve_ccdname(argv[i]));
401	}
402	return (error);
403}
404
405static int
406flags_to_val(char *flags)
407{
408	char *cp, *tok;
409	int i, tmp, val;
410
411	errno = 0;	/* to check for ERANGE */
412	val = (int)strtol(flags, &cp, 0);
413	if ((errno != ERANGE) && (*cp == '\0')) {
414		if (val & ~(CCDF_UNIFORM|CCDF_MIRROR))
415			return (-1);
416		return (val);
417	}
418
419	/* Check for values represented by strings. */
420	if ((cp = strdup(flags)) == NULL)
421		err(1, "no memory to parse flags");
422	tmp = 0;
423	for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) {
424		for (i = 0; flagvaltab[i].fv_flag != NULL; ++i)
425			if (strcmp(tok, flagvaltab[i].fv_flag) == 0)
426				break;
427		if (flagvaltab[i].fv_flag == NULL) {
428			free(cp);
429			return (-1);
430		}
431		tmp |= flagvaltab[i].fv_val;
432	}
433
434	/* If we get here, the string was ok. */
435	free(cp);
436	return (tmp);
437}
438
439static void
440usage(void)
441{
442	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
443		"usage: ccdconfig [-cv] ccd ileave [flags] dev ...",
444		"       ccdconfig -C [-v] [-f config_file]",
445		"       ccdconfig -u [-v] ccd ...",
446		"       ccdconfig -U [-v] [-f config_file]",
447		"       ccdconfig -g [ccd ...]");
448	exit(1);
449}
450