LCOV - code coverage report
Current view: top level - login - logind-session-device.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 0 235 0.0 %
Date: 2015-07-29 18:47:03 Functions: 0 15 0.0 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright 2013 David Herrmann
       7             : 
       8             :   systemd is free software; you can redistribute it and/or modify it
       9             :   under the terms of the GNU Lesser General Public License as published by
      10             :   the Free Software Foundation; either version 2.1 of the License, or
      11             :   (at your option) any later version.
      12             : 
      13             :   systemd is distributed in the hope that it will be useful, but
      14             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      16             :   Lesser General Public License for more details.
      17             : 
      18             :   You should have received a copy of the GNU Lesser General Public License
      19             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      20             : ***/
      21             : 
      22             : #include <fcntl.h>
      23             : #include <libudev.h>
      24             : #include <linux/input.h>
      25             : #include <string.h>
      26             : #include <sys/ioctl.h>
      27             : #include <sys/types.h>
      28             : 
      29             : #include "util.h"
      30             : #include "missing.h"
      31             : #include "bus-util.h"
      32             : #include "logind-session-device.h"
      33             : 
      34             : enum SessionDeviceNotifications {
      35             :         SESSION_DEVICE_RESUME,
      36             :         SESSION_DEVICE_TRY_PAUSE,
      37             :         SESSION_DEVICE_PAUSE,
      38             :         SESSION_DEVICE_RELEASE,
      39             : };
      40             : 
      41           0 : static int session_device_notify(SessionDevice *sd, enum SessionDeviceNotifications type) {
      42           0 :         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
      43           0 :         _cleanup_free_ char *path = NULL;
      44           0 :         const char *t = NULL;
      45             :         uint32_t major, minor;
      46             :         int r;
      47             : 
      48           0 :         assert(sd);
      49             : 
      50           0 :         major = major(sd->dev);
      51           0 :         minor = minor(sd->dev);
      52             : 
      53           0 :         if (!sd->session->controller)
      54           0 :                 return 0;
      55             : 
      56           0 :         path = session_bus_path(sd->session);
      57           0 :         if (!path)
      58           0 :                 return -ENOMEM;
      59             : 
      60           0 :         r = sd_bus_message_new_signal(
      61           0 :                         sd->session->manager->bus,
      62             :                         &m, path,
      63             :                         "org.freedesktop.login1.Session",
      64             :                         (type == SESSION_DEVICE_RESUME) ? "ResumeDevice" : "PauseDevice");
      65           0 :         if (!m)
      66           0 :                 return r;
      67             : 
      68           0 :         r = sd_bus_message_set_destination(m, sd->session->controller);
      69           0 :         if (r < 0)
      70           0 :                 return r;
      71             : 
      72           0 :         switch (type) {
      73             :         case SESSION_DEVICE_RESUME:
      74           0 :                 r = sd_bus_message_append(m, "uuh", major, minor, sd->fd);
      75           0 :                 if (r < 0)
      76           0 :                         return r;
      77           0 :                 break;
      78             :         case SESSION_DEVICE_TRY_PAUSE:
      79           0 :                 t = "pause";
      80           0 :                 break;
      81             :         case SESSION_DEVICE_PAUSE:
      82           0 :                 t = "force";
      83           0 :                 break;
      84             :         case SESSION_DEVICE_RELEASE:
      85           0 :                 t = "gone";
      86           0 :                 break;
      87             :         default:
      88           0 :                 return -EINVAL;
      89             :         }
      90             : 
      91           0 :         if (t) {
      92           0 :                 r = sd_bus_message_append(m, "uus", major, minor, t);
      93           0 :                 if (r < 0)
      94           0 :                         return r;
      95             :         }
      96             : 
      97           0 :         return sd_bus_send(sd->session->manager->bus, m, NULL);
      98             : }
      99             : 
     100           0 : static int sd_eviocrevoke(int fd) {
     101             :         static bool warned;
     102             :         int r;
     103             : 
     104           0 :         assert(fd >= 0);
     105             : 
     106           0 :         r = ioctl(fd, EVIOCREVOKE, NULL);
     107           0 :         if (r < 0) {
     108           0 :                 r = -errno;
     109           0 :                 if (r == -EINVAL && !warned) {
     110           0 :                         warned = true;
     111           0 :                         log_warning("kernel does not support evdev-revocation");
     112             :                 }
     113             :         }
     114             : 
     115           0 :         return 0;
     116             : }
     117             : 
     118           0 : static int sd_drmsetmaster(int fd) {
     119             :         int r;
     120             : 
     121           0 :         assert(fd >= 0);
     122             : 
     123           0 :         r = ioctl(fd, DRM_IOCTL_SET_MASTER, 0);
     124           0 :         if (r < 0)
     125           0 :                 return -errno;
     126             : 
     127           0 :         return 0;
     128             : }
     129             : 
     130           0 : static int sd_drmdropmaster(int fd) {
     131             :         int r;
     132             : 
     133           0 :         assert(fd >= 0);
     134             : 
     135           0 :         r = ioctl(fd, DRM_IOCTL_DROP_MASTER, 0);
     136           0 :         if (r < 0)
     137           0 :                 return -errno;
     138             : 
     139           0 :         return 0;
     140             : }
     141             : 
     142           0 : static int session_device_open(SessionDevice *sd, bool active) {
     143             :         int fd, r;
     144             : 
     145           0 :         assert(sd->type != DEVICE_TYPE_UNKNOWN);
     146             : 
     147             :         /* open device and try to get an udev_device from it */
     148           0 :         fd = open(sd->node, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
     149           0 :         if (fd < 0)
     150           0 :                 return -errno;
     151             : 
     152           0 :         switch (sd->type) {
     153             :         case DEVICE_TYPE_DRM:
     154           0 :                 if (active) {
     155             :                         /* Weird legacy DRM semantics might return an error
     156             :                          * even though we're master. No way to detect that so
     157             :                          * fail at all times and let caller retry in inactive
     158             :                          * state. */
     159           0 :                         r = sd_drmsetmaster(fd);
     160           0 :                         if (r < 0) {
     161           0 :                                 close_nointr(fd);
     162           0 :                                 return r;
     163             :                         }
     164             :                 } else {
     165             :                         /* DRM-Master is granted to the first user who opens a
     166             :                          * device automatically (ughh, racy!). Hence, we just
     167             :                          * drop DRM-Master in case we were the first. */
     168           0 :                         sd_drmdropmaster(fd);
     169             :                 }
     170           0 :                 break;
     171             :         case DEVICE_TYPE_EVDEV:
     172           0 :                 if (!active)
     173           0 :                         sd_eviocrevoke(fd);
     174           0 :                 break;
     175             :         case DEVICE_TYPE_UNKNOWN:
     176             :         default:
     177             :                 /* fallback for devices wihout synchronizations */
     178           0 :                 break;
     179             :         }
     180             : 
     181           0 :         return fd;
     182             : }
     183             : 
     184           0 : static int session_device_start(SessionDevice *sd) {
     185             :         int r;
     186             : 
     187           0 :         assert(sd);
     188           0 :         assert(session_is_active(sd->session));
     189             : 
     190           0 :         if (sd->active)
     191           0 :                 return 0;
     192             : 
     193           0 :         switch (sd->type) {
     194             :         case DEVICE_TYPE_DRM:
     195             :                 /* Device is kept open. Simply call drmSetMaster() and hope
     196             :                  * there is no-one else. In case it fails, we keep the device
     197             :                  * paused. Maybe at some point we have a drmStealMaster(). */
     198           0 :                 r = sd_drmsetmaster(sd->fd);
     199           0 :                 if (r < 0)
     200           0 :                         return r;
     201           0 :                 break;
     202             :         case DEVICE_TYPE_EVDEV:
     203             :                 /* Evdev devices are revoked while inactive. Reopen it and we
     204             :                  * are fine. */
     205           0 :                 r = session_device_open(sd, true);
     206           0 :                 if (r < 0)
     207           0 :                         return r;
     208           0 :                 close_nointr(sd->fd);
     209           0 :                 sd->fd = r;
     210           0 :                 break;
     211             :         case DEVICE_TYPE_UNKNOWN:
     212             :         default:
     213             :                 /* fallback for devices wihout synchronizations */
     214           0 :                 break;
     215             :         }
     216             : 
     217           0 :         sd->active = true;
     218           0 :         return 0;
     219             : }
     220             : 
     221           0 : static void session_device_stop(SessionDevice *sd) {
     222           0 :         assert(sd);
     223             : 
     224           0 :         if (!sd->active)
     225           0 :                 return;
     226             : 
     227           0 :         switch (sd->type) {
     228             :         case DEVICE_TYPE_DRM:
     229             :                 /* On DRM devices we simply drop DRM-Master but keep it open.
     230             :                  * This allows the user to keep resources allocated. The
     231             :                  * CAP_SYS_ADMIN restriction to DRM-Master prevents users from
     232             :                  * circumventing this. */
     233           0 :                 sd_drmdropmaster(sd->fd);
     234           0 :                 break;
     235             :         case DEVICE_TYPE_EVDEV:
     236             :                 /* Revoke access on evdev file-descriptors during deactivation.
     237             :                  * This will basically prevent any operations on the fd and
     238             :                  * cannot be undone. Good side is: it needs no CAP_SYS_ADMIN
     239             :                  * protection this way. */
     240           0 :                 sd_eviocrevoke(sd->fd);
     241           0 :                 break;
     242             :         case DEVICE_TYPE_UNKNOWN:
     243             :         default:
     244             :                 /* fallback for devices without synchronization */
     245           0 :                 break;
     246             :         }
     247             : 
     248           0 :         sd->active = false;
     249             : }
     250             : 
     251           0 : static DeviceType detect_device_type(struct udev_device *dev) {
     252             :         const char *sysname, *subsystem;
     253             :         DeviceType type;
     254             : 
     255           0 :         sysname = udev_device_get_sysname(dev);
     256           0 :         subsystem = udev_device_get_subsystem(dev);
     257           0 :         type = DEVICE_TYPE_UNKNOWN;
     258             : 
     259           0 :         if (streq_ptr(subsystem, "drm")) {
     260           0 :                 if (startswith(sysname, "card"))
     261           0 :                         type = DEVICE_TYPE_DRM;
     262           0 :         } else if (streq_ptr(subsystem, "input")) {
     263           0 :                 if (startswith(sysname, "event"))
     264           0 :                         type = DEVICE_TYPE_EVDEV;
     265             :         }
     266             : 
     267           0 :         return type;
     268             : }
     269             : 
     270           0 : static int session_device_verify(SessionDevice *sd) {
     271           0 :         struct udev_device *dev, *p = NULL;
     272             :         const char *sp, *node;
     273             :         int r;
     274             : 
     275           0 :         dev = udev_device_new_from_devnum(sd->session->manager->udev, 'c', sd->dev);
     276           0 :         if (!dev)
     277           0 :                 return -ENODEV;
     278             : 
     279           0 :         sp = udev_device_get_syspath(dev);
     280           0 :         node = udev_device_get_devnode(dev);
     281           0 :         if (!node) {
     282           0 :                 r = -EINVAL;
     283           0 :                 goto err_dev;
     284             :         }
     285             : 
     286             :         /* detect device type so we can find the correct sysfs parent */
     287           0 :         sd->type = detect_device_type(dev);
     288           0 :         if (sd->type == DEVICE_TYPE_UNKNOWN) {
     289           0 :                 r = -ENODEV;
     290           0 :                 goto err_dev;
     291           0 :         } else if (sd->type == DEVICE_TYPE_EVDEV) {
     292             :                 /* for evdev devices we need the parent node as device */
     293           0 :                 p = dev;
     294           0 :                 dev = udev_device_get_parent_with_subsystem_devtype(p, "input", NULL);
     295           0 :                 if (!dev) {
     296           0 :                         r = -ENODEV;
     297           0 :                         goto err_dev;
     298             :                 }
     299           0 :                 sp = udev_device_get_syspath(dev);
     300           0 :         } else if (sd->type != DEVICE_TYPE_DRM) {
     301             :                 /* Prevent opening unsupported devices. Especially devices of
     302             :                  * subsystem "input" must be opened via the evdev node as
     303             :                  * we require EVIOCREVOKE. */
     304           0 :                 r = -ENODEV;
     305           0 :                 goto err_dev;
     306             :         }
     307             : 
     308             :         /* search for an existing seat device and return it if available */
     309           0 :         sd->device = hashmap_get(sd->session->manager->devices, sp);
     310           0 :         if (!sd->device) {
     311             :                 /* The caller might have gotten the udev event before we were
     312             :                  * able to process it. Hence, fake the "add" event and let the
     313             :                  * logind-manager handle the new device. */
     314           0 :                 r = manager_process_seat_device(sd->session->manager, dev);
     315           0 :                 if (r < 0)
     316           0 :                         goto err_dev;
     317             : 
     318             :                 /* if it's still not available, then the device is invalid */
     319           0 :                 sd->device = hashmap_get(sd->session->manager->devices, sp);
     320           0 :                 if (!sd->device) {
     321           0 :                         r = -ENODEV;
     322           0 :                         goto err_dev;
     323             :                 }
     324             :         }
     325             : 
     326           0 :         if (sd->device->seat != sd->session->seat) {
     327           0 :                 r = -EPERM;
     328           0 :                 goto err_dev;
     329             :         }
     330             : 
     331           0 :         sd->node = strdup(node);
     332           0 :         if (!sd->node) {
     333           0 :                 r = -ENOMEM;
     334           0 :                 goto err_dev;
     335             :         }
     336             : 
     337           0 :         r = 0;
     338             : err_dev:
     339           0 :         udev_device_unref(p ? : dev);
     340           0 :         return r;
     341             : }
     342             : 
     343           0 : int session_device_new(Session *s, dev_t dev, SessionDevice **out) {
     344             :         SessionDevice *sd;
     345             :         int r;
     346             : 
     347           0 :         assert(s);
     348           0 :         assert(out);
     349             : 
     350           0 :         if (!s->seat)
     351           0 :                 return -EPERM;
     352             : 
     353           0 :         sd = new0(SessionDevice, 1);
     354           0 :         if (!sd)
     355           0 :                 return -ENOMEM;
     356             : 
     357           0 :         sd->session = s;
     358           0 :         sd->dev = dev;
     359           0 :         sd->fd = -1;
     360           0 :         sd->type = DEVICE_TYPE_UNKNOWN;
     361             : 
     362           0 :         r = session_device_verify(sd);
     363           0 :         if (r < 0)
     364           0 :                 goto error;
     365             : 
     366           0 :         r = hashmap_put(s->devices, &sd->dev, sd);
     367           0 :         if (r < 0) {
     368           0 :                 r = -ENOMEM;
     369           0 :                 goto error;
     370             :         }
     371             : 
     372             :         /* Open the device for the first time. We need a valid fd to pass back
     373             :          * to the caller. If the session is not active, this _might_ immediately
     374             :          * revoke access and thus invalidate the fd. But this is still needed
     375             :          * to pass a valid fd back. */
     376           0 :         sd->active = session_is_active(s);
     377           0 :         r = session_device_open(sd, sd->active);
     378           0 :         if (r < 0) {
     379             :                 /* EINVAL _may_ mean a master is active; retry inactive */
     380           0 :                 if (sd->active && r == -EINVAL) {
     381           0 :                         sd->active = false;
     382           0 :                         r = session_device_open(sd, false);
     383             :                 }
     384           0 :                 if (r < 0)
     385           0 :                         goto error;
     386             :         }
     387           0 :         sd->fd = r;
     388             : 
     389           0 :         LIST_PREPEND(sd_by_device, sd->device->session_devices, sd);
     390             : 
     391           0 :         *out = sd;
     392           0 :         return 0;
     393             : 
     394             : error:
     395           0 :         hashmap_remove(s->devices, &sd->dev);
     396           0 :         free(sd->node);
     397           0 :         free(sd);
     398           0 :         return r;
     399             : }
     400             : 
     401           0 : void session_device_free(SessionDevice *sd) {
     402           0 :         assert(sd);
     403             : 
     404           0 :         session_device_stop(sd);
     405           0 :         session_device_notify(sd, SESSION_DEVICE_RELEASE);
     406           0 :         close_nointr(sd->fd);
     407             : 
     408           0 :         LIST_REMOVE(sd_by_device, sd->device->session_devices, sd);
     409             : 
     410           0 :         hashmap_remove(sd->session->devices, &sd->dev);
     411             : 
     412           0 :         free(sd->node);
     413           0 :         free(sd);
     414           0 : }
     415             : 
     416           0 : void session_device_complete_pause(SessionDevice *sd) {
     417             :         SessionDevice *iter;
     418             :         Iterator i;
     419             : 
     420           0 :         if (!sd->active)
     421           0 :                 return;
     422             : 
     423           0 :         session_device_stop(sd);
     424             : 
     425             :         /* if not all devices are paused, wait for further completion events */
     426           0 :         HASHMAP_FOREACH(iter, sd->session->devices, i)
     427           0 :                 if (iter->active)
     428           0 :                         return;
     429             : 
     430             :         /* complete any pending session switch */
     431           0 :         seat_complete_switch(sd->session->seat);
     432             : }
     433             : 
     434           0 : void session_device_resume_all(Session *s) {
     435             :         SessionDevice *sd;
     436             :         Iterator i;
     437             :         int r;
     438             : 
     439           0 :         assert(s);
     440             : 
     441           0 :         HASHMAP_FOREACH(sd, s->devices, i) {
     442           0 :                 if (!sd->active) {
     443           0 :                         r = session_device_start(sd);
     444           0 :                         if (!r)
     445           0 :                                 session_device_notify(sd, SESSION_DEVICE_RESUME);
     446             :                 }
     447             :         }
     448           0 : }
     449             : 
     450           0 : void session_device_pause_all(Session *s) {
     451             :         SessionDevice *sd;
     452             :         Iterator i;
     453             : 
     454           0 :         assert(s);
     455             : 
     456           0 :         HASHMAP_FOREACH(sd, s->devices, i) {
     457           0 :                 if (sd->active) {
     458           0 :                         session_device_stop(sd);
     459           0 :                         session_device_notify(sd, SESSION_DEVICE_PAUSE);
     460             :                 }
     461             :         }
     462           0 : }
     463             : 
     464           0 : unsigned int session_device_try_pause_all(Session *s) {
     465             :         SessionDevice *sd;
     466             :         Iterator i;
     467           0 :         unsigned int num_pending = 0;
     468             : 
     469           0 :         assert(s);
     470             : 
     471           0 :         HASHMAP_FOREACH(sd, s->devices, i) {
     472           0 :                 if (sd->active) {
     473           0 :                         session_device_notify(sd, SESSION_DEVICE_TRY_PAUSE);
     474           0 :                         ++num_pending;
     475             :                 }
     476             :         }
     477             : 
     478           0 :         return num_pending;
     479             : }

Generated by: LCOV version 1.11