xref: /dragonfly/sbin/udevd/udevd_monitor.c (revision 0c8103dd622e5016512e2eedb2ceb865982089a5)
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 
43 #include <assert.h>
44 #include <ctype.h>
45 #include <err.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <inttypes.h>
49 #include <libgen.h>
50 #include <pthread.h>
51 #include <regex.h>
52 #include <signal.h>
53 #include <stdarg.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <syslog.h>
58 #include <unistd.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
monitor_queue_event(prop_dictionary_t ev_dict)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 *
udev_monitor_init(struct client_info * cli,prop_array_t filters)110 udev_monitor_init(struct client_info *cli, prop_array_t filters)
111 {
112           struct udev_monitor *udm;
113 
114           udm = malloc(sizeof(struct udev_monitor));
115           if (udm == NULL)
116                     return NULL;
117 
118           TAILQ_INIT(&udm->ev_queue);
119           TAILQ_INIT(&udm->ev_filt);
120 
121           pthread_mutex_init(&udm->q_lock, NULL);
122           pthread_cond_init(&udm->cond, NULL);
123           udm->cli = cli;
124 
125           if (filters != NULL) {
126                     __unused int error = _parse_filter_prop(udm, filters);
127                     /* XXX: ignore error for now */
128           }
129 
130           return udm;
131 }
132 
133 void
udev_monitor_free(struct udev_monitor * udm)134 udev_monitor_free(struct udev_monitor *udm)
135 {
136           struct event_filter *evf;
137           struct udev_monitor_event     *udm_ev;
138 
139           pthread_mutex_lock(&udm->q_lock);
140 
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);
146           }
147 
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);
156           }
157 
158           pthread_mutex_unlock(&udm->q_lock);
159           free(udm);
160 }
161 
162 int
client_cmd_monitor(struct client_info * cli,prop_dictionary_t dict)163 client_cmd_monitor(struct client_info *cli, prop_dictionary_t dict)
164 {
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;
174 
175           pa = NULL;
176           po = prop_dictionary_get(dict, "filters");
177           if ((po != NULL) && prop_object_type(po) == PROP_TYPE_ARRAY) {
178                     pa = po;
179           }
180 
181           udm = udev_monitor_init(cli, pa);
182           if (udm == NULL)
183                     return 1;
184 
185           ok = 1;
186           fds[0].fd = cli->fd;
187           fds[0].events = POLLRDNORM;
188 
189           MONITOR_LOCK();
190           TAILQ_INSERT_TAIL(&udev_monitor_list, udm, link);
191           MONITOR_UNLOCK();
192 
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);
198 
199                     if (ret == EINVAL) {
200                               syslog(LOG_ERR, "pthread_cond_timedwait error: EINVAL");
201                               goto end_nofree;
202                     }
203 
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;
208                     }
209 
210                     udm_ev = TAILQ_FIRST(&udm->ev_queue);
211                     if (udm_ev == NULL)
212                               continue;
213 
214                     assert(udm_ev->ev_dict != NULL);
215                     xml = prop_dictionary_externalize(udm_ev->ev_dict);
216                     if (xml == NULL)
217                               continue;
218 
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);
223 
224                     r = send_xml(cli->fd, xml);
225                     if (r <= 0)
226                               goto end;
227 
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;
236 
237           }
238 
239           MONITOR_LOCK();
240           TAILQ_REMOVE(&udev_monitor_list, udm, link);
241           MONITOR_UNLOCK();
242 
243           udev_monitor_free(udm);
244 
245           return 1;
246 }
247 
248 static int
_parse_filter_prop(struct udev_monitor * udm,prop_array_t pa)249 _parse_filter_prop(struct udev_monitor *udm, prop_array_t pa)
250 {
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;
257 
258           iter = prop_array_iterator(pa);
259           if (iter == NULL)
260                     return -1;
261 
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;
267 
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;
274 
275                     pn = prop_dictionary_get(dict, "type");
276                     if (pn == NULL)
277                               goto error_out_ps;
278 
279                     ps = prop_dictionary_get(dict, "expr");
280                     if (ps == NULL)
281                               goto error_out_ps;
282 
283                     if (prop_dictionary_get(dict, "negative"))
284                               evf->neg = 1;
285                     else
286                               evf->neg = 0;
287 
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;
295 
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;
301 
302                     default:
303                               goto error_out_ps;
304                     }
305 
306                     pthread_mutex_lock(&udm->q_lock);
307                     TAILQ_INSERT_TAIL(&udm->ev_filt, evf, link);
308                     pthread_mutex_unlock(&udm->q_lock);
309 
310           }
311 
312           prop_object_iterator_release(iter);
313           return 0;
314 
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;
322 }
323 
324 /*
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>
337 */
338 
339 static int
match_filter(struct event_filter * evf,prop_dictionary_t ev_dict)340 match_filter(struct event_filter *evf, prop_dictionary_t ev_dict)
341 {
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;
348 
349           if (ev_dict == NULL)
350                     return 0;
351 
352           prop_object_retain(ev_dict);
353 
354           if ((po = prop_dictionary_get(ev_dict, evf->key)) == NULL)
355                     goto no_match;
356 
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));
366                     }
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;
372           }
373 
374           switch (evf->type) {
375           case EVENT_FILTER_TYPE_WILDCARD:
376                     ret = WildCaseCmp(evf->wildcard_match, str);
377 
378                     if (ret != 0)
379                               goto no_match;
380 
381                     break;
382           case EVENT_FILTER_TYPE_REGEX:
383                     ret = regexec(&evf->regex_match, str, 0, NULL, 0);
384 
385                     if (ret != 0)
386                               goto no_match;
387                     break;
388           default:
389                     goto no_match;
390           }
391 
392           prop_object_release(ev_dict);
393           return 1;
394 
395 no_match:
396           prop_object_release(ev_dict);
397           return 0;
398 }
399 
400 int
match_event_filter(struct udev_monitor * udm,prop_dictionary_t ev_dict)401 match_event_filter(struct udev_monitor *udm, prop_dictionary_t ev_dict)
402 {
403           struct event_filter *evf;
404 
405           pthread_mutex_lock(&udm->q_lock);
406 
407           if (TAILQ_EMPTY(&udm->ev_filt))
408                     return 1;
409 
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;
415                     }
416           }
417 
418           pthread_mutex_unlock(&udm->q_lock);
419           return 1;
420 }
421 
422 static int
WildCaseCmp(const char * w,const char * s)423 WildCaseCmp(const char *w, const char *s)
424 {
425     int i;
426     int c;
427     int slen = strlen(s);
428     const char **mary;
429 
430     for (i = c = 0; w[i]; ++i) {
431           if (w[i] == '*')
432               ++c;
433     }
434     mary = malloc(sizeof(char *) * (c + 1));
435     if (mary == NULL)
436                return -1;
437 
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);
443 }
444 
445 /*
446  * WildCaseCmp() - compare wild string to sane string, case insensitive
447  *
448  *        Returns 0 on success, -1 on failure.
449  */
450 static int
wildCaseCmp(const char ** mary,int d,const char * w,const char * s)451 wildCaseCmp(const char **mary, int d, const char *w, const char *s)
452 {
453     int i;
454 
455     /*
456      * skip fixed portion
457      */
458     for (;;) {
459           switch(*w) {
460           case '*':
461               /*
462                * optimize terminator
463                */
464               if (w[1] == 0)
465                     return(0);
466               if (w[1] != '?' && w[1] != '*') {
467                     /*
468                      * optimize * followed by non-wild
469                      */
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);
473                     }
474               } else {
475                     /*
476                      * less-optimal
477                      */
478                     for (i = 0; s + i < mary[d]; ++i) {
479                         if (wildCaseCmp(mary, d + 1, w + 1, s + i) == 0)
480                               return(0);
481                     }
482               }
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);
495               }
496               if (*w == 0)    /* terminator */
497                     return(0);
498               ++w;
499               ++s;
500               break;
501           }
502     }
503     /* not reached */
504     return(-1);
505 }
506