xref: /freebsd/lib/libc/tests/stdio/getdelim_test.c (revision 559a218c)
1 /*-
2  * Copyright (c) 2009 David Schultz <das@FreeBSD.org>
3  * Copyright (c) 2021 Dell EMC
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/wait.h>
32 
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 
39 #include <atf-c.h>
40 
41 #define	CHUNK_MAX	10
42 
43 /* The assertions depend on this string. */
44 char apothegm[] = "All work and no play\0 makes Jack a dull boy.\n";
45 
46 /*
47  * This is a neurotic reader function designed to give getdelim() a
48  * hard time. It reads through the string `apothegm' and returns a
49  * random number of bytes up to the requested length.
50  */
51 static int
_reader(void * cookie,char * buf,int len)52 _reader(void *cookie, char *buf, int len)
53 {
54 	size_t *offp = cookie;
55 	size_t r;
56 
57 	r = random() % CHUNK_MAX + 1;
58 	if (len > r)
59 		len = r;
60 	if (len > sizeof(apothegm) - *offp)
61 		len = sizeof(apothegm) - *offp;
62 	memcpy(buf, apothegm + *offp, len);
63 	*offp += len;
64 	return (len);
65 }
66 
67 static FILE *
mkfilebuf(void)68 mkfilebuf(void)
69 {
70 	size_t *offp;
71 
72 	offp = malloc(sizeof(*offp));	/* XXX leak */
73 	*offp = 0;
74 	return (fropen(offp, _reader));
75 }
76 
77 ATF_TC_WITHOUT_HEAD(getline_basic);
ATF_TC_BODY(getline_basic,tc)78 ATF_TC_BODY(getline_basic, tc)
79 {
80 	FILE *fp;
81 	char *line;
82 	size_t linecap;
83 	int i;
84 
85 	srandom(0);
86 
87 	/*
88 	 * Test multiple times with different buffer sizes
89 	 * and different _reader() return values.
90 	 */
91 	errno = 0;
92 	for (i = 0; i < 8; i++) {
93 		fp = mkfilebuf();
94 		linecap = i;
95 		line = malloc(i);
96 		/* First line: the full apothegm */
97 		ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1);
98 		ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0);
99 		ATF_REQUIRE(linecap >= sizeof(apothegm));
100 		/* Second line: the NUL terminator following the newline */
101 		ATF_REQUIRE(getline(&line, &linecap, fp) == 1);
102 		ATF_REQUIRE(line[0] == '\0' && line[1] == '\0');
103 		/* Third line: EOF */
104 		line[0] = 'X';
105 		ATF_REQUIRE(getline(&line, &linecap, fp) == -1);
106 		ATF_REQUIRE(line[0] == '\0');
107 		free(line);
108 		line = NULL;
109 		ATF_REQUIRE(feof(fp));
110 		ATF_REQUIRE(!ferror(fp));
111 		fclose(fp);
112 	}
113 	ATF_REQUIRE(errno == 0);
114 }
115 
116 ATF_TC_WITHOUT_HEAD(stream_error);
ATF_TC_BODY(stream_error,tc)117 ATF_TC_BODY(stream_error, tc)
118 {
119 	char *line;
120 	size_t linecap;
121 
122 	/* Make sure read errors are handled properly. */
123 	line = NULL;
124 	linecap = 0;
125 	errno = 0;
126 	ATF_REQUIRE(getline(&line, &linecap, stdout) == -1);
127 	ATF_REQUIRE(errno == EBADF);
128 	errno = 0;
129 	ATF_REQUIRE(getdelim(&line, &linecap, 'X', stdout) == -1);
130 	ATF_REQUIRE(errno == EBADF);
131 	ATF_REQUIRE(ferror(stdout));
132 }
133 
134 ATF_TC_WITHOUT_HEAD(invalid_params);
ATF_TC_BODY(invalid_params,tc)135 ATF_TC_BODY(invalid_params, tc)
136 {
137 	FILE *fp;
138 	char *line;
139 	size_t linecap;
140 
141 	/* Make sure NULL linep or linecapp pointers are handled. */
142 	fp = mkfilebuf();
143 	ATF_REQUIRE(getline(NULL, &linecap, fp) == -1);
144 	ATF_REQUIRE(errno == EINVAL);
145 	ATF_REQUIRE(getline(&line, NULL, fp) == -1);
146 	ATF_REQUIRE(errno == EINVAL);
147 	ATF_REQUIRE(ferror(fp));
148 	fclose(fp);
149 }
150 
151 ATF_TC_WITHOUT_HEAD(eof);
ATF_TC_BODY(eof,tc)152 ATF_TC_BODY(eof, tc)
153 {
154 	FILE *fp;
155 	char *line;
156 	size_t linecap;
157 
158 	/* Make sure getline() allocates memory as needed if fp is at EOF. */
159 	errno = 0;
160 	fp = mkfilebuf();
161 	while (!feof(fp))	/* advance to EOF; can't fseek this stream */
162 		getc(fp);
163 	line = NULL;
164 	linecap = 0;
165 	printf("getline\n");
166 	ATF_REQUIRE(getline(&line, &linecap, fp) == -1);
167 	ATF_REQUIRE(line[0] == '\0');
168 	ATF_REQUIRE(linecap > 0);
169 	ATF_REQUIRE(errno == 0);
170 	printf("feof\n");
171 	ATF_REQUIRE(feof(fp));
172 	ATF_REQUIRE(!ferror(fp));
173 	fclose(fp);
174 }
175 
176 ATF_TC_WITHOUT_HEAD(nul);
ATF_TC_BODY(nul,tc)177 ATF_TC_BODY(nul, tc)
178 {
179 	FILE *fp;
180 	char *line;
181 	size_t linecap, n;
182 
183 	errno = 0;
184 	line = NULL;
185 	linecap = 0;
186 	/* Make sure a NUL delimiter works. */
187 	fp = mkfilebuf();
188 	n = strlen(apothegm);
189 	printf("getdelim\n");
190 	ATF_REQUIRE(getdelim(&line, &linecap, '\0', fp) == n + 1);
191 	ATF_REQUIRE(strcmp(line, apothegm) == 0);
192 	ATF_REQUIRE(line[n + 1] == '\0');
193 	ATF_REQUIRE(linecap > n + 1);
194 	n = strlen(apothegm + n + 1);
195 	printf("getdelim 2\n");
196 	ATF_REQUIRE(getdelim(&line, &linecap, '\0', fp) == n + 1);
197 	ATF_REQUIRE(line[n + 1] == '\0');
198 	ATF_REQUIRE(linecap > n + 1);
199 	ATF_REQUIRE(errno == 0);
200 	ATF_REQUIRE(!ferror(fp));
201 	fclose(fp);
202 }
203 
204 ATF_TC_WITHOUT_HEAD(empty_NULL_buffer);
ATF_TC_BODY(empty_NULL_buffer,tc)205 ATF_TC_BODY(empty_NULL_buffer, tc)
206 {
207 	FILE *fp;
208 	char *line;
209 	size_t linecap;
210 
211 	/* Make sure NULL *linep and zero *linecapp are handled. */
212 	fp = mkfilebuf();
213 	line = NULL;
214 	linecap = 42;
215 	ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1);
216 	ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0);
217 	fp = mkfilebuf();
218 	free(line);
219 	line = malloc(100);
220 	linecap = 0;
221 	ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1);
222 	ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0);
223 	free(line);
224 	ATF_REQUIRE(!ferror(fp));
225 	fclose(fp);
226 }
227 
228 static void
_ipc_read(int fd,char wait_c)229 _ipc_read(int fd, char wait_c)
230 {
231 	char c;
232 	ssize_t len;
233 
234 	c = 0;
235 	while (c != wait_c) {
236 		len = read(fd, &c, 1);
237 		ATF_CHECK_MSG(len != 0,
238 		    "EOF on IPC pipe while waiting. Did other side fail?");
239 		ATF_CHECK_MSG(len == 1 || errno == EINTR,
240 		    "read %zu bytes errno %d\n", len, errno);
241 		if (len != 1 || errno != EINTR)
242 			break;
243 	}
244 }
245 
246 static void
_ipc_write(int fd,char c)247 _ipc_write(int fd, char c)
248 {
249 
250 	while ((write(fd, &c, 1) != 1))
251 		ATF_REQUIRE(errno == EINTR);
252 }
253 
254 static void
ipc_wait(int ipcfd[2])255 ipc_wait(int ipcfd[2])
256 {
257 
258 	_ipc_read(ipcfd[0], '+');
259 	/* Send ACK. */
260 	_ipc_write(ipcfd[1], '-');
261 }
262 
263 static void
ipc_wakeup(int ipcfd[2])264 ipc_wakeup(int ipcfd[2])
265 {
266 
267 	_ipc_write(ipcfd[1], '+');
268 	/* Wait for ACK. */
269 	_ipc_read(ipcfd[0], '-');
270 }
271 
272 static void
_nonblock_eagain(int buf_mode)273 _nonblock_eagain(int buf_mode)
274 {
275 	FILE *fp;
276 	const char delim = '!';
277 	const char *strs[] = {
278 	    "first line partial!",
279 	    "second line is sent in full!",
280 	    "third line is sent partially!",
281 	    "last line is sent in full!",
282 	};
283 	char *line;
284 	size_t linecap, strslen[nitems(strs)];
285 	ssize_t linelen;
286 	int fd_fifo, flags, i, ipcfd[2], pipedes[2], pipedes2[2], status;
287 	pid_t pid;
288 
289 	line = NULL;
290 	linecap = 0;
291 	for (i = 0; i < nitems(strslen); i++)
292 		strslen[i] = strlen(strs[i]);
293 	ATF_REQUIRE(pipe2(pipedes, O_CLOEXEC) == 0);
294 	ATF_REQUIRE(pipe2(pipedes2, O_CLOEXEC) == 0);
295 
296 	(void)unlink("fifo");
297 	ATF_REQUIRE(mkfifo("fifo", 0666) == 0);
298 	ATF_REQUIRE((pid = fork()) >= 0);
299 	if (pid == 0) {
300 		close(pipedes[0]);
301 		ipcfd[1] = pipedes[1];
302 		ipcfd[0] = pipedes2[0];
303 		close(pipedes2[1]);
304 
305 		ATF_REQUIRE((fd_fifo = open("fifo", O_WRONLY)) != -1);
306 
307 		/* Partial write. */
308 		ATF_REQUIRE(write(fd_fifo, strs[0], strslen[0] - 3) ==
309 		    strslen[0] - 3);
310 		ipc_wakeup(ipcfd);
311 
312 		ipc_wait(ipcfd);
313 		/* Finish off the first line. */
314 		ATF_REQUIRE(write(fd_fifo,
315 		    &(strs[0][strslen[0] - 3]), 3) == 3);
316 		/* And include the second full line and a partial 3rd line. */
317 		ATF_REQUIRE(write(fd_fifo, strs[1], strslen[1]) == strslen[1]);
318 		ATF_REQUIRE(write(fd_fifo, strs[2], strslen[2] - 3) ==
319 		    strslen[2] - 3);
320 		ipc_wakeup(ipcfd);
321 
322 		ipc_wait(ipcfd);
323 		/* Finish the partial write and partially send the last. */
324 		ATF_REQUIRE(write(fd_fifo,
325 		    &(strs[2][strslen[2] - 3]), 3) == 3);
326 		ATF_REQUIRE(write(fd_fifo, strs[3], strslen[3] - 3) ==
327 		    strslen[3] - 3);
328 		ipc_wakeup(ipcfd);
329 
330 		ipc_wait(ipcfd);
331 		/* Finish the write */
332 		ATF_REQUIRE(write(fd_fifo,
333 		    &(strs[3][strslen[3] - 3]), 3) == 3);
334 		ipc_wakeup(ipcfd);
335 		_exit(0);
336 	}
337 	ipcfd[0] = pipedes[0];
338 	close(pipedes[1]);
339 	close(pipedes2[0]);
340 	ipcfd[1] = pipedes2[1];
341 
342 	ATF_REQUIRE((fp = fopen("fifo", "r")) != NULL);
343 	setvbuf(fp, (char *)NULL, buf_mode, 0);
344 	ATF_REQUIRE((flags = fcntl(fileno(fp), F_GETFL, 0)) != -1);
345 	ATF_REQUIRE(fcntl(fileno(fp), F_SETFL, flags | O_NONBLOCK) >= 0);
346 
347 	/* Wait until the writer completes its partial write. */
348 	ipc_wait(ipcfd);
349 	ATF_REQUIRE_ERRNO(EAGAIN,
350 	    (linelen = getdelim(&line, &linecap, delim, fp)) == -1);
351 	ATF_REQUIRE_STREQ("", line);
352 	ATF_REQUIRE(ferror(fp));
353 	ATF_REQUIRE(!feof(fp));
354 	clearerr(fp);
355 	ipc_wakeup(ipcfd);
356 
357 	ipc_wait(ipcfd);
358 	/*
359 	 * Should now have the finished first line, a full second line,
360 	 * and a partial third line.
361 	 */
362 	ATF_CHECK(getdelim(&line, &linecap, delim, fp) == strslen[0]);
363 	ATF_REQUIRE_STREQ(strs[0], line);
364 	ATF_REQUIRE(getdelim(&line, &linecap, delim, fp) == strslen[1]);
365 	ATF_REQUIRE_STREQ(strs[1], line);
366 
367 	ATF_REQUIRE_ERRNO(EAGAIN,
368 	    (linelen = getdelim(&line, &linecap, delim, fp)) == -1);
369 	ATF_REQUIRE_STREQ("", line);
370 	ATF_REQUIRE(ferror(fp));
371 	ATF_REQUIRE(!feof(fp));
372 	clearerr(fp);
373 	ipc_wakeup(ipcfd);
374 
375 	/* Wait for the partial write to be completed and another to be done. */
376 	ipc_wait(ipcfd);
377 	ATF_REQUIRE((linelen = getdelim(&line, &linecap, delim, fp)) != -1);
378 	ATF_REQUIRE(!ferror(fp));
379 	ATF_REQUIRE(!feof(fp));
380 	ATF_REQUIRE_STREQ(strs[2], line);
381 	ATF_REQUIRE(linelen == strslen[2]);
382 
383 	ATF_REQUIRE_ERRNO(EAGAIN,
384 	    (linelen = getdelim(&line, &linecap, delim, fp)) == -1);
385 	ATF_REQUIRE_STREQ("", line);
386 	ATF_REQUIRE(ferror(fp));
387 	ATF_REQUIRE(!feof(fp));
388 	clearerr(fp);
389 	ipc_wakeup(ipcfd);
390 
391 	ipc_wait(ipcfd);
392 	ATF_REQUIRE((linelen = getdelim(&line, &linecap, delim, fp)) != -1);
393 	ATF_REQUIRE(!ferror(fp));
394 	ATF_REQUIRE(!feof(fp));
395 	ATF_REQUIRE_STREQ(strs[3], line);
396 	ATF_REQUIRE(linelen == strslen[3]);
397 
398 	ATF_REQUIRE(waitpid(pid, &status, WEXITED) != -1);
399 	ATF_REQUIRE(WIFEXITED(status));
400 	ATF_REQUIRE(WEXITSTATUS(status) == 0);
401 }
402 
403 ATF_TC_WITHOUT_HEAD(nonblock_eagain_buffered);
ATF_TC_BODY(nonblock_eagain_buffered,tc)404 ATF_TC_BODY(nonblock_eagain_buffered, tc)
405 {
406 
407 	_nonblock_eagain(_IOFBF);
408 }
409 
410 ATF_TC_WITHOUT_HEAD(nonblock_eagain_unbuffered);
ATF_TC_BODY(nonblock_eagain_unbuffered,tc)411 ATF_TC_BODY(nonblock_eagain_unbuffered, tc)
412 {
413 
414 	_nonblock_eagain(_IONBF);
415 }
416 
417 
ATF_TP_ADD_TCS(tp)418 ATF_TP_ADD_TCS(tp)
419 {
420 
421 	ATF_TP_ADD_TC(tp, getline_basic);
422 	ATF_TP_ADD_TC(tp, stream_error);
423 	ATF_TP_ADD_TC(tp, eof);
424 	ATF_TP_ADD_TC(tp, invalid_params);
425 	ATF_TP_ADD_TC(tp, nul);
426 	ATF_TP_ADD_TC(tp, empty_NULL_buffer);
427 	ATF_TP_ADD_TC(tp, nonblock_eagain_unbuffered);
428 	ATF_TP_ADD_TC(tp, nonblock_eagain_buffered);
429 
430 	return (atf_no_error());
431 }
432