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