diff --git a/lib/libc/tests/stdio/Makefile b/lib/libc/tests/stdio/Makefile index 248a05bc45e7..792375ba17b8 100644 --- a/lib/libc/tests/stdio/Makefile +++ b/lib/libc/tests/stdio/Makefile @@ -2,6 +2,7 @@ .include +ATF_TESTS_C+= eintr_test ATF_TESTS_C+= fdopen_test ATF_TESTS_C+= fmemopen2_test ATF_TESTS_C+= fopen2_test @@ -30,6 +31,7 @@ NETBSD_ATF_TESTS_C+= popen_test NETBSD_ATF_TESTS_C+= printf_test NETBSD_ATF_TESTS_C+= scanf_test +LIBADD.eintr_test+= md LIBADD.printfloat_test+= m LIBADD.scanfloat_test+= m diff --git a/lib/libc/tests/stdio/eintr_test.c b/lib/libc/tests/stdio/eintr_test.c new file mode 100644 index 000000000000..55354c8926c5 --- /dev/null +++ b/lib/libc/tests/stdio/eintr_test.c @@ -0,0 +1,143 @@ +/* + * Initially written by Yar Tikhiy in PR 76398. + * Bug fixes and instrumentation by kib@freebsd.org. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define NDATA 1000 +#define DELAY 2 + +static void +hup(int signo __unused) +{ +} + +static int ndata, seq; + +static void +setdata(int n) +{ + ndata = n; + seq = 0; +} + +static char * +getdata(void) +{ + static char databuf[256]; + static char xeof[] = "#"; + + if (seq > ndata) + return (NULL); + if (seq == ndata) { + seq++; + return (xeof); + } + sprintf(databuf, "%08d xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n", seq++); + return (databuf); +} + +ATF_TC_WITHOUT_HEAD(eintr_test); +ATF_TC_BODY(eintr_test, tc) +{ + char c, digest0[33], digest[33], *p; + FILE *fp; + int i, s[2], total0, total; + MD5_CTX md5; + pid_t child; + struct sigaction sa; + + MD5Init(&md5); + setdata(NDATA); + for (total0 = 0; (p = getdata()) != NULL; total0 += strlen(p)) + MD5Update(&md5, p, strlen(p)); + p = MD5End(&md5, digest0); + + sa.sa_handler = hup; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + ATF_REQUIRE(sigaction(SIGHUP, &sa, NULL) == 0); + + ATF_REQUIRE(socketpair(PF_UNIX, SOCK_STREAM, 0, s) == 0); + + switch (child = fork()) { + case -1: + atf_tc_fail("fork failed %s", strerror(errno)); + break; + + case 0: + ATF_REQUIRE((fp = fdopen(s[0], "w")) != NULL); + close(s[1]); + setdata(NDATA); + while ((p = getdata())) { + for (; *p;) { + if (fputc(*p, fp) == EOF) { + if (errno == EINTR) { + clearerr(fp); + } else { + atf_tc_fail("fputc errno %s", + strerror(errno)); + } + } else { + p++; + } + } + } + fclose(fp); + break; + + default: + close(s[0]); + ATF_REQUIRE((fp = fdopen(s[1], "r")) != NULL); + sleep(DELAY); + ATF_REQUIRE(kill(child, SIGHUP) != -1); + sleep(DELAY); + MD5Init(&md5); + for (total = 0;;) { + i = fgetc(fp); + if (i == EOF) { + if (errno == EINTR) { + clearerr(fp); + } else { + atf_tc_fail("fgetc errno %s", + strerror(errno)); + } + continue; + } + total++; + c = i; + MD5Update(&md5, &c, 1); + if (i == '#') + break; + } + MD5End(&md5, digest); + fclose(fp); + ATF_REQUIRE_MSG(total == total0, + "Total number of bytes read does not match: %d %d", + total, total0); + ATF_REQUIRE_MSG(strcmp(digest, digest0) == 0, + "Digests do not match %s %s", digest, digest0); + break; + } +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, eintr_test); + return (atf_no_error()); +}