1 /* 2 * Copyright (c) 2010 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Alex Hornung <ahornung@gmail.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 #include <sys/types.h> 35 #include <sys/device.h> 36 #include <sys/wait.h> 37 #include <sys/socket.h> 38 #include <sys/ioctl.h> 39 #include <sys/poll.h> 40 #include <sys/queue.h> 41 #include <sys/un.h> 42 #include <cpu/inttypes.h> 43 44 #include <err.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <libgen.h> 48 #include <regex.h> 49 #include <signal.h> 50 #include <stdarg.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <syslog.h> 55 #include <unistd.h> 56 #include <pthread.h> 57 58 #include <libprop/proplib.h> 59 #include <sys/udev.h> 60 #define LIBDEVATTR_INTERNAL 61 #include "devattr.h" 62 63 struct udev_enumerate { 64 struct udev *udev_ctx; 65 prop_array_t ev_filt; 66 prop_array_t pa; 67 int refs; 68 TAILQ_HEAD(, udev_list_entry) list_entries; 69 }; 70 71 struct udev_list_entry { 72 struct udev *udev_ctx; 73 prop_dictionary_t dict; 74 TAILQ_ENTRY(udev_list_entry) link; 75 }; 76 77 struct udev_enumerate * 78 udev_enumerate_new(struct udev *udev_ctx) 79 { 80 struct udev_enumerate *udev_enum; 81 82 udev_enum = malloc(sizeof(struct udev_enumerate)); 83 84 udev_enum->refs = 1; 85 udev_enum->ev_filt = NULL; 86 udev_enum->pa = NULL; 87 TAILQ_INIT(&udev_enum->list_entries); 88 udev_enum->udev_ctx = udev_ref(udev_ctx); 89 90 return udev_enum; 91 } 92 93 struct udev_enumerate * 94 udev_enumerate_ref(struct udev_enumerate *udev_enum) 95 { 96 atomic_add_int(&udev_enum->refs, 1); 97 98 return udev_enum; 99 } 100 101 void 102 udev_enumerate_unref(struct udev_enumerate *udev_enum) 103 { 104 struct udev_list_entry *le; 105 int refcount; 106 107 refcount = atomic_fetchadd_int(&udev_enum->refs, -1); 108 109 if (refcount == 1) { 110 atomic_subtract_int(&udev_enum->refs, 0x400); /* in destruction */ 111 if (udev_enum->pa != NULL) 112 prop_object_release(udev_enum->pa); 113 if (udev_enum->ev_filt != NULL) 114 prop_object_release(udev_enum->ev_filt); 115 116 while (!TAILQ_EMPTY(&udev_enum->list_entries)) { 117 le = TAILQ_FIRST(&udev_enum->list_entries); 118 TAILQ_REMOVE(&udev_enum->list_entries, le, link); 119 prop_object_release(le->dict); 120 free(le); 121 } 122 udev_unref(udev_enum->udev_ctx); 123 free(udev_enum); 124 } 125 } 126 127 struct udev * 128 udev_enumerate_get_udev(struct udev_enumerate *udev_enum) 129 { 130 return udev_enum->udev_ctx; 131 } 132 133 int 134 udev_enumerate_scan_devices(struct udev_enumerate *udev_enum) 135 { 136 prop_array_t pa; 137 138 if (udev_get_fd(udev_enum->udev_ctx) == -1) 139 return -1; 140 141 pa = udevd_request_devs(udev_get_fd(udev_enum->udev_ctx), udev_enum->ev_filt); 142 if (pa == NULL) 143 return -1; 144 145 prop_object_retain(pa); 146 147 if (udev_enum->pa != NULL) 148 prop_object_release(udev_enum->pa); 149 150 udev_enum->pa = pa; 151 152 return 0; 153 } 154 155 struct udev_list_entry * 156 udev_enumerate_get_list_entry(struct udev_enumerate *udev_enum) 157 { 158 struct udev_list_entry *le; 159 prop_object_iterator_t iter; 160 prop_dictionary_t dict; 161 162 /* If the list is not empty, assume it was populated in an earlier call */ 163 if (!TAILQ_EMPTY(&udev_enum->list_entries)) 164 return TAILQ_FIRST(&udev_enum->list_entries); 165 166 iter = prop_array_iterator(udev_enum->pa); 167 if (iter == NULL) 168 return NULL; 169 170 while ((dict = prop_object_iterator_next(iter)) != NULL) { 171 le = malloc(sizeof(struct udev_list_entry)); 172 if (le == NULL) 173 goto out; 174 175 prop_object_retain(dict); 176 le->dict = dict; 177 le->udev_ctx = udev_enum->udev_ctx; 178 TAILQ_INSERT_TAIL(&udev_enum->list_entries, le, link); 179 } 180 181 le = TAILQ_FIRST(&udev_enum->list_entries); 182 183 out: 184 prop_object_iterator_release(iter); 185 return le; 186 } 187 188 prop_array_t 189 udev_enumerate_get_array(struct udev_enumerate *udev_enum) 190 { 191 return udev_enum->pa; 192 } 193 194 struct udev_list_entry * 195 udev_list_entry_get_next(struct udev_list_entry *list_entry) 196 { 197 return TAILQ_NEXT(list_entry, link); 198 } 199 200 prop_dictionary_t 201 udev_list_entry_get_dictionary(struct udev_list_entry *list_entry) 202 { 203 return list_entry->dict; 204 } 205 206 struct udev_device * 207 udev_list_entry_get_device(struct udev_list_entry *list_entry) 208 { 209 struct udev_device *udev_dev; 210 211 udev_dev = udev_device_new_from_dictionary(list_entry->udev_ctx, 212 list_entry->dict); 213 214 return udev_dev; 215 } 216 217 int 218 udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enum, 219 const char *subsystem) 220 { 221 int ret; 222 223 ret = _udev_enumerate_filter_add_match_gen(udev_enum, 224 EVENT_FILTER_TYPE_WILDCARD, 225 0, 226 "subsystem", 227 __DECONST(char *, subsystem)); 228 229 return ret; 230 } 231 232 int 233 udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enum, 234 const char *subsystem) 235 { 236 int ret; 237 238 ret = _udev_enumerate_filter_add_match_gen(udev_enum, 239 EVENT_FILTER_TYPE_WILDCARD, 240 1, 241 "subsystem", 242 __DECONST(char *, subsystem)); 243 244 return ret; 245 } 246 247 int 248 udev_enumerate_add_match_expr(struct udev_enumerate *udev_enum, 249 const char *key, 250 char *expr) 251 { 252 int ret; 253 254 ret = _udev_enumerate_filter_add_match_gen(udev_enum, 255 EVENT_FILTER_TYPE_WILDCARD, 256 0, 257 key, 258 expr); 259 260 return ret; 261 } 262 263 int 264 udev_enumerate_add_nomatch_expr(struct udev_enumerate *udev_enum, 265 const char *key, 266 char *expr) 267 { 268 int ret; 269 270 ret = _udev_enumerate_filter_add_match_gen(udev_enum, 271 EVENT_FILTER_TYPE_WILDCARD, 272 1, 273 key, 274 expr); 275 276 return ret; 277 } 278 279 int 280 udev_enumerate_add_match_regex(struct udev_enumerate *udev_enum, 281 const char *key, 282 char *expr) 283 { 284 int ret; 285 286 ret = _udev_enumerate_filter_add_match_gen(udev_enum, 287 EVENT_FILTER_TYPE_REGEX, 288 0, 289 key, 290 expr); 291 292 return ret; 293 } 294 295 int 296 udev_enumerate_add_nomatch_regex(struct udev_enumerate *udev_enum, 297 const char *key, 298 char *expr) 299 { 300 int ret; 301 302 ret = _udev_enumerate_filter_add_match_gen(udev_enum, 303 EVENT_FILTER_TYPE_REGEX, 304 1, 305 key, 306 expr); 307 308 return ret; 309 } 310 311 int 312 _udev_enumerate_filter_add_match_gen(struct udev_enumerate *udev_enum, 313 int type, 314 int neg, 315 const char *key, 316 char *expr) 317 { 318 prop_array_t pa; 319 int error; 320 321 if (udev_enum->ev_filt == NULL) { 322 pa = prop_array_create_with_capacity(5); 323 if (pa == NULL) 324 return -1; 325 326 udev_enum->ev_filt = pa; 327 } 328 329 error = _udev_filter_add_match_gen(udev_enum->ev_filt, type, neg, key, expr); 330 331 return error; 332 } 333