From 86b75745ad15b9785e0138de6183215a6814ecdf Mon Sep 17 00:00:00 2001 From: Jilles Tjoelker Date: Mon, 20 May 2013 13:05:51 +0000 Subject: [PATCH] Add a test program for popen(). --- tools/regression/lib/libc/gen/Makefile | 2 +- tools/regression/lib/libc/gen/test-popen.c | 227 +++++++++++++++++++++ 2 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 tools/regression/lib/libc/gen/test-popen.c diff --git a/tools/regression/lib/libc/gen/Makefile b/tools/regression/lib/libc/gen/Makefile index 4b29ad0a7de..4cba5de551d 100644 --- a/tools/regression/lib/libc/gen/Makefile +++ b/tools/regression/lib/libc/gen/Makefile @@ -1,7 +1,7 @@ # $FreeBSD$ TESTS= test-arc4random test-fmtcheck test-fmtmsg test-fnmatch \ - test-fpclassify test-ftw test-posix_spawn test-wordexp + test-fpclassify test-ftw test-popen test-posix_spawn test-wordexp .PHONY: tests tests: ${TESTS} diff --git a/tools/regression/lib/libc/gen/test-popen.c b/tools/regression/lib/libc/gen/test-popen.c new file mode 100644 index 00000000000..b1ae295aa12 --- /dev/null +++ b/tools/regression/lib/libc/gen/test-popen.c @@ -0,0 +1,227 @@ +/*- + * Copyright (c) 2013 Jilles Tjoelker + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Limited test program for popen() as specified by IEEE Std. 1003.1-2008, + * with BSD extensions. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +#include +#include +#include +#include +#include +#include +#include + +static int failures; +static volatile sig_atomic_t got_sigpipe; + +static void +sigpipe_handler(int sig __unused) +{ + got_sigpipe = 1; +} + +static void +check_cloexec(FILE *fp, const char *mode) +{ + int flags; + + flags = fcntl(fileno(fp), F_GETFD); + if (flags == -1) + fprintf(stderr, "fcntl(F_GETFD) failed\n"), failures++; + else if ((flags & FD_CLOEXEC) != + (strchr(mode, 'e') != NULL ? FD_CLOEXEC : 0)) + fprintf(stderr, "Bad cloexec flag\n"), failures++; +} + +int +main(int argc, char *argv[]) +{ + FILE *fp, *fp2; + int i, j, status; + const char *mode; + const char *allmodes[] = { "r", "w", "r+" }; + const char *rmodes[] = { "r", "r+" }; + const char *wmodes[] = { "w", "r+" }; + const char *rwmodes[] = { "r+" }; + char buf[80]; + struct sigaction act, oact; + + for (i = 0; i < sizeof(allmodes) / sizeof(allmodes[0]); i++) { + mode = allmodes[i]; + fp = popen("exit 7", mode); + if (fp == NULL) { + fprintf(stderr, "popen(, \"%s\") failed", mode); + failures++; + continue; + } + check_cloexec(fp, mode); + status = pclose(fp); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 7) + fprintf(stderr, "Bad exit status (no I/O)\n"), failures++; + } + + for (i = 0; i < sizeof(rmodes) / sizeof(rmodes[0]); i++) { + mode = rmodes[i]; + fp = popen("exit 9", mode); + if (fp == NULL) { + fprintf(stderr, "popen(, \"%s\") failed", mode); + failures++; + continue; + } + check_cloexec(fp, mode); + if (fgetc(fp) != EOF || !feof(fp) || ferror(fp)) + fprintf(stderr, "Input error 1\n"), failures++; + status = pclose(fp); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 9) + fprintf(stderr, "Bad exit status (input)\n"), failures++; + } + + for (i = 0; i < sizeof(rmodes) / sizeof(rmodes[0]); i++) { + mode = rmodes[i]; + fp = popen("echo hi there", mode); + if (fp == NULL) { + fprintf(stderr, "popen(, \"%s\") failed", mode); + failures++; + continue; + } + check_cloexec(fp, mode); + if (fgets(buf, sizeof(buf), fp) == NULL) + fprintf(stderr, "Input error 2\n"), failures++; + else if (strcmp(buf, "hi there\n") != 0) + fprintf(stderr, "Bad input 1\n"), failures++; + status = pclose(fp); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + fprintf(stderr, "Bad exit status (input)\n"), failures++; + } + + for (i = 0; i < sizeof(wmodes) / sizeof(wmodes[0]); i++) { + mode = wmodes[i]; + fp = popen("read x && [ \"$x\" = abcd ]", mode); + if (fp == NULL) { + fprintf(stderr, "popen(, \"%s\") failed", mode); + failures++; + continue; + } + check_cloexec(fp, mode); + if (fputs("abcd\n", fp) == EOF) + fprintf(stderr, "Output error 1\n"), failures++; + status = pclose(fp); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + fprintf(stderr, "Bad exit status (output)\n"), failures++; + } + + act.sa_handler = sigpipe_handler; + act.sa_flags = SA_RESTART; + sigemptyset(&act.sa_mask); + if (sigaction(SIGPIPE, &act, &oact) == -1) + fprintf(stderr, "sigaction() failed\n"), failures++; + for (i = 0; i < sizeof(wmodes) / sizeof(wmodes[0]); i++) { + mode = wmodes[i]; + fp = popen("exit 88", mode); + if (fp == NULL) { + fprintf(stderr, "popen(, \"%s\") failed", mode); + failures++; + continue; + } + check_cloexec(fp, mode); + got_sigpipe = 0; + while (fputs("abcd\n", fp) != EOF) + ; + if (!ferror(fp) || errno != EPIPE) + fprintf(stderr, "Expected EPIPE\n"), failures++; + if (!got_sigpipe) + fprintf(stderr, "Expected SIGPIPE\n"), failures++; + status = pclose(fp); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 88) + fprintf(stderr, "Bad exit status (EPIPE)\n"), failures++; + } + if (sigaction(SIGPIPE, &oact, NULL) == -1) + fprintf(stderr, "sigaction() failed\n"), failures++; + + for (i = 0; i < sizeof(rwmodes) / sizeof(rwmodes[0]); i++) { + mode = rwmodes[i]; + fp = popen("read x && printf '%s\\n' \"Q${x#a}\"", mode); + if (fp == NULL) { + fprintf(stderr, "popen(, \"%s\") failed", mode); + failures++; + continue; + } + check_cloexec(fp, mode); + if (fputs("abcd\n", fp) == EOF) + fprintf(stderr, "Output error 2\n"), failures++; + if (fgets(buf, sizeof(buf), fp) == NULL) + fprintf(stderr, "Input error 3\n"), failures++; + else if (strcmp(buf, "Qbcd\n") != 0) + fprintf(stderr, "Bad input 2\n"), failures++; + status = pclose(fp); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + fprintf(stderr, "Bad exit status (I/O)\n"), failures++; + } + + for (i = 0; i < sizeof(wmodes) / sizeof(wmodes[0]); i++) { + for (j = 0; j < sizeof(wmodes) / sizeof(wmodes[0]); j++) { + mode = wmodes[i]; + fp = popen("read x", mode); + if (fp == NULL) { + fprintf(stderr, "popen(, \"%s\") failed", mode); + failures++; + continue; + } + mode = wmodes[j]; + fp2 = popen("read x", mode); + if (fp2 == NULL) { + fprintf(stderr, "popen(, \"%s\") failed", mode); + failures++; + pclose(fp); + continue; + } + /* If fp2 inherits fp's pipe, we will deadlock here. */ + status = pclose(fp); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 1) { + fprintf(stderr, "Bad exit status (2 pipes)\n"); + failures++; + } + status = pclose(fp2); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 1) { + fprintf(stderr, "Bad exit status (2 pipes)\n"); + failures++; + } + } + } + + if (failures == 0) + printf("PASS popen()\n"); + + return (failures != 0); +} -- 2.45.0