1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2003 Poul-Henning Kamp
5 * Copyright (c) 1996 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Jason R. Thorpe.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *
32 * NetBSD: ccdconfig.c,v 1.6 1996/05/16 07:11:18 thorpej Exp $
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD$");
37
38#include <sys/param.h>
39#include <sys/linker.h>
40#include <sys/module.h>
41#include <ctype.h>
42#include <err.h>
43#include <errno.h>
44#include <limits.h>
45#include <paths.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <unistd.h>
50#include <libgeom.h>
51
52#define CCDF_UNIFORM    0x02    /* use LCCD of sizes for uniform interleave */
53#define CCDF_MIRROR     0x04    /* use mirroring */
54#define CCDF_NO_OFFSET  0x08    /* do not leave space in front */
55#define CCDF_LINUX      0x10    /* use Linux compatibility mode */
56
57#include "pathnames.h"
58
59static	int lineno = 0;
60static	int verbose = 0;
61static	const char *ccdconf = _PATH_CCDCONF;
62
63static struct flagval {
64	const char	*fv_flag;
65	int		fv_val;
66} flagvaltab[] = {
67	{ "CCDF_UNIFORM",	CCDF_UNIFORM },
68	{ "uniform",		CCDF_UNIFORM },
69	{ "CCDF_MIRROR",	CCDF_MIRROR },
70	{ "mirror",		CCDF_MIRROR },
71	{ "CCDF_NO_OFFSET",	CCDF_NO_OFFSET },
72	{ "no_offset",		CCDF_NO_OFFSET },
73	{ "CCDF_LINUX",		CCDF_LINUX },
74	{ "linux",		CCDF_LINUX },
75	{ "none",		0 },
76	{ NULL,			0 },
77};
78
79#define CCD_CONFIG		0	/* configure a device */
80#define CCD_CONFIGALL		1	/* configure all devices */
81#define CCD_UNCONFIG		2	/* unconfigure a device */
82#define CCD_UNCONFIGALL		3	/* unconfigure all devices */
83#define CCD_DUMP		4	/* dump a ccd's configuration */
84
85static	int do_single(int, char **, int);
86static	int do_all(int);
87static	int dump_ccd(int, char **);
88static	int flags_to_val(char *);
89static	int resolve_ccdname(char *);
90static	void usage(void);
91
92int
93main(int argc, char *argv[])
94{
95	int ch, options = 0, action = CCD_CONFIG;
96
97	while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) {
98		switch (ch) {
99		case 'c':
100			action = CCD_CONFIG;
101			++options;
102			break;
103
104		case 'C':
105			action = CCD_CONFIGALL;
106			++options;
107			break;
108
109		case 'f':
110			ccdconf = optarg;
111			break;
112
113		case 'g':
114			action = CCD_DUMP;
115			break;
116
117		case 'u':
118			action = CCD_UNCONFIG;
119			++options;
120			break;
121
122		case 'U':
123			action = CCD_UNCONFIGALL;
124			++options;
125			break;
126
127		case 'v':
128			verbose = 1;
129			break;
130
131		default:
132			usage();
133		}
134	}
135	argc -= optind;
136	argv += optind;
137
138	if (options > 1)
139		usage();
140
141	if (modfind("g_ccd") < 0) {
142		/* Not present in kernel, try loading it */
143		if (kldload("geom_ccd") < 0 || modfind("g_ccd") < 0)
144			warn("geom_ccd module not available!");
145	}
146
147	switch (action) {
148		case CCD_CONFIG:
149		case CCD_UNCONFIG:
150			exit(do_single(argc, argv, action));
151			/* NOTREACHED */
152
153		case CCD_CONFIGALL:
154		case CCD_UNCONFIGALL:
155			exit(do_all(action));
156			/* NOTREACHED */
157
158		case CCD_DUMP:
159			exit(dump_ccd(argc, argv));
160			/* NOTREACHED */
161	}
162	/* NOTREACHED */
163	return (0);
164}
165
166static int
167do_single(int argc, char **argv, int action)
168{
169	char *cp, *cp2;
170	int ccd, noflags = 0, i, ileave, flags = 0;
171	struct gctl_req *grq;
172	char const *errstr;
173	char buf1[BUFSIZ];
174	int ex;
175
176	/*
177	 * If unconfiguring, all arguments are treated as ccds.
178	 */
179	if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) {
180		ex = 0;
181		for (; argc != 0;) {
182			cp = *argv++; --argc;
183			if ((ccd = resolve_ccdname(cp)) < 0) {
184				warnx("invalid ccd name: %s", cp);
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	if (setegid(getgid()) != 0)
293		err(1, "setegid failed");
294	if ((f = fopen(ccdconf, "r")) == NULL) {
295		if (setegid(egid) != 0)
296			err(1, "setegid failed");
297		warn("fopen: %s", ccdconf);
298		return (1);
299	}
300	if (setegid(egid) != 0)
301		err(1, "setegid failed");
302
303	while (fgets(line, sizeof(line), f) != NULL) {
304		argc = 0;
305		argv = NULL;
306		++lineno;
307		if ((cp = strrchr(line, '\n')) != NULL)
308			*cp = '\0';
309
310		/* Break up the line and pass it's contents to do_single(). */
311		if (line[0] == '\0')
312			goto end_of_line;
313		for (cp = line; (cp = strtok(cp, " \t")) != NULL; cp = NULL) {
314			if (*cp == '#')
315				break;
316			if ((argv = realloc(argv,
317			    sizeof(char *) * ++argc)) == NULL) {
318				warnx("no memory to configure ccds");
319				return (1);
320			}
321			argv[argc - 1] = cp;
322			/*
323			 * If our action is to unconfigure all, then pass
324			 * just the first token to do_single() and ignore
325			 * the rest.  Since this will be encountered on
326			 * our first pass through the line, the Right
327			 * Thing will happen.
328			 */
329			if (action == CCD_UNCONFIGALL) {
330				if (do_single(argc, argv, action))
331					rval = 1;
332				goto end_of_line;
333			}
334		}
335		if (argc != 0)
336			if (do_single(argc, argv, action))
337				rval = 1;
338
339 end_of_line:
340		if (argv != NULL)
341			free(argv);
342	}
343
344	(void)fclose(f);
345	return (rval);
346}
347
348static int
349resolve_ccdname(char *name)
350{
351
352	if (!strncmp(name, _PATH_DEV, strlen(_PATH_DEV)))
353		name += strlen(_PATH_DEV);
354	if (strncmp(name, "ccd", 3))
355		return -1;
356	name += 3;
357	if (!isdigit(*name))
358		return -1;
359	return (strtoul(name, NULL, 10));
360}
361
362static int
363dumpout(int unit)
364{
365	static int v;
366	struct gctl_req *grq;
367	int ncp;
368	char *cp;
369	char const *errstr;
370
371	grq = gctl_get_handle();
372	ncp = 65536;
373	cp = malloc(ncp);
374	gctl_ro_param(grq, "verb", -1, "list");
375	gctl_ro_param(grq, "class", -1, "CCD");
376	gctl_ro_param(grq, "unit", sizeof(unit), &unit);
377	gctl_rw_param(grq, "output", ncp, cp);
378	errstr = gctl_issue(grq);
379	if (errstr != NULL)
380		errx(1, "%s\nor possibly kernel and ccdconfig out of sync",
381			errstr);
382	if (strlen(cp) == 0)
383		errx(1, "ccd%d not configured", unit);
384	if (verbose && !v) {
385		printf("# ccd\t\tileave\tflags\tcomponent devices\n");
386		v = 1;
387	}
388	printf("%s", cp);
389	free(cp);
390	return (0);
391}
392
393static int
394dump_ccd(int argc, char **argv)
395{
396	int i, error;
397
398	if (argc == 0) {
399		error = dumpout(-1);
400	} else {
401		error = 0;
402		for (i = 0; error == 0 && i < argc; i++)
403			error = dumpout(resolve_ccdname(argv[i]));
404	}
405	return (error);
406}
407
408static int
409flags_to_val(char *flags)
410{
411	char *cp, *tok;
412	int i, tmp, val;
413
414	errno = 0;	/* to check for ERANGE */
415	val = (int)strtol(flags, &cp, 0);
416	if ((errno != ERANGE) && (*cp == '\0')) {
417		if (val & ~(CCDF_UNIFORM|CCDF_MIRROR))
418			return (-1);
419		return (val);
420	}
421
422	/* Check for values represented by strings. */
423	if ((cp = strdup(flags)) == NULL)
424		err(1, "no memory to parse flags");
425	tmp = 0;
426	for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) {
427		for (i = 0; flagvaltab[i].fv_flag != NULL; ++i)
428			if (strcmp(tok, flagvaltab[i].fv_flag) == 0)
429				break;
430		if (flagvaltab[i].fv_flag == NULL) {
431			free(cp);
432			return (-1);
433		}
434		tmp |= flagvaltab[i].fv_val;
435	}
436
437	/* If we get here, the string was ok. */
438	free(cp);
439	return (tmp);
440}
441
442static void
443usage(void)
444{
445	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
446		"usage: ccdconfig [-cv] ccd ileave [flags] dev ...",
447		"       ccdconfig -C [-v] [-f config_file]",
448		"       ccdconfig -u [-v] ccd ...",
449		"       ccdconfig -U [-v] [-f config_file]",
450		"       ccdconfig -g [ccd ...]");
451	exit(1);
452}
453