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