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->key); 152 if (evf->type == EVENT_FILTER_TYPE_WILDCARD) 153 free(evf->wildcard_match); 154 else if (evf->type == EVENT_FILTER_TYPE_REGEX) 155 regfree(&evf->regex_match); 156 free(evf); 157 } 158 159 pthread_mutex_unlock(&udm->q_lock); 160 free(udm); 161 } 162 163 int 164 client_cmd_monitor(struct client_info *cli, prop_dictionary_t dict) 165 { 166 prop_array_t pa; 167 prop_object_t po; 168 struct udev_monitor *udm; 169 struct udev_monitor_event *udm_ev; 170 struct timespec abstime; 171 struct pollfd fds[1]; 172 char *xml; 173 ssize_t r; 174 int ret, ok, dummy; 175 176 pa = NULL; 177 po = prop_dictionary_get(dict, "filters"); 178 if ((po != NULL) && prop_object_type(po) == PROP_TYPE_ARRAY) { 179 pa = po; 180 } 181 182 udm = udev_monitor_init(cli, pa); 183 if (udm == NULL) 184 return 1; 185 186 ok = 1; 187 fds[0].fd = cli->fd; 188 fds[0].events = POLLRDNORM; 189 190 MONITOR_LOCK(); 191 TAILQ_INSERT_TAIL(&udev_monitor_list, udm, link); 192 MONITOR_UNLOCK(); 193 194 pthread_mutex_lock(&udm->q_lock); 195 while (ok) { 196 clock_gettime(CLOCK_REALTIME,&abstime); 197 abstime.tv_sec += 2; 198 ret = pthread_cond_timedwait(&udm->cond, &udm->q_lock, &abstime); 199 200 if (ret == EINVAL) { 201 syslog(LOG_ERR, "pthread_cond_timedwait error: EINVAL"); 202 goto end_nofree; 203 } 204 205 if ((ret = poll(fds, 1, 0)) > 0) { 206 ret = recv(fds[0].fd, &dummy, sizeof(dummy), MSG_DONTWAIT); 207 if ((ret == 0) || ((ret < 0) && (errno != EAGAIN))) 208 goto end_nofree; 209 } 210 211 udm_ev = TAILQ_FIRST(&udm->ev_queue); 212 if (udm_ev == NULL) 213 continue; 214 215 assert(udm_ev->ev_dict != NULL); 216 xml = prop_dictionary_externalize(udm_ev->ev_dict); 217 if (xml == NULL) 218 continue; 219 220 prop_object_release(udm_ev->ev_dict); 221 udm_ev->ev_dict = NULL; 222 TAILQ_REMOVE(&udm->ev_queue, udm_ev, link); 223 free(udm_ev); 224 225 r = send_xml(cli->fd, xml); 226 if (r <= 0) 227 goto end; 228 229 free(xml); 230 continue; 231 end: 232 free(xml); 233 end_nofree: 234 pthread_mutex_unlock(&udm->q_lock); 235 close(cli->fd); 236 ok = 0; 237 238 } 239 240 MONITOR_LOCK(); 241 TAILQ_REMOVE(&udev_monitor_list, udm, link); 242 MONITOR_UNLOCK(); 243 244 udev_monitor_free(udm); 245 246 return 1; 247 } 248 249 static int 250 _parse_filter_prop(struct udev_monitor *udm, prop_array_t pa) 251 { 252 prop_string_t ps; 253 prop_number_t pn; 254 prop_object_iterator_t iter; 255 prop_dictionary_t dict; 256 struct event_filter *evf; 257 int error; 258 259 iter = prop_array_iterator(pa); 260 if (iter == NULL) 261 return -1; 262 263 while ((dict = prop_object_iterator_next(iter)) != NULL) { 264 evf = malloc(sizeof(struct event_filter)); 265 bzero(evf, sizeof(struct event_filter)); 266 if (evf == NULL) 267 goto error_alloc; 268 269 ps = prop_dictionary_get(dict, "key"); 270 if (ps == NULL) 271 goto error_out; 272 evf->key = prop_string_cstring(ps); 273 if (evf->key == NULL) 274 goto error_out; 275 276 pn = prop_dictionary_get(dict, "type"); 277 if (pn == NULL) 278 goto error_out_ps; 279 280 ps = prop_dictionary_get(dict, "expr"); 281 if (ps == NULL) 282 goto error_out_ps; 283 284 if (prop_dictionary_get(dict, "negative")) 285 evf->neg = 1; 286 else 287 evf->neg = 0; 288 289 evf->type = prop_number_integer_value(pn); 290 switch (evf->type) { 291 case EVENT_FILTER_TYPE_WILDCARD: 292 evf->wildcard_match = prop_string_cstring(ps); 293 if (evf->wildcard_match == NULL) 294 goto error_out_ps; 295 break; 296 297 case EVENT_FILTER_TYPE_REGEX: 298 error = regcomp(&evf->regex_match, prop_string_cstring_nocopy(ps), REG_ICASE | REG_NOSUB); 299 if (error) 300 goto error_out_ps; 301 break; 302 303 default: 304 goto error_out_ps; 305 } 306 307 pthread_mutex_lock(&udm->q_lock); 308 TAILQ_INSERT_TAIL(&udm->ev_filt, evf, link); 309 pthread_mutex_unlock(&udm->q_lock); 310 311 } 312 313 prop_object_iterator_release(iter); 314 return 0; 315 316 error_out_ps: 317 free(evf->key); 318 error_out: 319 free(evf); 320 error_alloc: 321 prop_object_iterator_release(iter); 322 return -1; 323 } 324 325 /* 326 Event filter format: 327 <array> 328 <dictionary> 329 <key>key</key> 330 <value>(e.g. kptr, devnum, ...)</value> 331 <key>type</key> 332 <value>(e.g. wildcard or regex)</value> 333 <key>expr</key> 334 <value>(regex)</value> 335 </dictionary> 336 ... repeat ... 337 </array> 338 */ 339 340 static int 341 match_filter(struct event_filter *evf, prop_dictionary_t ev_dict) 342 { 343 prop_object_t po; 344 prop_string_t ps; 345 prop_number_t pn; 346 char *str; 347 char buf[128]; 348 int ret; 349 350 if (ev_dict == NULL) 351 return 0; 352 353 prop_object_retain(ev_dict); 354 355 if ((po = prop_dictionary_get(ev_dict, evf->key)) == NULL) 356 goto no_match; 357 358 if (prop_object_type(po) == PROP_TYPE_STRING) { 359 ps = po; 360 str = __DECONST(char *, prop_string_cstring_nocopy(ps)); 361 } else if (prop_object_type(po) == PROP_TYPE_NUMBER) { 362 pn = po; 363 if (prop_number_unsigned(pn)) { 364 snprintf(buf, sizeof(buf), "%" PRIu64, prop_number_unsigned_integer_value(pn)); 365 } else { 366 snprintf(buf, sizeof(buf), "%" PRIi64, prop_number_integer_value(pn)); 367 } 368 str = buf; 369 } else { 370 syslog(LOG_DEBUG, "Unexpected type in match_filter: %d\n", prop_object_type(po)); 371 /* Unexpected type */ 372 goto no_match; 373 } 374 375 switch (evf->type) { 376 case EVENT_FILTER_TYPE_WILDCARD: 377 ret = WildCaseCmp(evf->wildcard_match, str); 378 379 if (ret != 0) 380 goto no_match; 381 382 break; 383 case EVENT_FILTER_TYPE_REGEX: 384 ret = regexec(&evf->regex_match, str, 0, NULL, 0); 385 386 if (ret != 0) 387 goto no_match; 388 break; 389 default: 390 goto no_match; 391 } 392 393 prop_object_release(ev_dict); 394 return 1; 395 396 no_match: 397 prop_object_release(ev_dict); 398 return 0; 399 } 400 401 int 402 match_event_filter(struct udev_monitor *udm, prop_dictionary_t ev_dict) 403 { 404 struct event_filter *evf; 405 406 pthread_mutex_lock(&udm->q_lock); 407 408 if (TAILQ_EMPTY(&udm->ev_filt)) 409 return 1; 410 411 TAILQ_FOREACH(evf, &udm->ev_filt, link) { 412 if ((evf->neg == 0 && !match_filter(evf, ev_dict)) || 413 (evf->neg == 1 && match_filter(evf, ev_dict))) { 414 pthread_mutex_unlock(&udm->q_lock); 415 return 0; 416 } 417 } 418 419 pthread_mutex_unlock(&udm->q_lock); 420 return 1; 421 } 422 423 static int 424 WildCaseCmp(const char *w, const char *s) 425 { 426 int i; 427 int c; 428 int slen = strlen(s); 429 const char **mary; 430 431 for (i = c = 0; w[i]; ++i) { 432 if (w[i] == '*') 433 ++c; 434 } 435 mary = malloc(sizeof(char *) * (c + 1)); 436 if (mary == NULL) 437 return -1; 438 439 for (i = 0; i < c; ++i) 440 mary[i] = s + slen; 441 i = wildCaseCmp(mary, 0, w, s); 442 free(mary); 443 return(i); 444 } 445 446 /* 447 * WildCaseCmp() - compare wild string to sane string, case insensitive 448 * 449 * Returns 0 on success, -1 on failure. 450 */ 451 static int 452 wildCaseCmp(const char **mary, int d, const char *w, const char *s) 453 { 454 int i; 455 456 /* 457 * skip fixed portion 458 */ 459 for (;;) { 460 switch(*w) { 461 case '*': 462 /* 463 * optimize terminator 464 */ 465 if (w[1] == 0) 466 return(0); 467 if (w[1] != '?' && w[1] != '*') { 468 /* 469 * optimize * followed by non-wild 470 */ 471 for (i = 0; s + i < mary[d]; ++i) { 472 if (s[i] == w[1] && wildCaseCmp(mary, d + 1, w + 1, s + i) == 0) 473 return(0); 474 } 475 } else { 476 /* 477 * less-optimal 478 */ 479 for (i = 0; s + i < mary[d]; ++i) { 480 if (wildCaseCmp(mary, d + 1, w + 1, s + i) == 0) 481 return(0); 482 } 483 } 484 mary[d] = s; 485 return(-1); 486 case '?': 487 if (*s == 0) 488 return(-1); 489 ++w; 490 ++s; 491 break; 492 default: 493 if (*w != *s) { 494 if (tolower(*w) != tolower(*s)) 495 return(-1); 496 } 497 if (*w == 0) /* terminator */ 498 return(0); 499 ++w; 500 ++s; 501 break; 502 } 503 } 504 /* not reached */ 505 return(-1); 506 } 507