1250825Sjilles/*-
2250825Sjilles * Copyright (c) 2013 Jilles Tjoelker
3250825Sjilles * All rights reserved.
4250825Sjilles *
5250825Sjilles * Redistribution and use in source and binary forms, with or without
6250825Sjilles * modification, are permitted provided that the following conditions
7250825Sjilles * are met:
8250825Sjilles * 1. Redistributions of source code must retain the above copyright
9250825Sjilles *    notice, this list of conditions and the following disclaimer.
10250825Sjilles * 2. Redistributions in binary form must reproduce the above copyright
11250825Sjilles *    notice, this list of conditions and the following disclaimer in the
12250825Sjilles *    documentation and/or other materials provided with the distribution.
13250825Sjilles *
14250825Sjilles * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15250825Sjilles * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16250825Sjilles * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17250825Sjilles * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18250825Sjilles * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19250825Sjilles * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20250825Sjilles * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21250825Sjilles * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22250825Sjilles * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23250825Sjilles * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24250825Sjilles * SUCH DAMAGE.
25250825Sjilles */
26250825Sjilles
27250825Sjilles/*
28250825Sjilles * Limited test program for popen() as specified by IEEE Std. 1003.1-2008,
29250825Sjilles * with BSD extensions.
30250825Sjilles */
31250825Sjilles
32250825Sjilles#include <sys/cdefs.h>
33250825Sjilles__FBSDID("$FreeBSD$");
34250825Sjilles
35250825Sjilles#include <sys/wait.h>
36250825Sjilles
37250825Sjilles#include <assert.h>
38250825Sjilles#include <errno.h>
39250825Sjilles#include <fcntl.h>
40250825Sjilles#include <signal.h>
41250825Sjilles#include <stdio.h>
42250825Sjilles#include <stdlib.h>
43250825Sjilles#include <string.h>
44250825Sjilles
45250825Sjillesstatic int failures;
46250825Sjillesstatic volatile sig_atomic_t got_sigpipe;
47250825Sjilles
48250825Sjillesstatic void
49250825Sjillessigpipe_handler(int sig __unused)
50250825Sjilles{
51250825Sjilles	got_sigpipe = 1;
52250825Sjilles}
53250825Sjilles
54250825Sjillesstatic void
55250825Sjillescheck_cloexec(FILE *fp, const char *mode)
56250825Sjilles{
57250825Sjilles	int flags;
58250825Sjilles
59250825Sjilles	flags = fcntl(fileno(fp), F_GETFD);
60250825Sjilles	if (flags == -1)
61250825Sjilles		fprintf(stderr, "fcntl(F_GETFD) failed\n"), failures++;
62250825Sjilles	else if ((flags & FD_CLOEXEC) !=
63250825Sjilles	    (strchr(mode, 'e') != NULL ? FD_CLOEXEC : 0))
64250825Sjilles		fprintf(stderr, "Bad cloexec flag\n"), failures++;
65250825Sjilles}
66250825Sjilles
67250825Sjillesint
68250825Sjillesmain(int argc, char *argv[])
69250825Sjilles{
70250825Sjilles	FILE *fp, *fp2;
71250825Sjilles	int i, j, status;
72250825Sjilles	const char *mode;
73250827Sjilles	const char *allmodes[] = { "r", "w", "r+", "re", "we", "r+e", "re+" };
74250827Sjilles	const char *rmodes[] = { "r", "r+", "re", "r+e", "re+" };
75250827Sjilles	const char *wmodes[] = { "w", "r+", "we", "r+e", "re+" };
76250827Sjilles	const char *rwmodes[] = { "r+", "r+e", "re+" };
77250825Sjilles	char buf[80];
78250825Sjilles	struct sigaction act, oact;
79250825Sjilles
80250825Sjilles	for (i = 0; i < sizeof(allmodes) / sizeof(allmodes[0]); i++) {
81250825Sjilles		mode = allmodes[i];
82250825Sjilles		fp = popen("exit 7", mode);
83250825Sjilles		if (fp == NULL) {
84250825Sjilles			fprintf(stderr, "popen(, \"%s\") failed", mode);
85250825Sjilles			failures++;
86250825Sjilles			continue;
87250825Sjilles		}
88250825Sjilles		check_cloexec(fp, mode);
89250825Sjilles		status = pclose(fp);
90250825Sjilles		if (!WIFEXITED(status) || WEXITSTATUS(status) != 7)
91250825Sjilles			fprintf(stderr, "Bad exit status (no I/O)\n"), failures++;
92250825Sjilles	}
93250825Sjilles
94250825Sjilles	for (i = 0; i < sizeof(rmodes) / sizeof(rmodes[0]); i++) {
95250825Sjilles		mode = rmodes[i];
96250825Sjilles		fp = popen("exit 9", mode);
97250825Sjilles		if (fp == NULL) {
98250825Sjilles			fprintf(stderr, "popen(, \"%s\") failed", mode);
99250825Sjilles			failures++;
100250825Sjilles			continue;
101250825Sjilles		}
102250825Sjilles		check_cloexec(fp, mode);
103250825Sjilles		if (fgetc(fp) != EOF || !feof(fp) || ferror(fp))
104250825Sjilles			fprintf(stderr, "Input error 1\n"), failures++;
105250825Sjilles		status = pclose(fp);
106250825Sjilles		if (!WIFEXITED(status) || WEXITSTATUS(status) != 9)
107250825Sjilles			fprintf(stderr, "Bad exit status (input)\n"), failures++;
108250825Sjilles	}
109250825Sjilles
110250825Sjilles	for (i = 0; i < sizeof(rmodes) / sizeof(rmodes[0]); i++) {
111250825Sjilles		mode = rmodes[i];
112250825Sjilles		fp = popen("echo hi there", mode);
113250825Sjilles		if (fp == NULL) {
114250825Sjilles			fprintf(stderr, "popen(, \"%s\") failed", mode);
115250825Sjilles			failures++;
116250825Sjilles			continue;
117250825Sjilles		}
118250825Sjilles		check_cloexec(fp, mode);
119250825Sjilles		if (fgets(buf, sizeof(buf), fp) == NULL)
120250825Sjilles			fprintf(stderr, "Input error 2\n"), failures++;
121250825Sjilles		else if (strcmp(buf, "hi there\n") != 0)
122250825Sjilles			fprintf(stderr, "Bad input 1\n"), failures++;
123250825Sjilles		status = pclose(fp);
124250825Sjilles		if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
125250825Sjilles			fprintf(stderr, "Bad exit status (input)\n"), failures++;
126250825Sjilles	}
127250825Sjilles
128250825Sjilles	for (i = 0; i < sizeof(wmodes) / sizeof(wmodes[0]); i++) {
129250825Sjilles		mode = wmodes[i];
130250825Sjilles		fp = popen("read x && [ \"$x\" = abcd ]", mode);
131250825Sjilles		if (fp == NULL) {
132250825Sjilles			fprintf(stderr, "popen(, \"%s\") failed", mode);
133250825Sjilles			failures++;
134250825Sjilles			continue;
135250825Sjilles		}
136250825Sjilles		check_cloexec(fp, mode);
137250825Sjilles		if (fputs("abcd\n", fp) == EOF)
138250825Sjilles			fprintf(stderr, "Output error 1\n"), failures++;
139250825Sjilles		status = pclose(fp);
140250825Sjilles		if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
141250825Sjilles			fprintf(stderr, "Bad exit status (output)\n"), failures++;
142250825Sjilles	}
143250825Sjilles
144250825Sjilles	act.sa_handler = sigpipe_handler;
145250825Sjilles	act.sa_flags = SA_RESTART;
146250825Sjilles	sigemptyset(&act.sa_mask);
147250825Sjilles	if (sigaction(SIGPIPE, &act, &oact) == -1)
148250825Sjilles		fprintf(stderr, "sigaction() failed\n"), failures++;
149250825Sjilles	for (i = 0; i < sizeof(wmodes) / sizeof(wmodes[0]); i++) {
150250825Sjilles		mode = wmodes[i];
151250825Sjilles		fp = popen("exit 88", mode);
152250825Sjilles		if (fp == NULL) {
153250825Sjilles			fprintf(stderr, "popen(, \"%s\") failed", mode);
154250825Sjilles			failures++;
155250825Sjilles			continue;
156250825Sjilles		}
157250825Sjilles		check_cloexec(fp, mode);
158250825Sjilles		got_sigpipe = 0;
159250825Sjilles		while (fputs("abcd\n", fp) != EOF)
160250825Sjilles			;
161250825Sjilles		if (!ferror(fp) || errno != EPIPE)
162250825Sjilles			fprintf(stderr, "Expected EPIPE\n"), failures++;
163250825Sjilles		if (!got_sigpipe)
164250825Sjilles			fprintf(stderr, "Expected SIGPIPE\n"), failures++;
165250825Sjilles		status = pclose(fp);
166250825Sjilles		if (!WIFEXITED(status) || WEXITSTATUS(status) != 88)
167250825Sjilles			fprintf(stderr, "Bad exit status (EPIPE)\n"), failures++;
168250825Sjilles	}
169250825Sjilles	if (sigaction(SIGPIPE, &oact, NULL) == -1)
170250825Sjilles		fprintf(stderr, "sigaction() failed\n"), failures++;
171250825Sjilles
172250825Sjilles	for (i = 0; i < sizeof(rwmodes) / sizeof(rwmodes[0]); i++) {
173250825Sjilles		mode = rwmodes[i];
174250825Sjilles		fp = popen("read x && printf '%s\\n' \"Q${x#a}\"", mode);
175250825Sjilles		if (fp == NULL) {
176250825Sjilles			fprintf(stderr, "popen(, \"%s\") failed", mode);
177250825Sjilles			failures++;
178250825Sjilles			continue;
179250825Sjilles		}
180250825Sjilles		check_cloexec(fp, mode);
181250825Sjilles		if (fputs("abcd\n", fp) == EOF)
182250825Sjilles			fprintf(stderr, "Output error 2\n"), failures++;
183250825Sjilles		if (fgets(buf, sizeof(buf), fp) == NULL)
184250825Sjilles			fprintf(stderr, "Input error 3\n"), failures++;
185250825Sjilles		else if (strcmp(buf, "Qbcd\n") != 0)
186250825Sjilles			fprintf(stderr, "Bad input 2\n"), failures++;
187250825Sjilles		status = pclose(fp);
188250825Sjilles		if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
189250825Sjilles			fprintf(stderr, "Bad exit status (I/O)\n"), failures++;
190250825Sjilles	}
191250825Sjilles
192250825Sjilles	for (i = 0; i < sizeof(wmodes) / sizeof(wmodes[0]); i++) {
193250825Sjilles		for (j = 0; j < sizeof(wmodes) / sizeof(wmodes[0]); j++) {
194250825Sjilles			mode = wmodes[i];
195250825Sjilles			fp = popen("read x", mode);
196250825Sjilles			if (fp == NULL) {
197250825Sjilles				fprintf(stderr, "popen(, \"%s\") failed", mode);
198250825Sjilles				failures++;
199250825Sjilles				continue;
200250825Sjilles			}
201250825Sjilles			mode = wmodes[j];
202250825Sjilles			fp2 = popen("read x", mode);
203250825Sjilles			if (fp2 == NULL) {
204250825Sjilles				fprintf(stderr, "popen(, \"%s\") failed", mode);
205250825Sjilles				failures++;
206250825Sjilles				pclose(fp);
207250825Sjilles				continue;
208250825Sjilles			}
209250825Sjilles			/* If fp2 inherits fp's pipe, we will deadlock here. */
210250825Sjilles			status = pclose(fp);
211250825Sjilles			if (!WIFEXITED(status) || WEXITSTATUS(status) != 1) {
212250825Sjilles				fprintf(stderr, "Bad exit status (2 pipes)\n");
213250825Sjilles				failures++;
214250825Sjilles			}
215250825Sjilles			status = pclose(fp2);
216250825Sjilles			if (!WIFEXITED(status) || WEXITSTATUS(status) != 1) {
217250825Sjilles				fprintf(stderr, "Bad exit status (2 pipes)\n");
218250825Sjilles				failures++;
219250825Sjilles			}
220250825Sjilles		}
221250825Sjilles	}
222250825Sjilles
223250825Sjilles	if (failures == 0)
224250825Sjilles		printf("PASS popen()\n");
225250825Sjilles
226250825Sjilles	return (failures != 0);
227250825Sjilles}
228