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