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