├── .cirrus.yml ├── .gitignore ├── README ├── libudev.h ├── meson.build ├── udev-device.c ├── udev-device.h ├── udev-enumerate.c ├── udev-filter.c ├── udev-filter.h ├── udev-list.c ├── udev-list.h ├── udev-monitor.c ├── udev-utils.c ├── udev-utils.h ├── udev.c ├── udev.h ├── utils.c └── utils.h /.cirrus.yml: -------------------------------------------------------------------------------- 1 | env: 2 | CIRRUS_CLONE_DEPTH: 1 3 | ARCH: amd64 4 | 5 | task: 6 | freebsd_instance: 7 | matrix: 8 | image_family: freebsd-13-0-snap 9 | image_family: freebsd-12-1 10 | image: freebsd-11-3-stable-amd64-v20190801 11 | install_script: 12 | - sed -i.bak -e 's,quarterly,latest,' /etc/pkg/FreeBSD.conf 13 | - env ASSUME_ALWAYS_YES=yes pkg bootstrap -f 14 | - pkg upgrade -y 15 | - pkg install -y evdev-proto meson ninja pkgconf 16 | script: 17 | - env CPPFLAGS='-isystem /usr/local/include' CFLAGS='-isystem /usr/local/include' meson _build 18 | - ninja -v -C _build 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.pc 3 | *.la 4 | *.lo 5 | *.swp 6 | *~ 7 | *.sig 8 | *.tar.* 9 | *.announce 10 | *.patch 11 | *.rej 12 | *.trs 13 | Makefile 14 | Makefile.in 15 | aclocal.m4 16 | autom4te.cache/ 17 | compile 18 | config.guess 19 | config.h 20 | config.h.in 21 | config.log 22 | config.status 23 | config.sub 24 | configure 25 | depcomp 26 | install-sh 27 | libtool 28 | ltmain.sh 29 | missing 30 | stamp-h1 31 | .libs/ 32 | .deps/ 33 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | libudev-compatible interface for devd 2 | ===================================== 3 | 4 | Intended to work with xorg-server and libinput 5 | 6 | Installation: 7 | 8 | 1. Install multimedia/v4l_compat port. In case of evdev-enabled kernels it 9 | can be patched to use system evdev headers instead of webcamd-supplied 10 | 11 | 2. Build and install libudev-devd 12 | 13 | export CFLAGS=-I/usr/local/include 14 | export CPPFLAGS=-I/usr/local/include 15 | cd libudev-devd 16 | ./autogen.sh 17 | ./configure 18 | make && sudo make install 19 | 20 | 3. recompile x11-servers/xorg-server with DEVD port option disabled and 21 | following configure args added: --enable-config-udev=yes \ 22 | --disable-config-udev-kms --disable-systemd-logind 23 | 24 | If you are going to use /dev/kbdmux0 as keyboard input device patch[1] should 25 | be applied to config/udev.c file in xorg distribution 26 | 27 | 4. Apply changes to xorg.conf 28 | 29 | Remove all InputDevice from "ServerLayout" section of xorg.conf 30 | 31 | For gsoc2014 evdev-enabled kernels add following lines to xorg.conf 32 | 33 | <<< CUT 34 | 35 | # Use the libinput driver for all event devices 36 | Section "InputClass" 37 | Identifier "evdev" 38 | Driver "libinput" 39 | # Driver "evdev" 40 | MatchDevicePath "/dev/input/event*" 41 | EndSection 42 | 43 | # Explicitly set xkb_rules to evdev for keyboards 44 | Section "InputClass" 45 | Identifier "evdev keyboard" 46 | MatchIsKeyboard "on" 47 | MatchDevicePath "/dev/input/event*" 48 | Option "XkbRules" "evdev" 49 | EndSection 50 | 51 | # Disable kbdmux to not receive keyboard events twice 52 | Section "InputClass" 53 | Identifier "evdev disable kbdmux" 54 | MatchIsKeyboard "on" 55 | MatchProduct "System keyboard multiplexer" 56 | MatchDevicePath "/dev/input/event*" 57 | Option "Ignore" "true" 58 | EndSection 59 | 60 | << 153 | + #include 154 | ++#include 155 | + #include 156 | + 157 | + #include "input.h" 158 | +@@ -188,6 +189,20 @@ device_added(struct udev_device *udev_de 159 | + attrs.product = strdup(name); 160 | + input_options = input_option_new(input_options, "name", name); 161 | + input_options = input_option_new(input_options, "path", path); 162 | ++ if(strstr(path, "kbdmux") != NULL) { 163 | ++ /* 164 | ++ * Don't pass "device" option if the keyboard is already attached 165 | ++ * to the console (ie. open() fails). This would activate a special 166 | ++ * logic in xf86-input-keyboard. Prevent any other attached to console 167 | ++ * keyboards being processed. There can be only one such device. 168 | ++ */ 169 | ++ int fd = open(path, O_RDONLY); 170 | ++ if (fd > -1) { 171 | ++ close(fd); 172 | ++ input_options = input_option_new(input_options, "device", path); 173 | ++ } 174 | ++ } 175 | ++ else 176 | + input_options = input_option_new(input_options, "device", path); 177 | + input_options = input_option_new(input_options, "major", itoa(major(devnum))); 178 | + input_options = input_option_new(input_options, "minor", itoa(minor(devnum))); 179 | << 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | struct udev; 11 | struct udev_list_entry; 12 | struct udev_device; 13 | struct udev_monitor; 14 | struct udev_enumerate; 15 | 16 | struct udev *udev_new(void); 17 | struct udev *udev_ref(struct udev *udev); 18 | void udev_unref(struct udev *udev); 19 | const char *udev_get_dev_path(struct udev *udev); 20 | void *udev_get_userdata(struct udev *udev); 21 | void udev_set_userdata(struct udev *udev, void *userdata); 22 | 23 | struct udev_device *udev_device_new_from_syspath(struct udev *udev, 24 | const char *syspath); 25 | struct udev_device *udev_device_new_from_devnum(struct udev *udev, 26 | char type, dev_t devnum); 27 | struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, 28 | const char *subsystem, const char *sysname); 29 | struct udev_device *udev_device_ref(struct udev_device *udev_device); 30 | void udev_device_unref(struct udev_device *udev_device); 31 | char const *udev_device_get_devnode(struct udev_device *udev_device); 32 | char const *udev_device_get_property_value(struct udev_device *udev_device, 33 | char const *property); 34 | char const *udev_device_get_sysattr_value( 35 | struct udev_device *udev_device, const char *sysattr); 36 | int udev_device_set_sysattr_value( 37 | struct udev_device *udev_device, const char *sysattr, const char *value); 38 | struct udev_list_entry * udev_device_get_properties_list_entry( 39 | struct udev_device *udev_device); 40 | struct udev_list_entry * udev_device_get_sysattr_list_entry( 41 | struct udev_device *udev_device); 42 | struct udev_list_entry * udev_device_get_tags_list_entry( 43 | struct udev_device *udev_device); 44 | struct udev_list_entry * udev_device_get_devlinks_list_entry( 45 | struct udev_device *udev_device); 46 | struct udev *udev_device_get_udev(struct udev_device *udev_device); 47 | const char *udev_device_get_syspath(struct udev_device *udev_device); 48 | const char *udev_device_get_sysname(struct udev_device *udev_device); 49 | const char *udev_device_get_subsystem(struct udev_device *udev_device); 50 | struct udev_device *udev_device_get_parent(struct udev_device *udev_device); 51 | struct udev_device *udev_device_get_parent_with_subsystem_devtype( 52 | struct udev_device *udev_device, const char *subsystem, const char *devtype); 53 | int udev_device_get_is_initialized(struct udev_device *udev_device); 54 | dev_t udev_device_get_devnum(struct udev_device *udev_device); 55 | const char *udev_device_get_devtype(struct udev_device *udev_device); 56 | const char *udev_device_get_driver(struct udev_device *udev_device); 57 | const char *udev_device_get_sysnum(struct udev_device *udev_device); 58 | unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device); 59 | unsigned long long int udev_device_get_usec_since_initialized( 60 | struct udev_device *udev_device); 61 | 62 | struct udev_enumerate *udev_enumerate_new(struct udev *udev); 63 | struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate); 64 | void udev_enumerate_unref(struct udev_enumerate *udev_enumerate); 65 | int udev_enumerate_add_match_subsystem( 66 | struct udev_enumerate *udev_enumerate, const char *subsystem); 67 | int udev_enumerate_add_nomatch_subsystem( 68 | struct udev_enumerate *udev_enumerate, const char *subsystem); 69 | int udev_enumerate_add_match_sysname( 70 | struct udev_enumerate *udev_enumerate, const char *sysname); 71 | int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, 72 | const char *sysattr, const char *value); 73 | int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, 74 | const char *sysattr, const char *value); 75 | int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, 76 | const char *property, const char *value); 77 | int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, 78 | const char *tag); 79 | int udev_enumerate_add_match_is_initialized( 80 | struct udev_enumerate *udev_enumerate); 81 | int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate); 82 | int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate); 83 | struct udev_list_entry *udev_enumerate_get_list_entry( 84 | struct udev_enumerate *udev_enumerate); 85 | int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, 86 | const char *syspath); 87 | struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate); 88 | 89 | struct udev_list_entry *udev_list_entry_get_next( 90 | struct udev_list_entry *list_entry); 91 | const char *udev_list_entry_get_name( 92 | struct udev_list_entry *list_entry); 93 | const char *udev_list_entry_get_value(struct udev_list_entry *list_entry); 94 | #define udev_list_entry_foreach(list_entry, first_entry) \ 95 | for (list_entry = first_entry; \ 96 | list_entry != NULL; \ 97 | list_entry = udev_list_entry_get_next(list_entry)) 98 | 99 | struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, 100 | const char *name); 101 | struct udev_monitor *udev_monitor_ref(struct udev_monitor *um); 102 | void udev_monitor_unref(struct udev_monitor *udev_monitor); 103 | int udev_monitor_filter_add_match_subsystem_devtype( 104 | struct udev_monitor *udev_monitor, const char *subsystem, 105 | const char *devtype); 106 | int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor); 107 | int udev_monitor_get_fd(struct udev_monitor *udev_monitor); 108 | struct udev_device *udev_monitor_receive_device( 109 | struct udev_monitor *udev_monitor); 110 | const char *udev_device_get_action(struct udev_device *udev_device); 111 | struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor); 112 | 113 | #ifdef __cplusplus 114 | } /* extern "C" */ 115 | #endif 116 | 117 | #endif /* LIBUDEV_H_ */ 118 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('libudev-devd', 'c', 2 | version : '0.4.2', 3 | license : 'BSD2CLAUSE', 4 | default_options : [ 5 | 'buildtype=debugoptimized', 6 | 'warning_level=1', 7 | 'c_std=c11', 8 | 'werror=true' ], 9 | meson_version : '>=0.50.0') 10 | 11 | libudevdevd_version = meson.project_version().split('.') 12 | 13 | dir_data = join_paths(get_option('prefix'), get_option('datadir')) 14 | dir_sysconf = join_paths(get_option('prefix'), get_option('sysconfdir')) 15 | dir_libexec = join_paths(get_option('prefix'), get_option('libexecdir')) 16 | dir_lib = join_paths(get_option('prefix'), get_option('libdir')) 17 | dir_src = join_paths(meson.source_root()) 18 | 19 | # Compiler setup 20 | cc = meson.get_compiler('c') 21 | cflags = ['-fvisibility=hidden'] 22 | add_project_arguments(cflags, language: 'c') 23 | 24 | # config.h 25 | config_h = configuration_data() 26 | config_h_inc = include_directories('.') 27 | if get_option('buildtype') == 'debug' or get_option('buildtype') == 'debugoptimized' 28 | config_h.set_quoted('MESON_BUILD_ROOT', meson.build_root()) 29 | else 30 | config_h.set_quoted('MESON_BUILD_ROOT', '') 31 | endif 32 | 33 | if cc.has_header('devinfo.h') 34 | devinfo_dep = cc.find_library('devinfo') 35 | config_h.set('HAVE_DEVINFO_H', '1') 36 | endif 37 | 38 | procstat_inc = '''#include 39 | #include 40 | #include 41 | ''' 42 | if cc.has_header_symbol('libprocstat.h', 'procstat_open_sysctl', prefix : procstat_inc) 43 | procstat_dep = cc.find_library('procstat') 44 | config_h.set('HAVE_LIBPROCSTAT_H', '1') 45 | endif 46 | 47 | if cc.has_header('linux/input.h') 48 | config_h.set('HAVE_LINUX_INPUT_H', '1') 49 | endif 50 | 51 | if cc.has_function('pipe2') 52 | config_h.set('HAVE_PIPE2', '1') 53 | endif 54 | 55 | if cc.has_function('strchrnul') 56 | config_h.set('HAVE_STRCHRNUL', '1') 57 | endif 58 | 59 | libudevdevd_so_version = '0.0.0' 60 | # Dependencies 61 | thread_dep = dependency('threads') 62 | 63 | pkgconfig = import('pkgconfig') 64 | 65 | install_headers('libudev.h') 66 | src_libudevdevd = [ 'udev.c', 67 | 'udev-device.c', 68 | 'udev-device.h', 69 | 'udev-enumerate.c', 70 | 'udev-filter.c', 71 | 'udev-filter.h', 72 | 'udev-list.c', 73 | 'udev-list.h', 74 | 'udev-monitor.c', 75 | 'udev-utils.c', 76 | 'udev-utils.h', 77 | 'utils.c', 78 | 'utils.h' 79 | ] 80 | 81 | deps_libudevdevd = [ 82 | thread_dep, 83 | devinfo_dep, 84 | procstat_dep 85 | ] 86 | 87 | lib_libudevdevd = shared_library('udev', 88 | src_libudevdevd, 89 | include_directories : config_h_inc, 90 | dependencies : deps_libudevdevd, 91 | version : libudevdevd_so_version, 92 | install : true 93 | ) 94 | 95 | pkgconfig.generate(lib_libudevdevd, 96 | name : 'libudev', 97 | url : 'https://github.com/FreeBSDDesktop/libudev-devd', 98 | description : 'Library to access udev device information', 99 | version : '199', # XXX - should be a proper version 100 | ) 101 | 102 | # output files 103 | configure_file(output : 'config.h', install : false, configuration : config_h) 104 | -------------------------------------------------------------------------------- /udev-device.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Vladimir Kondratyev 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include "config.h" 28 | #include "libudev.h" 29 | #include "udev.h" 30 | #include "udev-device.h" 31 | #include "udev-filter.h" 32 | #include "udev-list.h" 33 | #include "udev-utils.h" 34 | #include "utils.h" 35 | 36 | #include 37 | #include 38 | #include 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | struct udev_device { 49 | _Atomic(int) refcount; 50 | struct { 51 | unsigned int action : 2; 52 | unsigned int is_parent : 1; 53 | } flags; 54 | struct udev_list prop_list; 55 | struct udev_list sysattr_list; 56 | struct udev_list tag_list; 57 | struct udev_list devlink_list; 58 | struct udev *udev; 59 | struct udev_device *parent; 60 | char syspath[]; 61 | }; 62 | 63 | LIBUDEV_EXPORT struct udev_device * 64 | udev_device_new_from_syspath(struct udev *udev, const char *syspath) 65 | { 66 | 67 | TRC("(%s)", syspath); 68 | return (udev_device_new_common(udev, syspath, UD_ACTION_NONE)); 69 | } 70 | 71 | LIBUDEV_EXPORT struct udev_device * 72 | udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum) 73 | { 74 | char devpath[DEV_PATH_MAX] = DEV_PATH_ROOT "/"; 75 | char devbuf[32], buf[32], *devbufptr; 76 | const char *syspath; 77 | struct udev_device *device, *parent; 78 | size_t dev_len; 79 | struct stat st; 80 | size_t buflen; 81 | 82 | dev_len = strlen(devpath); 83 | devname_r(devnum, S_IFCHR, devpath + dev_len, sizeof(devpath) - dev_len); 84 | 85 | /* Recheck path as devname_r returns zero-terminated garbage on error */ 86 | if (stat(devpath, &st) != 0 || st.st_rdev != devnum) { 87 | TRC("(%d) -> failed", (int)devnum); 88 | return NULL; 89 | } 90 | 91 | TRC("(%d) -> %s", (int)devnum, devpath); 92 | syspath = get_syspath_by_devpath(devpath); 93 | strncpy(devbuf, devpath + 1, 31); 94 | devbufptr = devbuf; 95 | devbufptr = strchrnul(devbufptr, '/'); 96 | while (*devbufptr != '\0') { 97 | *devbufptr = '.'; 98 | devbufptr = strchrnul(devbufptr, '/'); 99 | } 100 | snprintf(buf, 32, "%.24s.PCI_ID", devbuf); 101 | buflen = 32; 102 | 103 | sysctlbyname(buf, devbuf, &buflen, NULL, 0); 104 | 105 | device = udev_device_new_common(udev, syspath, UD_ACTION_NONE); 106 | parent = udev_device_new_common(udev, syspath, UD_ACTION_NONE); 107 | udev_list_insert(&parent->prop_list, "PCI_ID", devbuf); 108 | udev_device_set_parent(device, parent); 109 | return (device); 110 | } 111 | 112 | LIBUDEV_EXPORT struct udev_device * 113 | udev_device_new_from_subsystem_sysname(struct udev *udev, 114 | const char *subsystem, const char *sysname) 115 | { 116 | 117 | TRC("(%s, %s)", subsystem, sysname); 118 | UNIMPL(); 119 | return (NULL); 120 | } 121 | 122 | LIBUDEV_EXPORT char const * 123 | udev_device_get_devnode(struct udev_device *ud) 124 | { 125 | const char *devpath; 126 | 127 | TRC("(%p) %s", ud, ud->syspath); 128 | devpath = get_devpath_by_syspath(ud->syspath); 129 | return (devpath); 130 | } 131 | 132 | struct udev_list * 133 | udev_device_get_properties_list(struct udev_device *ud) 134 | { 135 | 136 | return (&ud->prop_list); 137 | } 138 | 139 | LIBUDEV_EXPORT struct udev_list_entry * 140 | udev_device_get_properties_list_entry(struct udev_device *ud) 141 | { 142 | 143 | TRC("(%p(%s))", ud, ud->syspath); 144 | return (udev_list_entry_get_first(udev_device_get_properties_list(ud))); 145 | } 146 | 147 | struct udev_list * 148 | udev_device_get_sysattr_list(struct udev_device *ud) 149 | { 150 | 151 | return (&ud->sysattr_list); 152 | } 153 | 154 | LIBUDEV_EXPORT struct udev_list_entry * 155 | udev_device_get_sysattr_list_entry(struct udev_device *ud) 156 | { 157 | 158 | 159 | TRC("(%p(%s))", ud, ud->syspath); 160 | return (udev_list_entry_get_first(udev_device_get_sysattr_list(ud))); 161 | } 162 | 163 | struct udev_list * 164 | udev_device_get_tags_list(struct udev_device *ud) 165 | { 166 | 167 | return (&ud->tag_list); 168 | } 169 | 170 | LIBUDEV_EXPORT struct udev_list_entry * 171 | udev_device_get_tags_list_entry(struct udev_device *ud) 172 | { 173 | 174 | TRC("(%p(%s))", ud, ud->syspath); 175 | return (udev_list_entry_get_first(udev_device_get_tags_list(ud))); 176 | } 177 | 178 | struct udev_list * 179 | udev_device_get_devlinks_list(struct udev_device *ud) 180 | { 181 | 182 | return (&ud->devlink_list); 183 | } 184 | 185 | LIBUDEV_EXPORT struct udev_list_entry * 186 | udev_device_get_devlinks_list_entry(struct udev_device *ud) 187 | { 188 | 189 | TRC("(%p(%s))", ud, ud->syspath); 190 | return (udev_list_entry_get_first(udev_device_get_devlinks_list(ud))); 191 | } 192 | 193 | LIBUDEV_EXPORT char const * 194 | udev_device_get_property_value(struct udev_device *ud, char const *property) 195 | { 196 | char const *key, *value; 197 | struct udev_list_entry *entry; 198 | 199 | udev_list_entry_foreach(entry, udev_list_entry_get_first(&ud->prop_list)) { 200 | key = _udev_list_entry_get_name(entry); 201 | if (!key) 202 | continue; 203 | if (strcmp(key, property) == 0) { 204 | value = _udev_list_entry_get_value(entry); 205 | TRC("(%p(%s), %s) %s", ud, ud->syspath, property, value); 206 | return (value); 207 | } 208 | } 209 | TRC("(%p(%s), %s) NULL", ud, ud->syspath, property); 210 | return (NULL); 211 | } 212 | 213 | LIBUDEV_EXPORT char const * 214 | udev_device_get_sysattr_value(struct udev_device *ud, const char *sysattr) 215 | { 216 | char const *key, *value; 217 | struct udev_list_entry *entry; 218 | 219 | udev_list_entry_foreach(entry, udev_list_entry_get_first(&ud->sysattr_list)) { 220 | key = _udev_list_entry_get_name(entry); 221 | if (!key) 222 | continue; 223 | if (strcmp(key, sysattr) == 0) { 224 | value = _udev_list_entry_get_value(entry); 225 | TRC("(%p(%s), %s) %s", ud, ud->syspath, sysattr, value); 226 | return (value); 227 | } 228 | } 229 | TRC("(%p(%s), %s) NULL", ud, ud->syspath, sysattr); 230 | return (NULL); 231 | } 232 | 233 | LIBUDEV_EXPORT int 234 | udev_device_set_sysattr_value(struct udev_device *ud, const char *sysattr, const char *value) 235 | { 236 | struct udev_list_entry *entry; 237 | 238 | udev_list_entry_foreach(entry, udev_list_entry_get_first(&ud->sysattr_list)) { 239 | char const *key; 240 | 241 | key = _udev_list_entry_get_name(entry); 242 | if (key && strcmp(key, sysattr) == 0) 243 | return -1; 244 | } 245 | 246 | return udev_list_insert(&ud->sysattr_list, sysattr, value); 247 | } 248 | 249 | LIBUDEV_EXPORT struct udev * 250 | udev_device_get_udev(struct udev_device *ud) 251 | { 252 | 253 | TRC("(%p(%s))", ud, ud->syspath); 254 | return (ud->udev); 255 | } 256 | 257 | struct udev_device * 258 | udev_device_new_common(struct udev *udev, const char *syspath, int action) 259 | { 260 | struct udev_device *ud; 261 | 262 | ud = calloc 263 | (1, offsetof(struct udev_device, syspath) + strlen(syspath) + 1); 264 | if (ud == NULL) 265 | return (NULL); 266 | 267 | _udev_ref(udev); 268 | ud->udev = udev; 269 | ud->flags.action = action; 270 | ud->parent = NULL; 271 | atomic_init(&ud->refcount, 1); 272 | strcpy(ud->syspath, syspath); 273 | udev_list_init(&ud->prop_list); 274 | udev_list_init(&ud->sysattr_list); 275 | udev_list_init(&ud->tag_list); 276 | udev_list_init(&ud->devlink_list); 277 | if (action != UD_ACTION_REMOVE) 278 | invoke_create_handler(ud); 279 | 280 | return (ud); 281 | } 282 | 283 | LIBUDEV_EXPORT const char * 284 | udev_device_get_syspath(struct udev_device *ud) 285 | { 286 | 287 | TRC("(%p) %s", ud, ud->syspath); 288 | return (ud->syspath); 289 | } 290 | 291 | LIBUDEV_EXPORT const char * 292 | udev_device_get_sysname(struct udev_device *ud) 293 | { 294 | const char *sysname; 295 | 296 | sysname = get_sysname_by_syspath(ud->syspath); 297 | TRC("(%p(%s)) %s", ud, ud->syspath, sysname); 298 | return (sysname); 299 | } 300 | 301 | LIBUDEV_EXPORT const char * 302 | udev_device_get_subsystem(struct udev_device *ud) 303 | { 304 | const char *subsystem; 305 | 306 | subsystem = get_subsystem_by_syspath(udev_device_get_syspath(ud)); 307 | TRC("(%p(%s)) %s", ud, ud->syspath, subsystem); 308 | return (subsystem); 309 | } 310 | 311 | LIBUDEV_EXPORT struct udev_device * 312 | udev_device_ref(struct udev_device *ud) 313 | { 314 | TRC("(%p/%s) %d", ud, ud->syspath, ud->refcount); 315 | 316 | if (!ud->flags.is_parent) 317 | atomic_fetch_add(&ud->refcount, 1); 318 | return (ud); 319 | } 320 | 321 | static void 322 | udev_device_free(struct udev_device *ud) 323 | { 324 | 325 | udev_list_free(&ud->prop_list); 326 | udev_list_free(&ud->sysattr_list); 327 | udev_list_free(&ud->tag_list); 328 | udev_list_free(&ud->devlink_list); 329 | if (ud->parent != NULL) 330 | udev_device_free(ud->parent); 331 | _udev_unref(ud->udev); 332 | free(ud); 333 | } 334 | 335 | LIBUDEV_EXPORT void 336 | udev_device_unref(struct udev_device *ud) 337 | { 338 | 339 | TRC("(%p/%s) %d", ud, ud->syspath, ud->refcount); 340 | if (ud->flags.is_parent) 341 | return; 342 | if (atomic_fetch_sub(&ud->refcount, 1) == 1) 343 | udev_device_free(ud); 344 | } 345 | 346 | LIBUDEV_EXPORT struct udev_device * 347 | udev_device_get_parent(struct udev_device *ud) 348 | { 349 | 350 | TRC("(%p/%s) %p", ud, ud->syspath, ud->parent); 351 | return (ud->parent); 352 | } 353 | 354 | LIBUDEV_EXPORT struct udev_device * 355 | udev_device_get_parent_with_subsystem_devtype(struct udev_device *ud, 356 | const char *subsystem, const char *devtype) 357 | { 358 | 359 | TRC("(%p/%s, %s, %s)", ud, ud->syspath, subsystem, devtype); 360 | UNIMPL(); 361 | return (ud->parent); 362 | } 363 | 364 | void 365 | udev_device_set_parent(struct udev_device *ud, struct udev_device *parent) 366 | { 367 | 368 | parent->flags.is_parent = 1; 369 | ud->parent = parent; 370 | } 371 | 372 | LIBUDEV_EXPORT int 373 | udev_device_get_is_initialized(struct udev_device *ud) 374 | { 375 | 376 | TRC("(%p/%s)", ud, ud->syspath); 377 | return (1); 378 | } 379 | 380 | LIBUDEV_EXPORT const char * 381 | udev_device_get_action(struct udev_device *ud) 382 | { 383 | const char *action; 384 | 385 | switch(ud->flags.action) { 386 | case UD_ACTION_NONE: 387 | action = "none"; 388 | break; 389 | case UD_ACTION_ADD: 390 | action = "add"; 391 | break; 392 | case UD_ACTION_REMOVE: 393 | action = "remove"; 394 | break; 395 | case UD_ACTION_HOTPLUG: 396 | action = "change"; 397 | break; 398 | default: 399 | action = "unknown"; 400 | } 401 | TRC("(%p/%s) %s", ud, ud->syspath, action); 402 | return (action); 403 | } 404 | 405 | LIBUDEV_EXPORT dev_t 406 | udev_device_get_devnum(struct udev_device *ud) 407 | { 408 | const char *devpath; 409 | struct stat st; 410 | 411 | TRC("(%p) %s", ud, ud->syspath); 412 | devpath = get_devpath_by_syspath(ud->syspath); 413 | if (devpath == NULL || 414 | stat(devpath, &st) < 0 || 415 | !S_ISCHR(st.st_mode)) 416 | return (makedev(0, 0)); 417 | 418 | return (st.st_rdev); 419 | } 420 | 421 | LIBUDEV_EXPORT const char * 422 | udev_device_get_devtype(struct udev_device *ud) 423 | { 424 | 425 | TRC("(%p) %s", ud, ud->syspath); 426 | UNIMPL(); 427 | return (NULL); 428 | } 429 | 430 | LIBUDEV_EXPORT const char * 431 | udev_device_get_driver(struct udev_device *ud) 432 | { 433 | 434 | TRC("(%p) %s", ud, ud->syspath); 435 | UNIMPL(); 436 | return (NULL); 437 | } 438 | 439 | LIBUDEV_EXPORT const char * 440 | udev_device_get_sysnum(struct udev_device *ud) 441 | { 442 | 443 | TRC("(%p) %s", ud, ud->syspath); 444 | return (ud->syspath + syspathlen_wo_units(ud->syspath)); 445 | } 446 | 447 | LIBUDEV_EXPORT unsigned long long int 448 | udev_device_get_seqnum(struct udev_device *ud) 449 | { 450 | 451 | TRC("(%p) %s", ud, ud->syspath); 452 | UNIMPL(); 453 | return (0); 454 | } 455 | 456 | LIBUDEV_EXPORT unsigned long long int 457 | udev_device_get_usec_since_initialized(struct udev_device *ud) 458 | { 459 | 460 | TRC("(%p) %s", ud, ud->syspath); 461 | UNIMPL(); 462 | return (0); 463 | } 464 | -------------------------------------------------------------------------------- /udev-device.h: -------------------------------------------------------------------------------- 1 | #ifndef UDEV_DEVICE_H_ 2 | #define UDEV_DEVICE_H_ 3 | 4 | #include "libudev.h" 5 | #include "udev-list.h" 6 | 7 | /* udev_device flags */ 8 | enum { 9 | UD_ACTION_NONE, 10 | UD_ACTION_ADD, 11 | UD_ACTION_REMOVE, 12 | UD_ACTION_HOTPLUG, 13 | }; 14 | 15 | struct udev_device *udev_device_new_common(struct udev *udev, 16 | const char *syspath, int action); 17 | struct udev_list *udev_device_get_properties_list(struct udev_device *ud); 18 | struct udev_list *udev_device_get_sysattr_list(struct udev_device *ud); 19 | struct udev_list *udev_device_get_tags_list(struct udev_device *ud); 20 | struct udev_list *udev_device_get_devlinks_list(struct udev_device *ud); 21 | void udev_device_set_parent(struct udev_device *ud, struct udev_device *parent); 22 | 23 | #endif /* UDEV_DVICE_H_ */ 24 | -------------------------------------------------------------------------------- /udev-enumerate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Vladimir Kondratyev 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include "config.h" 28 | #include "libudev.h" 29 | #include "udev-filter.h" 30 | #include "udev-list.h" 31 | #include "udev-utils.h" 32 | #include "utils.h" 33 | 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | struct udev_enumerate { 45 | _Atomic(int) refcount; 46 | struct udev_filter_head filters; 47 | struct udev_list dev_list; 48 | struct udev *udev; 49 | }; 50 | 51 | LIBUDEV_EXPORT struct udev_enumerate * 52 | udev_enumerate_new(struct udev *udev) 53 | { 54 | struct udev_enumerate *ue; 55 | 56 | TRC(); 57 | ue = calloc(1, sizeof(struct udev_enumerate)); 58 | if (ue == NULL) 59 | return (NULL); 60 | 61 | ue->udev = udev; 62 | udev_ref(udev); 63 | atomic_init(&ue->refcount, 1); 64 | udev_filter_init(&ue->filters); 65 | udev_list_init(&ue->dev_list); 66 | 67 | return (ue); 68 | } 69 | 70 | LIBUDEV_EXPORT struct udev_enumerate * 71 | udev_enumerate_ref(struct udev_enumerate *ue) 72 | { 73 | 74 | TRC("(%p) refcount=%d", ue, ue->refcount); 75 | atomic_fetch_add(&ue->refcount, 1); 76 | return (ue); 77 | } 78 | 79 | LIBUDEV_EXPORT void 80 | udev_enumerate_unref(struct udev_enumerate *ue) 81 | { 82 | 83 | TRC("(%p) refcount=%d", ue, ue->refcount); 84 | if (atomic_fetch_sub(&ue->refcount, 1) == 1) { 85 | udev_filter_free(&ue->filters); 86 | udev_list_free(&ue->dev_list); 87 | udev_unref(ue->udev); 88 | free(ue); 89 | } 90 | } 91 | 92 | LIBUDEV_EXPORT int 93 | udev_enumerate_add_match_subsystem(struct udev_enumerate *ue, 94 | const char *subsystem) 95 | { 96 | 97 | TRC("(%p, %s)", ue, subsystem); 98 | return (udev_filter_add(&ue->filters, UDEV_FILTER_TYPE_SUBSYSTEM, 0, 99 | subsystem, NULL)); 100 | } 101 | 102 | LIBUDEV_EXPORT int 103 | udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *ue, 104 | const char *subsystem) 105 | { 106 | 107 | TRC("(%p, %s)", ue, subsystem); 108 | return (udev_filter_add(&ue->filters, UDEV_FILTER_TYPE_SUBSYSTEM, 1, 109 | subsystem, NULL)); 110 | } 111 | 112 | LIBUDEV_EXPORT int 113 | udev_enumerate_add_match_sysname(struct udev_enumerate *ue, 114 | const char *sysname) 115 | { 116 | 117 | TRC("(%p, %s)", ue, sysname); 118 | return (udev_filter_add(&ue->filters, UDEV_FILTER_TYPE_SYSNAME, 0, 119 | sysname, NULL)); 120 | } 121 | 122 | LIBUDEV_EXPORT int 123 | udev_enumerate_add_match_sysattr(struct udev_enumerate *ue, 124 | const char *sysattr, const char *value) 125 | { 126 | 127 | TRC("(%p, %s, %s)", ue, sysattr, value); 128 | return (udev_filter_add(&ue->filters, UDEV_FILTER_TYPE_SYSATTR, 0, 129 | sysattr, value)); 130 | } 131 | 132 | LIBUDEV_EXPORT int 133 | udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *ue, 134 | const char *sysattr, const char *value) 135 | { 136 | 137 | TRC("(%p, %s, %s)", ue, sysattr, value); 138 | return (udev_filter_add(&ue->filters, UDEV_FILTER_TYPE_SYSATTR, 1, 139 | sysattr, value)); 140 | } 141 | 142 | 143 | LIBUDEV_EXPORT int 144 | udev_enumerate_add_match_property(struct udev_enumerate *ue, 145 | const char *property, const char *value) 146 | { 147 | 148 | TRC("(%p, %s, %s)", ue, property, value); 149 | return (udev_filter_add(&ue->filters, UDEV_FILTER_TYPE_PROPERTY, 0, 150 | property, value)); 151 | } 152 | 153 | LIBUDEV_EXPORT int 154 | udev_enumerate_add_match_tag(struct udev_enumerate *ue, const char *tag) 155 | { 156 | 157 | TRC("(%p, %s)", ue, tag); 158 | return (udev_filter_add(&ue->filters, UDEV_FILTER_TYPE_TAG, 0, tag, 159 | NULL)); 160 | } 161 | 162 | 163 | LIBUDEV_EXPORT int 164 | udev_enumerate_add_match_is_initialized(struct udev_enumerate *ue) 165 | { 166 | 167 | TRC("(%p)", ue); 168 | UNIMPL(); 169 | return (0); 170 | } 171 | 172 | static int 173 | enumerate_cb(const char *path, int type, void *arg) 174 | { 175 | struct udev_enumerate *ue = arg; 176 | const char *syspath; 177 | 178 | if (type == DT_LNK || type == DT_CHR) { 179 | syspath = get_syspath_by_devpath(path); 180 | if (udev_filter_match(ue->udev, &ue->filters, syspath) && 181 | udev_list_insert(&ue->dev_list, syspath, NULL) == -1) 182 | return (-1); 183 | } 184 | return (0); 185 | } 186 | 187 | LIBUDEV_EXPORT int 188 | udev_enumerate_scan_devices(struct udev_enumerate *ue) 189 | { 190 | struct scan_ctx ctx; 191 | char path[DEV_PATH_MAX] = DEV_PATH_ROOT "/"; 192 | int ret; 193 | 194 | TRC("(%p)", ue); 195 | 196 | udev_list_free(&ue->dev_list); 197 | ctx = (struct scan_ctx) { 198 | .recursive = true, 199 | .cb = enumerate_cb, 200 | .args = ue, 201 | }; 202 | 203 | ret = scandir_recursive(path, sizeof(path), &ctx); 204 | #ifdef HAVE_DEVINFO_H 205 | if (ret == 0) 206 | ret = scandev_recursive(&ctx); 207 | #endif 208 | if (ret == -1) 209 | udev_list_free(&ue->dev_list); 210 | return ret; 211 | } 212 | 213 | /* 214 | * Enumerate subsystems -- under /sys/modules, /sys/dev, only 215 | * list the directories. 216 | */ 217 | static int __attribute__((unused)) 218 | enumerate_ssys_cb(const char *path, int type, void *arg) 219 | { 220 | struct udev_enumerate *ue = arg; 221 | 222 | if (type == DT_DIR) { 223 | if (udev_list_insert(&ue->dev_list, path, NULL) == -1) 224 | return (-1); 225 | } 226 | return (0); 227 | } 228 | 229 | LIBUDEV_EXPORT int 230 | udev_enumerate_scan_subsystems(struct udev_enumerate *ue) 231 | { 232 | 233 | TRC("(%p)", ue); 234 | UNIMPL(); 235 | return (0); 236 | } 237 | 238 | LIBUDEV_EXPORT struct udev_list_entry * 239 | udev_enumerate_get_list_entry(struct udev_enumerate *ue) 240 | { 241 | 242 | TRC("(%p)", ue); 243 | return (udev_list_entry_get_first(&ue->dev_list)); 244 | } 245 | 246 | LIBUDEV_EXPORT struct udev * 247 | udev_enumerate_get_udev(struct udev_enumerate *ue) 248 | { 249 | 250 | TRC("(%p)", ue); 251 | return (ue->udev); 252 | } 253 | 254 | LIBUDEV_EXPORT int 255 | udev_enumerate_add_syspath(struct udev_enumerate *ue, const char *syspath) 256 | { 257 | 258 | TRC("(%p, %s)", ue, syspath); 259 | return (udev_list_insert(&ue->dev_list, syspath, NULL)); 260 | } 261 | -------------------------------------------------------------------------------- /udev-filter.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Vladimir Kondratyev 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include "config.h" 28 | #include "libudev.h" 29 | #include "udev-device.h" 30 | #include "udev-utils.h" 31 | #include "udev-filter.h" 32 | 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | struct udev_filter_entry { 44 | int type; 45 | int neg; 46 | STAILQ_ENTRY(udev_filter_entry) next; 47 | char *value; 48 | char expr[]; 49 | }; 50 | 51 | void 52 | udev_filter_init(struct udev_filter_head *ufh) 53 | { 54 | 55 | STAILQ_INIT(ufh); 56 | } 57 | 58 | int 59 | udev_filter_add(struct udev_filter_head *ufh, int type, int neg, 60 | const char *expr, const char *value) 61 | { 62 | struct udev_filter_entry *ufe; 63 | size_t exprlen, valuelen; 64 | 65 | exprlen = strlen(expr) + 1; 66 | valuelen = value == NULL ? 0 : strlen(value) + 1; 67 | 68 | ufe = calloc 69 | (1, offsetof(struct udev_filter_entry, expr) + exprlen + valuelen); 70 | if (ufe == NULL) 71 | return (-1); 72 | 73 | ufe->type = type; 74 | ufe->neg = neg; 75 | strcpy(ufe->expr, expr); 76 | ufe->value = NULL; 77 | if (value != NULL) { 78 | ufe->value = ufe->expr + exprlen; 79 | strcpy(ufe->value, value); 80 | } 81 | STAILQ_INSERT_TAIL(ufh, ufe, next); 82 | return (0); 83 | } 84 | 85 | void 86 | udev_filter_free(struct udev_filter_head *ufh) 87 | { 88 | struct udev_filter_entry *ufe1, *ufe2; 89 | 90 | ufe1 = STAILQ_FIRST(ufh); 91 | while (ufe1 != NULL) { 92 | ufe2 = STAILQ_NEXT(ufe1, next); 93 | free(ufe1); 94 | ufe1 = ufe2; 95 | } 96 | STAILQ_INIT(ufh); 97 | } 98 | 99 | static bool 100 | fnmatch_list(struct udev_list *list, struct udev_filter_entry *ufe) 101 | { 102 | struct udev_list_entry *entry; 103 | const char *key, *value; 104 | 105 | udev_list_entry_foreach(entry, udev_list_entry_get_first(list)) { 106 | key = _udev_list_entry_get_name(entry); 107 | if (fnmatch(ufe->expr, key, 0) == 0) { 108 | value = _udev_list_entry_get_value(entry); 109 | if (ufe->value == NULL && value == NULL) 110 | return (true); 111 | if (ufe->value != NULL && value != NULL && 112 | fnmatch(ufe->value, value, 0) == 0) 113 | return (true); 114 | } 115 | } 116 | return (false); 117 | } 118 | 119 | bool 120 | udev_filter_match(struct udev *udev, struct udev_filter_head *ufh, 121 | const char *syspath) 122 | { 123 | struct udev_filter_entry *ufe; 124 | struct udev_device *ud = NULL; 125 | const char *subsystem, *sysname; 126 | int ret; 127 | 128 | subsystem = get_subsystem_by_syspath(syspath); 129 | if (strcmp(subsystem, UNKNOWN_SUBSYSTEM) == 0) 130 | return (0); 131 | 132 | sysname = get_sysname_by_syspath(syspath); 133 | /* An empty filter list accepts everything. */ 134 | ret = STAILQ_EMPTY(ufh); 135 | 136 | STAILQ_FOREACH(ufe, ufh, next) { 137 | if (ufe->type == UDEV_FILTER_TYPE_SUBSYSTEM && 138 | ufe->neg == 0 && 139 | fnmatch(ufe->expr, subsystem, 0) == 0) { 140 | ret = true; 141 | break; 142 | } 143 | if (ufe->type == UDEV_FILTER_TYPE_SYSNAME && 144 | ufe->neg == 0 && 145 | fnmatch(ufe->expr, sysname, 0) == 0) { 146 | ret = true; 147 | break; 148 | } 149 | if (ufe->type == UDEV_FILTER_TYPE_PROPERTY && ufe->neg == 0) { 150 | ud = udev_device_new_common(udev, syspath, UD_ACTION_NONE); 151 | if (ud == NULL) 152 | break; 153 | if (fnmatch_list( 154 | udev_device_get_properties_list(ud), ufe)) { 155 | ret = true; 156 | break; 157 | } 158 | } 159 | if (ufe->type == UDEV_FILTER_TYPE_SYSATTR && ufe->neg == 0) { 160 | if (ud == NULL) 161 | ud = udev_device_new_common(udev, syspath, 162 | UD_ACTION_NONE); 163 | if (ud == NULL) 164 | break; 165 | if (fnmatch_list( 166 | udev_device_get_sysattr_list(ud), ufe)) { 167 | ret = true; 168 | break; 169 | } 170 | } 171 | } 172 | 173 | if (!ret) 174 | goto out; 175 | 176 | STAILQ_FOREACH(ufe, ufh, next) { 177 | if (ufe->type == UDEV_FILTER_TYPE_SUBSYSTEM && 178 | ufe->neg == 1 && 179 | fnmatch(ufe->expr, subsystem, 0) == 0) { 180 | ret = false; 181 | break; 182 | } 183 | if (ufe->type == UDEV_FILTER_TYPE_SYSNAME && 184 | ufe->neg == 1 && 185 | fnmatch(ufe->expr, sysname, 0) == 0) { 186 | ret = false; 187 | break; 188 | } 189 | if (ufe->type == UDEV_FILTER_TYPE_SYSATTR && ufe->neg == 1) { 190 | if (ud == NULL) 191 | ud = udev_device_new_common(udev, sysname, 192 | UD_ACTION_NONE); 193 | if (ud == NULL) 194 | break; 195 | if (fnmatch_list( 196 | udev_device_get_sysattr_list(ud), ufe)) { 197 | ret = false; 198 | break; 199 | } 200 | } 201 | } 202 | 203 | out: 204 | if (ud != NULL) 205 | udev_device_unref(ud); 206 | 207 | return (ret); 208 | } 209 | 210 | /* 211 | * Returns true if the given @p subsystem is accepted by the 212 | * filters applied to the enumerator @p ue. 213 | */ 214 | bool 215 | udev_filter_match_subsystem(struct udev_filter_head *ufh, const char *subsystem) 216 | { 217 | if (!subsystem) 218 | return false; 219 | 220 | if (STAILQ_EMPTY(ufh)) 221 | return true; 222 | 223 | struct udev_filter_entry *ufe; 224 | 225 | /* Scan for negative matches */ 226 | STAILQ_FOREACH(ufe, ufh, next) { 227 | if (ufe->type == UDEV_FILTER_TYPE_SUBSYSTEM && 228 | ufe->neg != 0 && 229 | fnmatch(ufe->expr, subsystem, 0) == 0) { 230 | return false; 231 | } 232 | } 233 | 234 | /* Not empty, scan for positive matches */ 235 | STAILQ_FOREACH(ufe, ufh, next) { 236 | if (ufe->type == UDEV_FILTER_TYPE_SUBSYSTEM && 237 | ufe->neg == 0 && 238 | fnmatch(ufe->expr, subsystem, 0) == 0) { 239 | return true; 240 | } 241 | } 242 | 243 | /* Not empty, matched nothing */ 244 | return false; 245 | } 246 | -------------------------------------------------------------------------------- /udev-filter.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | enum { 6 | UDEV_FILTER_TYPE_SUBSYSTEM, 7 | UDEV_FILTER_TYPE_SYSNAME, 8 | UDEV_FILTER_TYPE_PROPERTY, 9 | UDEV_FILTER_TYPE_INITIALIZED, 10 | UDEV_FILTER_TYPE_TAG, 11 | UDEV_FILTER_TYPE_SYSATTR, 12 | }; 13 | STAILQ_HEAD(udev_filter_head, udev_filter_entry); 14 | 15 | void udev_filter_init(struct udev_filter_head *ufh); 16 | bool udev_filter_match_subsystem(struct udev_filter_head *ufh, 17 | const char *subsystem); 18 | bool udev_filter_match(struct udev *udev, struct udev_filter_head *ufh, 19 | const char *syspath); 20 | int udev_filter_add(struct udev_filter_head *ufh, int type, int neg, 21 | const char *expr, const char *value); 22 | void udev_filter_free(struct udev_filter_head *ufh); 23 | -------------------------------------------------------------------------------- /udev-list.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Vladimir Kondratyev 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include "config.h" 28 | #include "libudev.h" 29 | #include "udev-list.h" 30 | #include "udev-utils.h" 31 | #include "utils.h" 32 | 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | struct udev_list_entry { 42 | RB_ENTRY(udev_list_entry) link; 43 | char *value; 44 | char name[]; 45 | }; 46 | 47 | void udev_list_entry_free(struct udev_list_entry *ule); 48 | 49 | RB_PROTOTYPE(udev_list, udev_list_entry, link, udev_list_entry_cmp); 50 | 51 | void 52 | udev_list_init(struct udev_list *ul) 53 | { 54 | 55 | RB_INIT(ul); 56 | } 57 | 58 | int 59 | udev_list_insert(struct udev_list *ul, char const *name, char const *value) 60 | { 61 | struct udev_list_entry *ule, *old_ule; 62 | size_t namelen, valuelen; 63 | 64 | namelen = strlen(name) + 1; 65 | valuelen = value == NULL ? 0 : strlen(value) + 1; 66 | ule = calloc 67 | (1, offsetof(struct udev_list_entry, name) + namelen + valuelen); 68 | if (!ule) 69 | return (-1); 70 | 71 | strcpy(ule->name, name); 72 | ule->value = NULL; 73 | if (value != NULL) { 74 | ule->value = ule->name + namelen; 75 | strcpy(ule->value, value); 76 | } 77 | 78 | old_ule = RB_FIND(udev_list, ul, ule); 79 | if (old_ule != NULL) { 80 | RB_REMOVE(udev_list, ul, old_ule); 81 | udev_list_entry_free(old_ule); 82 | } 83 | 84 | RB_INSERT(udev_list, ul, ule); 85 | return (0); 86 | } 87 | 88 | void 89 | udev_list_free(struct udev_list *ul) 90 | { 91 | struct udev_list_entry *ule1, *ule2; 92 | 93 | RB_FOREACH_SAFE (ule1, udev_list, ul, ule2) { 94 | RB_REMOVE(udev_list, ul, ule1); 95 | udev_list_entry_free(ule1); 96 | } 97 | 98 | RB_INIT(ul); 99 | } 100 | 101 | void 102 | udev_list_entry_free(struct udev_list_entry *ule) 103 | { 104 | 105 | free(ule); 106 | } 107 | 108 | struct udev_list_entry * 109 | udev_list_entry_get_first(struct udev_list *ul) 110 | { 111 | 112 | return (RB_MIN(udev_list, ul)); 113 | } 114 | 115 | LIBUDEV_EXPORT struct udev_list_entry * 116 | udev_list_entry_get_next(struct udev_list_entry *ule) 117 | { 118 | 119 | return (RB_NEXT(udev_list,, ule)); 120 | } 121 | 122 | const char * 123 | _udev_list_entry_get_name(struct udev_list_entry *ule) 124 | { 125 | 126 | return (ule->name); 127 | } 128 | 129 | LIBUDEV_EXPORT const char * 130 | udev_list_entry_get_name(struct udev_list_entry *ule) 131 | { 132 | const char *name; 133 | 134 | name = _udev_list_entry_get_name(ule); 135 | TRC("() %s", name); 136 | return (name); 137 | } 138 | 139 | const char * 140 | _udev_list_entry_get_value(struct udev_list_entry *ule) 141 | { 142 | 143 | return (ule->value); 144 | } 145 | 146 | LIBUDEV_EXPORT const char * 147 | udev_list_entry_get_value(struct udev_list_entry *ule) 148 | { 149 | const char *value; 150 | 151 | value = _udev_list_entry_get_value(ule); 152 | TRC("() %s", value); 153 | return (value); 154 | } 155 | 156 | static int 157 | udev_list_entry_cmp (struct udev_list_entry *le1, struct udev_list_entry *le2) 158 | { 159 | 160 | return (strcmp(le1->name, le2->name)); 161 | } 162 | 163 | RB_GENERATE(udev_list, udev_list_entry, link, udev_list_entry_cmp); 164 | -------------------------------------------------------------------------------- /udev-list.h: -------------------------------------------------------------------------------- 1 | #ifndef UDEV_LIST_H_ 2 | #define UDEV_LIST_H_ 3 | 4 | #include "libudev.h" 5 | 6 | #include 7 | #include 8 | 9 | RB_HEAD(udev_list, udev_list_entry); 10 | 11 | void udev_list_init(struct udev_list *ul); 12 | int udev_list_insert(struct udev_list *ul, char const *name, 13 | char const *value); 14 | void udev_list_free(struct udev_list *ul); 15 | struct udev_list_entry *udev_list_entry_get_first(struct udev_list *ul); 16 | const char *_udev_list_entry_get_name(struct udev_list_entry *ule); 17 | const char *_udev_list_entry_get_value(struct udev_list_entry *ule); 18 | 19 | #endif /* UDEV_LIST_H_ */ 20 | -------------------------------------------------------------------------------- /udev-monitor.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Vladimir Kondratyev 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include "config.h" 28 | #include "libudev.h" 29 | #include "udev.h" 30 | #include "udev-device.h" 31 | #include "udev-utils.h" 32 | #include "udev-filter.h" 33 | #include "udev-utils.h" 34 | #include "utils.h" 35 | 36 | #include 37 | #include 38 | #include 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | #define DEVD_SOCK_PATH "/var/run/devd.pipe" 51 | #define DEVD_RECONNECT_INTERVAL 1000 /* reconnect after 1 second */ 52 | 53 | #define DEVD_EVENT_ATTACH '+' 54 | #define DEVD_EVENT_DETACH '-' 55 | #define DEVD_EVENT_NOTICE '!' 56 | #define DEVD_EVENT_UNKNOWN '?' 57 | 58 | STAILQ_HEAD(udev_monitor_queue_head, udev_monitor_queue_entry); 59 | struct udev_monitor_queue_entry { 60 | struct udev_device *ud; 61 | STAILQ_ENTRY(udev_monitor_queue_entry) next; 62 | }; 63 | 64 | struct udev_monitor { 65 | _Atomic(int) refcount; 66 | int fds[2]; 67 | int kq; 68 | struct udev_filter_head filters; 69 | struct udev *udev; 70 | struct udev_monitor_queue_head queue; 71 | pthread_mutex_t mtx; 72 | pthread_t thread; 73 | }; 74 | 75 | LIBUDEV_EXPORT struct udev_device * 76 | udev_monitor_receive_device(struct udev_monitor *um) 77 | { 78 | struct udev_monitor_queue_entry *umqe; 79 | struct udev_device *ud; 80 | char buf[1]; 81 | 82 | TRC("(%p)", um); 83 | if (read(um->fds[0], buf, 1) < 0) 84 | return (NULL); 85 | 86 | if (STAILQ_EMPTY(&um->queue)) 87 | return (NULL); 88 | 89 | pthread_mutex_lock(&um->mtx); 90 | umqe = STAILQ_FIRST(&um->queue); 91 | STAILQ_REMOVE_HEAD(&um->queue, next); 92 | pthread_mutex_unlock(&um->mtx); 93 | ud = umqe->ud; 94 | free(umqe); 95 | 96 | return (ud); 97 | } 98 | 99 | static int 100 | udev_monitor_send_device(struct udev_monitor *um, const char *syspath, 101 | int action) 102 | { 103 | struct udev_monitor_queue_entry *umqe; 104 | 105 | umqe = calloc(1, sizeof(struct udev_monitor_queue_entry)); 106 | if (umqe == NULL) 107 | return (-1); 108 | 109 | umqe->ud = udev_device_new_common(um->udev, syspath, action); 110 | if (umqe->ud == NULL) { 111 | free(umqe); 112 | return (-1); 113 | } 114 | 115 | pthread_mutex_lock(&um->mtx); 116 | STAILQ_INSERT_TAIL(&um->queue, umqe, next); 117 | pthread_mutex_unlock(&um->mtx); 118 | 119 | if (write(um->fds[1], "*", 1) != 1) { 120 | pthread_mutex_lock(&um->mtx); 121 | STAILQ_REMOVE(&um->queue, umqe, udev_monitor_queue_entry, next); 122 | pthread_mutex_unlock(&um->mtx); 123 | udev_device_unref(umqe->ud); 124 | free(umqe); 125 | return (-1); 126 | } 127 | 128 | return (0); 129 | } 130 | 131 | static int 132 | parse_devd_message(char *msg, char *syspath, size_t syspathlen) 133 | { 134 | char devpath[DEV_PATH_MAX] = DEV_PATH_ROOT "/"; 135 | const char *type, *dev_name; 136 | size_t type_len, dev_len, root_len; 137 | int action; 138 | 139 | root_len = strlen(devpath); 140 | action = UD_ACTION_NONE; 141 | 142 | switch (msg[0]) { 143 | #ifdef HAVE_DEVINFO_H 144 | case DEVD_EVENT_ATTACH: 145 | action = UD_ACTION_ADD; 146 | /* FALLTHROUGH */ 147 | case DEVD_EVENT_DETACH: 148 | if (action == UD_ACTION_NONE) 149 | action = UD_ACTION_REMOVE; 150 | *(strchrnul(msg + 1, ' ')) = '\0'; 151 | strlcpy(syspath, msg + 1, syspathlen); 152 | break; 153 | #endif /* HAVE_DEVINFO_H */ 154 | case DEVD_EVENT_NOTICE: 155 | if (!(match_kern_prop_value(msg + 1, "system", "DEVFS") 156 | && match_kern_prop_value(msg + 1, "subsystem", "CDEV")) 157 | && !match_kern_prop_value(msg + 1, "system", "DRM")) 158 | break; 159 | type = get_kern_prop_value(msg + 1, "type", &type_len); 160 | dev_name = get_kern_prop_value(msg + 1, "cdev", &dev_len); 161 | if (type == NULL || 162 | dev_name == NULL || 163 | dev_len > (sizeof(devpath) - root_len - 1)) 164 | break; 165 | if (type_len == 6 && 166 | strncmp(type, "CREATE", type_len) == 0) 167 | action = UD_ACTION_ADD; 168 | else if (type_len == 7 && 169 | strncmp(type, "DESTROY", type_len) == 0) 170 | action = UD_ACTION_REMOVE; 171 | else if (type_len == 7 && 172 | strncmp(type, "HOTPLUG", type_len) == 0) 173 | action = UD_ACTION_HOTPLUG; 174 | else 175 | break; 176 | memcpy(devpath + root_len, dev_name, dev_len); 177 | devpath[dev_len + root_len] = 0; 178 | strlcpy(syspath, get_syspath_by_devpath(devpath), syspathlen); 179 | break; 180 | case DEVD_EVENT_UNKNOWN: 181 | default: 182 | break; 183 | } 184 | 185 | return (action); 186 | } 187 | 188 | /* Opens devd socket and set read kevent on success or timer kevent on failure */ 189 | static int 190 | devd_connect(int kq) 191 | { 192 | int devd_fd; 193 | struct kevent ke; 194 | 195 | devd_fd = socket_connect(DEVD_SOCK_PATH); 196 | 197 | if (devd_fd >= 0) { 198 | EV_SET(&ke, devd_fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0); 199 | if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) { 200 | close(devd_fd); 201 | devd_fd = -1; 202 | } 203 | } 204 | 205 | /* Set respawn timer */ 206 | if (devd_fd < 0) { 207 | EV_SET(&ke, 1, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_ONESHOT, 208 | 0, DEVD_RECONNECT_INTERVAL, 0); 209 | if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) 210 | devd_fd = -1; 211 | } 212 | 213 | return (devd_fd); 214 | } 215 | 216 | static void * 217 | udev_monitor_thread(void *args) 218 | { 219 | struct udev_monitor *um = args; 220 | char ev[1024], syspath[DEV_PATH_MAX]; 221 | int devd_fd = -1, ret, action; 222 | struct kevent ke; 223 | sigset_t set; 224 | 225 | sigfillset(&set); 226 | pthread_sigmask(SIG_BLOCK, &set, NULL); 227 | 228 | for (;;) { 229 | if (devd_fd < 0) 230 | devd_fd = devd_connect(um->kq); 231 | 232 | ret = kevent(um->kq, NULL, 0, &ke, 1, NULL); 233 | if (ret == -1 && errno == EINTR) 234 | continue; 235 | if (ret < 1) 236 | break; 237 | 238 | /* edev_monitor is finishing */ 239 | if (ke.filter == EVFILT_USER) 240 | break; 241 | 242 | /* connection respawn timer expired */ 243 | if (ke.filter == EVFILT_TIMER) { 244 | continue; 245 | } 246 | 247 | /* XXX: assert() should be placed here */ 248 | if (ke.filter != EVFILT_READ) 249 | continue; 250 | 251 | if (ke.flags & EV_EOF || 252 | socket_readline(devd_fd, ev, sizeof(ev)) < 0) { 253 | close(devd_fd); 254 | devd_fd = -1; 255 | continue; 256 | } 257 | 258 | action = parse_devd_message(ev, syspath, sizeof(syspath)); 259 | 260 | if (action != UD_ACTION_NONE) { 261 | if (udev_filter_match(um->udev, &um->filters, syspath)) 262 | udev_monitor_send_device(um, syspath, action); 263 | } 264 | } 265 | 266 | if (devd_fd >= 0) 267 | close(devd_fd); 268 | 269 | return (NULL); 270 | } 271 | 272 | LIBUDEV_EXPORT struct udev_monitor * 273 | udev_monitor_new_from_netlink(struct udev *udev, const char *name) 274 | { 275 | struct udev_monitor *um; 276 | 277 | TRC("(%p, %s)", udev, name); 278 | um = calloc(1, sizeof(struct udev_monitor)); 279 | if (!um) 280 | return (NULL); 281 | 282 | if (pipe2(um->fds, O_CLOEXEC) == -1) { 283 | ERR("pipe2 failed"); 284 | free(um); 285 | return (NULL); 286 | } 287 | 288 | um->udev = udev; 289 | _udev_ref(udev); 290 | um->kq = -1; 291 | atomic_init(&um->refcount, 1); 292 | udev_filter_init(&um->filters); 293 | STAILQ_INIT(&um->queue); 294 | pthread_mutex_init(&um->mtx, NULL); 295 | 296 | return (um); 297 | } 298 | 299 | LIBUDEV_EXPORT int 300 | udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *um, 301 | const char *subsystem, const char *devtype) 302 | { 303 | 304 | TRC("(%p, %s, %s)", um, subsystem, devtype); 305 | return (udev_filter_add(&um->filters, UDEV_FILTER_TYPE_SUBSYSTEM, 0, 306 | subsystem, NULL)); 307 | } 308 | 309 | LIBUDEV_EXPORT int 310 | udev_monitor_enable_receiving(struct udev_monitor *um) 311 | { 312 | 313 | TRC("(%p)", um); 314 | struct kevent ev; 315 | 316 | um->kq = kqueue(); 317 | if (um->kq < 0) 318 | goto error; 319 | 320 | EV_SET(&ev, 1, EVFILT_USER, EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0); 321 | if (kevent(um->kq, &ev, 1, NULL, 0, NULL) < 0) 322 | goto error; 323 | 324 | if (pthread_create(&um->thread, NULL, udev_monitor_thread, um) != 0) { 325 | ERR("thread_create failed"); 326 | goto error; 327 | } 328 | 329 | return (0); 330 | error: 331 | if (um->kq >= 0) { 332 | close (um->kq); 333 | um->kq = -1; 334 | } 335 | return (-1); 336 | } 337 | 338 | LIBUDEV_EXPORT int 339 | udev_monitor_get_fd(struct udev_monitor *um) 340 | { 341 | 342 | /* TRC("(%p)", um); */ 343 | return (um->fds[0]); 344 | } 345 | 346 | LIBUDEV_EXPORT struct udev_monitor * 347 | udev_monitor_ref(struct udev_monitor *um) 348 | { 349 | 350 | TRC("(%p) refcount=%d", um, um->refcount); 351 | atomic_fetch_add(&um->refcount, 1); 352 | return (um); 353 | } 354 | 355 | static void 356 | udev_monitor_queue_drop(struct udev_monitor_queue_head *umqh) 357 | { 358 | struct udev_monitor_queue_entry *umqe; 359 | 360 | while (!STAILQ_EMPTY(umqh)) { 361 | umqe = STAILQ_FIRST(umqh); 362 | STAILQ_REMOVE_HEAD(umqh, next); 363 | udev_device_unref(umqe->ud); 364 | free(umqe); 365 | } 366 | } 367 | 368 | LIBUDEV_EXPORT void 369 | udev_monitor_unref(struct udev_monitor *um) 370 | { 371 | struct kevent ev; 372 | 373 | TRC("(%p) refcount=%d", um, um->refcount); 374 | if (atomic_fetch_sub(&um->refcount, 1) == 1) { 375 | EV_SET(&ev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0); 376 | kevent(um->kq, &ev, 1, NULL, 0, NULL); 377 | pthread_join(um->thread, NULL); 378 | close(um->kq); 379 | 380 | close(um->fds[0]); 381 | close(um->fds[1]); 382 | udev_filter_free(&um->filters); 383 | udev_monitor_queue_drop(&um->queue); 384 | pthread_mutex_destroy(&um->mtx); 385 | _udev_unref(um->udev); 386 | free(um); 387 | } 388 | } 389 | 390 | LIBUDEV_EXPORT 391 | struct udev *udev_monitor_get_udev(struct udev_monitor *um) 392 | { 393 | 394 | TRC(); 395 | return (um->udev); 396 | } 397 | -------------------------------------------------------------------------------- /udev-utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Vladimir Kondratyev 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include "config.h" 28 | #include "libudev.h" 29 | #include "udev-device.h" 30 | #include "udev-list.h" 31 | #include "udev-utils.h" 32 | #include "utils.h" 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #ifdef HAVE_LINUX_INPUT_H 47 | #include 48 | #else 49 | #define BUS_PCI 0x01 50 | #define BUS_USB 0x03 51 | #define BUS_VIRTUAL 0x06 52 | #define BUS_ISA 0x10 53 | #define BUS_I8042 0x11 54 | #endif 55 | 56 | #define PS2_KEYBOARD_VENDOR 0x001 57 | #define PS2_KEYBOARD_PRODUCT 0x001 58 | #define PS2_MOUSE_VENDOR 0x002 59 | #define PS2_MOUSE_GENERIC_PRODUCT 0x001 60 | 61 | #ifdef HAVE_LINUX_INPUT_H 62 | static const char *virtual_sysname = "uinput"; 63 | #endif 64 | 65 | void create_evdev_handler(struct udev_device *udev_device); 66 | void create_keyboard_handler(struct udev_device *udev_device); 67 | void create_mouse_handler(struct udev_device *udev_device); 68 | void create_joystick_handler(struct udev_device *udev_device); 69 | void create_touchpad_handler(struct udev_device *udev_device); 70 | void create_touchscreen_handler(struct udev_device *udev_device); 71 | void create_sysmouse_handler(struct udev_device *udev_device); 72 | void create_kbdmux_handler(struct udev_device *udev_device); 73 | void create_drm_handler(struct udev_device *udev_device); 74 | 75 | struct subsystem_config { 76 | char *subsystem; 77 | char *syspath; 78 | int flags; /* See SCFLAG_* below. */ 79 | void (*create_handler)(struct udev_device *udev_device); 80 | }; 81 | 82 | enum { 83 | IT_NONE, 84 | IT_KEYBOARD, 85 | IT_MOUSE, 86 | IT_TOUCHPAD, 87 | IT_TOUCHSCREEN, 88 | IT_JOYSTICK, 89 | IT_TABLET, 90 | IT_ACCELEROMETER, 91 | IT_SWITCH, 92 | }; 93 | 94 | /* Flag which in indicates a device should be skipped because it's 95 | * already exposed through EVDEV when it's enabled. */ 96 | #define SCFLAG_SKIP_IF_EVDEV 0x01 97 | 98 | struct subsystem_config subsystems[] = { 99 | #ifdef HAVE_LINUX_INPUT_H 100 | { "input", DEV_PATH_ROOT "/input/event[0-9]*", 101 | 0, 102 | create_evdev_handler }, 103 | #endif 104 | { "input", DEV_PATH_ROOT "/ukbd[0-9]*", 105 | SCFLAG_SKIP_IF_EVDEV, 106 | create_keyboard_handler }, 107 | { "input", DEV_PATH_ROOT "/atkbd[0-9]*", 108 | SCFLAG_SKIP_IF_EVDEV, 109 | create_keyboard_handler }, 110 | { "input", DEV_PATH_ROOT "/kbdmux[0-9]*", 111 | SCFLAG_SKIP_IF_EVDEV, 112 | create_kbdmux_handler }, 113 | { "input", DEV_PATH_ROOT "/ums[0-9]*", 114 | SCFLAG_SKIP_IF_EVDEV, 115 | create_mouse_handler }, 116 | { "input", DEV_PATH_ROOT "/psm[0-9]*", 117 | SCFLAG_SKIP_IF_EVDEV, 118 | create_mouse_handler }, 119 | { "input", DEV_PATH_ROOT "/joy[0-9]*", 120 | 0, 121 | create_joystick_handler }, 122 | { "input", DEV_PATH_ROOT "/atp[0-9]*", 123 | 0, 124 | create_touchpad_handler }, 125 | { "input", DEV_PATH_ROOT "/wsp[0-9]*", 126 | 0, 127 | create_touchpad_handler }, 128 | { "input", DEV_PATH_ROOT "/uep[0-9]*", 129 | 0, 130 | create_touchscreen_handler }, 131 | { "input", DEV_PATH_ROOT "/sysmouse", 132 | SCFLAG_SKIP_IF_EVDEV, 133 | create_sysmouse_handler }, 134 | { "input", DEV_PATH_ROOT "/vboxguest", 135 | 0, 136 | create_mouse_handler }, 137 | { "drm", DEV_PATH_ROOT "/dri/card[0-9]*", 138 | 0, 139 | create_drm_handler }, 140 | }; 141 | 142 | static struct subsystem_config * 143 | get_subsystem_config_by_syspath(const char *path) 144 | { 145 | size_t i; 146 | 147 | for (i = 0; i < nitems(subsystems); i++) 148 | if (fnmatch(subsystems[i].syspath, path, 0) == 0) 149 | return (&subsystems[i]); 150 | 151 | return (NULL); 152 | } 153 | 154 | static bool 155 | kernel_has_evdev_enabled() 156 | { 157 | static int enabled = -1; 158 | size_t len; 159 | 160 | if (enabled != -1) 161 | return (enabled); 162 | 163 | if (sysctlbyname("kern.features.evdev_support", &enabled, &len, NULL, 0) < 0) 164 | return (0); 165 | 166 | TRC("() EVDEV enabled: %s", enabled ? "true" : "false"); 167 | return (enabled); 168 | } 169 | 170 | const char * 171 | get_subsystem_by_syspath(const char *syspath) 172 | { 173 | struct subsystem_config *sc; 174 | 175 | sc = get_subsystem_config_by_syspath(syspath); 176 | if (sc == NULL) 177 | return (UNKNOWN_SUBSYSTEM); 178 | if (sc->flags & SCFLAG_SKIP_IF_EVDEV && kernel_has_evdev_enabled()) { 179 | TRC("(%s) EVDEV enabled -> skipping device", syspath); 180 | return (UNKNOWN_SUBSYSTEM); 181 | } 182 | 183 | return (sc->subsystem); 184 | } 185 | 186 | const char * 187 | get_sysname_by_syspath(const char *syspath) 188 | { 189 | 190 | return (strbase(syspath)); 191 | } 192 | 193 | const char * 194 | get_devpath_by_syspath(const char *syspath) 195 | { 196 | 197 | return (syspath); 198 | } 199 | 200 | const char * 201 | get_syspath_by_devpath(const char *devpath) 202 | { 203 | 204 | return (devpath); 205 | } 206 | 207 | void 208 | invoke_create_handler(struct udev_device *ud) 209 | { 210 | const char *path; 211 | struct subsystem_config *sc; 212 | 213 | path = udev_device_get_syspath(ud); 214 | sc = get_subsystem_config_by_syspath(path); 215 | if (sc == NULL || sc->create_handler == NULL) 216 | return; 217 | if (sc->flags & SCFLAG_SKIP_IF_EVDEV && kernel_has_evdev_enabled()) { 218 | TRC("(%p) EVDEV enabled -> skipping device", ud); 219 | return; 220 | } 221 | 222 | sc->create_handler(ud); 223 | } 224 | 225 | static int 226 | set_input_device_type(struct udev_device *ud, int input_type) 227 | { 228 | struct udev_list *ul; 229 | 230 | ul = udev_device_get_properties_list(ud); 231 | if (udev_list_insert(ul, "ID_INPUT", "1") < 0) 232 | return (-1); 233 | switch (input_type) { 234 | case IT_KEYBOARD: 235 | udev_list_insert(ul, "ID_INPUT_KEY", "1"); 236 | udev_list_insert(ul, "ID_INPUT_KEYBOARD", "1"); 237 | break; 238 | case IT_MOUSE: 239 | udev_list_insert(ul, "ID_INPUT_MOUSE", "1"); 240 | break; 241 | case IT_TOUCHPAD: 242 | udev_list_insert(ul, "ID_INPUT_MOUSE", "1"); 243 | udev_list_insert(ul, "ID_INPUT_TOUCHPAD", "1"); 244 | break; 245 | case IT_TOUCHSCREEN: 246 | udev_list_insert(ul, "ID_INPUT_TOUCHSCREEN", "1"); 247 | break; 248 | case IT_JOYSTICK: 249 | udev_list_insert(ul, "ID_INPUT_JOYSTICK", "1"); 250 | break; 251 | case IT_TABLET: 252 | udev_list_insert(ul, "ID_INPUT_TABLET", "1"); 253 | break; 254 | case IT_ACCELEROMETER: 255 | udev_list_insert(ul, "ID_INPUT_ACCELEROMETER", "1"); 256 | break; 257 | case IT_SWITCH: 258 | udev_list_insert(ul, "ID_INPUT_SWITCH", "1"); 259 | break; 260 | } 261 | return (0); 262 | } 263 | 264 | static struct udev_device * 265 | create_xorg_parent(struct udev_device *ud, const char* sysname, 266 | const char *name, const char *product, const char *pnp_id) 267 | { 268 | struct udev_device *parent; 269 | struct udev *udev; 270 | struct udev_list *props, *sysattrs; 271 | 272 | /* xorg-server gets device name and vendor string from parent device */ 273 | udev = udev_device_get_udev(ud); 274 | parent = udev_device_new_common(udev, sysname, UD_ACTION_NONE); 275 | if (parent == NULL) 276 | return NULL; 277 | 278 | props = udev_device_get_properties_list(parent); 279 | sysattrs = udev_device_get_sysattr_list(parent); 280 | udev_list_insert(props, "NAME", name); 281 | udev_list_insert(sysattrs, "name", name); 282 | if (product != NULL) 283 | udev_list_insert(props, "PRODUCT", product); 284 | if (pnp_id != NULL) 285 | udev_list_insert(sysattrs, "id", product); 286 | 287 | return (parent); 288 | } 289 | 290 | #ifdef HAVE_LINUX_INPUT_H 291 | 292 | #define LONG_BITS (sizeof(long) * 8) 293 | #define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS) 294 | 295 | static inline bool 296 | bit_is_set(const unsigned long *array, int bit) 297 | { 298 | return !!(array[bit / LONG_BITS] & (1LL << (bit % LONG_BITS))); 299 | } 300 | 301 | static inline bool 302 | bit_find(const unsigned long *array, int start, int stop) 303 | { 304 | int i; 305 | 306 | for (i = start; i < stop; i++) 307 | if (bit_is_set(array, i)) 308 | return true; 309 | 310 | return false; 311 | } 312 | 313 | void 314 | create_evdev_handler(struct udev_device *ud) 315 | { 316 | struct udev_device *parent; 317 | const char *sysname, *unit; 318 | char name[80], product[80], phys[80], mib[32]; 319 | int fd = -1, input_type = IT_NONE; 320 | size_t len; 321 | bool opened = false; 322 | bool has_keys, has_buttons, has_lmr; 323 | bool has_rel_axes, has_abs_axes, has_mt, has_switches; 324 | unsigned long key_bits[NLONGS(KEY_CNT)]; 325 | unsigned long rel_bits[NLONGS(REL_CNT)]; 326 | unsigned long abs_bits[NLONGS(ABS_CNT)]; 327 | unsigned long sw_bits[NLONGS(SW_CNT)]; 328 | unsigned long prp_bits[NLONGS(INPUT_PROP_CNT)]; 329 | struct input_id id; 330 | 331 | sysname = udev_device_get_sysname(ud); 332 | len = syspathlen_wo_units(sysname); 333 | unit = sysname + len; 334 | 335 | snprintf(mib, sizeof(mib), "kern.evdev.input.%s.name", unit); 336 | len = sizeof(name); 337 | if (sysctlbyname(mib, name, &len, NULL, 0) < 0) 338 | goto use_ioctl; 339 | 340 | snprintf(mib, sizeof(mib), "kern.evdev.input.%s.phys", unit); 341 | len = sizeof(phys); 342 | if (sysctlbyname(mib, phys, &len, NULL, 0) < 0) 343 | goto use_ioctl; 344 | 345 | snprintf(mib, sizeof(mib), "kern.evdev.input.%s.id", unit); 346 | len = sizeof(id); 347 | if (sysctlbyname(mib, &id, &len, NULL, 0) < 0) 348 | goto use_ioctl; 349 | 350 | snprintf(mib, sizeof(mib), "kern.evdev.input.%s.key_bits", unit); 351 | len = sizeof(key_bits); 352 | if (sysctlbyname(mib, key_bits, &len, NULL, 0) < 0) 353 | goto use_ioctl; 354 | 355 | snprintf(mib, sizeof(mib), "kern.evdev.input.%s.rel_bits", unit); 356 | len = sizeof(rel_bits); 357 | if (sysctlbyname(mib, rel_bits, &len, NULL, 0) < 0) 358 | goto use_ioctl; 359 | 360 | snprintf(mib, sizeof(mib), "kern.evdev.input.%s.abs_bits", unit); 361 | len = sizeof(abs_bits); 362 | if (sysctlbyname(mib, abs_bits, &len, NULL, 0) < 0) 363 | goto use_ioctl; 364 | 365 | snprintf(mib, sizeof(mib), "kern.evdev.input.%s.sw_bits", unit); 366 | len = sizeof(sw_bits); 367 | if (sysctlbyname(mib, sw_bits, &len, NULL, 0) < 0) 368 | goto use_ioctl; 369 | 370 | snprintf(mib, sizeof(mib), "kern.evdev.input.%s.props", unit); 371 | len = sizeof(prp_bits); 372 | if (sysctlbyname(mib, prp_bits, &len, NULL, 0) < 0) 373 | goto use_ioctl; 374 | 375 | goto found_values; 376 | 377 | use_ioctl: 378 | ERR("sysctl not found, opening device and using ioctl"); 379 | 380 | fd = path_to_fd(udev_device_get_devnode(ud)); 381 | if (fd == -1) { 382 | fd = open(udev_device_get_devnode(ud), O_RDONLY | O_CLOEXEC); 383 | opened = true; 384 | } 385 | if (fd == -1) 386 | return; 387 | 388 | if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0 || 389 | (ioctl(fd, EVIOCGPHYS(sizeof(phys)), phys) < 0 && errno != ENOENT) || 390 | ioctl(fd, EVIOCGID, &id) < 0 || 391 | ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bits)), rel_bits) < 0 || 392 | ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)), abs_bits) < 0 || 393 | ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), key_bits) < 0 || 394 | ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bits)), sw_bits) < 0 || 395 | ioctl(fd, EVIOCGPROP(sizeof(prp_bits)), prp_bits) < 0) { 396 | ERR("could not query evdev"); 397 | goto bail_out; 398 | } 399 | 400 | found_values: 401 | /* Derived from EvdevProbe() function of xf86-input-evdev driver */ 402 | has_keys = bit_find(key_bits, 0, BTN_MISC); 403 | has_buttons = bit_find(key_bits, BTN_MISC, BTN_JOYSTICK); 404 | has_lmr = bit_find(key_bits, BTN_LEFT, BTN_MIDDLE + 1); 405 | has_rel_axes = bit_find(rel_bits, 0, REL_CNT); 406 | has_abs_axes = bit_find(abs_bits, 0, ABS_CNT); 407 | has_switches = bit_find(sw_bits, 0, SW_CNT); 408 | has_mt = bit_find(abs_bits, ABS_MT_SLOT, ABS_CNT); 409 | 410 | if (has_abs_axes) { 411 | if (has_mt && !has_buttons) { 412 | if (bit_is_set(key_bits, BTN_JOYSTICK)) { 413 | input_type = IT_JOYSTICK; 414 | goto detected; 415 | } else { 416 | has_buttons = true; 417 | } 418 | } 419 | 420 | if (bit_is_set(abs_bits, ABS_X) && 421 | bit_is_set(abs_bits, ABS_Y)) { 422 | if (bit_is_set(key_bits, BTN_TOOL_PEN) || 423 | bit_is_set(key_bits, BTN_STYLUS) || 424 | bit_is_set(key_bits, BTN_STYLUS2)) { 425 | input_type = IT_TABLET; 426 | goto detected; 427 | } else if (bit_is_set(key_bits, BTN_SELECT) || 428 | bit_is_set(key_bits, BTN_START) || 429 | bit_is_set(key_bits, BTN_TL) || 430 | bit_is_set(key_bits, BTN_TR)) { 431 | input_type = IT_JOYSTICK; 432 | goto detected; 433 | } else if (bit_is_set(abs_bits, ABS_PRESSURE) || 434 | bit_is_set(key_bits, BTN_TOUCH)) { 435 | if (has_lmr || 436 | bit_is_set(key_bits, BTN_TOOL_FINGER)) { 437 | input_type = IT_TOUCHPAD; 438 | } else { 439 | input_type = IT_TOUCHSCREEN; 440 | } 441 | goto detected; 442 | } else if (!(bit_is_set(rel_bits, REL_X) && 443 | bit_is_set(rel_bits, REL_Y)) && 444 | has_lmr) { 445 | /* some touchscreens use BTN_LEFT rather than BTN_TOUCH */ 446 | input_type = IT_TOUCHSCREEN; 447 | goto detected; 448 | } 449 | } 450 | } 451 | 452 | if (bit_is_set(prp_bits, INPUT_PROP_ACCELEROMETER)) 453 | input_type = IT_ACCELEROMETER; 454 | else if (has_keys) 455 | input_type = IT_KEYBOARD; 456 | else if (bit_is_set(prp_bits, INPUT_PROP_POINTER) || has_rel_axes || has_abs_axes || has_buttons) 457 | input_type = IT_MOUSE; 458 | else if (has_switches) 459 | input_type = IT_SWITCH; 460 | 461 | if (input_type == IT_NONE) 462 | goto bail_out; 463 | 464 | detected: 465 | set_input_device_type(ud, input_type); 466 | 467 | sysname = phys[0] == 0 ? virtual_sysname : phys; 468 | 469 | *(strchrnul(name, ',')) = '\0'; /* strip name */ 470 | 471 | snprintf(product, sizeof(product), "%x/%x/%x/%x", 472 | id.bustype, id.vendor, id.product, id.version); 473 | 474 | parent = create_xorg_parent(ud, sysname, name, product, NULL); 475 | if (parent != NULL) 476 | udev_device_set_parent(ud, parent); 477 | 478 | bail_out: 479 | if (opened) 480 | close(fd); 481 | } 482 | #endif 483 | 484 | size_t 485 | syspathlen_wo_units(const char *path) { 486 | size_t len; 487 | 488 | len = strlen(path); 489 | while (len > 0) { 490 | if (path[len-1] < '0' || path[len-1] > '9') 491 | break; 492 | --len; 493 | } 494 | return len; 495 | } 496 | 497 | void 498 | set_parent(struct udev_device *ud) 499 | { 500 | struct udev_device *parent; 501 | char devname[DEV_PATH_MAX], mib[32], pnpinfo[1024]; 502 | char name[80], product[80], parentname[80], *pnp_id; 503 | const char *sysname, *unit, *vendorstr, *prodstr, *devicestr; 504 | size_t len, vendorlen, prodlen, devicelen, pnplen; 505 | uint32_t bus, prod, vendor; 506 | 507 | sysname = udev_device_get_sysname(ud); 508 | len = syspathlen_wo_units(sysname); 509 | /* Check if device unit number found */ 510 | if (strlen(sysname) == len) 511 | return; 512 | snprintf(devname, len + 1, "%s", sysname); 513 | unit = sysname + len; 514 | 515 | snprintf(mib, sizeof(mib), "dev.%.17s.%.3s.%%desc", devname, unit); 516 | len = sizeof(name); 517 | if (sysctlbyname(mib, name, &len, NULL, 0) < 0) 518 | return; 519 | *(strchrnul(name, ',')) = '\0'; /* strip name */ 520 | 521 | snprintf(mib, sizeof(mib), "dev.%.14s.%.3s.%%pnpinfo", devname, unit); 522 | len = sizeof(pnpinfo); 523 | if (sysctlbyname(mib, pnpinfo, &len, NULL, 0) < 0) 524 | return; 525 | 526 | snprintf(mib, sizeof(mib), "dev.%.15s.%.3s.%%parent", devname, unit); 527 | len = sizeof(parentname); 528 | if (sysctlbyname(mib, parentname, &len, NULL, 0) < 0) 529 | return; 530 | 531 | vendorstr = get_kern_prop_value(pnpinfo, "vendor", &vendorlen); 532 | prodstr = get_kern_prop_value(pnpinfo, "product", &prodlen); 533 | devicestr = get_kern_prop_value(pnpinfo, "device", &devicelen); 534 | pnp_id = get_kern_prop_value(pnpinfo, "_HID", &pnplen); 535 | if (pnp_id != NULL && pnplen == 4 && strncmp(pnp_id, "none", 4) == 0) 536 | pnp_id = NULL; 537 | if (pnp_id != NULL) 538 | pnp_id[pnplen] = '\0'; 539 | if (prodstr != NULL && vendorstr != NULL) { 540 | /* XXX: should parent be compared to uhub* to detect usb? */ 541 | vendor = strtol(vendorstr, NULL, 0); 542 | prod = strtol(prodstr, NULL, 0); 543 | bus = BUS_USB; 544 | } else if (devicestr != NULL && vendorstr != NULL) { 545 | vendor = strtol(vendorstr, NULL, 0); 546 | prod = strtol(devicestr, NULL, 0); 547 | bus = BUS_PCI; 548 | } else if (strcmp(parentname, "atkbdc0") == 0) { 549 | if (strcmp(devname, "atkbd") == 0) { 550 | vendor = PS2_KEYBOARD_VENDOR; 551 | prod = PS2_KEYBOARD_PRODUCT; 552 | } else if (strcmp(devname, "psm") == 0) { 553 | vendor = PS2_MOUSE_VENDOR; 554 | prod = PS2_MOUSE_GENERIC_PRODUCT; 555 | } else { 556 | vendor = 0; 557 | prod = 0; 558 | } 559 | bus = BUS_I8042; 560 | } else { 561 | vendor = 0; 562 | prod = 0; 563 | bus = BUS_VIRTUAL; 564 | } 565 | snprintf(product, sizeof(product), "%x/%x/%x/0", bus, vendor, prod); 566 | parent = create_xorg_parent(ud, sysname, name, product, pnp_id); 567 | if (parent != NULL) 568 | udev_device_set_parent(ud, parent); 569 | 570 | return; 571 | } 572 | 573 | void 574 | create_keyboard_handler(struct udev_device *ud) 575 | { 576 | 577 | set_input_device_type(ud, IT_KEYBOARD); 578 | set_parent(ud); 579 | } 580 | 581 | void 582 | create_mouse_handler(struct udev_device *ud) 583 | { 584 | 585 | set_input_device_type(ud, IT_MOUSE); 586 | set_parent(ud); 587 | } 588 | 589 | void 590 | create_kbdmux_handler(struct udev_device *ud) 591 | { 592 | struct udev_device *parent; 593 | const char* sysname; 594 | 595 | set_input_device_type(ud, IT_KEYBOARD); 596 | sysname = udev_device_get_sysname(ud); 597 | parent = create_xorg_parent(ud, sysname, 598 | "System keyboard multiplexor", "6/1/1/0", NULL); 599 | if (parent != NULL) 600 | udev_device_set_parent(ud, parent); 601 | } 602 | 603 | void 604 | create_sysmouse_handler(struct udev_device *ud) 605 | { 606 | struct udev_device *parent; 607 | const char* sysname; 608 | 609 | set_input_device_type(ud, IT_MOUSE); 610 | sysname = udev_device_get_sysname(ud); 611 | parent = create_xorg_parent(ud, sysname, 612 | "System mouse", "6/2/1/0", NULL); 613 | if (parent != NULL) 614 | udev_device_set_parent(ud, parent); 615 | } 616 | 617 | void 618 | create_joystick_handler(struct udev_device *ud) 619 | { 620 | 621 | set_input_device_type(ud, IT_JOYSTICK); 622 | set_parent(ud); 623 | } 624 | 625 | void 626 | create_touchpad_handler(struct udev_device *ud) 627 | { 628 | 629 | set_input_device_type(ud, IT_TOUCHPAD); 630 | set_parent(ud); 631 | } 632 | 633 | void create_touchscreen_handler(struct udev_device *ud) 634 | { 635 | 636 | set_input_device_type(ud, IT_TOUCHSCREEN); 637 | set_parent(ud); 638 | } 639 | 640 | void 641 | create_drm_handler(struct udev_device *ud) 642 | { 643 | udev_list_insert(udev_device_get_properties_list(ud), "HOTPLUG", "1"); 644 | set_parent(ud); 645 | } 646 | -------------------------------------------------------------------------------- /udev-utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UDEV_UTILS_H_ 2 | #define UDEV_UTILS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "libudev.h" 9 | 10 | #define LIBUDEV_EXPORT __attribute__((visibility("default"))) 11 | 12 | #define DEV_PATH_ROOT "/dev" 13 | #define DEV_PATH_MAX 80 14 | #define SYS_PATH_MAX 80 15 | 16 | #define UNKNOWN_SUBSYSTEM "#" 17 | 18 | const char *get_subsystem_by_syspath(const char *syspath); 19 | const char *get_sysname_by_syspath(const char *syspath); 20 | const char *get_devpath_by_syspath(const char *syspath); 21 | const char *get_syspath_by_devpath(const char *devpath); 22 | 23 | void invoke_create_handler(struct udev_device *ud); 24 | size_t syspathlen_wo_units(const char *path); 25 | 26 | #endif /* UDEV_UTILS_H_ */ 27 | -------------------------------------------------------------------------------- /udev.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Vladimir Kondratyev 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include "config.h" 28 | #include "libudev.h" 29 | #include "udev.h" 30 | #include "udev-utils.h" 31 | #include "utils.h" 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | struct udev { 38 | _Atomic(int) refcount; 39 | void *userdata; 40 | }; 41 | 42 | LIBUDEV_EXPORT struct udev * 43 | udev_new(void) 44 | { 45 | struct udev *udev; 46 | 47 | TRC(); 48 | udev = calloc(1, sizeof(struct udev)); 49 | if (udev) { 50 | atomic_init(&udev->refcount, 1); 51 | udev->userdata = NULL; 52 | } 53 | 54 | return (udev); 55 | } 56 | 57 | struct udev * 58 | _udev_ref(struct udev *udev) 59 | { 60 | 61 | atomic_fetch_add(&udev->refcount, 1); 62 | return udev; 63 | } 64 | 65 | LIBUDEV_EXPORT struct udev * 66 | udev_ref(struct udev *udev) 67 | { 68 | 69 | TRC("(%p) refcount=%d", udev, udev->refcount); 70 | return (_udev_ref(udev)); 71 | } 72 | 73 | void 74 | _udev_unref(struct udev *udev) 75 | { 76 | 77 | if (atomic_fetch_sub(&udev->refcount, 1) == 1) 78 | free(udev); 79 | } 80 | 81 | LIBUDEV_EXPORT void 82 | udev_unref(struct udev *udev) 83 | { 84 | 85 | TRC("(%p) refcount=%d", udev, udev->refcount); 86 | _udev_unref(udev); 87 | } 88 | 89 | LIBUDEV_EXPORT const char * 90 | udev_get_dev_path(struct udev *udev __unused) 91 | { 92 | 93 | TRC(); 94 | return (DEV_PATH_ROOT); 95 | } 96 | 97 | LIBUDEV_EXPORT void * 98 | udev_get_userdata(struct udev *udev) 99 | { 100 | 101 | TRC(); 102 | return (udev->userdata); 103 | } 104 | 105 | LIBUDEV_EXPORT void 106 | udev_set_userdata(struct udev *udev, void *userdata) 107 | { 108 | 109 | TRC(); 110 | udev->userdata = userdata; 111 | } 112 | -------------------------------------------------------------------------------- /udev.h: -------------------------------------------------------------------------------- 1 | #ifndef UDEV_H_ 2 | #define UDEV_H_ 3 | 4 | #include "libudev.h" 5 | 6 | struct udev *_udev_ref(struct udev *udev); 7 | void _udev_unref(struct udev *udev); 8 | 9 | #endif /* UDEV_H_ */ 10 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Vladimir Kondratyev 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include "config.h" 28 | #include "utils.h" 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #ifdef HAVE_LIBPROCSTAT_H 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #else 46 | #include 47 | #endif 48 | 49 | #ifdef HAVE_DEVINFO_H 50 | #include 51 | #include 52 | static pthread_mutex_t devinfo_mtx = PTHREAD_MUTEX_INITIALIZER; 53 | #endif 54 | 55 | #ifndef HAVE_PIPE2 56 | #include 57 | #endif 58 | 59 | int 60 | socket_connect(const char *path) 61 | { 62 | struct sockaddr_un sa; 63 | int fd; 64 | 65 | fd = socket(AF_UNIX, SOCK_STREAM, 0); 66 | if (fd < 0) 67 | return (-1); 68 | 69 | sa.sun_family = AF_UNIX; 70 | strlcpy(sa.sun_path, path, sizeof(sa.sun_path)); 71 | 72 | if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { 73 | close(fd); 74 | return (-1); 75 | } 76 | 77 | return (fd); 78 | } 79 | 80 | ssize_t 81 | socket_readline(int fd, char *buf, size_t len) 82 | { 83 | size_t pos; 84 | 85 | for (pos = 0; pos < len; ++pos) { 86 | if (read(fd, buf + pos, 1) < 1) 87 | return (-1); 88 | 89 | if (buf[pos] == 0 || buf[pos] == '\n') { 90 | buf[pos] = 0; 91 | return (pos); 92 | } 93 | } 94 | return (-1); 95 | } 96 | 97 | /* 98 | * locates the occurrence of last component of the pathname 99 | * pointed to by path 100 | */ 101 | char * 102 | strbase(const char *path) 103 | { 104 | char *base; 105 | 106 | base = strrchr(path, '/'); 107 | if (base != NULL) 108 | base++; 109 | 110 | return (base); 111 | } 112 | 113 | char * 114 | get_kern_prop_value(const char *buf, const char *prop, size_t *len) 115 | { 116 | char *prop_pos; 117 | size_t prop_len; 118 | 119 | prop_len = strlen(prop); 120 | prop_pos = strstr(buf, prop); 121 | if (prop_pos == NULL || 122 | (prop_pos != buf && prop_pos[-1] != ' ') || 123 | prop_pos[prop_len] != '=') 124 | return (NULL); 125 | 126 | *len = strchrnul(prop_pos + prop_len + 1, ' ') - prop_pos - prop_len - 1; 127 | return (prop_pos + prop_len + 1); 128 | } 129 | 130 | int 131 | match_kern_prop_value(const char *buf, const char *prop, 132 | const char *match_value) 133 | { 134 | const char *value; 135 | size_t len; 136 | 137 | value = get_kern_prop_value(buf, prop, &len); 138 | if (value != NULL && 139 | len == strlen(match_value) && 140 | strncmp(value, match_value, len) == 0) 141 | return (1); 142 | 143 | return (0); 144 | } 145 | 146 | int 147 | path_to_fd(const char *path) 148 | { 149 | int fd = -1; 150 | 151 | #ifdef HAVE_LIBPROCSTAT_H 152 | struct procstat *procstat; 153 | struct kinfo_proc *kip; 154 | struct filestat_list *head = NULL; 155 | struct filestat *fst; 156 | unsigned int count; 157 | #else 158 | struct stat st, fst; 159 | #define MAX_FD 128 160 | #endif 161 | 162 | #ifdef HAVE_LIBPROCSTAT_H 163 | procstat = procstat_open_sysctl(); 164 | if (procstat == NULL) 165 | return (-1); 166 | 167 | count = 0; 168 | kip = procstat_getprocs(procstat, KERN_PROC_PID, getpid(), &count); 169 | if (kip == NULL || count != 1) 170 | goto out; 171 | 172 | head = procstat_getfiles(procstat, kip, 0); 173 | if (head == NULL) 174 | goto out; 175 | 176 | STAILQ_FOREACH(fst, head, next) { 177 | if (fst->fs_uflags == 0 && 178 | fst->fs_type == PS_FST_TYPE_VNODE && 179 | fst->fs_path != NULL && 180 | strcmp(fst->fs_path, path) == 0) { 181 | fd = fst->fs_fd; 182 | break; 183 | } 184 | } 185 | 186 | out: 187 | if (head != NULL) 188 | procstat_freefiles(procstat, head); 189 | if (kip != NULL) 190 | procstat_freeprocs(procstat, kip); 191 | procstat_close(procstat); 192 | #else 193 | if (stat(path, &st) != 0) 194 | return (-1); 195 | 196 | for (fd = 0; fd < MAX_FD; ++fd) { 197 | 198 | if (fstat(fd, &fst) != 0) { 199 | if (errno != EBADF) { 200 | return -1; 201 | } else { 202 | continue; 203 | } 204 | } 205 | 206 | if (fst.st_rdev == st.st_rdev) 207 | break; 208 | } 209 | 210 | if (fd == MAX_FD) 211 | return (-1); 212 | #endif 213 | 214 | return (fd); 215 | } 216 | 217 | static int 218 | scandir_sub(char *path, int off, int rem, struct scan_ctx *ctx) 219 | { 220 | DIR *dir; 221 | struct dirent *ent; 222 | 223 | dir = opendir(path); 224 | if (dir == NULL) 225 | return (errno == ENOENT ? 0 : -1); 226 | 227 | while ((ent = readdir(dir)) != NULL) { 228 | if (strcmp(ent->d_name, ".") == 0 || 229 | strcmp(ent->d_name, "..") == 0) 230 | continue; 231 | 232 | int len = strlen(ent->d_name); 233 | if (len > rem) 234 | continue; 235 | 236 | strcpy(path + off, ent->d_name); 237 | off += len; 238 | rem -= len; 239 | 240 | if ((ctx->recursive) && (ent->d_type == DT_DIR)) { 241 | if (rem < 1) 242 | break; 243 | path[off] = '/'; 244 | path[off+1] = '\0'; 245 | off++; 246 | rem--; 247 | /* recurse */ 248 | scandir_sub(path, off, rem, ctx); 249 | off--; 250 | rem++; 251 | } else { 252 | if ((ctx->cb)(path, ent->d_type, ctx->args) < 0) { 253 | closedir(dir); 254 | return (-1); 255 | } 256 | } 257 | off -= len; 258 | rem += len; 259 | } 260 | closedir(dir); 261 | return (0); 262 | } 263 | 264 | int 265 | scandir_recursive(char *path, size_t len, struct scan_ctx *ctx) 266 | { 267 | size_t root_len = strlen(path); 268 | 269 | return (scandir_sub(path, root_len, len - root_len - 1, ctx)); 270 | } 271 | 272 | #ifdef HAVE_DEVINFO_H 273 | static int 274 | scandev_sub(struct devinfo_dev *dev, void *args) 275 | { 276 | struct scan_ctx *ctx = args; 277 | 278 | if (dev->dd_name[0] != '\0' && dev->dd_state >= DS_ATTACHED) 279 | if ((ctx->cb)(dev->dd_name, DT_CHR, ctx->args) < 0) 280 | return (-1); 281 | 282 | /* recurse */ 283 | return (devinfo_foreach_device_child(dev, scandev_sub, args)); 284 | } 285 | 286 | 287 | int 288 | scandev_recursive (struct scan_ctx *ctx) 289 | { 290 | struct devinfo_dev *root; 291 | int ret; 292 | 293 | pthread_mutex_lock(&devinfo_mtx); 294 | if (devinfo_init()) { 295 | pthread_mutex_unlock(&devinfo_mtx); 296 | ERR("devinfo_init failed"); 297 | return (-1); 298 | } 299 | 300 | if ((root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE)) == NULL) { 301 | ERR("faled to init devinfo root device"); 302 | ret = -1; 303 | } else { 304 | ret = devinfo_foreach_device_child(root, scandev_sub, ctx); 305 | if (ret < 0) 306 | ERR("devinfo_foreach_device_child failed"); 307 | } 308 | 309 | devinfo_free(); 310 | pthread_mutex_unlock(&devinfo_mtx); 311 | return (ret); 312 | } 313 | #endif /* HAVE_DEVINFO_H */ 314 | 315 | #ifndef HAVE_PIPE2 316 | int 317 | pipe2(int fildes[2], int flags) 318 | { 319 | int ret; 320 | 321 | ret = pipe(fildes); 322 | if (ret != -1) { 323 | if (flags & O_CLOEXEC) { 324 | fcntl(fildes[0], F_SETFD, FD_CLOEXEC); 325 | fcntl(fildes[1], F_SETFD, FD_CLOEXEC); 326 | } 327 | if (flags & O_NONBLOCK) { 328 | fcntl(fildes[0], F_SETFL, O_NONBLOCK); 329 | fcntl(fildes[1], F_SETFL, O_NONBLOCK); 330 | } 331 | } 332 | 333 | return (ret); 334 | } 335 | #endif /* !HAVE_PIPE2 */ 336 | 337 | #ifndef HAVE_STRCHRNUL 338 | char * 339 | strchrnul(const char *p, int ch) 340 | { 341 | char c; 342 | 343 | c = ch; 344 | for (;; ++p) { 345 | if (*p == c || *p == '\0') 346 | return ((char *)p); 347 | } 348 | /* NOTREACHED */ 349 | } 350 | #endif /* !HAVE_STRCHRNUL */ 351 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H_ 2 | #define UTILS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | /* #define ENABLE_TRACE */ 10 | #define LOG_LEVEL 0 11 | 12 | /* 13 | #ifndef LOG_LEVEL 14 | #define LOG_LEVEL -1 15 | #endif 16 | */ 17 | #ifdef ENABLE_TRACE 18 | #define TRC(msg, ...) \ 19 | do { \ 20 | int saved_errno_ = errno; \ 21 | fprintf(stderr, "%s" msg "\n", __FUNCTION__, ##__VA_ARGS__); \ 22 | errno = saved_errno_; \ 23 | } while (0) 24 | #else 25 | #define TRC(msg, ...) 26 | #endif 27 | 28 | #define LOG(level, msg, ...) do { \ 29 | if (level < LOG_LEVEL) { \ 30 | if (level == 0 && errno != 0) \ 31 | fprintf(stderr, msg" %d(%s)\n", ##__VA_ARGS__, \ 32 | errno, strerror(errno)); \ 33 | else \ 34 | fprintf(stderr, msg"\n", ##__VA_ARGS__); \ 35 | } \ 36 | } while (0) 37 | #define ERR(...) LOG(0, __VA_ARGS__) 38 | #define DBG(...) LOG(1, __VA_ARGS__) 39 | 40 | #define UNIMPL() ERR("%s is unimplemented", __FUNCTION__) 41 | 42 | typedef int (* scan_cb_t) (const char *path, int type, void *args); 43 | 44 | /* If .recursive is true, then .cb gets called for non-dir 45 | * paths, an the overall scandir is recursive. If .recursive 46 | * is false, then .cb gets called for all paths in the 47 | * directory, and scandir is non-recursive. 48 | */ 49 | struct scan_ctx { 50 | bool recursive; 51 | scan_cb_t cb; 52 | void *args; 53 | }; 54 | 55 | char *strbase(const char *path); 56 | char *get_kern_prop_value(const char *buf, const char *prop, size_t *len); 57 | int match_kern_prop_value(const char *buf, const char *prop, const char *value); 58 | int socket_connect(const char *path); 59 | ssize_t socket_readline(int fd, char *buf, size_t len); 60 | int path_to_fd(const char *path); 61 | int scandir_recursive(char *path, size_t len, struct scan_ctx *ctx); 62 | #ifdef HAVE_DEVINFO_H 63 | int scandev_recursive(struct scan_ctx *ctx); 64 | #endif 65 | #ifndef HAVE_PIPE2 66 | int pipe2(int fildes[2], int flags); 67 | #endif 68 | #ifndef HAVE_STRCHRNUL 69 | char *strchrnul(const char *p, int ch); 70 | #endif 71 | 72 | #endif /* UTILS_H_ */ 73 | --------------------------------------------------------------------------------