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$");
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
62static struct 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 (; argc != 0;) {
181			cp = *argv++; --argc;
182			if ((ccd = resolve_ccdname(cp)) < 0) {
183				warnx("invalid ccd name: %s", cp);
184				continue;
185			}
186			grq = gctl_get_handle();
187			gctl_ro_param(grq, "verb", -1, "destroy geom");
188			gctl_ro_param(grq, "class", -1, "CCD");
189			sprintf(buf1, "ccd%d", ccd);
190			gctl_ro_param(grq, "geom", -1, buf1);
191			errstr = gctl_issue(grq);
192			if (errstr == NULL) {
193				if (verbose)
194					printf("%s unconfigured\n", cp);
195				gctl_free(grq);
196				continue;
197			}
198			warnx(
199			    "%s\nor possibly kernel and ccdconfig out of sync",
200			    errstr);
201			ex = 1;
202		}
203		return (ex);
204	}
205
206	/* Make sure there are enough arguments. */
207	if (argc < 4) {
208		if (argc == 3) {
209			/* Assume that no flags are specified. */
210			noflags = 1;
211		} else {
212			if (action == CCD_CONFIGALL) {
213				warnx("%s: bad line: %d", ccdconf, lineno);
214				return (1);
215			} else
216				usage();
217		}
218	}
219
220	/* First argument is the ccd to configure. */
221	cp = *argv++; --argc;
222	if ((ccd = resolve_ccdname(cp)) < 0) {
223		warnx("invalid ccd name: %s", cp);
224		return (1);
225	}
226
227	/* Next argument is the interleave factor. */
228	cp = *argv++; --argc;
229	errno = 0;	/* to check for ERANGE */
230	ileave = (int)strtol(cp, &cp2, 10);
231	if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) {
232		warnx("invalid interleave factor: %s", cp);
233		return (1);
234	}
235
236	if (noflags == 0) {
237		/* Next argument is the ccd configuration flags. */
238		cp = *argv++; --argc;
239		if ((flags = flags_to_val(cp)) < 0) {
240			warnx("invalid flags argument: %s", cp);
241			return (1);
242		}
243	}
244	grq = gctl_get_handle();
245	gctl_ro_param(grq, "verb", -1, "create geom");
246	gctl_ro_param(grq, "class", -1, "CCD");
247	gctl_ro_param(grq, "unit", sizeof(ccd), &ccd);
248	gctl_ro_param(grq, "ileave", sizeof(ileave), &ileave);
249	if (flags & CCDF_UNIFORM)
250		gctl_ro_param(grq, "uniform", -1, "");
251	if (flags & CCDF_MIRROR)
252		gctl_ro_param(grq, "mirror", -1, "");
253	if (flags & CCDF_NO_OFFSET)
254		gctl_ro_param(grq, "no_offset", -1, "");
255	if (flags & CCDF_LINUX)
256		gctl_ro_param(grq, "linux", -1, "");
257	gctl_ro_param(grq, "nprovider", sizeof(argc), &argc);
258	for (i = 0; i < argc; i++) {
259		sprintf(buf1, "provider%d", i);
260		cp = argv[i];
261		if (!strncmp(cp, _PATH_DEV, strlen(_PATH_DEV)))
262			cp += strlen(_PATH_DEV);
263		gctl_ro_param(grq, buf1, -1, cp);
264	}
265	gctl_rw_param(grq, "output", sizeof(buf1), buf1);
266	errstr = gctl_issue(grq);
267	if (errstr == NULL) {
268		if (verbose) {
269			printf("%s", buf1);
270		}
271		gctl_free(grq);
272		return (0);
273	}
274	warnx(
275	    "%s\nor possibly kernel and ccdconfig out of sync",
276	    errstr);
277	return (1);
278}
279
280static int
281do_all(int action)
282{
283	FILE *f;
284	char line[_POSIX2_LINE_MAX];
285	char *cp, **argv;
286	int argc, rval;
287	gid_t egid;
288
289	rval = 0;
290	egid = getegid();
291	if (setegid(getgid()) != 0)
292		err(1, "setegid failed");
293	if ((f = fopen(ccdconf, "r")) == NULL) {
294		if (setegid(egid) != 0)
295			err(1, "setegid failed");
296		warn("fopen: %s", ccdconf);
297		return (1);
298	}
299	if (setegid(egid) != 0)
300		err(1, "setegid failed");
301
302	while (fgets(line, sizeof(line), f) != NULL) {
303		argc = 0;
304		argv = NULL;
305		++lineno;
306		if ((cp = strrchr(line, '\n')) != NULL)
307			*cp = '\0';
308
309		/* Break up the line and pass it's contents to do_single(). */
310		if (line[0] == '\0')
311			goto end_of_line;
312		for (cp = line; (cp = strtok(cp, " \t")) != NULL; cp = NULL) {
313			if (*cp == '#')
314				break;
315			if ((argv = realloc(argv,
316			    sizeof(char *) * ++argc)) == NULL) {
317				warnx("no memory to configure ccds");
318				return (1);
319			}
320			argv[argc - 1] = cp;
321			/*
322			 * If our action is to unconfigure all, then pass
323			 * just the first token to do_single() and ignore
324			 * the rest.  Since this will be encountered on
325			 * our first pass through the line, the Right
326			 * Thing will happen.
327			 */
328			if (action == CCD_UNCONFIGALL) {
329				if (do_single(argc, argv, action))
330					rval = 1;
331				goto end_of_line;
332			}
333		}
334		if (argc != 0)
335			if (do_single(argc, argv, action))
336				rval = 1;
337
338 end_of_line:
339		if (argv != NULL)
340			free(argv);
341	}
342
343	(void)fclose(f);
344	return (rval);
345}
346
347static int
348resolve_ccdname(char *name)
349{
350
351	if (!strncmp(name, _PATH_DEV, strlen(_PATH_DEV)))
352		name += strlen(_PATH_DEV);
353	if (strncmp(name, "ccd", 3))
354		return -1;
355	name += 3;
356	if (!isdigit(*name))
357		return -1;
358	return (strtoul(name, NULL, 10));
359}
360
361static int
362dumpout(int unit)
363{
364	static int v;
365	struct gctl_req *grq;
366	int ncp;
367	char *cp;
368	char const *errstr;
369
370	grq = gctl_get_handle();
371	ncp = 65536;
372	cp = malloc(ncp);
373	gctl_ro_param(grq, "verb", -1, "list");
374	gctl_ro_param(grq, "class", -1, "CCD");
375	gctl_ro_param(grq, "unit", sizeof(unit), &unit);
376	gctl_rw_param(grq, "output", ncp, cp);
377	errstr = gctl_issue(grq);
378	if (errstr != NULL)
379		errx(1, "%s\nor possibly kernel and ccdconfig out of sync",
380			errstr);
381	if (strlen(cp) == 0)
382		errx(1, "ccd%d not configured", unit);
383	if (verbose && !v) {
384		printf("# ccd\t\tileave\tflags\tcomponent devices\n");
385		v = 1;
386	}
387	printf("%s", cp);
388	free(cp);
389	return (0);
390}
391
392static int
393dump_ccd(int argc, char **argv)
394{
395	int i, error;
396
397	if (argc == 0) {
398		error = dumpout(-1);
399	} else {
400		error = 0;
401		for (i = 0; error == 0 && i < argc; i++)
402			error = dumpout(resolve_ccdname(argv[i]));
403	}
404	return (error);
405}
406
407static int
408flags_to_val(char *flags)
409{
410	char *cp, *tok;
411	int i, tmp, val;
412
413	errno = 0;	/* to check for ERANGE */
414	val = (int)strtol(flags, &cp, 0);
415	if ((errno != ERANGE) && (*cp == '\0')) {
416		if (val & ~(CCDF_UNIFORM|CCDF_MIRROR))
417			return (-1);
418		return (val);
419	}
420
421	/* Check for values represented by strings. */
422	if ((cp = strdup(flags)) == NULL)
423		err(1, "no memory to parse flags");
424	tmp = 0;
425	for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) {
426		for (i = 0; flagvaltab[i].fv_flag != NULL; ++i)
427			if (strcmp(tok, flagvaltab[i].fv_flag) == 0)
428				break;
429		if (flagvaltab[i].fv_flag == NULL) {
430			free(cp);
431			return (-1);
432		}
433		tmp |= flagvaltab[i].fv_val;
434	}
435
436	/* If we get here, the string was ok. */
437	free(cp);
438	return (tmp);
439}
440
441static void
442usage(void)
443{
444	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
445		"usage: ccdconfig [-cv] ccd ileave [flags] dev ...",
446		"       ccdconfig -C [-v] [-f config_file]",
447		"       ccdconfig -u [-v] ccd ...",
448		"       ccdconfig -U [-v] [-f config_file]",
449		"       ccdconfig -g [ccd ...]");
450	exit(1);
451}
452