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 57 #include <libprop/proplib.h> 58 #include <sys/udev.h> 59 #define LIBDEVATTR_INTERNAL 60 #include "devattr.h" 61 62 struct udev_enumerate { 63 struct udev *udev_ctx; 64 prop_array_t ev_filt; 65 prop_array_t pa; 66 int refs; 67 TAILQ_HEAD(, udev_list_entry) list_entries; 68 }; 69 70 struct udev_list_entry { 71 struct udev *udev_ctx; 72 prop_dictionary_t dict; 73 TAILQ_ENTRY(udev_list_entry) link; 74 }; 75 76 struct udev_enumerate * 77 udev_enumerate_new(struct udev *udev_ctx) 78 { 79 struct udev_enumerate *udev_enum; 80 81 udev_enum = malloc(sizeof(struct udev_enumerate)); 82 83 udev_enum->refs = 1; 84 udev_enum->ev_filt = NULL; 85 udev_enum->pa = NULL; 86 TAILQ_INIT(&udev_enum->list_entries); 87 udev_enum->udev_ctx = udev_ref(udev_ctx); 88 89 return udev_enum; 90 } 91 92 struct udev_enumerate * 93 udev_enumerate_ref(struct udev_enumerate *udev_enum) 94 { 95 atomic_add_int(&udev_enum->refs, 1); 96 97 return udev_enum; 98 } 99 100 void 101 udev_enumerate_unref(struct udev_enumerate *udev_enum) 102 { 103 struct udev_list_entry *le; 104 int refcount; 105 106 refcount = atomic_fetchadd_int(&udev_enum->refs, -1); 107 108 if (refcount == 1) { 109 atomic_subtract_int(&udev_enum->refs, 0x400); /* in destruction */ 110 if (udev_enum->pa != NULL) 111 prop_object_release(udev_enum->pa); 112 if (udev_enum->ev_filt != NULL) 113 prop_object_release(udev_enum->ev_filt); 114 115 while (!TAILQ_EMPTY(&udev_enum->list_entries)) { 116 le = TAILQ_FIRST(&udev_enum->list_entries); 117 TAILQ_REMOVE(&udev_enum->list_entries, le, link); 118 prop_object_release(le->dict); 119 free(le); 120 } 121 udev_unref(udev_enum->udev_ctx); 122 free(udev_enum); 123 } 124 } 125 126 struct udev * 127 udev_enumerate_get_udev(struct udev_enumerate *udev_enum) 128 { 129 return udev_enum->udev_ctx; 130 } 131 132 int 133 udev_enumerate_scan_devices(struct udev_enumerate *udev_enum) 134 { 135 prop_array_t pa; 136 137 if (udev_get_fd(udev_enum->udev_ctx) == -1) 138 return -1; 139 140 pa = udevd_request_devs(udev_get_fd(udev_enum->udev_ctx), udev_enum->ev_filt); 141 if (pa == NULL) 142 return -1; 143 144 prop_object_retain(pa); 145 146 if (udev_enum->pa != NULL) 147 prop_object_release(udev_enum->pa); 148 149 udev_enum->pa = pa; 150 151 return 0; 152 } 153 154 struct udev_list_entry * 155 udev_enumerate_get_list_entry(struct udev_enumerate *udev_enum) 156 { 157 struct udev_list_entry *le; 158 prop_object_iterator_t iter; 159 prop_dictionary_t dict; 160 161 /* If the list is not empty, assume it was populated in an earlier call */ 162 if (!TAILQ_EMPTY(&udev_enum->list_entries)) 163 return TAILQ_FIRST(&udev_enum->list_entries); 164 165 iter = prop_array_iterator(udev_enum->pa); 166 if (iter == NULL) 167 return NULL; 168 169 while ((dict = prop_object_iterator_next(iter)) != NULL) { 170 le = malloc(sizeof(struct udev_list_entry)); 171 if (le == NULL) 172 goto out; 173 174 prop_object_retain(dict); 175 le->dict = dict; 176 le->udev_ctx = udev_enum->udev_ctx; 177 TAILQ_INSERT_TAIL(&udev_enum->list_entries, le, link); 178 } 179 180 le = TAILQ_FIRST(&udev_enum->list_entries); 181 182 out: 183 prop_object_iterator_release(iter); 184 return le; 185 } 186 187 prop_array_t 188 udev_enumerate_get_array(struct udev_enumerate *udev_enum) 189 { 190 return udev_enum->pa; 191 } 192 193 struct udev_list_entry * 194 udev_list_entry_get_next(struct udev_list_entry *list_entry) 195 { 196 return TAILQ_NEXT(list_entry, link); 197 } 198 199 prop_dictionary_t 200 udev_list_entry_get_dictionary(struct udev_list_entry *list_entry) 201 { 202 return list_entry->dict; 203 } 204 205 struct udev_device * 206 udev_list_entry_get_device(struct udev_list_entry *list_entry) 207 { 208 struct udev_device *udev_dev; 209 210 udev_dev = udev_device_new_from_dictionary(list_entry->udev_ctx, 211 list_entry->dict); 212 213 return udev_dev; 214 } 215 216 int 217 udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enum, 218 const char *subsystem) 219 { 220 int ret; 221 222 ret = _udev_enumerate_filter_add_match_gen(udev_enum, 223 EVENT_FILTER_TYPE_WILDCARD, 224 0, 225 "subsystem", 226 __DECONST(char *, subsystem)); 227 228 return ret; 229 } 230 231 int 232 udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enum, 233 const char *subsystem) 234 { 235 int ret; 236 237 ret = _udev_enumerate_filter_add_match_gen(udev_enum, 238 EVENT_FILTER_TYPE_WILDCARD, 239 1, 240 "subsystem", 241 __DECONST(char *, subsystem)); 242 243 return ret; 244 } 245 246 int 247 udev_enumerate_add_match_expr(struct udev_enumerate *udev_enum, 248 const char *key, 249 char *expr) 250 { 251 int ret; 252 253 ret = _udev_enumerate_filter_add_match_gen(udev_enum, 254 EVENT_FILTER_TYPE_WILDCARD, 255 0, 256 key, 257 expr); 258 259 return ret; 260 } 261 262 int 263 udev_enumerate_add_nomatch_expr(struct udev_enumerate *udev_enum, 264 const char *key, 265 char *expr) 266 { 267 int ret; 268 269 ret = _udev_enumerate_filter_add_match_gen(udev_enum, 270 EVENT_FILTER_TYPE_WILDCARD, 271 1, 272 key, 273 expr); 274 275 return ret; 276 } 277 278 int 279 udev_enumerate_add_match_regex(struct udev_enumerate *udev_enum, 280 const char *key, 281 char *expr) 282 { 283 int ret; 284 285 ret = _udev_enumerate_filter_add_match_gen(udev_enum, 286 EVENT_FILTER_TYPE_REGEX, 287 0, 288 key, 289 expr); 290 291 return ret; 292 } 293 294 int 295 udev_enumerate_add_nomatch_regex(struct udev_enumerate *udev_enum, 296 const char *key, 297 char *expr) 298 { 299 int ret; 300 301 ret = _udev_enumerate_filter_add_match_gen(udev_enum, 302 EVENT_FILTER_TYPE_REGEX, 303 1, 304 key, 305 expr); 306 307 return ret; 308 } 309 310 int 311 _udev_enumerate_filter_add_match_gen(struct udev_enumerate *udev_enum, 312 int type, 313 int neg, 314 const char *key, 315 char *expr) 316 { 317 prop_array_t pa; 318 int error; 319 320 if (udev_enum->ev_filt == NULL) { 321 pa = prop_array_create_with_capacity(5); 322 if (pa == NULL) 323 return -1; 324 325 udev_enum->ev_filt = pa; 326 } 327 328 error = _udev_filter_add_match_gen(udev_enum->ev_filt, type, neg, key, expr); 329 330 return error; 331 } 332