-/***
- This file is part of systemd.
-
- Copyright 2013 David Herrmann
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
+/* SPDX-License-Identifier: LGPL-2.1+ */
#include <fcntl.h>
#include <linux/input.h>
return r;
switch (type) {
+
case SESSION_DEVICE_RESUME:
r = sd_bus_message_append(m, "uuh", major, minor, sd->fd);
if (r < 0)
return r;
break;
+
case SESSION_DEVICE_TRY_PAUSE:
t = "pause";
break;
+
case SESSION_DEVICE_PAUSE:
t = "force";
break;
+
case SESSION_DEVICE_RELEASE:
t = "gone";
break;
+
default:
return -EINVAL;
}
return sd_bus_send(sd->session->manager->bus, m, NULL);
}
-static int sd_eviocrevoke(int fd) {
- static bool warned;
- int r;
+static void sd_eviocrevoke(int fd) {
+ static bool warned = false;
assert(fd >= 0);
- r = ioctl(fd, EVIOCREVOKE, NULL);
- if (r < 0) {
- r = -errno;
- if (r == -EINVAL && !warned) {
+ if (ioctl(fd, EVIOCREVOKE, NULL) < 0) {
+
+ if (errno == EINVAL && !warned) {
+ log_warning_errno(errno, "Kernel does not support evdev-revocation: %m");
warned = true;
- log_warning("kernel does not support evdev-revocation");
}
}
-
- return 0;
}
static int sd_drmsetmaster(int fd) {
- int r;
-
assert(fd >= 0);
- r = ioctl(fd, DRM_IOCTL_SET_MASTER, 0);
- if (r < 0)
+ if (ioctl(fd, DRM_IOCTL_SET_MASTER, 0) < 0)
return -errno;
return 0;
}
static int sd_drmdropmaster(int fd) {
- int r;
-
assert(fd >= 0);
- r = ioctl(fd, DRM_IOCTL_DROP_MASTER, 0);
- if (r < 0)
+ if (ioctl(fd, DRM_IOCTL_DROP_MASTER, 0) < 0)
return -errno;
return 0;
static int session_device_open(SessionDevice *sd, bool active) {
int fd, r;
+ assert(sd);
assert(sd->type != DEVICE_TYPE_UNKNOWN);
+ assert(sd->node);
/* open device and try to get an udev_device from it */
fd = open(sd->node, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
return -errno;
switch (sd->type) {
+
case DEVICE_TYPE_DRM:
if (active) {
- /* Weird legacy DRM semantics might return an error
- * even though we're master. No way to detect that so
- * fail at all times and let caller retry in inactive
- * state. */
+ /* Weird legacy DRM semantics might return an error even though we're master. No way to detect
+ * that so fail at all times and let caller retry in inactive state. */
r = sd_drmsetmaster(fd);
if (r < 0) {
close_nointr(fd);
return r;
}
- } else {
- /* DRM-Master is granted to the first user who opens a
- * device automatically (ughh, racy!). Hence, we just
- * drop DRM-Master in case we were the first. */
- sd_drmdropmaster(fd);
- }
+ } else
+ /* DRM-Master is granted to the first user who opens a device automatically (ughh,
+ * racy!). Hence, we just drop DRM-Master in case we were the first. */
+ (void) sd_drmdropmaster(fd);
break;
+
case DEVICE_TYPE_EVDEV:
if (!active)
sd_eviocrevoke(fd);
break;
+
case DEVICE_TYPE_UNKNOWN:
default:
/* fallback for devices wihout synchronizations */
return 0;
switch (sd->type) {
+
case DEVICE_TYPE_DRM:
- /* Device is kept open. Simply call drmSetMaster() and hope
- * there is no-one else. In case it fails, we keep the device
- * paused. Maybe at some point we have a drmStealMaster(). */
+ if (sd->fd < 0) {
+ log_error("Failed to re-activate DRM fd, as the fd was lost (maybe logind restart went wrong?)");
+ return -EBADF;
+ }
+
+ /* Device is kept open. Simply call drmSetMaster() and hope there is no-one else. In case it fails, we
+ * keep the device paused. Maybe at some point we have a drmStealMaster(). */
r = sd_drmsetmaster(sd->fd);
if (r < 0)
return r;
break;
+
case DEVICE_TYPE_EVDEV:
- /* Evdev devices are revoked while inactive. Reopen it and we
- * are fine. */
+ /* Evdev devices are revoked while inactive. Reopen it and we are fine. */
r = session_device_open(sd, true);
if (r < 0)
return r;
- /* For evdev devices, the file descriptor might be left
- * uninitialized. This might happen while resuming into a
- * session and logind has been restarted right before. */
+
+ /* For evdev devices, the file descriptor might be left uninitialized. This might happen while resuming
+ * into a session and logind has been restarted right before. */
safe_close(sd->fd);
sd->fd = r;
break;
+
case DEVICE_TYPE_UNKNOWN:
default:
- /* fallback for devices wihout synchronizations */
+ /* fallback for devices without synchronizations */
break;
}
return;
switch (sd->type) {
+
case DEVICE_TYPE_DRM:
+ if (sd->fd < 0) {
+ log_error("Failed to de-activate DRM fd, as the fd was lost (maybe logind restart went wrong?)");
+ return;
+ }
+
/* On DRM devices we simply drop DRM-Master but keep it open.
* This allows the user to keep resources allocated. The
* CAP_SYS_ADMIN restriction to DRM-Master prevents users from
* circumventing this. */
sd_drmdropmaster(sd->fd);
break;
+
case DEVICE_TYPE_EVDEV:
/* Revoke access on evdev file-descriptors during deactivation.
* This will basically prevent any operations on the fd and
* protection this way. */
sd_eviocrevoke(sd->fd);
break;
+
case DEVICE_TYPE_UNKNOWN:
default:
/* fallback for devices without synchronization */
goto error;
r = hashmap_put(s->devices, &sd->dev, sd);
- if (r < 0) {
- r = -ENOMEM;
+ if (r < 0)
goto error;
- }
if (open_device) {
/* Open the device for the first time. We need a valid fd to pass back
void session_device_free(SessionDevice *sd) {
assert(sd);
+ /* Make sure to remove the pushed fd. */
+ if (sd->pushed_fd) {
+ _cleanup_free_ char *m = NULL;
+ const char *id;
+ int r;
+
+ /* Session ID does not contain separators. */
+ id = sd->session->id;
+ assert(*(id + strcspn(id, "-\n")) == '\0');
+
+ r = asprintf(&m, "FDSTOREREMOVE=1\n"
+ "FDNAME=session-%s-device-%u-%u\n",
+ id, major(sd->dev), minor(sd->dev));
+ if (r >= 0)
+ (void) sd_notify(false, m);
+ }
+
session_device_stop(sd);
session_device_notify(sd, SESSION_DEVICE_RELEASE);
- close_nointr(sd->fd);
+ safe_close(sd->fd);
LIST_REMOVE(sd_by_device, sd->device->session_devices, sd);
assert(s);
HASHMAP_FOREACH(sd, s->devices, i) {
- if (!sd->active) {
- if (session_device_start(sd) < 0)
- continue;
- if (session_device_save(sd) < 0)
- continue;
- session_device_notify(sd, SESSION_DEVICE_RESUME);
- }
+ if (sd->active)
+ continue;
+
+ if (session_device_start(sd) < 0)
+ continue;
+ if (session_device_save(sd) < 0)
+ continue;
+
+ session_device_notify(sd, SESSION_DEVICE_RESUME);
}
}
assert(s);
HASHMAP_FOREACH(sd, s->devices, i) {
- if (sd->active) {
- session_device_stop(sd);
- session_device_notify(sd, SESSION_DEVICE_PAUSE);
- }
+ if (!sd->active)
+ continue;
+
+ session_device_stop(sd);
+ session_device_notify(sd, SESSION_DEVICE_PAUSE);
}
}
unsigned int session_device_try_pause_all(Session *s) {
+ unsigned num_pending = 0;
SessionDevice *sd;
Iterator i;
- unsigned int num_pending = 0;
assert(s);
HASHMAP_FOREACH(sd, s->devices, i) {
- if (sd->active) {
- session_device_notify(sd, SESSION_DEVICE_TRY_PAUSE);
- ++num_pending;
- }
+ if (!sd->active)
+ continue;
+
+ session_device_notify(sd, SESSION_DEVICE_TRY_PAUSE);
+ num_pending++;
}
return num_pending;
}
int session_device_save(SessionDevice *sd) {
- _cleanup_free_ char *state = NULL;
+ _cleanup_free_ char *m = NULL;
+ const char *id;
int r;
assert(sd);
- /* Store device fd in PID1. It will send it back to us on
- * restart so revocation will continue to work. To make things
- * simple, send fds for all type of devices even if they don't
- * support the revocation mechanism so we don't have to handle
- * them differently later.
+ /* Store device fd in PID1. It will send it back to us on restart so revocation will continue to work. To make
+ * things simple, send fds for all type of devices even if they don't support the revocation mechanism so we
+ * don't have to handle them differently later.
*
- * Note: for device supporting revocation, PID1 will drop a
- * stored fd automatically if the corresponding device is
- * revoked. */
- r = asprintf(&state, "FDSTORE=1\n"
- "FDNAME=session-%s", sd->session->id);
+ * Note: for device supporting revocation, PID1 will drop a stored fd automatically if the corresponding device
+ * is revoked. */
+
+ if (sd->pushed_fd)
+ return 0;
+
+ /* Session ID does not contain separators. */
+ id = sd->session->id;
+ assert(*(id + strcspn(id, "-\n")) == '\0');
+
+ r = asprintf(&m, "FDSTORE=1\n"
+ "FDNAME=session-%s-device-%u-%u\n",
+ id, major(sd->dev), minor(sd->dev));
if (r < 0)
- return -ENOMEM;
+ return r;
- return sd_pid_notify_with_fds(0, false, state, &sd->fd, 1);
+ r = sd_pid_notify_with_fds(0, false, m, &sd->fd, 1);
+ if (r < 0)
+ return r;
+
+ sd->pushed_fd = true;
+ return 1;
}
void session_device_attach_fd(SessionDevice *sd, int fd, bool active) {
- assert(fd > 0);
+ assert(fd >= 0);
assert(sd);
assert(sd->fd < 0);
assert(!sd->active);
sd->fd = fd;
+ sd->pushed_fd = true;
sd->active = active;
}