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