Home | History | Annotate | Line # | Download | only in rndctl
rndctl.c revision 1.37
      1 /*	$NetBSD: rndctl.c,v 1.37 2020/05/12 09:48:44 simonb Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1997 Michael Graff.
      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. Neither the name of the author nor the names of other contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 __RCSID("$NetBSD: rndctl.c,v 1.37 2020/05/12 09:48:44 simonb Exp $");
     35 #endif
     36 
     37 #include <sys/param.h>
     38 #include <sys/types.h>
     39 #include <sys/endian.h>
     40 #include <sys/ioctl.h>
     41 #include <sys/rndio.h>
     42 #include <sys/sha3.h>
     43 #include <sys/sysctl.h>
     44 
     45 #include <err.h>
     46 #include <errno.h>
     47 #include <fcntl.h>
     48 #include <paths.h>
     49 #include <sha1.h>
     50 #include <stdio.h>
     51 #include <stdlib.h>
     52 #include <string.h>
     53 #include <unistd.h>
     54 
     55 typedef struct {
     56 	const char *a_name;
     57 	u_int32_t a_type;
     58 } arg_t;
     59 
     60 static const arg_t source_types[] = {
     61 	{ "???",     RND_TYPE_UNKNOWN },
     62 	{ "disk",    RND_TYPE_DISK },
     63 	{ "net",     RND_TYPE_NET },
     64 	{ "tape",    RND_TYPE_TAPE },
     65 	{ "tty",     RND_TYPE_TTY },
     66 	{ "rng",     RND_TYPE_RNG },
     67 	{ "skew",    RND_TYPE_SKEW },
     68 	{ "env",     RND_TYPE_ENV },
     69 	{ "vm",      RND_TYPE_VM },
     70 	{ "power",   RND_TYPE_POWER },
     71 	{ NULL,      0 }
     72 };
     73 
     74 __dead static void usage(void);
     75 static u_int32_t find_type(const char *name);
     76 static const char *find_name(u_int32_t);
     77 static void do_ioctl(rndctl_t *);
     78 static char * strflags(u_int32_t);
     79 static void do_list(int, u_int32_t, char *);
     80 static void do_stats(void);
     81 
     82 static int iflag;
     83 static int vflag;
     84 
     85 static void
     86 usage(void)
     87 {
     88 
     89 	fprintf(stderr, "usage: %s [-CEce] [-d devname | -t devtype]\n",
     90 	    getprogname());
     91 	fprintf(stderr, "       %s [-lsv] [-d devname | -t devtype]\n",
     92 	    getprogname());
     93 	fprintf(stderr, "       %s [-i] -L save-file\n", getprogname());
     94 	fprintf(stderr, "       %s -S save-file\n", getprogname());
     95 	exit(1);
     96 }
     97 
     98 static u_int32_t
     99 find_type(const char *name)
    100 {
    101 	const arg_t *a;
    102 
    103 	a = source_types;
    104 
    105 	while (a->a_name != NULL) {
    106 		if (strcmp(a->a_name, name) == 0)
    107 			return (a->a_type);
    108 		a++;
    109 	}
    110 
    111 	errx(1, "device name %s unknown", name);
    112 	return (0);
    113 }
    114 
    115 static const char *
    116 find_name(u_int32_t type)
    117 {
    118 	const arg_t *a;
    119 
    120 	a = source_types;
    121 
    122 	while (a->a_name != NULL) {
    123 		if (type == a->a_type)
    124 			return (a->a_name);
    125 		a++;
    126 	}
    127 
    128 	warnx("device type %u unknown", type);
    129 	return ("???");
    130 }
    131 
    132 static int
    133 update_seed(const char *filename, int fd_seed, const char *tmp,
    134     const void *extra, size_t nextra, uint32_t extraentropy)
    135 {
    136 	uint32_t systementropy;
    137 	uint8_t buf[32];
    138 	SHAKE128_CTX shake128;
    139 	rndsave_t rs;
    140 	SHA1_CTX s;
    141 	ssize_t nread, nwrit;
    142 	int fd_random;
    143 
    144 	/* Paranoia: Avoid stack memory disclosure.  */
    145 	memset(&rs, 0, sizeof rs);
    146 
    147 	/* Open /dev/urandom to read data from the system.  */
    148 	if ((fd_random = open(_PATH_URANDOM, O_RDONLY)) == -1) {
    149 		warn("open /dev/urandom");
    150 		return -1;
    151 	}
    152 
    153 	/* Find how much entropy is in the pool.  */
    154 	if (ioctl(fd_random, RNDGETENTCNT, &systementropy) == -1) {
    155 		warn("ioctl(RNDGETENTCNT)");
    156 		systementropy = 0;
    157 	}
    158 
    159 	/* Read some data from /dev/urandom.  */
    160 	if ((size_t)(nread = read(fd_random, buf, sizeof buf)) != sizeof buf) {
    161 		if (nread == -1)
    162 			warn("read");
    163 		else
    164 			warnx("truncated read");
    165 		return -1;
    166 	}
    167 
    168 	/* Close /dev/urandom; we're done with it.  */
    169 	if (close(fd_random) == -1)
    170 		warn("close");
    171 	fd_random = -1;		/* paranoia */
    172 
    173 	/*
    174 	 * Hash what we read together with the extra input to generate
    175 	 * the seed data.
    176 	 */
    177 	SHAKE128_Init(&shake128);
    178 	SHAKE128_Update(&shake128, buf, sizeof buf);
    179 	SHAKE128_Update(&shake128, extra, nextra);
    180 	SHAKE128_Final(rs.data, sizeof(rs.data), &shake128);
    181 	explicit_memset(&shake128, 0, sizeof shake128); /* paranoia */
    182 
    183 	/*
    184 	 * Report an upper bound on the min-entropy of the seed data.
    185 	 * We take the larger of the system entropy and the extra
    186 	 * entropy -- the system state and the extra input may or may
    187 	 * not be independent, so we can't add them -- and clamp to the
    188 	 * size of the data.
    189 	 */
    190 	systementropy = MIN(systementropy,
    191 	    MIN(sizeof(buf), UINT32_MAX/NBBY)*NBBY);
    192 	extraentropy = MIN(extraentropy, MIN(nextra, UINT32_MAX/NBBY)*NBBY);
    193 	rs.entropy = MIN(MAX(systementropy, extraentropy),
    194 	    MIN(sizeof(rs.data), UINT32_MAX/NBBY)*NBBY);
    195 
    196 	/*
    197 	 * Compute the checksum on the 32-bit entropy count, followed
    198 	 * by the seed data.
    199 	 */
    200 	SHA1Init(&s);
    201 	SHA1Update(&s, (const uint8_t *)&rs.entropy, sizeof(rs.entropy));
    202 	SHA1Update(&s, rs.data, sizeof(rs.data));
    203 	SHA1Final(rs.digest, &s);
    204 	explicit_memset(&s, 0, sizeof s); /* paranoia */
    205 
    206 	/*
    207 	 * Write it to a temporary file and sync it before we commit.
    208 	 * This way either the old seed or the new seed is completely
    209 	 * written in the expected location on disk even if the system
    210 	 * crashes as long as the file system doesn't get corrupted too
    211 	 * badly.
    212 	 *
    213 	 * If interrupted after this point and the temporary file is
    214 	 * disclosed, no big deal -- either the pool was predictable to
    215 	 * begin with in which case we're hosed either way, or we've
    216 	 * just revealed some output which is not a problem.
    217 	 */
    218 	if ((size_t)(nwrit = write(fd_seed, &rs, sizeof rs)) != sizeof rs) {
    219 		int error = errno;
    220 		if (unlink(tmp) == -1)
    221 			warn("unlink");
    222 		if (nwrit == -1)
    223 			warnc(error, "write");
    224 		else
    225 			warnx("truncated write");
    226 		return -1;
    227 	}
    228 	explicit_memset(&rs, 0, sizeof rs); /* paranoia */
    229 	if (fsync_range(fd_seed, FDATASYNC|FDISKSYNC, 0, 0) == -1) {
    230 		int error = errno;
    231 		if (unlink(tmp) == -1)
    232 			warn("unlink");
    233 		warnc(error, "fsync_range");
    234 		return -1;
    235 	}
    236 	if (close(fd_seed) == -1)
    237 		warn("close");
    238 
    239 	/* Rename it over the original file to commit.  */
    240 	if (rename(tmp, filename) == -1) {
    241 		warn("rename");
    242 		return -1;
    243 	}
    244 
    245 	/* Success!  */
    246 	return 0;
    247 }
    248 
    249 static void
    250 do_save(const char *filename)
    251 {
    252 	char tmp[PATH_MAX];
    253 	int fd_seed;
    254 
    255 	/* Consolidate any pending samples.  */
    256 	if (sysctlbyname("kern.entropy.consolidate", NULL, NULL,
    257 		(const int[]){1}, sizeof(int)) == -1)
    258 		warn("consolidate entropy");
    259 
    260 	/* Format the temporary file name.  */
    261 	if (snprintf(tmp, sizeof tmp, "%s.tmp", filename) >= PATH_MAX)
    262 		errx(1, "path too long");
    263 
    264 	/* Create a temporary seed file.  */
    265 	if ((fd_seed = open(tmp, O_CREAT|O_TRUNC|O_WRONLY, 0600)) == -1)
    266 		err(1, "open seed file to save");
    267 
    268 	/* Update the seed.  Abort on failure.  */
    269 	if (update_seed(filename, fd_seed, tmp, NULL, 0, 0) == -1)
    270 		exit(1);
    271 }
    272 
    273 static void
    274 do_load(const char *filename)
    275 {
    276 	char tmp[PATH_MAX];
    277 	int fd_new, fd_old, fd_random;
    278 	rndsave_t rs;
    279 	rnddata_t rd;
    280 	ssize_t nread, nwrit;
    281 	SHA1_CTX s;
    282 	uint8_t digest[SHA1_DIGEST_LENGTH];
    283 	int ro = 0, fail = 0;
    284 	int error;
    285 
    286 	/*
    287 	 * 1. Load the old seed.
    288 	 * 2. Feed the old seed into the kernel.
    289 	 * 3. Generate and write a new seed.
    290 	 * 4. Erase the old seed if we can.
    291 	 *
    292 	 * We follow the procedure in
    293 	 *
    294 	 *	Niels Ferguson, Bruce Schneier, and Tadayoshi Kohno,
    295 	 *	_Cryptography Engineering_, Wiley, 2010, Sec. 9.6.2
    296 	 *	`Update Seed File'.
    297 	 *
    298 	 * Additionally, we zero the seed's stored entropy estimate if
    299 	 * it appears to be on a read-only medium.
    300 	 */
    301 
    302 	/* Format the temporary file name.  */
    303 	if (snprintf(tmp, sizeof tmp, "%s.tmp", filename) >= PATH_MAX)
    304 		errx(1, "path too long");
    305 
    306 	/* Create a new seed file or determine the medium is read-only. */
    307 	if ((fd_new = open(tmp, O_CREAT|O_TRUNC|O_WRONLY, 0600)) == -1) {
    308 		warn("update seed file");
    309 		ro = 1;
    310 	}
    311 
    312 	/*
    313 	 * 1. Load the old seed.
    314 	 */
    315 	if ((fd_old = open(filename, O_RDWR)) == -1) {
    316 		error = errno;
    317 		if ((error != EPERM && error != EROFS) ||
    318 		    (fd_old = open(filename, O_RDONLY)) == -1)
    319 			err(1, "open seed file to load");
    320 		if (fd_new != -1)
    321 			warnc(error, "can't overwrite old seed file");
    322 		ro = 1;
    323 	}
    324 	if ((size_t)(nread = read(fd_old, &rs, sizeof rs)) != sizeof rs) {
    325 		if (nread == -1)
    326 			err(1, "read seed");
    327 		else
    328 			errx(1, "seed too short");
    329 	}
    330 
    331 	/* Verify its checksum.  */
    332 	SHA1Init(&s);
    333 	SHA1Update(&s, (const uint8_t *)&rs.entropy, sizeof(rs.entropy));
    334 	SHA1Update(&s, rs.data, sizeof(rs.data));
    335 	SHA1Final(digest, &s);
    336 	if (!consttime_memequal(digest, rs.digest, sizeof(digest))) {
    337 		/*
    338 		 * If the checksum doesn't match, doesn't hurt to feed
    339 		 * the seed in anyway, but act as though it has zero
    340 		 * entropy in case it was corrupted with predictable
    341 		 * garbage.
    342 		 */
    343 		warnx("bad checksum");
    344 		rs.entropy = 0;
    345 	}
    346 
    347 	/*
    348 	 * If the entropy is insensibly large, try byte-swapping.
    349 	 * Otherwise assume the file is corrupted and act as though it
    350 	 * has zero entropy.
    351 	 */
    352 	if (howmany(rs.entropy, NBBY) > sizeof(rs.data)) {
    353 		rs.entropy = bswap32(rs.entropy);
    354 		if (howmany(rs.entropy, NBBY) > sizeof(rs.data)) {
    355 			warnx("bad entropy estimate");
    356 			rs.entropy = 0;
    357 		}
    358 	}
    359 
    360 	/* If the medium can't be updated, zero the entropy estimate.  */
    361 	if (ro)
    362 		rs.entropy = 0;
    363 
    364 	/* Fail later on if there's no entropy in the seed.  */
    365 	if (rs.entropy == 0) {
    366 		warnx("no entropy in seed");
    367 		fail = 1;
    368 	}
    369 
    370 	/* If the user asked, zero the entropy estimate, but succeed.  */
    371 	if (iflag)
    372 		rs.entropy = 0;
    373 
    374 	/*
    375 	 * 2. Feed the old seed into the kernel.
    376 	 *
    377 	 * This also has the effect of consolidating pending samples,
    378 	 * whether or not there are enough samples from sources deemed
    379 	 * to have full entropy, so that the updated seed will
    380 	 * incorporate them.
    381 	 */
    382 	rd.len = MIN(sizeof(rd.data), sizeof(rs.data));
    383 	rd.entropy = rs.entropy;
    384 	memcpy(rd.data, rs.data, rd.len);
    385 	explicit_memset(&rs, 0, sizeof rs); /* paranoia */
    386 	if ((fd_random = open(_PATH_URANDOM, O_WRONLY)) == -1)
    387 		err(1, "open /dev/urandom");
    388 	if (ioctl(fd_random, RNDADDDATA, &rd) == -1)
    389 		err(1, "RNDADDDATA");
    390 	explicit_memset(&rd, 0, sizeof rd); /* paranoia */
    391 	if (close(fd_random) == -1)
    392 		warn("close /dev/urandom");
    393 	fd_random = -1;		/* paranoia */
    394 
    395 	/*
    396 	 * 3. Generate and write a new seed.
    397 	 */
    398 	if (fd_new == -1 ||
    399 	    update_seed(filename, fd_new, tmp, rs.data, sizeof(rs.data),
    400 		rs.entropy) == -1)
    401 		fail = 1;
    402 
    403 	/*
    404 	 * 4. Erase the old seed.
    405 	 *
    406 	 * Only effective if we're on a fixed-address file system like
    407 	 * ffs -- doesn't help to erase the data on lfs, but doesn't
    408 	 * hurt either.  No need to unlink because update_seed will
    409 	 * have already renamed over it.
    410 	 */
    411 	if (!ro) {
    412 		memset(&rs, 0, sizeof rs);
    413 		if ((size_t)(nwrit = pwrite(fd_old, &rs, sizeof rs, 0)) !=
    414 		    sizeof rs) {
    415 			if (nwrit == -1)
    416 				err(1, "overwrite old seed");
    417 			else
    418 				errx(1, "truncated overwrite");
    419 		}
    420 		if (fsync_range(fd_old, FDATASYNC|FDISKSYNC, 0, 0) == -1)
    421 			err(1, "fsync_range");
    422 	}
    423 
    424 	/* Fail noisily if anything went wrong.  */
    425 	if (fail)
    426 		exit(1);
    427 }
    428 
    429 static void
    430 do_ioctl(rndctl_t *rctl)
    431 {
    432 	int fd;
    433 	int res;
    434 
    435 	fd = open(_PATH_URANDOM, O_RDONLY, 0644);
    436 	if (fd < 0)
    437 		err(1, "open");
    438 
    439 	res = ioctl(fd, RNDCTL, rctl);
    440 	if (res < 0)
    441 		err(1, "ioctl(RNDCTL)");
    442 
    443 	close(fd);
    444 }
    445 
    446 static char *
    447 strflags(u_int32_t fl)
    448 {
    449 	static char str[512];
    450 
    451 	str[0] = '\0';
    452 	if (fl & RND_FLAG_NO_ESTIMATE)
    453 		;
    454 	else
    455 		strlcat(str, "estimate, ", sizeof(str));
    456 
    457 	if (fl & RND_FLAG_NO_COLLECT)
    458 		;
    459 	else
    460 		strlcat(str, "collect, ", sizeof(str));
    461 
    462 	if (fl & RND_FLAG_COLLECT_VALUE)
    463 		strlcat(str, "v, ", sizeof(str));
    464 	if (fl & RND_FLAG_COLLECT_TIME)
    465 		strlcat(str, "t, ", sizeof(str));
    466 	if (fl & RND_FLAG_ESTIMATE_VALUE)
    467 		strlcat(str, "dv, ", sizeof(str));
    468 	if (fl & RND_FLAG_ESTIMATE_TIME)
    469 		strlcat(str, "dt, ", sizeof(str));
    470 
    471 	if (str[strlen(str) - 2] == ',')
    472 		str[strlen(str) - 2] = '\0';
    473 
    474 	return (str);
    475 }
    476 
    477 #define HEADER "Source                 Bits Type      Flags\n"
    478 
    479 static void
    480 do_list(int all, u_int32_t type, char *name)
    481 {
    482 	rndstat_est_t rstat;
    483 	rndstat_est_name_t rstat_name;
    484 	int fd;
    485 	int res;
    486 	uint32_t i;
    487 	u_int32_t start;
    488 
    489 	fd = open(_PATH_URANDOM, O_RDONLY, 0644);
    490 	if (fd < 0)
    491 		err(1, "open");
    492 
    493 	if (all == 0 && type == 0xff) {
    494 		strncpy(rstat_name.name, name, sizeof(rstat_name.name));
    495 		res = ioctl(fd, RNDGETESTNAME, &rstat_name);
    496 		if (res < 0)
    497 			err(1, "ioctl(RNDGETESTNAME)");
    498 		printf(HEADER);
    499 		printf("%-16s %10u %-4s %s\n",
    500 		    rstat_name.source.rt.name,
    501 		    rstat_name.source.rt.total,
    502 		    find_name(rstat_name.source.rt.type),
    503 		    strflags(rstat_name.source.rt.flags));
    504 		if (vflag) {
    505 			printf("\tDt samples = %d\n",
    506 			       rstat_name.source.dt_samples);
    507 			printf("\tDt bits = %d\n",
    508 			       rstat_name.source.dt_total);
    509 			printf("\tDv samples = %d\n",
    510 				rstat_name.source.dv_samples);
    511 			printf("\tDv bits = %d\n",
    512 			       rstat_name.source.dv_total);
    513 		}
    514 		close(fd);
    515 		return;
    516 	}
    517 
    518 	/*
    519 	 * Run through all the devices present in the system, and either
    520 	 * print out ones that match, or print out all of them.
    521 	 */
    522 	printf(HEADER);
    523 	start = 0;
    524 	for (;;) {
    525 		rstat.count = RND_MAXSTATCOUNT;
    526 		rstat.start = start;
    527 		res = ioctl(fd, RNDGETESTNUM, &rstat);
    528 		if (res < 0)
    529 			err(1, "ioctl(RNDGETESTNUM)");
    530 
    531 		if (rstat.count == 0)
    532 			break;
    533 
    534 		for (i = 0; i < rstat.count; i++) {
    535 			if (all != 0 ||
    536 			    type == rstat.source[i].rt.type)
    537 				printf("%-16s %10u %-4s %s\n",
    538 				    rstat.source[i].rt.name,
    539 				    rstat.source[i].rt.total,
    540 				    find_name(rstat.source[i].rt.type),
    541 				    strflags(rstat.source[i].rt.flags));
    542 			if (vflag) {
    543 				printf("\tDt samples = %d\n",
    544 				       rstat.source[i].dt_samples);
    545 				printf("\tDt bits = %d\n",
    546 				       rstat.source[i].dt_total);
    547 				printf("\tDv samples = %d\n",
    548 				       rstat.source[i].dv_samples);
    549 				printf("\tDv bits = %d\n",
    550 				       rstat.source[i].dv_total);
    551 			}
    552                 }
    553 		start += rstat.count;
    554 	}
    555 
    556 	close(fd);
    557 }
    558 
    559 static void
    560 do_stats(void)
    561 {
    562 	rndpoolstat_t rs;
    563 	int fd;
    564 
    565 	fd = open(_PATH_URANDOM, O_RDONLY, 0644);
    566 	if (fd < 0)
    567 		err(1, "open");
    568 
    569 	if (ioctl(fd, RNDGETPOOLSTAT, &rs) < 0)
    570 		err(1, "ioctl(RNDGETPOOLSTAT)");
    571 
    572 	printf("\t%9u bits mixed into pool\n", rs.added);
    573 	printf("\t%9u bits currently stored in pool (max %u)\n",
    574 	    rs.curentropy, rs.maxentropy);
    575 	printf("\t%9u bits of entropy discarded due to full pool\n",
    576 	    rs.discarded);
    577 	printf("\t%9u hard-random bits generated\n", rs.removed);
    578 	printf("\t%9u pseudo-random bits generated\n", rs.generated);
    579 
    580 	close(fd);
    581 }
    582 
    583 int
    584 main(int argc, char **argv)
    585 {
    586 	rndctl_t rctl;
    587 	int ch, cmd, lflag, mflag, sflag;
    588 	u_int32_t type;
    589 	char name[16];
    590 	const char *filename = NULL;
    591 
    592 	if (SHA3_Selftest() != 0)
    593 		errx(1, "SHA-3 self-test failed");
    594 
    595 	rctl.mask = 0;
    596 	rctl.flags = 0;
    597 
    598 	cmd = 0;
    599 	lflag = 0;
    600 	mflag = 0;
    601 	sflag = 0;
    602 	type = 0xff;
    603 
    604 	while ((ch = getopt(argc, argv, "CES:L:celit:d:sv")) != -1) {
    605 		switch (ch) {
    606 		case 'C':
    607 			rctl.flags |= RND_FLAG_NO_COLLECT;
    608 			rctl.mask |= RND_FLAG_NO_COLLECT;
    609 			mflag++;
    610 			break;
    611 		case 'E':
    612 			rctl.flags |= RND_FLAG_NO_ESTIMATE;
    613 			rctl.mask |= RND_FLAG_NO_ESTIMATE;
    614 			mflag++;
    615 			break;
    616 		case 'L':
    617 			if (cmd != 0)
    618 				usage();
    619 			cmd = 'L';
    620 			filename = optarg;
    621 			break;
    622 		case 'S':
    623 			if (cmd != 0)
    624 				usage();
    625 			cmd = 'S';
    626 			filename = optarg;
    627 			break;
    628 		case 'c':
    629 			rctl.flags &= ~RND_FLAG_NO_COLLECT;
    630 			rctl.mask |= RND_FLAG_NO_COLLECT;
    631 			mflag++;
    632 			break;
    633 		case 'e':
    634 			rctl.flags &= ~RND_FLAG_NO_ESTIMATE;
    635 			rctl.mask |= RND_FLAG_NO_ESTIMATE;
    636 			mflag++;
    637 			break;
    638 		case 'i':
    639 			iflag = 1;
    640 			break;
    641 		case 'l':
    642 			lflag++;
    643 			break;
    644 		case 't':
    645 			if (cmd != 0)
    646 				usage();
    647 			cmd = 't';
    648 
    649 			type = find_type(optarg);
    650 			break;
    651 		case 'd':
    652 			if (cmd != 0)
    653 				usage();
    654 			cmd = 'd';
    655 
    656 			type = 0xff;
    657 			strlcpy(name, optarg, sizeof(name));
    658 			break;
    659 		case 's':
    660 			sflag++;
    661 			break;
    662 		case 'v':
    663 			vflag++;
    664 			break;
    665 		case '?':
    666 		default:
    667 			usage();
    668 		}
    669 	}
    670 	argc -= optind;
    671 	argv += optind;
    672 
    673 	/*
    674 	 * No leftover non-option arguments.
    675 	 */
    676 	if (argc > 0)
    677 		usage();
    678 
    679 	/*
    680 	 * -i makes sense only with -L.
    681 	 */
    682 	if (iflag && cmd != 'L')
    683 		usage();
    684 
    685 	/*
    686 	 * Save.
    687 	 */
    688 	if (cmd == 'S') {
    689 		do_save(filename);
    690 		exit(0);
    691 	}
    692 
    693 	/*
    694 	 * Load.
    695 	 */
    696 	if (cmd == 'L') {
    697 		do_load(filename);
    698 		exit(0);
    699 	}
    700 
    701 	/*
    702 	 * Cannot list and modify at the same time.
    703 	 */
    704 	if ((lflag != 0 || sflag != 0) && mflag != 0)
    705 		usage();
    706 
    707 	/*
    708 	 * Bomb out on no-ops.
    709 	 */
    710 	if (lflag == 0 && mflag == 0 && sflag == 0)
    711 		usage();
    712 
    713 	/*
    714 	 * If not listing, we need a device name or a type.
    715 	 */
    716 	if (lflag == 0 && cmd == 0 && sflag == 0)
    717 		usage();
    718 
    719 	/*
    720 	 * Modify request.
    721 	 */
    722 	if (mflag != 0) {
    723 		rctl.type = type;
    724 		strncpy(rctl.name, name, sizeof(rctl.name));
    725 		do_ioctl(&rctl);
    726 
    727 		exit(0);
    728 	}
    729 
    730 	/*
    731 	 * List sources.
    732 	 */
    733 	if (lflag != 0)
    734 		do_list(cmd == 0, type, name);
    735 
    736 	if (sflag != 0)
    737 		do_stats();
    738 
    739 	exit(0);
    740 }
    741