<sys/vfscache.h>: Sync enum vtagtype with what we have.
[dragonfly.git] / sbin / udevd / udevd.c
blob66c03523c73d97c53058ad0579de52fbd12197ee
1 /*
2 * Copyright (c) 2010 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Alex Hornung <ahornung@gmail.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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.
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.
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>
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>
58 #include <libprop/proplib.h>
59 #include <sys/udev.h>
60 #include "udevd.h"
62 int debugopt = 0;
64 static int udevfd;
65 static int hangup_ongoing = 0;
66 static struct pollfd fds[NFDS];
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;
72 static void usage(void);
74 int match_dev_dict(prop_dictionary_t, prop_dictionary_t);
75 prop_dictionary_t find_dev_dict(int64_t, prop_dictionary_t, int *);
77 void udev_read_event(int);
78 prop_array_t udev_getdevs(int);
80 static
81 void
82 usage(void)
84 fprintf(stderr, "usage: udevd [-d]\n");
85 exit(1);
88 int
89 match_dev_dict(prop_dictionary_t dict, prop_dictionary_t match_dict)
91 prop_number_t pn, pn2;
92 prop_string_t ps, ps2;
94 if (dict == NULL)
95 return 0;
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;
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;
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;
118 return 1;
121 prop_dictionary_t
122 find_dev_dict(int64_t generation, prop_dictionary_t match_dict, int *idx)
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;
130 if (generation == -1)
131 pae = pdev_array_entry_get_last();
132 else
133 pae = pdev_array_entry_get(generation);
135 if (pae == NULL)
136 return NULL;
138 pa = pae->pdev_array;
140 iter = prop_array_iterator(pa);
141 if (iter == NULL) {
142 pdev_array_entry_unref(pae);
143 return NULL;
146 while ((dict = prop_object_iterator_next(iter)) != NULL) {
147 if (match_dev_dict(dict, match_dict))
148 break;
149 ++i;
152 prop_object_iterator_release(iter);
154 if (idx != NULL)
155 *idx = i;
157 pdev_array_entry_unref(pae);
158 return dict;
161 void
162 udev_read_event(int fd)
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;
174 sz = 4096 * 1024;
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;
185 goto again;
187 free(xml);
188 return;
191 dict = prop_dictionary_internalize(xml);
192 free(xml);
193 if (dict == NULL) {
194 syslog(LOG_ERR, "internalization of xml failed");
195 return;
198 pn = prop_dictionary_get(dict, "evtype");
199 if (pn == NULL) {
200 syslog(LOG_ERR, "read_event: no key evtype");
201 goto out;
204 evtype = prop_number_integer_value(pn);
206 evdict = prop_dictionary_get(dict, "evdict");
207 if (evdict == NULL) {
208 syslog(LOG_ERR, "read_event: no key evdict");
209 goto out;
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;
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;
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;
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;
256 default:
257 syslog(LOG_ERR, "read_event: unknown evtype %d", evtype);
260 out:
261 prop_object_release(dict);
262 return;
265 prop_array_t
266 udev_getdevs(int devfd)
268 prop_dictionary_t pd, rpd;
269 prop_string_t ps;
270 prop_array_t pa;
272 pd = prop_dictionary_create();
273 if (pd == NULL) {
274 err(1, "prop_dictionary_create()");
277 ps = prop_string_create_cstring("getdevs");
278 if (ps == NULL) {
279 prop_object_release(pd);
280 err(1, "prop_string_create_cstring()");
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()");
289 prop_object_release(ps);
291 /* Send dictionary to kernel space */
292 if (prop_dictionary_sendrecv_ioctl(pd, devfd, UDEVPROP, &rpd) != 0)
293 err(1, "prop_array_recv_ioctl()");
295 prop_object_release(pd);
297 pa = prop_dictionary_get(rpd, "array");
298 if (pa == NULL)
299 goto out;
300 prop_object_retain(pa);
302 out:
303 prop_object_release(rpd);
304 return pa;
307 static void
308 killed(int sig __unused)
310 syslog(LOG_ERR, "udevd stopped");
311 unlink("/var/run/udevd.pid");
312 pdev_array_clean();
313 exit(0);
316 static void
317 hangup(int sig __unused)
319 FILE *pidf;
320 int s;
322 syslog(LOG_ERR, "udevd hangup+resume");
324 pidf = fopen("/var/run/udevd.pid", "w");
325 if (pidf != NULL) {
326 fprintf(pidf, "%ld\n", (long)getpid());
327 fclose(pidf);
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");
337 fds[UDEV_SOCKET_FD_IDX].fd = s;
338 pdev_array_entry_insert(udev_getdevs(udevfd));
339 hangup_ongoing = 0;
343 ignore_signal(int signum)
345 struct sigaction act;
346 int ret;
348 act.sa_handler = SIG_IGN;
349 sigemptyset(&act.sa_mask);
350 act.sa_flags = 0;
352 ret = sigaction(signum, &act, NULL);
353 return ret;
356 static int
357 set_signal(int signum, sig_t sig_func)
359 struct sigaction act;
360 int ret;
362 act.sa_handler = sig_func;
363 sigemptyset(&act.sa_mask);
364 act.sa_flags = 0;
366 ret = sigaction(signum, &act, NULL);
367 return ret;
370 int main(int argc, char *argv[])
372 int error __unused, i, r, s;
373 FILE *pidf;
374 int ch = 0;
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 */
386 argc -= optind;
387 argv += optind;
389 TAILQ_INIT(&pdev_array_list);
390 TAILQ_INIT(&udev_monitor_list);
392 r = ignore_signal(SIGPIPE);
393 if (r != 0)
394 err(1, "could not ignore_signal SIGPIPE");
396 r = pthread_mutex_init(&(monitor_lock), NULL);
397 if (r != 0)
398 err(1, "could not allocate a pthread_mutex");
400 if ((udevfd = open(UDEV_DEVICE_PATH, O_RDWR | O_NONBLOCK)) == -1)
401 err(1, "%s", UDEV_DEVICE_PATH);
402 unblock_descriptor(udevfd);
404 s = init_local_server(LISTEN_SOCKET_FILE, SOCK_STREAM, 0);
405 if (s < 0)
406 err(1, "init_local_server");
408 pidf = fopen("/var/run/udevd.pid", "w");
409 #if 0
410 if (pidf == NULL)
411 err(1, "pidfile");
412 #endif
414 set_signal(SIGTERM, killed);
415 set_signal(SIGHUP, hangup);
417 if (debugopt == 0)
418 if (daemon(0, 0) == -1)
419 err(1, "daemon");
421 if (pidf != NULL) {
422 fprintf(pidf, "%ld\n", (long)getpid());
423 fclose(pidf);
426 syslog(LOG_ERR, "udevd started");
428 pdev_array_entry_insert(udev_getdevs(udevfd));
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;
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...");
446 } else {
447 usleep(20000); /* 20 ms */
448 continue;
452 for (i = 0; (i < NFDS) && (r > 0); i++) {
453 if (fds[i].revents == 0)
454 continue;
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;
470 syslog(LOG_ERR, "udevd is exiting normally");
471 return 0;