--- /dev/null 2016-07-11 15:11:00.000000000 +0300 +++ config/devd.c 2016-07-11 15:15:58.959332000 +0300 @@ -0,0 +1,885 @@ +/* + * Copyright (c) 2012 Baptiste Daroussin + * Copyright (c) 2013, 2014 Alex Kozlov + * Copyright (c) 2014 Robert Millan + * Copyright (c) 2014 Jean-Sebastien Pedron + * + * 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: Baptiste Daroussin + */ + +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "input.h" +#include "inputstr.h" +#include "hotplug.h" +#include "config-backends.h" +#include "os.h" + +#define DEVD_SOCK_PATH "/var/run/devd.pipe" + +#define RECONNECT_DELAY (5 * 1000) + +static int sock_devd; +static bool is_kbdmux = false; +static bool is_kernel_evdev = false; +OsTimerPtr rtimer; + +struct hw_type { + const char *driver; + int flag; + const char *xdriver; + const char *sysctldesc; +}; + +static const struct hw_type hw_types0[] = { + { _PATH_DEV "sysmouse", ATTR_POINTER, "mouse", NULL }, + { _PATH_DEV "vboxguest", ATTR_POINTER, "vboxmouse", NULL }, + { NULL, 0, NULL, NULL }, +}; + +static const struct hw_type hw_types1[] = { + { _PATH_DEV "ukbd%d", ATTR_KEYBOARD, "kbd", "ukbd" }, + { _PATH_DEV "atkbd%d", ATTR_KEYBOARD, "kbd", "atkbd" }, + { _PATH_DEV "kbdmux%d", ATTR_KEYBOARD, "kbd", NULL }, + { _PATH_DEV "ums%d", ATTR_POINTER, "mouse", "ums" }, + { _PATH_DEV "psm%d", ATTR_POINTER, "mouse", "psm" }, + { _PATH_DEV "joy%d", ATTR_JOYSTICK, "mouse", "joy" }, + { _PATH_DEV "atp%d", ATTR_TOUCHPAD, "mouse", "atp" }, + { _PATH_DEV "wsp%d", ATTR_TOUCHPAD, "mouse", "wsp" }, + { _PATH_DEV "uep%d", ATTR_TOUCHSCREEN, "mouse", "uep" }, + { _PATH_DEV "input/event%d", 0, "evdev", NULL }, + { NULL, 0, NULL, NULL }, +}; + +/* like basename() but returns a pointer to incoming string */ +static char * +bname(const char *path) +{ + char *bname = NULL; + + if (path != NULL) + bname = strrchr(path, '/'); + return (bname == NULL) ? (char *)path : bname + 1; +} + +struct dev_entry { + SLIST_ENTRY(dev_entry) next; + char name[]; +}; +SLIST_HEAD(dev_list, dev_entry); + +#define dev_list_init(dev_list) SLIST_INIT(dev_list) +#define dev_list_empty(dev_list) SLIST_EMPTY(dev_list) + +static void +dev_list_insert(struct dev_list *devs, const char *devname) +{ + struct dev_entry *dev; + + dev = malloc(offsetof(struct dev_entry, name) + strlen(devname) + 1); + if (dev != NULL) { + strcpy(dev->name, devname); + SLIST_INSERT_HEAD(devs, dev, next); + } +} + +static void +dev_list_destroy(struct dev_list *devs) +{ + struct dev_entry *dev; + + while (!SLIST_EMPTY(devs)) { + dev = SLIST_FIRST(devs); + SLIST_REMOVE_HEAD(devs, next); + free(dev); + } +} + +static bool +dev_list_search(struct dev_list *devs, const char *devname) +{ + struct dev_entry *dev; + + if (devname != NULL) + SLIST_FOREACH(dev, devs, next) + if (strcmp(devname, dev->name) == 0) + return true; + return false; +} + +/* Some definitions from linux/input.h */ +struct input_id { + uint16_t bustype; + uint16_t vendor; + uint16_t product; + uint16_t version; +}; + +#define EVIOCGBIT(ev,len) _IOC(IOC_OUT, 'E', 0x20 + (ev), len) +#define EVIOCGID _IOR('E', 0x02, struct input_id) +#define EVIOCGNAME(len) _IOC(IOC_OUT, 'E', 0x06, len) +#define EVIOCGPHYS(len) _IOC(IOC_OUT, 'E', 0x07, len) + +#define EV_KEY 0x01 +#define EV_REL 0x02 +#define EV_ABS 0x03 +#define BTN_MISC 0x100 +#define BTN_LEFT 0x110 +#define BTN_RIGHT 0x111 +#define BTN_MIDDLE 0x112 +#define BTN_JOYSTICK 0x120 +#define BTN_TOOL_PEN 0x140 +#define BTN_TOOL_FINGER 0x145 +#define BTN_TOUCH 0x14a +#define BTN_STYLUS 0x14b +#define BTN_STYLUS2 0x14c +#define KEY_MAX 0x2ff +#define KEY_CNT (KEY_MAX+1) +#define REL_X 0x00 +#define REL_Y 0x01 +#define REL_MAX 0x0f +#define REL_CNT (REL_MAX+1) +#define ABS_X 0x00 +#define ABS_Y 0x01 +#define ABS_PRESSURE 0x18 +#define ABS_MT_SLOT 0x2f +#define ABS_MAX 0x3f +#define ABS_CNT (ABS_MAX+1) + +#define LONG_BITS (sizeof(long) * 8) +#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS) + +static inline bool +bit_is_set(const unsigned long *array, int bit) +{ + return !!(array[bit / LONG_BITS] & (1LL << (bit % LONG_BITS))); +} + +static inline bool +bit_find(const unsigned long *array, int start, int stop) +{ + int i; + + for (i = start; i < stop; i++) + if (bit_is_set(array, i)) + return true; + + return false; +} + +/* + * Event device type detection routine. + * Derived from EvdevProbe() function of xf86-input-evdev driver + */ +static void +get_evdev_attrs(InputAttributes *attrs, const char *devicename) +{ + int fd, flags; + bool has_keys, has_buttons, has_lmr; + bool has_rel_axes, has_abs_axes, has_mt; + unsigned long key_bits[NLONGS(KEY_CNT)]; + unsigned long rel_bits[NLONGS(REL_CNT)]; + unsigned long abs_bits[NLONGS(ABS_CNT)]; + struct input_id id; + char name[80]; + + flags = 0; + + fd = open(devicename, O_RDONLY | O_CLOEXEC); + if (fd < 0) + goto out; + if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), name) < 0 || + ioctl(fd, EVIOCGID, &id) < 0 || + ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bits)), rel_bits) < 0 || + ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)), abs_bits) < 0 || + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), key_bits) < 0) { + close(fd); + goto out; + } + close(fd); + + *(strchrnul(name, ',')) = '\0'; /* strip name */ + attrs->product = strdup(name); + asprintf(&attrs->usb_id, "%04x:%04x", id.vendor, id.product); + asprintf(&attrs->vendor, "0x%04x", id.vendor); + + has_keys = bit_find(key_bits, 0, BTN_MISC); + has_buttons = bit_find(key_bits, BTN_MISC, BTN_JOYSTICK); + has_lmr = bit_find(key_bits, BTN_LEFT, BTN_MIDDLE); + has_rel_axes = bit_find(rel_bits, 0, REL_MAX); + has_abs_axes = bit_find(abs_bits, 0, ABS_MAX); + has_mt = bit_find(abs_bits, ABS_MT_SLOT, ABS_MAX); + + if (has_abs_axes) { + if (has_mt) { + if (!has_buttons) { + /* + * XXX: I'm not sure that joystick detection is + * done right. xf86-evdev does not support them. + */ + if (bit_is_set(key_bits, BTN_JOYSTICK)) { + flags = ATTR_JOYSTICK; + goto out; + } else { + has_buttons = true; + } + } + } + + if (bit_is_set(abs_bits, ABS_X) && + bit_is_set(abs_bits, ABS_Y)) { + if (bit_is_set(key_bits, BTN_TOOL_PEN) || + bit_is_set(key_bits, BTN_STYLUS) || + bit_is_set(key_bits, BTN_STYLUS2)) { + flags = ATTR_TABLET; + goto out; + } else if (bit_is_set(abs_bits, ABS_PRESSURE) || + bit_is_set(key_bits, BTN_TOUCH)) { + if (has_lmr || + bit_is_set(key_bits, BTN_TOOL_FINGER)) { + flags = ATTR_TOUCHPAD; + } else { + flags = ATTR_TOUCHSCREEN; + } + goto out; + } else if (!(bit_is_set(rel_bits, REL_X) && + bit_is_set(rel_bits, REL_Y)) && + has_lmr) { + /* some touchscreens use BTN_LEFT rather than BTN_TOUCH */ + flags = ATTR_TOUCHSCREEN; + goto out; + } + } + } + + if (has_keys) + flags = ATTR_KEYBOARD; + else if (has_rel_axes || has_abs_axes || has_buttons) + flags = ATTR_POINTER; + +out: + attrs->flags |= flags; +} + +/* Returns list of devices supporting evdev protocol */ +static void +get_evdev_blacklist(struct dev_list *devs) +{ + struct dirent *entry; + DIR *dp; + static const char ev_dir[] = _PATH_DEV "input"; +#define EV_LEN nitems(ev_dir) + char path[PATH_MAX + 1]; + char phys[80]; + int fd; + + snprintf(path, sizeof(path), "%s/", ev_dir); + if ((dp = opendir(ev_dir)) == NULL) + return; + + while ((entry = readdir(dp)) != NULL) { + if (entry->d_type != DT_CHR) + continue; + if (strncmp(entry->d_name, "event", 5) != 0) + continue; + strlcpy(path + EV_LEN, entry->d_name, sizeof(path) - EV_LEN); + fd = open(path, O_RDONLY | O_CLOEXEC); + if (fd < 0) + continue; + /* XXX: Should uinput- and cuse-backed devices be skipped? */ + if (ioctl(fd, EVIOCGPHYS(sizeof(phys) - 1), phys) == 0 && + phys[0] != 0) + dev_list_insert(devs, bname(phys)); + close(fd); + } + closedir(dp); + return; +} + +static void +get_usb_id(char **pptr, int fd) +{ + unsigned short vendor; + unsigned short product; + unsigned int speed; +#define WEBCAMD_IOCTL_GET_USB_VENDOR_ID _IOR('q', 250, unsigned short) +#define WEBCAMD_IOCTL_GET_USB_PRODUCT_ID _IOR('q', 251, unsigned short) +#define WEBCAMD_IOCTL_GET_USB_SPEED _IOR('q', 252, unsigned int) + if (ioctl(fd, WEBCAMD_IOCTL_GET_USB_VENDOR_ID, &vendor) == 0 && + ioctl(fd, WEBCAMD_IOCTL_GET_USB_PRODUCT_ID, &product) == 0 && + ioctl(fd, WEBCAMD_IOCTL_GET_USB_SPEED, &speed) == 0) { + if (asprintf(pptr, "%04x:%04x", vendor, product) == -1) + *pptr = NULL; + } +} + +static char * +get_prop_value(const char *buf, const char *prop, size_t *len) +{ + char *prop_pos; + size_t prop_len; + + prop_len = strlen(prop); + prop_pos = strstr(buf, prop); + if (prop_pos == NULL || + (prop_pos != buf && prop_pos[-1] != ' ') || + prop_pos[prop_len] != '=') + return (NULL); + + *len = strchrnul(prop_pos + prop_len + 1, ' ') - prop_pos - prop_len - 1; + return (prop_pos + prop_len + 1); +} + +static void +get_sysctl_attrs(InputAttributes *attrs, const char* driver, int unit) +{ + char mib[32]; + char name[80], pnpinfo[1024], *pnp_id; + const char *vendorstr, *prodstr, *devicestr; + size_t len, vendorlen, prodlen, devicelen, pnplen; + uint32_t product, vendor; + + snprintf(mib, sizeof(mib), "dev.%s.%d.%%desc", driver, unit); + len = sizeof(name); + if (sysctlbyname(mib, name, &len, NULL, 0) < 0) + return; + *(strchrnul(name, ',')) = '\0'; /* strip name */ + attrs->product = strdup(name); + + snprintf(mib, sizeof(mib), "dev.%s.%d.%%pnpinfo", driver, unit); + len = sizeof(pnpinfo); + if (sysctlbyname(mib, pnpinfo, &len, NULL, 0) < 0) + return; + + vendorstr = get_prop_value(pnpinfo, "vendor", &vendorlen); + prodstr = get_prop_value(pnpinfo, "product", &prodlen); + devicestr = get_prop_value(pnpinfo, "device", &devicelen); + pnp_id = get_prop_value(pnpinfo, "_HID", &pnplen); + if (pnp_id != NULL && pnplen == 4 && strncmp(pnp_id, "none", 4) == 0) + pnp_id = NULL; + if (pnp_id != NULL) { + pnp_id[pnplen] = '\0'; + attrs->pnp_id = strdup(pnp_id); + } + if (prodstr != NULL && vendorstr != NULL) { + /* bus = BUS_USB; */ + vendor = strtol(vendorstr, NULL, 0); + product = strtol(prodstr, NULL, 0); + } else if (devicestr != NULL && vendorstr != NULL) { + /* bus = BUS_PCI; */ + vendor = strtol(vendorstr, NULL, 0); + product = strtol(devicestr, NULL, 0); + } else + return; + + asprintf(&attrs->usb_id, "%04x:%04x", vendor, product); + asprintf(&attrs->vendor, "0x%04x", vendor); + + return; +} + +static const char * +skip_path_dev(const char *ptr) +{ + if (strstr(ptr, _PATH_DEV) == ptr) + ptr += strlen(_PATH_DEV); + return (ptr); +} + +static void +device_added(const char *devicename, struct dev_list *blacklist) +{ + InputAttributes attrs = { }; + InputOption *options = NULL; + char *config_info = NULL; + DeviceIntPtr dev = NULL; + struct hw_type hw_type; + int unit = 0; + int fd = -1; + int i; + bool allow_no_device = false; + + for (i = 0; hw_types0[i].driver != NULL; i++) { + if (strcmp(devicename, hw_types0[i].driver) == 0) { + hw_type = hw_types0[i]; + goto found; + } + } + for (i = 0; hw_types1[i].driver != NULL; i++) { + if (sscanf(devicename, hw_types1[i].driver, &unit) == 1) { + hw_type = hw_types1[i]; + goto found; + } + } + goto ignore; + +found: + if (hw_type.xdriver == NULL) + goto ignore; + + /* set flags, if any */ + attrs.flags |= hw_type.flag; + + if (strcmp(hw_type.xdriver, "evdev") == 0) { + get_evdev_attrs(&attrs, devicename); + /* Set keyboard rules explicitly for libinput */ + if (attrs.flags & ATTR_KEYBOARD) { + options = input_option_new(options, "xkb_rules", + "evdev"); + if (options == NULL) + goto error; + } + } else { + if (is_kernel_evdev) { + if (dev_list_empty(blacklist)) + get_evdev_blacklist(blacklist); + /* + * Prefer evdev input kernel interface to native one. + * Assume that both evdev 'physical path' and non-evdev + * character device path endings are device names so + * we can compare them and skip latter. + */ + if (dev_list_search(blacklist, bname(devicename))) + goto ignore; + } + } + + if (strcmp(hw_type.xdriver, "kbd") == 0) { + bool match = (strstr(hw_type.driver, + _PATH_DEV "kbdmux") == hw_type.driver); + + if (is_kbdmux) { + allow_no_device = true; + if (!match) + goto ignore; + } else { + if (match) + goto ignore; + } + } + + options = input_option_new(options, "_source", "server/devd"); + if (options == NULL) + goto error; + + if (hw_type.sysctldesc != NULL) + get_sysctl_attrs(&attrs, hw_type.sysctldesc, unit); + + if (attrs.product == NULL) + attrs.product = strdup(skip_path_dev(devicename)); + + options = input_option_new(options, "name", attrs.product); + if (options == NULL) + goto error; + + attrs.device = strdup(devicename); + + fd = open(devicename, O_RDONLY); + if (fd > -1) { + if (attrs.usb_id == NULL) + get_usb_id(&attrs.usb_id, fd); + close(fd); + options = input_option_new(options, "device", devicename); + if (options == NULL) + goto error; + } else if (allow_no_device) { + /* + * Don't pass "device" option if the keyboard is + * already attached to the console (ie. open() fails). + * This would activate a special logic in + * xf86-input-keyboard. Prevent any other attached to + * console keyboards being processed. There can be + * only one such device. + */ + } else { + goto ignore; + } + + options = input_option_new(options, "driver", hw_type.xdriver); + if (options == NULL) + goto error; + + if (asprintf(&config_info, "devd:%s", + skip_path_dev(devicename)) == -1) { + config_info = NULL; + goto error; + } + + if (device_is_duplicate(config_info)) + goto duplicate; + + options = input_option_new(options, "config_info", config_info); + if (options == NULL) + goto error; + + LogMessage(X_INFO, "config/devd: adding input device '%s'\n", + devicename); + + NewInputDeviceRequest(options, &attrs, &dev); + goto done; + +duplicate: + LogMessage(X_WARNING, "config/devd: device '%s' already " + "added. Ignoring\n", devicename); + goto done; + +error: + LogMessage(X_INFO, "config/devd: error adding device '%s'\n", + devicename); + goto done; + +ignore: + LogMessage(X_INFO, "config/devd: ignoring device '%s'\n", + devicename); + goto done; + +done: + free(config_info); + input_option_free_list(&options); + free(attrs.usb_id); + free(attrs.pnp_id); + free(attrs.product); + free(attrs.device); + free(attrs.vendor); +} + +static void +devpath_scan_sub(char *path, int off, int rem, void *udata) +{ + struct dirent *entry; + DIR *dp; + + if ((dp = opendir(path)) == NULL) { + LogMessage(X_INFO, "Cannot open directory '%s'\n", path); + return; + } + while ((entry = readdir(dp)) != NULL) { + int len = strlen(entry->d_name); + if (len > rem) + continue; + strcpy(path + off, entry->d_name); + off += len; + rem -= len; + switch (entry->d_type) { + case DT_DIR: + if (strcmp(entry->d_name, ".") == 0 || + strcmp(entry->d_name, "..") == 0) + break; + if (rem < 1) + break; + path[off] = '/'; + path[off+1] = '\0'; + off++; + rem--; + /* recurse */ + devpath_scan_sub(path, off, rem, udata); + off--; + rem++; + break; + case DT_SOCK: + case DT_FIFO: + case DT_LNK: + case DT_CHR: + /* add device, if any */ + device_added(path, udata); + break; + default: + break; + } + off -= len; + rem += len; + } + closedir(dp); +} + +static void +devpath_scan(void *udata) +{ + char path[PATH_MAX + 1]; + + strlcpy(path, _PATH_DEV, sizeof(path)); + + devpath_scan_sub(path, strlen(path), PATH_MAX - strlen(path), udata); +} + +static void +device_removed(char *devicename) +{ + char *config_info; + + if (asprintf(&config_info, "devd:%s", + skip_path_dev(devicename)) == -1) + return; + + if (device_is_duplicate(config_info)) { + LogMessage(X_INFO, "config/devd: removing input device '%s'\n", + devicename); + } + remove_devices("devd", config_info); + + free(config_info); +} + +static bool is_kbdmux_enabled(void) +{ + /* Xorg uses /dev/ttyv0 as a console device */ + static const char device[]= { _PATH_DEV "ttyv0" }; + keyboard_info_t info; + int fd; + + fd = open(device, O_RDONLY); + + if (fd < 0) + return false; + + if (ioctl(fd, KDGKBINFO, &info) == -1) { + close(fd); + return false; + } + + close(fd); + + if (!strncmp(info.kb_name, "kbdmux", 6)) + return true; + + return false; +} + +static void +disconnect_devd(int sock) +{ + if (sock >= 0) { + RemoveGeneralSocket(sock); + close(sock); + } +} + +static int +connect_devd(void) +{ + struct sockaddr_un devd; + int sock; + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + LogMessage(X_ERROR, "config/devd: fail opening stream socket\n"); + return -1; + } + + devd.sun_family = AF_UNIX; + strlcpy(devd.sun_path, DEVD_SOCK_PATH, sizeof(devd.sun_path)); + + if (connect(sock, (struct sockaddr *) &devd, sizeof(devd)) < 0) { + close(sock); + LogMessage(X_ERROR, "config/devd: fail to connect to devd\n"); + return -1; + } + + AddGeneralSocket(sock); + + return sock; +} + +static CARD32 +reconnect_handler(OsTimerPtr timer, CARD32 time, void *arg) +{ + int newsock; + + if ((newsock = connect_devd()) > 0) { + sock_devd = newsock; + TimerFree(rtimer); + rtimer = NULL; + LogMessage(X_INFO, "config/devd: reopening devd socket\n"); + return 0; + } + + /* Try again after RECONNECT_DELAY */ + return RECONNECT_DELAY; +} + +static ssize_t +socket_getline(int fd, char **out) +{ + char *buf, *newbuf; + ssize_t ret, cap, sz = 0; + char c; + + cap = 1024; + buf = malloc(cap * sizeof(char)); + if (!buf) + return -1; + + for (;;) { + ret = read(sock_devd, &c, 1); + if (ret < 0) { + if (errno == EINTR) + continue; + free(buf); + return -1; + /* EOF - devd socket is lost */ + } else if (ret == 0) { + disconnect_devd(sock_devd); + rtimer = TimerSet(NULL, 0, 1, reconnect_handler, NULL); + LogMessage(X_WARNING, "config/devd: devd socket is lost\n"); + return -1; + } + if (c == '\n') + break; + + if (sz + 1 >= cap) { + cap *= 2; + newbuf = realloc(buf, cap * sizeof(char)); + if (!newbuf) { + free(buf); + return -1; + } + buf = newbuf; + } + buf[sz] = c; + sz++; + } + + buf[sz] = '\0'; + if (sz >= 0) + *out = buf; + else + free(buf); + + /* Number of bytes in the line, not counting the line break */ + return sz; +} + +static void +wakeup_handler(void *data, int err, void *read_mask) +{ + static const char cdev_create[] = { "!system=DEVFS subsystem=CDEV type=CREATE cdev=" }; + static const char cdev_destroy[] = { "!system=DEVFS subsystem=CDEV type=DESTROY cdev=" }; + static const char cdev_path[] = { _PATH_DEV }; + char *line = NULL; + char *devicename; + char *walk; + struct dev_list blacklist; + + if (err < 0) + return; + + if (FD_ISSET(sock_devd, (fd_set *) read_mask)) { + if (socket_getline(sock_devd, &line) < 0) + return; + if (strstr(line, cdev_create) == line) { + devicename = line + strlen(cdev_create) - strlen(cdev_path); + memcpy(devicename, cdev_path, strlen(cdev_path)); + walk = strchr(devicename, ' '); + if (walk != NULL) + walk[0] = '\0'; + /* Blacklist is lazy-populated in device_added() */ + dev_list_init(&blacklist); + device_added(devicename, &blacklist); + dev_list_destroy(&blacklist); + } else if (strstr(line, cdev_destroy) == line) { + devicename = line + strlen(cdev_destroy) - strlen(cdev_path); + memcpy(devicename, cdev_path, strlen(cdev_path)); + walk = strchr(devicename, ' '); + if (walk != NULL) + walk[0] = '\0'; + device_removed(devicename); + } + free(line); + } +} + +static void +block_handler(void *data, struct timeval **tv, void *read_mask) +{ +} + +int +config_devd_init(void) +{ + struct dev_list blacklist; + + LogMessage(X_INFO, "config/devd: probing input devices...\n"); + + /* Blacklist is lazy-populated in device_added() */ + dev_list_init(&blacklist); + + /* Check if kbdmux is enabled */ + is_kbdmux = is_kbdmux_enabled(); + + /* Check if evdev support is compiled into kernel */ + is_kernel_evdev = feature_present("evdev") != 0; + + /* Connect to devd, so that we don't loose any events */ + if ((sock_devd = connect_devd()) < 0) + return 0; + + /* Scan what is currently connected */ + devpath_scan(&blacklist); + + /* Register wakeup handler */ + RegisterBlockAndWakeupHandlers(block_handler, + wakeup_handler, NULL); + + dev_list_destroy(&blacklist); + + return 1; +} + +void +config_devd_fini(void) +{ + LogMessage(X_INFO, "config/devd: terminating backend...\n"); + + if (rtimer) { + TimerFree(rtimer); + rtimer = NULL; + } + + disconnect_devd(sock_devd); + + RemoveBlockAndWakeupHandlers(block_handler, + wakeup_handler, NULL); +}