udevd - bring into sbin, add rc script
[dragonfly.git] / sbin / udevd / udevd_monitor.c
CommitLineData
3a3826b3
AH
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
67static int _parse_filter_prop(struct udev_monitor *udm, prop_array_t pa);
68static int match_filter(struct event_filter *evf, prop_dictionary_t dict);
69
70static int WildCaseCmp(const char *w, const char *s);
71static int wildCaseCmp(const char **mary, int d, const char *w, const char *s);
72
73TAILQ_HEAD(udev_monitor_list_head, udev_monitor) udev_monitor_list;
74pthread_mutex_t monitor_lock;
75
76
77void
78monitor_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
109struct udev_monitor *
110udev_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
134void
135udev_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
158int
159client_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;
207end:
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
223static 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
289error_out_ps:
290 free(evf->key);
291error_out:
292 free(evf);
293error_alloc:
294 prop_object_iterator_release(iter);
295 return -1;
296}
297
298/*
299Event 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
313static int
314match_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
370no_match:
371 prop_object_release(ev_dict);
372 return 0;
373}
374
375int
376match_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
402static int
403WildCaseCmp(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 */
430static int
431wildCaseCmp(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}