mirror of
https://https.git.savannah.gnu.org/git/gnulib.git
synced 2026-04-28 06:33:36 +00:00
104 lines
3.4 KiB
C
104 lines
3.4 KiB
C
/* Open a stream to a sub-process.
|
|
Copyright (C) 2009-2026 Free Software Foundation, Inc.
|
|
|
|
This file is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as
|
|
published by the Free Software Foundation, either version 3 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This file is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|
|
|
/* Written by Eric Blake <ebb9@byu.net>, 2009. */
|
|
|
|
#include <config.h>
|
|
|
|
/* Specification. */
|
|
#include <stdio.h>
|
|
|
|
#if defined _WIN32 && ! defined __CYGWIN__
|
|
/* Native Windows API. */
|
|
|
|
# include <string.h>
|
|
|
|
FILE *
|
|
popen (const char *filename, const char *mode)
|
|
{
|
|
/* Use binary mode by default. */
|
|
if (streq (mode, "r"))
|
|
mode = "rb";
|
|
else if (streq (mode, "w"))
|
|
mode = "wb";
|
|
|
|
return _popen (filename, mode);
|
|
}
|
|
|
|
#else
|
|
|
|
# include <errno.h>
|
|
# include <fcntl.h>
|
|
# include <stdlib.h>
|
|
# include <unistd.h>
|
|
|
|
# undef popen
|
|
|
|
FILE *
|
|
rpl_popen (const char *filename, const char *mode)
|
|
{
|
|
/* All other platforms have popen and fcntl.
|
|
The bug of the child clobbering its own file descriptors if stdin
|
|
or stdout was closed in the parent can be worked around by
|
|
opening those two fds as close-on-exec to begin with. */
|
|
/* Cygwin 1.5.x also has a bug where the popen fd is improperly
|
|
marked close-on-exec, and if the application undoes this, then
|
|
the fd leaks into subsequent popen calls. We could work around
|
|
this by maintaining a list of all fd's opened by popen, and
|
|
temporarily marking them cloexec around the real popen call, but
|
|
we would also have to override pclose, and the bookkeeping seems
|
|
extreme given that cygwin 1.7 no longer has the bug. */
|
|
int cloexec0 = fcntl (STDIN_FILENO, F_GETFD);
|
|
int cloexec1 = fcntl (STDOUT_FILENO, F_GETFD);
|
|
|
|
/* If either stdin or stdout was closed (that is, fcntl failed),
|
|
then we open a dummy close-on-exec fd to occupy that slot. That
|
|
way, popen's internal use of pipe() will not contain either fd 0
|
|
or 1, overcoming the fact that the child process blindly calls
|
|
close() on the parent's end of the pipe without first checking
|
|
whether it is clobbering the fd just placed there via dup2(); the
|
|
exec will get rid of the dummy fd's in the child. Fortunately,
|
|
closed stderr in the parent does not cause problems in the
|
|
child. */
|
|
if (cloexec0 < 0)
|
|
{
|
|
if (open ("/dev/null", O_RDONLY) != STDIN_FILENO
|
|
|| fcntl (STDIN_FILENO, F_SETFD,
|
|
fcntl (STDIN_FILENO, F_GETFD) | FD_CLOEXEC) == -1)
|
|
abort ();
|
|
}
|
|
if (cloexec1 < 0)
|
|
{
|
|
if (open ("/dev/null", O_RDONLY) != STDOUT_FILENO
|
|
|| fcntl (STDOUT_FILENO, F_SETFD,
|
|
fcntl (STDOUT_FILENO, F_GETFD) | FD_CLOEXEC) == -1)
|
|
abort ();
|
|
}
|
|
FILE *result = popen (filename, mode);
|
|
/* Now, close any dummy fd's created in the parent. */
|
|
{
|
|
int saved_errno = errno;
|
|
if (cloexec0 < 0)
|
|
close (STDIN_FILENO);
|
|
if (cloexec1 < 0)
|
|
close (STDOUT_FILENO);
|
|
errno = saved_errno;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#endif
|