udevd - bring into sbin, add rc script
[dragonfly.git] / sbin / udevd / udevd.c
CommitLineData
3a3826b3
AH
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
62static int udevfd;
63
64extern pthread_mutex_t monitor_lock;
65extern TAILQ_HEAD(udev_monitor_list_head, udev_monitor) udev_monitor_list;
66extern TAILQ_HEAD(pdev_array_list_head, pdev_array_entry) pdev_array_list;
67
68int match_dev_dict(prop_dictionary_t, prop_dictionary_t);
69prop_dictionary_t find_dev_dict(int64_t, prop_dictionary_t, int *);
70
71void udev_read_event(int);
72prop_array_t udev_getdevs(int);
73
74int
75match_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
107prop_dictionary_t
108find_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
147void
148udev_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 */
163again:
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
243out:
244 prop_object_release(dict);
245 return;
246}
247
248prop_array_t
249udev_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
285out:
286 prop_object_release(rpd);
287 return pa;
288}
289
290int
291ignore_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
304int 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
2e7bf158
AH
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");
3a3826b3
AH
330
331 syslog(LOG_ERR, "udevd started");
332
333 pdev_array_entry_insert(udev_getdevs(udevfd));
334
3a3826b3
AH
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}