From 8a6854d222ad62cfcbd9cbafb949751c2b3ed098 Mon Sep 17 00:00:00 2001 From: James Youngman Date: Sun, 4 Apr 2010 01:07:51 +0100 Subject: [PATCH] Fix open_cloexec on hosts which ignore O_CLOEXEC (i.e. old kernels). To: address@hidden * lib/fdleak.c (o_cloexec_works): New function, detects whether the open flag O_CLOEXEC has any effect. (open_cloexec): Call o_cloexec_works, just once, to find out whether O_CLOEXEC is effective. If not, set the close-on-exec flag on fds by calling set_cloexec_flag. Signed-off-by: James Youngman --- ChangeLog | 9 +++++++++ lib/fdleak.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletions(-) diff --git a/ChangeLog b/ChangeLog index b419405..cee57a8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2010-04-04 James Youngman + + Fix open_cloexec on hosts which ignore O_CLOEXEC (i.e. old kernels). + * lib/fdleak.c (o_cloexec_works): New function, detects whether + the open flag O_CLOEXEC has any effect. + (open_cloexec): Call o_cloexec_works, just once, to find out + whether O_CLOEXEC is effective. If not, set the close-on-exec + flag on fds by calling set_cloexec_flag. + 2010-04-03 James Youngman Move on from 4.5.7. diff --git a/lib/fdleak.c b/lib/fdleak.c index 2c362cf..02614c2 100644 --- a/lib/fdleak.c +++ b/lib/fdleak.c @@ -303,11 +303,27 @@ find_first_leaked_fd (const int* prev_non_cloexec_fds, size_t n) return context.leaked_fd; } +static bool +o_cloexec_works (void) +{ + bool result = false; + int fd = open ("/", O_RDONLY|O_CLOEXEC); + if (fd >= 0) + { + result = fd_is_cloexec (fd); + close (fd); + } + return result; +} + + int open_cloexec (const char *path, int flags, ...) { int fd; mode_t mode = 0; + static bool cloexec_works = false; + static bool cloexec_status_known = false; if (flags & O_CREAT) { @@ -322,8 +338,19 @@ open_cloexec (const char *path, int flags, ...) va_end (ap); } + /* Kernels usually ignore open flags they don't recognise, so it + * is possible this program was built against a library which + * defines O_CLOEXEC, but is running on a kernel that (silently) + * does not recognise it. We figure this out by just trying it, + * once. + */ + if (!cloexec_status_known) + { + cloexec_works = o_cloexec_works (); + cloexec_status_known = true; + } fd = open (path, flags|O_CLOEXEC, mode); - if ((fd >= 0) && !O_CLOEXEC) + if ((fd >= 0) && !(O_CLOEXEC && cloexec_works)) { set_cloexec_flag (fd, true); } -- 1.7.0