mirror of
https://github.com/brl/mutter.git
synced 2024-12-03 21:30:41 -05:00
e72f81c24f
Now that we have a setuid launcher binary, we can make use of using a private protocol through the socket we're passed at startup. We also use the new hook in clutter-evdev to ask mutter-launch for the FDs of the input devices we need, and we emulate the old X DRM lock with a nested GMainContext without sources. In the future, mutter-launch will be replaced with the new logind API currently in development. https://bugzilla.gnome.org/show_bug.cgi?id=705861
454 lines
11 KiB
C
454 lines
11 KiB
C
/*
|
|
* Copyright (C) 2013 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 <gio/gio.h>
|
|
#include <gio/gunixfdmessage.h>
|
|
|
|
#include <clutter/clutter.h>
|
|
#include <clutter/evdev/clutter-evdev.h>
|
|
|
|
#include <glib.h>
|
|
#include <sys/time.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <stdlib.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <drm.h>
|
|
#include <xf86drm.h>
|
|
#include <xf86drmMode.h>
|
|
|
|
#include "meta-weston-launch.h"
|
|
|
|
struct _MetaLauncherClass
|
|
{
|
|
GObjectClass parent_class;
|
|
|
|
void (*enter) (MetaLauncher *);
|
|
void (*leave) (MetaLauncher *);
|
|
};
|
|
|
|
struct _MetaLauncher
|
|
{
|
|
GObject parent;
|
|
|
|
GSocket *weston_launch;
|
|
|
|
gboolean vt_switched;
|
|
|
|
GMainContext *nested_context;
|
|
GMainLoop *nested_loop;
|
|
|
|
GSource *inner_source;
|
|
GSource *outer_source;
|
|
};
|
|
|
|
enum {
|
|
SIGNAL_ENTER,
|
|
SIGNAL_LEAVE,
|
|
SIGNAL_LAST
|
|
};
|
|
|
|
static int signals[SIGNAL_LAST];
|
|
|
|
G_DEFINE_TYPE (MetaLauncher, meta_launcher, G_TYPE_OBJECT);
|
|
|
|
static void handle_request_vt_switch (MetaLauncher *self);
|
|
|
|
static gboolean
|
|
request_vt_switch_idle (gpointer user_data)
|
|
{
|
|
handle_request_vt_switch (user_data);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
send_message_to_wl (MetaLauncher *self,
|
|
void *message,
|
|
gsize size,
|
|
GSocketControlMessage *out_cmsg,
|
|
GSocketControlMessage **in_cmsg,
|
|
GError **error)
|
|
{
|
|
struct weston_launcher_reply reply;
|
|
GInputVector in_iov = { &reply, sizeof (reply) };
|
|
GOutputVector out_iov = { message, size };
|
|
GSocketControlMessage *out_all_cmsg[2];
|
|
GSocketControlMessage **in_all_cmsg;
|
|
int flags = 0;
|
|
int i;
|
|
|
|
out_all_cmsg[0] = out_cmsg;
|
|
out_all_cmsg[1] = NULL;
|
|
if (g_socket_send_message (self->weston_launch, NULL,
|
|
&out_iov, 1,
|
|
out_all_cmsg, -1,
|
|
flags, NULL, error) != (gssize)size)
|
|
return FALSE;
|
|
|
|
if (g_socket_receive_message (self->weston_launch, NULL,
|
|
&in_iov, 1,
|
|
&in_all_cmsg, NULL,
|
|
&flags, NULL, error) != sizeof (reply))
|
|
return FALSE;
|
|
|
|
while (reply.header.opcode != ((struct weston_launcher_message*)message)->opcode)
|
|
{
|
|
/* There were events queued */
|
|
g_assert ((reply.header.opcode & WESTON_LAUNCHER_EVENT) == WESTON_LAUNCHER_EVENT);
|
|
|
|
/* This can never happen, because the only time mutter-launch can queue
|
|
this event is after confirming a VT switch, and we don't make requests
|
|
during that time.
|
|
|
|
Note that getting this event would be really bad, because we would be
|
|
in the wrong loop/context.
|
|
*/
|
|
g_assert (reply.header.opcode != WESTON_LAUNCHER_SERVER_VT_ENTER);
|
|
|
|
switch (reply.header.opcode)
|
|
{
|
|
case WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH:
|
|
g_idle_add (request_vt_switch_idle, self);
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
if (g_socket_receive_message (self->weston_launch, NULL,
|
|
&in_iov, 1,
|
|
NULL, NULL,
|
|
&flags, NULL, error) != sizeof (reply))
|
|
return FALSE;
|
|
}
|
|
|
|
if (reply.ret != 0)
|
|
{
|
|
if (reply.ret == -1)
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Got failure from weston-launch");
|
|
else
|
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-reply.ret),
|
|
"Got failure from weston-launch: %s", strerror (-reply.ret));
|
|
|
|
for (i = 0; in_all_cmsg && in_all_cmsg[i]; i++)
|
|
g_object_unref (in_all_cmsg[i]);
|
|
g_free (in_all_cmsg);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (in_all_cmsg && in_all_cmsg[0])
|
|
{
|
|
for (i = 1; in_all_cmsg[i]; i++)
|
|
g_object_unref (in_all_cmsg[i]);
|
|
*in_cmsg = in_all_cmsg[0];
|
|
}
|
|
|
|
g_free (in_all_cmsg);
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
meta_launcher_set_drm_fd (MetaLauncher *self,
|
|
int drm_fd,
|
|
GError **error)
|
|
{
|
|
struct weston_launcher_message message;
|
|
GSocketControlMessage *cmsg;
|
|
gboolean ok;
|
|
|
|
message.opcode = WESTON_LAUNCHER_DRM_SET_FD;
|
|
|
|
cmsg = g_unix_fd_message_new ();
|
|
if (g_unix_fd_message_append_fd (G_UNIX_FD_MESSAGE (cmsg),
|
|
drm_fd, error) == FALSE)
|
|
{
|
|
g_object_unref (cmsg);
|
|
return FALSE;
|
|
}
|
|
|
|
ok = send_message_to_wl (self, &message, sizeof message, cmsg, NULL, error);
|
|
|
|
g_object_unref (cmsg);
|
|
return ok;
|
|
}
|
|
|
|
int
|
|
meta_launcher_open_input_device (MetaLauncher *self,
|
|
const char *name,
|
|
int flags,
|
|
GError **error)
|
|
{
|
|
struct weston_launcher_open *message;
|
|
GSocketControlMessage *cmsg;
|
|
gboolean ok;
|
|
gsize size;
|
|
int *fds, n_fd;
|
|
int ret;
|
|
|
|
size = sizeof (struct weston_launcher_open) + strlen (name) + 1;
|
|
message = g_malloc (size);
|
|
message->header.opcode = WESTON_LAUNCHER_OPEN;
|
|
message->flags = flags;
|
|
strcpy (message->path, name);
|
|
message->path[strlen(name)] = 0;
|
|
|
|
ok = send_message_to_wl (self, message, size, NULL, &cmsg, error);
|
|
|
|
if (ok)
|
|
{
|
|
g_assert (G_IS_UNIX_FD_MESSAGE (cmsg));
|
|
|
|
fds = g_unix_fd_message_steal_fds (G_UNIX_FD_MESSAGE (cmsg), &n_fd);
|
|
g_assert (n_fd == 1);
|
|
|
|
ret = fds[0];
|
|
g_free (fds);
|
|
g_object_unref (cmsg);
|
|
}
|
|
else
|
|
ret = -1;
|
|
|
|
g_free (message);
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
meta_launcher_finalize (GObject *object)
|
|
{
|
|
MetaLauncher *launcher = META_LAUNCHER (object);
|
|
|
|
g_source_destroy (launcher->outer_source);
|
|
g_source_destroy (launcher->inner_source);
|
|
|
|
g_main_loop_unref (launcher->nested_loop);
|
|
g_main_context_unref (launcher->nested_context);
|
|
|
|
g_object_unref (launcher->weston_launch);
|
|
|
|
G_OBJECT_CLASS (meta_launcher_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
meta_launcher_enter (MetaLauncher *launcher)
|
|
{
|
|
ClutterBackend *backend;
|
|
CoglContext *cogl_context;
|
|
CoglDisplay *cogl_display;
|
|
|
|
backend = clutter_get_default_backend ();
|
|
cogl_context = clutter_backend_get_cogl_context (backend);
|
|
cogl_display = cogl_context_get_display (cogl_context);
|
|
cogl_kms_display_queue_modes_reset (cogl_display);
|
|
|
|
clutter_evdev_reclaim_devices ();
|
|
}
|
|
|
|
static void
|
|
meta_launcher_leave (MetaLauncher *launcher)
|
|
{
|
|
clutter_evdev_release_devices ();
|
|
}
|
|
|
|
static int
|
|
on_evdev_device_open (const char *path,
|
|
int flags,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
MetaLauncher *launcher = user_data;
|
|
|
|
return meta_launcher_open_input_device (launcher, path, flags, error);
|
|
}
|
|
|
|
static void
|
|
handle_vt_enter (MetaLauncher *launcher)
|
|
{
|
|
g_assert (launcher->vt_switched);
|
|
|
|
g_main_loop_quit (launcher->nested_loop);
|
|
}
|
|
|
|
static void
|
|
handle_request_vt_switch (MetaLauncher *launcher)
|
|
{
|
|
struct weston_launcher_message message;
|
|
GError *error;
|
|
gboolean ok;
|
|
|
|
g_signal_emit (launcher, signals[SIGNAL_LEAVE], 0);
|
|
|
|
message.opcode = WESTON_LAUNCHER_CONFIRM_VT_SWITCH;
|
|
|
|
error = NULL;
|
|
ok = send_message_to_wl (launcher, &message, sizeof (message), NULL, NULL, &error);
|
|
if (!ok) {
|
|
g_warning ("Failed to acknowledge VT switch: %s", error->message);
|
|
g_error_free (error);
|
|
|
|
return;
|
|
}
|
|
|
|
g_assert (!launcher->vt_switched);
|
|
launcher->vt_switched = TRUE;
|
|
|
|
/* We can't do anything at this point, because we don't
|
|
have input devices and we don't have the DRM master,
|
|
so let's run a nested busy loop until the VT is reentered */
|
|
g_main_loop_run (launcher->nested_loop);
|
|
|
|
g_assert (launcher->vt_switched);
|
|
launcher->vt_switched = FALSE;
|
|
|
|
g_signal_emit (launcher, signals[SIGNAL_ENTER], 0);
|
|
}
|
|
|
|
static gboolean
|
|
on_socket_readable (GSocket *socket,
|
|
GIOCondition condition,
|
|
gpointer user_data)
|
|
{
|
|
MetaLauncher *launcher = user_data;
|
|
struct weston_launcher_event event;
|
|
gssize read;
|
|
GError *error;
|
|
|
|
if ((condition & G_IO_IN) == 0)
|
|
return TRUE;
|
|
|
|
error = NULL;
|
|
read = g_socket_receive (socket, (char*)&event, sizeof(event), NULL, &error);
|
|
if (read < (gssize)sizeof(event))
|
|
{
|
|
g_warning ("Error reading from weston-launcher socket: %s", error->message);
|
|
g_error_free (error);
|
|
return TRUE;
|
|
}
|
|
|
|
switch (event.header.opcode)
|
|
{
|
|
case WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH:
|
|
handle_request_vt_switch (launcher);
|
|
break;
|
|
|
|
case WESTON_LAUNCHER_SERVER_VT_ENTER:
|
|
handle_vt_enter (launcher);
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
env_get_fd (const char *env)
|
|
{
|
|
const char *value;
|
|
|
|
value = g_getenv (env);
|
|
|
|
if (value == NULL)
|
|
return -1;
|
|
else
|
|
return g_ascii_strtoll (value, NULL, 10);
|
|
}
|
|
|
|
static void
|
|
meta_launcher_init (MetaLauncher *self)
|
|
{
|
|
int launch_fd;
|
|
|
|
launch_fd = env_get_fd ("WESTON_LAUNCHER_SOCK");
|
|
if (launch_fd < 0)
|
|
g_error ("Invalid mutter-launch socket");
|
|
|
|
self->weston_launch = g_socket_new_from_fd (launch_fd, NULL);
|
|
|
|
clutter_evdev_set_open_callback (on_evdev_device_open, self);
|
|
|
|
self->nested_context = g_main_context_new ();
|
|
self->nested_loop = g_main_loop_new (self->nested_context, FALSE);
|
|
|
|
self->outer_source = g_socket_create_source (self->weston_launch, G_IO_IN, NULL);
|
|
g_source_set_callback (self->outer_source, (GSourceFunc)on_socket_readable, self, NULL);
|
|
g_source_attach (self->outer_source, NULL);
|
|
g_source_unref (self->outer_source);
|
|
|
|
self->inner_source = g_socket_create_source (self->weston_launch, G_IO_IN, NULL);
|
|
g_source_set_callback (self->inner_source, (GSourceFunc)on_socket_readable, self, NULL);
|
|
g_source_attach (self->inner_source, self->nested_context);
|
|
g_source_unref (self->inner_source);
|
|
}
|
|
|
|
static void
|
|
meta_launcher_class_init (MetaLauncherClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = meta_launcher_finalize;
|
|
|
|
klass->enter = meta_launcher_enter;
|
|
klass->leave = meta_launcher_leave;
|
|
|
|
signals[SIGNAL_ENTER] = g_signal_new ("enter",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (MetaLauncherClass, enter),
|
|
NULL, NULL, /* accumulator */
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
signals[SIGNAL_LEAVE] = g_signal_new ("leave",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (MetaLauncherClass, leave),
|
|
NULL, NULL, /* accumulator */
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
}
|
|
|
|
MetaLauncher *
|
|
meta_launcher_new (void)
|
|
{
|
|
return g_object_new (META_TYPE_LAUNCHER, NULL);
|
|
}
|
|
|
|
gboolean
|
|
meta_launcher_activate_vt (MetaLauncher *launcher,
|
|
int vt,
|
|
GError **error)
|
|
{
|
|
struct weston_launcher_activate_vt message;
|
|
|
|
message.header.opcode = WESTON_LAUNCHER_ACTIVATE_VT;
|
|
message.vt = vt;
|
|
|
|
return send_message_to_wl (launcher, &message, sizeof (message), NULL, NULL, error);
|
|
}
|
|
|