1 /*- 2 * Copyright (c) 2013 Jilles Tjoelker 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 /* 28 * Limited test program for popen() as specified by IEEE Std. 1003.1-2008, 29 * with BSD extensions. 30 */ 31 32 #include <sys/param.h> 33 #include <sys/wait.h> 34 #include <errno.h> 35 #include <fcntl.h> 36 #include <signal.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 41 #include <atf-c.h> 42 43 static volatile sig_atomic_t got_sigpipe; 44 45 static void 46 sigpipe_handler(int sig __unused) 47 { 48 got_sigpipe = 1; 49 } 50 51 static void 52 check_cloexec(FILE *fp, const char *mode) 53 { 54 int exp_flags, flags; 55 56 flags = fcntl(fileno(fp), F_GETFD); 57 ATF_CHECK_MSG(flags != -1, "fcntl(F_GETFD) failed; errno=%d", errno); 58 if (flags == -1) 59 return; 60 if (strchr(mode, 'e') != NULL) 61 exp_flags = FD_CLOEXEC; 62 else 63 exp_flags = 0; 64 ATF_CHECK_MSG((flags & FD_CLOEXEC) == exp_flags, 65 "bad cloexec flag; %d != %d", flags, exp_flags); 66 } 67 68 ATF_TC_WITHOUT_HEAD(popen_all_modes_test); 69 ATF_TC_BODY(popen_all_modes_test, tc) 70 { 71 FILE *fp; 72 int i, status; 73 const char *mode; 74 const char *allmodes[] = { "r", "w", "r+", "re", "we", "r+e", "re+" }; 75 76 for (i = 0; i < nitems(allmodes); i++) { 77 mode = allmodes[i]; 78 fp = popen("exit 7", mode); 79 ATF_CHECK_MSG(fp != NULL, "popen(, \"%s\") failed", mode); 80 if (fp == NULL) 81 continue; 82 check_cloexec(fp, mode); 83 status = pclose(fp); 84 ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 7, 85 "bad exit status (no I/O)"); 86 } 87 } 88 89 ATF_TC_WITHOUT_HEAD(popen_rmodes_test); 90 ATF_TC_BODY(popen_rmodes_test, tc) 91 { 92 FILE *fp; 93 const char *rmodes[] = { "r", "r+", "re", "r+e", "re+" }; 94 const char *mode; 95 char buf[80]; 96 int i, status; 97 98 for (i = 0; i < nitems(rmodes); i++) { 99 mode = rmodes[i]; 100 fp = popen("exit 9", mode); 101 ATF_CHECK_MSG(fp != NULL, "popen(, \"%s\") failed", mode); 102 if (fp == NULL) 103 continue; 104 check_cloexec(fp, mode); 105 bool input_error_1 = !(fgetc(fp) != EOF || !feof(fp) || !ferror(fp)); 106 ATF_CHECK_MSG(!input_error_1, "input error 1"); 107 if (input_error_1) 108 continue; 109 status = pclose(fp); 110 ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 9, 111 "bad exit status (input)"); 112 } 113 114 for (i = 0; i < nitems(rmodes); i++) { 115 char *sres; 116 mode = rmodes[i]; 117 fp = popen("echo hi there", mode); 118 ATF_CHECK_MSG(fp != NULL, "popen(, \"%s\") failed", mode); 119 if (fp == NULL) 120 continue; 121 check_cloexec(fp, mode); 122 ATF_CHECK_MSG((sres = fgets(buf, sizeof(buf), fp)) != NULL, 123 "Input error 2"); 124 if (sres != NULL) 125 ATF_CHECK_MSG(strcmp(buf, "hi there\n") == 0, 126 "Bad input 1"); 127 status = pclose(fp); 128 ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0, 129 "Bad exit status (input)"); 130 } 131 } 132 133 ATF_TC_WITHOUT_HEAD(popen_wmodes_test); 134 ATF_TC_BODY(popen_wmodes_test, tc) 135 { 136 FILE *fp, *fp2; 137 const char *wmodes[] = { "w", "r+", "we", "r+e", "re+" }; 138 const char *mode; 139 struct sigaction act, oact; 140 int i, j, status; 141 142 for (i = 0; i < nitems(wmodes); i++) { 143 mode = wmodes[i]; 144 fp = popen("read x && [ \"$x\" = abcd ]", mode); 145 ATF_CHECK_MSG(fp != NULL, "popen(, \"%s\") failed", mode); 146 if (fp == NULL) 147 continue; 148 check_cloexec(fp, mode); 149 ATF_CHECK_MSG(fputs("abcd\n", fp) != EOF, 150 "Output error 1"); 151 status = pclose(fp); 152 ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0, 153 "Bad exit status (output)"); 154 } 155 156 act.sa_handler = sigpipe_handler; 157 act.sa_flags = SA_RESTART; 158 sigemptyset(&act.sa_mask); 159 ATF_CHECK_MSG(sigaction(SIGPIPE, &act, &oact) != -1, 160 "sigaction() failed"); 161 for (i = 0; i < nitems(wmodes); i++) { 162 mode = wmodes[i]; 163 fp = popen("exit 88", mode); 164 ATF_CHECK_MSG(fp != NULL, "popen(, \"%s\") failed", mode); 165 if (fp == NULL) 166 continue; 167 check_cloexec(fp, mode); 168 got_sigpipe = 0; 169 while (fputs("abcd\n", fp) != EOF) 170 ; 171 ATF_CHECK_MSG(ferror(fp) && errno == EPIPE, "Expected EPIPE"); 172 ATF_CHECK_MSG(got_sigpipe, "Expected SIGPIPE"); 173 status = pclose(fp); 174 ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 88, 175 "Bad exit status (EPIPE)"); 176 } 177 ATF_CHECK_MSG(sigaction(SIGPIPE, &oact, NULL) != -1, 178 "sigaction() failed"); 179 180 for (i = 0; i < nitems(wmodes); i++) { 181 for (j = 0; j < nitems(wmodes); j++) { 182 mode = wmodes[i]; 183 fp = popen("read x", mode); 184 ATF_CHECK_MSG(fp != NULL, 185 "popen(, \"%s\") failed", mode); 186 if (fp == NULL) 187 continue; 188 mode = wmodes[j]; 189 fp2 = popen("read x", mode); 190 ATF_CHECK_MSG(fp2 != NULL, 191 "popen(, \"%s\") failed", mode); 192 if (fp2 == NULL) { 193 pclose(fp); 194 continue; 195 } 196 /* If fp2 inherits fp's pipe, we will deadlock here. */ 197 status = pclose(fp); 198 ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 1, 199 "bad exit status (2 pipes)"); 200 status = pclose(fp2); 201 ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 1, 202 "bad exit status (2 pipes)"); 203 } 204 } 205 } 206 207 ATF_TC_WITHOUT_HEAD(popen_rwmodes_test); 208 ATF_TC_BODY(popen_rwmodes_test, tc) 209 { 210 const char *rwmodes[] = { "r+", "r+e", "re+" }; 211 FILE *fp; 212 const char *mode; 213 char *sres; 214 char buf[80]; 215 int i, ires, status; 216 217 for (i = 0; i < nitems(rwmodes); i++) { 218 mode = rwmodes[i]; 219 fp = popen("read x && printf '%s\\n' \"Q${x#a}\"", mode); 220 ATF_CHECK_MSG(fp != NULL, "popen(, \"%s\") failed", mode); 221 if (fp == NULL) 222 continue; 223 check_cloexec(fp, mode); 224 ATF_CHECK_MSG((ires = fputs("abcd\n", fp)) != EOF, 225 "Output error 2"); 226 if (ires != EOF) { 227 sres = fgets(buf, sizeof(buf), fp); 228 ATF_CHECK_MSG(sres != NULL, "Input error 3"); 229 if (sres != NULL) 230 ATF_CHECK_MSG(strcmp(buf, "Qbcd\n") == 0, 231 "Bad input 2"); 232 } 233 status = pclose(fp); 234 ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0, 235 "bad exit status (I/O)"); 236 } 237 } 238 239 ATF_TP_ADD_TCS(tp) 240 { 241 242 ATF_TP_ADD_TC(tp, popen_all_modes_test); 243 ATF_TP_ADD_TC(tp, popen_rmodes_test); 244 ATF_TP_ADD_TC(tp, popen_wmodes_test); 245 ATF_TP_ADD_TC(tp, popen_rwmodes_test); 246 247 return (atf_no_error()); 248 } 249