ccdconfig.c revision 69793
143561Skato/*	$NetBSD: ccdconfig.c,v 1.2.2.1 1995/11/11 02:43:35 thorpej Exp $	*/
243561Skato
343561Skato/*
443561Skato * Copyright (c) 1995 Jason R. Thorpe.
543561Skato * All rights reserved.
643561Skato *
743561Skato * Redistribution and use in source and binary forms, with or without
843561Skato * modification, are permitted provided that the following conditions
943561Skato * are met:
1043561Skato * 1. Redistributions of source code must retain the above copyright
1143561Skato *    notice, this list of conditions and the following disclaimer.
1243561Skato * 2. Redistributions in binary form must reproduce the above copyright
1343561Skato *    notice, this list of conditions and the following disclaimer in the
1443561Skato *    documentation and/or other materials provided with the distribution.
1543561Skato * 3. All advertising materials mentioning features or use of this software
1643561Skato *    must display the following acknowledgement:
1743561Skato *	This product includes software developed for the NetBSD Project
1843561Skato *	by Jason R. Thorpe.
1943561Skato * 4. The name of the author may not be used to endorse or promote products
2043561Skato *    derived from this software without specific prior written permission.
2143561Skato *
2243561Skato * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2343561Skato * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2443561Skato * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2543561Skato * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2643561Skato * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27119880Sobrien * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28119880Sobrien * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29119880Sobrien * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3043561Skato * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3143561Skato * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3243561Skato * SUCH DAMAGE.
3343561Skato */
3443561Skato
3543561Skato#ifndef lint
3643561Skatostatic const char rcsid[] =
3743561Skato  "$FreeBSD: head/sbin/ccdconfig/ccdconfig.c 69793 2000-12-09 09:35:55Z obrien $";
3843561Skato#endif /* not lint */
3943561Skato
4043561Skato#include <sys/param.h>
4145241Skato#include <sys/linker.h>
4243561Skato#include <sys/disklabel.h>
4343561Skato#include <sys/stat.h>
4458871Skato#include <sys/module.h>
4558871Skato#include <ctype.h>
4658871Skato#include <err.h>
4743561Skato#include <errno.h>
4843561Skato#include <fcntl.h>
4943561Skato#include <kvm.h>
5043561Skato#include <limits.h>
5143561Skato#include <paths.h>
5258871Skato#include <stdio.h>
5358871Skato#include <stdlib.h>
5443561Skato#include <string.h>
5543561Skato#include <unistd.h>
5643561Skato
5743561Skato#include <sys/devicestat.h>
5843561Skato#include <sys/ccdvar.h>
5943561Skato
6043561Skato#include "pathnames.h"
6143561Skato
6243561Skatostatic	int lineno = 0;
6343561Skatostatic	int verbose = 0;
6443561Skatostatic	char *ccdconf = _PATH_CCDCONF;
6543561Skato
6643561Skatostatic	char *core = NULL;
6768358Snyanstatic	char *kernel = NULL;
6843561Skato
6943561Skatostruct	flagval {
7043561Skato	char	*fv_flag;
7143561Skato	int	fv_val;
7243561Skato} flagvaltab[] = {
7343561Skato	{ "CCDF_SWAP",		CCDF_SWAP },
7443561Skato	{ "CCDF_UNIFORM",	CCDF_UNIFORM },
7568358Snyan	{ "CCDF_MIRROR",	CCDF_MIRROR },
7643561Skato	{ "CCDF_PARITY",	CCDF_PARITY },
7743561Skato	{ NULL,			0 },
7843561Skato};
7943561Skato
8043561Skatostatic	struct nlist nl[] = {
8143561Skato	{ "_ccd_softc" },
8243561Skato#define SYM_CCDSOFTC		0
8343561Skato	{ "_numccd" },
8458871Skato#define SYM_NUMCCD		1
8543561Skato	{ NULL },
8643561Skato};
8743561Skato
8843561Skato#define CCD_CONFIG		0	/* configure a device */
8955342Snyan#define CCD_CONFIGALL		1	/* configure all devices */
9059167Skato#define CCD_UNCONFIG		2	/* unconfigure a device */
9155342Snyan#define CCD_UNCONFIGALL		3	/* unconfigure all devices */
9258871Skato#define CCD_DUMP		4	/* dump a ccd's configuration */
9343561Skato
9443561Skatostatic	int checkdev __P((char *));
9543561Skatostatic	int do_io __P((char *, u_long, struct ccd_ioctl *));
9643561Skatostatic	int do_single __P((int, char **, int));
9743561Skatostatic	int do_all __P((int));
9843561Skatostatic	int dump_ccd __P((int, char **));
9943561Skatostatic	int getmaxpartitions __P((void));
10043561Skatostatic	int getrawpartition __P((void));
101150751Snyanstatic	int flags_to_val __P((char *));
102146698Sjhbstatic	void print_ccd_info __P((struct ccd_softc *, kvm_t *));
103146698Sjhbstatic	char *resolve_ccdname __P((char *));
104146698Sjhbstatic	void usage __P((void));
105146698Sjhb
106146698Sjhbint
107146698Sjhbmain(argc, argv)
10843561Skato	int argc;
109146698Sjhb	char **argv;
11066246Skato{
11143561Skato	int ch, options = 0, action = CCD_CONFIG;
11243561Skato
11343561Skato	while ((ch = getopt(argc, argv, "cCf:gM:N:uUv")) != -1) {
11443561Skato		switch (ch) {
11543561Skato		case 'c':
11643561Skato			action = CCD_CONFIG;
11743561Skato			++options;
11843561Skato			break;
11986131Snyan
12058871Skato		case 'C':
121126970Snyan			action = CCD_CONFIGALL;
12286131Snyan			++options;
12386131Snyan			break;
12486131Snyan
12586131Snyan		case 'f':
12686131Snyan			ccdconf = optarg;
12786131Snyan			break;
12886131Snyan
12986131Snyan		case 'g':
13058871Skato			action = CCD_DUMP;
13158871Skato			break;
13258871Skato
13343561Skato		case 'M':
13443561Skato			core = optarg;
13543561Skato			break;
13643561Skato
13743561Skato		case 'N':
13855342Snyan			kernel = optarg;
139136891Snyan			break;
140136891Snyan
141136891Snyan		case 'u':
142136891Snyan			action = CCD_UNCONFIG;
14343561Skato			++options;
14443561Skato			break;
14555342Snyan
14643561Skato		case 'U':
14743561Skato			action = CCD_UNCONFIGALL;
14843561Skato			++options;
14943561Skato			break;
15043561Skato
15143561Skato		case 'v':
15243561Skato			verbose = 1;
15343561Skato			break;
15443561Skato
15543561Skato		default:
15643561Skato			usage();
15743561Skato		}
15843561Skato	}
15943561Skato	argc -= optind;
16068358Snyan	argv += optind;
16168358Snyan
16268358Snyan	if (options > 1)
16343561Skato		usage();
16443561Skato
16543561Skato	/*
16643561Skato	 * Discard setgid privileges if not the running kernel so that bad
16743561Skato	 * guys can't print interesting stuff from kernel memory.
16843561Skato	 */
16943561Skato	if (core != NULL || kernel != NULL || action != CCD_DUMP) {
17043561Skato		setegid(getgid());
17143561Skato		setgid(getgid());
17243561Skato	}
17343561Skato
17468358Snyan	if (modfind("ccd") < 0) {
17586131Snyan		/* Not present in kernel, try loading it */
17643561Skato		if (kldload("ccd") < 0 || modfind("ccd") < 0)
17758871Skato			warn("ccd module not available!");
17868358Snyan	}
17943561Skato
18058871Skato	switch (action) {
181126970Snyan		case CCD_CONFIG:
18258871Skato		case CCD_UNCONFIG:
18386131Snyan			exit(do_single(argc, argv, action));
18486131Snyan			/* NOTREACHED */
18586131Snyan
18658871Skato		case CCD_CONFIGALL:
18758871Skato		case CCD_UNCONFIGALL:
18868358Snyan			exit(do_all(action));
18968358Snyan			/* NOTREACHED */
19058871Skato
19158871Skato		case CCD_DUMP:
19268358Snyan			exit(dump_ccd(argc, argv));
19368358Snyan			/* NOTREACHED */
19458871Skato	}
19558871Skato	/* NOTREACHED */
19658871Skato	return (0);
19743561Skato}
19868358Snyan
19968358Snyanstatic int
20043561Skatodo_single(argc, argv, action)
20143561Skato	int argc;
20268358Snyan	char **argv;
20368358Snyan	int action;
20468358Snyan{
20543561Skato	struct ccd_ioctl ccio;
20643561Skato	char *ccd, *cp, *cp2, **disks;
20743561Skato	int noflags = 0, i, ileave, flags = 0, j;
20843561Skato
20943561Skato	bzero(&ccio, sizeof(ccio));
21043561Skato
21143561Skato	/*
21243561Skato	 * If unconfiguring, all arguments are treated as ccds.
21343561Skato	 */
21453218Snyan	if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) {
21544463Skato		for (i = 0; argc != 0; ) {
21644463Skato			cp = *argv++; --argc;
21744463Skato			if ((ccd = resolve_ccdname(cp)) == NULL) {
21844463Skato				warnx("invalid ccd name: %s", cp);
21953218Snyan				i = 1;
22043561Skato				continue;
22186131Snyan			}
22243561Skato			if (do_io(ccd, CCDIOCCLR, &ccio))
22358871Skato				i = 1;
22458871Skato			else
22558871Skato				if (verbose)
22658871Skato					printf("%s unconfigured\n", cp);
22786131Snyan		}
22868358Snyan		return (i);
22943561Skato	}
23043561Skato
23168358Snyan	/* Make sure there are enough arguments. */
23243561Skato	if (argc < 4) {
23368358Snyan		if (argc == 3) {
23468358Snyan			/* Assume that no flags are specified. */
23568358Snyan			noflags = 1;
23668358Snyan		} else {
23743561Skato			if (action == CCD_CONFIGALL) {
23843561Skato				warnx("%s: bad line: %d", ccdconf, lineno);
23943561Skato				return (1);
24043561Skato			} else
24143561Skato				usage();
24243561Skato		}
24343561Skato	}
24459535Snyan
24543561Skato	/* First argument is the ccd to configure. */
24659535Snyan	cp = *argv++; --argc;
24759535Snyan	if ((ccd = resolve_ccdname(cp)) == NULL) {
24859535Snyan		warnx("invalid ccd name: %s", cp);
24959535Snyan		return (1);
25043561Skato	}
25143561Skato
25243561Skato	/* Next argument is the interleave factor. */
25343561Skato	cp = *argv++; --argc;
25443561Skato	errno = 0;	/* to check for ERANGE */
25543561Skato	ileave = (int)strtol(cp, &cp2, 10);
25643561Skato	if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) {
25743561Skato		warnx("invalid interleave factor: %s", cp);
25843561Skato		return (1);
25943561Skato	}
26043561Skato
26143561Skato	if (noflags == 0) {
26243561Skato		/* Next argument is the ccd configuration flags. */
26343561Skato		cp = *argv++; --argc;
26443561Skato		if ((flags = flags_to_val(cp)) < 0) {
26543561Skato			warnx("invalid flags argument: %s", cp);
26643561Skato			return (1);
26743561Skato		}
26843561Skato	}
26943561Skato
27043561Skato	/* Next is the list of disks to make the ccd from. */
27143561Skato	disks = malloc(argc * sizeof(char *));
27243561Skato	if (disks == NULL) {
27343561Skato		warnx("no memory to configure ccd");
27443561Skato		return (1);
27543561Skato	}
27643561Skato	for (i = 0; argc != 0; ) {
27743561Skato		cp = *argv++; --argc;
27843561Skato		if ((j = checkdev(cp)) == 0)
27943561Skato			disks[i++] = cp;
28043561Skato		else {
28143561Skato			warnx("%s: %s", cp, strerror(j));
28243561Skato			return (1);
28343561Skato		}
28443561Skato	}
28543561Skato
28643561Skato	/* Fill in the ccio. */
28743561Skato	ccio.ccio_disks = disks;
28843561Skato	ccio.ccio_ndisks = i;
28943561Skato	ccio.ccio_ileave = ileave;
29043561Skato	ccio.ccio_flags = flags;
29143561Skato
29243561Skato	if (do_io(ccd, CCDIOCSET, &ccio)) {
29343561Skato		free(disks);
29443561Skato		return (1);
29543561Skato	}
29643561Skato
29743561Skato	if (verbose) {
29843561Skato		printf("ccd%d: %d components ", ccio.ccio_unit,
29943561Skato		    ccio.ccio_ndisks);
30043561Skato		for (i = 0; i < ccio.ccio_ndisks; ++i) {
30143561Skato			if ((cp2 = strrchr(disks[i], '/')) != NULL)
302				++cp2;
303			else
304				cp2 = disks[i];
305			printf("%c%s%c",
306			    i == 0 ? '(' : ' ', cp2,
307			    i == ccio.ccio_ndisks - 1 ? ')' : ',');
308		}
309		printf(", %lu blocks ", (u_long)ccio.ccio_size);
310		if (ccio.ccio_ileave != 0)
311			printf("interleaved at %d blocks\n", ccio.ccio_ileave);
312		else
313			printf("concatenated\n");
314	}
315
316	free(disks);
317	return (0);
318}
319
320static int
321do_all(action)
322	int action;
323{
324	FILE *f;
325	char line[_POSIX2_LINE_MAX];
326	char *cp, **argv;
327	int argc, rval;
328	gid_t egid;
329
330	rval = 0;
331	egid = getegid();
332	setegid(getgid());
333	if ((f = fopen(ccdconf, "r")) == NULL) {
334		setegid(egid);
335		warn("fopen: %s", ccdconf);
336		return (1);
337	}
338	setegid(egid);
339
340	while (fgets(line, sizeof(line), f) != NULL) {
341		argc = 0;
342		argv = NULL;
343		++lineno;
344		if ((cp = strrchr(line, '\n')) != NULL)
345			*cp = '\0';
346
347		/* Break up the line and pass it's contents to do_single(). */
348		if (line[0] == '\0')
349			goto end_of_line;
350		for (cp = line; (cp = strtok(cp, " \t")) != NULL; cp = NULL) {
351			if (*cp == '#')
352				break;
353			if ((argv = realloc(argv,
354			    sizeof(char *) * ++argc)) == NULL) {
355				warnx("no memory to configure ccds");
356				return (1);
357			}
358			argv[argc - 1] = cp;
359			/*
360			 * If our action is to unconfigure all, then pass
361			 * just the first token to do_single() and ignore
362			 * the rest.  Since this will be encountered on
363			 * our first pass through the line, the Right
364			 * Thing will happen.
365			 */
366			if (action == CCD_UNCONFIGALL) {
367				if (do_single(argc, argv, action))
368					rval = 1;
369				goto end_of_line;
370			}
371		}
372		if (argc != 0)
373			if (do_single(argc, argv, action))
374				rval = 1;
375
376 end_of_line:
377		if (argv != NULL)
378			free(argv);
379	}
380
381	(void)fclose(f);
382	return (rval);
383}
384
385static int
386checkdev(path)
387	char *path;
388{
389	struct stat st;
390
391	if (stat(path, &st) != 0)
392		return (errno);
393
394	if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
395		return (EINVAL);
396
397	return (0);
398}
399
400static int
401pathtounit(path, unitp)
402	char *path;
403	int *unitp;
404{
405	struct stat st;
406	int maxpartitions;
407
408	if (stat(path, &st) != 0)
409		return (errno);
410
411	if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
412		return (EINVAL);
413
414	if ((maxpartitions = getmaxpartitions()) < 0)
415		return (errno);
416
417	*unitp = minor(st.st_rdev) / maxpartitions;
418
419	return (0);
420}
421
422static char *
423resolve_ccdname(name)
424	char *name;
425{
426	char c, *path;
427	size_t len, newlen;
428	int rawpart;
429
430	if (name[0] == '/' || name[0] == '.') {
431		/* Assume they gave the correct pathname. */
432		return (strdup(name));
433	}
434
435	len = strlen(name);
436	c = name[len - 1];
437
438	newlen = len + 8;
439	if ((path = malloc(newlen)) == NULL)
440		return (NULL);
441	bzero(path, newlen);
442
443	if (isdigit(c)) {
444		if ((rawpart = getrawpartition()) < 0) {
445			free(path);
446			return (NULL);
447		}
448		(void)sprintf(path, "%s%s%c", _PATH_DEV, name, 'a' + rawpart);
449	} else
450		(void)sprintf(path, "%s%s", _PATH_DEV, name);
451
452	return (path);
453}
454
455static int
456do_io(path, cmd, cciop)
457	char *path;
458	u_long cmd;
459	struct ccd_ioctl *cciop;
460{
461	int fd;
462	char *cp;
463
464	if ((fd = open(path, O_RDWR, 0640)) < 0) {
465		warn("open: %s", path);
466		return (1);
467	}
468
469	if (ioctl(fd, cmd, cciop) < 0) {
470		switch (cmd) {
471		case CCDIOCSET:
472			cp = "CCDIOCSET";
473			break;
474
475		case CCDIOCCLR:
476			cp = "CCDIOCCLR";
477			break;
478
479		default:
480			cp = "unknown";
481		}
482		warn("ioctl (%s): %s", cp, path);
483		return (1);
484	}
485
486	return (0);
487}
488
489#define KVM_ABORT(kd, str) {						\
490	(void)kvm_close((kd));						\
491	warnx("%s", (str));							\
492	warnx("%s", kvm_geterr((kd)));					\
493	return (1);							\
494}
495
496static int
497dump_ccd(argc, argv)
498	int argc;
499	char **argv;
500{
501	char errbuf[_POSIX2_LINE_MAX], *ccd, *cp;
502	struct ccd_softc *cs, *kcs;
503	size_t readsize;
504	int i, error, numccd, numconfiged = 0;
505	kvm_t *kd;
506
507	bzero(errbuf, sizeof(errbuf));
508
509	if ((kd = kvm_openfiles(kernel, core, NULL, O_RDONLY,
510	    errbuf)) == NULL) {
511		warnx("can't open kvm: %s", errbuf);
512		return (1);
513	}
514
515	if (kvm_nlist(kd, nl))
516		KVM_ABORT(kd, "ccd-related symbols not available");
517
518	/* Check to see how many ccds are currently configured. */
519	if (kvm_read(kd, nl[SYM_NUMCCD].n_value, (char *)&numccd,
520	    sizeof(numccd)) != sizeof(numccd))
521		KVM_ABORT(kd, "can't determine number of configured ccds");
522
523	if (numccd == 0) {
524		printf("ccd driver in kernel, but is uninitialized\n");
525		goto done;
526	}
527
528	/* Allocate space for the configuration data. */
529	readsize = numccd * sizeof(struct ccd_softc);
530	if ((cs = malloc(readsize)) == NULL) {
531		warnx("no memory for configuration data");
532		goto bad;
533	}
534	bzero(cs, readsize);
535
536	/*
537	 * Read the ccd configuration data from the kernel and dump
538	 * it to stdout.
539	 */
540	if (kvm_read(kd, nl[SYM_CCDSOFTC].n_value, (char *)&kcs,
541	    sizeof(kcs)) != sizeof(kcs)) {
542		free(cs);
543		KVM_ABORT(kd, "can't find pointer to configuration data");
544	}
545	if (kvm_read(kd, (u_long)kcs, (char *)cs, readsize) != readsize) {
546		free(cs);
547		KVM_ABORT(kd, "can't read configuration data");
548	}
549
550	if (argc == 0) {
551		for (i = 0; i < numccd; ++i)
552			if (cs[i].sc_flags & CCDF_INITED) {
553				++numconfiged;
554				print_ccd_info(&cs[i], kd);
555			}
556
557		if (numconfiged == 0)
558			printf("no concatenated disks configured\n");
559	} else {
560		while (argc) {
561			cp = *argv++; --argc;
562			if ((ccd = resolve_ccdname(cp)) == NULL) {
563				warnx("invalid ccd name: %s", cp);
564				continue;
565			}
566			if ((error = pathtounit(ccd, &i)) != 0) {
567				warnx("%s: %s", ccd, strerror(error));
568				continue;
569			}
570			if (i >= numccd) {
571				warnx("ccd%d not configured", i);
572				continue;
573			}
574			if (cs[i].sc_flags & CCDF_INITED)
575				print_ccd_info(&cs[i], kd);
576			else
577				printf("ccd%d not configured\n", i);
578		}
579	}
580
581	free(cs);
582
583 done:
584	(void)kvm_close(kd);
585	return (0);
586
587 bad:
588	(void)kvm_close(kd);
589	return (1);
590}
591
592static void
593print_ccd_info(cs, kd)
594	struct ccd_softc *cs;
595	kvm_t *kd;
596{
597	static int header_printed = 0;
598	struct ccdcinfo *cip;
599	size_t readsize;
600	char path[MAXPATHLEN];
601	int i;
602
603	if (header_printed == 0 && verbose) {
604		printf("# ccd\t\tileave\tflags\tcompnent devices\n");
605		header_printed = 1;
606	}
607
608	readsize = cs->sc_nccdisks * sizeof(struct ccdcinfo);
609	if ((cip = malloc(readsize)) == NULL) {
610		warn("ccd%d: can't allocate memory for component info",
611		    cs->sc_unit);
612		return;
613	}
614	bzero(cip, readsize);
615
616	/* Dump out softc information. */
617	printf("ccd%d\t\t%d\t%d\t", cs->sc_unit, cs->sc_ileave,
618	    cs->sc_cflags & CCDF_USERMASK);
619	fflush(stdout);
620
621	/* Read in the component info. */
622	if (kvm_read(kd, (u_long)cs->sc_cinfo, (char *)cip,
623	    readsize) != readsize) {
624		printf("\n");
625		warnx("can't read component info");
626		warnx("%s", kvm_geterr(kd));
627		goto done;
628	}
629
630	/* Read component pathname and display component info. */
631	for (i = 0; i < cs->sc_nccdisks; ++i) {
632		if (kvm_read(kd, (u_long)cip[i].ci_path, (char *)path,
633		    cip[i].ci_pathlen) != cip[i].ci_pathlen) {
634			printf("\n");
635			warnx("can't read component pathname");
636			warnx("%s", kvm_geterr(kd));
637			goto done;
638		}
639		printf((i + 1 < cs->sc_nccdisks) ? "%s " : "%s\n", path);
640		fflush(stdout);
641	}
642
643 done:
644	free(cip);
645}
646
647static int
648getmaxpartitions()
649{
650    return (MAXPARTITIONS);
651}
652
653static int
654getrawpartition()
655{
656	return (RAW_PART);
657}
658
659static int
660flags_to_val(flags)
661	char *flags;
662{
663	char *cp, *tok;
664	int i, tmp, val = ~CCDF_USERMASK;
665	size_t flagslen;
666
667	/*
668	 * The most common case is that of NIL flags, so check for
669	 * those first.
670	 */
671	if (strcmp("none", flags) == 0 || strcmp("0x0", flags) == 0 ||
672	    strcmp("0", flags) == 0)
673		return (0);
674
675	flagslen = strlen(flags);
676
677	/* Check for values represented by strings. */
678	if ((cp = strdup(flags)) == NULL)
679		err(1, "no memory to parse flags");
680	tmp = 0;
681	for (tok = cp; (tok = strtok(tok, ",")) != NULL; tok = NULL) {
682		for (i = 0; flagvaltab[i].fv_flag != NULL; ++i)
683			if (strcmp(tok, flagvaltab[i].fv_flag) == 0)
684				break;
685		if (flagvaltab[i].fv_flag == NULL) {
686			free(cp);
687			goto bad_string;
688		}
689		tmp |= flagvaltab[i].fv_val;
690	}
691
692	/* If we get here, the string was ok. */
693	free(cp);
694	val = tmp;
695	goto out;
696
697 bad_string:
698
699	/* Check for values represented in hex. */
700	if (flagslen > 2 && flags[0] == '0' && flags[1] == 'x') {
701		errno = 0;	/* to check for ERANGE */
702		val = (int)strtol(&flags[2], &cp, 16);
703		if ((errno == ERANGE) || (*cp != '\0'))
704			return (-1);
705		goto out;
706	}
707
708	/* Check for values represented in decimal. */
709	errno = 0;	/* to check for ERANGE */
710	val = (int)strtol(flags, &cp, 10);
711	if ((errno == ERANGE) || (*cp != '\0'))
712		return (-1);
713
714 out:
715	return (((val & ~CCDF_USERMASK) == 0) ? val : -1);
716}
717
718static void
719usage()
720{
721	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
722		"usage: ccdconfig [-cv] ccd ileave [flags] dev [...]",
723		"       ccdconfig -C [-v] [-f config_file]",
724		"       ccdconfig -u [-v] ccd [...]",
725		"       ccdconfig -U [-v] [-f config_file]",
726		"       ccdconfig -g [-M core] [-N system] [ccd [...]]");
727	exit(1);
728}
729
730/* Local Variables: */
731/* c-argdecl-indent: 8 */
732/* c-indent-level: 8 */
733/* End: */
734