diff options
Diffstat (limited to 'xorg-server/hw/xfree86/os-support/linux')
4 files changed, 588 insertions, 20 deletions
diff --git a/xorg-server/hw/xfree86/os-support/linux/Makefile.am b/xorg-server/hw/xfree86/os-support/linux/Makefile.am index 83e7e0027..1686dc2c1 100644 --- a/xorg-server/hw/xfree86/os-support/linux/Makefile.am +++ b/xorg-server/hw/xfree86/os-support/linux/Makefile.am @@ -21,6 +21,11 @@ APM_SRCS = lnx_apm.c XORG_CFLAGS += -DHAVE_APM endif +if SYSTEMD_LOGIND +LOGIND_SRCS = systemd-logind.c +XORG_CFLAGS += $(DBUS_CFLAGS) +endif + liblinux_la_SOURCES = lnx_init.c lnx_video.c \ lnx_agp.c lnx_kmod.c lnx_bell.c lnx_platform.c \ $(srcdir)/../shared/bios_mmap.c \ @@ -30,6 +35,7 @@ liblinux_la_SOURCES = lnx_init.c lnx_video.c \ $(srcdir)/../shared/sigio.c \ $(ACPI_SRCS) \ $(APM_SRCS) \ + $(LOGIND_SRCS) \ $(PLATFORM_PCI_SUPPORT) AM_CFLAGS = -DUSESTDRES -DHAVE_SYSV_IPC $(DIX_CFLAGS) $(XORG_CFLAGS) $(PLATFORM_DEFINES) diff --git a/xorg-server/hw/xfree86/os-support/linux/lnx_init.c b/xorg-server/hw/xfree86/os-support/linux/lnx_init.c index 46438e655..85709c629 100644 --- a/xorg-server/hw/xfree86/os-support/linux/lnx_init.c +++ b/xorg-server/hw/xfree86/os-support/linux/lnx_init.c @@ -79,20 +79,15 @@ switch_to(int vt, const char *from) void xf86OpenConsole(void) { - int i, fd = -1, ret; + int i, fd = -1, ret, current_vt = -1; struct vt_mode VT; struct vt_stat vts; + struct stat st; MessageType from = X_PROBED; const char *tty0[] = { "/dev/tty0", "/dev/vc/0", NULL }; const char *vcs[] = { "/dev/vc/%d", "/dev/tty%d", NULL }; if (serverGeneration == 1) { - - /* when KeepTty check if we're run with euid==0 */ - if (KeepTty && geteuid() != 0) - FatalError("xf86OpenConsole:" - " Server must be suid root for option \"KeepTTY\"\n"); - /* * setup the virtual terminal manager */ @@ -132,6 +127,22 @@ xf86OpenConsole(void) xf86Msg(from, "using VT number %d\n\n", xf86Info.vtno); + /* Some of stdin / stdout / stderr maybe redirected to a file */ + for (i = STDIN_FILENO; i <= STDERR_FILENO; i++) { + ret = fstat(i, &st); + if (ret == 0 && S_ISCHR(st.st_mode) && major(st.st_rdev) == 4) { + current_vt = minor(st.st_rdev); + break; + } + } + + if (!KeepTty && current_vt == xf86Info.vtno) { + xf86Msg(X_PROBED, + "controlling tty is VT number %d, auto-enabling KeepTty\n", + current_vt); + KeepTty = TRUE; + } + if (!KeepTty) { pid_t ppid = getppid(); pid_t ppgid; diff --git a/xorg-server/hw/xfree86/os-support/linux/lnx_platform.c b/xorg-server/hw/xfree86/os-support/linux/lnx_platform.c index 1865b31b9..109a9a774 100644 --- a/xorg-server/hw/xfree86/os-support/linux/lnx_platform.c +++ b/xorg-server/hw/xfree86/os-support/linux/lnx_platform.c @@ -18,16 +18,38 @@ #include "xf86Bus.h" #include "hotplug.h" +#include "systemd-logind.h" static Bool get_drm_info(struct OdevAttributes *attribs, char *path, int delayed_index) { drmSetVersion sv; char *buf; - int fd; + int major, minor, fd; int err = 0; + Bool paused, server_fd = FALSE; + + major = config_odev_get_int_attribute(attribs, ODEV_ATTRIB_MAJOR, 0); + minor = config_odev_get_int_attribute(attribs, ODEV_ATTRIB_MINOR, 0); + + fd = systemd_logind_take_fd(major, minor, path, &paused); + if (fd != -1) { + if (paused) { + LogMessage(X_ERROR, + "Error systemd-logind returned paused fd for drm node\n"); + systemd_logind_release_fd(major, minor); + return FALSE; + } + if (!config_odev_add_int_attribute(attribs, ODEV_ATTRIB_FD, fd)) { + systemd_logind_release_fd(major, minor); + return FALSE; + } + server_fd = TRUE; + } + + if (fd == -1) + fd = open(path, O_RDWR, O_CLOEXEC); - fd = open(path, O_RDWR, O_CLOEXEC); if (fd == -1) return FALSE; @@ -44,16 +66,20 @@ get_drm_info(struct OdevAttributes *attribs, char *path, int delayed_index) /* for a delayed probe we've already added the device */ if (delayed_index == -1) { - xf86_add_platform_device(attribs); + xf86_add_platform_device(attribs, FALSE); delayed_index = xf86_num_platform_devices - 1; } + if (server_fd) + xf86_platform_devices[delayed_index].flags |= XF86_PDEV_SERVER_FD; + buf = drmGetBusid(fd); xf86_add_platform_device_attrib(delayed_index, ODEV_ATTRIB_BUSID, buf); drmFreeBusid(buf); out: - close(fd); + if (!server_fd) + close(fd); return (err == 0); } @@ -118,17 +144,11 @@ xf86PlatformReprobeDevice(int index, struct OdevAttributes *attribs) void xf86PlatformDeviceProbe(struct OdevAttributes *attribs) { - struct OdevAttribute *attrib; int i; char *path = NULL; Bool ret; - xorg_list_for_each_entry(attrib, &attribs->list, member) { - if (attrib->attrib_id == ODEV_ATTRIB_PATH) { - path = attrib->attrib_name; - break; - } - } + path = config_odev_get_attribute(attribs, ODEV_ATTRIB_PATH); if (!path) goto out_free; @@ -148,8 +168,7 @@ xf86PlatformDeviceProbe(struct OdevAttributes *attribs) if (!xf86VTOwner()) { /* if we don't currently own the VT then don't probe the device, just mark it as unowned for later use */ - attribs->unowned = TRUE; - xf86_add_platform_device(attribs); + xf86_add_platform_device(attribs, TRUE); return; } diff --git a/xorg-server/hw/xfree86/os-support/linux/systemd-logind.c b/xorg-server/hw/xfree86/os-support/linux/systemd-logind.c new file mode 100644 index 000000000..abb8e44d9 --- /dev/null +++ b/xorg-server/hw/xfree86/os-support/linux/systemd-logind.c @@ -0,0 +1,532 @@ +/* + * Copyright © 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Hans de Goede <hdegoede@redhat.com> + */ + +#ifdef HAVE_XORG_CONFIG_H +#include <xorg-config.h> +#endif + +#include <dbus/dbus.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +#include "os.h" +#include "dbus-core.h" +#include "xf86.h" +#include "xf86platformBus.h" +#include "xf86Xinput.h" + +#include "systemd-logind.h" + +#define DBUS_TIMEOUT 500 /* Wait max 0.5 seconds */ + +struct systemd_logind_info { + DBusConnection *conn; + char *session; + Bool active; + Bool vt_active; +}; + +static struct systemd_logind_info logind_info; + +int +systemd_logind_take_fd(int _major, int _minor, const char *path, + Bool *paused_ret) +{ + struct systemd_logind_info *info = &logind_info; + DBusError error; + DBusMessage *msg = NULL; + DBusMessage *reply = NULL; + dbus_int32_t major = _major; + dbus_int32_t minor = _minor; + dbus_bool_t paused; + int fd = -1; + + if (!info->session || major == 0) + return -1; + + /* logind does not support mouse devs (with evdev we don't need them) */ + if (strstr(path, "mouse")) + return -1; + + dbus_error_init(&error); + + msg = dbus_message_new_method_call("org.freedesktop.login1", info->session, + "org.freedesktop.login1.Session", "TakeDevice"); + if (!msg) { + LogMessage(X_ERROR, "systemd-logind: out of memory\n"); + goto cleanup; + } + + if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &major, + DBUS_TYPE_UINT32, &minor, + DBUS_TYPE_INVALID)) { + LogMessage(X_ERROR, "systemd-logind: out of memory\n"); + goto cleanup; + } + + reply = dbus_connection_send_with_reply_and_block(info->conn, msg, + DBUS_TIMEOUT, &error); + if (!reply) { + LogMessage(X_ERROR, "systemd-logind: failed to take device %s: %s\n", + path, error.message); + goto cleanup; + } + + if (!dbus_message_get_args(reply, &error, + DBUS_TYPE_UNIX_FD, &fd, + DBUS_TYPE_BOOLEAN, &paused, + DBUS_TYPE_INVALID)) { + LogMessage(X_ERROR, "systemd-logind: TakeDevice %s: %s\n", + path, error.message); + goto cleanup; + } + + *paused_ret = paused; + + LogMessage(X_INFO, "systemd-logind: got fd for %s %u:%u fd %d paused %d\n", + path, major, minor, fd, paused); + +cleanup: + if (msg) + dbus_message_unref(msg); + if (reply) + dbus_message_unref(reply); + dbus_error_free(&error); + + return fd; +} + +void +systemd_logind_release_fd(int _major, int _minor) +{ + struct systemd_logind_info *info = &logind_info; + DBusError error; + DBusMessage *msg = NULL; + DBusMessage *reply = NULL; + dbus_int32_t major = _major; + dbus_int32_t minor = _minor; + + if (!info->session || major == 0) + return; + + dbus_error_init(&error); + + msg = dbus_message_new_method_call("org.freedesktop.login1", info->session, + "org.freedesktop.login1.Session", "ReleaseDevice"); + if (!msg) { + LogMessage(X_ERROR, "systemd-logind: out of memory\n"); + goto cleanup; + } + + if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &major, + DBUS_TYPE_UINT32, &minor, + DBUS_TYPE_INVALID)) { + LogMessage(X_ERROR, "systemd-logind: out of memory\n"); + goto cleanup; + } + + reply = dbus_connection_send_with_reply_and_block(info->conn, msg, + DBUS_TIMEOUT, &error); + if (!reply) + LogMessage(X_ERROR, "systemd-logind: failed to release device: %s\n", + error.message); + +cleanup: + if (msg) + dbus_message_unref(msg); + if (reply) + dbus_message_unref(reply); + dbus_error_free(&error); +} + +int +systemd_logind_controls_session(void) +{ + return logind_info.session ? 1 : 0; +} + +void +systemd_logind_vtenter(void) +{ + struct systemd_logind_info *info = &logind_info; + InputInfoPtr pInfo; + int i; + + if (!info->session) + return; /* Not using systemd-logind */ + + if (!info->active) + return; /* Session not active */ + + if (info->vt_active) + return; /* Already did vtenter */ + + for (i = 0; i < xf86_num_platform_devices; i++) { + if (xf86_platform_devices[i].flags & XF86_PDEV_PAUSED) + break; + } + if (i != xf86_num_platform_devices) + return; /* Some drm nodes are still paused wait for resume */ + + xf86VTEnter(); + info->vt_active = TRUE; + + /* Activate any input devices which were resumed before the drm nodes */ + for (pInfo = xf86InputDevs; pInfo; pInfo = pInfo->next) + if ((pInfo->flags & XI86_SERVER_FD) && pInfo->fd != -1) + xf86EnableInputDeviceForVTSwitch(pInfo); + + /* Do delayed input probing, this must be done after the above enabling */ + xf86InputEnableVTProbe(); +} + +static InputInfoPtr +systemd_logind_find_info_ptr_by_devnum(int major, int minor) +{ + InputInfoPtr pInfo; + + for (pInfo = xf86InputDevs; pInfo; pInfo = pInfo->next) + if (pInfo->major == major && pInfo->minor == minor && + (pInfo->flags & XI86_SERVER_FD)) + return pInfo; + + return NULL; +} + +static void +systemd_logind_ack_pause(struct systemd_logind_info *info, + dbus_int32_t minor, dbus_int32_t major) +{ + DBusError error; + DBusMessage *msg = NULL; + DBusMessage *reply = NULL; + + dbus_error_init(&error); + + msg = dbus_message_new_method_call("org.freedesktop.login1", info->session, + "org.freedesktop.login1.Session", "PauseDeviceComplete"); + if (!msg) { + LogMessage(X_ERROR, "systemd-logind: out of memory\n"); + goto cleanup; + } + + if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &major, + DBUS_TYPE_UINT32, &minor, + DBUS_TYPE_INVALID)) { + LogMessage(X_ERROR, "systemd-logind: out of memory\n"); + goto cleanup; + } + + reply = dbus_connection_send_with_reply_and_block(info->conn, msg, + DBUS_TIMEOUT, &error); + if (!reply) + LogMessage(X_ERROR, "systemd-logind: failed to ack pause: %s\n", + error.message); + +cleanup: + if (msg) + dbus_message_unref(msg); + if (reply) + dbus_message_unref(reply); + dbus_error_free(&error); +} + +static DBusHandlerResult +message_filter(DBusConnection * connection, DBusMessage * message, void *data) +{ + struct systemd_logind_info *info = data; + struct xf86_platform_device *pdev = NULL; + InputInfoPtr pInfo = NULL; + int ack = 0, pause = 0, fd = -1; + DBusError error; + dbus_int32_t major, minor; + char *pause_str; + + if (strcmp(dbus_message_get_path(message), info->session) != 0) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + dbus_error_init(&error); + + if (dbus_message_is_signal(message, "org.freedesktop.login1.Session", + "PauseDevice")) { + if (!dbus_message_get_args(message, &error, + DBUS_TYPE_UINT32, &major, + DBUS_TYPE_UINT32, &minor, + DBUS_TYPE_STRING, &pause_str, + DBUS_TYPE_INVALID)) { + LogMessage(X_ERROR, "systemd-logind: PauseDevice: %s\n", + error.message); + dbus_error_free(&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + if (strcmp(pause_str, "pause") == 0) { + pause = 1; + ack = 1; + } + else if (strcmp(pause_str, "force") == 0) { + pause = 1; + } + else if (strcmp(pause_str, "gone") == 0) { + /* Device removal is handled through udev */ + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + else { + LogMessage(X_WARNING, "systemd-logind: unknown pause type: %s\n", + pause_str); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + } + else if (dbus_message_is_signal(message, "org.freedesktop.login1.Session", + "ResumeDevice")) { + if (!dbus_message_get_args(message, &error, + DBUS_TYPE_UINT32, &major, + DBUS_TYPE_UINT32, &minor, + DBUS_TYPE_UNIX_FD, &fd, + DBUS_TYPE_INVALID)) { + LogMessage(X_ERROR, "systemd-logind: ResumeDevice: %s\n", + error.message); + dbus_error_free(&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + } else + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + LogMessage(X_INFO, "systemd-logind: got %s for %u:%u\n", + pause ? "pause" : "resume", major, minor); + + pdev = xf86_find_platform_device_by_devnum(major, minor); + if (!pdev) + pInfo = systemd_logind_find_info_ptr_by_devnum(major, minor); + if (!pdev && !pInfo) { + LogMessage(X_WARNING, "systemd-logind: could not find dev %u:%u\n", + major, minor); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + if (pause) { + /* Our VT_PROCESS usage guarantees we've already given up the vt */ + info->active = info->vt_active = FALSE; + /* Note the actual vtleave has already been handled by xf86Events.c */ + if (pdev) + pdev->flags |= XF86_PDEV_PAUSED; + else { + close(pInfo->fd); + pInfo->fd = -1; + pInfo->options = xf86ReplaceIntOption(pInfo->options, "fd", -1); + } + if (ack) + systemd_logind_ack_pause(info, major, minor); + } + else { + /* info->vt_active gets set by systemd_logind_vtenter() */ + info->active = TRUE; + + if (pdev) { + pdev->flags &= ~XF86_PDEV_PAUSED; + systemd_logind_vtenter(); + } + else { + pInfo->fd = fd; + pInfo->options = xf86ReplaceIntOption(pInfo->options, "fd", fd); + if (info->vt_active) + xf86EnableInputDeviceForVTSwitch(pInfo); + } + } + return DBUS_HANDLER_RESULT_HANDLED; +} + +static void +connect_hook(DBusConnection *connection, void *data) +{ + struct systemd_logind_info *info = data; + DBusError error; + DBusMessage *msg = NULL; + DBusMessage *reply = NULL; + dbus_int32_t arg; + char *session = NULL; + + dbus_error_init(&error); + + msg = dbus_message_new_method_call("org.freedesktop.login1", + "/org/freedesktop/login1", "org.freedesktop.login1.Manager", + "GetSessionByPID"); + if (!msg) { + LogMessage(X_ERROR, "systemd-logind: out of memory\n"); + goto cleanup; + } + + arg = getpid(); + if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &arg, + DBUS_TYPE_INVALID)) { + LogMessage(X_ERROR, "systemd-logind: out of memory\n"); + goto cleanup; + } + + reply = dbus_connection_send_with_reply_and_block(connection, msg, + DBUS_TIMEOUT, &error); + if (!reply) { + LogMessage(X_ERROR, "systemd-logind: failed to get session: %s\n", + error.message); + goto cleanup; + } + dbus_message_unref(msg); + + if (!dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &session, + DBUS_TYPE_INVALID)) { + LogMessage(X_ERROR, "systemd-logind: GetSessionByPID: %s\n", + error.message); + goto cleanup; + } + session = XNFstrdup(session); + + dbus_message_unref(reply); + reply = NULL; + + + msg = dbus_message_new_method_call("org.freedesktop.login1", + session, "org.freedesktop.login1.Session", "TakeControl"); + if (!msg) { + LogMessage(X_ERROR, "systemd-logind: out of memory\n"); + goto cleanup; + } + + arg = FALSE; /* Don't forcibly take over over the session */ + if (!dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &arg, + DBUS_TYPE_INVALID)) { + LogMessage(X_ERROR, "systemd-logind: out of memory\n"); + goto cleanup; + } + + reply = dbus_connection_send_with_reply_and_block(connection, msg, + DBUS_TIMEOUT, &error); + if (!reply) { + LogMessage(X_ERROR, "systemd-logind: TakeControl failed: %s\n", + error.message); + goto cleanup; + } + + /* + * HdG: This is not useful with systemd <= 208 since the signal only + * contains invalidated property names there, rather than property, val + * pairs as it should. Instead we just use the first resume / pause now. + */ +#if 0 + snprintf(match, sizeof(match), + "type='signal',sender='org.freedesktop.login1',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path='%s'", + session); + dbus_bus_add_match(connection, match, &error); + if (dbus_error_is_set(&error)) { + LogMessage(X_ERROR, "systemd-logind: could not add match: %s\n", + error.message); + goto cleanup; + } +#endif + + if (!dbus_connection_add_filter(connection, message_filter, info, NULL)) { + LogMessage(X_ERROR, "systemd-logind: could not add filter: %s\n", + error.message); + goto cleanup; + } + + LogMessage(X_INFO, "systemd-logind: took control of session %s\n", + session); + info->conn = connection; + info->session = session; + info->vt_active = info->active = TRUE; /* The server owns the vt during init */ + session = NULL; + +cleanup: + free(session); + if (msg) + dbus_message_unref(msg); + if (reply) + dbus_message_unref(reply); + dbus_error_free(&error); +} + +static void +systemd_logind_release_control(struct systemd_logind_info *info) +{ + DBusError error; + DBusMessage *msg = NULL; + DBusMessage *reply = NULL; + + dbus_error_init(&error); + + msg = dbus_message_new_method_call("org.freedesktop.login1", + info->session, "org.freedesktop.login1.Session", "ReleaseControl"); + if (!msg) { + LogMessage(X_ERROR, "systemd-logind: out of memory\n"); + goto cleanup; + } + + reply = dbus_connection_send_with_reply_and_block(info->conn, msg, + DBUS_TIMEOUT, &error); + if (!reply) { + LogMessage(X_ERROR, "systemd-logind: ReleaseControl failed: %s\n", + error.message); + goto cleanup; + } + +cleanup: + if (msg) + dbus_message_unref(msg); + if (reply) + dbus_message_unref(reply); + dbus_error_free(&error); +} + +static void +disconnect_hook(void *data) +{ + struct systemd_logind_info *info = data; + + free(info->session); + info->session = NULL; + info->conn = NULL; +} + +static struct dbus_core_hook core_hook = { + .connect = connect_hook, + .disconnect = disconnect_hook, + .data = &logind_info, +}; + +int +systemd_logind_init(void) +{ + return dbus_core_add_hook(&core_hook); +} + +void +systemd_logind_fini(void) +{ + if (logind_info.session) + systemd_logind_release_control(&logind_info); + + dbus_core_remove_hook(&core_hook); +} |