xref: /netbsd-src/tests/lib/libc/stdio/h_intr.c (revision bdb5d51c43e0698030f2d2eaf8207053047a3dbb)
1*bdb5d51cSrillig /*	$NetBSD: h_intr.c,v 1.6 2021/09/11 18:18:28 rillig Exp $	*/
299be3a9dSchristos 
399be3a9dSchristos /**
4ad9a00f7Schristos  * Test of interrupted I/O to popen()ed commands.
599be3a9dSchristos  *
699be3a9dSchristos  * Example 1:
7ad9a00f7Schristos  * ./h_intr -c "gzip -t" *.gz
899be3a9dSchristos  *
999be3a9dSchristos  * Example 2:
10ad9a00f7Schristos  * while :; do ./h_intr -b $((12*1024)) -t 10 -c "bzip2 -t" *.bz2; sleep 2; done
1199be3a9dSchristos  *
1299be3a9dSchristos  * Example 3:
1399be3a9dSchristos  * Create checksum file:
1499be3a9dSchristos  * find /mnt -type f -exec sha512 -n {} + >SHA512
1599be3a9dSchristos  *
1699be3a9dSchristos  * Check program:
17ad9a00f7Schristos  * find /mnt -type f -exec ./h_intr -b 512 -c run.sh {} +
1899be3a9dSchristos  *
1999be3a9dSchristos  * ./run.sh:
2099be3a9dSchristos 	#!/bin/sh
2199be3a9dSchristos 	set -eu
2299be3a9dSchristos 	grep -q "^$(sha512 -q)" SHA512
2399be3a9dSchristos  *
2499be3a9dSchristos  * Author: RVP at sdf.org
2599be3a9dSchristos  */
2699be3a9dSchristos 
2799be3a9dSchristos #include <sys/cdefs.h>
28*bdb5d51cSrillig __RCSID("$NetBSD: h_intr.c,v 1.6 2021/09/11 18:18:28 rillig Exp $");
2999be3a9dSchristos 
3099be3a9dSchristos #include <time.h>
3199be3a9dSchristos #include <err.h>
3299be3a9dSchristos #include <errno.h>
3399be3a9dSchristos #include <stdbool.h>
3499be3a9dSchristos #include <libgen.h>
3599be3a9dSchristos #include <signal.h>
3699be3a9dSchristos #include <stdio.h>
3799be3a9dSchristos #include <stdlib.h>
3899be3a9dSchristos #include <string.h>
3999be3a9dSchristos #include <unistd.h>
4099be3a9dSchristos 
41ad9a00f7Schristos static bool process(const char *fn);
4299be3a9dSchristos ssize_t maxread(FILE *fp, void *buf, size_t size);
4399be3a9dSchristos ssize_t smaxread(FILE *fp, void *buf, size_t size);
4499be3a9dSchristos ssize_t maxwrite(FILE *fp, const void *buf, size_t size);
4599be3a9dSchristos ssize_t smaxwrite(FILE *fp, const void *buf, size_t size);
46ad9a00f7Schristos static int rndbuf(void);
47ad9a00f7Schristos static int rndmode(void);
4899be3a9dSchristos static sig_t xsignal(int signo, sig_t handler);
4999be3a9dSchristos static void alarmtimer(int wait);
5099be3a9dSchristos static void pr_star(int signo);
5199be3a9dSchristos static int do_opts(int argc, char *argv[]);
5299be3a9dSchristos static void usage(FILE *fp);
5399be3a9dSchristos 
5499be3a9dSchristos /* Globals */
5599be3a9dSchristos static struct options {
56d0dfc7dfSkre 	const char *cmd;	/* cmd to run (which must read from stdin) */
57ad9a00f7Schristos 	size_t bsize;		/* block size to use */
58ad9a00f7Schristos 	size_t asize;		/* alt. stdio buffer size */
59ad9a00f7Schristos 	int btype;		/* buffering type: _IONBF, ... */
60ad9a00f7Schristos 	int tmout;		/* alarm timeout */
61ad9a00f7Schristos 	int flush;		/* call fflush() after write if 1 */
62ad9a00f7Schristos 	int rndbuf;		/* switch buffer randomly if 1 */
63ad9a00f7Schristos 	int rndmod;		/* switch buffering modes randomly if 1 */
6499be3a9dSchristos } opts;
6599be3a9dSchristos 
6699be3a9dSchristos static const struct {
6799be3a9dSchristos 	const char *name;
6899be3a9dSchristos 	int value;
6999be3a9dSchristos } btypes[] = {
7099be3a9dSchristos 	{ "IONBF", _IONBF },
7199be3a9dSchristos 	{ "IOLBF", _IOLBF },
7299be3a9dSchristos 	{ "IOFBF", _IOFBF },
7399be3a9dSchristos };
7499be3a9dSchristos 
75ad9a00f7Schristos static void (*alarm_fn)(int);				/* real/dummy alarm fn. */
76ad9a00f7Schristos static int (*sintr_fn)(int, int);			/*  " siginterrupt fn. */
77ad9a00f7Schristos static ssize_t (*rd_fn)(FILE *, void *, size_t);	/* read fn. */
78ad9a00f7Schristos static ssize_t (*wr_fn)(FILE *, const void *, size_t);	/* write fn. */
79ad9a00f7Schristos 
8099be3a9dSchristos enum {
81ad9a00f7Schristos 	MB = 1024 * 1024,	/* a megabyte */
82ad9a00f7Schristos 	BSIZE = 16 * 1024,	/* default RW buffer size */
83ad9a00f7Schristos 	DEF_MS = 100,		/* interrupt 10x a second */
84ad9a00f7Schristos 	MS = 1000,		/* msecs. in a second */
8599be3a9dSchristos };
8699be3a9dSchristos 
8799be3a9dSchristos 
8899be3a9dSchristos 
89ad9a00f7Schristos 
90ad9a00f7Schristos /**
91ad9a00f7Schristos  * M A I N
92ad9a00f7Schristos  */
9399be3a9dSchristos int
main(int argc,char * argv[])9499be3a9dSchristos main(int argc, char *argv[])
9599be3a9dSchristos {
9699be3a9dSchristos 	int i, rc = EXIT_SUCCESS;
9799be3a9dSchristos 
9899be3a9dSchristos 	i = do_opts(argc, argv);
9999be3a9dSchristos 	argc -= i;
10099be3a9dSchristos 	argv += i;
10199be3a9dSchristos 
10299be3a9dSchristos 	if (argc == 0) {
10399be3a9dSchristos 		usage(stderr);
10499be3a9dSchristos 		return rc;
10599be3a9dSchristos 	}
10699be3a9dSchristos 
10799be3a9dSchristos 	xsignal(SIGPIPE, SIG_IGN);
10899be3a9dSchristos 	for (i = 0; i < argc; i++) {
10999be3a9dSchristos 		char *s = strdup(argv[i]);
11099be3a9dSchristos 		printf("%s...", basename(s));
11199be3a9dSchristos 		fflush(stdout);
11299be3a9dSchristos 		free(s);
11399be3a9dSchristos 
11499be3a9dSchristos 		sig_t osig = xsignal(SIGALRM, pr_star);
11599be3a9dSchristos 
116ad9a00f7Schristos 		if (process(argv[i]) == true)
117ad9a00f7Schristos 			printf(" OK\n");
11899be3a9dSchristos 		else
11999be3a9dSchristos 			rc = EXIT_FAILURE;
12099be3a9dSchristos 
12199be3a9dSchristos 		xsignal(SIGALRM, osig);
12299be3a9dSchristos 	}
12399be3a9dSchristos 
12499be3a9dSchristos 	return rc;
12599be3a9dSchristos }
12699be3a9dSchristos 
127ad9a00f7Schristos static bool
process(const char * fn)12899be3a9dSchristos process(const char *fn)
12999be3a9dSchristos {
13099be3a9dSchristos 	FILE *ifp, *ofp;
131ad9a00f7Schristos 	char *buf, *abuf;
132ad9a00f7Schristos 	int rc = false;
13399be3a9dSchristos 	size_t nw = 0;
13499be3a9dSchristos 	ssize_t n;
13599be3a9dSchristos 
136ad9a00f7Schristos 	abuf = NULL;
13799be3a9dSchristos 
138ad9a00f7Schristos 	if ((buf = malloc(opts.bsize)) == NULL) {
139ad9a00f7Schristos 		warn("buffer alloc failed");
14099be3a9dSchristos 		return rc;
14199be3a9dSchristos 	}
14299be3a9dSchristos 
143ad9a00f7Schristos 	if ((abuf = malloc(opts.asize)) == NULL) {
144ad9a00f7Schristos 		warn("alt. buffer alloc failed");
145ad9a00f7Schristos 		goto fail;
146ad9a00f7Schristos 	}
14799be3a9dSchristos 
148ad9a00f7Schristos 	if ((ifp = fopen(fn, "r")) == NULL) {
149ad9a00f7Schristos 		warn("fopen failed: %s", fn);
150ad9a00f7Schristos 		goto fail;
151ad9a00f7Schristos 	}
15299be3a9dSchristos 
153ad9a00f7Schristos 	if ((ofp = popen(opts.cmd, "w")) == NULL) {
154ad9a00f7Schristos 		warn("popen failed `%s'", opts.cmd);
155ad9a00f7Schristos 		goto fail;
156ad9a00f7Schristos 	}
157ad9a00f7Schristos 
158ad9a00f7Schristos 	setvbuf(ofp, NULL, opts.btype, opts.asize);
159ad9a00f7Schristos 	setvbuf(ifp, NULL, opts.btype, opts.asize);
160ad9a00f7Schristos 
161ad9a00f7Schristos 	alarm_fn(opts.tmout);
162ad9a00f7Schristos 
163ad9a00f7Schristos 	while ((n = rd_fn(ifp, buf, opts.bsize)) > 0) {
16499be3a9dSchristos 		ssize_t i;
165ad9a00f7Schristos 
166ad9a00f7Schristos 		if (opts.rndbuf || opts.rndmod) {
167ad9a00f7Schristos 			int r = rndbuf();
168ad9a00f7Schristos 			setvbuf(ofp, r ? abuf : NULL,
169ad9a00f7Schristos 				rndmode(), r ? opts.asize : 0);
170ad9a00f7Schristos 		}
171ad9a00f7Schristos 
172ad9a00f7Schristos 		sintr_fn(SIGALRM, 0);
173ad9a00f7Schristos 
174ad9a00f7Schristos 		if ((i = wr_fn(ofp, buf, n)) == -1) {
175ad9a00f7Schristos 			sintr_fn(SIGALRM, 1);
17699be3a9dSchristos 			warn("write failed");
17799be3a9dSchristos 			break;
17899be3a9dSchristos 		}
179ad9a00f7Schristos 
180ad9a00f7Schristos 		if (opts.flush)
181ad9a00f7Schristos 			if (fflush(ofp))
182ad9a00f7Schristos 				warn("fflush failed");
183ad9a00f7Schristos 
184ad9a00f7Schristos 		sintr_fn(SIGALRM, 1);
18599be3a9dSchristos 		nw += i;
18699be3a9dSchristos 	}
187ad9a00f7Schristos 
188ad9a00f7Schristos 	alarm_fn(0);
189209a58fbSchristos 	// printf("%zu\n", nw);
19099be3a9dSchristos 
19199be3a9dSchristos 	fclose(ifp);
19299be3a9dSchristos 	if (pclose(ofp) != 0)
19399be3a9dSchristos 		warn("command failed `%s'", opts.cmd);
19499be3a9dSchristos 	else
195ad9a00f7Schristos 		rc = true;
196ad9a00f7Schristos 
197ad9a00f7Schristos fail:
198ad9a00f7Schristos 	free(abuf);
199ad9a00f7Schristos 	free(buf);
20099be3a9dSchristos 
20199be3a9dSchristos 	return rc;
20299be3a9dSchristos }
20399be3a9dSchristos 
20499be3a9dSchristos /**
20599be3a9dSchristos  * maxread - syscall version
20699be3a9dSchristos  */
20799be3a9dSchristos ssize_t
smaxread(FILE * fp,void * buf,size_t size)20899be3a9dSchristos smaxread(FILE* fp, void *buf, size_t size)
20999be3a9dSchristos {
21099be3a9dSchristos 	char *p = buf;
21199be3a9dSchristos 	ssize_t nrd = 0;
21299be3a9dSchristos 	ssize_t n;
21399be3a9dSchristos 
21499be3a9dSchristos 	while (size > 0) {
21599be3a9dSchristos 		n = read(fileno(fp), p, size);
21699be3a9dSchristos 		if (n < 0) {
21799be3a9dSchristos 			if (errno == EINTR)
21899be3a9dSchristos 				continue;
21999be3a9dSchristos 			else
22099be3a9dSchristos 				return -1;
22199be3a9dSchristos 		} else if (n == 0)
22299be3a9dSchristos 			break;
22399be3a9dSchristos 		p += n;
22499be3a9dSchristos 		nrd += n;
22599be3a9dSchristos 		size -= n;
22699be3a9dSchristos 	}
22799be3a9dSchristos 	return nrd;
22899be3a9dSchristos }
22999be3a9dSchristos 
23099be3a9dSchristos /**
23199be3a9dSchristos  * maxread - stdio version
23299be3a9dSchristos  */
23399be3a9dSchristos ssize_t
maxread(FILE * fp,void * buf,size_t size)23499be3a9dSchristos maxread(FILE* fp, void *buf, size_t size)
23599be3a9dSchristos {
23699be3a9dSchristos 	char *p = buf;
23799be3a9dSchristos 	ssize_t nrd = 0;
23899be3a9dSchristos 	size_t n;
23999be3a9dSchristos 
24099be3a9dSchristos 	while (size > 0) {
24199be3a9dSchristos 		errno = 0;
24299be3a9dSchristos 		n = fread(p, 1, size, fp);
24399be3a9dSchristos 		if (n == 0) {
24499be3a9dSchristos 			printf("ir.");
24599be3a9dSchristos 			fflush(stdout);
24699be3a9dSchristos 			if (errno == EINTR)
24799be3a9dSchristos 				continue;
24899be3a9dSchristos 			if (feof(fp) || nrd > 0)
24999be3a9dSchristos 				break;
25099be3a9dSchristos 			return -1;
25199be3a9dSchristos 		}
25299be3a9dSchristos 		if (n != size)
25399be3a9dSchristos 			clearerr(fp);
25499be3a9dSchristos 		p += n;
25599be3a9dSchristos 		nrd += n;
25699be3a9dSchristos 		size -= n;
25799be3a9dSchristos 	}
25899be3a9dSchristos 	return nrd;
25999be3a9dSchristos }
26099be3a9dSchristos 
26199be3a9dSchristos /**
26299be3a9dSchristos  * maxwrite - syscall version
26399be3a9dSchristos  */
26499be3a9dSchristos ssize_t
smaxwrite(FILE * fp,const void * buf,size_t size)26599be3a9dSchristos smaxwrite(FILE* fp, const void *buf, size_t size)
26699be3a9dSchristos {
26799be3a9dSchristos 	const char *p = buf;
26899be3a9dSchristos 	ssize_t nwr = 0;
26999be3a9dSchristos 	ssize_t n;
27099be3a9dSchristos 
27199be3a9dSchristos 	while (size > 0) {
27299be3a9dSchristos 		n = write(fileno(fp), p, size);
27399be3a9dSchristos 		if (n <= 0) {
27499be3a9dSchristos 			if (errno == EINTR)
27599be3a9dSchristos 				n = 0;
27699be3a9dSchristos 			else
27799be3a9dSchristos 				return -1;
27899be3a9dSchristos 		}
27999be3a9dSchristos 		p += n;
28099be3a9dSchristos 		nwr += n;
28199be3a9dSchristos 		size -= n;
28299be3a9dSchristos 	}
28399be3a9dSchristos 	return nwr;
28499be3a9dSchristos }
28599be3a9dSchristos 
28699be3a9dSchristos /**
287ad9a00f7Schristos  * maxwrite - stdio version (warning: substrate may be buggy)
28899be3a9dSchristos  */
28999be3a9dSchristos ssize_t
maxwrite(FILE * fp,const void * buf,size_t size)29099be3a9dSchristos maxwrite(FILE* fp, const void *buf, size_t size)
29199be3a9dSchristos {
29299be3a9dSchristos 	const char *p = buf;
29399be3a9dSchristos 	ssize_t nwr = 0;
29499be3a9dSchristos 	size_t n;
29599be3a9dSchristos 
29699be3a9dSchristos 	while (size > 0) {
29799be3a9dSchristos 		errno = 0;
29899be3a9dSchristos 		n = fwrite(p, 1, size, fp);
29999be3a9dSchristos 		if (n == 0) {
30099be3a9dSchristos 			printf("iw.");
30199be3a9dSchristos 			fflush(stdout);
30299be3a9dSchristos 			if (errno == EINTR)
30399be3a9dSchristos 				continue;
30499be3a9dSchristos 			if (nwr > 0)
30599be3a9dSchristos 				break;
30699be3a9dSchristos 			return -1;
30799be3a9dSchristos 		}
30899be3a9dSchristos 		if (n != size)
30999be3a9dSchristos 			clearerr(fp);
31099be3a9dSchristos 		p += n;
31199be3a9dSchristos 		nwr += n;
31299be3a9dSchristos 		size -= n;
31399be3a9dSchristos 	}
31499be3a9dSchristos 	return nwr;
31599be3a9dSchristos }
31699be3a9dSchristos 
317ad9a00f7Schristos static int
rndbuf(void)318ad9a00f7Schristos rndbuf(void)
319ad9a00f7Schristos {
320ad9a00f7Schristos 	if (opts.rndbuf == 0)
321ad9a00f7Schristos 		return 0;
322ad9a00f7Schristos 	return arc4random_uniform(2);
323ad9a00f7Schristos }
324ad9a00f7Schristos 
325ad9a00f7Schristos static int
rndmode(void)326ad9a00f7Schristos rndmode(void)
327ad9a00f7Schristos {
328ad9a00f7Schristos 	if (opts.rndmod == 0)
329ad9a00f7Schristos 		return opts.btype;
330ad9a00f7Schristos 
331ad9a00f7Schristos 	switch (arc4random_uniform(3)) {
332ad9a00f7Schristos 	case 0:	return _IONBF;
333ad9a00f7Schristos 	case 1: return _IOLBF;
334ad9a00f7Schristos 	case 2: return _IOFBF;
335ad9a00f7Schristos 	default: errx(EXIT_FAILURE, "programmer error!");
336ad9a00f7Schristos 	}
337ad9a00f7Schristos }
338ad9a00f7Schristos 
33999be3a9dSchristos /**
34099be3a9dSchristos  * wrapper around sigaction() because we want POSIX semantics:
34199be3a9dSchristos  * no auto-restarting of interrupted slow syscalls.
34299be3a9dSchristos  */
34399be3a9dSchristos static sig_t
xsignal(int signo,sig_t handler)34499be3a9dSchristos xsignal(int signo, sig_t handler)
34599be3a9dSchristos {
34699be3a9dSchristos 	struct sigaction sa, osa;
34799be3a9dSchristos 
34899be3a9dSchristos 	sa.sa_handler = handler;
34999be3a9dSchristos 	sa.sa_flags = 0;
35099be3a9dSchristos 	sigemptyset(&sa.sa_mask);
35199be3a9dSchristos 	if (sigaction(signo, &sa, &osa) < 0)
35299be3a9dSchristos 		return SIG_ERR;
35399be3a9dSchristos 	return osa.sa_handler;
35499be3a9dSchristos }
35599be3a9dSchristos 
35699be3a9dSchristos static void
alarmtimer(int wait)35799be3a9dSchristos alarmtimer(int wait)
35899be3a9dSchristos {
35999be3a9dSchristos 	struct itimerval itv;
36099be3a9dSchristos 
36199be3a9dSchristos 	itv.it_value.tv_sec = wait / MS;
36299be3a9dSchristos 	itv.it_value.tv_usec = (wait - itv.it_value.tv_sec * MS) * MS;
36399be3a9dSchristos 	itv.it_interval = itv.it_value;
36499be3a9dSchristos 	setitimer(ITIMER_REAL, &itv, NULL);
36599be3a9dSchristos }
36699be3a9dSchristos 
367ad9a00f7Schristos static void
dummytimer(int dummy)368ad9a00f7Schristos dummytimer(int dummy)
369ad9a00f7Schristos {
370ad9a00f7Schristos 	(void)dummy;
371ad9a00f7Schristos }
372ad9a00f7Schristos 
373ad9a00f7Schristos static int
dummysintr(int dum1,int dum2)374ad9a00f7Schristos dummysintr(int dum1, int dum2)
375ad9a00f7Schristos {
376ad9a00f7Schristos 	(void)dum1;
377ad9a00f7Schristos 	(void)dum2;
378ad9a00f7Schristos 	return 0;	/* OK */
379ad9a00f7Schristos }
380ad9a00f7Schristos 
38199be3a9dSchristos /**
38299be3a9dSchristos  * Print a `*' each time an alarm signal occurs.
38399be3a9dSchristos  */
38499be3a9dSchristos static void
pr_star(int signo)38599be3a9dSchristos pr_star(int signo)
38699be3a9dSchristos {
38799be3a9dSchristos 	int oe = errno;
38899be3a9dSchristos 	(void)signo;
38999be3a9dSchristos 
39099be3a9dSchristos #if 0
39199be3a9dSchristos 	write(1, "*", 1);
39299be3a9dSchristos #endif
39399be3a9dSchristos 	errno = oe;
39499be3a9dSchristos }
39599be3a9dSchristos 
39699be3a9dSchristos /**
397ad9a00f7Schristos  * return true if not empty or blank; false otherwise.
39899be3a9dSchristos  */
39999be3a9dSchristos static bool
isvalid(const char * s)40099be3a9dSchristos isvalid(const char *s)
40199be3a9dSchristos {
40299be3a9dSchristos 	return strspn(s, " \t") != strlen(s);
40399be3a9dSchristos }
40499be3a9dSchristos 
40599be3a9dSchristos static const char *
btype2str(int val)406ad9a00f7Schristos btype2str(int val)
407ad9a00f7Schristos {
40899be3a9dSchristos 	for (size_t i = 0; i < __arraycount(btypes); i++)
40999be3a9dSchristos 		if (btypes[i].value == val)
41099be3a9dSchristos 			return btypes[i].name;
41199be3a9dSchristos 	return "*invalid*";
41299be3a9dSchristos }
41399be3a9dSchristos 
414ad9a00f7Schristos static int
str2btype(const char * s)415ad9a00f7Schristos str2btype(const char *s)
416ad9a00f7Schristos {
417ad9a00f7Schristos 	for (size_t i = 0; i < __arraycount(btypes); i++)
418ad9a00f7Schristos 		if (strcmp(btypes[i].name, s) == 0)
419ad9a00f7Schristos 			return btypes[i].value;
420ad9a00f7Schristos 	return EOF;
421ad9a00f7Schristos }
422ad9a00f7Schristos 
42399be3a9dSchristos /**
42499be3a9dSchristos  * Print usage information.
42599be3a9dSchristos  */
42699be3a9dSchristos static void
usage(FILE * fp)42799be3a9dSchristos usage(FILE* fp)
42899be3a9dSchristos {
429ad9a00f7Schristos 	fprintf(fp, "Usage: %s [-a SIZE] [-b SIZE] [-fihmnrsw]"
430ad9a00f7Schristos 		    " [-p TYPE] [-t TMOUT] -c CMD FILE...\n",
43199be3a9dSchristos 		getprogname());
43299be3a9dSchristos 	fprintf(fp, "%s: Test interrupted writes to popen()ed CMD.\n",
43399be3a9dSchristos 		getprogname());
43499be3a9dSchristos 	fprintf(fp, "\n");
435ad9a00f7Schristos 	fprintf(fp, "Usual options:\n");
436ad9a00f7Schristos 	fprintf(fp, "  -a SIZE   Alt. stdio buffer size (%zu)\n", opts.asize);
437ad9a00f7Schristos 	fprintf(fp, "  -b SIZE   Program buffer size (%zu)\n", opts.bsize);
438ad9a00f7Schristos 	fprintf(fp, "  -c CMD    Command to run on each FILE\n");
439ad9a00f7Schristos 	fprintf(fp, "  -h        This message\n");
440ad9a00f7Schristos 	fprintf(fp, "  -p TYPE   Buffering type (%s)\n", btype2str(opts.btype));
44199be3a9dSchristos 	fprintf(fp, "  -t TMOUT  Interrupt writing to CMD every (%d) ms\n",
44299be3a9dSchristos 		opts.tmout);
443ad9a00f7Schristos 	fprintf(fp, "Debug options:\n");
444ad9a00f7Schristos 	fprintf(fp, "  -f        Do fflush() after writing each block\n");
445ad9a00f7Schristos 	fprintf(fp, "  -i        Use siginterrupt to block interrupts\n");
446ad9a00f7Schristos 	fprintf(fp, "  -m        Use random buffering modes\n");
447ad9a00f7Schristos 	fprintf(fp, "  -n        No interruptions (turns off -i)\n");
448ad9a00f7Schristos 	fprintf(fp, "  -r        Use read() instead of fread()\n");
449ad9a00f7Schristos 	fprintf(fp, "  -s        Switch between own/stdio buffers at random\n");
450ad9a00f7Schristos 	fprintf(fp, "  -w        Use write() instead of fwrite()\n");
45199be3a9dSchristos }
45299be3a9dSchristos 
45399be3a9dSchristos /**
45499be3a9dSchristos  * Process program options.
45599be3a9dSchristos  */
45699be3a9dSchristos static int
do_opts(int argc,char * argv[])45799be3a9dSchristos do_opts(int argc, char *argv[])
45899be3a9dSchristos {
459ad9a00f7Schristos 	int opt, i;
46099be3a9dSchristos 
46199be3a9dSchristos 	/* defaults */
462ad9a00f7Schristos 	opts.cmd = "";
46399be3a9dSchristos 	opts.btype = _IONBF;
464ad9a00f7Schristos 	opts.asize = BSIZE;		/* 16K */
46599be3a9dSchristos 	opts.bsize = BSIZE;		/* 16K */
46699be3a9dSchristos 	opts.tmout = DEF_MS;		/* 100ms */
467ad9a00f7Schristos 	opts.flush = 0;			/* no fflush() after each write */
468ad9a00f7Schristos 	opts.rndbuf = 0;		/* no random buffer switching */
469ad9a00f7Schristos 	opts.rndmod = 0;		/* no random mode    " */
470ad9a00f7Schristos 	alarm_fn = alarmtimer;
471ad9a00f7Schristos 	sintr_fn = dummysintr;		/* don't protect writes with siginterrupt() */
472ad9a00f7Schristos 	rd_fn = maxread;		/* read using stdio funcs. */
473ad9a00f7Schristos 	wr_fn = maxwrite;		/* write   "   */
47499be3a9dSchristos 
475ad9a00f7Schristos 	while ((opt = getopt(argc, argv, "a:b:c:fhimnp:rst:w")) != -1) {
47699be3a9dSchristos 		switch (opt) {
477ad9a00f7Schristos 		case 'a':
478ad9a00f7Schristos 			i = atoi(optarg);
479ad9a00f7Schristos 			if (i <= 0 || i > MB)
480ad9a00f7Schristos 				errx(EXIT_FAILURE,
481ad9a00f7Schristos 				     "alt. buffer size not in range (1 - %d): %d",
482ad9a00f7Schristos 				     MB, i);
483ad9a00f7Schristos 			opts.asize = i;
484ad9a00f7Schristos 			break;
48599be3a9dSchristos 		case 'b':
48699be3a9dSchristos 			i = atoi(optarg);
48799be3a9dSchristos 			if (i <= 0 || i > MB)
48899be3a9dSchristos 				errx(EXIT_FAILURE,
48999be3a9dSchristos 				     "buffer size not in range (1 - %d): %d",
49099be3a9dSchristos 				     MB, i);
49199be3a9dSchristos 			opts.bsize = i;
49299be3a9dSchristos 			break;
49399be3a9dSchristos 		case 'c':
49499be3a9dSchristos 			opts.cmd = optarg;
49599be3a9dSchristos 			break;
496ad9a00f7Schristos 		case 'f':
497ad9a00f7Schristos 			opts.flush = 1;
49899be3a9dSchristos 			break;
499ad9a00f7Schristos 		case 'i':
500ad9a00f7Schristos 			sintr_fn = siginterrupt;
501ad9a00f7Schristos 			break;
502ad9a00f7Schristos 		case 'm':
503ad9a00f7Schristos 			opts.rndmod = 1;
504ad9a00f7Schristos 			break;
505ad9a00f7Schristos 		case 'n':
506ad9a00f7Schristos 			alarm_fn = dummytimer;
507ad9a00f7Schristos 			break;
508ad9a00f7Schristos 		case 'p':
509ad9a00f7Schristos 			i = str2btype(optarg);
510ad9a00f7Schristos 			if (i == EOF)
51199be3a9dSchristos 				errx(EXIT_FAILURE,
51299be3a9dSchristos 				     "unknown buffering type: `%s'", optarg);
513ad9a00f7Schristos 			opts.btype = i;
514ad9a00f7Schristos 			break;
515ad9a00f7Schristos 		case 'r':
516ad9a00f7Schristos 			rd_fn = smaxread;
517ad9a00f7Schristos 			break;
518ad9a00f7Schristos 		case 'w':
519ad9a00f7Schristos 			wr_fn = smaxwrite;
52099be3a9dSchristos 			break;
52199be3a9dSchristos 		case 's':
522ad9a00f7Schristos 			opts.rndbuf = 1;
52399be3a9dSchristos 			break;
52499be3a9dSchristos 		case 't':
52599be3a9dSchristos 			i = atoi(optarg);
52699be3a9dSchristos 			if ((i < 10 || i > 10000) && i != 0)
52799be3a9dSchristos 				errx(EXIT_FAILURE,
52899be3a9dSchristos 				    "timeout not in range (10ms - 10s): %d", i);
52999be3a9dSchristos 			opts.tmout = i;
53099be3a9dSchristos 			break;
531ad9a00f7Schristos 		case 'h':
532ad9a00f7Schristos 			usage(stdout);
533ad9a00f7Schristos 			exit(EXIT_SUCCESS);
53499be3a9dSchristos 		default:
53599be3a9dSchristos 			usage(stderr);
53699be3a9dSchristos 			exit(EXIT_FAILURE);
53799be3a9dSchristos 		}
53899be3a9dSchristos 	}
53999be3a9dSchristos 
54099be3a9dSchristos 	if (!isvalid(opts.cmd))
54199be3a9dSchristos 		errx(EXIT_FAILURE, "Please specify a valid command with -c");
54299be3a9dSchristos 
543ad9a00f7Schristos 	/* don't call siginterrupt() if not interrupting */
544ad9a00f7Schristos 	if (alarm_fn == dummytimer)
545ad9a00f7Schristos 		sintr_fn = dummysintr;
546ad9a00f7Schristos 
54799be3a9dSchristos 	return optind;
54899be3a9dSchristos }
549