113044Sasami/*
2116111Sphk * Copyright (c) 2003 Poul-Henning Kamp
313044Sasami * Copyright (c) 1995 Jason R. Thorpe.
413044Sasami * All rights reserved.
513044Sasami *
613044Sasami * Redistribution and use in source and binary forms, with or without
713044Sasami * modification, are permitted provided that the following conditions
813044Sasami * are met:
913044Sasami * 1. Redistributions of source code must retain the above copyright
1013044Sasami *    notice, this list of conditions and the following disclaimer.
1113044Sasami * 2. Redistributions in binary form must reproduce the above copyright
1213044Sasami *    notice, this list of conditions and the following disclaimer in the
1313044Sasami *    documentation and/or other materials provided with the distribution.
1413044Sasami * 3. All advertising materials mentioning features or use of this software
1513044Sasami *    must display the following acknowledgement:
1613044Sasami *	This product includes software developed for the NetBSD Project
1713044Sasami *	by Jason R. Thorpe.
1813044Sasami * 4. The name of the author may not be used to endorse or promote products
1913044Sasami *    derived from this software without specific prior written permission.
2013044Sasami *
2113044Sasami * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2213044Sasami * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2313044Sasami * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2413044Sasami * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2513044Sasami * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2613044Sasami * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2713044Sasami * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2813044Sasami * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2913044Sasami * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3013044Sasami * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3113044Sasami * SUCH DAMAGE.
3213044Sasami */
3313044Sasami
34114589Sobrien#include <sys/cdefs.h>
35114589Sobrien__FBSDID("$FreeBSD$");
3636628Scharnier
3713044Sasami#include <sys/param.h>
3848568Sbillf#include <sys/linker.h>
3945329Speter#include <sys/module.h>
4013044Sasami#include <ctype.h>
4113044Sasami#include <err.h>
4213044Sasami#include <errno.h>
4313044Sasami#include <limits.h>
4469793Sobrien#include <paths.h>
4513044Sasami#include <stdio.h>
4613044Sasami#include <stdlib.h>
4713044Sasami#include <string.h>
4813044Sasami#include <unistd.h>
49115730Sphk#include <libgeom.h>
5013044Sasami
51116111Sphk#define CCDF_UNIFORM    0x02    /* use LCCD of sizes for uniform interleave */
52116111Sphk#define CCDF_MIRROR     0x04    /* use mirroring */
53157740Scracauer#define CCDF_NO_OFFSET  0x08    /* do not leave space in front */
54157740Scracauer#define CCDF_LINUX      0x10    /* use Linux compatibility mode */
5513044Sasami
5613044Sasami#include "pathnames.h"
5713044Sasami
5813044Sasamistatic	int lineno = 0;
5913044Sasamistatic	int verbose = 0;
60109417Sphkstatic	const char *ccdconf = _PATH_CCDCONF;
6113044Sasami
6213044Sasamistruct	flagval {
63109417Sphk	const char	*fv_flag;
64116111Sphk	int		fv_val;
6513044Sasami} flagvaltab[] = {
6613044Sasami	{ "CCDF_UNIFORM",	CCDF_UNIFORM },
67116111Sphk	{ "uniform",		CCDF_UNIFORM },
6813762Sasami	{ "CCDF_MIRROR",	CCDF_MIRROR },
69116111Sphk	{ "mirror",		CCDF_MIRROR },
70157740Scracauer	{ "CCDF_NO_OFFSET",	CCDF_NO_OFFSET },
71157740Scracauer	{ "no_offset",		CCDF_NO_OFFSET },
72157740Scracauer	{ "CCDF_LINUX",		CCDF_LINUX },
73157740Scracauer	{ "linux",		CCDF_LINUX },
74116111Sphk	{ "none",		0 },
7513044Sasami	{ NULL,			0 },
7613044Sasami};
7713044Sasami
7813044Sasami#define CCD_CONFIG		0	/* configure a device */
7913044Sasami#define CCD_CONFIGALL		1	/* configure all devices */
8013044Sasami#define CCD_UNCONFIG		2	/* unconfigure a device */
8113044Sasami#define CCD_UNCONFIGALL		3	/* unconfigure all devices */
8213044Sasami#define CCD_DUMP		4	/* dump a ccd's configuration */
8313044Sasami
8492539Simpstatic	int do_single(int, char **, int);
8592539Simpstatic	int do_all(int);
8692539Simpstatic	int dump_ccd(int, char **);
8792539Simpstatic	int flags_to_val(char *);
88109417Sphkstatic	int resolve_ccdname(char *);
8992539Simpstatic	void usage(void);
9013044Sasami
9113044Sasamiint
9292539Simpmain(int argc, char *argv[])
9313044Sasami{
9413044Sasami	int ch, options = 0, action = CCD_CONFIG;
9513044Sasami
9683329Sru	while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) {
9713044Sasami		switch (ch) {
9813044Sasami		case 'c':
9913044Sasami			action = CCD_CONFIG;
10013044Sasami			++options;
10113044Sasami			break;
10213044Sasami
10313044Sasami		case 'C':
10413044Sasami			action = CCD_CONFIGALL;
10513044Sasami			++options;
10613044Sasami			break;
10713044Sasami
10813044Sasami		case 'f':
10913044Sasami			ccdconf = optarg;
11013044Sasami			break;
11113044Sasami
11213044Sasami		case 'g':
11313044Sasami			action = CCD_DUMP;
11413044Sasami			break;
11513044Sasami
11613044Sasami		case 'u':
11713044Sasami			action = CCD_UNCONFIG;
11813044Sasami			++options;
11913044Sasami			break;
12013044Sasami
12113044Sasami		case 'U':
12213044Sasami			action = CCD_UNCONFIGALL;
12313044Sasami			++options;
12413044Sasami			break;
12513044Sasami
12613044Sasami		case 'v':
12713044Sasami			verbose = 1;
12813044Sasami			break;
12913044Sasami
13013044Sasami		default:
13113044Sasami			usage();
13213044Sasami		}
13313044Sasami	}
13413044Sasami	argc -= optind;
13513044Sasami	argv += optind;
13613044Sasami
13713044Sasami	if (options > 1)
13813044Sasami		usage();
13913044Sasami
140116111Sphk	if (modfind("g_ccd") < 0) {
14145329Speter		/* Not present in kernel, try loading it */
142116126Sphk		if (kldload("geom_ccd") < 0 || modfind("g_ccd") < 0)
143116126Sphk			warn("geom_ccd module not available!");
14445329Speter	}
14545329Speter
14613044Sasami	switch (action) {
14713044Sasami		case CCD_CONFIG:
14813044Sasami		case CCD_UNCONFIG:
14913044Sasami			exit(do_single(argc, argv, action));
15013044Sasami			/* NOTREACHED */
15113044Sasami
15213044Sasami		case CCD_CONFIGALL:
15313044Sasami		case CCD_UNCONFIGALL:
15413044Sasami			exit(do_all(action));
15513044Sasami			/* NOTREACHED */
15613044Sasami
15713044Sasami		case CCD_DUMP:
15813044Sasami			exit(dump_ccd(argc, argv));
15913044Sasami			/* NOTREACHED */
16013044Sasami	}
16113044Sasami	/* NOTREACHED */
16236628Scharnier	return (0);
16313044Sasami}
16413044Sasami
16513044Sasamistatic int
16692539Simpdo_single(int argc, char **argv, int action)
16713044Sasami{
168116111Sphk	char *cp, *cp2;
169116111Sphk	int ccd, noflags = 0, i, ileave, flags = 0;
170116111Sphk	struct gctl_req *grq;
171116111Sphk	char const *errstr;
172116111Sphk	char buf1[BUFSIZ];
173116111Sphk	int ex;
17413044Sasami
17513044Sasami	/*
17613044Sasami	 * If unconfiguring, all arguments are treated as ccds.
17713044Sasami	 */
17813044Sasami	if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) {
179116111Sphk		ex = 0;
180209052Suqs		for (; argc != 0;) {
18113044Sasami			cp = *argv++; --argc;
182109472Sphk			if ((ccd = resolve_ccdname(cp)) < 0) {
18313044Sasami				warnx("invalid ccd name: %s", cp);
18413044Sasami				continue;
18513044Sasami			}
186116111Sphk			grq = gctl_get_handle();
187116111Sphk			gctl_ro_param(grq, "verb", -1, "destroy geom");
188116111Sphk			gctl_ro_param(grq, "class", -1, "CCD");
189116111Sphk			sprintf(buf1, "ccd%d", ccd);
190116111Sphk			gctl_ro_param(grq, "geom", -1, buf1);
191116111Sphk			errstr = gctl_issue(grq);
192116111Sphk			if (errstr == NULL) {
19313044Sasami				if (verbose)
19413044Sasami					printf("%s unconfigured\n", cp);
195116111Sphk				gctl_free(grq);
196116111Sphk				continue;
197116111Sphk			}
198116111Sphk			warnx(
199116111Sphk			    "%s\nor possibly kernel and ccdconfig out of sync",
200116111Sphk			    errstr);
201116111Sphk			ex = 1;
20213044Sasami		}
203116111Sphk		return (ex);
20413044Sasami	}
20513044Sasami
20613044Sasami	/* Make sure there are enough arguments. */
20748568Sbillf	if (argc < 4) {
20813044Sasami		if (argc == 3) {
20913044Sasami			/* Assume that no flags are specified. */
21013044Sasami			noflags = 1;
21113044Sasami		} else {
21213044Sasami			if (action == CCD_CONFIGALL) {
21313044Sasami				warnx("%s: bad line: %d", ccdconf, lineno);
21413044Sasami				return (1);
21513044Sasami			} else
21613044Sasami				usage();
21713044Sasami		}
21848568Sbillf	}
21913044Sasami
22013044Sasami	/* First argument is the ccd to configure. */
22113044Sasami	cp = *argv++; --argc;
222109472Sphk	if ((ccd = resolve_ccdname(cp)) < 0) {
22313044Sasami		warnx("invalid ccd name: %s", cp);
22413044Sasami		return (1);
22513044Sasami	}
22613044Sasami
22713044Sasami	/* Next argument is the interleave factor. */
22813044Sasami	cp = *argv++; --argc;
22913044Sasami	errno = 0;	/* to check for ERANGE */
23013044Sasami	ileave = (int)strtol(cp, &cp2, 10);
23113044Sasami	if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) {
23213044Sasami		warnx("invalid interleave factor: %s", cp);
23313044Sasami		return (1);
23413044Sasami	}
23513044Sasami
23613044Sasami	if (noflags == 0) {
23713044Sasami		/* Next argument is the ccd configuration flags. */
23813044Sasami		cp = *argv++; --argc;
23913044Sasami		if ((flags = flags_to_val(cp)) < 0) {
24013044Sasami			warnx("invalid flags argument: %s", cp);
24113044Sasami			return (1);
24213044Sasami		}
24313044Sasami	}
244116111Sphk	grq = gctl_get_handle();
245116111Sphk	gctl_ro_param(grq, "verb", -1, "create geom");
246116111Sphk	gctl_ro_param(grq, "class", -1, "CCD");
247116111Sphk	gctl_ro_param(grq, "unit", sizeof(ccd), &ccd);
248116111Sphk	gctl_ro_param(grq, "ileave", sizeof(ileave), &ileave);
249116111Sphk	if (flags & CCDF_UNIFORM)
250116111Sphk		gctl_ro_param(grq, "uniform", -1, "");
251116111Sphk	if (flags & CCDF_MIRROR)
252116111Sphk		gctl_ro_param(grq, "mirror", -1, "");
253157740Scracauer	if (flags & CCDF_NO_OFFSET)
254157740Scracauer		gctl_ro_param(grq, "no_offset", -1, "");
255157740Scracauer	if (flags & CCDF_LINUX)
256157740Scracauer		gctl_ro_param(grq, "linux", -1, "");
257116111Sphk	gctl_ro_param(grq, "nprovider", sizeof(argc), &argc);
258116111Sphk	for (i = 0; i < argc; i++) {
259116111Sphk		sprintf(buf1, "provider%d", i);
260116111Sphk		cp = argv[i];
261116111Sphk		if (!strncmp(cp, _PATH_DEV, strlen(_PATH_DEV)))
262116111Sphk			cp += strlen(_PATH_DEV);
263116111Sphk		gctl_ro_param(grq, buf1, -1, cp);
26413044Sasami	}
265116111Sphk	gctl_rw_param(grq, "output", sizeof(buf1), buf1);
266116111Sphk	errstr = gctl_issue(grq);
267116111Sphk	if (errstr == NULL) {
268116111Sphk		if (verbose) {
269116111Sphk			printf("%s", buf1);
27013044Sasami		}
271116111Sphk		gctl_free(grq);
272116111Sphk		return (0);
27313044Sasami	}
274116111Sphk	warnx(
275116111Sphk	    "%s\nor possibly kernel and ccdconfig out of sync",
276116111Sphk	    errstr);
277116111Sphk	return (1);
27813044Sasami}
27913044Sasami
28013044Sasamistatic int
28192539Simpdo_all(int action)
28213044Sasami{
28313044Sasami	FILE *f;
28413044Sasami	char line[_POSIX2_LINE_MAX];
28513044Sasami	char *cp, **argv;
28613044Sasami	int argc, rval;
28732116Simp	gid_t egid;
28813044Sasami
28951690Sbillf	rval = 0;
29032116Simp	egid = getegid();
291242166Seadler	if (setegid(getgid()) != 0)
292242166Seadler		err(1, "setegid failed");
29313044Sasami	if ((f = fopen(ccdconf, "r")) == NULL) {
294242166Seadler		if (setegid(egid) != 0)
295242166Seadler			err(1, "setegid failed");
29613044Sasami		warn("fopen: %s", ccdconf);
29713044Sasami		return (1);
29813044Sasami	}
299242166Seadler	if (setegid(egid) != 0)
300242166Seadler		err(1, "setegid failed");
30113044Sasami
30213044Sasami	while (fgets(line, sizeof(line), f) != NULL) {
30313044Sasami		argc = 0;
30413044Sasami		argv = NULL;
30513044Sasami		++lineno;
30613044Sasami		if ((cp = strrchr(line, '\n')) != NULL)
30713044Sasami			*cp = '\0';
30813044Sasami
30913044Sasami		/* Break up the line and pass it's contents to do_single(). */
31013044Sasami		if (line[0] == '\0')
31113044Sasami			goto end_of_line;
31213044Sasami		for (cp = line; (cp = strtok(cp, " \t")) != NULL; cp = NULL) {
31313044Sasami			if (*cp == '#')
31413044Sasami				break;
31513044Sasami			if ((argv = realloc(argv,
31613044Sasami			    sizeof(char *) * ++argc)) == NULL) {
31713044Sasami				warnx("no memory to configure ccds");
31813044Sasami				return (1);
31913044Sasami			}
32013044Sasami			argv[argc - 1] = cp;
32113044Sasami			/*
32213044Sasami			 * If our action is to unconfigure all, then pass
32313044Sasami			 * just the first token to do_single() and ignore
32413044Sasami			 * the rest.  Since this will be encountered on
32513044Sasami			 * our first pass through the line, the Right
32613044Sasami			 * Thing will happen.
32713044Sasami			 */
32813044Sasami			if (action == CCD_UNCONFIGALL) {
32913044Sasami				if (do_single(argc, argv, action))
33013044Sasami					rval = 1;
33113044Sasami				goto end_of_line;
33213044Sasami			}
33313044Sasami		}
33413044Sasami		if (argc != 0)
33513044Sasami			if (do_single(argc, argv, action))
33613044Sasami				rval = 1;
33713044Sasami
33813044Sasami end_of_line:
33913044Sasami		if (argv != NULL)
34013044Sasami			free(argv);
34113044Sasami	}
34213044Sasami
34313044Sasami	(void)fclose(f);
34413044Sasami	return (rval);
34513044Sasami}
34613044Sasami
34713044Sasamistatic int
34892539Simpresolve_ccdname(char *name)
34913044Sasami{
35013044Sasami
351109417Sphk	if (!strncmp(name, _PATH_DEV, strlen(_PATH_DEV)))
352109417Sphk		name += strlen(_PATH_DEV);
353109417Sphk	if (strncmp(name, "ccd", 3))
354109417Sphk		return -1;
355109417Sphk	name += 3;
356109417Sphk	if (!isdigit(*name))
357109417Sphk		return -1;
358109417Sphk	return (strtoul(name, NULL, 10));
35913044Sasami}
36013044Sasami
36113044Sasamistatic int
362115731Sphkdumpout(int unit)
36313044Sasami{
364115731Sphk	static int v;
365115731Sphk	struct gctl_req *grq;
366115731Sphk	int ncp;
367109417Sphk	char *cp;
368115730Sphk	char const *errstr;
36913044Sasami
370115730Sphk	grq = gctl_get_handle();
371115731Sphk	ncp = 65536;
372115731Sphk	cp = malloc(ncp);
373115730Sphk	gctl_ro_param(grq, "verb", -1, "list");
374115730Sphk	gctl_ro_param(grq, "class", -1, "CCD");
375115731Sphk	gctl_ro_param(grq, "unit", sizeof(unit), &unit);
376115731Sphk	gctl_rw_param(grq, "output", ncp, cp);
377115730Sphk	errstr = gctl_issue(grq);
378115731Sphk	if (errstr != NULL)
379115731Sphk		errx(1, "%s\nor possibly kernel and ccdconfig out of sync",
380115731Sphk			errstr);
381115731Sphk	if (strlen(cp) == 0)
382115731Sphk		errx(1, "ccd%d not configured", unit);
383115731Sphk	if (verbose && !v) {
384115731Sphk		printf("# ccd\t\tileave\tflags\tcomponent devices\n");
385115731Sphk		v = 1;
386115730Sphk	}
387115731Sphk	printf("%s", cp);
388115731Sphk	free(cp);
38913044Sasami	return (0);
39013044Sasami}
39113044Sasami
392115731Sphkstatic int
393115731Sphkdump_ccd(int argc, char **argv)
39413044Sasami{
395118632Sjohan	int i, error;
39613044Sasami
397115731Sphk	if (argc == 0) {
398118632Sjohan		error = dumpout(-1);
399115731Sphk	} else {
400118632Sjohan		error = 0;
401118632Sjohan		for (i = 0; error == 0 && i < argc; i++)
402118632Sjohan			error = dumpout(resolve_ccdname(argv[i]));
40313044Sasami	}
404118632Sjohan	return (error);
40513044Sasami}
40613044Sasami
40713044Sasamistatic int
40892539Simpflags_to_val(char *flags)
40913044Sasami{
41013044Sasami	char *cp, *tok;
411116111Sphk	int i, tmp, val;
41213044Sasami
413116111Sphk	errno = 0;	/* to check for ERANGE */
414116111Sphk	val = (int)strtol(flags, &cp, 0);
415116111Sphk	if ((errno != ERANGE) && (*cp == '\0')) {
416116111Sphk		if (val & ~(CCDF_UNIFORM|CCDF_MIRROR))
417116111Sphk			return (-1);
418116111Sphk		return (val);
419116111Sphk	}
42013044Sasami
42113044Sasami	/* Check for values represented by strings. */
42213044Sasami	if ((cp = strdup(flags)) == NULL)
42313044Sasami		err(1, "no memory to parse flags");
42413044Sasami	tmp = 0;
42513044Sasami	for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) {
42613044Sasami		for (i = 0; flagvaltab[i].fv_flag != NULL; ++i)
42713044Sasami			if (strcmp(tok, flagvaltab[i].fv_flag) == 0)
42813044Sasami				break;
42913044Sasami		if (flagvaltab[i].fv_flag == NULL) {
43013044Sasami			free(cp);
431116111Sphk			return (-1);
43213044Sasami		}
43313044Sasami		tmp |= flagvaltab[i].fv_val;
43413044Sasami	}
43513044Sasami
43613044Sasami	/* If we get here, the string was ok. */
43713044Sasami	free(cp);
438116111Sphk	return (tmp);
43913044Sasami}
44013044Sasami
44113044Sasamistatic void
44292539Simpusage(void)
44313044Sasami{
44426541Scharnier	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
445141611Sru		"usage: ccdconfig [-cv] ccd ileave [flags] dev ...",
44626541Scharnier		"       ccdconfig -C [-v] [-f config_file]",
447141611Sru		"       ccdconfig -u [-v] ccd ...",
44826541Scharnier		"       ccdconfig -U [-v] [-f config_file]",
449141611Sru		"       ccdconfig -g [ccd ...]");
45013044Sasami	exit(1);
45113044Sasami}
452