xref: /dragonfly/sbin/udevd/udevd_monitor.c (revision 8a0bcd56)
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 	int all_negative = 1;
406 
407 	pthread_mutex_lock(&udm->q_lock);
408 
409 	if (TAILQ_EMPTY(&udm->ev_filt))
410 		return 1;
411 
412 	TAILQ_FOREACH(evf, &udm->ev_filt, link) {
413 		//printf("match_event_filter 3\n");
414 		if (evf->neg == 0)
415 			all_negative = 0;
416 
417 		if (match_filter(evf, ev_dict)) {
418 			pthread_mutex_unlock(&udm->q_lock);
419 			return (1 ^ evf->neg); /* return 1; or 0 for 'nomatch' hit */
420 		}
421 		//printf("match_event_filter 5\n");
422 	}
423 
424 	pthread_mutex_unlock(&udm->q_lock);
425 	return (all_negative == 1)?1:0;
426 }
427 
428 static int
429 WildCaseCmp(const char *w, const char *s)
430 {
431     int i;
432     int c;
433     int slen = strlen(s);
434     const char **mary;
435 
436     for (i = c = 0; w[i]; ++i) {
437 	if (w[i] == '*')
438 	    ++c;
439     }
440     mary = malloc(sizeof(char *) * (c + 1));
441     if (mary == NULL)
442 	     return -1;
443 
444     for (i = 0; i < c; ++i)
445 	mary[i] = s + slen;
446     i = wildCaseCmp(mary, 0, w, s);
447     free(mary);
448     return(i);
449 }
450 
451 /*
452  * WildCaseCmp() - compare wild string to sane string, case insensitive
453  *
454  *	Returns 0 on success, -1 on failure.
455  */
456 static int
457 wildCaseCmp(const char **mary, int d, const char *w, const char *s)
458 {
459     int i;
460 
461     /*
462      * skip fixed portion
463      */
464     for (;;) {
465 	switch(*w) {
466 	case '*':
467 	    /*
468 	     * optimize terminator
469 	     */
470 	    if (w[1] == 0)
471 		return(0);
472 	    if (w[1] != '?' && w[1] != '*') {
473 		/*
474 		 * optimize * followed by non-wild
475 		 */
476 		for (i = 0; s + i < mary[d]; ++i) {
477 		    if (s[i] == w[1] && wildCaseCmp(mary, d + 1, w + 1, s + i) == 0)
478 			return(0);
479 		}
480 	    } else {
481 		/*
482 		 * less-optimal
483 		 */
484 		for (i = 0; s + i < mary[d]; ++i) {
485 		    if (wildCaseCmp(mary, d + 1, w + 1, s + i) == 0)
486 			return(0);
487 		}
488 	    }
489 	    mary[d] = s;
490 	    return(-1);
491 	case '?':
492 	    if (*s == 0)
493 		return(-1);
494 	    ++w;
495 	    ++s;
496 	    break;
497 	default:
498 	    if (*w != *s) {
499 		if (tolower(*w) != tolower(*s))
500 		    return(-1);
501 	    }
502 	    if (*w == 0)	/* terminator */
503 		return(0);
504 	    ++w;
505 	    ++s;
506 	    break;
507 	}
508     }
509     /* not reached */
510     return(-1);
511 }
512