--- config/devd.c.orig 2015-05-19 19:41:49 UTC +++ config/devd.c @@ -0,0 +1,737 @@ +/* + * Copyright (c) 2012 Baptiste Daroussin + * Copyright (c) 2013, 2014 Alex Kozlov + * Copyright (c) 2014 Robert Millan + * Copyright (c) 2014 Jean-Sebastien Pedron + * Copyright (c) 2015 - 2016 Rozhuk Ivan + * + * 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 "input.h" +#include "inputstr.h" +#include "hotplug.h" +#include "config-backends.h" +#include "os.h" + + +/* from */ +/* get identifier string */ +#define JSIOCGNAME(len) _IOC(_IOC_READ, 'j', 0x13, len) + +#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) + + +#define _PATH_DEV_LEN (sizeof(_PATH_DEV) - 1) +#define DEVD_PATH_DEV "devd:" _PATH_DEV +#define DEVD_PATH_DEV_LEN (sizeof(DEVD_PATH_DEV) - 1) + +#define DEVD_SOCK_PATH _PATH_VARRUN "devd.pipe" + +#define DEVD_EVENT_ADD '+' +#define DEVD_EVENT_REMOVE '-' +#define DEVD_EVENT_NOTIFY '!' + +#define RECONNECT_DELAY (5 * 1000) + + +#define is_meuqual(__v1, __v1sz, __v2, __v2sz) \ + ((__v1sz) == (__v2sz) && NULL != (__v1) && NULL != (__v2) && \ + 0 == memcmp((__v1), (__v2), (__v1sz))) + +#define is_meuqual_cstr(__cstr, __v, __vsz) \ + is_meuqual(__cstr, (sizeof(__cstr) - 1), __v, __vsz) + +#define is_de_euqual_cstr(__de, __cstr) \ + (NULL != (__de) && \ + is_meuqual((__de)->d_name, (__de)->d_namlen, __cstr, (sizeof(__cstr) - 1))) + +#define devd_get_val_cstr(__cstr, __buf, __bufsz, __valsz) \ + devd_get_val((__buf), (__bufsz), __cstr, (sizeof(__cstr) - 1), \ + (__valsz)) + + +static int devd_skt; +static char devd_buf[4096]; +static size_t devd_buf_used = 0; +static int is_kbdmux = 0; +OsTimerPtr rtimer; + + +/* Input devices. */ +typedef struct hw_type_s { + const char *dev_name; + size_t dev_name_size; + size_t path_offset; + int flags; + const char *xdriver; +} hw_type_t, *hw_type_p; + +/* xdriver can be set via config "InputClass" section. + * Do not set xdriver name if device have more than one + * xf86-input-* drivers. + * "input/event" can be hadled by: xf86-input-evdev and + * xf86-input-wacom, let user chooses. + */ +static hw_type_t hw_types[] = { + { "ukbd", 4, 0, ATTR_KEYBOARD, "kbd" }, + { "atkbd", 5, 0, ATTR_KEYBOARD, "kbd" }, + { "kbdmux", 6, 0, ATTR_KEYBOARD, "kbd" }, + { "sysmouse", 8, 0, ATTR_POINTER, "mouse" }, + { "ums", 3, 0, ATTR_POINTER, "mouse" }, + { "psm", 3, 0, ATTR_POINTER, "mouse" }, + { "vboxguest", 9, 0, ATTR_POINTER, "vboxmouse" }, + { "joy", 3, 0, ATTR_JOYSTICK, NULL }, + { "atp", 3, 0, ATTR_TOUCHPAD, NULL }, + { "uep", 3, 0, ATTR_TOUCHSCREEN, NULL }, + { "input/event",5, 6, ATTR_TOUCHPAD, NULL }, + { "input/js", 2, 6, ATTR_JOYSTICK, NULL }, + { NULL, 0, 0, 0, NULL }, +}; + +/* Input devices paths. */ +static hw_type_t hw_type_path[] = { + { "input/", 0, 6, 0, NULL }, + { NULL, 0, 0, 0, NULL }, +}; + + + +static hw_type_p +get_dev_type_by_name(const char *dev_name, size_t dev_name_size) +{ + size_t i; + + if (NULL == dev_name || 0 == dev_name_size) + return (NULL); + for (i = 0; NULL != hw_types[i].dev_name; i ++) { + if (dev_name_size >= (hw_types[i].dev_name_size + hw_types[i].path_offset) && + 0 == memcmp(dev_name, hw_types[i].dev_name, + (hw_types[i].path_offset + hw_types[i].dev_name_size))) { + return (&hw_types[i]); + } + } + return (NULL); +} + +static hw_type_p +get_dev_type_by_path(const char *dev_name, size_t dev_name_size, + hw_type_p hw_type_cust) +{ + size_t i; + + if (NULL == dev_name || 0 == dev_name_size || NULL == hw_type_cust) + return (NULL); + for (i = 0; NULL != hw_type_path[i].dev_name; i ++) { + if (dev_name_size <= hw_type_path[i].path_offset || + 0 != memcmp(dev_name, hw_type_path[i].dev_name, + hw_type_path[i].path_offset)) + continue; + /* Path in white list. */ + hw_type_cust->dev_name = dev_name; + hw_type_cust->path_offset = hw_type_path[i].path_offset; + for (i = hw_type_cust->path_offset; i < dev_name_size; i ++) { + if (isdigit(dev_name[i])) + break; + } + hw_type_cust->dev_name_size = (i - hw_type_cust->path_offset); + hw_type_cust->flags = hw_type_path[i].flags; + hw_type_cust->xdriver = hw_type_path[i].xdriver; + return (hw_type_cust); + } + return (NULL); +} + +static int +is_kbdmux_enabled(void) +{ + /* Xorg uses /dev/ttyv0 as a console device */ + /* const char device[]="/dev/console"; */ + int fd; + const char *device = _PATH_TTY "v0"; + keyboard_info_t info; + + fd = open(device, O_RDONLY); + if (0 > fd) + return (0); + if (-1 == ioctl(fd, KDGKBINFO, &info) || + 0 != memcmp(info.kb_name, "kbdmux", 6)) { + close(fd); + return (0); + } + close(fd); + return (1); +} + +static char * +sysctl_get_str(const char *sysctlname, size_t *size_ret) +{ + char *dest; + size_t len; + + if (NULL == sysctlname) + return (NULL); + if (0 != sysctlbyname(sysctlname, NULL, &len, NULL, 0)) + return (NULL); + dest = malloc(len + 4); + if (NULL == dest) + return (NULL); + if (0 != sysctlbyname(sysctlname, dest, &len, NULL, 0)) { + free(dest); + return (NULL); + } + dest[len] = 0; + if (NULL != size_ret) + (*size_ret) = len; + return (dest); +} + +static char * +devd_get_val(char *buf, size_t buf_size, const char *val_name, + size_t val_name_size, size_t *val_size) +{ + char *ret, *buf_end, *ptr; + + buf_end = (buf + buf_size); + for (ret = buf; ret != NULL && ret < buf_end;) { + ret = memmem(ret, (buf_end - ret), val_name, val_name_size); + if (ret == NULL) + return (NULL); + /* Found. */ + /* Check: space before or buf+1. */ + if ((buf + 1) < ret && ret[-1] != ' ') { + ret += val_name_size; + continue; + } + /* Check: = after name and size for value. */ + ret += val_name_size; + if ((ret + 1) >= buf_end) + return (NULL); + if ('=' != ret[0]) + continue; + ret ++; + break; + } + if (ret == NULL || val_size == NULL) + return (ret); + /* Calc value data size. */ + ptr = memchr(ret, ' ', (buf_end - ret)); + if (ptr == NULL) /* End of string/last value. */ + ptr = buf_end; + (*val_size) = (ptr - ret); + return (ret); +} + +static void +device_added(const char *dev_name, size_t dev_name_size, int allow_no_device) +{ + int fd; + InputOption *options = NULL; + InputAttributes attrs; + DeviceIntPtr dev_iptr = NULL; + keyboard_info_t kbdi; + mousehw_t mshw; + struct input_id iid; + char *dev_path, config_info[(PATH_MAX + 32)]; + char sysctlname[PATH_MAX], *sdata, *ptr_pid, *ptr_vid; + char pnp_usb_id[PATH_MAX], vendor[PATH_MAX], product[PATH_MAX]; + size_t sdata_size, pid_size, vid_size; + hw_type_t *hwtype, hwtype_cust; + unsigned short vid, pid; + + + if (NULL == dev_name || 0 == dev_name_size || PATH_MAX < dev_name_size) + return; + /* Make dev_name null ended string. */ + memcpy(config_info, DEVD_PATH_DEV, DEVD_PATH_DEV_LEN); + memcpy((config_info + DEVD_PATH_DEV_LEN), dev_name, dev_name_size); + config_info[(DEVD_PATH_DEV_LEN + dev_name_size)] = 0; + /* Set / update pointers to dev_name and dev_path. */ + dev_path = (config_info + 5); /* Skip: "devd:" */ + dev_name = (dev_path + _PATH_DEV_LEN); /* Skip: "/dev/" */ + + /* Is known input device or path? */ + hwtype = get_dev_type_by_name(dev_name, dev_name_size); + if (NULL == hwtype) { + hwtype = get_dev_type_by_path(dev_name, dev_name_size, + &hwtype_cust); + } + if (NULL == hwtype) /* Not found in white list. */ + return; + /* Init and set attributes. */ + memset(&attrs, 0, sizeof(attrs)); + attrs.device = dev_path; + attrs.flags = hwtype->flags; + + /* Skip keyboard devices if kbdmux is enabled */ + if (is_kbdmux && 0 == allow_no_device && (hwtype->flags & ATTR_KEYBOARD)) { + LogMessage(X_INFO, "config/devd: kbdmux is enabled, ignoring device %s\n", + dev_name); + return; + } + /* Skip duplicate devices. */ + if (device_is_duplicate(config_info)) { + LogMessage(X_WARNING, "config/devd: device %s already added. ignoring\n", + dev_path); + return; + } + + /* Try to open device. */ + fd = open(dev_path, O_RDONLY); + if (0 > fd) { + if (0 == (hwtype->flags & ATTR_KEYBOARD)) { + LogMessage(X_INFO, "config/devd: device %s already opened\n", + dev_path); + /* + * Fail if cannot open device, it breaks AllowMouseOpenFail, + * but it should not matter when config/devd enabled + */ + return; + } + if (0 == allow_no_device) { + /* + * There can be only one keyboard attached to console and + * it is already added. + */ + LogMessage(X_WARNING, "config/devd: console keyboard is " + "already added, ignoring %s\n", + dev_path); + return; + } + /* + * 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. + */ + goto skip_ioctl; + } + + /* Try to get device info via ioctl(). */ + if (-1 != ioctl(fd, KDGKBINFO, &kbdi)) { /* Is this keyboard? */ + memcpy(product, kbdi.kb_name, sizeof(kbdi.kb_name)); + attrs.product = product; + attrs.flags = ATTR_KEYBOARD; + LogMessage(X_INFO, "config/devd: detected keyboard: %s, kb_index=%i, kb_unit=%i, kb_type=%i, kb_config=%i\n", + kbdi.kb_name, kbdi.kb_index, kbdi.kb_unit, kbdi.kb_type, kbdi.kb_config); + } else if (-1 != ioctl(fd, MOUSE_GETHWINFO, &mshw)) { /* Is this mouse? */ +#ifdef notyet /* FreeBSD mouse drivers does not return real vid+pid. */ + /* construct USB ID in lowercase hex - "0000:ffff" */ + snprintf(pnp_usb_id, sizeof(pnp_usb_id), "%04x:%04x", + mshw.hwid, mshw.model); + attrs.usb_id = pnp_usb_id; +#endif + + switch (mshw.type) { + case MOUSE_MOUSE: + case MOUSE_TRACKBALL: + case MOUSE_STICK: + attrs.flags = ATTR_POINTER; + break; + case MOUSE_PAD: + attrs.flags = ATTR_TOUCHPAD; + break; + } + LogMessage(X_INFO, "config/devd: detected mouse: hwid=%04x, model=%04x, type=%04x, iftype=%04x, buttons=%04x\n", + mshw.hwid, mshw.model, mshw.type, mshw.iftype, mshw.buttons); + } else if (-1 != ioctl(fd, JSIOCGNAME((sizeof(product) - 1)), product)) { /* Is this joystick? */ + attrs.product = product; + attrs.flags = ATTR_JOYSTICK; + LogMessage(X_INFO, "config/devd: detected joystick: %s\n", + product); + } else if (-1 != ioctl(fd, EVIOCGID, &iid) && + -1 != ioctl(fd, EVIOCGNAME((sizeof(product) - 1)), product)) { /* Is this event? */ + /* construct USB ID in lowercase hex - "0000:ffff" */ + snprintf(pnp_usb_id, sizeof(pnp_usb_id), "%04x:%04x", + iid.vendor, iid.product); + attrs.usb_id = pnp_usb_id; + attrs.product = product; + attrs.flags = (ATTR_POINTER | ATTR_TABLET | ATTR_TOUCHPAD); + LogMessage(X_INFO, "config/devd: detected event: %s, bustype=%04x, vendor=%04x, product=%04x, version=%04x\n", + product, iid.bustype, iid.vendor, iid.product, iid.version); + } + + if (NULL == attrs.usb_id) { /* Is this webcamd device? */ + if (-1 != ioctl(fd, WEBCAMD_IOCTL_GET_USB_VENDOR_ID, &vid) && + -1 != ioctl(fd, WEBCAMD_IOCTL_GET_USB_PRODUCT_ID, &pid)) { + snprintf(pnp_usb_id, sizeof(pnp_usb_id), + "%04x:%04x", vid, pid); + attrs.usb_id = pnp_usb_id; + LogMessage(X_INFO, "config/devd: WebCamD device: %s\n", pnp_usb_id); + } + } + + close(fd); + +skip_ioctl: + /* Try to get device info via sysctl(). */ + if (NULL == attrs.usb_id && NULL == attrs.pnp_id) { + snprintf(sysctlname, sizeof(sysctlname), "dev.%.*s.%s.%%pnpinfo", + (int)hwtype->dev_name_size, (hwtype->dev_name + hwtype->path_offset), + (dev_name + hwtype->path_offset + hwtype->dev_name_size)); + sdata = sysctl_get_str(sysctlname, &sdata_size); + if (NULL != sdata) { + ptr_vid = devd_get_val_cstr("vendor", + sdata, sdata_size, &vid_size); + ptr_pid = devd_get_val_cstr("product", + sdata, sdata_size, &pid_size); + if (NULL != ptr_vid && NULL != ptr_pid) { /* usb_id */ + ptr_vid[vid_size] = 0; + ptr_pid[pid_size] = 0; + snprintf(pnp_usb_id, sizeof(pnp_usb_id), + "%s:%s", ptr_vid, ptr_pid); + attrs.usb_id = pnp_usb_id; + LogMessage(X_INFO, "config/devd: usb_id: %s\n", pnp_usb_id); + } else { /* pnp_id */ + strlcpy(pnp_usb_id, sdata, sizeof(pnp_usb_id)); + attrs.pnp_id = pnp_usb_id; + } + free(sdata); + } + } + if (NULL == attrs.vendor || NULL == attrs.product) { + snprintf(sysctlname, sizeof(sysctlname), "dev.%.*s.%s.%%desc", + (int)hwtype->dev_name_size, (hwtype->dev_name + hwtype->path_offset), + (dev_name + hwtype->path_offset + hwtype->dev_name_size)); + sdata = sysctl_get_str(sysctlname, &sdata_size); + if (NULL != sdata) { + /* Vendor. */ + ptr_pid = memchr(sdata, ' ', sdata_size); + if (NULL != ptr_pid) + ptr_pid[0] = 0; + strlcpy(vendor, sdata, sizeof(vendor)); + attrs.vendor = vendor; + /* Product. */ + if (NULL == attrs.product && NULL != ptr_pid) { + ptr_pid ++; + ptr_vid = memchr(ptr_pid, ',', + (sdata_size - (ptr_pid - sdata))); + if (NULL != ptr_vid) + ptr_vid[0] = 0; + strlcpy(product, ptr_pid, sizeof(product)); + attrs.product = product; + } else { + product[0] = 0; + } + free(sdata); + LogMessage(X_INFO, "config/devd: vendor: %s, product: %s\n", vendor, product); + } + } + + /* Init options. */ + options = input_option_new(NULL, "_source", "server/devd"); + if (NULL == options) + goto err_out; + + options = input_option_new(options, "config_info", config_info); + if (NULL == options) + goto err_out; + + options = input_option_new(options, "name", + ((NULL != attrs.product) ? attrs.product : dev_name)); + if (NULL == options) + goto err_out; + + if (fd >= 0) { + options = input_option_new(options, "device", dev_path); + if (NULL == options) + goto err_out; + } + + if (NULL != hwtype->xdriver) { + options = input_option_new(options, "driver", hwtype->xdriver); + if (NULL == options) + goto err_out; + } + + LogMessage(X_INFO, "config/devd: adding input device %s\n", dev_path); + NewInputDeviceRequest(options, &attrs, &dev_iptr); + goto done; + +err_out: + LogMessage(X_INFO, "config/devd: error adding device '%s'\n", + dev_path); +done: + input_option_free_list(&options); +} + +static void +device_removed(const char *dev_name, size_t dev_name_size) +{ + char config_info[(PATH_MAX + 32)]; + hw_type_t hwtype_cust; + + if (NULL == dev_name || 0 == dev_name_size || PATH_MAX < dev_name_size) + return; + if (NULL == get_dev_type_by_name(dev_name, dev_name_size) && + NULL == get_dev_type_by_path(dev_name, dev_name_size, &hwtype_cust)) + return; /* Device not in list - unknown. */ + memcpy(config_info, DEVD_PATH_DEV, DEVD_PATH_DEV_LEN); + memcpy((config_info + DEVD_PATH_DEV_LEN), dev_name, dev_name_size); + config_info[(DEVD_PATH_DEV_LEN + dev_name_size)] = 0; + + /* Skip non added devices. */ + if (device_is_duplicate(config_info)) { + LogMessage(X_INFO, "config/devd: removing input device '%s'\n", + (config_info + DEVD_PATH_DEV_LEN)); + } + remove_devices("devd", config_info); +} + + +static void +disconnect_devd(int sock) +{ + if (0 > sock) + return; + RemoveGeneralSocket(sock); + close(sock); +} + +static int +connect_devd(void) +{ + int sock; + struct sockaddr_un devd; + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (0 > sock) { + LogMessage(X_ERROR, "config/devd: fail opening stream socket\n"); + return (-1); + } + + devd.sun_family = AF_UNIX; + memcpy(devd.sun_path, DEVD_SOCK_PATH, sizeof(DEVD_SOCK_PATH)); + if (0 > connect(sock, (struct sockaddr*)&devd, sizeof(devd))) { + 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) +{ + devd_buf_used = 0; + devd_skt = connect_devd(); + if (-1 == devd_skt) /* Try again after RECONNECT_DELAY */ + return (RECONNECT_DELAY); + TimerFree(rtimer); + rtimer = NULL; + LogMessage(X_INFO, "config/devd: reopening devd socket\n"); + + return (0); +} + +static void +wakeup_handler(void *data, int err, void *read_mask) +{ + int error; + char *ptr, *line, *val, *cdev; + size_t line_size, val_size, cdev_size; + ssize_t ios; + + if (0 > err) + return; + if (!FD_ISSET(devd_skt, (fd_set*)read_mask)) + return; + /* Read new data. */ + for (;;) { + ios = recv(devd_skt, (devd_buf + devd_buf_used), + (sizeof(devd_buf) - devd_buf_used), MSG_DONTWAIT); + if (0 < ios) { /* Read OK. */ + devd_buf_used += ios; + continue; /* Try to read more. */ + } + /* Something wrong. */ + error = errno; + if (error == EAGAIN) + break; /* All avaible data readed. */ + if (error == EINTR) + continue; + if (sizeof(devd_buf) == devd_buf_used) { /* Message to long, reset buf. */ + devd_buf_used = 0; + continue; + } + /* devd socket is lost */ + disconnect_devd(devd_skt); + rtimer = TimerSet(NULL, 0, 1, reconnect_handler, NULL); + LogMessage(X_WARNING, "config/devd: devd socket read error: %i - %s\n", error, strerror(error)); + return; + } + ptr = memchr(devd_buf, '\n', devd_buf_used); + if (NULL == ptr) + return; + + /* Process data. */ + line = (devd_buf + 1); + for (;;) { + line_size = (ptr - line); + if (DEVD_EVENT_NOTIFY != (*(line - 1))) + goto move_next_event; /* Handle only notify. */ + /* Check: is system=DEVFS. */ + val = devd_get_val_cstr("system", line, line_size, &val_size); + if (!is_meuqual_cstr("DEVFS", val, val_size)) + goto move_next_event; + /* Check: is subsystem=CDEV. */ + val = devd_get_val_cstr("subsystem", line, line_size, &val_size); + if (!is_meuqual_cstr("CDEV", val, val_size)) + goto move_next_event; + /* Get device name. */ + cdev = devd_get_val_cstr("cdev", line, line_size, &cdev_size); + if (NULL == cdev) + goto move_next_event; + /* Get event type. */ + val = devd_get_val_cstr("type", line, line_size, &val_size); + if (is_meuqual_cstr("CREATE", val, val_size)) { + device_added(cdev, cdev_size, 0); + } else if (is_meuqual_cstr("DESTROY", val, val_size)) { + device_removed(cdev, cdev_size); + } + +move_next_event: + line += (line_size + 2); /* Skip '\n' and event type byte. */ + line_size = (line - devd_buf); + if (devd_buf_used <= line_size) { + devd_buf_used = 0; + return; + } + ptr = memchr(line, '\n', (devd_buf_used - line_size)); + if (NULL == ptr) + break; + } + /* Save line without end marker. */ + devd_buf_used -= (line_size - 1); + memmove(devd_buf, (line - 1), devd_buf_used); +} + +static void +block_handler(void *data, struct timeval **tv, void *read_mask) +{ +} + +int +config_devd_init(void) +{ + size_t i, j, dir_cnt, sdir_cnt, tm; + char devicename[PATH_MAX]; + struct dirent *de, **namelist, *sde, **snamelist; + + LogMessage(X_INFO, "config/devd: probing input devices...\n"); + + /* + * Add fake keyboard and give up on keyboards management + * if kbdmux is enabled + */ + is_kbdmux = is_kbdmux_enabled(); + if (is_kbdmux) + device_added("kbdmux0", 7, 1); + + /* Scan /dev/ for devices. */ + dir_cnt = scandir(_PATH_DEV, &namelist, 0, alphasort); + for (i = 0; i < dir_cnt; i ++) { + de = namelist[i]; + if (is_de_euqual_cstr(de, ".") || + is_de_euqual_cstr(de, "..")) { + free(de); + continue; + } + if (DT_DIR != de->d_type) { + device_added(de->d_name, de->d_namlen, 0); + } else { /* Sub folder. */ + snprintf(devicename, sizeof(devicename), + _PATH_DEV "%s", de->d_name); + sdir_cnt = scandir(devicename, &snamelist, 0, alphasort); + for (j = 0; j < sdir_cnt; j ++) { + sde = snamelist[j]; + if (!is_de_euqual_cstr(sde, ".") && + !is_de_euqual_cstr(sde, "..") && + DT_DIR != sde->d_type) { + tm = snprintf(devicename, sizeof(devicename), + "%s/%s", de->d_name, sde->d_name); + device_added(devicename, tm, 0); + } + free(sde); + } + free(snamelist); + } + free(de); + } + free(namelist); + + devd_buf_used = 0; + devd_skt = connect_devd(); + if (-1 == devd_skt) + return (0); + + /* Register wakeup handler */ + RegisterBlockAndWakeupHandlers(block_handler, wakeup_handler, NULL); + + return (1); +} + +void +config_devd_fini(void) +{ + LogMessage(X_INFO, "config/devd: terminating backend...\n"); + + if (rtimer) { + TimerFree(rtimer); + rtimer = NULL; + } + + disconnect_devd(devd_skt); + + RemoveBlockAndWakeupHandlers(block_handler, wakeup_handler, NULL); +}