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