ccdconfig.c revision 92539
1219019Sgabor/*	$NetBSD: ccdconfig.c,v 1.2.2.1 1995/11/11 02:43:35 thorpej Exp $	*/
2219019Sgabor
3219019Sgabor/*
4219019Sgabor * Copyright (c) 1995 Jason R. Thorpe.
5219019Sgabor * All rights reserved.
6219019Sgabor *
7219019Sgabor * Redistribution and use in source and binary forms, with or without
8219019Sgabor * modification, are permitted provided that the following conditions
9219019Sgabor * are met:
10219019Sgabor * 1. Redistributions of source code must retain the above copyright
11219019Sgabor *    notice, this list of conditions and the following disclaimer.
12219019Sgabor * 2. Redistributions in binary form must reproduce the above copyright
13219019Sgabor *    notice, this list of conditions and the following disclaimer in the
14219019Sgabor *    documentation and/or other materials provided with the distribution.
15219019Sgabor * 3. All advertising materials mentioning features or use of this software
16219019Sgabor *    must display the following acknowledgement:
17219019Sgabor *	This product includes software developed for the NetBSD Project
18219019Sgabor *	by Jason R. Thorpe.
19219019Sgabor * 4. The name of the author may not be used to endorse or promote products
20219019Sgabor *    derived from this software without specific prior written permission.
21219019Sgabor *
22219019Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23219019Sgabor * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24219019Sgabor * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25219019Sgabor * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26219019Sgabor * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27219019Sgabor * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28219019Sgabor * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29219019Sgabor * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30219019Sgabor * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31219019Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32219019Sgabor * SUCH DAMAGE.
33219019Sgabor */
34219019Sgabor
35219019Sgabor#ifndef lint
36219019Sgaborstatic const char rcsid[] =
37219019Sgabor  "$FreeBSD: head/sbin/ccdconfig/ccdconfig.c 92539 2002-03-18 05:00:52Z imp $";
38219019Sgabor#endif /* not lint */
39219019Sgabor
40219019Sgabor#include <sys/param.h>
41219019Sgabor#include <sys/linker.h>
42219019Sgabor#include <sys/disklabel.h>
43219019Sgabor#include <sys/stat.h>
44219019Sgabor#include <sys/module.h>
45219019Sgabor#include <ctype.h>
46219019Sgabor#include <err.h>
47219019Sgabor#include <errno.h>
48219019Sgabor#include <fcntl.h>
49219019Sgabor#include <limits.h>
50219019Sgabor#include <paths.h>
51219019Sgabor#include <stdio.h>
52219019Sgabor#include <stdlib.h>
53219019Sgabor#include <string.h>
54219019Sgabor#include <unistd.h>
55219019Sgabor
56219019Sgabor#include <sys/devicestat.h>
57219019Sgabor#include <sys/ccdvar.h>
58219019Sgabor
59219019Sgabor#include "pathnames.h"
60219019Sgabor
61219019Sgaborstatic	int lineno = 0;
62219019Sgaborstatic	int verbose = 0;
63219019Sgaborstatic	char *ccdconf = _PATH_CCDCONF;
64219019Sgabor
65219019Sgaborstruct	flagval {
66219019Sgabor	char	*fv_flag;
67219019Sgabor	int	fv_val;
68219019Sgabor} flagvaltab[] = {
69219019Sgabor	{ "CCDF_SWAP",		CCDF_SWAP },
70219019Sgabor	{ "CCDF_UNIFORM",	CCDF_UNIFORM },
71219019Sgabor	{ "CCDF_MIRROR",	CCDF_MIRROR },
72219019Sgabor	{ "CCDF_PARITY",	CCDF_PARITY },
73219019Sgabor	{ NULL,			0 },
74219019Sgabor};
75219019Sgabor
76219019Sgabor#define CCD_CONFIG		0	/* configure a device */
77219019Sgabor#define CCD_CONFIGALL		1	/* configure all devices */
78219019Sgabor#define CCD_UNCONFIG		2	/* unconfigure a device */
79219019Sgabor#define CCD_UNCONFIGALL		3	/* unconfigure all devices */
80219019Sgabor#define CCD_DUMP		4	/* dump a ccd's configuration */
81219019Sgabor
82219019Sgaborstatic	int checkdev(char *);
83219019Sgaborstatic	int do_io(char *, u_long, struct ccd_ioctl *);
84219019Sgaborstatic	int do_single(int, char **, int);
85219019Sgaborstatic	int do_all(int);
86219019Sgaborstatic	int dump_ccd(int, char **);
87219019Sgaborstatic	int getmaxpartitions(void);
88219019Sgaborstatic	int getrawpartition(void);
89219019Sgaborstatic	int flags_to_val(char *);
90219019Sgaborstatic	void print_ccd_info(struct ccd_s *);
91219019Sgaborstatic	char *resolve_ccdname(char *);
92219019Sgaborstatic	void usage(void);
93219019Sgabor
94219019Sgaborint
95219019Sgabormain(int argc, char *argv[])
96219019Sgabor{
97219019Sgabor	int ch, options = 0, action = CCD_CONFIG;
98219019Sgabor
99219019Sgabor	while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) {
100219019Sgabor		switch (ch) {
101219019Sgabor		case 'c':
102219019Sgabor			action = CCD_CONFIG;
103			++options;
104			break;
105
106		case 'C':
107			action = CCD_CONFIGALL;
108			++options;
109			break;
110
111		case 'f':
112			ccdconf = optarg;
113			break;
114
115		case 'g':
116			action = CCD_DUMP;
117			break;
118
119		case 'u':
120			action = CCD_UNCONFIG;
121			++options;
122			break;
123
124		case 'U':
125			action = CCD_UNCONFIGALL;
126			++options;
127			break;
128
129		case 'v':
130			verbose = 1;
131			break;
132
133		default:
134			usage();
135		}
136	}
137	argc -= optind;
138	argv += optind;
139
140	if (options > 1)
141		usage();
142
143	if (modfind("ccd") < 0) {
144		/* Not present in kernel, try loading it */
145		if (kldload("ccd") < 0 || modfind("ccd") < 0)
146			warn("ccd module not available!");
147	}
148
149	switch (action) {
150		case CCD_CONFIG:
151		case CCD_UNCONFIG:
152			exit(do_single(argc, argv, action));
153			/* NOTREACHED */
154
155		case CCD_CONFIGALL:
156		case CCD_UNCONFIGALL:
157			exit(do_all(action));
158			/* NOTREACHED */
159
160		case CCD_DUMP:
161			exit(dump_ccd(argc, argv));
162			/* NOTREACHED */
163	}
164	/* NOTREACHED */
165	return (0);
166}
167
168static int
169do_single(int argc, char **argv, int action)
170{
171	struct ccd_ioctl ccio;
172	char *ccd, *cp, *cp2, **disks;
173	int noflags = 0, i, ileave, flags = 0, j;
174
175	bzero(&ccio, sizeof(ccio));
176
177	/*
178	 * If unconfiguring, all arguments are treated as ccds.
179	 */
180	if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) {
181		for (i = 0; argc != 0; ) {
182			cp = *argv++; --argc;
183			if ((ccd = resolve_ccdname(cp)) == NULL) {
184				warnx("invalid ccd name: %s", cp);
185				i = 1;
186				continue;
187			}
188			if (do_io(ccd, CCDIOCCLR, &ccio))
189				i = 1;
190			else
191				if (verbose)
192					printf("%s unconfigured\n", cp);
193		}
194		return (i);
195	}
196
197	/* Make sure there are enough arguments. */
198	if (argc < 4) {
199		if (argc == 3) {
200			/* Assume that no flags are specified. */
201			noflags = 1;
202		} else {
203			if (action == CCD_CONFIGALL) {
204				warnx("%s: bad line: %d", ccdconf, lineno);
205				return (1);
206			} else
207				usage();
208		}
209	}
210
211	/* First argument is the ccd to configure. */
212	cp = *argv++; --argc;
213	if ((ccd = resolve_ccdname(cp)) == NULL) {
214		warnx("invalid ccd name: %s", cp);
215		return (1);
216	}
217
218	/* Next argument is the interleave factor. */
219	cp = *argv++; --argc;
220	errno = 0;	/* to check for ERANGE */
221	ileave = (int)strtol(cp, &cp2, 10);
222	if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) {
223		warnx("invalid interleave factor: %s", cp);
224		return (1);
225	}
226
227	if (noflags == 0) {
228		/* Next argument is the ccd configuration flags. */
229		cp = *argv++; --argc;
230		if ((flags = flags_to_val(cp)) < 0) {
231			warnx("invalid flags argument: %s", cp);
232			return (1);
233		}
234	}
235
236	/* Next is the list of disks to make the ccd from. */
237	disks = malloc(argc * sizeof(char *));
238	if (disks == NULL) {
239		warnx("no memory to configure ccd");
240		return (1);
241	}
242	for (i = 0; argc != 0; ) {
243		cp = *argv++; --argc;
244		if ((j = checkdev(cp)) == 0)
245			disks[i++] = cp;
246		else {
247			warnx("%s: %s", cp, strerror(j));
248			return (1);
249		}
250	}
251
252	/* Fill in the ccio. */
253	ccio.ccio_disks = disks;
254	ccio.ccio_ndisks = i;
255	ccio.ccio_ileave = ileave;
256	ccio.ccio_flags = flags;
257
258	if (do_io(ccd, CCDIOCSET, &ccio)) {
259		free(disks);
260		return (1);
261	}
262
263	if (verbose) {
264		printf("ccd%d: %d components ", ccio.ccio_unit,
265		    ccio.ccio_ndisks);
266		for (i = 0; i < ccio.ccio_ndisks; ++i) {
267			if ((cp2 = strrchr(disks[i], '/')) != NULL)
268				++cp2;
269			else
270				cp2 = disks[i];
271			printf("%c%s%c",
272			    i == 0 ? '(' : ' ', cp2,
273			    i == ccio.ccio_ndisks - 1 ? ')' : ',');
274		}
275		printf(", %lu blocks ", (u_long)ccio.ccio_size);
276		if (ccio.ccio_ileave != 0)
277			printf("interleaved at %d blocks\n", ccio.ccio_ileave);
278		else
279			printf("concatenated\n");
280	}
281
282	free(disks);
283	return (0);
284}
285
286static int
287do_all(int action)
288{
289	FILE *f;
290	char line[_POSIX2_LINE_MAX];
291	char *cp, **argv;
292	int argc, rval;
293	gid_t egid;
294
295	rval = 0;
296	egid = getegid();
297	setegid(getgid());
298	if ((f = fopen(ccdconf, "r")) == NULL) {
299		setegid(egid);
300		warn("fopen: %s", ccdconf);
301		return (1);
302	}
303	setegid(egid);
304
305	while (fgets(line, sizeof(line), f) != NULL) {
306		argc = 0;
307		argv = NULL;
308		++lineno;
309		if ((cp = strrchr(line, '\n')) != NULL)
310			*cp = '\0';
311
312		/* Break up the line and pass it's contents to do_single(). */
313		if (line[0] == '\0')
314			goto end_of_line;
315		for (cp = line; (cp = strtok(cp, " \t")) != NULL; cp = NULL) {
316			if (*cp == '#')
317				break;
318			if ((argv = realloc(argv,
319			    sizeof(char *) * ++argc)) == NULL) {
320				warnx("no memory to configure ccds");
321				return (1);
322			}
323			argv[argc - 1] = cp;
324			/*
325			 * If our action is to unconfigure all, then pass
326			 * just the first token to do_single() and ignore
327			 * the rest.  Since this will be encountered on
328			 * our first pass through the line, the Right
329			 * Thing will happen.
330			 */
331			if (action == CCD_UNCONFIGALL) {
332				if (do_single(argc, argv, action))
333					rval = 1;
334				goto end_of_line;
335			}
336		}
337		if (argc != 0)
338			if (do_single(argc, argv, action))
339				rval = 1;
340
341 end_of_line:
342		if (argv != NULL)
343			free(argv);
344	}
345
346	(void)fclose(f);
347	return (rval);
348}
349
350static int
351checkdev(char *path)
352{
353	struct stat st;
354
355	if (stat(path, &st) != 0)
356		return (errno);
357
358	if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
359		return (EINVAL);
360
361	return (0);
362}
363
364static int
365pathtounit(char *path, int *unitp)
366{
367	struct stat st;
368	int maxpartitions;
369
370	if (stat(path, &st) != 0)
371		return (errno);
372
373	if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
374		return (EINVAL);
375
376	if ((maxpartitions = getmaxpartitions()) < 0)
377		return (errno);
378
379	*unitp = minor(st.st_rdev) / maxpartitions;
380
381	return (0);
382}
383
384static char *
385resolve_ccdname(char *name)
386{
387	char c, *path;
388	size_t len, newlen;
389	int rawpart;
390
391	if (name[0] == '/' || name[0] == '.') {
392		/* Assume they gave the correct pathname. */
393		return (strdup(name));
394	}
395
396	len = strlen(name);
397	c = name[len - 1];
398
399	newlen = len + 8;
400	if ((path = malloc(newlen)) == NULL)
401		return (NULL);
402	bzero(path, newlen);
403
404	if (isdigit(c)) {
405		if ((rawpart = getrawpartition()) < 0) {
406			free(path);
407			return (NULL);
408		}
409		(void)sprintf(path, "%s%s%c", _PATH_DEV, name, 'a' + rawpart);
410	} else
411		(void)sprintf(path, "%s%s", _PATH_DEV, name);
412
413	return (path);
414}
415
416static int
417do_io(char *path, u_long cmd, struct ccd_ioctl *cciop)
418{
419	int fd;
420	char *cp;
421
422	if ((fd = open(path, O_RDWR, 0640)) < 0) {
423		warn("open: %s", path);
424		return (1);
425	}
426
427	if (ioctl(fd, cmd, cciop) < 0) {
428		switch (cmd) {
429		case CCDIOCSET:
430			cp = "CCDIOCSET";
431			break;
432
433		case CCDIOCCLR:
434			cp = "CCDIOCCLR";
435			break;
436
437		case CCDCONFINFO:
438			cp = "CCDCONFINFO";
439			break;
440
441		case CCDCPPINFO:
442			cp = "CCDCPPINFO";
443			break;
444
445		default:
446			cp = "unknown";
447		}
448		warn("ioctl (%s): %s", cp, path);
449		return (1);
450	}
451
452	return (0);
453}
454
455static int
456dump_ccd(int argc, char **argv)
457{
458	char *ccd, *cp;
459	int i, error, numccd, numconfiged = 0;
460	struct ccdconf conf;
461
462	/*
463	 * Read the ccd configuration data from the kernel and dump
464	 * it to stdout.
465	 */
466	if ((ccd = resolve_ccdname("ccd0")) == NULL) {		/* XXX */
467		warnx("invalid ccd name: %s", cp);
468		return (1);
469	}
470	conf.size = 0;
471	if (do_io(ccd, CCDCONFINFO, (struct ccd_ioctl *) &conf))
472		return (1);
473	if (conf.size == 0) {
474		printf("no concatenated disks configured\n");
475		return (0);
476	}
477	/* Allocate space for the configuration data. */
478	conf.buffer = alloca(conf.size);
479	if (conf.buffer == NULL) {
480		warnx("no memory for configuration data");
481		return (1);
482	}
483	if (do_io(ccd, CCDCONFINFO, (struct ccd_ioctl *) &conf))
484		return (1);
485
486	numconfiged = conf.size / sizeof(struct ccd_s);
487
488	if (argc == 0) {
489		for (i = 0; i < numconfiged; i++)
490			print_ccd_info(&(conf.buffer[i]));
491	} else {
492		while (argc) {
493			cp = *argv++; --argc;
494			if ((ccd = resolve_ccdname(cp)) == NULL) {
495				warnx("invalid ccd name: %s", cp);
496				continue;
497			}
498			if ((error = pathtounit(ccd, &numccd)) != 0) {
499				warnx("%s: %s", ccd, strerror(error));
500				continue;
501			}
502			error = 1;
503			for (i = 0; i < numconfiged; i++) {
504				if (conf.buffer[i].sc_unit == numccd) {
505					print_ccd_info(&(conf.buffer[i]));
506					error = 0;
507					break;
508				}
509			}
510			if (error) {
511				warnx("ccd%d not configured", numccd);
512				continue;
513			}
514		}
515	}
516
517	return (0);
518}
519
520static void
521print_ccd_info(struct ccd_s *cs)
522{
523	char *cp, *ccd;
524	static int header_printed = 0;
525	struct ccdcpps cpps;
526
527	/* Print out header if necessary*/
528	if (header_printed == 0 && verbose) {
529		printf("# ccd\t\tileave\tflags\tcompnent devices\n");
530		header_printed = 1;
531	}
532
533	/* Dump out softc information. */
534	printf("ccd%d\t\t%d\t%d\t", cs->sc_unit, cs->sc_ileave,
535	    cs->sc_cflags & CCDF_USERMASK);
536	fflush(stdout);
537
538	/* Read in the component info. */
539	asprintf(&cp, "%s%d", cs->device_stats.device_name,
540	    cs->device_stats.unit_number);
541	if (cp == NULL) {
542		printf("\n");
543		warn("ccd%d: can't allocate memory",
544		    cs->sc_unit);
545		return;
546	}
547
548	if ((ccd = resolve_ccdname(cp)) == NULL) {
549		printf("\n");
550		warnx("can't read component info: invalid ccd name: %s", cp);
551		return;
552	}
553	cpps.size = 0;
554	if (do_io(ccd, CCDCPPINFO, (struct ccd_ioctl *) &cpps)) {
555		printf("\n");
556		warnx("can't read component info");
557		return;
558	}
559	cpps.buffer = alloca(cpps.size);
560	if (cpps.buffer == NULL) {
561		printf("\n");
562		warn("ccd%d: can't allocate memory for component info",
563		    cs->sc_unit);
564		return;
565	}
566	if (do_io(ccd, CCDCPPINFO, (struct ccd_ioctl *) &cpps)) {
567		printf("\n");
568		warnx("can't read component info");
569		return;
570	}
571
572	/* Display component info. */
573	for (cp = cpps.buffer; cp - cpps.buffer < cpps.size; cp += strlen(cp) + 1) {
574		printf((cp + strlen(cp) + 1) < (cpps.buffer + cpps.size) ?
575		    "%s " : "%s\n", cp);
576		fflush(stdout);
577	}
578	return;
579}
580
581static int
582getmaxpartitions(void)
583{
584    return (MAXPARTITIONS);
585}
586
587static int
588getrawpartition(void)
589{
590	return (RAW_PART);
591}
592
593static int
594flags_to_val(char *flags)
595{
596	char *cp, *tok;
597	int i, tmp, val = ~CCDF_USERMASK;
598	size_t flagslen;
599
600	/*
601	 * The most common case is that of NIL flags, so check for
602	 * those first.
603	 */
604	if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 ||
605	    strcmp("0", flags) == 0)
606		return (0);
607
608	flagslen = strlen(flags);
609
610	/* Check for values represented by strings. */
611	if ((cp = strdup(flags)) == NULL)
612		err(1, "no memory to parse flags");
613	tmp = 0;
614	for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) {
615		for (i = 0; flagvaltab[i].fv_flag != NULL; ++i)
616			if (strcmp(tok, flagvaltab[i].fv_flag) == 0)
617				break;
618		if (flagvaltab[i].fv_flag == NULL) {
619			free(cp);
620			goto bad_string;
621		}
622		tmp |= flagvaltab[i].fv_val;
623	}
624
625	/* If we get here, the string was ok. */
626	free(cp);
627	val = tmp;
628	goto out;
629
630 bad_string:
631
632	/* Check for values represented in hex. */
633	if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') {
634		errno = 0;	/* to check for ERANGE */
635		val = (int)strtol(&flags[2], &cp, 16);
636		if ((errno == ERANGE) || (*cp != '\0'))
637			return (-1);
638		goto out;
639	}
640
641	/* Check for values represented in decimal. */
642	errno = 0;	/* to check for ERANGE */
643	val = (int)strtol(flags, &cp, 10);
644	if ((errno == ERANGE) || (*cp != '\0'))
645		return (-1);
646
647 out:
648	return (((val & ~CCDF_USERMASK) == 0) ? val : -1);
649}
650
651static void
652usage(void)
653{
654	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
655		"usage: ccdconfig [-cv] ccd ileave [flags] dev [...]",
656		"       ccdconfig -C [-v] [-f config_file]",
657		"       ccdconfig -u [-v] ccd [...]",
658		"       ccdconfig -U [-v] [-f config_file]",
659		"       ccdconfig -g [ccd [...]]");
660	exit(1);
661}
662
663/* Local Variables: */
664/* c-argdecl-indent: 8 */
665/* c-indent-level: 8 */
666/* End: */
667