diff --git a/src/backends/native/meta-backend-native-private.h b/src/backends/native/meta-backend-native-private.h index 04583259e..cd184685f 100644 --- a/src/backends/native/meta-backend-native-private.h +++ b/src/backends/native/meta-backend-native-private.h @@ -25,8 +25,11 @@ #ifndef META_BACKEND_NATIVE_PRIVATE_H #define META_BACKEND_NATIVE_PRIVATE_H +#include "backends/native/meta-backend-native.h" #include "backends/native/meta-barrier-native.h" MetaBarrierManagerNative *meta_backend_native_get_barrier_manager (MetaBackendNative *native); +MetaDevicePool * meta_backend_native_get_device_pool (MetaBackendNative *native); + #endif /* META_BACKEND_NATIVE_PRIVATE_H */ diff --git a/src/backends/native/meta-backend-native-types.h b/src/backends/native/meta-backend-native-types.h index f16f6b1d0..5fb7d3704 100644 --- a/src/backends/native/meta-backend-native-types.h +++ b/src/backends/native/meta-backend-native-types.h @@ -29,6 +29,7 @@ typedef struct _MetaRendererNative MetaRendererNative; typedef struct _MetaGpuKms MetaGpuKms; typedef struct _MetaCrtcVirtual MetaCrtcVirtual; typedef struct _MetaCrtcModeVirtual MetaCrtcModeVirtual; +typedef struct _MetaDevicePool MetaDevicePool; typedef enum _MetaSeatNativeFlag { diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c index 9524d91f3..79c73f35c 100644 --- a/src/backends/native/meta-backend-native.c +++ b/src/backends/native/meta-backend-native.c @@ -50,6 +50,7 @@ #include "backends/meta-settings-private.h" #include "backends/meta-stage-private.h" #include "backends/native/meta-clutter-backend-native.h" +#include "backends/native/meta-device-pool-private.h" #include "backends/native/meta-kms.h" #include "backends/native/meta-kms-device.h" #include "backends/native/meta-launcher.h" @@ -81,6 +82,7 @@ struct _MetaBackendNative MetaBackend parent; MetaLauncher *launcher; + MetaDevicePool *device_pool; MetaUdev *udev; MetaKms *kms; @@ -119,6 +121,7 @@ meta_backend_native_dispose (GObject *object) g_clear_object (&native->kms); g_clear_object (&native->udev); + g_clear_object (&native->device_pool); g_clear_pointer (&native->launcher, meta_launcher_free); } @@ -574,6 +577,8 @@ meta_backend_native_initable_init (GInitable *initable, return FALSE; } + native->device_pool = meta_device_pool_new (native->launcher); + #ifdef HAVE_WAYLAND meta_backend_init_wayland_display (META_BACKEND (native)); #endif @@ -672,6 +677,12 @@ meta_backend_native_get_launcher (MetaBackendNative *native) return native->launcher; } +MetaDevicePool * +meta_backend_native_get_device_pool (MetaBackendNative *native) +{ + return native->device_pool; +} + MetaUdev * meta_backend_native_get_udev (MetaBackendNative *native) { diff --git a/src/backends/native/meta-device-pool-private.h b/src/backends/native/meta-device-pool-private.h new file mode 100644 index 000000000..06a43f6f4 --- /dev/null +++ b/src/backends/native/meta-device-pool-private.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef META_DEVICE_POOL_PRIVATE_H +#define META_DEVICE_POOL_PRIVATE_H + +#include + +#include "backends/native/meta-device-pool.h" +#include "backends/native/meta-launcher.h" + +#define META_TYPE_DEVICE_POOL (meta_device_pool_get_type ()) +G_DECLARE_FINAL_TYPE (MetaDevicePool, meta_device_pool, + META, DEVICE_POOL, + GObject) + +MetaDevicePool * meta_device_pool_new (MetaLauncher *launcher); + +#endif /* META_DEVICE_POOL_PRIVATE_H */ diff --git a/src/backends/native/meta-device-pool.c b/src/backends/native/meta-device-pool.c new file mode 100644 index 000000000..baca12c5f --- /dev/null +++ b/src/backends/native/meta-device-pool.c @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2013-2021 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "config.h" + +#include "backends/native/meta-device-pool-private.h" + +#include +#include +#include +#include +#include +#include + +#include "backends/native/meta-launcher.h" + +#include "meta-dbus-login1.h" + +struct _MetaDeviceFile +{ + MetaDevicePool *pool; + + grefcount ref_count; + + char *path; + int major; + int minor; + int fd; + MetaDeviceFileFlags flags; +}; + +struct _MetaDevicePool +{ + GObject parent; + + MetaDbusLogin1Session *session_proxy; + + GMutex mutex; + + GList *files; +}; + +G_DEFINE_TYPE (MetaDevicePool, meta_device_pool, G_TYPE_OBJECT) + +static void +release_device_file (MetaDevicePool *pool, + MetaDeviceFile *file); + +static MetaDeviceFile * +meta_device_file_new (MetaDevicePool *pool, + const char *path, + int major, + int minor, + int fd, + MetaDeviceFileFlags flags) +{ + MetaDeviceFile *file; + + file = g_new0 (MetaDeviceFile, 1); + + file->pool = pool; + g_ref_count_init (&file->ref_count); + + file->path = g_strdup (path); + file->major = major; + file->minor = minor; + file->fd = fd; + file->flags = flags; + + return file; +} + +static void +meta_device_file_free (MetaDeviceFile *file) +{ + g_free (file->path); + g_free (file); +} + +int +meta_device_file_get_fd (MetaDeviceFile *device_file) +{ + g_assert (!g_ref_count_compare (&device_file->ref_count, 0)); + + return device_file->fd; +} + +const char * +meta_device_file_get_path (MetaDeviceFile *device_file) +{ + return device_file->path; +} + +static MetaDeviceFile * +meta_device_file_acquire_locked (MetaDeviceFile *file) +{ + g_ref_count_inc (&file->ref_count); + return file; +} + +MetaDeviceFile * +meta_device_file_acquire (MetaDeviceFile *file) +{ + g_mutex_lock (&file->pool->mutex); + meta_device_file_acquire_locked (file); + g_mutex_unlock (&file->pool->mutex); + + return file; +} + +void +meta_device_file_release (MetaDeviceFile *file) +{ + g_warn_if_fail (file->fd != -1); + + release_device_file (file->pool, file); +} + +MetaDevicePool * +meta_device_file_get_pool (MetaDeviceFile *device_file) +{ + return device_file->pool; +} + +static MetaDeviceFile * +find_device_file_from_path (MetaDevicePool *pool, + const char *path) +{ + GList *l; + + for (l = pool->files; l; l = l->next) + { + MetaDeviceFile *file = l->data; + + if (g_strcmp0 (file->path, path) == 0) + return file; + } + + return NULL; +} + +static gboolean +take_device (MetaDbusLogin1Session *session_proxy, + int dev_major, + int dev_minor, + int *out_fd, + GCancellable *cancellable, + GError **error) +{ + g_autoptr (GVariant) fd_variant = NULL; + g_autoptr (GUnixFDList) fd_list = NULL; + int fd = -1; + + if (!meta_dbus_login1_session_call_take_device_sync (session_proxy, + dev_major, + dev_minor, + NULL, + &fd_variant, + NULL, /* paused */ + &fd_list, + cancellable, + error)) + return FALSE; + + fd = g_unix_fd_list_get (fd_list, g_variant_get_handle (fd_variant), error); + if (fd == -1) + return FALSE; + + *out_fd = fd; + return TRUE; +} + +static gboolean +get_device_info_from_path (const char *path, + int *out_major, + int *out_minor) +{ + int ret; + struct stat st; + + ret = stat (path, &st); + if (ret < 0 || !S_ISCHR (st.st_mode)) + return FALSE; + + *out_major = major (st.st_rdev); + *out_minor = minor (st.st_rdev); + return TRUE; +} + +MetaDeviceFile * +meta_device_pool_open (MetaDevicePool *pool, + const char *path, + MetaDeviceFileFlags flags, + GError **error) +{ + g_autoptr (GMutexLocker) locker = NULL; + MetaDeviceFile *file; + int major, minor; + int fd; + + locker = g_mutex_locker_new (&pool->mutex); + + file = find_device_file_from_path (pool, path); + if (file) + { + g_warn_if_fail (file->flags == flags); + meta_device_file_acquire_locked (file); + return file; + } + + if (!get_device_info_from_path (path, &major, &minor)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "Could not get device info for path %s: %m", path); + return NULL; + } + + if (flags & META_DEVICE_FILE_FLAG_TAKE_CONTROL) + { + if (!pool->session_proxy) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "Can't take control without logind session"); + return NULL; + } + + if (!take_device (pool->session_proxy, major, minor, &fd, NULL, error)) + return NULL; + } + else + { + fd = open (path, O_RDWR | O_CLOEXEC, 0); + if (fd == -1) + { + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), + "Failed to open device '%s': %s", + path, g_strerror (errno)); + return NULL; + } + } + + file = meta_device_file_new (pool, path, major, minor, fd, flags); + pool->files = g_list_prepend (pool->files, file); + + return file; +} + +static void +release_device_file (MetaDevicePool *pool, + MetaDeviceFile *file) +{ + g_autoptr (GMutexLocker) locker = NULL; + g_autoptr (GError) error = NULL; + + locker = g_mutex_locker_new (&pool->mutex); + + if (!g_ref_count_dec (&file->ref_count)) + return; + + pool->files = g_list_remove (pool->files, file); + + if (file->flags & META_DEVICE_FILE_FLAG_TAKE_CONTROL) + { + MetaDbusLogin1Session *session_proxy; + + session_proxy = pool->session_proxy; + if (!meta_dbus_login1_session_call_release_device_sync (session_proxy, + file->major, + file->minor, + NULL, &error)) + { + g_warning ("Could not release device '%s' (%d,%d): %s", + file->path, + file->major, file->minor, + error->message); + } + } + + close (file->fd); + + meta_device_file_free (file); +} + +MetaDevicePool * +meta_device_pool_new (MetaLauncher *launcher) +{ + MetaDevicePool *pool; + + pool = g_object_new (META_TYPE_DEVICE_POOL, NULL); + + if (launcher) + pool->session_proxy = meta_launcher_get_session_proxy (launcher); + + return pool; +} + +static void +meta_device_pool_finalize (GObject *object) +{ + MetaDevicePool *pool = META_DEVICE_POOL (object); + + g_mutex_clear (&pool->mutex); + g_warn_if_fail (!pool->files); + + G_OBJECT_CLASS (meta_device_pool_parent_class)->finalize (object); +} + +static void +meta_device_pool_init (MetaDevicePool *pool) +{ + g_mutex_init (&pool->mutex); +} + +static void +meta_device_pool_class_init (MetaDevicePoolClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = meta_device_pool_finalize; +} diff --git a/src/backends/native/meta-device-pool.h b/src/backends/native/meta-device-pool.h new file mode 100644 index 000000000..5889ac7f1 --- /dev/null +++ b/src/backends/native/meta-device-pool.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2021 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef META_DEVICE_POOL_H +#define META_DEVICE_POOL_H + +#include +#include + +typedef enum _MetaDeviceFileFlags +{ + META_DEVICE_FILE_FLAG_NONE = 0, + META_DEVICE_FILE_FLAG_TAKE_CONTROL = 1 << 0, +} MetaDeviceFileFlags; + +typedef struct _MetaDeviceFile MetaDeviceFile; +typedef struct _MetaDevicePool MetaDevicePool; + +int meta_device_file_get_fd (MetaDeviceFile *device_file); + +const char * meta_device_file_get_path (MetaDeviceFile *device_file); + +MetaDeviceFile * meta_device_file_acquire (MetaDeviceFile *file); + +void meta_device_file_release (MetaDeviceFile *device_file); + +MetaDevicePool * meta_device_file_get_pool (MetaDeviceFile *device_file); + +MetaDeviceFile * meta_device_pool_open (MetaDevicePool *pool, + const char *path, + MetaDeviceFileFlags flags, + GError **error); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaDeviceFile, meta_device_file_release) + +#endif /* META_DEVICE_FILE_POOL_H */ diff --git a/src/backends/native/meta-launcher.c b/src/backends/native/meta-launcher.c index 301c40937..251fa3eb2 100644 --- a/src/backends/native/meta-launcher.c +++ b/src/backends/native/meta-launcher.c @@ -503,6 +503,12 @@ get_seat_id (GError **error) return seat_id; } +MetaDbusLogin1Session * +meta_launcher_get_session_proxy (MetaLauncher *launcher) +{ + return launcher->session_proxy; +} + MetaLauncher * meta_launcher_new (GError **error) { diff --git a/src/backends/native/meta-launcher.h b/src/backends/native/meta-launcher.h index 3473f0cf7..265be958e 100644 --- a/src/backends/native/meta-launcher.h +++ b/src/backends/native/meta-launcher.h @@ -23,6 +23,7 @@ #include typedef struct _MetaLauncher MetaLauncher; +typedef struct _MetaDbusLogin1Session MetaDbusLogin1Session; MetaLauncher *meta_launcher_new (GError **error); void meta_launcher_free (MetaLauncher *self); @@ -40,5 +41,7 @@ int meta_launcher_open_restricted (MetaLauncher *launcher, void meta_launcher_close_restricted (MetaLauncher *launcher, int fd); +MetaDbusLogin1Session * meta_launcher_get_session_proxy (MetaLauncher *launcher); + #endif /* META_LAUNCHER_H */ diff --git a/src/meson.build b/src/meson.build index 85d66a455..5e4aff9b6 100644 --- a/src/meson.build +++ b/src/meson.build @@ -662,6 +662,9 @@ if have_native_backend 'backends/native/meta-crtc-virtual.h', 'backends/native/meta-cursor-renderer-native.c', 'backends/native/meta-cursor-renderer-native.h', + 'backends/native/meta-device-pool-private.h', + 'backends/native/meta-device-pool.c', + 'backends/native/meta-device-pool.h', 'backends/native/meta-drm-buffer-dumb.c', 'backends/native/meta-drm-buffer-dumb.h', 'backends/native/meta-drm-buffer-gbm.c',