From d6e0c2a67dbdb3f940d60f68bbcf58a5bdee4a97 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Sun, 27 Jul 2008 18:26:13 +0000 Subject: * sysdeps/unix/sysv/linux/syscalls.list: Add __pipe2 alias. * io/pipe2.c: Likewise. * sysdeps/unix/sysv/linux/kernel-features.h: Define __ASSUME_PIPE2 instead of __ASSUME_PACCEPT. * include/unistd.h: Declare __have_pipe2. * libio/iopopen.c: Implement "e" flag. * libio/Makefile (tests): Add tst-popen1. * libio/tst-popen1.c: New file. --- libio/Makefile | 4 +- libio/iopopen.c | 115 +++++++++++++++++++++++++++++++++++++++++++---------- libio/tst-popen1.c | 49 +++++++++++++++++++++++ 3 files changed, 144 insertions(+), 24 deletions(-) create mode 100644 libio/tst-popen1.c (limited to 'libio') diff --git a/libio/Makefile b/libio/Makefile index 31fac70cfd..385040fb96 100644 --- a/libio/Makefile +++ b/libio/Makefile @@ -1,4 +1,4 @@ -# Copyright (C) 1995-2002,2003,2004,2006,2007 Free Software Foundation, Inc. +# Copyright (C) 1995-2004,2006,2007,2008 Free Software Foundation, Inc. # This file is part of the GNU C Library. # The GNU C Library is free software; you can redistribute it and/or @@ -58,7 +58,7 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc \ tst-memstream1 tst-memstream2 \ tst-wmemstream1 tst-wmemstream2 \ bug-memstream1 bug-wmemstream1 \ - tst-setvbuf1 + tst-setvbuf1 tst-popen1 test-srcs = test-freopen all: # Make this the default target; it will be defined in Rules. diff --git a/libio/iopopen.c b/libio/iopopen.c index d5c6305b09..0ea7c2317b 100644 --- a/libio/iopopen.c +++ b/libio/iopopen.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1993, 1997-2002, 2003, 2004, 2007 +/* Copyright (C) 1993, 1997-2002, 2003, 2004, 2007, 2008 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Per Bothner . @@ -56,15 +56,6 @@ extern _IO_pid_t _IO_fork (void) __THROW; #endif /* _IO_HAVE_SYS_WAIT */ -#ifndef _IO_pipe -#ifdef _LIBC -#define _IO_pipe __pipe -#else -#define _IO_pipe pipe -#endif -extern int _IO_pipe (int des[2]) __THROW; -#endif - #ifndef _IO_dup2 #ifdef _LIBC #define _IO_dup2 __dup2 @@ -131,41 +122,99 @@ _IO_new_proc_open (fp, command, mode) volatile int parent_end, child_end; int pipe_fds[2]; _IO_pid_t child_pid; + + int do_read = 0; + int do_write = 0; + int do_cloexec = 0; + while (*mode != '\0') + switch (*mode++) + { + case 'r': + do_read = 1; + break; + case 'w': + do_write = 1; + break; + case 'e': + do_cloexec = 1; + break; + default: + errout: + __set_errno (EINVAL); + return NULL; + } + + if ((do_read ^ do_write) == 0) + goto errout; + if (_IO_file_is_open (fp)) return NULL; - if (_IO_pipe (pipe_fds) < 0) - return NULL; - if (mode[0] == 'r' && mode[1] == '\0') + +#ifdef O_CLOEXEC +# ifndef __ASSUME_PIPE2 + if (__have_pipe2 >= 0) +# endif + { + int r = __pipe2 (pipe_fds, O_CLOEXEC); +# ifndef __ASSUME_PIPE2 + if (__have_pipe2 == 0) + __have_pipe2 = r != -1 || errno != ENOSYS ? 1 : -1; + + if (__have_pipe2 > 0) +# endif + if (r < 0) + return NULL; + } +#endif +#ifndef __ASSUME_PIPE2 +# ifdef O_CLOEXEC + if (__have_pipe2 < 0) +# endif + if (__pipe (pipe_fds) < 0) + return NULL; +#endif + + if (do_read) { parent_end = pipe_fds[0]; child_end = pipe_fds[1]; read_or_write = _IO_NO_WRITES; } - else if (mode[0] == 'w' && mode[1] == '\0') + else { parent_end = pipe_fds[1]; child_end = pipe_fds[0]; read_or_write = _IO_NO_READS; } - else - { - _IO_close (pipe_fds[0]); - _IO_close (pipe_fds[1]); - __set_errno (EINVAL); - return NULL; - } + ((_IO_proc_file *) fp)->pid = child_pid = _IO_fork (); if (child_pid == 0) { - int child_std_end = mode[0] == 'r' ? 1 : 0; + int child_std_end = do_read ? 1 : 0; struct _IO_proc_file *p; +#ifndef __ASSUME_PIPE2 + /* If we have pipe2 the descriptor is marked for close-on-exec. */ _IO_close (parent_end); +#endif if (child_end != child_std_end) { _IO_dup2 (child_end, child_std_end); +#ifndef __ASSUME_PIPE2 _IO_close (child_end); +#endif + } +#ifdef O_CLOEXEC + else + { + /* The descriptor is already the one we will use. But it must + not be marked close-on-exec. Undo the effects. */ +# ifndef __ASSUME_PIPE2 + if (__have_pipe2 > 0) +# endif + __fcntl (child_end, F_SETFD, 0); } +#endif /* POSIX.2: "popen() shall ensure that any streams from previous popen() calls that remain open in the parent process are closed in the new child process." */ @@ -189,6 +238,28 @@ _IO_new_proc_open (fp, command, mode) _IO_close (parent_end); return NULL; } + + if (do_cloexec) + { +#ifndef __ASSUME_PIPE2 +# ifdef O_CLOEXEC + if (__have_pipe2 < 0) +# endif + __fcntl (parent_end, F_SETFD, FD_CLOEXEC); +#endif + } + else + { +#ifdef O_CLOEXEC + /* Undo the effects of the pipe2 call which set the + close-on-exec flag. */ +# ifndef __ASSUME_PIPE2 + if (__have_pipe2 > 0) +# endif + __fcntl (parent_end, F_SETFD, 0); +#endif + } + _IO_fileno (fp) = parent_end; /* Link into proc_file_chain. */ diff --git a/libio/tst-popen1.c b/libio/tst-popen1.c new file mode 100644 index 0000000000..bae6615b9b --- /dev/null +++ b/libio/tst-popen1.c @@ -0,0 +1,49 @@ +#include +#include + +static int +do_test (void) +{ + int res = 0; + + FILE *fp = popen ("echo hello", "r"); + if (fp == NULL) + { + puts ("first popen failed"); + res = 1; + } + else + { + int fd = fileno (fp); + if (fcntl (fd, F_GETFD) == FD_CLOEXEC) + { + puts ("first popen(\"r\") set FD_CLOEXEC"); + res = 1; + } + + fclose (fp); + } + + fp = popen ("echo hello", "re"); + if (fp == NULL) + { + puts ("second popen failed"); + res = 1; + } + else + { + int fd = fileno (fp); + if (fcntl (fd, F_GETFD) != FD_CLOEXEC) + { + puts ("second popen(\"r\") did not set FD_CLOEXEC"); + res = 1; + } + + fclose (fp); + } + + return res; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" -- cgit 1.4.1