diff --git a/NEWS b/NEWS index 3964984749..9a5a705d9d 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,11 @@ Release 3.0.2 There was a typo in a NetBSD-specific fcntl() call. It also turns out that NetBSD doesn't support some ISO C99 math functions like llroundl(); this has been worked around by using other functions. Issue #593. + * Fixed file descriptor closing issues on FreeBSD + Phusion Passenger child processes didn't correct close file descriptors + on FreeBSD because it queries /dev/fd to do that. On FreeBSD /dev/fd + only returns meaningful results if fdescfs is mounted, which it isn't + by default. Issue #597. Release 3.0.1 diff --git a/ext/common/Utils.cpp b/ext/common/Utils.cpp index 8c51135c8b..047df0380b 100644 --- a/ext/common/Utils.cpp +++ b/ext/common/Utils.cpp @@ -837,7 +837,26 @@ getHighestFileDescriptor() { sigaction(SIGFPE, &action, NULL); sigaction(SIGABRT, &action, NULL); - DIR *dir = opendir("/dev/fd"); + DIR *dir = NULL; + #ifdef __APPLE__ + /* /dev/fd can always be trusted on OS X. */ + dir = opendir("/dev/fd"); + #else + /* On FreeBSD and possibly other operating systems, /dev/fd only + * works if fdescfs is mounted. If it isn't mounted then /dev/fd + * still exists but always returns [0, 1, 2] and thus can't be + * trusted. If /dev and /dev/fd are on different filesystems + * then that probably means fdescfs is mounted. + */ + struct stat dirbuf1, dirbuf2; + if (stat("/dev", &dirbuf1) == -1 + || stat("/dev/fd", &dirbuf2) == -1) { + _exit(1); + } + if (dirbuf1.st_dev != dirbuf2.st_dev) { + dir = opendir("/dev/fd"); + } + #endif if (dir == NULL) { dir = opendir("/proc/self/fd"); if (dir == NULL) {