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