Home | History | Annotate | Line # | Download | only in rndctl
rndctl.c revision 1.30.16.1
      1 /*	$NetBSD: rndctl.c,v 1.30.16.1 2020/04/08 14:07:20 martin 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 #include <sys/cdefs.h>
     32 #include <sys/types.h>
     33 #include <sha1.h>
     34 
     35 #ifndef lint
     36 __RCSID("$NetBSD: rndctl.c,v 1.30.16.1 2020/04/08 14:07:20 martin Exp $");
     37 #endif
     38 
     39 
     40 #include <sys/types.h>
     41 #include <sys/ioctl.h>
     42 #include <sys/param.h>
     43 #include <sys/rndio.h>
     44 #include <sys/sha3.h>
     45 
     46 #include <stdio.h>
     47 #include <stdlib.h>
     48 #include <unistd.h>
     49 #include <fcntl.h>
     50 #include <errno.h>
     51 #include <err.h>
     52 #include <paths.h>
     53 #include <string.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 vflag;
     83 
     84 static void
     85 usage(void)
     86 {
     87 
     88 	fprintf(stderr, "usage: %s [-CEce] [-d devname | -t devtype]\n",
     89 	    getprogname());
     90 	fprintf(stderr, "       %s [-lsv] [-d devname | -t devtype]\n",
     91 	    getprogname());
     92 	fprintf(stderr, "	%s -[L|S] save-file\n", getprogname());
     93 	exit(1);
     94 }
     95 
     96 static u_int32_t
     97 find_type(const char *name)
     98 {
     99 	const arg_t *a;
    100 
    101 	a = source_types;
    102 
    103 	while (a->a_name != NULL) {
    104 		if (strcmp(a->a_name, name) == 0)
    105 			return (a->a_type);
    106 		a++;
    107 	}
    108 
    109 	errx(1, "device name %s unknown", name);
    110 	return (0);
    111 }
    112 
    113 static const char *
    114 find_name(u_int32_t type)
    115 {
    116 	const arg_t *a;
    117 
    118 	a = source_types;
    119 
    120 	while (a->a_name != NULL) {
    121 		if (type == a->a_type)
    122 			return (a->a_name);
    123 		a++;
    124 	}
    125 
    126 	warnx("device type %u unknown", type);
    127 	return ("???");
    128 }
    129 
    130 static void
    131 do_save(const char *filename, const void *extra, size_t nextra,
    132     uint32_t extraentropy)
    133 {
    134 	char tmp[PATH_MAX];
    135 	uint32_t systementropy;
    136 	uint8_t buf[32];
    137 	SHAKE128_CTX shake128;
    138 	rndsave_t rs;
    139 	SHA1_CTX s;
    140 	ssize_t nread, nwrit;
    141 	int fd;
    142 
    143 	/* Paranoia: Avoid stack memory disclosure.  */
    144 	memset(&rs, 0, sizeof rs);
    145 
    146 	/* Format the temporary file name.  */
    147 	if (snprintf(tmp, sizeof tmp, "%s.tmp", filename) >= PATH_MAX)
    148 		errx(1, "path too long");
    149 
    150 	/* Open /dev/urandom.  */
    151 	if ((fd = open(_PATH_URANDOM, O_RDONLY)) == -1)
    152 		err(1, "device open");
    153 
    154 	/* Find how much entropy is in the pool.  */
    155 	if (ioctl(fd, RNDGETENTCNT, &systementropy) == -1)
    156 		err(1, "ioctl(RNDGETENTCNT)");
    157 
    158 	/* Read some data from /dev/urandom.  */
    159 	if ((size_t)(nread = read(fd, buf, sizeof buf)) != sizeof buf) {
    160 		if (nread == -1)
    161 			err(1, "read");
    162 		else
    163 			errx(1, "truncated read");
    164 	}
    165 
    166 	/* Close /dev/urandom; we're done with it.  */
    167 	if (close(fd) == -1)
    168 		warn("close");
    169 	fd = -1;		/* paranoia */
    170 
    171 	/*
    172 	 * Hash what we read together with the extra input to generate
    173 	 * the seed data.
    174 	 */
    175 	SHAKE128_Init(&shake128);
    176 	SHAKE128_Update(&shake128, buf, sizeof buf);
    177 	SHAKE128_Update(&shake128, extra, nextra);
    178 	SHAKE128_Final(rs.data, sizeof(rs.data), &shake128);
    179 	explicit_memset(&shake128, 0, sizeof shake128); /* paranoia */
    180 
    181 	/*
    182 	 * Report an upper bound on the min-entropy of the seed data.
    183 	 * We take the larger of the system entropy and the extra
    184 	 * entropy -- the system state and the extra input may or may
    185 	 * not be independent, so we can't add them -- and clamp to the
    186 	 * size of the data.
    187 	 */
    188 	systementropy = MIN(systementropy,
    189 	    MIN(sizeof(buf), UINT32_MAX/NBBY)*NBBY);
    190 	extraentropy = MIN(extraentropy, MIN(nextra, UINT32_MAX/NBBY)*NBBY);
    191 	rs.entropy = MIN(MAX(systementropy, extraentropy),
    192 	    MIN(sizeof(rs.data), UINT32_MAX/NBBY)*NBBY);
    193 
    194 	/*
    195 	 * Compute the checksum on the 32-bit entropy count, in host
    196 	 * byte order (XXX this means it is not portable across
    197 	 * different-endian platforms!), followed by the seed data.
    198 	 */
    199 	SHA1Init(&s);
    200 	SHA1Update(&s, (const uint8_t *)&rs.entropy, sizeof(rs.entropy));
    201 	SHA1Update(&s, rs.data, sizeof(rs.data));
    202 	SHA1Final(rs.digest, &s);
    203 	explicit_memset(&s, 0, sizeof s); /* paranoia */
    204 
    205 	/*
    206 	 * Write it to a temporary file and sync it before we commit.
    207 	 * This way either the old seed or the new seed is completely
    208 	 * written in the expected location on disk even if the system
    209 	 * crashes as long as the file system doesn't get corrupted too
    210 	 * badly.
    211 	 *
    212 	 * If interrupted after this point and the temporary file is
    213 	 * disclosed, no big deal -- either the pool was predictable to
    214 	 * begin with in which case we're hosed either way, or we've
    215 	 * just revealed some output which is not a problem.
    216 	 */
    217 	if ((fd = open(tmp, O_CREAT|O_TRUNC|O_WRONLY, 0600)) == -1)
    218 		err(1, "open seed file to save");
    219 	if ((size_t)(nwrit = write(fd, &rs, sizeof rs)) != sizeof rs) {
    220 		int error = errno;
    221 		if (unlink(tmp) == -1)
    222 			warn("unlink");
    223 		if (nwrit == -1)
    224 			errc(1, error, "write");
    225 		else
    226 			errx(1, "truncated write");
    227 	}
    228 	explicit_memset(&rs, 0, sizeof rs); /* paranoia */
    229 	if (fsync_range(fd, FDATASYNC|FDISKSYNC, 0, 0) == -1) {
    230 		int error = errno;
    231 		if (unlink(tmp) == -1)
    232 			warn("unlink");
    233 		errc(1, error, "fsync_range");
    234 	}
    235 	if (close(fd) == -1)
    236 		warn("close");
    237 
    238 	/* Rename it over the original file to commit.  */
    239 	if (rename(tmp, filename) == -1)
    240 		err(1, "rename");
    241 }
    242 
    243 static void
    244 do_load(const char *filename)
    245 {
    246 	char tmp[PATH_MAX];
    247 	int fd_seed, fd_random;
    248 	rndsave_t rs;
    249 	rnddata_t rd;
    250 	ssize_t nread, nwrit;
    251 	SHA1_CTX s;
    252 	uint8_t digest[SHA1_DIGEST_LENGTH];
    253 
    254 	/*
    255 	 * The order of operations is important here:
    256 	 *
    257 	 * 1. Load the old seed.
    258 	 * 2. Feed the old seed into the kernel.
    259 	 * 3. Generate and write a new seed.
    260 	 * 4. Erase the old seed.
    261 	 *
    262 	 * This follows the procedure in
    263 	 *
    264 	 *	Niels Ferguson, Bruce Schneier, and Tadayoshi Kohno,
    265 	 *	_Cryptography Engineering_, Wiley, 2010, Sec. 9.6.2
    266 	 *	`Update Seed File'.
    267 	 *
    268 	 * There is a race condition: If another process generates a
    269 	 * key from /dev/urandom after step (2) but before step (3),
    270 	 * and if the machine crashes before step (3), an adversary who
    271 	 * can read the disk after the crash can probably guess the
    272 	 * complete state of the entropy pool and thereby predict the
    273 	 * key.
    274 	 *
    275 	 * There's not much we can do here without some kind of
    276 	 * systemwide lock on /dev/urandom and without introducing an
    277 	 * opportunity for a crash to wipe out the entropy altogether.
    278 	 * To avoid this race, you should ensure that any key
    279 	 * generation happens _after_ `rndctl -L' has completed.
    280 	 */
    281 
    282 	/* Format the temporary file name.  */
    283 	if (snprintf(tmp, sizeof tmp, "%s.tmp", filename) >= PATH_MAX)
    284 		errx(1, "path too long");
    285 
    286 	/* 1. Load the old seed.  */
    287 	if ((fd_seed = open(filename, O_RDWR)) == -1)
    288 		err(1, "open seed file to load");
    289 	if ((size_t)(nread = read(fd_seed, &rs, sizeof rs)) != sizeof rs) {
    290 		if (nread == -1)
    291 			err(1, "read seed");
    292 		else
    293 			errx(1, "seed too short");
    294 	}
    295 
    296 	/* Verify its checksum.  */
    297 	SHA1Init(&s);
    298 	SHA1Update(&s, (const uint8_t *)&rs.entropy, sizeof(rs.entropy));
    299 	SHA1Update(&s, rs.data, sizeof(rs.data));
    300 	SHA1Final(digest, &s);
    301 	if (!consttime_memequal(digest, rs.digest, sizeof(digest))) {
    302 		/*
    303 		 * If the checksum doesn't match, doesn't hurt to feed
    304 		 * the seed in anyway, but act as though it has zero
    305 		 * entropy in case it was corrupted with predictable
    306 		 * garbage.
    307 		 */
    308 		warnx("bad checksum");
    309 		rs.entropy = 0;
    310 	}
    311 
    312 	/* Format the ioctl request.  */
    313 	rd.len = MIN(sizeof(rd.data), sizeof(rs.data));
    314 	rd.entropy = rs.entropy;
    315 	memcpy(rd.data, rs.data, rd.len);
    316 	explicit_memset(&rs, 0, sizeof rs); /* paranoia */
    317 
    318 	/* 2. Feed the old seed into the kernel.  */
    319 	if ((fd_random = open(_PATH_URANDOM, O_WRONLY)) == -1)
    320 		err(1, "open /dev/urandom");
    321 	if (ioctl(fd_random, RNDADDDATA, &rd) == -1)
    322 		err(1, "RNDADDDATA");
    323 	if (close(fd_random) == -1)
    324 		warn("close /dev/urandom");
    325 	fd_random = -1;		/* paranoia */
    326 
    327 	/*
    328 	 * 3. Generate and write a new seed.  Note that we hash the old
    329 	 * seed together with whatever /dev/urandom returns in do_save.
    330 	 * Why?  After RNDADDDATA, the input may not be distributed
    331 	 * immediately to /dev/urandom.
    332 	 */
    333 	do_save(filename, rd.data, rd.len, rd.entropy);
    334 	explicit_memset(&rd, 0, sizeof rd); /* paranoia */
    335 
    336 	/*
    337 	 * 4. Erase the old seed.  Only effective if we're on a
    338 	 * fixed-address file system like ffs -- doesn't help to erase
    339 	 * the data on lfs, but doesn't hurt either.  No need to unlink
    340 	 * because do_save will have already overwritten it.
    341 	 */
    342 	memset(&rs, 0, sizeof rs);
    343 	if ((size_t)(nwrit = pwrite(fd_seed, &rs, sizeof rs, 0)) !=
    344 	    sizeof rs) {
    345 		if (nwrit == -1)
    346 			err(1, "overwrite old seed");
    347 		else
    348 			errx(1, "truncated overwrite");
    349 	}
    350 	if (fsync_range(fd_seed, FDATASYNC|FDISKSYNC, 0, 0) == -1)
    351 		err(1, "fsync_range");
    352 }
    353 
    354 static void
    355 do_ioctl(rndctl_t *rctl)
    356 {
    357 	int fd;
    358 	int res;
    359 
    360 	fd = open(_PATH_URANDOM, O_RDONLY, 0644);
    361 	if (fd < 0)
    362 		err(1, "open");
    363 
    364 	res = ioctl(fd, RNDCTL, rctl);
    365 	if (res < 0)
    366 		err(1, "ioctl(RNDCTL)");
    367 
    368 	close(fd);
    369 }
    370 
    371 static char *
    372 strflags(u_int32_t fl)
    373 {
    374 	static char str[512];
    375 
    376 	str[0] = '\0';
    377 	if (fl & RND_FLAG_NO_ESTIMATE)
    378 		;
    379 	else
    380 		strlcat(str, "estimate, ", sizeof(str));
    381 
    382 	if (fl & RND_FLAG_NO_COLLECT)
    383 		;
    384 	else
    385 		strlcat(str, "collect, ", sizeof(str));
    386 
    387 	if (fl & RND_FLAG_COLLECT_VALUE)
    388 		strlcat(str, "v, ", sizeof(str));
    389 	if (fl & RND_FLAG_COLLECT_TIME)
    390 		strlcat(str, "t, ", sizeof(str));
    391 	if (fl & RND_FLAG_ESTIMATE_VALUE)
    392 		strlcat(str, "dv, ", sizeof(str));
    393 	if (fl & RND_FLAG_ESTIMATE_TIME)
    394 		strlcat(str, "dt, ", sizeof(str));
    395 
    396 	if (str[strlen(str) - 2] == ',')
    397 		str[strlen(str) - 2] = '\0';
    398 
    399 	return (str);
    400 }
    401 
    402 #define HEADER "Source                 Bits Type      Flags\n"
    403 
    404 static void
    405 do_list(int all, u_int32_t type, char *name)
    406 {
    407 	rndstat_est_t rstat;
    408 	rndstat_est_name_t rstat_name;
    409 	int fd;
    410 	int res;
    411 	uint32_t i;
    412 	u_int32_t start;
    413 
    414 	fd = open(_PATH_URANDOM, O_RDONLY, 0644);
    415 	if (fd < 0)
    416 		err(1, "open");
    417 
    418 	if (all == 0 && type == 0xff) {
    419 		strncpy(rstat_name.name, name, sizeof(rstat_name.name));
    420 		res = ioctl(fd, RNDGETESTNAME, &rstat_name);
    421 		if (res < 0)
    422 			err(1, "ioctl(RNDGETESTNAME)");
    423 		printf(HEADER);
    424 		printf("%-16s %10u %-4s %s\n",
    425 		    rstat_name.source.rt.name,
    426 		    rstat_name.source.rt.total,
    427 		    find_name(rstat_name.source.rt.type),
    428 		    strflags(rstat_name.source.rt.flags));
    429 		if (vflag) {
    430 			printf("\tDt samples = %d\n",
    431 			       rstat_name.source.dt_samples);
    432 			printf("\tDt bits = %d\n",
    433 			       rstat_name.source.dt_total);
    434 			printf("\tDv samples = %d\n",
    435 				rstat_name.source.dv_samples);
    436 			printf("\tDv bits = %d\n",
    437 			       rstat_name.source.dv_total);
    438 		}
    439 		close(fd);
    440 		return;
    441 	}
    442 
    443 	/*
    444 	 * Run through all the devices present in the system, and either
    445 	 * print out ones that match, or print out all of them.
    446 	 */
    447 	printf(HEADER);
    448 	start = 0;
    449 	for (;;) {
    450 		rstat.count = RND_MAXSTATCOUNT;
    451 		rstat.start = start;
    452 		res = ioctl(fd, RNDGETESTNUM, &rstat);
    453 		if (res < 0)
    454 			err(1, "ioctl(RNDGETESTNUM)");
    455 
    456 		if (rstat.count == 0)
    457 			break;
    458 
    459 		for (i = 0; i < rstat.count; i++) {
    460 			if (all != 0 ||
    461 			    type == rstat.source[i].rt.type)
    462 				printf("%-16s %10u %-4s %s\n",
    463 				    rstat.source[i].rt.name,
    464 				    rstat.source[i].rt.total,
    465 				    find_name(rstat.source[i].rt.type),
    466 				    strflags(rstat.source[i].rt.flags));
    467 			if (vflag) {
    468 				printf("\tDt samples = %d\n",
    469 				       rstat.source[i].dt_samples);
    470 				printf("\tDt bits = %d\n",
    471 				       rstat.source[i].dt_total);
    472 				printf("\tDv samples = %d\n",
    473 				       rstat.source[i].dv_samples);
    474 				printf("\tDv bits = %d\n",
    475 				       rstat.source[i].dv_total);
    476 			}
    477                 }
    478 		start += rstat.count;
    479 	}
    480 
    481 	close(fd);
    482 }
    483 
    484 static void
    485 do_stats(void)
    486 {
    487 	rndpoolstat_t rs;
    488 	int fd;
    489 
    490 	fd = open(_PATH_URANDOM, O_RDONLY, 0644);
    491 	if (fd < 0)
    492 		err(1, "open");
    493 
    494 	if (ioctl(fd, RNDGETPOOLSTAT, &rs) < 0)
    495 		err(1, "ioctl(RNDGETPOOLSTAT)");
    496 
    497 	printf("\t%9u bits mixed into pool\n", rs.added);
    498 	printf("\t%9u bits currently stored in pool (max %u)\n",
    499 	    rs.curentropy, rs.maxentropy);
    500 	printf("\t%9u bits of entropy discarded due to full pool\n",
    501 	    rs.discarded);
    502 	printf("\t%9u hard-random bits generated\n", rs.removed);
    503 	printf("\t%9u pseudo-random bits generated\n", rs.generated);
    504 
    505 	close(fd);
    506 }
    507 
    508 int
    509 main(int argc, char **argv)
    510 {
    511 	rndctl_t rctl;
    512 	int ch, cmd, lflag, mflag, sflag;
    513 	u_int32_t type;
    514 	char name[16];
    515 	const char *filename = NULL;
    516 
    517 	if (SHA3_Selftest() != 0)
    518 		errx(1, "SHA-3 self-test failed");
    519 
    520 	rctl.mask = 0;
    521 	rctl.flags = 0;
    522 
    523 	cmd = 0;
    524 	lflag = 0;
    525 	mflag = 0;
    526 	sflag = 0;
    527 	type = 0xff;
    528 
    529 	while ((ch = getopt(argc, argv, "CES:L:celt:d:sv")) != -1) {
    530 		switch (ch) {
    531 		case 'C':
    532 			rctl.flags |= RND_FLAG_NO_COLLECT;
    533 			rctl.mask |= RND_FLAG_NO_COLLECT;
    534 			mflag++;
    535 			break;
    536 		case 'E':
    537 			rctl.flags |= RND_FLAG_NO_ESTIMATE;
    538 			rctl.mask |= RND_FLAG_NO_ESTIMATE;
    539 			mflag++;
    540 			break;
    541 		case 'L':
    542 			if (cmd != 0)
    543 				usage();
    544 			cmd = 'L';
    545 			filename = optarg;
    546 			break;
    547 		case 'S':
    548 			if (cmd != 0)
    549 				usage();
    550 			cmd = 'S';
    551 			filename = optarg;
    552 			break;
    553 		case 'c':
    554 			rctl.flags &= ~RND_FLAG_NO_COLLECT;
    555 			rctl.mask |= RND_FLAG_NO_COLLECT;
    556 			mflag++;
    557 			break;
    558 		case 'e':
    559 			rctl.flags &= ~RND_FLAG_NO_ESTIMATE;
    560 			rctl.mask |= RND_FLAG_NO_ESTIMATE;
    561 			mflag++;
    562 			break;
    563 		case 'l':
    564 			lflag++;
    565 			break;
    566 		case 't':
    567 			if (cmd != 0)
    568 				usage();
    569 			cmd = 't';
    570 
    571 			type = find_type(optarg);
    572 			break;
    573 		case 'd':
    574 			if (cmd != 0)
    575 				usage();
    576 			cmd = 'd';
    577 
    578 			type = 0xff;
    579 			strlcpy(name, optarg, sizeof(name));
    580 			break;
    581 		case 's':
    582 			sflag++;
    583 			break;
    584 		case 'v':
    585 			vflag++;
    586 			break;
    587 		case '?':
    588 		default:
    589 			usage();
    590 		}
    591 	}
    592 	argc -= optind;
    593 	argv += optind;
    594 
    595 	/*
    596 	 * No leftover non-option arguments.
    597 	 */
    598 	if (argc > 0)
    599 		usage();
    600 
    601 	/*
    602 	 * Save.
    603 	 */
    604 	if (cmd == 'S') {
    605 		do_save(filename, NULL, 0, 0);
    606 		exit(0);
    607 	}
    608 
    609 	/*
    610 	 * Load.
    611 	 */
    612 	if (cmd == 'L') {
    613 		do_load(filename);
    614 		exit(0);
    615 	}
    616 
    617 	/*
    618 	 * Cannot list and modify at the same time.
    619 	 */
    620 	if ((lflag != 0 || sflag != 0) && mflag != 0)
    621 		usage();
    622 
    623 	/*
    624 	 * Bomb out on no-ops.
    625 	 */
    626 	if (lflag == 0 && mflag == 0 && sflag == 0)
    627 		usage();
    628 
    629 	/*
    630 	 * If not listing, we need a device name or a type.
    631 	 */
    632 	if (lflag == 0 && cmd == 0 && sflag == 0)
    633 		usage();
    634 
    635 	/*
    636 	 * Modify request.
    637 	 */
    638 	if (mflag != 0) {
    639 		rctl.type = type;
    640 		strncpy(rctl.name, name, sizeof(rctl.name));
    641 		do_ioctl(&rctl);
    642 
    643 		exit(0);
    644 	}
    645 
    646 	/*
    647 	 * List sources.
    648 	 */
    649 	if (lflag != 0)
    650 		do_list(cmd == 0, type, name);
    651 
    652 	if (sflag != 0)
    653 		do_stats();
    654 
    655 	exit(0);
    656 }
    657