summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPekka Paalanen <ppaalanen@gmail.com>2012-04-23 13:55:55 +0300
committerPekka Paalanen <ppaalanen@gmail.com>2012-04-25 09:32:58 +0300
commit1463a41f893b0705c30035182bb070a95f822b1c (patch)
tree6188458f3283df4ef6521255067ac20f3a539856
parent3b29783dc8f0e856afce7a9edf10c0ca4d12f284 (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.c3
-rw-r--r--src/wayland-os.c15
-rw-r--r--src/wayland-os.h7
-rw-r--r--tests/os-wrappers-test.c70
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);
+}