os: wrap F_DUPFD_CLOEXEC
authorPekka Paalanen <ppaalanen@gmail.com>
Mon, 23 Apr 2012 10:55:55 +0000 (13:55 +0300)
committerPekka Paalanen <ppaalanen@gmail.com>
Wed, 25 Apr 2012 06:32:58 +0000 (09:32 +0300)
Some system C libraries do not have F_DUPFD_CLOEXEC. Provide a fallback.

Add tests for the new wl_os_dupfd_cloexec() wrapper.

Add per-wrapper call counters in os_wrappers-test.c. Makes it easier to
determine the minimum required number of wrapped calls.

Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
src/connection.c
src/wayland-os.c
src/wayland-os.h
tests/os-wrappers-test.c

index a599f91..c49ca3d 100644 (file)
@@ -37,6 +37,7 @@
 
 #include "wayland-util.h"
 #include "wayland-private.h"
+#include "wayland-os.h"
 
 #define DIV_ROUNDUP(n, a) ( ((n) + ((a) - 1)) / (a) )
 
@@ -518,7 +519,7 @@ wl_closure_vmarshal(struct wl_closure *closure,
                        extra += sizeof *fd_ptr;
 
                        fd = va_arg(ap, int);
-                       dup_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
+                       dup_fd = wl_os_dupfd_cloexec(fd, 0);
                        if (dup_fd < 0) {
                                fprintf(stderr, "dup failed: %m");
                                abort();
index 4db8050..4a19da6 100644 (file)
@@ -64,3 +64,18 @@ wl_os_socket_cloexec(int domain, int type, int protocol)
        fd = socket(domain, type, protocol);
        return set_cloexec_or_close(fd);
 }
+
+int
+wl_os_dupfd_cloexec(int fd, long minfd)
+{
+       int newfd;
+
+       newfd = fcntl(fd, F_DUPFD_CLOEXEC, minfd);
+       if (newfd >= 0)
+               return newfd;
+       if (errno != EINVAL)
+               return -1;
+
+       newfd = fcntl(fd, F_DUPFD, minfd);
+       return set_cloexec_or_close(newfd);
+}
index a57b3aa..456d8b0 100644 (file)
@@ -26,6 +26,9 @@
 int
 wl_os_socket_cloexec(int domain, int type, int protocol);
 
+int
+wl_os_dupfd_cloexec(int fd, long minfd);
+
 /*
  * The following are for wayland-os.c and the unit tests.
  * Do not use them elsewhere.
@@ -37,6 +40,10 @@ wl_os_socket_cloexec(int domain, int type, int protocol);
 #define SOCK_CLOEXEC 02000000
 #endif
 
+#ifndef F_DUPFD_CLOEXEC
+#define F_DUPFD_CLOEXEC 1030
+#endif
+
 #endif /* __linux__ */
 
 #endif
index c6bf26e..2272b73 100644 (file)
 #include <unistd.h>
 #include <dlfcn.h>
 #include <errno.h>
+#include <stdarg.h>
+#include <fcntl.h>
 
 #include "test-runner.h"
 #include "../src/wayland-os.h"
 
 static int fall_back;
-static int wrapped_calls;
 
 static int (*real_socket)(int, int, int);
+static int wrapped_calls_socket;
+
+static int (*real_fcntl)(int, int, ...);
+static int wrapped_calls_fcntl;
 
 static void
 init_fallbacks(int do_fallbacks)
 {
        fall_back = do_fallbacks;
        real_socket = dlsym(RTLD_NEXT, "socket");
+       real_fcntl = dlsym(RTLD_NEXT, "fcntl");
 }
 
 __attribute__ ((visibility("default"))) int
 socket(int domain, int type, int protocol)
 {
-       wrapped_calls++;
+       wrapped_calls_socket++;
 
        if (fall_back && (type & SOCK_CLOEXEC)) {
                errno = EINVAL;
@@ -58,6 +64,26 @@ socket(int domain, int type, int protocol)
        return real_socket(domain, type, protocol);
 }
 
+__attribute__ ((visibility("default"))) int
+fcntl(int fd, int cmd, ...)
+{
+       va_list ap;
+       void *arg;
+
+       wrapped_calls_fcntl++;
+
+       if (fall_back && (cmd == F_DUPFD_CLOEXEC)) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       va_start(ap, cmd);
+       arg = va_arg(ap, void*);
+       va_end(ap);
+
+       return real_fcntl(fd, cmd, arg);
+}
+
 static void
 do_os_wrappers_socket_cloexec(int n)
 {
@@ -74,7 +100,7 @@ do_os_wrappers_socket_cloexec(int n)
         * Must have 2 calls if falling back, but must also allow
         * falling back without a forced fallback.
         */
-       assert(wrapped_calls > n);
+       assert(wrapped_calls_socket > n);
 
        exec_fd_leak_check(nr_fds);
 }
@@ -92,3 +118,41 @@ TEST(os_wrappers_socket_cloexec_fallback)
        init_fallbacks(1);
        do_os_wrappers_socket_cloexec(1);
 }
+
+static void
+do_os_wrappers_dupfd_cloexec(int n)
+{
+       int base_fd;
+       int fd;
+       int nr_fds;
+
+       nr_fds = count_open_fds();
+
+       base_fd = socket(PF_LOCAL, SOCK_STREAM, 0);
+       assert(base_fd >= 0);
+
+       fd = wl_os_dupfd_cloexec(base_fd, 13);
+       assert(fd >= 13);
+
+       close(base_fd);
+
+       /*
+        * Must have 4 calls if falling back, but must also allow
+        * falling back without a forced fallback.
+        */
+       assert(wrapped_calls_fcntl > n);
+
+       exec_fd_leak_check(nr_fds);
+}
+
+TEST(os_wrappers_dupfd_cloexec)
+{
+       init_fallbacks(0);
+       do_os_wrappers_dupfd_cloexec(0);
+}
+
+TEST(os_wrappers_dupfd_cloexec_fallback)
+{
+       init_fallbacks(1);
+       do_os_wrappers_dupfd_cloexec(3);
+}