[PATCH 03/12] server: Add support for BSD’s xucred credentials

Philip Withnall philip at tecnocode.co.uk
Fri Feb 15 04:56:31 PST 2013


This is an alternative to the traditional ucred on Linux.

Signed-off-by: Philip Withnall <philip at tecnocode.co.uk>
---
 configure.ac         |  3 ++
 src/wayland-server.c | 33 +++++++++++++++++++
 src/wayland-shm.c    | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 126 insertions(+), 1 deletion(-)

diff --git a/configure.ac b/configure.ac
index 935afbd..3ad39ab 100644
--- a/configure.ac
+++ b/configure.ac
@@ -49,6 +49,9 @@ fi
 
 AC_CHECK_HEADERS([sys/signalfd.h sys/timerfd.h])
 
+# Credential support on FreeBSD.
+AC_CHECK_HEADERS([sys/ucred.h])
+
 AC_ARG_ENABLE([scanner],
               [AC_HELP_STRING([--disable-scanner],
                               [Disable compilation of wayland-scanner])],
diff --git a/src/wayland-server.c b/src/wayland-server.c
index dae7177..614dd2f 100644
--- a/src/wayland-server.c
+++ b/src/wayland-server.c
@@ -22,6 +22,8 @@
 
 #define _GNU_SOURCE
 
+#include "../config.h"
+
 #include <stdlib.h>
 #include <stdint.h>
 #include <stddef.h>
@@ -40,6 +42,10 @@
 #include <sys/file.h>
 #include <sys/stat.h>
 #include <ffi.h>
+#ifdef HAVE_SYS_UCRED_H
+#include <sys/types.h>
+#include <sys/ucred.h>
+#endif
 
 #include "wayland-private.h"
 #include "wayland-server.h"
@@ -75,7 +81,13 @@ struct wl_client {
 	struct wl_list link;
 	struct wl_map objects;
 	struct wl_signal destroy_signal;
+#ifdef HAVE_SYS_UCRED_H
+	/* FreeBSD. */
+	struct xucred xucred;
+#else
+	/* Linux. */
 	struct ucred ucred;
+#endif
 	int error;
 };
 
@@ -346,10 +358,20 @@ wl_client_create(struct wl_display *display, int fd)
 	if (!client->source)
 		goto err_client;
 
+#if defined(SO_PEERCRED)
+	/* Linux. */
 	len = sizeof client->ucred;
 	if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED,
 		       &client->ucred, &len) < 0)
 		goto err_source;
+#elif defined(LOCAL_PEERCRED)
+	/* FreeBSD. */
+	len = sizeof client->xucred;
+	if (getsockopt(fd, SOL_SOCKET, LOCAL_PEERCRED, &client->xucred,
+	               &len) < 0 ||
+	    client->xucred.cr_version != XUCRED_VERSION)
+		goto err_source;
+#endif
 
 	client->connection = wl_connection_create(fd);
 	if (client->connection == NULL)
@@ -384,12 +406,23 @@ WL_EXPORT void
 wl_client_get_credentials(struct wl_client *client,
 			  pid_t *pid, uid_t *uid, gid_t *gid)
 {
+#ifdef HAVE_SYS_UCRED_H
+	/* FreeBSD. */
+	if (pid)
+		*pid = 0; /* FIXME: not defined on FreeBSD */
+	if (uid)
+		*uid = client->xucred.cr_uid;
+	if (gid)
+		*gid = client->xucred.cr_gid;
+#else
+	/* Linux. */
 	if (pid)
 		*pid = client->ucred.pid;
 	if (uid)
 		*uid = client->ucred.uid;
 	if (gid)
 		*gid = client->ucred.gid;
+#endif
 }
 
 WL_EXPORT uint32_t
diff --git a/src/wayland-shm.c b/src/wayland-shm.c
index 47c126b..c783d0e 100644
--- a/src/wayland-shm.c
+++ b/src/wayland-shm.c
@@ -27,6 +27,8 @@
 
 #define _GNU_SOURCE
 
+#include "../config.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -41,6 +43,9 @@ struct wl_shm_pool {
 	int refcount;
 	char *data;
 	int size;
+#ifdef HAVE_SYS_UCRED_H
+	int fd;
+#endif
 };
 
 struct wl_shm_buffer {
@@ -58,6 +63,10 @@ shm_pool_unref(struct wl_shm_pool *pool)
 	if (pool->refcount)
 		return;
 
+#ifdef HAVE_SYS_UCRED_H
+	close(pool->fd);
+#endif
+
 	munmap(pool->data, pool->size);
 	free(pool);
 }
@@ -154,14 +163,88 @@ shm_pool_destroy(struct wl_client *client, struct wl_resource *resource)
 	wl_resource_destroy(resource);
 }
 
+#ifdef HAVE_MREMAP
+static void *
+mremap_compat_maymove(void *old_address, size_t old_size, size_t new_size,
+                      int old_prot, int old_flags, int old_fd)
+{
+	return mremap(old_address, old_size, new_size, MREMAP_MAYMOVE);
+}
+#else
+static void *
+mremap_compat_maymove(void *old_address, size_t old_size, size_t new_size,
+                      int old_prot, int old_flags, int old_fd)
+{
+	/* FreeBSD doesn't support mremap() yet, so we have to emulate it.
+	 * This assumes MREMAP_MAYMOVE is the only flag in use. */
+	if (new_size == old_size) {
+		return old_address;
+	} else if (new_size < old_size) {
+		/* Shrinking: munmap() the spare region. */
+		munmap(old_address + old_size, new_size - old_size);
+		return old_address;
+	} else {
+		void *ret;
+
+		/* Growing. Try and mmap() the extra region at the end of
+		 * our existing allocation. If that gets mapped in the
+		 * wrong place, fall back to mmap()ing an entirely new
+		 * region of new_size and copying the data across. */
+		ret = mmap(old_address + old_size, new_size - old_size,
+		           old_prot, old_flags, old_fd, 0);
+
+/* TODO: msync() before munmap()? */
+		if (ret == MAP_FAILED) {
+			/* Total failure! */
+			return ret;
+		} else if (ret == old_address + old_size) {
+			/* Success. */
+			return old_address;
+		} else if (ret != old_address + old_size) {
+			/* Partial failure. Fall back to mapping an
+			 * entirely new region. Unmap the region we
+			 * just mapped first. */
+			munmap(ret, new_size - old_size);
+
+			/* Map an entirely new region. */
+			ret = mmap(NULL, new_size,
+			           old_prot, old_flags, old_fd, 0);
+
+			if (ret == MAP_FAILED) {
+				/* Total failure! */
+				return ret;
+			}
+
+			/* Copy the old data across. Implicit assumption
+			 * that the old and new regions don't overlap. */
+			memcpy(ret, old_address, old_size);
+
+			/* Unmap the old region. */
+			munmap(old_address, old_size);
+
+			return ret;
+		}
+	}
+
+	/* Unreachable. */
+	return MAP_FAILED;
+}
+#endif
+
 static void
 shm_pool_resize(struct wl_client *client, struct wl_resource *resource,
 		int32_t size)
 {
 	struct wl_shm_pool *pool = resource->data;
 	void *data;
+	int fd = -1;
+
+#ifdef HAVE_SYS_UCRED_H
+	fd = pool->fd;
+#endif
 
-	data = mremap(pool->data, pool->size, size, MREMAP_MAYMOVE);
+	data = mremap_compat_maymove(pool->data, pool->size, size,
+	                             PROT_READ | PROT_WRITE, MAP_SHARED, fd);
 
 	if (data == MAP_FAILED) {
 		wl_resource_post_error(resource,
@@ -209,7 +292,13 @@ shm_create_pool(struct wl_client *client, struct wl_resource *resource,
 				       "failed mmap fd %d", fd);
 		goto err_free;
 	}
+#ifdef HAVE_SYS_UCRED_H
+	/* We need to keep the FD around on FreeBSD so we can implement
+	 * mremap(). See: mremap_compat_maymove(). */
+	pool->fd = fd;
+#else
 	close(fd);
+#endif
 
 	pool->resource.object.id = id;
 	pool->resource.object.interface = &wl_shm_pool_interface;
-- 
1.7.11.7

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 230 bytes
Desc: This is a digitally signed message part
URL: <http://lists.freedesktop.org/archives/wayland-devel/attachments/20130215/68acaf64/attachment-0001.pgp>


More information about the wayland-devel mailing list