ccdconfig.c revision 92539
1139804Simp/*	$NetBSD: ccdconfig.c,v 1.2.2.1 1995/11/11 02:43:35 thorpej Exp $	*/
265534Salfred
3143427Srwatson/*
465534Salfred * Copyright (c) 1995 Jason R. Thorpe.
561837Salfred * All rights reserved.
661837Salfred *
761837Salfred * Redistribution and use in source and binary forms, with or without
861837Salfred * modification, are permitted provided that the following conditions
961837Salfred * are met:
1061837Salfred * 1. Redistributions of source code must retain the above copyright
1161837Salfred *    notice, this list of conditions and the following disclaimer.
1261837Salfred * 2. Redistributions in binary form must reproduce the above copyright
1361837Salfred *    notice, this list of conditions and the following disclaimer in the
1461837Salfred *    documentation and/or other materials provided with the distribution.
1561837Salfred * 3. All advertising materials mentioning features or use of this software
1661837Salfred *    must display the following acknowledgement:
1761837Salfred *	This product includes software developed for the NetBSD Project
1861837Salfred *	by Jason R. Thorpe.
1961837Salfred * 4. The name of the author may not be used to endorse or promote products
2061837Salfred *    derived from this software without specific prior written permission.
2161837Salfred *
2261837Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2361837Salfred * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2461837Salfred * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2561837Salfred * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2661837Salfred * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2761837Salfred * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2861837Salfred * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29116182Sobrien * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30116182Sobrien * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31116182Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261837Salfred * SUCH DAMAGE.
3361837Salfred */
3477598Sjesper
3561837Salfred#ifndef lint
3661837Salfredstatic const char rcsid[] =
3761837Salfred  "$FreeBSD: head/sbin/ccdconfig/ccdconfig.c 92539 2002-03-18 05:00:52Z imp $";
3861837Salfred#endif /* not lint */
39129920Srwatson
4061837Salfred#include <sys/param.h>
4161837Salfred#include <sys/linker.h>
42129876Sphk#include <sys/disklabel.h>
43129920Srwatson#include <sys/stat.h>
4461837Salfred#include <sys/module.h>
4565534Salfred#include <ctype.h>
4661837Salfred#include <err.h>
4761837Salfred#include <errno.h>
4861837Salfred#include <fcntl.h>
4961837Salfred#include <limits.h>
50129920Srwatson#include <paths.h>
51129920Srwatson#include <stdio.h>
52129920Srwatson#include <stdlib.h>
53129920Srwatson#include <string.h>
54129920Srwatson#include <unistd.h>
55129920Srwatson
5661837Salfred#include <sys/devicestat.h>
5761837Salfred#include <sys/ccdvar.h>
5861837Salfred
5961837Salfred#include "pathnames.h"
6061837Salfred
6165534Salfredstatic	int lineno = 0;
6265534Salfredstatic	int verbose = 0;
6365534Salfredstatic	char *ccdconf = _PATH_CCDCONF;
6465534Salfred
6565534Salfredstruct	flagval {
6665534Salfred	char	*fv_flag;
6765534Salfred	int	fv_val;
6861837Salfred} flagvaltab[] = {
69142056Srwatson	{ "CCDF_SWAP",		CCDF_SWAP },
70142056Srwatson	{ "CCDF_UNIFORM",	CCDF_UNIFORM },
71142056Srwatson	{ "CCDF_MIRROR",	CCDF_MIRROR },
7261837Salfred	{ "CCDF_PARITY",	CCDF_PARITY },
7361837Salfred	{ NULL,			0 },
7461837Salfred};
7561837Salfred
7661837Salfred#define CCD_CONFIG		0	/* configure a device */
7761837Salfred#define CCD_CONFIGALL		1	/* configure all devices */
78129920Srwatson#define CCD_UNCONFIG		2	/* unconfigure a device */
7961837Salfred#define CCD_UNCONFIGALL		3	/* unconfigure all devices */
8061837Salfred#define CCD_DUMP		4	/* dump a ccd's configuration */
8161837Salfred
82129920Srwatsonstatic	int checkdev(char *);
8361837Salfredstatic	int do_io(char *, u_long, struct ccd_ioctl *);
8461837Salfredstatic	int do_single(int, char **, int);
8561837Salfredstatic	int do_all(int);
86129920Srwatsonstatic	int dump_ccd(int, char **);
8761837Salfredstatic	int getmaxpartitions(void);
8861837Salfredstatic	int getrawpartition(void);
8961837Salfredstatic	int flags_to_val(char *);
9061837Salfredstatic	void print_ccd_info(struct ccd_s *);
9161837Salfredstatic	char *resolve_ccdname(char *);
9261837Salfredstatic	void usage(void);
9361837Salfred
94129920Srwatsonint
9561837Salfredmain(int argc, char *argv[])
9661837Salfred{
9761837Salfred	int ch, options = 0, action = CCD_CONFIG;
9861837Salfred
9961837Salfred	while ((ch = getopt(argc, argv, "cCf:guUv")) != -1) {
10061837Salfred		switch (ch) {
10161837Salfred		case 'c':
10261837Salfred			action = CCD_CONFIG;
10361837Salfred			++options;
10461837Salfred			break;
10561837Salfred
10661837Salfred		case 'C':
10761837Salfred			action = CCD_CONFIGALL;
10861837Salfred			++options;
10961837Salfred			break;
11061837Salfred
11161837Salfred		case 'f':
11261837Salfred			ccdconf = optarg;
11361837Salfred			break;
11461837Salfred
11561837Salfred		case 'g':
116129920Srwatson			action = CCD_DUMP;
11761837Salfred			break;
11861837Salfred
119129920Srwatson		case 'u':
120129920Srwatson			action = CCD_UNCONFIG;
12161837Salfred			++options;
122129920Srwatson			break;
12361837Salfred
12461837Salfred		case 'U':
12561837Salfred			action = CCD_UNCONFIGALL;
12661837Salfred			++options;
12761837Salfred			break;
12861837Salfred
12961837Salfred		case 'v':
130142060Srwatson			verbose = 1;
13161837Salfred			break;
13261837Salfred
13361837Salfred		default:
134142056Srwatson			usage();
135142056Srwatson		}
13661837Salfred	}
13761837Salfred	argc -= optind;
13861837Salfred	argv += optind;
13961837Salfred
14061837Salfred	if (options > 1)
14163645Salfred		usage();
142142056Srwatson
143142056Srwatson	if (modfind("ccd") < 0) {
144142056Srwatson		/* Not present in kernel, try loading it */
145142056Srwatson		if (kldload("ccd") < 0 || modfind("ccd") < 0)
14663645Salfred			warn("ccd module not available!");
14765534Salfred	}
14865534Salfred
14965534Salfred	switch (action) {
15065534Salfred		case CCD_CONFIG:
15161837Salfred		case CCD_UNCONFIG:
15261837Salfred			exit(do_single(argc, argv, action));
15361837Salfred			/* NOTREACHED */
15461837Salfred
15561837Salfred		case CCD_CONFIGALL:
15661837Salfred		case CCD_UNCONFIGALL:
15761837Salfred			exit(do_all(action));
15861837Salfred			/* NOTREACHED */
15961837Salfred
16061837Salfred		case CCD_DUMP:
16161837Salfred			exit(dump_ccd(argc, argv));
16261837Salfred			/* NOTREACHED */
16361837Salfred	}
164142058Srwatson	/* NOTREACHED */
165142058Srwatson	return (0);
166143463Srwatson}
167143463Srwatson
168143463Srwatsonstatic int
169143463Srwatsondo_single(int argc, char **argv, int action)
170143463Srwatson{
171143463Srwatson	struct ccd_ioctl ccio;
172143463Srwatson	char *ccd, *cp, *cp2, **disks;
173143463Srwatson	int noflags = 0, i, ileave, flags = 0, j;
174143463Srwatson
175143463Srwatson	bzero(&ccio, sizeof(ccio));
176143463Srwatson
177143463Srwatson	/*
178143463Srwatson	 * If unconfiguring, all arguments are treated as ccds.
179147300Smaxim	 */
180147300Smaxim	if (action == CCD_UNCONFIG || action == CCD_UNCONFIGALL) {
181143463Srwatson		for (i = 0; argc != 0; ) {
182147300Smaxim			cp = *argv++; --argc;
183143463Srwatson			if ((ccd = resolve_ccdname(cp)) == NULL) {
184143463Srwatson				warnx("invalid ccd name: %s", cp);
185143463Srwatson				i = 1;
186143463Srwatson				continue;
187143463Srwatson			}
188143463Srwatson			if (do_io(ccd, CCDIOCCLR, &ccio))
189143463Srwatson				i = 1;
190143463Srwatson			else
191143463Srwatson				if (verbose)
192143463Srwatson					printf("%s unconfigured\n", cp);
193143463Srwatson		}
194143463Srwatson		return (i);
195142060Srwatson	}
196142058Srwatson
197142060Srwatson	/* Make sure there are enough arguments. */
198142060Srwatson	if (argc < 4) {
199142060Srwatson		if (argc == 3) {
200142060Srwatson			/* Assume that no flags are specified. */
201142058Srwatson			noflags = 1;
202142058Srwatson		} else {
203143427Srwatson			if (action == CCD_CONFIGALL) {
204142058Srwatson				warnx("%s: bad line: %d", ccdconf, lineno);
205147300Smaxim				return (1);
206143427Srwatson			} else
207143427Srwatson				usage();
208143427Srwatson		}
209143427Srwatson	}
210143427Srwatson
211142058Srwatson	/* First argument is the ccd to configure. */
212142058Srwatson	cp = *argv++; --argc;
213142058Srwatson	if ((ccd = resolve_ccdname(cp)) == NULL) {
214142058Srwatson		warnx("invalid ccd name: %s", cp);
215142058Srwatson		return (1);
216142058Srwatson	}
217143427Srwatson
218142058Srwatson	/* Next argument is the interleave factor. */
219142058Srwatson	cp = *argv++; --argc;
220142058Srwatson	errno = 0;	/* to check for ERANGE */
221142058Srwatson	ileave = (int)strtol(cp, &cp2, 10);
222142058Srwatson	if ((errno == ERANGE) || (ileave < 0) || (*cp2 != '\0')) {
223142058Srwatson		warnx("invalid interleave factor: %s", cp);
224142058Srwatson		return (1);
225142058Srwatson	}
226143427Srwatson
227143427Srwatson	if (noflags == 0) {
228143461Srwatson		/* Next argument is the ccd configuration flags. */
229143461Srwatson		cp = *argv++; --argc;
230143427Srwatson		if ((flags = flags_to_val(cp)) < 0) {
231142058Srwatson			warnx("invalid flags argument: %s", cp);
232142058Srwatson			return (1);
233142058Srwatson		}
234142058Srwatson	}
235142058Srwatson
236142058Srwatson	/* Next is the list of disks to make the ccd from. */
237142058Srwatson	disks = malloc(argc * sizeof(char *));
238142058Srwatson	if (disks == NULL) {
239142058Srwatson		warnx("no memory to configure ccd");
240142058Srwatson		return (1);
241142058Srwatson	}
242142058Srwatson	for (i = 0; argc != 0; ) {
243142058Srwatson		cp = *argv++; --argc;
244142058Srwatson		if ((j = checkdev(cp)) == 0)
245142058Srwatson			disks[i++] = cp;
246143461Srwatson		else {
247143461Srwatson			warnx("%s: %s", cp, strerror(j));
248143461Srwatson			return (1);
249143461Srwatson		}
250142058Srwatson	}
251142058Srwatson
252142058Srwatson	/* Fill in the ccio. */
253142058Srwatson	ccio.ccio_disks = disks;
254142058Srwatson	ccio.ccio_ndisks = i;
255142058Srwatson	ccio.ccio_ileave = ileave;
256142058Srwatson	ccio.ccio_flags = flags;
257142058Srwatson
258142058Srwatson	if (do_io(ccd, CCDIOCSET, &ccio)) {
259142058Srwatson		free(disks);
260143461Srwatson		return (1);
261143461Srwatson	}
262143461Srwatson
263143461Srwatson	if (verbose) {
264142058Srwatson		printf("ccd%d: %d components ", ccio.ccio_unit,
265143461Srwatson		    ccio.ccio_ndisks);
266143461Srwatson		for (i = 0; i < ccio.ccio_ndisks; ++i) {
267142058Srwatson			if ((cp2 = strrchr(disks[i], '/')) != NULL)
268142058Srwatson				++cp2;
269142058Srwatson			else
270143461Srwatson				cp2 = disks[i];
271142058Srwatson			printf("%c%s%c",
272143461Srwatson			    i == 0 ? '(' : ' ', cp2,
273143461Srwatson			    i == ccio.ccio_ndisks - 1 ? ')' : ',');
274143461Srwatson		}
275142058Srwatson		printf(", %lu blocks ", (u_long)ccio.ccio_size);
276142058Srwatson		if (ccio.ccio_ileave != 0)
277142058Srwatson			printf("interleaved at %d blocks\n", ccio.ccio_ileave);
278142058Srwatson		else
279142058Srwatson			printf("concatenated\n");
280142058Srwatson	}
281142058Srwatson
282142058Srwatson	free(disks);
283142058Srwatson	return (0);
284142058Srwatson}
285142058Srwatson
286142058Srwatsonstatic int
287142058Srwatsondo_all(int action)
288142058Srwatson{
289142058Srwatson	FILE *f;
290142058Srwatson	char line[_POSIX2_LINE_MAX];
291142058Srwatson	char *cp, **argv;
292142058Srwatson	int argc, rval;
293142058Srwatson	gid_t egid;
294142058Srwatson
295142058Srwatson	rval = 0;
296142058Srwatson	egid = getegid();
297142058Srwatson	setegid(getgid());
298142058Srwatson	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