os: wrap socket(SOCK_CLOEXEC) calls
authorPekka Paalanen <ppaalanen@gmail.com>
Wed, 21 Mar 2012 09:11:26 +0000 (11:11 +0200)
committerPekka Paalanen <ppaalanen@gmail.com>
Fri, 20 Apr 2012 13:26:37 +0000 (16:26 +0300)
Some system C libraries do not offer SOCK_CLOEXEC flag.

Add a new header for OS compatibility wrappers. Wrap socket() calls into
wl_os_socket_cloexec() which makes sure the O_CLOEXEC flag gets set on
the file descriptor.

On systems having SOCK_CLOEXEC this uses the old socket() call, and
falls back if it fails due to the flag (kernel not supporting it).

wayland-os.h is private and not exported.

Add close-on-exec tests for both normal and forced fallback paths.

Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
src/Makefile.am
src/wayland-client.c
src/wayland-os.c [new file with mode: 0644]
src/wayland-os.h [new file with mode: 0644]
src/wayland-server.c
tests/Makefile.am
tests/os-wrappers-test.c [new file with mode: 0644]

index 7217236..c685885 100644 (file)
@@ -14,6 +14,8 @@ libwayland_util_la_SOURCES =                  \
        connection.c                            \
        wayland-util.c                          \
        wayland-util.h                          \
+       wayland-os.c                            \
+       wayland-os.h                            \
        wayland-private.h
 
 libwayland_server_la_LIBADD = $(FFI_LIBS) libwayland-util.la -lrt
index 713c894..794f716 100644 (file)
@@ -36,6 +36,7 @@
 #include <sys/poll.h>
 
 #include "wayland-util.h"
+#include "wayland-os.h"
 #include "wayland-client.h"
 #include "wayland-private.h"
 
@@ -307,7 +308,7 @@ connect_to_socket(struct wl_display *display, const char *name)
        const char *runtime_dir;
        size_t name_size;
 
-       display->fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
+       display->fd = wl_os_socket_cloexec(PF_LOCAL, SOCK_STREAM, 0);
        if (display->fd < 0)
                return -1;
 
diff --git a/src/wayland-os.c b/src/wayland-os.c
new file mode 100644 (file)
index 0000000..5ff4391
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright © 2012 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "wayland-os.h"
+
+static int
+set_cloexec_or_close(int fd)
+{
+       long flags;
+
+       if (fd == -1)
+               return -1;
+
+       flags = fcntl(fd, F_GETFD);
+       if (flags == -1)
+               goto err;
+
+       if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
+               goto err;
+
+       return fd;
+
+err:
+       close(fd);
+       return -1;
+}
+
+int
+wl_os_socket_cloexec(int domain, int type, int protocol)
+{
+       int fd;
+
+#ifdef SOCK_CLOEXEC
+       errno = 0;
+       fd = socket(domain, type | SOCK_CLOEXEC, protocol);
+       if (fd >= 0)
+               return fd;
+       if (errno != EINVAL)
+               return -1;
+#endif
+
+       fd = socket(domain, type, protocol);
+       return set_cloexec_or_close(fd);
+}
diff --git a/src/wayland-os.h b/src/wayland-os.h
new file mode 100644 (file)
index 0000000..49adc2b
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright © 2012 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef WAYLAND_OS_H
+#define WAYLAND_OS_H
+
+int
+wl_os_socket_cloexec(int domain, int type, int protocol);
+
+#endif
index 8ca27bd..1fe00a9 100644 (file)
@@ -44,6 +44,7 @@
 #include "wayland-private.h"
 #include "wayland-server.h"
 #include "wayland-server-protocol.h"
+#include "wayland-os.h"
 
 struct wl_socket {
        int fd;
@@ -970,7 +971,7 @@ wl_display_add_socket(struct wl_display *display, const char *name)
        if (s == NULL)
                return -1;
 
-       s->fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
+       s->fd = wl_os_socket_cloexec(PF_LOCAL, SOCK_STREAM, 0);
        if (s->fd < 0) {
                free(s);
                return -1;
index af76c5f..5bb4784 100644 (file)
@@ -5,7 +5,8 @@ my_check_programs =                             \
        list-test                               \
        connection-test                         \
        event-loop-test                         \
-       client-test
+       client-test                             \
+       os-wrappers-test
 
 TESTS = $(my_check_programs)
 
@@ -23,6 +24,11 @@ connection_test_SOURCES = connection-test.c $(test_runner_src)
 event_loop_test_SOURCES = event-loop-test.c $(test_runner_src)
 client_test_SOURCES = client-test.c $(test_runner_src)
 
+os_wrappers_test_SOURCES =                     \
+       os-wrappers-test.c                      \
+       ../src/wayland-os.c                     \
+       $(test_runner_src)
+
 AM_CFLAGS = $(GCC_CFLAGS)
 LDADD = $(top_builddir)/src/libwayland-util.la \
        $(top_builddir)/src/libwayland-server.la \
diff --git a/tests/os-wrappers-test.c b/tests/os-wrappers-test.c
new file mode 100644 (file)
index 0000000..aa2631b
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright © 2012 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <errno.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 void
+init_fallbacks(int do_fallbacks)
+{
+       fall_back = do_fallbacks;
+       real_socket = dlsym(RTLD_NEXT, "socket");
+}
+
+__attribute__ ((visibility("default"))) int
+socket(int domain, int type, int protocol)
+{
+       wrapped_calls++;
+
+#ifdef SOCK_CLOEXEC
+       if (fall_back && (type & SOCK_CLOEXEC)) {
+               errno = EINVAL;
+               return -1;
+       }
+#endif
+
+       return real_socket(domain, type, protocol);
+}
+
+static void
+do_os_wrappers_socket_cloexec(int n)
+{
+       int fd;
+       int nr_fds;
+
+       nr_fds = count_open_fds();
+
+       /* simply create a socket that closes on exec */
+       fd = wl_os_socket_cloexec(PF_LOCAL, SOCK_STREAM, 0);
+
+#ifdef SOCK_CLOEXEC
+       /*
+        * Must have 2 calls if falling back, but must also allow
+        * falling back without a forced fallback.
+        */
+       assert(wrapped_calls > n);
+#else
+       assert(wrapped_calls == 1);
+#endif
+       assert(fd >= 0);
+
+       exec_fd_leak_check(nr_fds);
+}
+
+TEST(os_wrappers_socket_cloexec)
+{
+       /* normal case */
+       init_fallbacks(0);
+       do_os_wrappers_socket_cloexec(0);
+}
+
+TEST(os_wrappers_socket_cloexec_fallback)
+{
+       /* forced fallback */
+       init_fallbacks(1);
+       do_os_wrappers_socket_cloexec(1);
+}