2021-03-31 12:28:19 -04:00
|
|
|
/*
|
|
|
|
* 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 <fcntl.h>
|
|
|
|
#include <gio/gunixfdlist.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/sysmacros.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
|
|
#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;
|
2021-04-06 11:06:38 -04:00
|
|
|
int major = -1, minor = -1;
|
2021-03-31 12:28:19 -04:00
|
|
|
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 (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;
|
|
|
|
}
|
|
|
|
|
2021-04-06 11:06:38 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-03-31 12:28:19 -04:00
|
|
|
if (!take_device (pool->session_proxy, major, minor, &fd, NULL, error))
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-04-06 11:08:10 -04:00
|
|
|
do
|
|
|
|
{
|
|
|
|
fd = open (path, O_RDWR | O_CLOEXEC);
|
|
|
|
}
|
|
|
|
while (fd == -1 && errno == EINTR);
|
|
|
|
|
2021-03-31 12:28:19 -04:00
|
|
|
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;
|
|
|
|
}
|