[PATCH] wfits: run without superuser

Eoff, Ullysses A ullysses.a.eoff at intel.com
Fri May 31 14:39:47 PDT 2013


Martin,

Thanks for your contribution!

This patch is still very much in proof of concept since it still contains some of the hacks
we did together to get it working.  There are several places where we disabled some
code to get it to work... and this disabled code breaks the other existing input
emulators.  So getting this in wayland-fits upstream will depend on two things:

 1. The evdev.h patch to Weston is accepted upstream.
 2. Your new input emulator should not break the other input emulators.

Currently, the weston-wfits extension has a UInput based emulator used when executing
on drm and x11 backends and a Weston Notify API based emulator used when running
on the headless backend.  These will be configurable soon.

I also wanted to note (for other readers) that using the UInput based emulator will give you
the *most* code coverage in Weston since it also exercises the x11-backend input events
and the drm-backend evdev input events.  Unfortunately you have to run Weston as root
to use the UInput based emulator.  The Weston Notify API based emulator does not require
Weston to run as root.  However, this means less Weston code gets exercised/covered.

Another thing to keep in mind is that the weston-wfits extension currently supports
three (3) different versions of the Weston SDK.  SDK rev 1 is for Weston 1.0, SDK rev 2
is for Weston 1.1, and SDK rev 3 is currently tracking master (1.2).  If the evdev.h patch
only goes into Weston master, then anywhere we need to use evdev.h from the
Weston SDK will need to have compile time guards so that wayland-fits continues to
work with the older Weston SDK's.  Hopefully the Weston SDK stabilizes soon... we'll
probably drop support for some of the older ones after some period of time.

I am currently refactoring the weston-wfits extension to use more C++ style classes.
This will include an InputEmulator abstract class that can be inherited to implement new
input emulator methods.  This should encapsulate things better and make it easier to
extend functionality.  I'm also adding a feature to allow the user to choose the input
emulator at runtime.  I'll send the patches to this list when I'm done.  We can revisit your
new input emulator technique after that ;-)

Cheers,

U. Artie 


> -----Original Message-----
> From: wayland-devel-bounces+ullysses.a.eoff=intel.com at lists.freedesktop.org [mailto:wayland-devel-
> bounces+ullysses.a.eoff=intel.com at lists.freedesktop.org] On Behalf Of Martin Minarik
> Sent: Friday, May 31, 2013 8:14 AM
> To: wayland-devel at lists.freedesktop.org
> Subject: [PATCH] wfits: run without superuser
> 
> This patch makes it possible to run wayland-fits without root.
> This approach also stresses the evdev.c stack. (calling the notify_* api approach would
> bypass the evdev.c stack)
> Testing also the evdev.c can help code test coverage. Probably can catch more bugs
> this way.
> 
> How it works. Basically, we make a pipe. Then we create evdev device and connect it
> to the pipe. Then we play the events via the pipe. In the end we destroy both pipe
> and the device.
> 
> What effects does the patch have?
> This assumes weston sdk exports evdev.h
> It uses evdev_device_create() and evdev_device_destroy() in the tested compositor.
> In case the symbols are not available, wfits module will fail to be loaded.
> ---
>  src/extensions/weston/Makefile.am      |    5 +-
>  src/extensions/weston/weston-wfits.cpp |  166 +++++++++++++++++++++++---------
>  2 files changed, 122 insertions(+), 49 deletions(-)
> 
> diff --git a/src/extensions/weston/Makefile.am b/src/extensions/weston/Makefile.am
> index 4af2f1d..b65a93f 100644
> --- a/src/extensions/weston/Makefile.am
> +++ b/src/extensions/weston/Makefile.am
> @@ -10,11 +10,14 @@ weston_wfits = weston-wfits.la
>  AM_CXXFLAGS =			\
>  	$(GCC_CFLAGS)				\
>  	$(WAYLAND_SERVER_CFLAGS)		\
> -	$(WESTON_CFLAGS)
> +	$(WESTON_CFLAGS)			\
> +	-std=gnu++11
> +
>  weston_wfits_la_LDFLAGS = -module -avoid-version
>  weston_wfits_la_LIBADD =			\
>  	$(WAYLAND_SERVER_LIBS)			\
>  	$(WESTON_LIBS)
> +
>  weston_wfits_la_SOURCES =						\
>  	weston-wfits.cpp							\
>  	$(top_srcdir)/src/extensions/protocol/wayland-fits-protocol.c
> diff --git a/src/extensions/weston/weston-wfits.cpp b/src/extensions/weston/weston-wfits.cpp
> index a54e0fe..96cf617 100644
> --- a/src/extensions/weston/weston-wfits.cpp
> +++ b/src/extensions/weston/weston-wfits.cpp
> @@ -42,6 +42,10 @@ extern "C" {
>  #undef private
>  }
> 
> +extern "C" {
> +#include <weston/evdev.h>
> +}
> +
>  #include "config.h"
>  #include "extensions/protocol/wayland-fits-server-protocol.h"
>  #include "common/util.h"
> @@ -76,8 +80,10 @@ struct x11_output {
>  struct wfits {
>  	struct weston_compositor *compositor;
>  	struct wl_listener compositor_destroy_listener;
> -	int input_fd; // file descriptor for our uinput device.
> +	int pipe_fd[2]; // file descriptor for the pipe or the uinput fd twice
>  	bool is_headless; // whether weston is using a headless backend.
> +	bool is_pipeston;
> +	struct evdev_device *dev;
>  };
> 
>  /**
> @@ -153,14 +159,14 @@ global_size(struct wfits *wfits, int32_t *width, int32_t *height)
> 
>  	output = get_output(wfits);
> 
> -	if (std::string(output->make) == "xwayland") {
> -		x11_compositor = (struct x11_compositor*) wfits->compositor;
> -		*width = x11_compositor->screen->width_in_pixels;
> -		*height = x11_compositor->screen->height_in_pixels;
> -	} else {
> +// 	if (std::string(output->make) == "xwayland") {
> +// 		x11_compositor = (struct x11_compositor*) wfits->compositor;
> +// 		*width = x11_compositor->screen->width_in_pixels;
> +// 		*height = x11_compositor->screen->height_in_pixels;
> +// 	} else {
>  		*width = output->width;
>  		*height = output->height;
> -	}
> +// 	}
>  }
> 
>  /**
> @@ -226,6 +232,8 @@ write_event_to_fd(int fd, struct input_event *event)
>  static void
>  move_pointer(struct wfits *wfits, const int32_t x, const int32_t y)
>  {
> +	wfits->compositor->focus = 1;
> +
>  	if (wfits->is_headless) {
>  		/**
>  		 * Weston is using a headless backend, so originate/trigger
> @@ -247,24 +255,22 @@ move_pointer(struct wfits *wfits, const int32_t x, const int32_t y)
>  	struct input_event event;
>  	int32_t gx(x), gy(y);
> 
> -	compositor_to_global(wfits, &gx, &gy);
> -
>  	memset(&event, 0, sizeof(event));
> 
>  	event.type = EV_ABS;
>  	event.code = ABS_X;
>  	event.value = gx;
> -	write_event_to_fd (wfits->input_fd, &event);
> +	write_event_to_fd (wfits->pipe_fd[1], &event);
> 
>  	event.type = EV_ABS;
>  	event.code = ABS_Y;
>  	event.value = gy;
> -	write_event_to_fd (wfits->input_fd, &event);
> +	write_event_to_fd (wfits->pipe_fd[1], &event);
> 
>  	event.type = EV_SYN;
>  	event.code = SYN_REPORT;
>  	event.value = 0;
> -	write_event_to_fd (wfits->input_fd, &event);
> +	write_event_to_fd (wfits->pipe_fd[1], &event);
>  }
> 
>  /**
> @@ -285,6 +291,8 @@ input_move_pointer(struct wl_client *client, struct wl_resource *resource,
>  static void
>  key_send(struct wfits *wfits, const uint32_t key, const uint32_t state)
>  {
> +	wfits->compositor->focus = 1;
> +
>  	if (wfits->is_headless) {
>  		/**
>  		 * Weston is using a headless backend, so originate/trigger the
> @@ -292,8 +300,6 @@ key_send(struct wfits *wfits, const uint32_t key, const uint32_t state)
>  		 **/
>  		struct weston_seat *seat = get_seat(wfits);
> 
> -		wfits->compositor->focus = 1;
> -
>  		if (key == BTN_LEFT or key == BTN_MIDDLE or key == BTN_RIGHT) {
>  			notify_button(seat, 100, key,
>  				static_cast<wl_pointer_button_state>(state));
> @@ -317,12 +323,12 @@ key_send(struct wfits *wfits, const uint32_t key, const uint32_t state)
>  	event.type = EV_KEY;
>  	event.code = key;
>  	event.value = state;
> -	write_event_to_fd(wfits->input_fd, &event);
> +	write_event_to_fd(wfits->pipe_fd[1], &event);
> 
>  	event.type = EV_SYN;
>  	event.code = SYN_REPORT;
>  	event.value = 0;
> -	write_event_to_fd(wfits->input_fd, &event);
> +	write_event_to_fd(wfits->pipe_fd[1], &event);
>  }
> 
>  /**
> @@ -384,56 +390,96 @@ bind_input(struct wl_client *client, void *data, uint32_t version, uint32_t id)
>  	resource->destroy = unbind_input_client;
>  }
> 
> -static void
> -create_input(struct wfits* wfits)
> +static int
> +create_input_pipeston(struct wfits* wfits)
> +{
> +	int32_t width;
> +	int32_t height;
> +
> +	if (pipe(wfits->pipe_fd) < 0) {
> +		return -1;
> +	}
> +
> +	fcntl(wfits->pipe_fd[0], F_SETFD, fcntl(wfits->pipe_fd[0], F_GETFD) | FD_CLOEXEC);
> +	fcntl(wfits->pipe_fd[0], F_SETFL, fcntl(wfits->pipe_fd[0], F_GETFL) | O_NONBLOCK);
> +	fcntl(wfits->pipe_fd[1], F_SETFD, fcntl(wfits->pipe_fd[1], F_GETFD) | FD_CLOEXEC);
> +
> +	/*
> +		create only 1 device on 1 seat for now.
> +	*/
> +
> +	wfits->dev = evdev_device_create(get_seat(wfits), "<mock-dev-path>", wfits->pipe_fd[0]);
> +
> +
> +	if ((wfits->dev == NULL) || (wfits->dev == EVDEV_UNHANDLED_DEVICE)) {
> +		weston_log("ERROR creating device\n");
> +		wfits->dev = NULL;
> +		return -2;
> +	}
> +
> +	wfits->dev->output = get_output(wfits);
> +	global_size(wfits, &width, &height);
> +	weston_log("%dx%d\n", width, height);
> +	wfits->dev->abs.max_x = width - 1;
> +	wfits->dev->abs.max_y = height - 1;
> +	wfits->dev->abs.min_x = 0;
> +	wfits->dev->abs.min_y = 0;
> +
> +	wl_list_init(&wfits->dev->link);
> +
> +	wfits->is_pipeston = true;
> +
> +	return 0;
> +}
> +
> +static int
> +create_input_uinput(struct wfits* wfits)
>  {
>  	struct uinput_user_dev device;
>  	struct weston_output *output = get_output(wfits);
>  	int32_t width;
>  	int32_t height;
> -
> -	weston_log("weston-wfits: creating uinput device\n");
> 
> -	wfits->input_fd = open("/dev/uinput", O_WRONLY | O_NDELAY);
> -	if (wfits->input_fd < 0) {
> -		weston_log("weston-wfits: failed to create uinput device\n");
> -		exit(EXIT_FAILURE);
> +	wfits->pipe_fd[0] = wfits->pipe_fd[1] = open("/dev/uinput", O_WRONLY | O_NDELAY);
> +	if (wfits->pipe_fd[1] < 0) {
> +		return -1;
>  	}
> 
> -	if (ioctl(wfits->input_fd, UI_SET_EVBIT, EV_KEY) < 0) {
> -		exit(EXIT_FAILURE);
> +	if (ioctl(wfits->pipe_fd[1], UI_SET_EVBIT, EV_KEY) < 0) {
> +		return -2;
>  	}
> 
>  	for (int32_t i(0); i < 255; ++i) {
> -		if (ioctl(wfits->input_fd, UI_SET_KEYBIT, i) < 0) {
> -			exit(EXIT_FAILURE);
> +		if (ioctl(wfits->pipe_fd[1], UI_SET_KEYBIT, i) < 0) {
> +			return -3;
>  		}
>  	}
> 
> -	if (ioctl(wfits->input_fd, UI_SET_KEYBIT, BTN_LEFT) < 0) {
> -		exit(EXIT_FAILURE);
> +	if (ioctl(wfits->pipe_fd[1], UI_SET_KEYBIT, BTN_LEFT) < 0) {
> +		return -4;
>  	}
> 
> -	if (ioctl(wfits->input_fd, UI_SET_KEYBIT, BTN_RIGHT) < 0) {
> -		exit(EXIT_FAILURE);
> +	if (ioctl(wfits->pipe_fd[1], UI_SET_KEYBIT, BTN_RIGHT) < 0) {
> +		return -5;
>  	}
> 
> -	if (ioctl(wfits->input_fd, UI_SET_KEYBIT, BTN_MIDDLE) < 0) {
> -		exit(EXIT_FAILURE);
> +	if (ioctl(wfits->pipe_fd[1], UI_SET_KEYBIT, BTN_MIDDLE) < 0) {
> +		return -6;
>  	}
> 
> -	if (ioctl(wfits->input_fd, UI_SET_EVBIT, EV_ABS) < 0) {
> -		exit(EXIT_FAILURE);
> +	if (ioctl(wfits->pipe_fd[1], UI_SET_EVBIT, EV_ABS) < 0) {
> +		return -7;
>  	}
> 
> -	if (ioctl(wfits->input_fd, UI_SET_ABSBIT, ABS_X) < 0) {
> -		exit(EXIT_FAILURE);
> +	if (ioctl(wfits->pipe_fd[1], UI_SET_ABSBIT, ABS_X) < 0) {
> +		return -8;
>  	}
> 
> -	if (ioctl(wfits->input_fd, UI_SET_ABSBIT, ABS_Y) < 0) {
> -		exit(EXIT_FAILURE);
> +	if (ioctl(wfits->pipe_fd[1], UI_SET_ABSBIT, ABS_Y) < 0) {
> +		return -9;
>  	}
> 
> +
>  	memset(&device, 0, sizeof(device));
>  	snprintf(device.name, UINPUT_MAX_NAME_SIZE, "WFITS INPUT");
>  	device.id.bustype = BUS_USB;
> @@ -450,13 +496,17 @@ create_input(struct wfits* wfits)
>  	device.absmin[ABS_Y] = 0;
>  	device.absmax[ABS_Y] = height - 1;
> 
> -	if (write(wfits->input_fd, &device, sizeof(device)) < 0) {
> -		exit(EXIT_FAILURE);
> +	if (write(wfits->pipe_fd[1], &device, sizeof(device)) < 0) {
> +		return -10;
>  	}
> 
> -	if (ioctl(wfits->input_fd, UI_DEV_CREATE) < 0) {
> -		exit(EXIT_FAILURE);
> +	if (ioctl(wfits->pipe_fd[1], UI_DEV_CREATE) < 0) {
> +		return -11;
>  	}
> +
> +	wfits->is_pipeston = false;
> +
> +	return 0;
>  }
> 
>  static void
> @@ -534,8 +584,15 @@ compositor_destroy(struct wl_listener *listener, void *data)
>  	if (not wfits->is_headless) {
>  		weston_log("weston-wfits: destroying uinput device\n");
> 
> -		if (ioctl(wfits->input_fd, UI_DEV_DESTROY) < 0) {
> -			weston_log("weston-wfits: failed to destroy uinput device\n");
> +		if (not wfits->is_pipeston) {
> +			if (ioctl(wfits->pipe_fd[1], UI_DEV_DESTROY) < 0) {
> +				weston_log("weston-wfits: failed to destroy uinput device\n");
> +			}
> +		} else {
> +			if (wfits->dev != NULL)
> +				evdev_device_destroy(wfits->dev);
> +			close(wfits->pipe_fd[1]);
> +			close(wfits->pipe_fd[0]);
>  		}
>  	}
>  }
> @@ -546,8 +603,21 @@ init_input(void *data)
>  	struct wfits *wfits = static_cast<struct wfits*>(data);
> 
>  	if (not wfits->is_headless) {
> -		create_input(wfits);
> -		/* sync our input pointer device with weston */
> +		int err1 = 0, err2 = 0;
> +
> +		if (err1 = create_input_uinput(wfits)) {
> +			weston_log("weston-wfits: creating pipeston device\n");
> +			err2 = create_input_pipeston(wfits);
> +		} else {
> +			weston_log("weston-wfits: created uinput device\n");
> +		}
> +
> +		if (err1 && err2) {
> +			weston_log("weston-wfits: create uinput failed: %i, "
> +					"pipeston failed: %i\n", err1, err2);
> +			exit(EXIT_FAILURE);
> +		}
> +		/* sync our single input pointer device with weston */
>  		wl_fixed_t cx, cy;
>  		get_pointer_xy(wfits, &cx, &cy);
>  		move_pointer(wfits, wl_fixed_to_int(cx), wl_fixed_to_int(cy));
> --
> 1.7.10.4
> 
> _______________________________________________
> wayland-devel mailing list
> wayland-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/wayland-devel


More information about the wayland-devel mailing list