Instead of connecting to weston-launch from launcher-util, we now try to connect to logind first. If logind provides session-devices, we use them. If not, we fall back to the old weston-launch facility.dev
parent
e461f85385
commit
cc5b2ed2b8
@ -0,0 +1,913 @@ |
|||||||
|
/*
|
||||||
|
* Copyright © 2013 David Herrmann |
||||||
|
* |
||||||
|
* Permission to use, copy, modify, distribute, and sell this software and |
||||||
|
* its documentation for any purpose is hereby granted without fee, provided |
||||||
|
* that the above copyright notice appear in all copies and that both that |
||||||
|
* copyright notice and this permission notice appear in supporting |
||||||
|
* documentation, and that the name of the copyright holders not be used in |
||||||
|
* advertising or publicity pertaining to distribution of the software |
||||||
|
* without specific, written prior permission. The copyright holders make |
||||||
|
* no representations about the suitability of this software for any |
||||||
|
* purpose. It is provided "as is" without express or implied warranty. |
||||||
|
* |
||||||
|
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS |
||||||
|
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND |
||||||
|
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY |
||||||
|
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER |
||||||
|
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF |
||||||
|
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
||||||
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "config.h" |
||||||
|
|
||||||
|
#include <errno.h> |
||||||
|
#include <dbus.h> |
||||||
|
#include <fcntl.h> |
||||||
|
#include <linux/vt.h> |
||||||
|
#include <linux/kd.h> |
||||||
|
#include <linux/major.h> |
||||||
|
#include <signal.h> |
||||||
|
#include <stdarg.h> |
||||||
|
#include <stdbool.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <sys/ioctl.h> |
||||||
|
#include <sys/signalfd.h> |
||||||
|
#include <sys/stat.h> |
||||||
|
#include <systemd/sd-login.h> |
||||||
|
#include <unistd.h> |
||||||
|
|
||||||
|
#include "compositor.h" |
||||||
|
#include "dbus.h" |
||||||
|
#include "logind-util.h" |
||||||
|
|
||||||
|
#ifndef KDSKBMUTE |
||||||
|
#define KDSKBMUTE 0x4B51 |
||||||
|
#endif |
||||||
|
|
||||||
|
struct weston_logind { |
||||||
|
struct weston_compositor *compositor; |
||||||
|
char *seat; |
||||||
|
char *sid; |
||||||
|
unsigned int vtnr; |
||||||
|
int vt; |
||||||
|
int kb_mode; |
||||||
|
int sfd; |
||||||
|
struct wl_event_source *sfd_source; |
||||||
|
|
||||||
|
DBusConnection *dbus; |
||||||
|
struct wl_event_source *dbus_ctx; |
||||||
|
char *spath; |
||||||
|
DBusPendingCall *pending_active; |
||||||
|
}; |
||||||
|
|
||||||
|
static int |
||||||
|
weston_logind_take_device(struct weston_logind *wl, uint32_t major, |
||||||
|
uint32_t minor, bool *paused_out) |
||||||
|
{ |
||||||
|
DBusMessage *m, *reply; |
||||||
|
bool b, paused; |
||||||
|
int r, fd; |
||||||
|
|
||||||
|
m = dbus_message_new_method_call("org.freedesktop.login1", |
||||||
|
wl->spath, |
||||||
|
"org.freedesktop.login1.Session", |
||||||
|
"TakeDevice"); |
||||||
|
if (!m) |
||||||
|
return -ENOMEM; |
||||||
|
|
||||||
|
b = dbus_message_append_args(m, |
||||||
|
DBUS_TYPE_UINT32, &major, |
||||||
|
DBUS_TYPE_UINT32, &minor, |
||||||
|
DBUS_TYPE_INVALID); |
||||||
|
if (!b) { |
||||||
|
r = -ENOMEM; |
||||||
|
goto err_unref; |
||||||
|
} |
||||||
|
|
||||||
|
reply = dbus_connection_send_with_reply_and_block(wl->dbus, m, |
||||||
|
-1, NULL); |
||||||
|
if (!reply) { |
||||||
|
r = -ENODEV; |
||||||
|
goto err_unref; |
||||||
|
} |
||||||
|
|
||||||
|
b = dbus_message_get_args(reply, NULL, |
||||||
|
DBUS_TYPE_UNIX_FD, &fd, |
||||||
|
DBUS_TYPE_BOOLEAN, &paused, |
||||||
|
DBUS_TYPE_INVALID); |
||||||
|
if (!b) { |
||||||
|
r = -ENODEV; |
||||||
|
goto err_reply; |
||||||
|
} |
||||||
|
|
||||||
|
r = fd; |
||||||
|
if (paused_out) |
||||||
|
*paused_out = paused; |
||||||
|
|
||||||
|
err_reply: |
||||||
|
dbus_message_unref(reply); |
||||||
|
err_unref: |
||||||
|
dbus_message_unref(m); |
||||||
|
return r; |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
weston_logind_release_device(struct weston_logind *wl, uint32_t major, |
||||||
|
uint32_t minor) |
||||||
|
{ |
||||||
|
DBusMessage *m; |
||||||
|
bool b; |
||||||
|
|
||||||
|
m = dbus_message_new_method_call("org.freedesktop.login1", |
||||||
|
wl->spath, |
||||||
|
"org.freedesktop.login1.Session", |
||||||
|
"ReleaseDevice"); |
||||||
|
if (m) { |
||||||
|
b = dbus_message_append_args(m, |
||||||
|
DBUS_TYPE_UINT32, &major, |
||||||
|
DBUS_TYPE_UINT32, &minor, |
||||||
|
DBUS_TYPE_INVALID); |
||||||
|
if (b) |
||||||
|
dbus_connection_send(wl->dbus, m, NULL); |
||||||
|
dbus_message_unref(m); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
weston_logind_pause_device_complete(struct weston_logind *wl, uint32_t major, |
||||||
|
uint32_t minor) |
||||||
|
{ |
||||||
|
DBusMessage *m; |
||||||
|
bool b; |
||||||
|
|
||||||
|
m = dbus_message_new_method_call("org.freedesktop.login1", |
||||||
|
wl->spath, |
||||||
|
"org.freedesktop.login1.Session", |
||||||
|
"PauseDeviceComplete"); |
||||||
|
if (m) { |
||||||
|
b = dbus_message_append_args(m, |
||||||
|
DBUS_TYPE_UINT32, &major, |
||||||
|
DBUS_TYPE_UINT32, &minor, |
||||||
|
DBUS_TYPE_INVALID); |
||||||
|
if (b) |
||||||
|
dbus_connection_send(wl->dbus, m, NULL); |
||||||
|
dbus_message_unref(m); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
WL_EXPORT int |
||||||
|
weston_logind_open(struct weston_logind *wl, const char *path, |
||||||
|
int flags) |
||||||
|
{ |
||||||
|
struct stat st; |
||||||
|
int fl, r, fd; |
||||||
|
|
||||||
|
r = stat(path, &st); |
||||||
|
if (r < 0) |
||||||
|
return -errno; |
||||||
|
if (!S_ISCHR(st.st_mode)) |
||||||
|
return -ENODEV; |
||||||
|
|
||||||
|
fd = weston_logind_take_device(wl, major(st.st_rdev), |
||||||
|
minor(st.st_rdev), NULL); |
||||||
|
if (fd < 0) |
||||||
|
return fd; |
||||||
|
|
||||||
|
/* Compared to weston_launcher_open() we cannot specify the open-mode
|
||||||
|
* directly. Instead, logind passes us an fd with sane default modes. |
||||||
|
* For DRM and evdev this means O_RDWR | O_CLOEXEC. If we want |
||||||
|
* something else, we need to change it afterwards. We currently |
||||||
|
* only support dropping FD_CLOEXEC and setting O_NONBLOCK. Changing |
||||||
|
* access-modes is not possible so accept whatever logind passes us. */ |
||||||
|
|
||||||
|
fl = fcntl(fd, F_GETFL); |
||||||
|
if (fl < 0) { |
||||||
|
r = -errno; |
||||||
|
goto err_close; |
||||||
|
} |
||||||
|
|
||||||
|
if (flags & O_NONBLOCK) |
||||||
|
fl |= O_NONBLOCK; |
||||||
|
|
||||||
|
r = fcntl(fd, F_SETFL, fl); |
||||||
|
if (r < 0) { |
||||||
|
r = -errno; |
||||||
|
goto err_close; |
||||||
|
} |
||||||
|
|
||||||
|
fl = fcntl(fd, F_GETFD); |
||||||
|
if (fl < 0) { |
||||||
|
r = -errno; |
||||||
|
goto err_close; |
||||||
|
} |
||||||
|
|
||||||
|
if (!(flags & O_CLOEXEC)) |
||||||
|
fl &= ~FD_CLOEXEC; |
||||||
|
|
||||||
|
r = fcntl(fd, F_SETFD, fl); |
||||||
|
if (r < 0) { |
||||||
|
r = -errno; |
||||||
|
goto err_close; |
||||||
|
} |
||||||
|
|
||||||
|
return fd; |
||||||
|
|
||||||
|
err_close: |
||||||
|
close(fd); |
||||||
|
weston_logind_release_device(wl, major(st.st_rdev), |
||||||
|
minor(st.st_rdev)); |
||||||
|
return r; |
||||||
|
} |
||||||
|
|
||||||
|
WL_EXPORT void |
||||||
|
weston_logind_close(struct weston_logind *wl, int fd) |
||||||
|
{ |
||||||
|
struct stat st; |
||||||
|
int r; |
||||||
|
|
||||||
|
r = fstat(fd, &st); |
||||||
|
if (r < 0) { |
||||||
|
weston_log("logind: cannot fstat fd: %m\n"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (!S_ISCHR(st.st_mode)) { |
||||||
|
weston_log("logind: invalid device passed\n"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
weston_logind_release_device(wl, major(st.st_rdev), |
||||||
|
minor(st.st_rdev)); |
||||||
|
} |
||||||
|
|
||||||
|
WL_EXPORT void |
||||||
|
weston_logind_restore(struct weston_logind *wl) |
||||||
|
{ |
||||||
|
struct vt_mode mode = { 0 }; |
||||||
|
|
||||||
|
ioctl(wl->vt, KDSETMODE, KD_TEXT); |
||||||
|
ioctl(wl->vt, KDSKBMUTE, 0); |
||||||
|
ioctl(wl->vt, KDSKBMODE, wl->kb_mode); |
||||||
|
mode.mode = VT_AUTO; |
||||||
|
ioctl(wl->vt, VT_SETMODE, &mode); |
||||||
|
} |
||||||
|
|
||||||
|
WL_EXPORT int |
||||||
|
weston_logind_activate_vt(struct weston_logind *wl, int vt) |
||||||
|
{ |
||||||
|
int r; |
||||||
|
|
||||||
|
r = ioctl(wl->vt, VT_ACTIVATE, vt); |
||||||
|
if (r < 0) |
||||||
|
return -errno; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
weston_logind_set_active(struct weston_logind *wl, bool active) |
||||||
|
{ |
||||||
|
if (active) |
||||||
|
wl->compositor->session_active = 1; |
||||||
|
else |
||||||
|
wl->compositor->session_active = 0; |
||||||
|
|
||||||
|
wl_signal_emit(&wl->compositor->session_signal, |
||||||
|
wl->compositor); |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
get_active_cb(DBusPendingCall *pending, void *data) |
||||||
|
{ |
||||||
|
struct weston_logind *wl = data; |
||||||
|
DBusMessage *m; |
||||||
|
DBusMessageIter iter, sub; |
||||||
|
int type; |
||||||
|
bool b; |
||||||
|
|
||||||
|
dbus_pending_call_unref(wl->pending_active); |
||||||
|
wl->pending_active = NULL; |
||||||
|
|
||||||
|
m = dbus_pending_call_steal_reply(pending); |
||||||
|
if (!m) |
||||||
|
return; |
||||||
|
|
||||||
|
type = dbus_message_get_type(m); |
||||||
|
if (type != DBUS_MESSAGE_TYPE_METHOD_RETURN) |
||||||
|
goto err_unref; |
||||||
|
|
||||||
|
if (!dbus_message_iter_init(m, &iter) || |
||||||
|
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) |
||||||
|
goto err_unref; |
||||||
|
|
||||||
|
dbus_message_iter_recurse(&iter, &sub); |
||||||
|
|
||||||
|
if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN) |
||||||
|
goto err_unref; |
||||||
|
|
||||||
|
dbus_message_iter_get_basic(&sub, &b); |
||||||
|
weston_logind_set_active(wl, b); |
||||||
|
|
||||||
|
err_unref: |
||||||
|
dbus_message_unref(m); |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
weston_logind_get_active(struct weston_logind *wl) |
||||||
|
{ |
||||||
|
DBusPendingCall *pending; |
||||||
|
DBusMessage *m; |
||||||
|
bool b; |
||||||
|
const char *iface, *name; |
||||||
|
|
||||||
|
m = dbus_message_new_method_call("org.freedesktop.login1", |
||||||
|
wl->spath, |
||||||
|
"org.freedesktop.DBus.Properties", |
||||||
|
"Get"); |
||||||
|
if (!m) |
||||||
|
return; |
||||||
|
|
||||||
|
iface = "org.freedesktop.login1.Session"; |
||||||
|
name = "Active"; |
||||||
|
b = dbus_message_append_args(m, |
||||||
|
DBUS_TYPE_STRING, &iface, |
||||||
|
DBUS_TYPE_STRING, &name, |
||||||
|
DBUS_TYPE_INVALID); |
||||||
|
if (!b) |
||||||
|
goto err_unref; |
||||||
|
|
||||||
|
b = dbus_connection_send_with_reply(wl->dbus, m, &pending, -1); |
||||||
|
if (!b) |
||||||
|
goto err_unref; |
||||||
|
|
||||||
|
b = dbus_pending_call_set_notify(pending, get_active_cb, wl, NULL); |
||||||
|
if (!b) { |
||||||
|
dbus_pending_call_cancel(pending); |
||||||
|
dbus_pending_call_unref(pending); |
||||||
|
goto err_unref; |
||||||
|
} |
||||||
|
|
||||||
|
if (wl->pending_active) { |
||||||
|
dbus_pending_call_cancel(wl->pending_active); |
||||||
|
dbus_pending_call_unref(wl->pending_active); |
||||||
|
} |
||||||
|
wl->pending_active = pending; |
||||||
|
return; |
||||||
|
|
||||||
|
err_unref: |
||||||
|
dbus_message_unref(m); |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
disconnected_dbus(struct weston_logind *wl) |
||||||
|
{ |
||||||
|
weston_log("logind: dbus connection lost, exiting..\n"); |
||||||
|
weston_logind_restore(wl); |
||||||
|
exit(-1); |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
session_removed(struct weston_logind *wl, DBusMessage *m) |
||||||
|
{ |
||||||
|
const char *name, *obj; |
||||||
|
bool r; |
||||||
|
|
||||||
|
r = dbus_message_get_args(m, NULL, |
||||||
|
DBUS_TYPE_STRING, &name, |
||||||
|
DBUS_TYPE_OBJECT_PATH, &obj, |
||||||
|
DBUS_TYPE_INVALID); |
||||||
|
if (!r) { |
||||||
|
weston_log("logind: cannot parse SessionRemoved dbus signal\n"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (!strcmp(name, wl->sid)) { |
||||||
|
weston_log("logind: our session got closed, exiting..\n"); |
||||||
|
weston_logind_restore(wl); |
||||||
|
exit(-1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
property_changed(struct weston_logind *wl, DBusMessage *m) |
||||||
|
{ |
||||||
|
DBusMessageIter iter, sub, entry; |
||||||
|
const char *interface, *name; |
||||||
|
dbus_bool_t b; |
||||||
|
|
||||||
|
if (!dbus_message_iter_init(m, &iter) || |
||||||
|
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) |
||||||
|
goto error; |
||||||
|
|
||||||
|
dbus_message_iter_get_basic(&iter, &interface); |
||||||
|
|
||||||
|
if (!dbus_message_iter_next(&iter) || |
||||||
|
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) |
||||||
|
goto error; |
||||||
|
|
||||||
|
dbus_message_iter_recurse(&iter, &sub); |
||||||
|
|
||||||
|
while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY) { |
||||||
|
dbus_message_iter_recurse(&sub, &entry); |
||||||
|
|
||||||
|
if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) |
||||||
|
goto error; |
||||||
|
|
||||||
|
dbus_message_iter_get_basic(&entry, &name); |
||||||
|
if (!dbus_message_iter_next(&entry)) |
||||||
|
goto error; |
||||||
|
|
||||||
|
if (!strcmp(name, "Active")) { |
||||||
|
if (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_BOOLEAN) { |
||||||
|
dbus_message_iter_get_basic(&entry, &b); |
||||||
|
weston_logind_set_active(wl, b); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
dbus_message_iter_next(&sub); |
||||||
|
} |
||||||
|
|
||||||
|
if (!dbus_message_iter_next(&iter) || |
||||||
|
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) |
||||||
|
goto error; |
||||||
|
|
||||||
|
dbus_message_iter_recurse(&iter, &sub); |
||||||
|
|
||||||
|
while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) { |
||||||
|
dbus_message_iter_get_basic(&sub, &name); |
||||||
|
|
||||||
|
if (!strcmp(name, "Active")) { |
||||||
|
weston_logind_get_active(wl); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
dbus_message_iter_next(&sub); |
||||||
|
} |
||||||
|
|
||||||
|
return; |
||||||
|
|
||||||
|
error: |
||||||
|
weston_log("logind: cannot parse PropertiesChanged dbus signal\n"); |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
device_paused(struct weston_logind *wl, DBusMessage *m) |
||||||
|
{ |
||||||
|
bool r; |
||||||
|
const char *type; |
||||||
|
uint32_t major, minor; |
||||||
|
|
||||||
|
r = dbus_message_get_args(m, NULL, |
||||||
|
DBUS_TYPE_UINT32, &major, |
||||||
|
DBUS_TYPE_UINT32, &minor, |
||||||
|
DBUS_TYPE_STRING, &type, |
||||||
|
DBUS_TYPE_INVALID); |
||||||
|
if (!r) { |
||||||
|
weston_log("logind: cannot parse PauseDevice dbus signal\n"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
/* "pause" means synchronous pausing. Acknowledge it unconditionally
|
||||||
|
* as we support asynchronous device shutdowns, anyway. |
||||||
|
* "force" means asynchronous pausing. We ignore it as the following |
||||||
|
* session-deactivation will suffice as notification. |
||||||
|
* "gone" means the device is gone. We ignore it as we receive a |
||||||
|
* udev notification, anyway. */ |
||||||
|
|
||||||
|
if (!strcmp(type, "pause")) |
||||||
|
weston_logind_pause_device_complete(wl, major, minor); |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
device_resumed(struct weston_logind *wl, DBusMessage *m) |
||||||
|
{ |
||||||
|
/*
|
||||||
|
* DeviceResumed messages provide us a new file-descriptor for |
||||||
|
* resumed devices. For DRM devices it's the same as before, for evdev |
||||||
|
* devices it's a new open-file. As we reopen evdev devices, anyway, |
||||||
|
* there is no need for us to handle this event. If we ever optimize |
||||||
|
* our evdev code to support resuming devices, this event can be |
||||||
|
* parsed like this: |
||||||
|
* r = dbus_message_get_args(m, NULL, |
||||||
|
* DBUS_TYPE_UINT32, &major, |
||||||
|
* DBUS_TYPE_UINT32, &minor, |
||||||
|
* DBUS_TYPE_UNIX_FD, &fd, |
||||||
|
* DBUS_TYPE_INVALID); |
||||||
|
*/ |
||||||
|
} |
||||||
|
|
||||||
|
static DBusHandlerResult |
||||||
|
filter_dbus(DBusConnection *c, DBusMessage *m, void *data) |
||||||
|
{ |
||||||
|
struct weston_logind *wl = data; |
||||||
|
|
||||||
|
if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected")) { |
||||||
|
disconnected_dbus(wl); |
||||||
|
} else if (dbus_message_is_signal(m, "org.freedesktop.login1.Manager", |
||||||
|
"SessionRemoved")) { |
||||||
|
session_removed(wl, m); |
||||||
|
} else if (dbus_message_is_signal(m, "org.freedesktop.DBus.Properties", |
||||||
|
"PropertiesChanged")) { |
||||||
|
property_changed(wl, m); |
||||||
|
} else if (dbus_message_is_signal(m, "org.freedesktop.login1.Session", |
||||||
|
"PauseDevice")) { |
||||||
|
device_paused(wl, m); |
||||||
|
} else if (dbus_message_is_signal(m, "org.freedesktop.login1.Session", |
||||||
|
"ResumeDevice")) { |
||||||
|
device_resumed(wl, m); |
||||||
|
} |
||||||
|
|
||||||
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
||||||
|
} |
||||||
|
|
||||||
|
static int |
||||||
|
weston_logind_setup_dbus(struct weston_logind *wl) |
||||||
|
{ |
||||||
|
bool b; |
||||||
|
int r; |
||||||
|
|
||||||
|
r = asprintf(&wl->spath, "/org/freedesktop/login1/session/%s", |
||||||
|
wl->sid); |
||||||
|
if (r < 0) |
||||||
|
return -ENOMEM; |
||||||
|
|
||||||
|
b = dbus_connection_add_filter(wl->dbus, filter_dbus, wl, NULL); |
||||||
|
if (!b) { |
||||||
|
weston_log("logind: cannot add dbus filter\n"); |
||||||
|
r = -ENOMEM; |
||||||
|
goto err_spath; |
||||||
|
} |
||||||
|
|
||||||
|
r = weston_dbus_add_match_signal(wl->dbus, |
||||||
|
"org.freedesktop.login1", |
||||||
|
"org.freedesktop.login1.Manager", |
||||||
|
"SessionRemoved", |
||||||
|
"/org/freedesktop/login1"); |
||||||
|
if (r < 0) { |
||||||
|
weston_log("logind: cannot add dbus match\n"); |
||||||
|
goto err_spath; |
||||||
|
} |
||||||
|
|
||||||
|
r = weston_dbus_add_match_signal(wl->dbus, |
||||||
|
"org.freedesktop.login1", |
||||||
|
"org.freedesktop.login1.Session", |
||||||
|
"PauseDevice", |
||||||
|
wl->spath); |
||||||
|
if (r < 0) { |
||||||
|
weston_log("logind: cannot add dbus match\n"); |
||||||
|
goto err_spath; |
||||||
|
} |
||||||
|
|
||||||
|
r = weston_dbus_add_match_signal(wl->dbus, |
||||||
|
"org.freedesktop.login1", |
||||||
|
"org.freedesktop.login1.Session", |
||||||
|
"ResumeDevice", |
||||||
|
wl->spath); |
||||||
|
if (r < 0) { |
||||||
|
weston_log("logind: cannot add dbus match\n"); |
||||||
|
goto err_spath; |
||||||
|
} |
||||||
|
|
||||||
|
r = weston_dbus_add_match_signal(wl->dbus, |
||||||
|
"org.freedesktop.login1", |
||||||
|
"org.freedesktop.DBus.Properties", |
||||||
|
"PropertiesChanged", |
||||||
|
wl->spath); |
||||||
|
if (r < 0) { |
||||||
|
weston_log("logind: cannot add dbus match\n"); |
||||||
|
goto err_spath; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
|
||||||
|
err_spath: |
||||||
|
/* don't remove any dbus-match as the connection is closed, anyway */ |
||||||
|
free(wl->spath); |
||||||
|
return r; |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
weston_logind_destroy_dbus(struct weston_logind *wl) |
||||||
|
{ |
||||||
|
/* don't remove any dbus-match as the connection is closed, anyway */ |
||||||
|
free(wl->spath); |
||||||
|
} |
||||||
|
|
||||||
|
static int |
||||||
|
weston_logind_take_control(struct weston_logind *wl) |
||||||
|
{ |
||||||
|
DBusError err; |
||||||
|
DBusMessage *m, *reply; |
||||||
|
dbus_bool_t force; |
||||||
|
bool b; |
||||||
|
int r; |
||||||
|
|
||||||
|
dbus_error_init(&err); |
||||||
|
|
||||||
|
m = dbus_message_new_method_call("org.freedesktop.login1", |
||||||
|
wl->spath, |
||||||
|
"org.freedesktop.login1.Session", |
||||||
|
"TakeControl"); |
||||||
|
if (!m) |
||||||
|
return -ENOMEM; |
||||||
|
|
||||||
|
force = false; |
||||||
|
b = dbus_message_append_args(m, |
||||||
|
DBUS_TYPE_BOOLEAN, &force, |
||||||
|
DBUS_TYPE_INVALID); |
||||||
|
if (!b) { |
||||||
|
r = -ENOMEM; |
||||||
|
goto err_unref; |
||||||
|
} |
||||||
|
|
||||||
|
reply = dbus_connection_send_with_reply_and_block(wl->dbus, |
||||||
|
m, -1, &err); |
||||||
|
if (!reply) { |
||||||
|
if (dbus_error_has_name(&err, DBUS_ERROR_UNKNOWN_METHOD)) |
||||||
|
weston_log("logind: old systemd version detected\n"); |
||||||
|
else |
||||||
|
weston_log("logind: cannot take control over session %s\n", wl->sid); |
||||||
|
|
||||||
|
dbus_error_free(&err); |
||||||
|
r = -EIO; |
||||||
|
goto err_unref; |
||||||
|
} |
||||||
|
|
||||||
|
dbus_message_unref(reply); |
||||||
|
dbus_message_unref(m); |
||||||
|
return 0; |
||||||
|
|
||||||
|
err_unref: |
||||||
|
dbus_message_unref(m); |
||||||
|
return r; |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
weston_logind_release_control(struct weston_logind *wl) |
||||||
|
{ |
||||||
|
DBusMessage *m; |
||||||
|
|
||||||
|
m = dbus_message_new_method_call("org.freedesktop.login1", |
||||||
|
wl->spath, |
||||||
|
"org.freedesktop.login1.Session", |
||||||
|
"ReleaseControl"); |
||||||
|
if (m) { |
||||||
|
dbus_connection_send(wl->dbus, m, NULL); |
||||||
|
dbus_message_unref(m); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static int |
||||||
|
signal_event(int fd, uint32_t mask, void *data) |
||||||
|
{ |
||||||
|
struct weston_logind *wl = data; |
||||||
|
struct signalfd_siginfo sig; |
||||||
|
|
||||||
|
if (read(fd, &sig, sizeof sig) != sizeof sig) { |
||||||
|
weston_log("logind: cannot read signalfd: %m\n"); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
switch (sig.ssi_signo) { |
||||||
|
case SIGUSR1: |
||||||
|
ioctl(wl->vt, VT_RELDISP, 1); |
||||||
|
break; |
||||||
|
case SIGUSR2: |
||||||
|
ioctl(wl->vt, VT_RELDISP, VT_ACKACQ); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int |
||||||
|
weston_logind_setup_vt(struct weston_logind *wl) |
||||||
|
{ |
||||||
|
struct stat st; |
||||||
|
char buf[64]; |
||||||
|
struct vt_mode mode = { 0 }; |
||||||
|
int r; |
||||||
|
sigset_t mask; |
||||||
|
struct wl_event_loop *loop; |
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "/dev/tty%d", wl->vtnr); |
||||||
|
buf[sizeof(buf) - 1] = 0; |
||||||
|
|
||||||
|
wl->vt = open(buf, O_RDWR|O_CLOEXEC|O_NONBLOCK); |
||||||
|
|
||||||
|
if (wl->vt < 0) { |
||||||
|
r = -errno; |
||||||
|
weston_log("logind: cannot open VT %s: %m\n", buf); |
||||||
|
return r; |
||||||
|
} |
||||||
|
|
||||||
|
if (fstat(wl->vt, &st) == -1 || |
||||||
|
major(st.st_rdev) != TTY_MAJOR || minor(st.st_rdev) <= 0 || |
||||||
|
minor(st.st_rdev) >= 64) { |
||||||
|
r = -EINVAL; |
||||||
|
weston_log("logind: TTY %s is no virtual terminal\n", buf); |
||||||
|
goto err_close; |
||||||
|
} |
||||||
|
|
||||||
|
/*r = setsid();
|
||||||
|
if (r < 0 && errno != EPERM) { |
||||||
|
r = -errno; |
||||||
|
weston_log("logind: setsid() failed: %m\n"); |
||||||
|
goto err_close; |
||||||
|
} |
||||||
|
|
||||||
|
r = ioctl(wl->vt, TIOCSCTTY, 0); |
||||||
|
if (r < 0) |
||||||
|
weston_log("logind: VT %s already in use\n", buf);*/ |
||||||
|
|
||||||
|
if (ioctl(wl->vt, KDGKBMODE, &wl->kb_mode) < 0) { |
||||||
|
weston_log("logind: cannot read keyboard mode on %s: %m\n", |
||||||
|
buf); |
||||||
|
wl->kb_mode = K_UNICODE; |
||||||
|
} else if (wl->kb_mode == K_OFF) { |
||||||
|
wl->kb_mode = K_UNICODE; |
||||||
|
} |
||||||
|
|
||||||
|
if (ioctl(wl->vt, KDSKBMUTE, 1) < 0 && |
||||||
|
ioctl(wl->vt, KDSKBMODE, K_OFF) < 0) { |
||||||
|
r = -errno; |
||||||
|
weston_log("logind: cannot set K_OFF KB-mode on %s: %m\n", |
||||||
|
buf); |
||||||
|
goto err_close; |
||||||
|
} |
||||||
|
|
||||||
|
if (ioctl(wl->vt, KDSETMODE, KD_GRAPHICS) < 0) { |
||||||
|
r = -errno; |
||||||
|
weston_log("logind: cannot set KD_GRAPHICS mode on %s: %m\n", |
||||||
|
buf); |
||||||
|
goto err_kbmode; |
||||||
|
} |
||||||
|
|
||||||
|
sigemptyset(&mask); |
||||||
|
sigaddset(&mask, SIGUSR1); |
||||||
|
sigaddset(&mask, SIGUSR2); |
||||||
|
sigprocmask(SIG_BLOCK, &mask, NULL); |
||||||
|
|
||||||
|
wl->sfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC); |
||||||
|
if (wl->sfd < 0) { |
||||||
|
r = -errno; |
||||||
|
weston_log("logind: cannot create signalfd: %m\n"); |
||||||
|
goto err_mode; |
||||||
|
} |
||||||
|
|
||||||
|
loop = wl_display_get_event_loop(wl->compositor->wl_display); |
||||||
|
wl->sfd_source = wl_event_loop_add_fd(loop, wl->sfd, |
||||||
|
WL_EVENT_READABLE, |
||||||
|
signal_event, wl); |
||||||
|
if (!wl->sfd_source) { |
||||||
|
r = -errno; |
||||||
|
weston_log("logind: cannot create signalfd source: %m\n"); |
||||||
|
goto err_sfd; |
||||||
|
} |
||||||
|
|
||||||
|
mode.mode = VT_PROCESS; |
||||||
|
mode.relsig = SIGUSR1; |
||||||
|
mode.acqsig = SIGUSR2; |
||||||
|
if (ioctl(wl->vt, VT_SETMODE, &mode) < 0) { |
||||||
|
r = -errno; |
||||||
|
weston_log("logind: cannot take over VT: %m\n"); |
||||||
|
goto err_sfd_source; |
||||||
|
} |
||||||
|
|
||||||
|
weston_log("logind: using VT %s\n", buf); |
||||||
|
return 0; |
||||||
|
|
||||||
|
err_sfd_source: |
||||||
|
wl_event_source_remove(wl->sfd_source); |
||||||
|
err_sfd: |
||||||
|
close(wl->sfd); |
||||||
|
err_mode: |
||||||
|
ioctl(wl->vt, KDSETMODE, KD_TEXT); |
||||||
|
err_kbmode: |
||||||
|
ioctl(wl->vt, KDSKBMUTE, 0); |
||||||
|
ioctl(wl->vt, KDSKBMODE, wl->kb_mode); |
||||||
|
err_close: |
||||||
|
close(wl->vt); |
||||||
|
return r; |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
weston_logind_destroy_vt(struct weston_logind *wl) |
||||||
|
{ |
||||||
|
weston_logind_restore(wl); |
||||||
|
wl_event_source_remove(wl->sfd_source); |
||||||
|
close(wl->sfd); |
||||||
|
close(wl->vt); |
||||||
|
} |
||||||
|
|
||||||
|
WL_EXPORT int |
||||||
|
weston_logind_connect(struct weston_logind **out, |
||||||
|
struct weston_compositor *compositor, |
||||||
|
const char *seat_id, int tty) |
||||||
|
{ |
||||||
|
struct weston_logind *wl; |
||||||
|
struct wl_event_loop *loop; |
||||||
|
char *t; |
||||||
|
int r; |
||||||
|
|
||||||
|
wl = calloc(1, sizeof(*wl)); |
||||||
|
if (!wl) { |
||||||
|
r = -ENOMEM; |
||||||
|
goto err_out; |
||||||
|
} |
||||||
|
|
||||||
|
wl->compositor = compositor; |
||||||
|
|
||||||
|
wl->seat = strdup(seat_id); |
||||||
|
if (!wl->seat) { |
||||||
|
r = -ENOMEM; |
||||||
|
goto err_wl; |
||||||
|
} |
||||||
|
|
||||||
|
r = sd_pid_get_session(getpid(), &wl->sid); |
||||||
|
if (r < 0) { |
||||||
|
weston_log("logind: not running in a systemd session\n"); |
||||||
|
goto err_seat; |
||||||
|
} |
||||||
|
|
||||||
|
t = NULL; |
||||||
|
r = sd_session_get_seat(wl->sid, &t); |
||||||
|
if (r < 0 || strcmp(seat_id, t)) { |
||||||
|
weston_log("logind: weston's seat '%s' differs from session-seat '%s'\n", |
||||||
|
seat_id, t); |
||||||
|
free(t); |
||||||
|
goto err_session; |
||||||
|
} |
||||||
|
free(t); |
||||||
|
|
||||||
|
r = weston_sd_session_get_vt(wl->sid, &wl->vtnr); |
||||||
|
if (r < 0) { |
||||||
|
weston_log("logind: session not running on a VT\n"); |
||||||
|
goto err_session; |
||||||
|
} else if (tty > 0 && wl->vtnr != (unsigned int )tty) { |
||||||
|
weston_log("logind: requested VT --tty=%d differs from real session VT %u\n", |
||||||
|
tty, wl->vtnr); |
||||||
|
goto err_session; |
||||||
|
} |
||||||
|
|
||||||
|
loop = wl_display_get_event_loop(compositor->wl_display); |
||||||
|
r = weston_dbus_open(loop, DBUS_BUS_SYSTEM, &wl->dbus, &wl->dbus_ctx); |
||||||
|
if (r < 0) { |
||||||
|
weston_log("logind: cannot connect to system dbus\n"); |
||||||
|
goto err_session; |
||||||
|
} |
||||||
|
|
||||||
|
r = weston_logind_setup_dbus(wl); |
||||||
|
if (r < 0) |
||||||
|
goto err_dbus; |
||||||
|
|
||||||
|
r = weston_logind_take_control(wl); |
||||||
|
if (r < 0) |
||||||
|
goto err_dbus_cleanup; |
||||||
|
|
||||||
|
r = weston_logind_setup_vt(wl); |
||||||
|
if (r < 0) |
||||||
|
goto err_control; |
||||||
|
|
||||||
|
weston_log("logind: session control granted\n"); |
||||||
|
*out = wl; |
||||||
|
return 0; |
||||||
|
|
||||||
|
err_control: |
||||||
|
weston_logind_release_control(wl); |
||||||
|
err_dbus_cleanup: |
||||||
|
weston_logind_destroy_dbus(wl); |
||||||
|
err_dbus: |
||||||
|
weston_dbus_close(wl->dbus, wl->dbus_ctx); |
||||||
|
err_session: |
||||||
|
free(wl->sid); |
||||||
|
err_seat: |
||||||
|
free(wl->seat); |
||||||
|
err_wl: |
||||||
|
free(wl); |
||||||
|
err_out: |
||||||
|
weston_log("logind: cannot setup systemd-logind helper (%d), using legacy fallback\n", r); |
||||||
|
return r; |
||||||
|
} |
||||||
|
|
||||||
|
WL_EXPORT void |
||||||
|
weston_logind_destroy(struct weston_logind *wl) |
||||||
|
{ |
||||||
|
if (wl->pending_active) { |
||||||
|
dbus_pending_call_cancel(wl->pending_active); |
||||||
|
dbus_pending_call_unref(wl->pending_active); |
||||||
|
} |
||||||
|
|
||||||
|
weston_logind_destroy_vt(wl); |
||||||
|
weston_logind_release_control(wl); |
||||||
|
weston_logind_destroy_dbus(wl); |
||||||
|
weston_dbus_close(wl->dbus, wl->dbus_ctx); |
||||||
|
free(wl->sid); |
||||||
|
free(wl->seat); |
||||||
|
free(wl); |
||||||
|
} |
@ -0,0 +1,120 @@ |
|||||||
|
/*
|
||||||
|
* Copyright © 2013 David Herrmann |
||||||
|
* |
||||||
|
* Permission to use, copy, modify, distribute, and sell this software and |
||||||
|
* its documentation for any purpose is hereby granted without fee, provided |
||||||
|
* that the above copyright notice appear in all copies and that both that |
||||||
|
* copyright notice and this permission notice appear in supporting |
||||||
|
* documentation, and that the name of the copyright holders not be used in |
||||||
|
* advertising or publicity pertaining to distribution of the software |
||||||
|
* without specific, written prior permission. The copyright holders make |
||||||
|
* no representations about the suitability of this software for any |
||||||
|
* purpose. It is provided "as is" without express or implied warranty. |
||||||
|
* |
||||||
|
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS |
||||||
|
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND |
||||||
|
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY |
||||||
|
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER |
||||||
|
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF |
||||||
|
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
||||||
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "config.h" |
||||||
|
|
||||||
|
#include <errno.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include "compositor.h" |
||||||
|
|
||||||
|
struct weston_logind; |
||||||
|
|
||||||
|
#if defined(HAVE_SYSTEMD_LOGIN) && defined(HAVE_DBUS) |
||||||
|
|
||||||
|
#include <systemd/sd-login.h> |
||||||
|
|
||||||
|
int |
||||||
|
weston_logind_open(struct weston_logind *wl, const char *path, |
||||||
|
int flags); |
||||||
|
|
||||||
|
void |
||||||
|
weston_logind_close(struct weston_logind *wl, int fd); |
||||||
|
|
||||||
|
void |
||||||
|
weston_logind_restore(struct weston_logind *wl); |
||||||
|
|
||||||
|
int |
||||||
|
weston_logind_activate_vt(struct weston_logind *wl, int vt); |
||||||
|
|
||||||
|
int |
||||||
|
weston_logind_connect(struct weston_logind **out, |
||||||
|
struct weston_compositor *compositor, |
||||||
|
const char *seat_id, int tty); |
||||||
|
|
||||||
|
void |
||||||
|
weston_logind_destroy(struct weston_logind *wl); |
||||||
|
|
||||||
|
static inline int |
||||||
|
weston_sd_session_get_vt(const char *sid, unsigned int *out) |
||||||
|
{ |
||||||
|
#ifdef HAVE_SYSTEMD_LOGIN_209 |
||||||
|
return sd_session_get_vt(sid, out); |
||||||
|
#else |
||||||
|
int r; |
||||||
|
char *tty; |
||||||
|
|
||||||
|
r = sd_session_get_tty(sid, &tty); |
||||||
|
if (r < 0) |
||||||
|
return r; |
||||||
|
|
||||||
|
r = sscanf(tty, "tty%u", out); |
||||||
|
free(tty); |
||||||
|
|
||||||
|
if (r != 1) |
||||||
|
return -EINVAL; |
||||||
|
|
||||||
|
return 0; |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
#else /* defined(HAVE_SYSTEMD_LOGIN) && defined(HAVE_DBUS) */ |
||||||
|
|
||||||
|
static inline int |
||||||
|
weston_logind_open(struct weston_logind *wl, const char *path, |
||||||
|
int flags) |
||||||
|
{ |
||||||
|
return -ENOSYS; |
||||||
|
} |
||||||
|
|
||||||
|
static inline void |
||||||
|
weston_logind_close(struct weston_logind *wl, int fd) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
static inline void |
||||||
|
weston_logind_restore(struct weston_logind *wl) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
static inline int |
||||||
|
weston_logind_activate_vt(struct weston_logind *wl, int vt) |
||||||
|
{ |
||||||
|
return -ENOSYS; |
||||||
|
} |
||||||
|
|
||||||
|
static inline int |
||||||
|
weston_logind_connect(struct weston_logind **out, |
||||||
|
struct weston_compositor *compositor, |
||||||
|
const char *seat_id, int tty) |
||||||
|
{ |
||||||
|
return -ENOSYS; |
||||||
|
} |
||||||
|
|
||||||
|
static inline void |
||||||
|
weston_logind_destroy(struct weston_logind *wl) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
#endif /* defined(HAVE_SYSTEMD_LOGIN) && defined(HAVE_DBUS) */ |
Loading…
Reference in new issue