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