ccdconfig.c revision 141611
178344Sobrien/*
278344Sobrien * Copyright (c) 2003 Poul-Henning Kamp
398184Sgordon * Copyright (c) 1995 Jason R. Thorpe.
498184Sgordon * All rights reserved.
578344Sobrien *
678344Sobrien * Redistribution and use in source and binary forms, with or without
778344Sobrien * modification, are permitted provided that the following conditions
878344Sobrien * are met:
998184Sgordon * 1. Redistributions of source code must retain the above copyright
1098184Sgordon *    notice, this list of conditions and the following disclaimer.
1198184Sgordon * 2. Redistributions in binary form must reproduce the above copyright
1278344Sobrien *    notice, this list of conditions and the following disclaimer in the
1398184Sgordon *    documentation and/or other materials provided with the distribution.
1498184Sgordon * 3. All advertising materials mentioning features or use of this software
1598184Sgordon *    must display the following acknowledgement:
1678344Sobrien *	This product includes software developed for the NetBSD Project
1778344Sobrien *	by Jason R. Thorpe.
1878344Sobrien * 4. The name of the author may not be used to endorse or promote products
1998184Sgordon *    derived from this software without specific prior written permission.
2078344Sobrien *
2178344Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22103019Sgordon * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23102864Sgordon * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24102864Sgordon * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25102864Sgordon * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26102864Sgordon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27102864Sgordon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28102864Sgordon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29102864Sgordon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30102864Sgordon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31102864Sgordon * SUCH DAMAGE.
32102864Sgordon */
33102864Sgordon
34102864Sgordon#include <sys/cdefs.h>
35102864Sgordon__FBSDID("$FreeBSD: head/sbin/ccdconfig/ccdconfig.c 141611 2005-02-10 09:19:34Z ru $");
36102864Sgordon
37102864Sgordon#include <sys/param.h>
38102864Sgordon#include <sys/linker.h>
39102864Sgordon#include <sys/module.h>
40102864Sgordon#include <ctype.h>
41102864Sgordon#include <err.h>
42102864Sgordon#include <errno.h>
43102864Sgordon#include <limits.h>
44102864Sgordon#include <paths.h>
45102864Sgordon#include <stdio.h>
46102864Sgordon#include <stdlib.h>
4778344Sobrien#include <string.h>
4878344Sobrien#include <unistd.h>
4978344Sobrien#include <libgeom.h>
5078344Sobrien
5178344Sobrien#define CCDF_UNIFORM    0x02    /* use LCCD of sizes for uniform interleave */
5298184Sgordon#define CCDF_MIRROR     0x04    /* use mirroring */
5378344Sobrien
5478344Sobrien#include "pathnames.h"
5578344Sobrien
5678344Sobrienstatic	int lineno = 0;
5778344Sobrienstatic	int verbose = 0;
5878344Sobrienstatic	const char *ccdconf = _PATH_CCDCONF;
5978344Sobrien
6078344Sobrienstruct	flagval {
6178344Sobrien	const char	*fv_flag;
6278344Sobrien	int		fv_val;
6378344Sobrien} flagvaltab[] = {
6478344Sobrien	{ "CCDF_UNIFORM",	CCDF_UNIFORM },
6578344Sobrien	{ "uniform",		CCDF_UNIFORM },
6678344Sobrien	{ "CCDF_MIRROR",	CCDF_MIRROR },
6778344Sobrien	{ "mirror",		CCDF_MIRROR },
6878344Sobrien	{ "none",		0 },
6978344Sobrien	{ NULL,			0 },
7078344Sobrien};
7178344Sobrien
7278344Sobrien#define CCD_CONFIG		0	/* configure a device */
7378344Sobrien#define CCD_CONFIGALL		1	/* configure all devices */
7478344Sobrien#define CCD_UNCONFIG		2	/* unconfigure a device */
7598184Sgordon#define CCD_UNCONFIGALL		3	/* unconfigure all devices */
7698184Sgordon#define CCD_DUMP		4	/* dump a ccd's configuration */
7778344Sobrien
7878344Sobrienstatic	int do_single(int, char **, int);
7978344Sobrienstatic	int do_all(int);
8078344Sobrienstatic	int dump_ccd(int, char **);
8178344Sobrienstatic	int flags_to_val(char *);
8278344Sobrienstatic	int resolve_ccdname(char *);
8378344Sobrienstatic	void usage(void);
8478344Sobrien
8598184Sgordonint
86103019Sgordonmain(int argc, char *argv[])
8798184Sgordon{
88102864Sgordon	int ch, options = 0, action = CCD_CONFIG;
89104980Sschweikh
90103264Sgordon	while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) {
91102864Sgordon		switch (ch) {
92102864Sgordon		case 'c':
93102864Sgordon			action = CCD_CONFIG;
94102864Sgordon			++options;
95103264Sgordon			break;
9698184Sgordon
97102864Sgordon		case 'C':
98102864Sgordon			action = CCD_CONFIGALL;
99102864Sgordon			++options;
100102864Sgordon			break;
101102864Sgordon
102102864Sgordon		case 'f':
10398184Sgordon			ccdconf = optarg;
10498184Sgordon			break;
10598184Sgordon
106102864Sgordon		case 'g':
107102864Sgordon			action = CCD_DUMP;
10898184Sgordon			break;
10998184Sgordon
11098184Sgordon		case 'u':
11198184Sgordon			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