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 #include <assert.h> 44 45 #include <ctype.h> 46 #include <err.h> 47 #include <errno.h> 48 #include <fcntl.h> 49 #include <libgen.h> 50 #include <regex.h> 51 #include <signal.h> 52 #include <stdarg.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <syslog.h> 57 #include <unistd.h> 58 #include <pthread.h> 59 60 #include <libprop/proplib.h> 61 #include <sys/udev.h> 62 #include "udevd.h" 63 64 #define MONITOR_LOCK() pthread_mutex_lock(&monitor_lock) 65 #define MONITOR_UNLOCK() pthread_mutex_unlock(&monitor_lock) 66 67 static int _parse_filter_prop(struct udev_monitor *udm, prop_array_t pa); 68 static int match_filter(struct event_filter *evf, prop_dictionary_t dict); 69 70 static int WildCaseCmp(const char *w, const char *s); 71 static int wildCaseCmp(const char **mary, int d, const char *w, const char *s); 72 73 TAILQ_HEAD(udev_monitor_list_head, udev_monitor) udev_monitor_list; 74 pthread_mutex_t monitor_lock; 75 76 77 void 78 monitor_queue_event(prop_dictionary_t ev_dict) 79 { 80 struct udev_monitor *udm; 81 struct udev_monitor_event *udm_ev; 82 83 MONITOR_LOCK(); 84 85 TAILQ_FOREACH(udm, &udev_monitor_list, link) { 86 udm_ev = malloc(sizeof(struct udev_monitor_event)); 87 if (udm_ev == NULL) 88 continue; 89 90 prop_object_retain(ev_dict); 91 udm_ev->ev_dict = ev_dict; 92 93 if (match_event_filter(udm, 94 prop_dictionary_get(udm_ev->ev_dict, "evdict")) == 0) { 95 prop_object_release(ev_dict); 96 free(udm_ev); 97 continue; 98 } 99 100 pthread_mutex_lock(&udm->q_lock); 101 TAILQ_INSERT_TAIL(&udm->ev_queue, udm_ev, link); 102 pthread_cond_signal(&udm->cond); 103 pthread_mutex_unlock(&udm->q_lock); 104 } 105 106 MONITOR_UNLOCK(); 107 } 108 109 struct udev_monitor * 110 udev_monitor_init(struct client_info *cli, prop_array_t filters) 111 { 112 struct udev_monitor *udm; 113 int error; 114 115 udm = malloc(sizeof(struct udev_monitor)); 116 if (udm == NULL) 117 return NULL; 118 119 TAILQ_INIT(&udm->ev_queue); 120 TAILQ_INIT(&udm->ev_filt); 121 122 pthread_mutex_init(&udm->q_lock, NULL); 123 pthread_cond_init(&udm->cond, NULL); 124 udm->cli = cli; 125 126 if (filters != NULL) { 127 error = _parse_filter_prop(udm, filters); 128 /* XXX: ignore error for now */ 129 } 130 131 return udm; 132 } 133 134 void 135 udev_monitor_free(struct udev_monitor *udm) 136 { 137 struct event_filter *evf; 138 struct udev_monitor_event *udm_ev; 139 140 pthread_mutex_lock(&udm->q_lock); 141 142 while ((udm_ev = TAILQ_FIRST(&udm->ev_queue)) != NULL) { 143 prop_object_release(udm_ev->ev_dict); 144 udm_ev->ev_dict = NULL; 145 TAILQ_REMOVE(&udm->ev_queue, udm_ev, link); 146 free(udm_ev); 147 } 148 149 while ((evf = TAILQ_FIRST(&udm->ev_filt)) != NULL) { 150 TAILQ_REMOVE(&udm->ev_filt, evf, link); 151 free(evf); 152 } 153 154 pthread_mutex_unlock(&udm->q_lock); 155 free(udm); 156 } 157 158 int 159 client_cmd_monitor(struct client_info *cli, prop_dictionary_t dict) 160 { 161 prop_array_t pa; 162 prop_object_t po; 163 struct udev_monitor *udm; 164 struct udev_monitor_event *udm_ev; 165 char *xml; 166 ssize_t r; 167 int ok = 1; 168 169 pa = NULL; 170 po = prop_dictionary_get(dict, "filters"); 171 if ((po != NULL) && prop_object_type(po) == PROP_TYPE_ARRAY) { 172 pa = po; 173 } 174 175 udm = udev_monitor_init(cli, pa); 176 if (udm == NULL) 177 return 1; 178 179 MONITOR_LOCK(); 180 TAILQ_INSERT_TAIL(&udev_monitor_list, udm, link); 181 MONITOR_UNLOCK(); 182 183 pthread_mutex_lock(&udm->q_lock); 184 while (ok) { 185 pthread_cond_wait(&udm->cond, &udm->q_lock); 186 187 udm_ev = TAILQ_FIRST(&udm->ev_queue); 188 if (udm_ev == NULL) 189 continue; 190 191 assert(udm_ev->ev_dict != NULL); 192 xml = prop_dictionary_externalize(udm_ev->ev_dict); 193 if (xml == NULL) 194 continue; 195 196 prop_object_release(udm_ev->ev_dict); 197 udm_ev->ev_dict = NULL; 198 TAILQ_REMOVE(&udm->ev_queue, udm_ev, link); 199 free(udm_ev); 200 201 r = send_xml(cli->fd, xml); 202 if (r <= 0) 203 goto end; 204 205 free(xml); 206 continue; 207 end: 208 pthread_mutex_unlock(&udm->q_lock); 209 close(cli->fd); 210 ok = 0; 211 free(xml); 212 } 213 214 MONITOR_LOCK(); 215 TAILQ_REMOVE(&udev_monitor_list, udm, link); 216 MONITOR_UNLOCK(); 217 218 udev_monitor_free(udm); 219 220 return 1; 221 } 222 223 static int 224 _parse_filter_prop(struct udev_monitor *udm, prop_array_t pa) 225 { 226 prop_string_t ps; 227 prop_number_t pn; 228 prop_object_iterator_t iter; 229 prop_dictionary_t dict; 230 struct event_filter *evf; 231 int error; 232 233 iter = prop_array_iterator(pa); 234 if (iter == NULL) 235 return -1; 236 237 while ((dict = prop_object_iterator_next(iter)) != NULL) { 238 evf = malloc(sizeof(struct event_filter)); 239 if (evf == NULL) 240 goto error_alloc; 241 242 ps = prop_dictionary_get(dict, "key"); 243 if (ps == NULL) 244 goto error_out; 245 evf->key = prop_string_cstring(ps); 246 if (evf->key == NULL) 247 goto error_out; 248 249 pn = prop_dictionary_get(dict, "type"); 250 if (pn == NULL) 251 goto error_out_ps; 252 253 ps = prop_dictionary_get(dict, "expr"); 254 if (ps == NULL) 255 goto error_out_ps; 256 257 if (prop_dictionary_get(dict, "negative")) 258 evf->neg = 1; 259 else 260 evf->neg = 0; 261 262 evf->type = prop_number_integer_value(pn); 263 switch (evf->type) { 264 case EVENT_FILTER_TYPE_WILDCARD: 265 evf->wildcard_match = prop_string_cstring(ps); 266 if (evf->wildcard_match == NULL) 267 goto error_out_ps; 268 break; 269 270 case EVENT_FILTER_TYPE_REGEX: 271 error = regcomp(&evf->regex_match, prop_string_cstring_nocopy(ps), REG_ICASE | REG_NOSUB); 272 if (error) 273 goto error_out_ps; 274 break; 275 276 default: 277 goto error_out_ps; 278 } 279 280 pthread_mutex_lock(&udm->q_lock); 281 TAILQ_INSERT_TAIL(&udm->ev_filt, evf, link); 282 pthread_mutex_unlock(&udm->q_lock); 283 284 } 285 286 prop_object_iterator_release(iter); 287 return 0; 288 289 error_out_ps: 290 free(evf->key); 291 error_out: 292 free(evf); 293 error_alloc: 294 prop_object_iterator_release(iter); 295 return -1; 296 } 297 298 /* 299 Event filter format: 300 <array> 301 <dictionary> 302 <key>key</key> 303 <value>(e.g. kptr, devnum, ...)</value> 304 <key>type</key> 305 <value>(e.g. wildcard or regex)</value> 306 <key>expr</key> 307 <value>(regex)</value> 308 </dictionary> 309 ... repeat ... 310 </array> 311 */ 312 313 static int 314 match_filter(struct event_filter *evf, prop_dictionary_t ev_dict) 315 { 316 prop_object_t po; 317 prop_string_t ps; 318 prop_number_t pn; 319 char *str; 320 char buf[128]; 321 int ret; 322 323 if (ev_dict == NULL) 324 return 0; 325 326 prop_object_retain(ev_dict); 327 328 assert(prop_dictionary_externalize(ev_dict) != NULL); 329 if ((po = prop_dictionary_get(ev_dict, evf->key)) == NULL) 330 goto no_match; 331 332 if (prop_object_type(po) == PROP_TYPE_STRING) { 333 ps = po; 334 str = __DECONST(char *, prop_string_cstring_nocopy(ps)); 335 } else if (prop_object_type(po) == PROP_TYPE_NUMBER) { 336 pn = po; 337 if (prop_number_unsigned(pn)) { 338 snprintf(buf, sizeof(buf), "%" PRIu64, prop_number_unsigned_integer_value(pn)); 339 } else { 340 snprintf(buf, sizeof(buf), "%" PRIi64, prop_number_integer_value(pn)); 341 } 342 str = buf; 343 } else { 344 syslog(LOG_DEBUG, "Unexpected type in match_filter: %d\n", prop_object_type(po)); 345 /* Unexpected type */ 346 goto no_match; 347 } 348 349 switch (evf->type) { 350 case EVENT_FILTER_TYPE_WILDCARD: 351 ret = WildCaseCmp(evf->wildcard_match, str); 352 353 if (ret != 0) 354 goto no_match; 355 356 break; 357 case EVENT_FILTER_TYPE_REGEX: 358 ret = regexec(&evf->regex_match, str, 0, NULL, 0); 359 360 if (ret != 0) 361 goto no_match; 362 break; 363 default: 364 goto no_match; 365 } 366 367 prop_object_release(ev_dict); 368 return 1; 369 370 no_match: 371 prop_object_release(ev_dict); 372 return 0; 373 } 374 375 int 376 match_event_filter(struct udev_monitor *udm, prop_dictionary_t ev_dict) 377 { 378 struct event_filter *evf; 379 int all_negative = 1; 380 381 pthread_mutex_lock(&udm->q_lock); 382 383 if (TAILQ_EMPTY(&udm->ev_filt)) 384 return 1; 385 386 TAILQ_FOREACH(evf, &udm->ev_filt, link) { 387 //printf("match_event_filter 3\n"); 388 if (evf->neg == 0) 389 all_negative = 0; 390 391 if (match_filter(evf, ev_dict)) { 392 pthread_mutex_unlock(&udm->q_lock); 393 return (1 ^ evf->neg); /* return 1; or 0 for 'nomatch' hit */ 394 } 395 //printf("match_event_filter 5\n"); 396 } 397 398 pthread_mutex_unlock(&udm->q_lock); 399 return (all_negative == 1)?1:0; 400 } 401 402 static int 403 WildCaseCmp(const char *w, const char *s) 404 { 405 int i; 406 int c; 407 int slen = strlen(s); 408 const char **mary; 409 410 for (i = c = 0; w[i]; ++i) { 411 if (w[i] == '*') 412 ++c; 413 } 414 mary = malloc(sizeof(char *) * (c + 1)); 415 if (mary == NULL) 416 return -1; 417 418 for (i = 0; i < c; ++i) 419 mary[i] = s + slen; 420 i = wildCaseCmp(mary, 0, w, s); 421 free(mary); 422 return(i); 423 } 424 425 /* 426 * WildCaseCmp() - compare wild string to sane string, case insensitive 427 * 428 * Returns 0 on success, -1 on failure. 429 */ 430 static int 431 wildCaseCmp(const char **mary, int d, const char *w, const char *s) 432 { 433 int i; 434 435 /* 436 * skip fixed portion 437 */ 438 for (;;) { 439 switch(*w) { 440 case '*': 441 /* 442 * optimize terminator 443 */ 444 if (w[1] == 0) 445 return(0); 446 if (w[1] != '?' && w[1] != '*') { 447 /* 448 * optimize * followed by non-wild 449 */ 450 for (i = 0; s + i < mary[d]; ++i) { 451 if (s[i] == w[1] && wildCaseCmp(mary, d + 1, w + 1, s + i) == 0) 452 return(0); 453 } 454 } else { 455 /* 456 * less-optimal 457 */ 458 for (i = 0; s + i < mary[d]; ++i) { 459 if (wildCaseCmp(mary, d + 1, w + 1, s + i) == 0) 460 return(0); 461 } 462 } 463 mary[d] = s; 464 return(-1); 465 case '?': 466 if (*s == 0) 467 return(-1); 468 ++w; 469 ++s; 470 break; 471 default: 472 if (*w != *s) { 473 if (tolower(*w) != tolower(*s)) 474 return(-1); 475 } 476 if (*w == 0) /* terminator */ 477 return(0); 478 ++w; 479 ++s; 480 break; 481 } 482 } 483 /* not reached */ 484 return(-1); 485 } 486