Red Hat Bugzilla – Bug 37695
rpm leaves duplicate of STDOUT_FILENO in script processes
Last modified: 2007-04-18 12:32:51 EDT
From Bugzilla Helper:
User-Agent: Mozilla/4.76 [en] (X11; U; Linux 2.2.14-12 i686)
In runScript() in lib/psm.c (in the current CVS repository; this would be
in lib/uninstall.c in rpm 4.0.2), a duplicate of STDOUT_FILENO is created
if ts->scriptFd is not set, which is the normal case. (Oddly, there is
logic for conditionalizing on rpmIsVerbose() when ts->scriptFd is set, but
no such logic otherwise.) The child process does not close "out" when
scriptFd is unset, so the script processes run with a duplicate of
STDOUT_FILENO in their space.
This odd behavior can lead to problems if the script starts a daemon
process, even a well-behaved daemon which closes fds 0, 1, and 2. (Some
daemons close all fds, but I don't think there is any consensus that a
daemon is required to do this.) In that case, the daemon process will
still have a reference to rpm's output fd in its file table, so if rpm
output was passed through a pipe, the right side of the pipe will never
We don't see this problem in Red Hat RPMs on a modern system because
/sbin/initlog (used by "daemon" in initscripts) now closes all file
descriptors >= 3 before running the daemon process. This was not always
so, and I have encountered hanging pipeline problems upgrading from Red Hat
6.2 or Red Hat 7.0 to Red Hat 7.1 because RPMs like "at" and "gpm", if they
happen to be upgraded by the transaction before initscripts is upgraded,
will restart their daemons with the daemon process holding the pipe open.
The fix is pretty easy. Either stop creating "out" when ts->scriptFd isn't
set (and conditionalize the parent's close of "out" on ts->scriptFd being
set), or move the child's close of "out" outside of the ts->scriptFd
Steps to Reproduce:
As noted in the description, producing an actual problem is kind of
difficult due to all the players involved like /sbin/initlog. Hopefully my
exposition of the bug is sufficient to show its presence.
The problem is actually worse than stdout, as the same affected daemons will
inherit open rpmdb file descriptors as well.
So far, I've been saying
Fix the daemon package.
as daemons truly must lose all open file descriptors, that's SOP in BSD
for A Long Time Now. Closing and reopening the rpmdb descriptors, unlike stdout,
is trickier ...
Can't you just set the rpmdb file descriptors FD_CLOEXEC? Unlike stdout, they
shouldn't be needed by any subprocess of rpm.
Anyway, the overall problem may be larger than stdout, but the stdout problem
can have a particularly nasty failure mode, and is easy to fix.
Finally, the BSD daemon() function does not close all file descriptors; it
merely redirects 0/1/2 to /dev/null. I checked NetBSD, FreeBSD, and OpenBSD;
here's a URL to the current NetBSD version for reference.
Close on exec would work, might have some portability problems.
You're correct that stdout is a different, and probably solvable, problem
I'll take a look.
Thanks for the correction wrto daemonize(), although I'm quite sure
that I've seen all file descriptors being routinely closed in BSD code,
even though I've mistakenly identified the routine.
Let me try a different tack. Any daemon that inherits file descriptors
from rpm will inherit file descriptors from whatever program starts the
daemon. That indicates that the daemon *must* be fixed, independent
of what rpm (or any other program that starts daemons) does. That's
the correct way to prevent surprising fd inheiritance.
OK, the single open(2) call deep in rpmio now is dressed with
FD_CLOEXEC. Will be in rpm-4.0.4-0.30 (and rpm-4.0.4 final).
I believe most everything else now sets FD_CLOEXEC, will check.
Nothing to be done for stdout/stderr however, they'll inherit from
the invoking shell as always.