Merge branch 'vendor/BZIP'
[dragonfly.git] / sbin / udevd / udevd.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
44 #include <err.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <libgen.h>
48 #include <regex.h>
49 #include <signal.h>
50 #include <stdarg.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <syslog.h>
55 #include <unistd.h>
56 #include <pthread.h>
57
58 #include <libprop/proplib.h>
59 #include <sys/udev.h>
60 #include "udevd.h"
61
62 int debugopt = 0;
63
64 static int udevfd;
65
66 extern pthread_mutex_t  monitor_lock;
67 extern TAILQ_HEAD(udev_monitor_list_head, udev_monitor) udev_monitor_list;
68 extern TAILQ_HEAD(pdev_array_list_head, pdev_array_entry)       pdev_array_list;
69
70 static void usage(void);
71
72 int match_dev_dict(prop_dictionary_t, prop_dictionary_t);
73 prop_dictionary_t find_dev_dict(int64_t, prop_dictionary_t, int *);
74
75 void udev_read_event(int);
76 prop_array_t udev_getdevs(int);
77
78 static
79 void
80 usage(void)
81 {
82         fprintf(stderr, "usage: udevd [-d]\n");
83         exit(1);
84 }
85
86 int
87 match_dev_dict(prop_dictionary_t dict, prop_dictionary_t match_dict)
88 {
89         prop_number_t   pn, pn2;
90         prop_string_t   ps, ps2;
91
92         if (dict == NULL)
93                 return 0;
94
95         if ((ps = prop_dictionary_get(dict, "name")) == NULL)
96                 return 0;
97         if ((ps2 = prop_dictionary_get(match_dict, "name")) == NULL)
98                 return 0;
99         if (!prop_string_equals(ps, ps2))
100                 return 0;
101
102         if ((pn = prop_dictionary_get(dict, "devnum")) == NULL)
103                 return 0;
104         if ((pn2 = prop_dictionary_get(match_dict, "devnum")) == NULL)
105                 return 0;
106         if (!prop_number_equals(pn, pn2))
107                 return 0;
108
109         if ((pn = prop_dictionary_get(dict, "kptr")) == NULL)
110                 return 0;
111         if ((pn2 = prop_dictionary_get(match_dict, "kptr")) == NULL)
112                 return 0;
113         if (!prop_number_equals(pn, pn2))
114                 return 0;
115
116         return 1;
117 }
118
119 prop_dictionary_t
120 find_dev_dict(int64_t generation, prop_dictionary_t match_dict, int *idx)
121 {
122         struct pdev_array_entry *pae;
123         prop_array_t            pa;
124         prop_object_iterator_t  iter;
125         prop_dictionary_t       dict;
126         int i = 0;
127
128         if (generation == -1)
129                 pae = pdev_array_entry_get_last();
130         else
131                 pae = pdev_array_entry_get(generation);
132
133         if (pae == NULL)
134                 return NULL;
135
136         pa = pae->pdev_array;
137
138         iter = prop_array_iterator(pa);
139         if (iter == NULL) {
140                 pdev_array_entry_unref(pae);
141                 return NULL;
142         }
143
144         while ((dict = prop_object_iterator_next(iter)) != NULL) {
145                 if (match_dev_dict(dict, match_dict))
146                         break;
147                 ++i;
148         }
149
150         prop_object_iterator_release(iter);
151
152         if (idx != NULL)
153                 *idx = i;
154
155         pdev_array_entry_unref(pae);
156         return dict;
157 }
158
159 void
160 udev_read_event(int fd)
161 {
162         struct pdev_array_entry *pae;
163         prop_dictionary_t       dict, evdict, devdict;
164         prop_number_t           pn;
165         prop_string_t           ps;
166         prop_object_t           po;
167         prop_array_t            pa;
168         char    *xml;
169         int     n, idx, evtype;
170         size_t  sz;
171
172         sz = 4096 * 1024;
173
174         xml = malloc(sz); /* 4 MB */
175 again:
176         if ((n = read(fd, xml, sz)) <= 0) {
177                 if (errno == ENOMEM) {
178                         sz <<= 2;
179                         realloc(xml, sz);
180                         goto again;
181                 }
182                 free(xml);
183                 return;
184         }
185
186         dict = prop_dictionary_internalize(xml);
187         free(xml);
188         if (dict == NULL) {
189                 syslog(LOG_ERR, "internalization of xml failed");
190                 return;
191         }
192
193         pn = prop_dictionary_get(dict, "evtype");
194         if (pn == NULL) {
195                 syslog(LOG_ERR, "read_event: no key evtype");
196                 goto out;
197         }
198
199         evtype = prop_number_integer_value(pn);
200
201         evdict = prop_dictionary_get(dict, "evdict");
202         if (evdict == NULL) {
203                 syslog(LOG_ERR, "read_event: no key evdict");
204                 goto out;
205         }
206
207         switch (evtype) {
208         case UDEV_EVENT_ATTACH:
209                 monitor_queue_event(dict);
210                 pae = pdev_array_entry_get_last();
211                 pa = prop_array_copy(pae->pdev_array);
212                 pdev_array_entry_unref(pae);
213                 if (pa == NULL)
214                         goto out;
215                 prop_array_add(pa, evdict);
216                 pdev_array_entry_insert(pa);
217                 break;
218
219         case UDEV_EVENT_DETACH:
220                 monitor_queue_event(dict);
221                 if ((devdict = find_dev_dict(-1, evdict, &idx)) == NULL)
222                         goto out;
223                 pae = pdev_array_entry_get_last();
224                 pa = prop_array_copy(pae->pdev_array);
225                 pdev_array_entry_unref(pae);
226                 if (pa == NULL)
227                         goto out;
228                 prop_array_remove(pa, idx);
229                 pdev_array_entry_insert(pa);
230                 break;
231
232         case UDEV_EV_KEY_UPDATE:
233                 if ((devdict = find_dev_dict(-1, evdict, NULL)) == NULL)
234                         goto out;
235                 if ((ps = prop_dictionary_get(evdict, "key")) == NULL)
236                         goto out;
237                 if ((po = prop_dictionary_get(evdict, "value")) == NULL)
238                         goto out;
239                 /* prop_object_retain(po); */ /* not necessary afaik */
240                 prop_dictionary_set(devdict, prop_string_cstring_nocopy(ps), po);
241                 break;
242
243         case UDEV_EV_KEY_REMOVE:
244                 if ((devdict = find_dev_dict(-1, evdict, NULL)) == NULL)
245                         goto out;
246                 if ((ps = prop_dictionary_get(evdict, "key")) == NULL)
247                         goto out;
248                 prop_dictionary_remove(devdict, prop_string_cstring_nocopy(ps));
249                 break;
250
251         default:
252                 syslog(LOG_ERR, "read_event: unknown evtype %d", evtype);
253         }
254
255 out:
256         prop_object_release(dict);
257         return;
258 }
259
260 prop_array_t
261 udev_getdevs(int devfd)
262 {
263         prop_dictionary_t       pd, rpd;
264         prop_string_t           ps;
265         prop_array_t            pa;
266
267         pd = prop_dictionary_create();
268         if (pd == NULL) {
269                 err(1, "prop_dictionary_create()");
270         }
271
272         ps = prop_string_create_cstring("getdevs");
273         if (ps == NULL) {
274                 prop_object_release(pd);
275                 err(1, "prop_string_create_cstring()");
276         }
277
278         if (prop_dictionary_set(pd, "command", ps) == false) {
279                 prop_object_release(ps);
280                 prop_object_release(pd);
281                 err(1, "prop_dictionary_set()");
282         }
283
284         prop_object_release(ps);
285
286         /* Send dictionary to kernel space */
287         if (prop_dictionary_sendrecv_ioctl(pd, devfd, UDEVPROP, &rpd) != 0)
288                 err(1, "prop_array_recv_ioctl()");
289
290         prop_object_release(pd);
291
292         pa = prop_dictionary_get(rpd, "array");
293         if (pa == NULL)
294                 goto out;
295         prop_object_retain(pa);
296
297 out:
298         prop_object_release(rpd);
299         return pa;
300 }
301
302 static void
303 killed(int sig __unused)
304 {
305         syslog(LOG_ERR, "udevd stopped");
306         unlink("/var/run/udevd.pid");
307         pdev_array_clean();
308 }
309
310 int
311 ignore_signal(int signum)
312 {
313         struct sigaction act;
314         int ret;
315
316         act.sa_handler = SIG_IGN;
317         sigemptyset(&act.sa_mask);
318         act.sa_flags = 0;
319
320         ret = sigaction(signum, &act, NULL);
321         return ret;
322 }
323
324 static int
325 set_killed_signal(void)
326 {
327         struct sigaction act;
328         int ret;
329
330         act.sa_handler = killed;
331         sigemptyset(&act.sa_mask);
332         act.sa_flags = 0;
333
334         ret = sigaction(SIGTERM, &act, NULL);
335         return ret;
336 }
337
338 int main(int argc, char *argv[])
339 {
340         int error __unused, i, r, s;
341         struct pollfd fds[NFDS];
342         FILE *pidf;
343         int ch = 0;
344
345         while ((ch = getopt(argc, argv, "d")) != -1) {
346                 switch(ch) {
347                 case 'd':
348                         debugopt = 1;
349                         break;
350                 default:
351                         usage();
352                         /* NOT REACHED */
353                 }
354         }
355         argc -= optind;
356         argv -= optind;
357
358         TAILQ_INIT(&pdev_array_list);
359         TAILQ_INIT(&udev_monitor_list);
360
361         r = ignore_signal(SIGPIPE);
362         if (r != 0)
363                 err(1, "could not ignore_signal SIGPIPE");
364
365         r = pthread_mutex_init(&(monitor_lock), NULL);
366         if (r != 0)
367                 err(1, "could not allocate a pthread_mutex");
368
369         if ((udevfd = open(UDEV_DEVICE_PATH, O_RDWR | O_NONBLOCK)) == -1)
370                 err(1, "%s", UDEV_DEVICE_PATH);
371         unblock_descriptor(udevfd);
372
373         s = init_local_server(LISTEN_SOCKET_FILE, SOCK_STREAM, 0);
374         if (s < 0)
375                 err(1, "init_local_server");
376
377         pidf = fopen("/var/run/udevd.pid", "w");
378         if (pidf == NULL)
379                 err(1, "pidfile");
380
381         set_killed_signal();
382
383         if (debugopt == 0)
384                 if (daemon(0, 0) == -1)
385                         err(1, "daemon");
386
387         fprintf(pidf, "%ld\n", (long)getpid());
388         fclose(pidf);
389
390         syslog(LOG_ERR, "udevd started");
391
392         pdev_array_entry_insert(udev_getdevs(udevfd));
393
394         memset(fds, 0 , sizeof(fds));
395         fds[UDEV_DEVICE_FD_IDX].fd = udevfd;
396         fds[UDEV_DEVICE_FD_IDX].events = POLLIN;
397         fds[UDEV_SOCKET_FD_IDX].fd = s;
398         fds[UDEV_SOCKET_FD_IDX].events = POLLIN | POLLPRI;
399
400         for (;;) {
401                 r = poll(fds, NFDS, -1);
402                 if (r < 0)
403                         err(1, "polling...");
404
405                 for (i = 0; (i < NFDS) && (r > 0); i++) {
406                         if (fds[i].revents == 0)
407                                 continue;
408
409                         --r;
410                         switch (i) {
411                         case UDEV_DEVICE_FD_IDX:
412                                 udev_read_event(udevfd);
413                                 break;
414                         case UDEV_SOCKET_FD_IDX:
415                                 handle_new_connection(s);
416                                 break;
417                         default:
418                                 break;
419                         }
420                 }
421         }
422
423         return 0;
424 }