ccdconfig.c revision 115731
1/*	$NetBSD: ccdconfig.c,v 1.2.2.1 1995/11/11 02:43:35 thorpej Exp $	*/
2
3/*
4 * Copyright (c) 1995 Jason R. Thorpe.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed for the NetBSD Project
18 *	by Jason R. Thorpe.
19 * 4. The name of the author may not be used to endorse or promote products
20 *    derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: head/sbin/ccdconfig/ccdconfig.c 115731 2003-06-02 21:29:04Z phk $");
37
38#include <sys/param.h>
39#include <sys/linker.h>
40#include <sys/disklabel.h>
41#include <sys/stat.h>
42#include <sys/module.h>
43#include <ctype.h>
44#include <err.h>
45#include <errno.h>
46#include <fcntl.h>
47#include <limits.h>
48#include <paths.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <string.h>
52#include <unistd.h>
53#include <libgeom.h>
54
55#include <sys/devicestat.h>
56#include <sys/ccdvar.h>
57
58#include "pathnames.h"
59
60static	int lineno = 0;
61static	int verbose = 0;
62static	const char *ccdconf = _PATH_CCDCONF;
63
64struct	flagval {
65	const char	*fv_flag;
66	int	fv_val;
67} flagvaltab[] = {
68	{ "CCDF_UNIFORM",	CCDF_UNIFORM },
69	{ "CCDF_MIRROR",	CCDF_MIRROR },
70	{ NULL,			0 },
71};
72
73#define CCD_CONFIG		0	/* configure a device */
74#define CCD_CONFIGALL		1	/* configure all devices */
75#define CCD_UNCONFIG		2	/* unconfigure a device */
76#define CCD_UNCONFIGALL		3	/* unconfigure all devices */
77#define CCD_DUMP		4	/* dump a ccd's configuration */
78
79static	int checkdev(char *);
80static	int do_io(int, u_long, struct ccd_ioctl *);
81static	int do_single(int, char **, int);
82static	int do_all(int);
83static	int dump_ccd(int, char **);
84static	int flags_to_val(char *);
85static	int resolve_ccdname(char *);
86static	void usage(void);
87
88int
89main(int argc, char *argv[])
90{
91	int ch, options = 0, action = CCD_CONFIG;
92
93	while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) {
94		switch (ch) {
95		case 'c':
96			action = CCD_CONFIG;
97			++options;
98			break;
99
100		case 'C':
101			action = CCD_CONFIGALL;
102			++options;
103			break;
104
105		case 'f':
106			ccdconf = optarg;
107			break;
108
109		case 'g':
110			action = CCD_DUMP;
111			break;
112
113		case 'u':
114			action = CCD_UNCONFIG;
115			++options;
116			break;
117
118		case 'U':
119			action = CCD_UNCONFIGALL;
120			++options;
121			break;
122
123		case 'v':
124			verbose = 1;
125			break;
126
127		default:
128			usage();
129		}
130	}
131	argc -= optind;
132	argv += optind;
133
134	if (options > 1)
135		usage();
136
137	if (modfind("ccd") < 0) {
138		/* Not present in kernel, try loading it */
139		if (kldload("ccd") < 0 || modfind("ccd") < 0)
140			warn("ccd module not available!");
141	}
142
143	switch (action) {
144		case CCD_CONFIG:
145		case CCD_UNCONFIG:
146			exit(do_single(argc, argv, action));
147			/* NOTREACHED */
148
149		case CCD_CONFIGALL:
150		case CCD_UNCONFIGALL:
151			exit(do_all(action));
152			/* NOTREACHED */
153
154		case CCD_DUMP:
155			exit(dump_ccd(argc, argv));
156			/* NOTREACHED */
157	}
158	/* NOTREACHED */
159	return (0);
160}
161
162static int
163do_single(int argc, char **argv, int action)
164{
165	struct ccd_ioctl ccio;
166	char *cp, *cp2, **disks;
167	int ccd, noflags = 0, i, ileave, flags = 0, j;
168	u_int u;
169
170	bzero(&ccio, sizeof(ccio));
171
172	/*
173	 * If unconfiguring, all arguments are treated as ccds.
174	 */
175	if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) {
176		for (i = 0; argc != 0; ) {
177			cp = *argv++; --argc;
178			if ((ccd = resolve_ccdname(cp)) < 0) {
179				warnx("invalid ccd name: %s", cp);
180				i = 1;
181				continue;
182			}
183			ccio.ccio_size = ccd;
184			if (do_io(ccd, CCDIOCCLR, &ccio))
185				i = 1;
186			else
187				if (verbose)
188					printf("%s unconfigured\n", cp);
189		}
190		return (i);
191	}
192
193	/* Make sure there are enough arguments. */
194	if (argc < 4) {
195		if (argc == 3) {
196			/* Assume that no flags are specified. */
197			noflags = 1;
198		} else {
199			if (action == CCD_CONFIGALL) {
200				warnx("%s: bad line: %d", ccdconf, lineno);
201				return (1);
202			} else
203				usage();
204		}
205	}
206
207	/* First argument is the ccd to configure. */
208	cp = *argv++; --argc;
209	if ((ccd = resolve_ccdname(cp)) < 0) {
210		warnx("invalid ccd name: %s", cp);
211		return (1);
212	}
213
214	/* Next argument is the interleave factor. */
215	cp = *argv++; --argc;
216	errno = 0;	/* to check for ERANGE */
217	ileave = (int)strtol(cp, &cp2, 10);
218	if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) {
219		warnx("invalid interleave factor: %s", cp);
220		return (1);
221	}
222
223	if (noflags == 0) {
224		/* Next argument is the ccd configuration flags. */
225		cp = *argv++; --argc;
226		if ((flags = flags_to_val(cp)) < 0) {
227			warnx("invalid flags argument: %s", cp);
228			return (1);
229		}
230	}
231
232	/* Next is the list of disks to make the ccd from. */
233	disks = malloc(argc * sizeof(char *));
234	if (disks == NULL) {
235		warnx("no memory to configure ccd");
236		return (1);
237	}
238	for (i = 0; argc != 0; ) {
239		cp = *argv++; --argc;
240		if ((j = checkdev(cp)) == 0)
241			disks[i++] = cp;
242		else {
243			warnx("%s: %s", cp, strerror(j));
244			return (1);
245		}
246	}
247
248	/* Fill in the ccio. */
249	ccio.ccio_disks = disks;
250	ccio.ccio_ndisks = i;
251	ccio.ccio_ileave = ileave;
252	ccio.ccio_flags = flags;
253	ccio.ccio_size = ccd;
254
255	if (do_io(ccd, CCDIOCSET, &ccio)) {
256		free(disks);
257		return (1);
258	}
259
260	if (verbose) {
261		printf("ccd%d: %d components ", ccio.ccio_unit,
262		    ccio.ccio_ndisks);
263		for (u = 0; u < ccio.ccio_ndisks; ++u) {
264			if ((cp2 = strrchr(disks[u], '/')) != NULL)
265				++cp2;
266			else
267				cp2 = disks[u];
268			printf("%c%s%c",
269			    u == 0 ? '(' : ' ', cp2,
270			    u == ccio.ccio_ndisks - 1 ? ')' : ',');
271		}
272		printf(", %lu blocks ", (u_long)ccio.ccio_size);
273		if (ccio.ccio_ileave != 0)
274			printf("interleaved at %d blocks\n", ccio.ccio_ileave);
275		else
276			printf("concatenated\n");
277	}
278
279	free(disks);
280	return (0);
281}
282
283static int
284do_all(int action)
285{
286	FILE *f;
287	char line[_POSIX2_LINE_MAX];
288	char *cp, **argv;
289	int argc, rval;
290	gid_t egid;
291
292	rval = 0;
293	egid = getegid();
294	setegid(getgid());
295	if ((f = fopen(ccdconf, "r")) == NULL) {
296		setegid(egid);
297		warn("fopen: %s", ccdconf);
298		return (1);
299	}
300	setegid(egid);
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
348checkdev(char *path)
349{
350	struct stat st;
351
352	if (stat(path, &st) != 0)
353		return (errno);
354
355	if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
356		return (EINVAL);
357
358	return (0);
359}
360
361static int
362resolve_ccdname(char *name)
363{
364
365	if (!strncmp(name, _PATH_DEV, strlen(_PATH_DEV)))
366		name += strlen(_PATH_DEV);
367	if (strncmp(name, "ccd", 3))
368		return -1;
369	name += 3;
370	if (!isdigit(*name))
371		return -1;
372	return (strtoul(name, NULL, 10));
373}
374
375static int
376do_io(int unit, u_long cmd, struct ccd_ioctl *cciop)
377{
378	int fd;
379	char *cp;
380	char *path;
381
382	asprintf(&path, "%s%s", _PATH_DEV, _PATH_CCDCTL);
383
384	if ((fd = open(path, O_RDWR, 0640)) < 0) {
385		asprintf(&path, "%sccd%dc", _PATH_DEV, unit);
386		if ((fd = open(path, O_RDWR, 0640)) < 0) {
387			warn("open: %s", path);
388			return (1);
389		}
390		fprintf(stderr,
391		    "***WARNING***: Kernel older than ccdconfig(8), please upgrade it.\n");
392		fprintf(stderr,
393		    "***WARNING***: Continuing in 30 seconds\n");
394		sleep(30);
395	}
396
397	if (ioctl(fd, cmd, cciop) < 0) {
398		switch (cmd) {
399		case CCDIOCSET:
400			cp = "CCDIOCSET";
401			break;
402
403		case CCDIOCCLR:
404			cp = "CCDIOCCLR";
405			break;
406
407		default:
408			cp = "unknown";
409		}
410		warn("ioctl (%s): %s", cp, path);
411		return (1);
412	}
413
414	return (0);
415}
416
417static int
418dumpout(int unit)
419{
420	static int v;
421	struct gctl_req *grq;
422	int ncp;
423	char *cp;
424	char const *errstr;
425
426
427	grq = gctl_get_handle();
428	ncp = 65536;
429	cp = malloc(ncp);
430	gctl_ro_param(grq, "verb", -1, "list");
431	gctl_ro_param(grq, "class", -1, "CCD");
432	gctl_ro_param(grq, "unit", sizeof(unit), &unit);
433	gctl_rw_param(grq, "output", ncp, cp);
434	errstr = gctl_issue(grq);
435	if (errstr != NULL)
436		errx(1, "%s\nor possibly kernel and ccdconfig out of sync",
437			errstr);
438	if (strlen(cp) == 0)
439		errx(1, "ccd%d not configured", unit);
440	if (verbose && !v) {
441		printf("# ccd\t\tileave\tflags\tcomponent devices\n");
442		v = 1;
443	}
444	printf("%s", cp);
445	free(cp);
446	return (0);
447}
448
449static int
450dump_ccd(int argc, char **argv)
451{
452	int i, err;
453
454	if (argc == 0) {
455		err = dumpout(-1);
456	} else {
457		err = 0;
458		for (i = 0; err == 0 && i < argc; i++)
459			err = dumpout(resolve_ccdname(argv[i]));
460	}
461	return (err);
462}
463
464static int
465flags_to_val(char *flags)
466{
467	char *cp, *tok;
468	int i, tmp, val = ~CCDF_USERMASK;
469	size_t flagslen;
470
471	/*
472	 * The most common case is that of NIL flags, so check for
473	 * those first.
474	 */
475	if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 ||
476	    strcmp("0", flags) == 0)
477		return (0);
478
479	flagslen = strlen(flags);
480
481	/* Check for values represented by strings. */
482	if ((cp = strdup(flags)) == NULL)
483		err(1, "no memory to parse flags");
484	tmp = 0;
485	for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) {
486		for (i = 0; flagvaltab[i].fv_flag != NULL; ++i)
487			if (strcmp(tok, flagvaltab[i].fv_flag) == 0)
488				break;
489		if (flagvaltab[i].fv_flag == NULL) {
490			free(cp);
491			goto bad_string;
492		}
493		tmp |= flagvaltab[i].fv_val;
494	}
495
496	/* If we get here, the string was ok. */
497	free(cp);
498	val = tmp;
499	goto out;
500
501 bad_string:
502
503	/* Check for values represented in hex. */
504	if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') {
505		errno = 0;	/* to check for ERANGE */
506		val = (int)strtol(&flags[2], &cp, 16);
507		if ((errno == ERANGE) || (*cp != '\0'))
508			return (-1);
509		goto out;
510	}
511
512	/* Check for values represented in decimal. */
513	errno = 0;	/* to check for ERANGE */
514	val = (int)strtol(flags, &cp, 10);
515	if ((errno == ERANGE) || (*cp != '\0'))
516		return (-1);
517
518 out:
519	return (((val & ~CCDF_USERMASK) == 0) ? val : -1);
520}
521
522static void
523usage(void)
524{
525	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
526		"usage: ccdconfig [-cv] ccd ileave [flags] dev [...]",
527		"       ccdconfig -C [-v] [-f config_file]",
528		"       ccdconfig -u [-v] ccd [...]",
529		"       ccdconfig -U [-v] [-f config_file]",
530		"       ccdconfig -g [ccd [...]]");
531	exit(1);
532}
533
534/* Local Variables: */
535/* c-argdecl-indent: 8 */
536/* c-indent-level: 8 */
537/* End: */
538