LCOV - code coverage report
Current view: top level - login - logind-session-device.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 0 276 0.0 %
Date: 2019-08-22 15:41:25 Functions: 0 17 0.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <fcntl.h>
       4             : #include <linux/input.h>
       5             : #include <string.h>
       6             : #include <sys/ioctl.h>
       7             : #include <sys/types.h>
       8             : 
       9             : #include "sd-device.h"
      10             : #include "sd-daemon.h"
      11             : 
      12             : #include "alloc-util.h"
      13             : #include "bus-util.h"
      14             : #include "fd-util.h"
      15             : #include "logind-session-dbus.h"
      16             : #include "logind-session-device.h"
      17             : #include "missing.h"
      18             : #include "parse-util.h"
      19             : #include "util.h"
      20             : 
      21             : enum SessionDeviceNotifications {
      22             :         SESSION_DEVICE_RESUME,
      23             :         SESSION_DEVICE_TRY_PAUSE,
      24             :         SESSION_DEVICE_PAUSE,
      25             :         SESSION_DEVICE_RELEASE,
      26             : };
      27             : 
      28           0 : static int session_device_notify(SessionDevice *sd, enum SessionDeviceNotifications type) {
      29           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
      30           0 :         _cleanup_free_ char *path = NULL;
      31           0 :         const char *t = NULL;
      32             :         uint32_t major, minor;
      33             :         int r;
      34             : 
      35           0 :         assert(sd);
      36             : 
      37           0 :         major = major(sd->dev);
      38           0 :         minor = minor(sd->dev);
      39             : 
      40           0 :         if (!sd->session->controller)
      41           0 :                 return 0;
      42             : 
      43           0 :         path = session_bus_path(sd->session);
      44           0 :         if (!path)
      45           0 :                 return -ENOMEM;
      46             : 
      47           0 :         r = sd_bus_message_new_signal(
      48           0 :                         sd->session->manager->bus,
      49             :                         &m, path,
      50             :                         "org.freedesktop.login1.Session",
      51             :                         (type == SESSION_DEVICE_RESUME) ? "ResumeDevice" : "PauseDevice");
      52           0 :         if (!m)
      53           0 :                 return r;
      54             : 
      55           0 :         r = sd_bus_message_set_destination(m, sd->session->controller);
      56           0 :         if (r < 0)
      57           0 :                 return r;
      58             : 
      59           0 :         switch (type) {
      60             : 
      61           0 :         case SESSION_DEVICE_RESUME:
      62           0 :                 r = sd_bus_message_append(m, "uuh", major, minor, sd->fd);
      63           0 :                 if (r < 0)
      64           0 :                         return r;
      65           0 :                 break;
      66             : 
      67           0 :         case SESSION_DEVICE_TRY_PAUSE:
      68           0 :                 t = "pause";
      69           0 :                 break;
      70             : 
      71           0 :         case SESSION_DEVICE_PAUSE:
      72           0 :                 t = "force";
      73           0 :                 break;
      74             : 
      75           0 :         case SESSION_DEVICE_RELEASE:
      76           0 :                 t = "gone";
      77           0 :                 break;
      78             : 
      79           0 :         default:
      80           0 :                 return -EINVAL;
      81             :         }
      82             : 
      83           0 :         if (t) {
      84           0 :                 r = sd_bus_message_append(m, "uus", major, minor, t);
      85           0 :                 if (r < 0)
      86           0 :                         return r;
      87             :         }
      88             : 
      89           0 :         return sd_bus_send(sd->session->manager->bus, m, NULL);
      90             : }
      91             : 
      92           0 : static void sd_eviocrevoke(int fd) {
      93             :         static bool warned = false;
      94             : 
      95           0 :         assert(fd >= 0);
      96             : 
      97           0 :         if (ioctl(fd, EVIOCREVOKE, NULL) < 0) {
      98             : 
      99           0 :                 if (errno == EINVAL && !warned) {
     100           0 :                         log_warning_errno(errno, "Kernel does not support evdev-revocation: %m");
     101           0 :                         warned = true;
     102             :                 }
     103             :         }
     104           0 : }
     105             : 
     106           0 : static int sd_drmsetmaster(int fd) {
     107           0 :         assert(fd >= 0);
     108             : 
     109           0 :         if (ioctl(fd, DRM_IOCTL_SET_MASTER, 0) < 0)
     110           0 :                 return -errno;
     111             : 
     112           0 :         return 0;
     113             : }
     114             : 
     115           0 : static int sd_drmdropmaster(int fd) {
     116           0 :         assert(fd >= 0);
     117             : 
     118           0 :         if (ioctl(fd, DRM_IOCTL_DROP_MASTER, 0) < 0)
     119           0 :                 return -errno;
     120             : 
     121           0 :         return 0;
     122             : }
     123             : 
     124           0 : static int session_device_open(SessionDevice *sd, bool active) {
     125             :         int fd, r;
     126             : 
     127           0 :         assert(sd);
     128           0 :         assert(sd->type != DEVICE_TYPE_UNKNOWN);
     129           0 :         assert(sd->node);
     130             : 
     131             :         /* open device and try to get an udev_device from it */
     132           0 :         fd = open(sd->node, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
     133           0 :         if (fd < 0)
     134           0 :                 return -errno;
     135             : 
     136           0 :         switch (sd->type) {
     137             : 
     138           0 :         case DEVICE_TYPE_DRM:
     139           0 :                 if (active) {
     140             :                         /* Weird legacy DRM semantics might return an error even though we're master. No way to detect
     141             :                          * that so fail at all times and let caller retry in inactive state. */
     142           0 :                         r = sd_drmsetmaster(fd);
     143           0 :                         if (r < 0) {
     144           0 :                                 close_nointr(fd);
     145           0 :                                 return r;
     146             :                         }
     147             :                 } else
     148             :                         /* DRM-Master is granted to the first user who opens a device automatically (ughh,
     149             :                          * racy!). Hence, we just drop DRM-Master in case we were the first. */
     150           0 :                         (void) sd_drmdropmaster(fd);
     151           0 :                 break;
     152             : 
     153           0 :         case DEVICE_TYPE_EVDEV:
     154           0 :                 if (!active)
     155           0 :                         sd_eviocrevoke(fd);
     156           0 :                 break;
     157             : 
     158           0 :         case DEVICE_TYPE_UNKNOWN:
     159             :         default:
     160             :                 /* fallback for devices without synchronizations */
     161           0 :                 break;
     162             :         }
     163             : 
     164           0 :         return fd;
     165             : }
     166             : 
     167           0 : static int session_device_start(SessionDevice *sd) {
     168             :         int r;
     169             : 
     170           0 :         assert(sd);
     171           0 :         assert(session_is_active(sd->session));
     172             : 
     173           0 :         if (sd->active)
     174           0 :                 return 0;
     175             : 
     176           0 :         switch (sd->type) {
     177             : 
     178           0 :         case DEVICE_TYPE_DRM:
     179           0 :                 if (sd->fd < 0)
     180           0 :                         return log_error_errno(SYNTHETIC_ERRNO(EBADF),
     181             :                                                "Failed to re-activate DRM fd, as the fd was lost (maybe logind restart went wrong?)");
     182             : 
     183             :                 /* Device is kept open. Simply call drmSetMaster() and hope there is no-one else. In case it fails, we
     184             :                  * keep the device paused. Maybe at some point we have a drmStealMaster(). */
     185           0 :                 r = sd_drmsetmaster(sd->fd);
     186           0 :                 if (r < 0)
     187           0 :                         return r;
     188           0 :                 break;
     189             : 
     190           0 :         case DEVICE_TYPE_EVDEV:
     191             :                 /* Evdev devices are revoked while inactive. Reopen it and we are fine. */
     192           0 :                 r = session_device_open(sd, true);
     193           0 :                 if (r < 0)
     194           0 :                         return r;
     195             : 
     196             :                 /* For evdev devices, the file descriptor might be left uninitialized. This might happen while resuming
     197             :                  * into a session and logind has been restarted right before. */
     198           0 :                 safe_close(sd->fd);
     199           0 :                 sd->fd = r;
     200           0 :                 break;
     201             : 
     202           0 :         case DEVICE_TYPE_UNKNOWN:
     203             :         default:
     204             :                 /* fallback for devices without synchronizations */
     205           0 :                 break;
     206             :         }
     207             : 
     208           0 :         sd->active = true;
     209           0 :         return 0;
     210             : }
     211             : 
     212           0 : static void session_device_stop(SessionDevice *sd) {
     213           0 :         assert(sd);
     214             : 
     215           0 :         if (!sd->active)
     216           0 :                 return;
     217             : 
     218           0 :         switch (sd->type) {
     219             : 
     220           0 :         case DEVICE_TYPE_DRM:
     221           0 :                 if (sd->fd < 0) {
     222           0 :                         log_error("Failed to de-activate DRM fd, as the fd was lost (maybe logind restart went wrong?)");
     223           0 :                         return;
     224             :                 }
     225             : 
     226             :                 /* On DRM devices we simply drop DRM-Master but keep it open.
     227             :                  * This allows the user to keep resources allocated. The
     228             :                  * CAP_SYS_ADMIN restriction to DRM-Master prevents users from
     229             :                  * circumventing this. */
     230           0 :                 sd_drmdropmaster(sd->fd);
     231           0 :                 break;
     232             : 
     233           0 :         case DEVICE_TYPE_EVDEV:
     234             :                 /* Revoke access on evdev file-descriptors during deactivation.
     235             :                  * This will basically prevent any operations on the fd and
     236             :                  * cannot be undone. Good side is: it needs no CAP_SYS_ADMIN
     237             :                  * protection this way. */
     238           0 :                 sd_eviocrevoke(sd->fd);
     239           0 :                 break;
     240             : 
     241           0 :         case DEVICE_TYPE_UNKNOWN:
     242             :         default:
     243             :                 /* fallback for devices without synchronization */
     244           0 :                 break;
     245             :         }
     246             : 
     247           0 :         sd->active = false;
     248             : }
     249             : 
     250           0 : static DeviceType detect_device_type(sd_device *dev) {
     251             :         const char *sysname, *subsystem;
     252           0 :         DeviceType type = DEVICE_TYPE_UNKNOWN;
     253             : 
     254           0 :         if (sd_device_get_sysname(dev, &sysname) < 0 ||
     255           0 :             sd_device_get_subsystem(dev, &subsystem) < 0)
     256           0 :                 return type;
     257             : 
     258           0 :         if (streq(subsystem, "drm")) {
     259           0 :                 if (startswith(sysname, "card"))
     260           0 :                         type = DEVICE_TYPE_DRM;
     261           0 :         } else if (streq(subsystem, "input")) {
     262           0 :                 if (startswith(sysname, "event"))
     263           0 :                         type = DEVICE_TYPE_EVDEV;
     264             :         }
     265             : 
     266           0 :         return type;
     267             : }
     268             : 
     269           0 : static int session_device_verify(SessionDevice *sd) {
     270           0 :         _cleanup_(sd_device_unrefp) sd_device *p = NULL;
     271             :         const char *sp, *node;
     272             :         sd_device *dev;
     273             :         int r;
     274             : 
     275           0 :         r = sd_device_new_from_devnum(&p, 'c', sd->dev);
     276           0 :         if (r < 0)
     277           0 :                 return r;
     278             : 
     279           0 :         dev = p;
     280             : 
     281           0 :         if (sd_device_get_syspath(dev, &sp) < 0 ||
     282           0 :             sd_device_get_devname(dev, &node) < 0)
     283           0 :                 return -EINVAL;
     284             : 
     285             :         /* detect device type so we can find the correct sysfs parent */
     286           0 :         sd->type = detect_device_type(dev);
     287           0 :         if (sd->type == DEVICE_TYPE_UNKNOWN)
     288           0 :                 return -ENODEV;
     289             : 
     290           0 :         else if (sd->type == DEVICE_TYPE_EVDEV) {
     291             :                 /* for evdev devices we need the parent node as device */
     292           0 :                 if (sd_device_get_parent_with_subsystem_devtype(p, "input", NULL, &dev) < 0)
     293           0 :                         return -ENODEV;
     294           0 :                 if (sd_device_get_syspath(dev, &sp) < 0)
     295           0 :                         return -ENODEV;
     296             : 
     297           0 :         } else if (sd->type != DEVICE_TYPE_DRM)
     298             :                 /* Prevent opening unsupported devices. Especially devices of
     299             :                  * subsystem "input" must be opened via the evdev node as
     300             :                  * we require EVIOCREVOKE. */
     301           0 :                 return -ENODEV;
     302             : 
     303             :         /* search for an existing seat device and return it if available */
     304           0 :         sd->device = hashmap_get(sd->session->manager->devices, sp);
     305           0 :         if (!sd->device) {
     306             :                 /* The caller might have gotten the udev event before we were
     307             :                  * able to process it. Hence, fake the "add" event and let the
     308             :                  * logind-manager handle the new device. */
     309           0 :                 r = manager_process_seat_device(sd->session->manager, dev);
     310           0 :                 if (r < 0)
     311           0 :                         return r;
     312             : 
     313             :                 /* if it's still not available, then the device is invalid */
     314           0 :                 sd->device = hashmap_get(sd->session->manager->devices, sp);
     315           0 :                 if (!sd->device)
     316           0 :                         return -ENODEV;
     317             :         }
     318             : 
     319           0 :         if (sd->device->seat != sd->session->seat)
     320           0 :                 return -EPERM;
     321             : 
     322           0 :         sd->node = strdup(node);
     323           0 :         if (!sd->node)
     324           0 :                 return -ENOMEM;
     325             : 
     326           0 :         return 0;
     327             : }
     328             : 
     329           0 : int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **out) {
     330             :         SessionDevice *sd;
     331             :         int r;
     332             : 
     333           0 :         assert(s);
     334           0 :         assert(out);
     335             : 
     336           0 :         if (!s->seat)
     337           0 :                 return -EPERM;
     338             : 
     339           0 :         sd = new0(SessionDevice, 1);
     340           0 :         if (!sd)
     341           0 :                 return -ENOMEM;
     342             : 
     343           0 :         sd->session = s;
     344           0 :         sd->dev = dev;
     345           0 :         sd->fd = -1;
     346           0 :         sd->type = DEVICE_TYPE_UNKNOWN;
     347             : 
     348           0 :         r = session_device_verify(sd);
     349           0 :         if (r < 0)
     350           0 :                 goto error;
     351             : 
     352           0 :         r = hashmap_put(s->devices, &sd->dev, sd);
     353           0 :         if (r < 0)
     354           0 :                 goto error;
     355             : 
     356           0 :         if (open_device) {
     357             :                 /* Open the device for the first time. We need a valid fd to pass back
     358             :                  * to the caller. If the session is not active, this _might_ immediately
     359             :                  * revoke access and thus invalidate the fd. But this is still needed
     360             :                  * to pass a valid fd back. */
     361           0 :                 sd->active = session_is_active(s);
     362           0 :                 r = session_device_open(sd, sd->active);
     363           0 :                 if (r < 0) {
     364             :                         /* EINVAL _may_ mean a master is active; retry inactive */
     365           0 :                         if (sd->active && r == -EINVAL) {
     366           0 :                                 sd->active = false;
     367           0 :                                 r = session_device_open(sd, false);
     368             :                         }
     369           0 :                         if (r < 0)
     370           0 :                                 goto error;
     371             :                 }
     372           0 :                 sd->fd = r;
     373             :         }
     374             : 
     375           0 :         LIST_PREPEND(sd_by_device, sd->device->session_devices, sd);
     376             : 
     377           0 :         *out = sd;
     378           0 :         return 0;
     379             : 
     380           0 : error:
     381           0 :         hashmap_remove(s->devices, &sd->dev);
     382           0 :         free(sd->node);
     383           0 :         free(sd);
     384           0 :         return r;
     385             : }
     386             : 
     387           0 : void session_device_free(SessionDevice *sd) {
     388           0 :         assert(sd);
     389             : 
     390             :         /* Make sure to remove the pushed fd. */
     391           0 :         if (sd->pushed_fd)
     392           0 :                 (void) sd_notifyf(false,
     393             :                                   "FDSTOREREMOVE=1\n"
     394             :                                   "FDNAME=session-%s-device-%u-%u",
     395           0 :                                   sd->session->id, major(sd->dev), minor(sd->dev));
     396             : 
     397           0 :         session_device_stop(sd);
     398           0 :         session_device_notify(sd, SESSION_DEVICE_RELEASE);
     399           0 :         safe_close(sd->fd);
     400             : 
     401           0 :         LIST_REMOVE(sd_by_device, sd->device->session_devices, sd);
     402             : 
     403           0 :         hashmap_remove(sd->session->devices, &sd->dev);
     404             : 
     405           0 :         free(sd->node);
     406           0 :         free(sd);
     407           0 : }
     408             : 
     409           0 : void session_device_complete_pause(SessionDevice *sd) {
     410             :         SessionDevice *iter;
     411             :         Iterator i;
     412             : 
     413           0 :         if (!sd->active)
     414           0 :                 return;
     415             : 
     416           0 :         session_device_stop(sd);
     417             : 
     418             :         /* if not all devices are paused, wait for further completion events */
     419           0 :         HASHMAP_FOREACH(iter, sd->session->devices, i)
     420           0 :                 if (iter->active)
     421           0 :                         return;
     422             : 
     423             :         /* complete any pending session switch */
     424           0 :         seat_complete_switch(sd->session->seat);
     425             : }
     426             : 
     427           0 : void session_device_resume_all(Session *s) {
     428             :         SessionDevice *sd;
     429             :         Iterator i;
     430             : 
     431           0 :         assert(s);
     432             : 
     433           0 :         HASHMAP_FOREACH(sd, s->devices, i) {
     434           0 :                 if (sd->active)
     435           0 :                         continue;
     436             : 
     437           0 :                 if (session_device_start(sd) < 0)
     438           0 :                         continue;
     439           0 :                 if (session_device_save(sd) < 0)
     440           0 :                         continue;
     441             : 
     442           0 :                 session_device_notify(sd, SESSION_DEVICE_RESUME);
     443             :         }
     444           0 : }
     445             : 
     446           0 : void session_device_pause_all(Session *s) {
     447             :         SessionDevice *sd;
     448             :         Iterator i;
     449             : 
     450           0 :         assert(s);
     451             : 
     452           0 :         HASHMAP_FOREACH(sd, s->devices, i) {
     453           0 :                 if (!sd->active)
     454           0 :                         continue;
     455             : 
     456           0 :                 session_device_stop(sd);
     457           0 :                 session_device_notify(sd, SESSION_DEVICE_PAUSE);
     458             :         }
     459           0 : }
     460             : 
     461           0 : unsigned session_device_try_pause_all(Session *s) {
     462           0 :         unsigned num_pending = 0;
     463             :         SessionDevice *sd;
     464             :         Iterator i;
     465             : 
     466           0 :         assert(s);
     467             : 
     468           0 :         HASHMAP_FOREACH(sd, s->devices, i) {
     469           0 :                 if (!sd->active)
     470           0 :                         continue;
     471             : 
     472           0 :                 session_device_notify(sd, SESSION_DEVICE_TRY_PAUSE);
     473           0 :                 num_pending++;
     474             :         }
     475             : 
     476           0 :         return num_pending;
     477             : }
     478             : 
     479           0 : int session_device_save(SessionDevice *sd) {
     480           0 :         _cleanup_free_ char *m = NULL;
     481             :         const char *id;
     482             :         int r;
     483             : 
     484           0 :         assert(sd);
     485             : 
     486             :         /* Store device fd in PID1. It will send it back to us on restart so revocation will continue to work. To make
     487             :          * things simple, send fds for all type of devices even if they don't support the revocation mechanism so we
     488             :          * don't have to handle them differently later.
     489             :          *
     490             :          * Note: for device supporting revocation, PID1 will drop a stored fd automatically if the corresponding device
     491             :          * is revoked. */
     492             : 
     493           0 :         if (sd->pushed_fd)
     494           0 :                 return 0;
     495             : 
     496             :         /* Session ID does not contain separators. */
     497           0 :         id = sd->session->id;
     498           0 :         assert(*(id + strcspn(id, "-\n")) == '\0');
     499             : 
     500           0 :         r = asprintf(&m, "FDSTORE=1\n"
     501             :                          "FDNAME=session-%s-device-%u-%u\n",
     502             :                          id, major(sd->dev), minor(sd->dev));
     503           0 :         if (r < 0)
     504           0 :                 return r;
     505             : 
     506           0 :         r = sd_pid_notify_with_fds(0, false, m, &sd->fd, 1);
     507           0 :         if (r < 0)
     508           0 :                 return r;
     509             : 
     510           0 :         sd->pushed_fd = true;
     511           0 :         return 1;
     512             : }
     513             : 
     514           0 : void session_device_attach_fd(SessionDevice *sd, int fd, bool active) {
     515           0 :         assert(fd >= 0);
     516           0 :         assert(sd);
     517           0 :         assert(sd->fd < 0);
     518           0 :         assert(!sd->active);
     519             : 
     520           0 :         sd->fd = fd;
     521           0 :         sd->pushed_fd = true;
     522           0 :         sd->active = active;
     523           0 : }

Generated by: LCOV version 1.14