<sys/vfscache.h>: Sync enum vtagtype with what we have.
[dragonfly.git] / sbin / udevd / udevd_monitor.c
blob286a668b768c26c6765da547eea02f92be73fa39
1 /*
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
9 * are met:
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.
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.
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>
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>
60 #include <libprop/proplib.h>
61 #include <sys/udev.h>
62 #include "udevd.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;
77 void
78 monitor_queue_event(prop_dictionary_t ev_dict)
80 struct udev_monitor *udm;
81 struct udev_monitor_event *udm_ev;
83 MONITOR_LOCK();
85 TAILQ_FOREACH(udm, &udev_monitor_list, link) {
86 udm_ev = malloc(sizeof(struct udev_monitor_event));
87 if (udm_ev == NULL)
88 continue;
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);
96 free(udm_ev);
97 continue;
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);
106 MONITOR_UNLOCK();
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));
115 if (udm == NULL)
116 return NULL;
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);
123 udm->cli = cli;
125 if (filters != NULL) {
126 __unused int error = _parse_filter_prop(udm, filters);
127 /* XXX: ignore error for now */
130 return udm;
133 void
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);
145 free(udm_ev);
148 while ((evf = TAILQ_FIRST(&udm->ev_filt)) != NULL) {
149 TAILQ_REMOVE(&udm->ev_filt, evf, link);
150 free(evf->key);
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);
155 free(evf);
158 pthread_mutex_unlock(&udm->q_lock);
159 free(udm);
163 client_cmd_monitor(struct client_info *cli, prop_dictionary_t dict)
165 prop_array_t pa;
166 prop_object_t po;
167 struct udev_monitor *udm;
168 struct udev_monitor_event *udm_ev;
169 struct timespec abstime;
170 struct pollfd fds[1];
171 char *xml;
172 ssize_t r;
173 int ret, ok, dummy;
175 pa = NULL;
176 po = prop_dictionary_get(dict, "filters");
177 if ((po != NULL) && prop_object_type(po) == PROP_TYPE_ARRAY) {
178 pa = po;
181 udm = udev_monitor_init(cli, pa);
182 if (udm == NULL)
183 return 1;
185 ok = 1;
186 fds[0].fd = cli->fd;
187 fds[0].events = POLLRDNORM;
189 MONITOR_LOCK();
190 TAILQ_INSERT_TAIL(&udev_monitor_list, udm, link);
191 MONITOR_UNLOCK();
193 pthread_mutex_lock(&udm->q_lock);
194 while (ok) {
195 clock_gettime(CLOCK_REALTIME,&abstime);
196 abstime.tv_sec += 2;
197 ret = pthread_cond_timedwait(&udm->cond, &udm->q_lock, &abstime);
199 if (ret == EINVAL) {
200 syslog(LOG_ERR, "pthread_cond_timedwait error: EINVAL");
201 goto end_nofree;
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)))
207 goto end_nofree;
210 udm_ev = TAILQ_FIRST(&udm->ev_queue);
211 if (udm_ev == NULL)
212 continue;
214 assert(udm_ev->ev_dict != NULL);
215 xml = prop_dictionary_externalize(udm_ev->ev_dict);
216 if (xml == NULL)
217 continue;
219 prop_object_release(udm_ev->ev_dict);
220 udm_ev->ev_dict = NULL;
221 TAILQ_REMOVE(&udm->ev_queue, udm_ev, link);
222 free(udm_ev);
224 r = send_xml(cli->fd, xml);
225 if (r <= 0)
226 goto end;
228 free(xml);
229 continue;
230 end:
231 free(xml);
232 end_nofree:
233 pthread_mutex_unlock(&udm->q_lock);
234 close(cli->fd);
235 ok = 0;
239 MONITOR_LOCK();
240 TAILQ_REMOVE(&udev_monitor_list, udm, link);
241 MONITOR_UNLOCK();
243 udev_monitor_free(udm);
245 return 1;
248 static int
249 _parse_filter_prop(struct udev_monitor *udm, prop_array_t pa)
251 prop_string_t ps;
252 prop_number_t pn;
253 prop_object_iterator_t iter;
254 prop_dictionary_t dict;
255 struct event_filter *evf;
256 int error;
258 iter = prop_array_iterator(pa);
259 if (iter == NULL)
260 return -1;
262 while ((dict = prop_object_iterator_next(iter)) != NULL) {
263 evf = malloc(sizeof(struct event_filter));
264 bzero(evf, sizeof(struct event_filter));
265 if (evf == NULL)
266 goto error_alloc;
268 ps = prop_dictionary_get(dict, "key");
269 if (ps == NULL)
270 goto error_out;
271 evf->key = prop_string_cstring(ps);
272 if (evf->key == NULL)
273 goto error_out;
275 pn = prop_dictionary_get(dict, "type");
276 if (pn == NULL)
277 goto error_out_ps;
279 ps = prop_dictionary_get(dict, "expr");
280 if (ps == NULL)
281 goto error_out_ps;
283 if (prop_dictionary_get(dict, "negative"))
284 evf->neg = 1;
285 else
286 evf->neg = 0;
288 evf->type = prop_number_integer_value(pn);
289 switch (evf->type) {
290 case EVENT_FILTER_TYPE_WILDCARD:
291 evf->wildcard_match = prop_string_cstring(ps);
292 if (evf->wildcard_match == NULL)
293 goto error_out_ps;
294 break;
296 case EVENT_FILTER_TYPE_REGEX:
297 error = regcomp(&evf->regex_match, prop_string_cstring_nocopy(ps), REG_ICASE | REG_NOSUB);
298 if (error)
299 goto error_out_ps;
300 break;
302 default:
303 goto error_out_ps;
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);
313 return 0;
315 error_out_ps:
316 free(evf->key);
317 error_out:
318 free(evf);
319 error_alloc:
320 prop_object_iterator_release(iter);
321 return -1;
325 Event filter format:
326 <array>
327 <dictionary>
328 <key>key</key>
329 <value>(e.g. kptr, devnum, ...)</value>
330 <key>type</key>
331 <value>(e.g. wildcard or regex)</value>
332 <key>expr</key>
333 <value>(regex)</value>
334 </dictionary>
335 ... repeat ...
336 </array>
339 static int
340 match_filter(struct event_filter *evf, prop_dictionary_t ev_dict)
342 prop_object_t po;
343 prop_string_t ps;
344 prop_number_t pn;
345 char *str;
346 char buf[128];
347 int ret;
349 if (ev_dict == NULL)
350 return 0;
352 prop_object_retain(ev_dict);
354 if ((po = prop_dictionary_get(ev_dict, evf->key)) == NULL)
355 goto no_match;
357 if (prop_object_type(po) == PROP_TYPE_STRING) {
358 ps = po;
359 str = __DECONST(char *, prop_string_cstring_nocopy(ps));
360 } else if (prop_object_type(po) == PROP_TYPE_NUMBER) {
361 pn = po;
362 if (prop_number_unsigned(pn)) {
363 snprintf(buf, sizeof(buf), "%" PRIu64, prop_number_unsigned_integer_value(pn));
364 } else {
365 snprintf(buf, sizeof(buf), "%" PRIi64, prop_number_integer_value(pn));
367 str = buf;
368 } else {
369 syslog(LOG_DEBUG, "Unexpected type in match_filter: %d\n", prop_object_type(po));
370 /* Unexpected type */
371 goto no_match;
374 switch (evf->type) {
375 case EVENT_FILTER_TYPE_WILDCARD:
376 ret = WildCaseCmp(evf->wildcard_match, str);
378 if (ret != 0)
379 goto no_match;
381 break;
382 case EVENT_FILTER_TYPE_REGEX:
383 ret = regexec(&evf->regex_match, str, 0, NULL, 0);
385 if (ret != 0)
386 goto no_match;
387 break;
388 default:
389 goto no_match;
392 prop_object_release(ev_dict);
393 return 1;
395 no_match:
396 prop_object_release(ev_dict);
397 return 0;
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))
408 return 1;
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);
414 return 0;
418 pthread_mutex_unlock(&udm->q_lock);
419 return 1;
422 static int
423 WildCaseCmp(const char *w, const char *s)
425 int i;
426 int c;
427 int slen = strlen(s);
428 const char **mary;
430 for (i = c = 0; w[i]; ++i) {
431 if (w[i] == '*')
432 ++c;
434 mary = malloc(sizeof(char *) * (c + 1));
435 if (mary == NULL)
436 return -1;
438 for (i = 0; i < c; ++i)
439 mary[i] = s + slen;
440 i = wildCaseCmp(mary, 0, w, s);
441 free(mary);
442 return(i);
446 * WildCaseCmp() - compare wild string to sane string, case insensitive
448 * Returns 0 on success, -1 on failure.
450 static int
451 wildCaseCmp(const char **mary, int d, const char *w, const char *s)
453 int i;
456 * skip fixed portion
458 for (;;) {
459 switch(*w) {
460 case '*':
462 * optimize terminator
464 if (w[1] == 0)
465 return(0);
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)
472 return(0);
474 } else {
476 * less-optimal
478 for (i = 0; s + i < mary[d]; ++i) {
479 if (wildCaseCmp(mary, d + 1, w + 1, s + i) == 0)
480 return(0);
483 mary[d] = s;
484 return(-1);
485 case '?':
486 if (*s == 0)
487 return(-1);
488 ++w;
489 ++s;
490 break;
491 default:
492 if (*w != *s) {
493 if (tolower(*w) != tolower(*s))
494 return(-1);
496 if (*w == 0) /* terminator */
497 return(0);
498 ++w;
499 ++s;
500 break;
503 /* not reached */
504 return(-1);