1/*
2 * Initially written by Yar Tikhiy <yar@freebsd.org> in PR 76398.
3 * Bug fixes and instrumentation by kib@freebsd.org.
4 */
5
6#include <sys/types.h>
7#include <sys/socket.h>
8#include <sys/stat.h>
9#include <sys/wait.h>
10#include <err.h>
11#include <errno.h>
12#include <fcntl.h>
13#include <md5.h>
14#include <signal.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <unistd.h>
19
20#include <atf-c.h>
21
22#define NDATA	1000
23#define	DELAY	2
24
25static void
26hup(int signo __unused)
27{
28}
29
30static int ndata, seq;
31
32static void
33setdata(int n)
34{
35	ndata = n;
36	seq = 0;
37}
38
39static char *
40getdata(void)
41{
42	static char databuf[256];
43	static char xeof[] = "#";
44
45	if (seq > ndata)
46		return (NULL);
47	if (seq == ndata) {
48		seq++;
49		return (xeof);
50	}
51	sprintf(databuf, "%08d xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n", seq++);
52	return (databuf);
53}
54
55ATF_TC_WITHOUT_HEAD(eintr_test);
56ATF_TC_BODY(eintr_test, tc)
57{
58	char c, digest0[33], digest[33], *p;
59	FILE *fp;
60	int i, s[2], total0, total;
61	MD5_CTX md5;
62	pid_t child;
63	struct sigaction sa;
64
65	MD5Init(&md5);
66	setdata(NDATA);
67	for (total0 = 0; (p = getdata()) != NULL; total0 += strlen(p))
68		MD5Update(&md5, p, strlen(p));
69	p = MD5End(&md5, digest0);
70
71	sa.sa_handler = hup;
72	sigemptyset(&sa.sa_mask);
73	sa.sa_flags = 0;
74	ATF_REQUIRE(sigaction(SIGHUP, &sa, NULL) == 0);
75
76	ATF_REQUIRE(socketpair(PF_UNIX, SOCK_STREAM, 0, s) == 0);
77
78	switch (child = fork()) {
79	case -1:
80		atf_tc_fail("fork failed %s", strerror(errno));
81		break;
82
83	case 0:
84		ATF_REQUIRE((fp = fdopen(s[0], "w")) != NULL);
85		close(s[1]);
86		setdata(NDATA);
87		while ((p = getdata())) {
88			for (; *p;) {
89				if (fputc(*p, fp) == EOF) {
90					if (errno == EINTR) {
91						clearerr(fp);
92					} else {
93						atf_tc_fail("fputc errno %s",
94						    strerror(errno));
95					}
96				} else {
97					p++;
98				}
99			}
100		}
101		fclose(fp);
102		break;
103
104	default:
105		close(s[0]);
106		ATF_REQUIRE((fp = fdopen(s[1], "r")) != NULL);
107		sleep(DELAY);
108		ATF_REQUIRE(kill(child, SIGHUP) != -1);
109		sleep(DELAY);
110		MD5Init(&md5);
111		for (total = 0;;) {
112			i = fgetc(fp);
113			if (i == EOF) {
114				if (errno == EINTR) {
115					clearerr(fp);
116				} else {
117					atf_tc_fail("fgetc errno %s",
118					    strerror(errno));
119				}
120				continue;
121			}
122			total++;
123			c = i;
124			MD5Update(&md5, &c, 1);
125			if (i == '#')
126				break;
127		}
128		MD5End(&md5, digest);
129		fclose(fp);
130		ATF_REQUIRE_MSG(total == total0,
131		    "Total number of bytes read does not match: %d %d",
132		    total, total0);
133		ATF_REQUIRE_MSG(strcmp(digest, digest0) == 0,
134		    "Digests do not match %s %s", digest, digest0);
135		break;
136	}
137}
138
139ATF_TP_ADD_TCS(tp)
140{
141	ATF_TP_ADD_TC(tp, eintr_test);
142	return (atf_no_error());
143}
144