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