udev.c revision ed6184df
1/*
2 * Copyright © 2009 Julien Cristau
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Author: Julien Cristau <jcristau@debian.org>
24 */
25
26#ifdef HAVE_DIX_CONFIG_H
27#include <dix-config.h>
28#endif
29
30#include <libudev.h>
31#include <ctype.h>
32#include <unistd.h>
33
34#include "input.h"
35#include "inputstr.h"
36#include "hotplug.h"
37#include "config-backends.h"
38#include "os.h"
39#include "globals.h"
40#include "systemd-logind.h"
41
42#ifdef HAVE_SYS_SYSMACROS_H
43#include <sys/sysmacros.h>
44#endif
45
46#define UDEV_XKB_PROP_KEY "xkb"
47
48#define LOG_PROPERTY(path, prop, val)                                   \
49    LogMessageVerb(X_INFO, 10,                                          \
50                   "config/udev: getting property %s on %s "            \
51                   "returned \"%s\"\n",                                 \
52                   (prop), (path), (val) ? (val) : "(null)")
53#define LOG_SYSATTR(path, attr, val)                                    \
54    LogMessageVerb(X_INFO, 10,                                          \
55                   "config/udev: getting attribute %s on %s "           \
56                   "returned \"%s\"\n",                                 \
57                   (attr), (path), (val) ? (val) : "(null)")
58
59static struct udev_monitor *udev_monitor;
60
61#ifdef CONFIG_UDEV_KMS
62static void
63config_udev_odev_setup_attribs(struct udev_device *udev_device, const char *path, const char *syspath,
64                               int major, int minor,
65                               config_odev_probe_proc_ptr probe_callback);
66#endif
67
68static char itoa_buf[16];
69
70static const char *itoa(int i)
71{
72    snprintf(itoa_buf, sizeof(itoa_buf), "%d", i);
73    return itoa_buf;
74}
75
76static Bool
77check_seat(struct udev_device *udev_device)
78{
79    const char *dev_seat;
80
81    dev_seat = udev_device_get_property_value(udev_device, "ID_SEAT");
82    if (!dev_seat)
83        dev_seat = "seat0";
84
85    if (SeatId && strcmp(dev_seat, SeatId))
86        return FALSE;
87
88    if (!SeatId && strcmp(dev_seat, "seat0"))
89        return FALSE;
90
91    return TRUE;
92}
93
94static void
95device_added(struct udev_device *udev_device)
96{
97    const char *path, *name = NULL;
98    char *config_info = NULL;
99    const char *syspath;
100    const char *tags_prop;
101    const char *key, *value, *tmp;
102#ifdef CONFIG_UDEV_KMS
103    const char *subsys = NULL;
104#endif
105    InputOption *input_options;
106    InputAttributes attrs = { };
107    DeviceIntPtr dev = NULL;
108    struct udev_list_entry *set, *entry;
109    struct udev_device *parent;
110    int rc;
111    dev_t devnum;
112
113    path = udev_device_get_devnode(udev_device);
114
115    syspath = udev_device_get_syspath(udev_device);
116
117    if (!path || !syspath)
118        return;
119
120    if (!check_seat(udev_device))
121        return;
122
123    devnum = udev_device_get_devnum(udev_device);
124
125#ifdef CONFIG_UDEV_KMS
126    subsys = udev_device_get_subsystem(udev_device);
127
128    if (subsys && !strcmp(subsys, "drm")) {
129        const char *sysname = udev_device_get_sysname(udev_device);
130
131        if (strncmp(sysname, "card", 4) != 0)
132            return;
133
134        /* Check for devices already added through xf86platformProbe() */
135        if (xf86_find_platform_device_by_devnum(major(devnum), minor(devnum)))
136            return;
137
138        LogMessage(X_INFO, "config/udev: Adding drm device (%s)\n", path);
139
140        config_udev_odev_setup_attribs(udev_device, path, syspath, major(devnum),
141                                       minor(devnum), NewGPUDeviceRequest);
142        return;
143    }
144#endif
145
146    value = udev_device_get_property_value(udev_device, "ID_INPUT");
147    if (!value || !strcmp(value, "0")) {
148        LogMessageVerb(X_INFO, 10,
149                       "config/udev: ignoring device %s without "
150                       "property ID_INPUT set\n", path);
151        return;
152    }
153
154    input_options = input_option_new(NULL, "_source", "server/udev");
155    if (!input_options)
156        return;
157
158    parent = udev_device_get_parent(udev_device);
159    if (parent) {
160        const char *ppath = udev_device_get_devnode(parent);
161        const char *product = udev_device_get_property_value(parent, "PRODUCT");
162        const char *pnp_id = udev_device_get_sysattr_value(parent, "id");
163        unsigned int usb_vendor, usb_model;
164
165        name = udev_device_get_sysattr_value(parent, "name");
166        LOG_SYSATTR(ppath, "name", name);
167        if (!name) {
168            name = udev_device_get_property_value(parent, "NAME");
169            LOG_PROPERTY(ppath, "NAME", name);
170        }
171
172        /* construct USB ID in lowercase hex - "0000:ffff" */
173        if (product &&
174            sscanf(product, "%*x/%4x/%4x/%*x", &usb_vendor, &usb_model) == 2) {
175            char *usb_id;
176            if (asprintf(&usb_id, "%04x:%04x", usb_vendor, usb_model)
177                == -1)
178                usb_id = NULL;
179            else
180                LOG_PROPERTY(ppath, "PRODUCT", product);
181            attrs.usb_id = usb_id;
182        }
183
184        while (!pnp_id && (parent = udev_device_get_parent(parent))) {
185            pnp_id = udev_device_get_sysattr_value(parent, "id");
186            if (!pnp_id)
187                continue;
188
189            attrs.pnp_id = strdup(pnp_id);
190            ppath = udev_device_get_devnode(parent);
191            LOG_SYSATTR(ppath, "id", pnp_id);
192        }
193
194    }
195    if (!name)
196        name = "(unnamed)";
197    else
198        attrs.product = strdup(name);
199    input_options = input_option_new(input_options, "name", name);
200    input_options = input_option_new(input_options, "path", path);
201    input_options = input_option_new(input_options, "device", path);
202    input_options = input_option_new(input_options, "major", itoa(major(devnum)));
203    input_options = input_option_new(input_options, "minor", itoa(minor(devnum)));
204    if (path)
205        attrs.device = strdup(path);
206
207    tags_prop = udev_device_get_property_value(udev_device, "ID_INPUT.tags");
208    LOG_PROPERTY(path, "ID_INPUT.tags", tags_prop);
209    attrs.tags = xstrtokenize(tags_prop, ",");
210
211    if (asprintf(&config_info, "udev:%s", syspath) == -1) {
212        config_info = NULL;
213        goto unwind;
214    }
215
216    if (device_is_duplicate(config_info)) {
217        LogMessage(X_WARNING, "config/udev: device %s already added. "
218                   "Ignoring.\n", name);
219        goto unwind;
220    }
221
222    set = udev_device_get_properties_list_entry(udev_device);
223    udev_list_entry_foreach(entry, set) {
224        key = udev_list_entry_get_name(entry);
225        if (!key)
226            continue;
227        value = udev_list_entry_get_value(entry);
228        if (!strncasecmp(key, UDEV_XKB_PROP_KEY, sizeof(UDEV_XKB_PROP_KEY) - 1)) {
229            LOG_PROPERTY(path, key, value);
230            tmp = key + sizeof(UDEV_XKB_PROP_KEY) - 1;
231            if (!strcasecmp(tmp, "rules"))
232                input_options =
233                    input_option_new(input_options, "xkb_rules", value);
234            else if (!strcasecmp(tmp, "layout"))
235                input_options =
236                    input_option_new(input_options, "xkb_layout", value);
237            else if (!strcasecmp(tmp, "variant"))
238                input_options =
239                    input_option_new(input_options, "xkb_variant", value);
240            else if (!strcasecmp(tmp, "model"))
241                input_options =
242                    input_option_new(input_options, "xkb_model", value);
243            else if (!strcasecmp(tmp, "options"))
244                input_options =
245                    input_option_new(input_options, "xkb_options", value);
246        }
247        else if (!strcmp(key, "ID_VENDOR")) {
248            LOG_PROPERTY(path, key, value);
249            attrs.vendor = strdup(value);
250        } else if (!strncmp(key, "ID_INPUT_", 9)) {
251            const struct pfmap {
252                const char *property;
253                unsigned int flag;
254            } map[] = {
255                { "ID_INPUT_KEY", ATTR_KEY },
256                { "ID_INPUT_KEYBOARD", ATTR_KEYBOARD },
257                { "ID_INPUT_MOUSE", ATTR_POINTER },
258                { "ID_INPUT_JOYSTICK", ATTR_JOYSTICK },
259                { "ID_INPUT_TABLET", ATTR_TABLET },
260                { "ID_INPUT_TABLET_PAD", ATTR_TABLET_PAD },
261                { "ID_INPUT_TOUCHPAD", ATTR_TOUCHPAD },
262                { "ID_INPUT_TOUCHSCREEN", ATTR_TOUCHSCREEN },
263                { NULL, 0 },
264            };
265
266            /* Anything but the literal string "0" is considered a
267             * boolean true. The empty string isn't a thing with udev
268             * properties anyway */
269            if (value && strcmp(value, "0")) {
270                const struct pfmap *m = map;
271
272                while (m->property != NULL) {
273                    if (!strcmp(m->property, key)) {
274                        LOG_PROPERTY(path, key, value);
275                        attrs.flags |= m->flag;
276                    }
277                    m++;
278                }
279            }
280        }
281    }
282
283    input_options = input_option_new(input_options, "config_info", config_info);
284
285    /* Default setting needed for non-seat0 seats */
286    if (ServerIsNotSeat0())
287        input_options = input_option_new(input_options, "GrabDevice", "on");
288
289    LogMessage(X_INFO, "config/udev: Adding input device %s (%s)\n",
290               name, path);
291    rc = NewInputDeviceRequest(input_options, &attrs, &dev);
292    if (rc != Success)
293        goto unwind;
294
295 unwind:
296    free(config_info);
297    input_option_free_list(&input_options);
298
299    free(attrs.usb_id);
300    free(attrs.pnp_id);
301    free(attrs.product);
302    free(attrs.device);
303    free(attrs.vendor);
304    if (attrs.tags) {
305        char **tag = attrs.tags;
306
307        while (*tag) {
308            free(*tag);
309            tag++;
310        }
311        free(attrs.tags);
312    }
313
314    return;
315}
316
317static void
318device_removed(struct udev_device *device)
319{
320    char *value;
321    const char *syspath = udev_device_get_syspath(device);
322
323#ifdef CONFIG_UDEV_KMS
324    const char *subsys = udev_device_get_subsystem(device);
325
326    if (subsys && !strcmp(subsys, "drm")) {
327        const char *sysname = udev_device_get_sysname(device);
328        const char *path = udev_device_get_devnode(device);
329        dev_t devnum = udev_device_get_devnum(device);
330
331        if ((strncmp(sysname,"card", 4) != 0) || (path == NULL))
332            return;
333
334        LogMessage(X_INFO, "config/udev: removing GPU device %s %s\n",
335                   syspath, path);
336        config_udev_odev_setup_attribs(device, path, syspath, major(devnum),
337                                       minor(devnum), DeleteGPUDeviceRequest);
338        /* Retry vtenter after a drm node removal */
339        systemd_logind_vtenter();
340        return;
341    }
342#endif
343
344    if (asprintf(&value, "udev:%s", syspath) == -1)
345        return;
346
347    remove_devices("udev", value);
348
349    free(value);
350}
351
352static void
353socket_handler(int fd, int ready, void *data)
354{
355    struct udev_device *udev_device;
356    const char *action;
357
358    input_lock();
359    udev_device = udev_monitor_receive_device(udev_monitor);
360    if (!udev_device) {
361        input_unlock();
362        return;
363    }
364    action = udev_device_get_action(udev_device);
365    if (action) {
366        if (!strcmp(action, "add")) {
367            device_removed(udev_device);
368            device_added(udev_device);
369        } else if (!strcmp(action, "change")) {
370            /* ignore change for the drm devices */
371            const char *subsys = udev_device_get_subsystem(udev_device);
372
373            if (subsys && strcmp(subsys, "drm")) {
374                device_removed(udev_device);
375                device_added(udev_device);
376            }
377        }
378        else if (!strcmp(action, "remove"))
379            device_removed(udev_device);
380    }
381    udev_device_unref(udev_device);
382    input_unlock();
383}
384
385int
386config_udev_pre_init(void)
387{
388    struct udev *udev;
389
390    udev = udev_new();
391    if (!udev)
392        return 0;
393
394    udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
395    if (!udev_monitor)
396        return 0;
397
398    udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "input",
399                                                    NULL);
400    /* For Wacom serial devices */
401    udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "tty", NULL);
402#ifdef CONFIG_UDEV_KMS
403    /* For output GPU devices */
404    udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "drm", NULL);
405#endif
406
407#ifdef HAVE_UDEV_MONITOR_FILTER_ADD_MATCH_TAG
408    if (ServerIsNotSeat0())
409        udev_monitor_filter_add_match_tag(udev_monitor, SeatId);
410#endif
411    if (udev_monitor_enable_receiving(udev_monitor)) {
412        ErrorF("config/udev: failed to bind the udev monitor\n");
413        return 0;
414    }
415    return 1;
416}
417
418int
419config_udev_init(void)
420{
421    struct udev *udev;
422    struct udev_enumerate *enumerate;
423    struct udev_list_entry *devices, *device;
424
425    udev = udev_monitor_get_udev(udev_monitor);
426    enumerate = udev_enumerate_new(udev);
427    if (!enumerate)
428        return 0;
429
430    udev_enumerate_add_match_subsystem(enumerate, "input");
431    udev_enumerate_add_match_subsystem(enumerate, "tty");
432#ifdef CONFIG_UDEV_KMS
433    udev_enumerate_add_match_subsystem(enumerate, "drm");
434#endif
435
436#ifdef HAVE_UDEV_ENUMERATE_ADD_MATCH_TAG
437    if (ServerIsNotSeat0())
438        udev_enumerate_add_match_tag(enumerate, SeatId);
439#endif
440
441    udev_enumerate_scan_devices(enumerate);
442    devices = udev_enumerate_get_list_entry(enumerate);
443    udev_list_entry_foreach(device, devices) {
444        const char *syspath = udev_list_entry_get_name(device);
445        struct udev_device *udev_device =
446            udev_device_new_from_syspath(udev, syspath);
447
448        /* Device might be gone by the time we try to open it */
449        if (!udev_device)
450            continue;
451
452        device_added(udev_device);
453        udev_device_unref(udev_device);
454    }
455    udev_enumerate_unref(enumerate);
456
457    SetNotifyFd(udev_monitor_get_fd(udev_monitor), socket_handler, X_NOTIFY_READ, NULL);
458
459    return 1;
460}
461
462void
463config_udev_fini(void)
464{
465    struct udev *udev;
466
467    if (!udev_monitor)
468        return;
469
470    udev = udev_monitor_get_udev(udev_monitor);
471
472    RemoveNotifyFd(udev_monitor_get_fd(udev_monitor));
473    udev_monitor_unref(udev_monitor);
474    udev_monitor = NULL;
475    udev_unref(udev);
476}
477
478#ifdef CONFIG_UDEV_KMS
479
480/* Find the last occurrence of the needle in haystack */
481static char *strrstr(const char *haystack, const char *needle)
482{
483    char *prev, *last, *tmp;
484
485    prev = strstr(haystack, needle);
486    if (!prev)
487        return NULL;
488
489    last = prev;
490    tmp = prev + 1;
491
492    while (tmp) {
493        last = strstr(tmp, needle);
494        if (!last)
495            return prev;
496        else {
497            prev = last;
498            tmp = prev + 1;
499        }
500    }
501
502    return last;
503}
504
505/* For certain devices udev does not create ID_PATH entry (which is presumably a bug
506 * in udev). We work around that by implementing a minimal ID_PATH calculator
507 * ourselves along the same logic that udev uses. This works only for the case of
508 * a PCI device being directly connected to a PCI bus, but it will cover most end
509 * users with e.g. a new laptop which only has beta hardware driver support.
510 * See https://gitlab.freedesktop.org/xorg/xserver/-/issues/993 */
511static char*
512config_udev_get_fallback_bus_id(struct udev_device *udev_device)
513{
514    const char *sysname;
515    char *busid;
516
517    udev_device = udev_device_get_parent(udev_device);
518    if (udev_device == NULL)
519        return NULL;
520
521    if (strcmp(udev_device_get_subsystem(udev_device), "pci") != 0)
522        return NULL;
523
524    sysname = udev_device_get_sysname(udev_device);
525    busid = XNFalloc(strlen(sysname) + 5);
526    busid[0] = '\0';
527    strcat(busid, "pci:");
528    strcat(busid, sysname);
529
530    return busid;
531}
532
533static void
534config_udev_odev_setup_attribs(struct udev_device *udev_device, const char *path, const char *syspath,
535                               int major, int minor,
536                               config_odev_probe_proc_ptr probe_callback)
537{
538    struct OdevAttributes *attribs = config_odev_allocate_attributes();
539    const char *value, *str;
540
541    attribs->path = XNFstrdup(path);
542    attribs->syspath = XNFstrdup(syspath);
543    attribs->major = major;
544    attribs->minor = minor;
545
546    value = udev_device_get_property_value(udev_device, "ID_PATH");
547    if (value && (str = strrstr(value, "pci-"))) {
548        value = str;
549
550        if ((str = strstr(value, "usb-")))
551            value = str;
552
553        attribs->busid = XNFstrdup(value);
554        attribs->busid[3] = ':';
555    }
556
557    if (!value)
558        attribs->busid = config_udev_get_fallback_bus_id(udev_device);
559
560    /* ownership of attribs is passed to probe layer */
561    probe_callback(attribs);
562}
563
564void
565config_udev_odev_probe(config_odev_probe_proc_ptr probe_callback)
566{
567    struct udev *udev;
568    struct udev_enumerate *enumerate;
569    struct udev_list_entry *devices, *device;
570
571    udev = udev_monitor_get_udev(udev_monitor);
572    enumerate = udev_enumerate_new(udev);
573    if (!enumerate)
574        return;
575
576    udev_enumerate_add_match_subsystem(enumerate, "drm");
577    udev_enumerate_add_match_sysname(enumerate, "card[0-9]*");
578#ifdef HAVE_UDEV_ENUMERATE_ADD_MATCH_TAG
579    if (ServerIsNotSeat0())
580        udev_enumerate_add_match_tag(enumerate, SeatId);
581#endif
582    udev_enumerate_scan_devices(enumerate);
583    devices = udev_enumerate_get_list_entry(enumerate);
584    udev_list_entry_foreach(device, devices) {
585        const char *syspath = udev_list_entry_get_name(device);
586        struct udev_device *udev_device = udev_device_new_from_syspath(udev, syspath);
587        const char *path = udev_device_get_devnode(udev_device);
588        const char *sysname = udev_device_get_sysname(udev_device);
589        dev_t devnum = udev_device_get_devnum(udev_device);
590        const char *subsys = udev_device_get_subsystem(udev_device);
591
592        if (!path || !syspath || !subsys)
593            goto no_probe;
594        else if (strcmp(subsys, "drm") != 0)
595            goto no_probe;
596        else if (strncmp(sysname, "card", 4) != 0)
597            goto no_probe;
598        else if (!check_seat(udev_device))
599            goto no_probe;
600
601        config_udev_odev_setup_attribs(udev_device, path, syspath, major(devnum),
602                                       minor(devnum), probe_callback);
603    no_probe:
604        udev_device_unref(udev_device);
605    }
606    udev_enumerate_unref(enumerate);
607    return;
608}
609#endif
610
611