xref: /freebsd/lib/libc/tests/gen/popen_test.c (revision 559a218c9b257775fb249b67945fe4a05b7a6b9f)
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
sigpipe_handler(int sig __unused)46  sigpipe_handler(int sig __unused)
47  {
48  	got_sigpipe = 1;
49  }
50  
51  static void
check_cloexec(FILE * fp,const char * mode)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);
ATF_TC_BODY(popen_all_modes_test,tc)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);
ATF_TC_BODY(popen_rmodes_test,tc)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);
ATF_TC_BODY(popen_wmodes_test,tc)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);
ATF_TC_BODY(popen_rwmodes_test,tc)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  
ATF_TP_ADD_TCS(tp)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