Module Name: src Committed By: christos Date: Fri Jul 9 15:26:59 UTC 2021
Modified Files: src/tests/lib/libc/stdio: h_intr.c t_intr.sh Log Message: fixes from RVP To generate a diff of this commit: cvs rdiff -u -r1.2 -r1.3 src/tests/lib/libc/stdio/h_intr.c cvs rdiff -u -r1.1 -r1.2 src/tests/lib/libc/stdio/t_intr.sh Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/tests/lib/libc/stdio/h_intr.c diff -u src/tests/lib/libc/stdio/h_intr.c:1.2 src/tests/lib/libc/stdio/h_intr.c:1.3 --- src/tests/lib/libc/stdio/h_intr.c:1.2 Thu Jul 8 11:21:40 2021 +++ src/tests/lib/libc/stdio/h_intr.c Fri Jul 9 11:26:59 2021 @@ -1,20 +1,20 @@ -/* $NetBSD: h_intr.c,v 1.2 2021/07/08 15:21:40 christos Exp $ */ +/* $NetBSD: h_intr.c,v 1.3 2021/07/09 15:26:59 christos Exp $ */ /** - * Test of interrupted writes to popen()'ed commands. + * Test of interrupted I/O to popen()ed commands. * * Example 1: - * ./h_fwrite -c "gzip -t" *.gz + * ./h_intr -c "gzip -t" *.gz * * Example 2: - * while :; do ./h_fwrite -b $((12*1024)) -t 10 -c "bzip2 -t" *.bz2; sleep 2; done + * while :; do ./h_intr -b $((12*1024)) -t 10 -c "bzip2 -t" *.bz2; sleep 2; done * * Example 3: * Create checksum file: * find /mnt -type f -exec sha512 -n {} + >SHA512 * * Check program: - * find /mnt -type f -exec ./h_fwrite -b 512 -c run.sh {} + + * find /mnt -type f -exec ./h_intr -b 512 -c run.sh {} + * * ./run.sh: #!/bin/sh @@ -25,7 +25,7 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: h_intr.c,v 1.2 2021/07/08 15:21:40 christos Exp $"); +__RCSID("$NetBSD: h_intr.c,v 1.3 2021/07/09 15:26:59 christos Exp $"); #include <time.h> #include <err.h> @@ -38,25 +38,29 @@ __RCSID("$NetBSD: h_intr.c,v 1.2 2021/07 #include <string.h> #include <unistd.h> -static int process(const char *fn); +static bool process(const char *fn); ssize_t maxread(FILE *fp, void *buf, size_t size); ssize_t smaxread(FILE *fp, void *buf, size_t size); ssize_t maxwrite(FILE *fp, const void *buf, size_t size); ssize_t smaxwrite(FILE *fp, const void *buf, size_t size); +static int rndbuf(void); +static int rndmode(void); static sig_t xsignal(int signo, sig_t handler); static void alarmtimer(int wait); static void pr_star(int signo); -static bool isvalid(const char *s); static int do_opts(int argc, char* argv[]); -static void usage(FILE* fp); +static void usage(FILE *fp); /* Globals */ static struct options { - size_t bsize; - size_t ssize; - int btype; - int tmout; - const char *cmd; + char* cmd; /* cmd to run (which must read from stdin) */ + size_t bsize; /* block size to use */ + size_t asize; /* alt. stdio buffer size */ + int btype; /* buffering type: _IONBF, ... */ + int tmout; /* alarm timeout */ + int flush; /* call fflush() after write if 1 */ + int rndbuf; /* switch buffer randomly if 1 */ + int rndmod; /* switch buffering modes randomly if 1 */ } opts; static const struct { @@ -68,15 +72,24 @@ static const struct { { "IOFBF", _IOFBF }, }; +static void (*alarm_fn)(int); /* real/dummy alarm fn. */ +static int (*sintr_fn)(int, int); /* " siginterrupt fn. */ +static ssize_t (*rd_fn)(FILE*, void*, size_t); /* read fn. */ +static ssize_t (*wr_fn)(FILE*, const void*, size_t); /* write fn. */ + enum { - MB = 1024 * 1024, - BSIZE = 16 * 1024, - DEF_MS = 100, - MS = 1000, + MB = 1024 * 1024, /* a megabyte */ + BSIZE = 16 * 1024, /* default RW buffer size */ + DEF_MS = 100, /* interrupt 10x a second */ + MS = 1000, /* msecs. in a second */ }; + +/** + * M A I N + */ int main(int argc, char* argv[]) { @@ -100,8 +113,8 @@ main(int argc, char* argv[]) sig_t osig = xsignal(SIGALRM, pr_star); - if (process(argv[i]) == 0) - printf("ok\n"); + if (process(argv[i]) == true) + printf(" OK\n"); else rc = EXIT_FAILURE; @@ -111,46 +124,79 @@ main(int argc, char* argv[]) return rc; } -static int +static bool process(const char *fn) { FILE *ifp, *ofp; - char *buf; + char *buf, *abuf; + int rc = false; size_t nw = 0; - int rc = EXIT_FAILURE; ssize_t n; - if ((buf = malloc(opts.bsize)) == NULL) - err(rc, "buffer alloc failed"); + abuf = NULL; + + if ((buf = malloc(opts.bsize)) == NULL) { + warn("buffer alloc failed"); + return rc; + } + + if ((abuf = malloc(opts.asize)) == NULL) { + warn("alt. buffer alloc failed"); + goto fail; + } if ((ifp = fopen(fn, "r")) == NULL) { warn("fopen failed: %s", fn); - return rc; + goto fail; + } + + if ((ofp = popen(opts.cmd, "w")) == NULL) { + warn("popen failed `%s'", opts.cmd); + goto fail; } - if ((ofp = popen(opts.cmd, "w")) == NULL) - err(rc, "popen failed `%s'", opts.cmd); + setvbuf(ofp, NULL, opts.btype, opts.asize); + setvbuf(ifp, NULL, opts.btype, opts.asize); - setvbuf(ofp, NULL, opts.btype, opts.ssize); - setvbuf(ifp, NULL, opts.btype, opts.ssize); + alarm_fn(opts.tmout); - alarmtimer(opts.tmout); - while ((n = maxread(ifp, buf, opts.bsize)) > 0) { + while ((n = rd_fn(ifp, buf, opts.bsize)) > 0) { ssize_t i; - if ((i = maxwrite(ofp, buf, n)) == -1) { + + if (opts.rndbuf || opts.rndmod) { + int r = rndbuf(); + setvbuf(ofp, r ? abuf : NULL, + rndmode(), r ? opts.asize : 0); + } + + sintr_fn(SIGALRM, 0); + + if ((i = wr_fn(ofp, buf, n)) == -1) { + sintr_fn(SIGALRM, 1); warn("write failed"); break; } + + if (opts.flush) + if (fflush(ofp)) + warn("fflush failed"); + + sintr_fn(SIGALRM, 1); nw += i; } - alarmtimer(0); + + alarm_fn(0); // printf("%zu\n", nw); fclose(ifp); if (pclose(ofp) != 0) warn("command failed `%s'", opts.cmd); else - rc = EXIT_SUCCESS; + rc = true; + +fail: + free(abuf); + free(buf); return rc; } @@ -238,7 +284,7 @@ smaxwrite(FILE* fp, const void* buf, siz } /** - * maxwrite - stdio version (substrate is buggy) + * maxwrite - stdio version (warning: substrate may be buggy) */ ssize_t maxwrite(FILE* fp, const void* buf, size_t size) @@ -268,6 +314,28 @@ maxwrite(FILE* fp, const void* buf, size return nwr; } +static int +rndbuf(void) +{ + if (opts.rndbuf == 0) + return 0; + return arc4random_uniform(2); +} + +static int +rndmode(void) +{ + if (opts.rndmod == 0) + return opts.btype; + + switch (arc4random_uniform(3)) { + case 0: return _IONBF; + case 1: return _IOLBF; + case 2: return _IOFBF; + default: errx(EXIT_FAILURE, "programmer error!"); + } +} + /** * wrapper around sigaction() because we want POSIX semantics: * no auto-restarting of interrupted slow syscalls. @@ -296,6 +364,20 @@ alarmtimer(int wait) setitimer(ITIMER_REAL, &itv, NULL); } +static void +dummytimer(int dummy) +{ + (void)dummy; +} + +static int +dummysintr(int dum1, int dum2) +{ + (void)dum1; + (void)dum2; + return 0; /* OK */ +} + /** * Print a `*' each time an alarm signal occurs. */ @@ -312,42 +394,60 @@ pr_star(int signo) } /** - * return true if not empty or blank; FAIL otherwise. + * return true if not empty or blank; false otherwise. */ static bool isvalid(const char *s) { - if (*s == '\0') - return false; return strspn(s, " \t") != strlen(s); } static const char * -getbtype(int val) { +btype2str(int val) +{ for (size_t i = 0; i < __arraycount(btypes); i++) if (btypes[i].value == val) return btypes[i].name; return "*invalid*"; } +static int +str2btype(const char* s) +{ + for (size_t i = 0; i < __arraycount(btypes); i++) + if (strcmp(btypes[i].name, s) == 0) + return btypes[i].value; + return EOF; +} + /** * Print usage information. */ static void usage(FILE* fp) { - fprintf(fp, "Usage: %s [-b SIZE] [-h] [-t TMOUT] -c CMD FILE...\n", - getprogname()); + fprintf(fp, "Usage: %s [-a SIZE] [-b SIZE] [-fihmnrsw]" + " [-p TYPE] [-t TMOUT] -c CMD FILE...\n", + getprogname()); fprintf(fp, "%s: Test interrupted writes to popen()ed CMD.\n", - getprogname()); + getprogname()); fprintf(fp, "\n"); - fprintf(fp, " -b SIZE Buffer size (%zu)\n", opts.bsize); - fprintf(fp, " -c CMD Command to run on each FILE.\n"); - fprintf(fp, " -h This message.\n"); - fprintf(fp, " -p Buffering type %s.\n", getbtype(opts.btype)); - fprintf(fp, " -s SIZE stdio buffer size (%zu)\n", opts.ssize); + fprintf(fp, "Usual options:\n"); + fprintf(fp, " -a SIZE Alt. stdio buffer size (%zu)\n", opts.asize); + fprintf(fp, " -b SIZE Program buffer size (%zu)\n", opts.bsize); + fprintf(fp, " -c CMD Command to run on each FILE\n"); + fprintf(fp, " -h This message\n"); + fprintf(fp, " -p TYPE Buffering type (%s)\n", btype2str(opts.btype)); fprintf(fp, " -t TMOUT Interrupt writing to CMD every (%d) ms\n", - opts.tmout); + opts.tmout); + fprintf(fp, "Debug options:\n"); + fprintf(fp, " -f Do fflush() after writing each block\n"); + fprintf(fp, " -i Use siginterrupt to block interrupts\n"); + fprintf(fp, " -m Use random buffering modes\n"); + fprintf(fp, " -n No interruptions (turns off -i)\n"); + fprintf(fp, " -r Use read() instead of fread()\n"); + fprintf(fp, " -s Switch between own/stdio buffers at random\n"); + fprintf(fp, " -w Use write() instead of fwrite()\n"); } /** @@ -356,50 +456,70 @@ usage(FILE* fp) static int do_opts(int argc, char *argv[]) { - int opt; - int i; - size_t j; + int opt, i; /* defaults */ + opts.cmd = ""; opts.btype = _IONBF; - opts.ssize = BSIZE; /* 16K */ + opts.asize = BSIZE; /* 16K */ opts.bsize = BSIZE; /* 16K */ opts.tmout = DEF_MS; /* 100ms */ - opts.cmd = ""; + opts.flush = 0; /* no fflush() after each write */ + opts.rndbuf = 0; /* no random buffer switching */ + opts.rndmod = 0; /* no random mode " */ + alarm_fn = alarmtimer; + sintr_fn = dummysintr; /* don't protect writes with siginterrupt() */ + rd_fn = maxread; /* read using stdio funcs. */ + wr_fn = maxwrite; /* write " */ - while ((opt = getopt(argc, argv, "b:c:hp:s:t:")) != -1) { + while ((opt = getopt(argc, argv, "a:b:c:fhimnp:rst:w")) != -1) { switch (opt) { + case 'a': + i = atoi(optarg); + if (i <= 0 || i > MB) + errx(EXIT_FAILURE, + "alt. buffer size not in range (1 - %d): %d", + MB, i); + opts.asize = i; + break; case 'b': i = atoi(optarg); if (i <= 0 || i > MB) errx(EXIT_FAILURE, - "buffer size not in range (1 - %d): %d", - MB, i); + "buffer size not in range (1 - %d): %d", + MB, i); opts.bsize = i; break; case 'c': opts.cmd = optarg; break; - case 'h': - usage(stdout); - exit(EXIT_SUCCESS); + case 'f': + opts.flush = 1; + break; + case 'i': + sintr_fn = siginterrupt; + break; + case 'm': + opts.rndmod = 1; + break; + case 'n': + alarm_fn = dummytimer; + break; case 'p': - for (j = 0; j < __arraycount(btypes); j++) - if (strcmp(btypes[j].name, optarg) == 0) { - opts.btype = btypes[j].value; - break; - } - if (j == __arraycount(btypes)) + i = str2btype(optarg); + if (i == EOF) errx(EXIT_FAILURE, - "unknown buffering type: `%s'", optarg); + "unknown buffering type: `%s'", optarg); + opts.btype = i; + break; + case 'r': + rd_fn = smaxread; + break; + case 'w': + wr_fn = smaxwrite; break; case 's': - i = atoi(optarg); - if (i <= 0 || i > MB) - errx(EXIT_FAILURE, - "buffer size not in range (1 - %d): %d", - MB, i); - opts.ssize = i; + opts.rndbuf = 1; break; case 't': i = atoi(optarg); @@ -408,6 +528,9 @@ do_opts(int argc, char *argv[]) "timeout not in range (10ms - 10s): %d", i); opts.tmout = i; break; + case 'h': + usage(stdout); + exit(EXIT_SUCCESS); default: usage(stderr); exit(EXIT_FAILURE); @@ -417,5 +540,9 @@ do_opts(int argc, char *argv[]) if (!isvalid(opts.cmd)) errx(EXIT_FAILURE, "Please specify a valid command with -c"); + /* don't call siginterrupt() if not interrupting */ + if (alarm_fn == dummytimer) + sintr_fn = dummysintr; + return optind; } Index: src/tests/lib/libc/stdio/t_intr.sh diff -u src/tests/lib/libc/stdio/t_intr.sh:1.1 src/tests/lib/libc/stdio/t_intr.sh:1.2 --- src/tests/lib/libc/stdio/t_intr.sh:1.1 Thu Jul 8 05:07:46 2021 +++ src/tests/lib/libc/stdio/t_intr.sh Fri Jul 9 11:26:59 2021 @@ -1,4 +1,4 @@ -# $NetBSD: t_intr.sh,v 1.1 2021/07/08 09:07:46 christos Exp $ +# $NetBSD: t_intr.sh,v 1.2 2021/07/09 15:26:59 christos Exp $ # # Copyright (c) 2021 The NetBSD Foundation, Inc. # All rights reserved. @@ -38,7 +38,7 @@ TMOUT=20 h_test() { "${DIR}/h_makenumbers" "$1" > numbers.in "${DIR}/h_intr" \ - -p "$2" -b ${BSIZE} -s ${SSIZE} -t ${TMOUT} \ + -p "$2" -a ${SSIZE} -b ${BSIZE} -t ${TMOUT} \ -c "dd of=numbers.out msgfmt=quiet" numbers.in "${DIR}/h_testnumbers" < numbers.out }