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 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 * 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); 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); 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); 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); 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); 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); 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 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 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 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 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 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); 404 ATF_TC_BODY(nonblock_eagain_buffered, tc) 405 { 406 407 _nonblock_eagain(_IOFBF); 408 } 409 410 ATF_TC_WITHOUT_HEAD(nonblock_eagain_unbuffered); 411 ATF_TC_BODY(nonblock_eagain_unbuffered, tc) 412 { 413 414 _nonblock_eagain(_IONBF); 415 } 416 417 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