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