ccdconfig.c revision 116111
155714Skris/*
2194206Ssimon * Copyright (c) 2003 Poul-Henning Kamp
3142425Snectar * Copyright (c) 1995 Jason R. Thorpe.
455714Skris * All rights reserved.
555714Skris *
655714Skris * Redistribution and use in source and binary forms, with or without
755714Skris * modification, are permitted provided that the following conditions
855714Skris * are met:
955714Skris * 1. Redistributions of source code must retain the above copyright
1055714Skris *    notice, this list of conditions and the following disclaimer.
1155714Skris * 2. Redistributions in binary form must reproduce the above copyright
1255714Skris *    notice, this list of conditions and the following disclaimer in the
1355714Skris *    documentation and/or other materials provided with the distribution.
1455714Skris * 3. All advertising materials mentioning features or use of this software
1555714Skris *    must display the following acknowledgement:
1655714Skris *	This product includes software developed for the NetBSD Project
1755714Skris *	by Jason R. Thorpe.
1855714Skris * 4. The name of the author may not be used to endorse or promote products
1955714Skris *    derived from this software without specific prior written permission.
2055714Skris *
2155714Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2255714Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2355714Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2455714Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2555714Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2655714Skris * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2755714Skris * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2855714Skris * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2955714Skris * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155714Skris * SUCH DAMAGE.
3255714Skris */
3355714Skris
3455714Skris#include <sys/cdefs.h>
3555714Skris__FBSDID("$FreeBSD: head/sbin/ccdconfig/ccdconfig.c 116111 2003-06-09 19:25:07Z phk $");
3655714Skris
3755714Skris#include <sys/param.h>
3855714Skris#include <sys/linker.h>
3955714Skris#include <sys/module.h>
4055714Skris#include <ctype.h>
4155714Skris#include <err.h>
4255714Skris#include <errno.h>
4355714Skris#include <limits.h>
4455714Skris#include <paths.h>
4555714Skris#include <stdio.h>
4655714Skris#include <stdlib.h>
4755714Skris#include <string.h>
4855714Skris#include <unistd.h>
4955714Skris#include <libgeom.h>
5055714Skris
5155714Skris#define CCDF_UNIFORM    0x02    /* use LCCD of sizes for uniform interleave */
5255714Skris#define CCDF_MIRROR     0x04    /* use mirroring */
5355714Skris
5455714Skris#include "pathnames.h"
5555714Skris
5655714Skrisstatic	int lineno = 0;
5755714Skrisstatic	int verbose = 0;
5855714Skrisstatic	const char *ccdconf = _PATH_CCDCONF;
5955714Skris
6055714Skrisstruct	flagval {
61109998Smarkm	const char	*fv_flag;
6255714Skris	int		fv_val;
6355714Skris} flagvaltab[] = {
6455714Skris	{ "CCDF_UNIFORM",	CCDF_UNIFORM },
6555714Skris	{ "uniform",		CCDF_UNIFORM },
6655714Skris	{ "CCDF_MIRROR",	CCDF_MIRROR },
67109998Smarkm	{ "mirror",		CCDF_MIRROR },
68109998Smarkm	{ "none",		0 },
69109998Smarkm	{ NULL,			0 },
70109998Smarkm};
7155714Skris
72109998Smarkm#define CCD_CONFIG		0	/* configure a device */
7355714Skris#define CCD_CONFIGALL		1	/* configure all devices */
74109998Smarkm#define CCD_UNCONFIG		2	/* unconfigure a device */
75109998Smarkm#define CCD_UNCONFIGALL		3	/* unconfigure all devices */
76109998Smarkm#define CCD_DUMP		4	/* dump a ccd's configuration */
77109998Smarkm
78109998Smarkmstatic	int do_single(int, char **, int);
79109998Smarkmstatic	int do_all(int);
8055714Skrisstatic	int dump_ccd(int, char **);
81109998Smarkmstatic	int flags_to_val(char *);
8255714Skrisstatic	int resolve_ccdname(char *);
8355714Skrisstatic	void usage(void);
8455714Skris
85238405Sjkimint
86238405Sjkimmain(int argc, char *argv[])
8755714Skris{
8855714Skris	int ch, options = 0, action = CCD_CONFIG;
89238405Sjkim
90238405Sjkim	while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) {
91238405Sjkim		switch (ch) {
9255714Skris		case 'c':
9355714Skris			action = CCD_CONFIG;
94238405Sjkim			++options;
9555714Skris			break;
9655714Skris
9755714Skris		case 'C':
9859191Skris			action = CCD_CONFIGALL;
9955714Skris			++options;
10059191Skris			break;
10159191Skris
102238405Sjkim		case 'f':
10359191Skris			ccdconf = optarg;
10459191Skris			break;
10559191Skris
10659191Skris		case 'g':
10759191Skris			action = CCD_DUMP;
10855714Skris			break;
10955714Skris
11055714Skris		case 'u':
11155714Skris			action = CCD_UNCONFIG;
11255714Skris			++options;
11359191Skris			break;
11455714Skris
11555714Skris		case 'U':
11655714Skris			action = CCD_UNCONFIGALL;
117238405Sjkim			++options;
118238405Sjkim			break;
119238405Sjkim
120238405Sjkim		case 'v':
121238405Sjkim			verbose = 1;
122238405Sjkim			break;
123238405Sjkim
12455714Skris		default:
125109998Smarkm			usage();
126109998Smarkm		}
127238405Sjkim	}
128238405Sjkim	argc -= optind;
129238405Sjkim	argv += optind;
13055714Skris
131238405Sjkim	if (options > 1)
13255714Skris		usage();
133142425Snectar
13455714Skris	if (modfind("g_ccd") < 0) {
13555714Skris		/* Not present in kernel, try loading it */
136238405Sjkim		if (kldload("g_ccd") < 0 || modfind("g_ccd") < 0)
137238405Sjkim			warn("g_ccd module not available!");
138238405Sjkim	}
139238405Sjkim
140238405Sjkim	switch (action) {
141238405Sjkim		case CCD_CONFIG:
142238405Sjkim		case CCD_UNCONFIG:
143238405Sjkim			exit(do_single(argc, argv, action));
144238405Sjkim			/* NOTREACHED */
14555714Skris
14655714Skris		case CCD_CONFIGALL:
14755714Skris		case CCD_UNCONFIGALL:
14855714Skris			exit(do_all(action));
149238405Sjkim			/* NOTREACHED */
150238405Sjkim
151238405Sjkim		case CCD_DUMP:
152238405Sjkim			exit(dump_ccd(argc, argv));
15355714Skris			/* NOTREACHED */
154238405Sjkim	}
15555714Skris	/* NOTREACHED */
156238405Sjkim	return (0);
15755714Skris}
158238405Sjkim
15955714Skrisstatic int
160238405Sjkimdo_single(int argc, char **argv, int action)
161238405Sjkim{
16255714Skris	char *cp, *cp2;
16355714Skris	int ccd, noflags = 0, i, ileave, flags = 0;
16455714Skris	struct gctl_req *grq;
16555714Skris	char const *errstr;
16655714Skris	char buf1[BUFSIZ];
16755714Skris	int ex;
16855714Skris
16955714Skris	/*
17055714Skris	 * If unconfiguring, all arguments are treated as ccds.
17155714Skris	 */
172238405Sjkim	if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) {
17355714Skris		ex = 0;
17455714Skris		for (i = 0; argc != 0; ) {
17555714Skris			cp = *argv++; --argc;
17655714Skris			if ((ccd = resolve_ccdname(cp)) < 0) {
17755714Skris				warnx("invalid ccd name: %s", cp);
17855714Skris				i = 1;
17955714Skris				continue;
18055714Skris			}
18155714Skris			grq = gctl_get_handle();
182238405Sjkim			gctl_ro_param(grq, "verb", -1, "destroy geom");
18355714Skris			gctl_ro_param(grq, "class", -1, "CCD");
18455714Skris			sprintf(buf1, "ccd%d", ccd);
18555714Skris			gctl_ro_param(grq, "geom", -1, buf1);
18655714Skris			errstr = gctl_issue(grq);
18755714Skris			if (errstr == NULL) {
18855714Skris				if (verbose)
18955714Skris					printf("%s unconfigured\n", cp);
19055714Skris				gctl_free(grq);
19155714Skris				continue;
19255714Skris			}
193238405Sjkim			warnx(
194238405Sjkim			    "%s\nor possibly kernel and ccdconfig out of sync",
195238405Sjkim			    errstr);
196238405Sjkim			ex = 1;
197238405Sjkim		}
198238405Sjkim		return (ex);
199238405Sjkim	}
200238405Sjkim
201238405Sjkim	/* Make sure there are enough arguments. */
202238405Sjkim	if (argc < 4) {
203238405Sjkim		if (argc == 3) {
204238405Sjkim			/* Assume that no flags are specified. */
205238405Sjkim			noflags = 1;
206238405Sjkim		} else {
207238405Sjkim			if (action == CCD_CONFIGALL) {
208238405Sjkim				warnx("%s: bad line: %d", ccdconf, lineno);
209238405Sjkim				return (1);
210238405Sjkim			} else
211238405Sjkim				usage();
212238405Sjkim		}
213238405Sjkim	}
214238405Sjkim
215238405Sjkim	/* First argument is the ccd to configure. */
216238405Sjkim	cp = *argv++; --argc;
217238405Sjkim	if ((ccd = resolve_ccdname(cp)) < 0) {
218238405Sjkim		warnx("invalid ccd name: %s", cp);
219238405Sjkim		return (1);
220238405Sjkim	}
221238405Sjkim
222238405Sjkim	/* Next argument is the interleave factor. */
223238405Sjkim	cp = *argv++; --argc;
224238405Sjkim	errno = 0;	/* to check for ERANGE */
225238405Sjkim	ileave = (int)strtol(cp, &cp2, 10);
226238405Sjkim	if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) {
227238405Sjkim		warnx("invalid interleave factor: %s", cp);
228238405Sjkim		return (1);
229238405Sjkim	}
230238405Sjkim
231238405Sjkim	if (noflags == 0) {
232238405Sjkim		/* Next argument is the ccd configuration flags. */
233238405Sjkim		cp = *argv++; --argc;
234238405Sjkim		if ((flags = flags_to_val(cp)) < 0) {
235238405Sjkim			warnx("invalid flags argument: %s", cp);
236238405Sjkim			return (1);
237238405Sjkim		}
238238405Sjkim	}
239238405Sjkim	grq = gctl_get_handle();
240238405Sjkim	gctl_ro_param(grq, "verb", -1, "create geom");
241238405Sjkim	gctl_ro_param(grq, "class", -1, "CCD");
242238405Sjkim	gctl_ro_param(grq, "unit", sizeof(ccd), &ccd);
243238405Sjkim	gctl_ro_param(grq, "ileave", sizeof(ileave), &ileave);
244238405Sjkim	if (flags & CCDF_UNIFORM)
245238405Sjkim		gctl_ro_param(grq, "uniform", -1, "");
246238405Sjkim	if (flags & CCDF_MIRROR)
247238405Sjkim		gctl_ro_param(grq, "mirror", -1, "");
248238405Sjkim	gctl_ro_param(grq, "nprovider", sizeof(argc), &argc);
249238405Sjkim	for (i = 0; i < argc; i++) {
250238405Sjkim		sprintf(buf1, "provider%d", i);
251238405Sjkim		cp = argv[i];
252238405Sjkim		if (!strncmp(cp, _PATH_DEV, strlen(_PATH_DEV)))
253238405Sjkim			cp += strlen(_PATH_DEV);
254238405Sjkim		gctl_ro_param(grq, buf1, -1, cp);
255238405Sjkim	}
256238405Sjkim	gctl_rw_param(grq, "output", sizeof(buf1), buf1);
257238405Sjkim	errstr = gctl_issue(grq);
258238405Sjkim	if (errstr == NULL) {
259238405Sjkim		if (verbose) {
260238405Sjkim			printf("%s", buf1);
261238405Sjkim		}
262238405Sjkim		gctl_free(grq);
263238405Sjkim		return (0);
264238405Sjkim	}
265238405Sjkim	warnx(
266238405Sjkim	    "%s\nor possibly kernel and ccdconfig out of sync",
267238405Sjkim	    errstr);
268238405Sjkim	return (1);
269238405Sjkim}
270238405Sjkim
271238405Sjkimstatic int
272238405Sjkimdo_all(int action)
273238405Sjkim{
274238405Sjkim	FILE *f;
275238405Sjkim	char line[_POSIX2_LINE_MAX];
276238405Sjkim	char *cp, **argv;
277238405Sjkim	int argc, rval;
278238405Sjkim	gid_t egid;
279238405Sjkim
280238405Sjkim	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, err;
384
385	if (argc == 0) {
386		err = dumpout(-1);
387	} else {
388		err = 0;
389		for (i = 0; err == 0 && i < argc; i++)
390			err = dumpout(resolve_ccdname(argv[i]));
391	}
392	return (err);
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