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