4dfcd4e495659da1bf7b3bd1a627ff96a357dd3a
[dragonfly.git] / usr.sbin / udevd / udevd_monitor.c
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);
152         }
153
154         pthread_mutex_unlock(&udm->q_lock);
155         free(udm);
156 }
157
158 int
159 client_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;
207 end:
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
223 static 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
289 error_out_ps:
290         free(evf->key);
291 error_out:
292         free(evf);
293 error_alloc:
294         prop_object_iterator_release(iter);
295         return -1;
296 }
297
298 /*
299 Event 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
313 static int
314 match_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
370 no_match:
371         prop_object_release(ev_dict);
372         return 0;
373 }
374
375 int
376 match_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
402 static int
403 WildCaseCmp(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  */
430 static int
431 wildCaseCmp(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 }