xref: /dragonfly/sbin/udevd/udevd.c (revision cd1c6085)
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 }
473