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/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 35 #include <sys/param.h> 36 #include <sys/wait.h> 37 #include <errno.h> 38 #include <fcntl.h> 39 #include <signal.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 44 #include <atf-c.h> 45 46 static volatile sig_atomic_t got_sigpipe; 47 48 static void 49 sigpipe_handler(int sig __unused) 50 { 51 got_sigpipe = 1; 52 } 53 54 static void 55 check_cloexec(FILE *fp, const char *mode) 56 { 57 int exp_flags, flags; 58 59 flags = fcntl(fileno(fp), F_GETFD); 60 ATF_CHECK_MSG(flags != -1, "fcntl(F_GETFD) failed; errno=%d", errno); 61 if (flags == -1) 62 return; 63 if (strchr(mode, 'e') != NULL) 64 exp_flags = FD_CLOEXEC; 65 else 66 exp_flags = 0; 67 ATF_CHECK_MSG((flags & FD_CLOEXEC) == exp_flags, 68 "bad cloexec flag; %d != %d", flags, exp_flags); 69 } 70 71 ATF_TC_WITHOUT_HEAD(popen_all_modes_test); 72 ATF_TC_BODY(popen_all_modes_test, tc) 73 { 74 FILE *fp; 75 int i, status; 76 const char *mode; 77 const char *allmodes[] = { "r", "w", "r+", "re", "we", "r+e", "re+" }; 78 79 for (i = 0; i < nitems(allmodes); i++) { 80 mode = allmodes[i]; 81 fp = popen("exit 7", mode); 82 ATF_CHECK_MSG(fp != NULL, "popen(, \"%s\") failed", mode); 83 if (fp == NULL) 84 continue; 85 check_cloexec(fp, mode); 86 status = pclose(fp); 87 ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 7, 88 "bad exit status (no I/O)"); 89 } 90 } 91 92 ATF_TC_WITHOUT_HEAD(popen_rmodes_test); 93 ATF_TC_BODY(popen_rmodes_test, tc) 94 { 95 FILE *fp; 96 const char *rmodes[] = { "r", "r+", "re", "r+e", "re+" }; 97 const char *mode; 98 char buf[80]; 99 int i, status; 100 101 for (i = 0; i < nitems(rmodes); i++) { 102 mode = rmodes[i]; 103 fp = popen("exit 9", mode); 104 ATF_CHECK_MSG(fp != NULL, "popen(, \"%s\") failed", mode); 105 if (fp == NULL) 106 continue; 107 check_cloexec(fp, mode); 108 bool input_error_1 = !(fgetc(fp) != EOF || !feof(fp) || !ferror(fp)); 109 ATF_CHECK_MSG(!input_error_1, "input error 1"); 110 if (input_error_1) 111 continue; 112 status = pclose(fp); 113 ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 9, 114 "bad exit status (input)"); 115 } 116 117 for (i = 0; i < nitems(rmodes); i++) { 118 char *sres; 119 mode = rmodes[i]; 120 fp = popen("echo hi there", mode); 121 ATF_CHECK_MSG(fp != NULL, "popen(, \"%s\") failed", mode); 122 if (fp == NULL) 123 continue; 124 check_cloexec(fp, mode); 125 ATF_CHECK_MSG((sres = fgets(buf, sizeof(buf), fp)) != NULL, 126 "Input error 2"); 127 if (sres != NULL) 128 ATF_CHECK_MSG(strcmp(buf, "hi there\n") == 0, 129 "Bad input 1"); 130 status = pclose(fp); 131 ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0, 132 "Bad exit status (input)"); 133 } 134 } 135 136 ATF_TC_WITHOUT_HEAD(popen_wmodes_test); 137 ATF_TC_BODY(popen_wmodes_test, tc) 138 { 139 FILE *fp, *fp2; 140 const char *wmodes[] = { "w", "r+", "we", "r+e", "re+" }; 141 const char *mode; 142 struct sigaction act, oact; 143 int i, j, status; 144 145 for (i = 0; i < nitems(wmodes); i++) { 146 mode = wmodes[i]; 147 fp = popen("read x && [ \"$x\" = abcd ]", mode); 148 ATF_CHECK_MSG(fp != NULL, "popen(, \"%s\") failed", mode); 149 if (fp == NULL) 150 continue; 151 check_cloexec(fp, mode); 152 ATF_CHECK_MSG(fputs("abcd\n", fp) != EOF, 153 "Output error 1"); 154 status = pclose(fp); 155 ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0, 156 "Bad exit status (output)"); 157 } 158 159 act.sa_handler = sigpipe_handler; 160 act.sa_flags = SA_RESTART; 161 sigemptyset(&act.sa_mask); 162 ATF_CHECK_MSG(sigaction(SIGPIPE, &act, &oact) != -1, 163 "sigaction() failed"); 164 for (i = 0; i < nitems(wmodes); i++) { 165 mode = wmodes[i]; 166 fp = popen("exit 88", mode); 167 ATF_CHECK_MSG(fp != NULL, "popen(, \"%s\") failed", mode); 168 if (fp == NULL) 169 continue; 170 check_cloexec(fp, mode); 171 got_sigpipe = 0; 172 while (fputs("abcd\n", fp) != EOF) 173 ; 174 ATF_CHECK_MSG(ferror(fp) && errno == EPIPE, "Expected EPIPE"); 175 ATF_CHECK_MSG(got_sigpipe, "Expected SIGPIPE"); 176 status = pclose(fp); 177 ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 88, 178 "Bad exit status (EPIPE)"); 179 } 180 ATF_CHECK_MSG(sigaction(SIGPIPE, &oact, NULL) != -1, 181 "sigaction() failed"); 182 183 for (i = 0; i < nitems(wmodes); i++) { 184 for (j = 0; j < nitems(wmodes); j++) { 185 mode = wmodes[i]; 186 fp = popen("read x", mode); 187 ATF_CHECK_MSG(fp != NULL, 188 "popen(, \"%s\") failed", mode); 189 if (fp == NULL) 190 continue; 191 mode = wmodes[j]; 192 fp2 = popen("read x", mode); 193 ATF_CHECK_MSG(fp2 != NULL, 194 "popen(, \"%s\") failed", mode); 195 if (fp2 == NULL) { 196 pclose(fp); 197 continue; 198 } 199 /* If fp2 inherits fp's pipe, we will deadlock here. */ 200 status = pclose(fp); 201 ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 1, 202 "bad exit status (2 pipes)"); 203 status = pclose(fp2); 204 ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 1, 205 "bad exit status (2 pipes)"); 206 } 207 } 208 } 209 210 ATF_TC_WITHOUT_HEAD(popen_rwmodes_test); 211 ATF_TC_BODY(popen_rwmodes_test, tc) 212 { 213 const char *rwmodes[] = { "r+", "r+e", "re+" }; 214 FILE *fp; 215 const char *mode; 216 char *sres; 217 char buf[80]; 218 int i, ires, status; 219 220 for (i = 0; i < nitems(rwmodes); i++) { 221 mode = rwmodes[i]; 222 fp = popen("read x && printf '%s\\n' \"Q${x#a}\"", mode); 223 ATF_CHECK_MSG(fp != NULL, "popen(, \"%s\") failed", mode); 224 if (fp == NULL) 225 continue; 226 check_cloexec(fp, mode); 227 ATF_CHECK_MSG((ires = fputs("abcd\n", fp)) != EOF, 228 "Output error 2"); 229 if (ires != EOF) { 230 sres = fgets(buf, sizeof(buf), fp); 231 ATF_CHECK_MSG(sres != NULL, "Input error 3"); 232 if (sres != NULL) 233 ATF_CHECK_MSG(strcmp(buf, "Qbcd\n") == 0, 234 "Bad input 2"); 235 } 236 status = pclose(fp); 237 ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0, 238 "bad exit status (I/O)"); 239 } 240 } 241 242 ATF_TP_ADD_TCS(tp) 243 { 244 245 ATF_TP_ADD_TC(tp, popen_all_modes_test); 246 ATF_TP_ADD_TC(tp, popen_rmodes_test); 247 ATF_TP_ADD_TC(tp, popen_wmodes_test); 248 ATF_TP_ADD_TC(tp, popen_rwmodes_test); 249 250 return (atf_no_error()); 251 } 252