diff options
author | Pekka Paalanen <ppaalanen@gmail.com> | 2012-04-23 13:55:55 +0300 |
---|---|---|
committer | Pekka Paalanen <ppaalanen@gmail.com> | 2012-04-25 09:32:58 +0300 |
commit | 1463a41f893b0705c30035182bb070a95f822b1c (patch) | |
tree | 6188458f3283df4ef6521255067ac20f3a539856 | |
parent | 3b29783dc8f0e856afce7a9edf10c0ca4d12f284 (diff) |
os: wrap F_DUPFD_CLOEXEC
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>
-rw-r--r-- | src/connection.c | 3 | ||||
-rw-r--r-- | src/wayland-os.c | 15 | ||||
-rw-r--r-- | src/wayland-os.h | 7 | ||||
-rw-r--r-- | tests/os-wrappers-test.c | 70 |
4 files changed, 91 insertions, 4 deletions
diff --git a/src/connection.c b/src/connection.c index a599f91..c49ca3d 100644 --- a/src/connection.c +++ b/src/connection.c @@ -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(); diff --git a/src/wayland-os.c b/src/wayland-os.c index 4db8050..4a19da6 100644 --- a/src/wayland-os.c +++ b/src/wayland-os.c @@ -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); +} diff --git a/src/wayland-os.h b/src/wayland-os.h index a57b3aa..456d8b0 100644 --- a/src/wayland-os.h +++ b/src/wayland-os.h @@ -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 diff --git a/tests/os-wrappers-test.c b/tests/os-wrappers-test.c index c6bf26e..2272b73 100644 --- a/tests/os-wrappers-test.c +++ b/tests/os-wrappers-test.c @@ -29,26 +29,32 @@ #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); +} |