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