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