2 * Copyright (c) 2010 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Alex Hornung <ahornung@gmail.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
34 #include <sys/types.h>
35 #include <sys/device.h>
37 #include <sys/socket.h>
38 #include <sys/ioctl.h>
40 #include <sys/queue.h>
42 #include <cpu/inttypes.h>
60 #include <libprop/proplib.h>
64 #define MONITOR_LOCK() pthread_mutex_lock(&monitor_lock)
65 #define MONITOR_UNLOCK() pthread_mutex_unlock(&monitor_lock)
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
);
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
);
73 TAILQ_HEAD(udev_monitor_list_head
, udev_monitor
) udev_monitor_list
;
74 pthread_mutex_t monitor_lock
;
78 monitor_queue_event(prop_dictionary_t ev_dict
)
80 struct udev_monitor
*udm
;
81 struct udev_monitor_event
*udm_ev
;
85 TAILQ_FOREACH(udm
, &udev_monitor_list
, link
) {
86 udm_ev
= malloc(sizeof(struct udev_monitor_event
));
90 prop_object_retain(ev_dict
);
91 udm_ev
->ev_dict
= ev_dict
;
93 if (match_event_filter(udm
,
94 prop_dictionary_get(udm_ev
->ev_dict
, "evdict")) == 0) {
95 prop_object_release(ev_dict
);
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
);
109 struct udev_monitor
*
110 udev_monitor_init(struct client_info
*cli
, prop_array_t filters
)
112 struct udev_monitor
*udm
;
114 udm
= malloc(sizeof(struct udev_monitor
));
118 TAILQ_INIT(&udm
->ev_queue
);
119 TAILQ_INIT(&udm
->ev_filt
);
121 pthread_mutex_init(&udm
->q_lock
, NULL
);
122 pthread_cond_init(&udm
->cond
, NULL
);
125 if (filters
!= NULL
) {
126 __unused
int error
= _parse_filter_prop(udm
, filters
);
127 /* XXX: ignore error for now */
134 udev_monitor_free(struct udev_monitor
*udm
)
136 struct event_filter
*evf
;
137 struct udev_monitor_event
*udm_ev
;
139 pthread_mutex_lock(&udm
->q_lock
);
141 while ((udm_ev
= TAILQ_FIRST(&udm
->ev_queue
)) != NULL
) {
142 prop_object_release(udm_ev
->ev_dict
);
143 udm_ev
->ev_dict
= NULL
;
144 TAILQ_REMOVE(&udm
->ev_queue
, udm_ev
, link
);
148 while ((evf
= TAILQ_FIRST(&udm
->ev_filt
)) != NULL
) {
149 TAILQ_REMOVE(&udm
->ev_filt
, evf
, link
);
151 if (evf
->type
== EVENT_FILTER_TYPE_WILDCARD
)
152 free(evf
->wildcard_match
);
153 else if (evf
->type
== EVENT_FILTER_TYPE_REGEX
)
154 regfree(&evf
->regex_match
);
158 pthread_mutex_unlock(&udm
->q_lock
);
163 client_cmd_monitor(struct client_info
*cli
, prop_dictionary_t dict
)
167 struct udev_monitor
*udm
;
168 struct udev_monitor_event
*udm_ev
;
169 struct timespec abstime
;
170 struct pollfd fds
[1];
176 po
= prop_dictionary_get(dict
, "filters");
177 if ((po
!= NULL
) && prop_object_type(po
) == PROP_TYPE_ARRAY
) {
181 udm
= udev_monitor_init(cli
, pa
);
187 fds
[0].events
= POLLRDNORM
;
190 TAILQ_INSERT_TAIL(&udev_monitor_list
, udm
, link
);
193 pthread_mutex_lock(&udm
->q_lock
);
195 clock_gettime(CLOCK_REALTIME
,&abstime
);
197 ret
= pthread_cond_timedwait(&udm
->cond
, &udm
->q_lock
, &abstime
);
200 syslog(LOG_ERR
, "pthread_cond_timedwait error: EINVAL");
204 if ((ret
= poll(fds
, 1, 0)) > 0) {
205 ret
= recv(fds
[0].fd
, &dummy
, sizeof(dummy
), MSG_DONTWAIT
);
206 if ((ret
== 0) || ((ret
< 0) && (errno
!= EAGAIN
)))
210 udm_ev
= TAILQ_FIRST(&udm
->ev_queue
);
214 assert(udm_ev
->ev_dict
!= NULL
);
215 xml
= prop_dictionary_externalize(udm_ev
->ev_dict
);
219 prop_object_release(udm_ev
->ev_dict
);
220 udm_ev
->ev_dict
= NULL
;
221 TAILQ_REMOVE(&udm
->ev_queue
, udm_ev
, link
);
224 r
= send_xml(cli
->fd
, xml
);
233 pthread_mutex_unlock(&udm
->q_lock
);
240 TAILQ_REMOVE(&udev_monitor_list
, udm
, link
);
243 udev_monitor_free(udm
);
249 _parse_filter_prop(struct udev_monitor
*udm
, prop_array_t pa
)
253 prop_object_iterator_t iter
;
254 prop_dictionary_t dict
;
255 struct event_filter
*evf
;
258 iter
= prop_array_iterator(pa
);
262 while ((dict
= prop_object_iterator_next(iter
)) != NULL
) {
263 evf
= malloc(sizeof(struct event_filter
));
264 bzero(evf
, sizeof(struct event_filter
));
268 ps
= prop_dictionary_get(dict
, "key");
271 evf
->key
= prop_string_cstring(ps
);
272 if (evf
->key
== NULL
)
275 pn
= prop_dictionary_get(dict
, "type");
279 ps
= prop_dictionary_get(dict
, "expr");
283 if (prop_dictionary_get(dict
, "negative"))
288 evf
->type
= prop_number_integer_value(pn
);
290 case EVENT_FILTER_TYPE_WILDCARD
:
291 evf
->wildcard_match
= prop_string_cstring(ps
);
292 if (evf
->wildcard_match
== NULL
)
296 case EVENT_FILTER_TYPE_REGEX
:
297 error
= regcomp(&evf
->regex_match
, prop_string_cstring_nocopy(ps
), REG_ICASE
| REG_NOSUB
);
306 pthread_mutex_lock(&udm
->q_lock
);
307 TAILQ_INSERT_TAIL(&udm
->ev_filt
, evf
, link
);
308 pthread_mutex_unlock(&udm
->q_lock
);
312 prop_object_iterator_release(iter
);
320 prop_object_iterator_release(iter
);
329 <value>(e.g. kptr, devnum, ...)</value>
331 <value>(e.g. wildcard or regex)</value>
333 <value>(regex)</value>
340 match_filter(struct event_filter
*evf
, prop_dictionary_t ev_dict
)
352 prop_object_retain(ev_dict
);
354 if ((po
= prop_dictionary_get(ev_dict
, evf
->key
)) == NULL
)
357 if (prop_object_type(po
) == PROP_TYPE_STRING
) {
359 str
= __DECONST(char *, prop_string_cstring_nocopy(ps
));
360 } else if (prop_object_type(po
) == PROP_TYPE_NUMBER
) {
362 if (prop_number_unsigned(pn
)) {
363 snprintf(buf
, sizeof(buf
), "%" PRIu64
, prop_number_unsigned_integer_value(pn
));
365 snprintf(buf
, sizeof(buf
), "%" PRIi64
, prop_number_integer_value(pn
));
369 syslog(LOG_DEBUG
, "Unexpected type in match_filter: %d\n", prop_object_type(po
));
370 /* Unexpected type */
375 case EVENT_FILTER_TYPE_WILDCARD
:
376 ret
= WildCaseCmp(evf
->wildcard_match
, str
);
382 case EVENT_FILTER_TYPE_REGEX
:
383 ret
= regexec(&evf
->regex_match
, str
, 0, NULL
, 0);
392 prop_object_release(ev_dict
);
396 prop_object_release(ev_dict
);
401 match_event_filter(struct udev_monitor
*udm
, prop_dictionary_t ev_dict
)
403 struct event_filter
*evf
;
405 pthread_mutex_lock(&udm
->q_lock
);
407 if (TAILQ_EMPTY(&udm
->ev_filt
))
410 TAILQ_FOREACH(evf
, &udm
->ev_filt
, link
) {
411 if ((evf
->neg
== 0 && !match_filter(evf
, ev_dict
)) ||
412 (evf
->neg
== 1 && match_filter(evf
, ev_dict
))) {
413 pthread_mutex_unlock(&udm
->q_lock
);
418 pthread_mutex_unlock(&udm
->q_lock
);
423 WildCaseCmp(const char *w
, const char *s
)
427 int slen
= strlen(s
);
430 for (i
= c
= 0; w
[i
]; ++i
) {
434 mary
= malloc(sizeof(char *) * (c
+ 1));
438 for (i
= 0; i
< c
; ++i
)
440 i
= wildCaseCmp(mary
, 0, w
, s
);
446 * WildCaseCmp() - compare wild string to sane string, case insensitive
448 * Returns 0 on success, -1 on failure.
451 wildCaseCmp(const char **mary
, int d
, const char *w
, const char *s
)
462 * optimize terminator
466 if (w
[1] != '?' && w
[1] != '*') {
468 * optimize * followed by non-wild
470 for (i
= 0; s
+ i
< mary
[d
]; ++i
) {
471 if (s
[i
] == w
[1] && wildCaseCmp(mary
, d
+ 1, w
+ 1, s
+ i
) == 0)
478 for (i
= 0; s
+ i
< mary
[d
]; ++i
) {
479 if (wildCaseCmp(mary
, d
+ 1, w
+ 1, s
+ i
) == 0)
493 if (tolower(*w
) != tolower(*s
))
496 if (*w
== 0) /* terminator */