ccdconfig.c revision 114589
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 114589 2003-05-03 18:41:59Z obrien $");
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
54#include <sys/devicestat.h>
55#include <sys/ccdvar.h>
56
57#include "pathnames.h"
58
59static	int lineno = 0;
60static	int verbose = 0;
61static	const char *ccdconf = _PATH_CCDCONF;
62
63struct	flagval {
64	const char	*fv_flag;
65	int	fv_val;
66} flagvaltab[] = {
67	{ "CCDF_UNIFORM",	CCDF_UNIFORM },
68	{ "CCDF_MIRROR",	CCDF_MIRROR },
69	{ NULL,			0 },
70};
71
72#define CCD_CONFIG		0	/* configure a device */
73#define CCD_CONFIGALL		1	/* configure all devices */
74#define CCD_UNCONFIG		2	/* unconfigure a device */
75#define CCD_UNCONFIGALL		3	/* unconfigure all devices */
76#define CCD_DUMP		4	/* dump a ccd's configuration */
77
78static	int checkdev(char *);
79static	int do_io(int, u_long, struct ccd_ioctl *);
80static	int do_single(int, char **, int);
81static	int do_all(int);
82static	int dump_ccd(int, char **);
83static	int flags_to_val(char *);
84static	void print_ccd_info(struct ccd_s *);
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		case CCDCONFINFO:
408			cp = "CCDCONFINFO";
409			break;
410
411		case CCDCPPINFO:
412			cp = "CCDCPPINFO";
413			break;
414
415		default:
416			cp = "unknown";
417		}
418		warn("ioctl (%s): %s", cp, path);
419		return (1);
420	}
421
422	return (0);
423}
424
425static int
426dump_ccd(int argc, char **argv)
427{
428	char *cp;
429	int i, error, numccd, numconfiged = 0;
430	struct ccdconf conf;
431	int ccd;
432
433	/*
434	 * Read the ccd configuration data from the kernel and dump
435	 * it to stdout.
436	 */
437	if ((ccd = resolve_ccdname("ccd0")) < 0) {		/* XXX */
438		warnx("invalid ccd name: %s", cp);
439		return (1);
440	}
441	conf.size = 0;
442	if (do_io(ccd, CCDCONFINFO, (struct ccd_ioctl *) &conf))
443		return (1);
444	if (conf.size == 0) {
445		printf("no concatenated disks configured\n");
446		return (0);
447	}
448	/* Allocate space for the configuration data. */
449	conf.buffer = alloca(conf.size);
450	if (conf.buffer == NULL) {
451		warnx("no memory for configuration data");
452		return (1);
453	}
454	if (do_io(ccd, CCDCONFINFO, (struct ccd_ioctl *) &conf))
455		return (1);
456
457	numconfiged = conf.size / sizeof(struct ccd_s);
458
459	if (argc == 0) {
460		for (i = 0; i < numconfiged; i++)
461			print_ccd_info(&(conf.buffer[i]));
462	} else {
463		while (argc) {
464			cp = *argv++; --argc;
465			if ((ccd = resolve_ccdname(cp)) < 0) {
466				warnx("invalid ccd name: %s", cp);
467				continue;
468			}
469			error = 1;
470			for (i = 0; i < numconfiged; i++) {
471				if (conf.buffer[i].sc_unit == ccd) {
472					print_ccd_info(&(conf.buffer[i]));
473					error = 0;
474					break;
475				}
476			}
477			if (error) {
478				warnx("ccd%d not configured", numccd);
479				continue;
480			}
481		}
482	}
483
484	return (0);
485}
486
487static void
488print_ccd_info(struct ccd_s *cs)
489{
490	char *cp;
491	static int header_printed = 0;
492	struct ccdcpps cpps;
493	int ccd;
494
495	/* Print out header if necessary*/
496	if (header_printed == 0 && verbose) {
497		printf("# ccd\t\tileave\tflags\tcompnent devices\n");
498		header_printed = 1;
499	}
500
501	/* Dump out softc information. */
502	printf("ccd%d\t\t%d\t%d\t", cs->sc_unit, cs->sc_ileave,
503	    cs->sc_cflags & CCDF_USERMASK);
504	fflush(stdout);
505
506	/* Read in the component info. */
507	asprintf(&cp, "ccd%d", cs->sc_unit);
508	if (cp == NULL) {
509		printf("\n");
510		warn("ccd%d: can't allocate memory",
511		    cs->sc_unit);
512		return;
513	}
514
515	if ((ccd = resolve_ccdname(cp)) < 0) {
516		printf("\n");
517		warnx("can't read component info: invalid ccd name: %s", cp);
518		return;
519	}
520	cpps.size = 1024;
521	cpps.buffer = alloca(cpps.size);
522	memcpy(cpps.buffer, &ccd, sizeof ccd);
523	if (do_io(ccd, CCDCPPINFO, (struct ccd_ioctl *) &cpps)) {
524		printf("\n");
525		warnx("can't read component info");
526		return;
527	}
528
529	/* Display component info. */
530	for (cp = cpps.buffer; *cp && cp - cpps.buffer < cpps.size; cp += strlen(cp) + 1) {
531		printf((cp + strlen(cp) + 1) < (cpps.buffer + cpps.size) ?
532		    "%s " : "%s", cp);
533	}
534	printf("\n");
535	return;
536}
537
538static int
539flags_to_val(char *flags)
540{
541	char *cp, *tok;
542	int i, tmp, val = ~CCDF_USERMASK;
543	size_t flagslen;
544
545	/*
546	 * The most common case is that of NIL flags, so check for
547	 * those first.
548	 */
549	if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 ||
550	    strcmp("0", flags) == 0)
551		return (0);
552
553	flagslen = strlen(flags);
554
555	/* Check for values represented by strings. */
556	if ((cp = strdup(flags)) == NULL)
557		err(1, "no memory to parse flags");
558	tmp = 0;
559	for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) {
560		for (i = 0; flagvaltab[i].fv_flag != NULL; ++i)
561			if (strcmp(tok, flagvaltab[i].fv_flag) == 0)
562				break;
563		if (flagvaltab[i].fv_flag == NULL) {
564			free(cp);
565			goto bad_string;
566		}
567		tmp |= flagvaltab[i].fv_val;
568	}
569
570	/* If we get here, the string was ok. */
571	free(cp);
572	val = tmp;
573	goto out;
574
575 bad_string:
576
577	/* Check for values represented in hex. */
578	if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') {
579		errno = 0;	/* to check for ERANGE */
580		val = (int)strtol(&flags[2], &cp, 16);
581		if ((errno == ERANGE) || (*cp != '\0'))
582			return (-1);
583		goto out;
584	}
585
586	/* Check for values represented in decimal. */
587	errno = 0;	/* to check for ERANGE */
588	val = (int)strtol(flags, &cp, 10);
589	if ((errno == ERANGE) || (*cp != '\0'))
590		return (-1);
591
592 out:
593	return (((val & ~CCDF_USERMASK) == 0) ? val : -1);
594}
595
596static void
597usage(void)
598{
599	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
600		"usage: ccdconfig [-cv] ccd ileave [flags] dev [...]",
601		"       ccdconfig -C [-v] [-f config_file]",
602		"       ccdconfig -u [-v] ccd [...]",
603		"       ccdconfig -U [-v] [-f config_file]",
604		"       ccdconfig -g [ccd [...]]");
605	exit(1);
606}
607
608/* Local Variables: */
609/* c-argdecl-indent: 8 */
610/* c-indent-level: 8 */
611/* End: */
612