xref: /dflybsd-src/sys/kern/kern_udev.c (revision bc73772033acf16cec326bfb764d6fd9dc1dc660)
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/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/proc.h>
38 #include <sys/buf.h>
39 #include <sys/conf.h>
40 #include <sys/poll.h>
41 #include <sys/ioccom.h>
42 #include <sys/malloc.h>
43 #include <sys/ctype.h>
44 #include <sys/syslog.h>
45 #include <sys/udev.h>
46 #include <sys/devfs.h>
47 #include <libprop/proplib.h>
48 
49 MALLOC_DEFINE(M_UDEV, "udev", "udev allocs");
50 
51 /* XXX: use UUIDs for identification; would need help from devfs */
52 
53 static cdev_t		udev_dev;
54 static d_open_t		udev_dev_open;
55 static d_close_t	udev_dev_close;
56 static d_read_t		udev_dev_read;
57 static d_poll_t		udev_dev_poll;
58 static d_ioctl_t	udev_dev_ioctl;
59 
60 static int _udev_dict_set_cstr(prop_dictionary_t, const char *, char *);
61 static int _udev_dict_set_int(prop_dictionary_t, const char *, int64_t);
62 static int _udev_dict_set_uint(prop_dictionary_t, const char *, uint64_t);
63 static int _udev_dict_delete_key(prop_dictionary_t, const char *);
64 static prop_dictionary_t udev_init_dict_event(cdev_t, const char *);
65 static int udev_init_dict(cdev_t);
66 static int udev_destroy_dict(cdev_t);
67 static void udev_event_insert(int, prop_dictionary_t);
68 static struct udev_event_kernel *udev_event_remove(void);
69 static void udev_event_free(struct udev_event_kernel *);
70 static char *udev_event_externalize(struct udev_event_kernel *);
71 static void udev_getdevs_scan_callback(cdev_t, void *);
72 static int udev_getdevs_ioctl(struct plistref *, u_long, prop_dictionary_t);
73 
74 struct cmd_function {
75 	const char *cmd;
76 	int  (*fn)(struct plistref *, u_long, prop_dictionary_t);
77 };
78 
79 struct udev_prop_ctx {
80 	prop_array_t cdevs;
81 	int error;
82 };
83 
84 struct udev_event_kernel {
85 	struct udev_event ev;
86 	TAILQ_ENTRY(udev_event_kernel)	link;
87 };
88 
89 struct udev_softc {
90 	int opened;
91 	int initiated;
92 
93 	struct selinfo sel;
94 
95 	int qlen;
96 	struct lock lock;
97 	TAILQ_HEAD(, udev_event_kernel) ev_queue;	/* list of thread_io */
98 } udevctx;
99 
100 static struct dev_ops udev_dev_ops = {
101 	{ "udev", 0, 0 },
102 	.d_open = udev_dev_open,
103 	.d_close = udev_dev_close,
104 	.d_read = udev_dev_read,
105 	.d_poll = udev_dev_poll,
106 	.d_ioctl = udev_dev_ioctl
107 };
108 
109 static struct cmd_function cmd_fn[] = {
110 		{ .cmd = "getdevs", .fn = udev_getdevs_ioctl},
111 		{NULL, NULL}
112 };
113 
114 static int
115 _udev_dict_set_cstr(prop_dictionary_t dict, const char *key, char *str)
116 {
117 	prop_string_t	ps;
118 
119 	KKASSERT(dict != NULL);
120 
121 	ps = prop_string_create_cstring(str);
122 	if (ps == NULL) {
123 		return ENOMEM;
124 	}
125 
126 	if (prop_dictionary_set(dict, key, ps) == false) {
127 		prop_object_release(ps);
128 		return ENOMEM;
129 	}
130 
131 	prop_object_release(ps);
132 	return 0;
133 }
134 
135 static int
136 _udev_dict_set_int(prop_dictionary_t dict, const char *key, int64_t val)
137 {
138 	prop_number_t	pn;
139 
140 	KKASSERT(dict != NULL);
141 
142 	pn = prop_number_create_integer(val);
143 	if (pn == NULL)
144 		return ENOMEM;
145 
146 	if (prop_dictionary_set(dict, key, pn) == false) {
147 		prop_object_release(pn);
148 		return ENOMEM;
149 	}
150 
151 	prop_object_release(pn);
152 	return 0;
153 }
154 
155 static int
156 _udev_dict_set_uint(prop_dictionary_t dict, const char *key, uint64_t val)
157 {
158 	prop_number_t	pn;
159 
160 	KKASSERT(dict != NULL);
161 
162 	pn = prop_number_create_unsigned_integer(val);
163 	if (pn == NULL)
164 		return ENOMEM;
165 
166 	if (prop_dictionary_set(dict, key, pn) == false) {
167 		prop_object_release(pn);
168 		return ENOMEM;
169 	}
170 
171 	prop_object_release(pn);
172 	return 0;
173 }
174 
175 static int
176 _udev_dict_delete_key(prop_dictionary_t dict, const char *key)
177 {
178 	KKASSERT(dict != NULL);
179 
180 	prop_dictionary_remove(dict, key);
181 
182 	return 0;
183 }
184 
185 /*
186  * Initialize an event dictionary, which contains three parameters to
187  * identify the device referred to (name, devnum, kptr) and the affected key.
188  */
189 static prop_dictionary_t
190 udev_init_dict_event(cdev_t dev, const char *key)
191 {
192 	prop_dictionary_t	dict;
193 	uint64_t	kptr;
194 	int error;
195 
196 	kptr = (uint64_t)(uintptr_t)dev;
197 	KKASSERT(dev != NULL);
198 
199 	dict = prop_dictionary_create();
200 	if (dict == NULL) {
201 		log(LOG_DEBUG, "udev_init_dict_event: prop_dictionary_create() failed\n");
202 		return NULL;
203 	}
204 
205 	if ((error = _udev_dict_set_cstr(dict, "name", dev->si_name)))
206 		goto error_out;
207 	if ((error = _udev_dict_set_uint(dict, "devnum", dev->si_inode)))
208 		goto error_out;
209 	if ((error = _udev_dict_set_uint(dict, "kptr", kptr)))
210 		goto error_out;
211 	if ((error = _udev_dict_set_cstr(dict, "key", __DECONST(char *, key))))
212 		goto error_out;
213 
214 	return dict;
215 
216 error_out:
217 	prop_object_release(dict);
218 	return NULL;
219 }
220 
221 int
222 udev_dict_set_cstr(cdev_t dev, const char *key, char *str)
223 {
224 	prop_dictionary_t	dict;
225 	int error;
226 
227 	KKASSERT(dev != NULL);
228 
229 	if (dev->si_dict == NULL) {
230 		error = udev_init_dict(dev);
231 		if (error)
232 			return -1;
233 	}
234 
235 	/* Queue a key update event */
236 	dict = udev_init_dict_event(dev, key);
237 	if (dict == NULL)
238 		return ENOMEM;
239 
240 	if ((error = _udev_dict_set_cstr(dict, "value", str))) {
241 		prop_object_release(dict);
242 		return error;
243 	}
244 	udev_event_insert(UDEV_EV_KEY_UPDATE, dict);
245 	prop_object_release(dict);
246 
247 	error = _udev_dict_set_cstr(dev->si_dict, key, str);
248 	return error;
249 }
250 
251 int
252 udev_dict_set_int(cdev_t dev, const char *key, int64_t val)
253 {
254 	prop_dictionary_t	dict;
255 	int error;
256 
257 	KKASSERT(dev != NULL);
258 
259 	if (dev->si_dict == NULL) {
260 		error = udev_init_dict(dev);
261 		if (error)
262 			return -1;
263 	}
264 
265 	/* Queue a key update event */
266 	dict = udev_init_dict_event(dev, key);
267 	if (dict == NULL)
268 		return ENOMEM;
269 	if ((error = _udev_dict_set_int(dict, "value", val))) {
270 		prop_object_release(dict);
271 		return error;
272 	}
273 	udev_event_insert(UDEV_EV_KEY_UPDATE, dict);
274 	prop_object_release(dict);
275 
276 	return _udev_dict_set_int(dev->si_dict, key, val);
277 }
278 
279 int
280 udev_dict_set_uint(cdev_t dev, const char *key, uint64_t val)
281 {
282 	prop_dictionary_t	dict;
283 	int error;
284 
285 	KKASSERT(dev != NULL);
286 
287 	if (dev->si_dict == NULL) {
288 		error = udev_init_dict(dev);
289 		if (error)
290 			return -1;
291 	}
292 
293 	/* Queue a key update event */
294 	dict = udev_init_dict_event(dev, key);
295 	if (dict == NULL)
296 		return ENOMEM;
297 	if ((error = _udev_dict_set_uint(dict, "value", val))) {
298 		prop_object_release(dict);
299 		return error;
300 	}
301 	udev_event_insert(UDEV_EV_KEY_UPDATE, dict);
302 	prop_object_release(dict);
303 
304 	return _udev_dict_set_uint(dev->si_dict, key, val);
305 }
306 
307 int
308 udev_dict_delete_key(cdev_t dev, const char *key)
309 {
310 	prop_dictionary_t	dict;
311 
312 	KKASSERT(dev != NULL);
313 
314 	/* Queue a key removal event */
315 	dict = udev_init_dict_event(dev, key);
316 	if (dict == NULL)
317 		return ENOMEM;
318 	udev_event_insert(UDEV_EV_KEY_REMOVE, dict);
319 	prop_object_release(dict);
320 
321 	return _udev_dict_delete_key(dev->si_dict, key);
322 }
323 
324 static int
325 udev_init_dict(cdev_t dev)
326 {
327 	prop_dictionary_t dict;
328 	uint64_t	kptr;
329 	int error;
330 
331 	kptr = (uint64_t)(uintptr_t)dev;
332 
333 	KKASSERT(dev != NULL);
334 
335 	if (dev->si_dict != NULL) {
336 #if 0
337 		log(LOG_DEBUG,
338 		    "udev_init_dict: new dict for %s, but has dict already (%p)!\n",
339 		    dev->si_name, dev->si_dict);
340 #endif
341 		return 0;
342 	}
343 
344 	dict = prop_dictionary_create();
345 	if (dict == NULL) {
346 		log(LOG_DEBUG, "udev_init_dict: prop_dictionary_create() failed\n");
347 		return ENOMEM;
348 	}
349 
350 	if ((error = _udev_dict_set_cstr(dict, "name", dev->si_name)))
351 		goto error_out;
352 	if ((error = _udev_dict_set_uint(dict, "devnum", dev->si_inode)))
353 		goto error_out;
354 	if ((error = _udev_dict_set_uint(dict, "kptr", kptr)))
355 		goto error_out;
356 
357 	/* XXX: The next 3 are marginallly useful, if at all */
358 	if ((error = _udev_dict_set_uint(dict, "uid", dev->si_uid)))
359 		goto error_out;
360 	if ((error = _udev_dict_set_uint(dict, "gid", dev->si_gid)))
361 		goto error_out;
362 	if ((error = _udev_dict_set_int(dict, "mode", dev->si_perms)))
363 		goto error_out;
364 
365 	if ((error = _udev_dict_set_int(dict, "major", umajor(dev->si_inode))))
366 		goto error_out;
367 	if ((error = _udev_dict_set_int(dict, "minor", dev->si_uminor)))
368 		goto error_out;
369 	if (dev->si_ops->head.name != NULL) {
370 		if ((error = _udev_dict_set_cstr(dict, "driver",
371 		    __DECONST(char *, dev->si_ops->head.name))))
372 			goto error_out;
373 	}
374 
375 	dev->si_dict = dict;
376 	return 0;
377 
378 error_out:
379 	dev->si_dict = NULL;
380 	prop_object_release(dict);
381 	return error;
382 }
383 
384 static int
385 udev_destroy_dict(cdev_t dev)
386 {
387 	KKASSERT(dev != NULL);
388 
389 	if (dev->si_dict != NULL) {
390 		prop_object_release(dev->si_dict);
391 		dev->si_dict = NULL;
392 	}
393 
394 	return 0;
395 }
396 
397 static void
398 udev_event_insert(int ev_type, prop_dictionary_t dict)
399 {
400 	struct udev_event_kernel *ev;
401 
402 	/* Only start queing events after client has initiated properly */
403 	if (!udevctx.initiated)
404 		return;
405 
406 	/* XXX: use objcache eventually */
407 	ev = kmalloc(sizeof(*ev), M_UDEV, M_WAITOK);
408 	ev->ev.ev_dict = prop_dictionary_copy(dict);
409 	if (ev->ev.ev_dict == NULL) {
410 		kfree(ev, M_UDEV);
411 		return;
412 	}
413 	ev->ev.ev_type = ev_type;
414 
415 	lockmgr(&udevctx.lock, LK_EXCLUSIVE);
416 	TAILQ_INSERT_TAIL(&udevctx.ev_queue, ev, link);
417 	++udevctx.qlen;
418 	lockmgr(&udevctx.lock, LK_RELEASE);
419 
420 	wakeup(&udevctx);
421 	selwakeup(&udevctx.sel);
422 }
423 
424 static struct udev_event_kernel *
425 udev_event_remove(void)
426 {
427 	struct udev_event_kernel *ev;
428 
429 	lockmgr(&udevctx.lock, LK_EXCLUSIVE);
430 	if (TAILQ_EMPTY(&udevctx.ev_queue)) {
431 		lockmgr(&udevctx.lock, LK_RELEASE);
432 		return NULL;
433 	}
434 
435 	ev = TAILQ_FIRST(&udevctx.ev_queue);
436 	TAILQ_REMOVE(&udevctx.ev_queue, ev, link);
437 	--udevctx.qlen;
438 	lockmgr(&udevctx.lock, LK_RELEASE);
439 
440 	return ev;
441 }
442 
443 static void
444 udev_event_free(struct udev_event_kernel *ev)
445 {
446 	/* XXX: use objcache eventually */
447 	kfree(ev, M_UDEV);
448 }
449 
450 static char *
451 udev_event_externalize(struct udev_event_kernel *ev)
452 {
453 	prop_dictionary_t	dict;
454 	char *xml;
455 	int error;
456 
457 
458 	dict = prop_dictionary_create();
459 	if (dict == NULL) {
460 		log(LOG_DEBUG, "udev_event_externalize: prop_dictionary_create() failed\n");
461 		return NULL;
462 	}
463 
464 	if ((error = _udev_dict_set_int(dict, "evtype", ev->ev.ev_type))) {
465 		prop_object_release(dict);
466 		return NULL;
467 	}
468 
469 	if (prop_dictionary_set(dict, "evdict", ev->ev.ev_dict) == false) {
470 		prop_object_release(dict);
471 		return NULL;
472 	}
473 
474 	prop_object_release(ev->ev.ev_dict);
475 
476 	xml = prop_dictionary_externalize(dict);
477 
478 	prop_object_release(dict);
479 
480 	return xml;
481 }
482 
483 int
484 udev_event_attach(cdev_t dev, char *name, int alias)
485 {
486 	prop_dictionary_t	dict;
487 	int error;
488 
489 	KKASSERT(dev != NULL);
490 
491 	error = ENOMEM;
492 
493 	if (alias) {
494 		dict = prop_dictionary_copy(dev->si_dict);
495 		if (dict == NULL)
496 			goto error_out;
497 
498 		if ((error = _udev_dict_set_cstr(dict, "name", name))) {
499 			prop_object_release(dict);
500 			goto error_out;
501 		}
502 
503 		_udev_dict_set_int(dict, "alias", 1);
504 
505 		udev_event_insert(UDEV_EVENT_ATTACH, dict);
506 		prop_object_release(dict);
507 	} else {
508 		error = udev_init_dict(dev);
509 		if (error)
510 			goto error_out;
511 
512 		_udev_dict_set_int(dev->si_dict, "alias", 0);
513 		udev_event_insert(UDEV_EVENT_ATTACH, dev->si_dict);
514 	}
515 
516 error_out:
517 	return error;
518 }
519 
520 int
521 udev_event_detach(cdev_t dev, char *name, int alias)
522 {
523 	prop_dictionary_t	dict;
524 
525 	KKASSERT(dev != NULL);
526 
527 	if (alias) {
528 		dict = prop_dictionary_copy(dev->si_dict);
529 		if (dict == NULL)
530 			goto error_out;
531 
532 		if (_udev_dict_set_cstr(dict, "name", name)) {
533 			prop_object_release(dict);
534 			goto error_out;
535 		}
536 
537 		_udev_dict_set_int(dict, "alias", 1);
538 
539 		udev_event_insert(UDEV_EVENT_DETACH, dict);
540 		prop_object_release(dict);
541 	} else {
542 		udev_event_insert(UDEV_EVENT_DETACH, dev->si_dict);
543 	}
544 
545 error_out:
546 	udev_destroy_dict(dev);
547 
548 	return 0;
549 }
550 
551 /*
552  * dev stuff
553  */
554 static int
555 udev_dev_open(struct dev_open_args *ap)
556 {
557 	if (udevctx.opened)
558 		return EBUSY;
559 
560 	udevctx.opened = 1;
561 
562 	return 0;
563 }
564 
565 static int
566 udev_dev_close(struct dev_close_args *ap)
567 {
568 	udevctx.opened = 0;
569 	udevctx.initiated = 0;
570 	wakeup(&udevctx);
571 
572 	return 0;
573 }
574 
575 static int
576 udev_dev_poll(struct dev_poll_args *ap)
577 {
578 	int revents = 0;
579 
580         lockmgr(&udevctx.lock, LK_EXCLUSIVE);
581         if (ap->a_events & (POLLIN | POLLRDNORM)) {
582                 if (!TAILQ_EMPTY(&udevctx.ev_queue))
583                         revents = ap->a_events & (POLLIN | POLLRDNORM);
584                 else
585                         selrecord(curthread, &udevctx.sel);
586         }
587         lockmgr(&udevctx.lock, LK_RELEASE);
588 
589         ap->a_events = revents;
590         return 0;
591 }
592 
593 static int
594 udev_dev_read(struct dev_read_args *ap)
595 {
596 	struct udev_event_kernel *ev;
597 	struct uio *uio = ap->a_uio;
598 	char	*xml;
599 	size_t	len;
600 	int	error;
601 
602 
603 	lockmgr(&udevctx.lock, LK_EXCLUSIVE);
604 
605 	for (;;) {
606 		if ((ev = udev_event_remove()) != NULL) {
607 			if ((xml = udev_event_externalize(ev)) == NULL) {
608 				lockmgr(&udevctx.lock, LK_RELEASE);
609 				return ENOMEM;
610 			}
611 
612 			len = strlen(xml) + 1; /* account for NULL-termination */
613 			if (uio->uio_resid < len) {
614 				error = ENOMEM;
615 			} else {
616 				error = uiomove((caddr_t)xml, len, uio);
617 			}
618 
619 			kfree(xml, M_TEMP);
620 			udev_event_free(ev);
621 			lockmgr(&udevctx.lock, LK_RELEASE);
622 			return error;
623 		}
624 
625 		if ((error = lksleep(&udevctx, &udevctx.lock, 0, "udevq", 0))) {
626 			lockmgr(&udevctx.lock, LK_RELEASE);
627 			return error;
628 		}
629 	}
630 
631 	lockmgr(&udevctx.lock, LK_RELEASE);
632 
633 }
634 
635 static int
636 udev_dev_ioctl(struct dev_ioctl_args *ap)
637 {
638 	prop_dictionary_t dict;
639 	prop_object_t	po;
640 	prop_string_t	ps;
641 	struct plistref *pref;
642 	int i, error;
643 
644 	error = 0;
645 
646 	switch(ap->a_cmd) {
647 	case UDEVPROP:
648 		/* Use proplib(3) for userspace/kernel communication */
649 		pref = (struct plistref *)ap->a_data;
650 		error = prop_dictionary_copyin_ioctl(pref, ap->a_cmd, &dict);
651 		if (error)
652 			return error;
653 
654 		po = prop_dictionary_get(dict, "command");
655 		if (po == NULL || prop_object_type(po) != PROP_TYPE_STRING) {
656 			log(LOG_DEBUG, "udev: prop_dictionary_get() failed\n");
657 			prop_object_release(dict);
658 			return EINVAL;
659 		}
660 
661 		ps = po;
662 		/* Handle cmd */
663 		for(i = 0; cmd_fn[i].cmd != NULL; i++) {
664 			if (prop_string_equals_cstring(ps, cmd_fn[i].cmd))
665 				break;
666 		}
667 
668 		if (cmd_fn[i].cmd != NULL) {
669 			error = cmd_fn[i].fn(pref, ap->a_cmd, dict);
670 		} else {
671 			error = EINVAL;
672 		}
673 
674 		//prop_object_release(po);
675 		prop_object_release(dict);
676 		break;
677 	default:
678 		error = ENOTTY; /* Inappropriate ioctl for device */
679 		break;
680 	}
681 
682 	return(error);
683 }
684 
685 static void
686 udev_getdevs_scan_callback(cdev_t cdev, void *arg)
687 {
688 	struct udev_prop_ctx *ctx = arg;
689 
690 	KKASSERT(arg != NULL);
691 
692 	if (cdev->si_dict == NULL)
693 		return;
694 
695 	if (prop_array_add(ctx->cdevs, cdev->si_dict) == false) {
696 		ctx->error = EINVAL;
697 		return;
698 	}
699 }
700 
701 static int
702 udev_getdevs_ioctl(struct plistref *pref, u_long cmd, prop_dictionary_t dict)
703 {
704 	prop_dictionary_t odict;
705 	struct udev_prop_ctx ctx;
706 	int error;
707 
708 	ctx.error = 0;
709 	ctx.cdevs = prop_array_create();
710 	if (ctx.cdevs == NULL) {
711 		log(LOG_DEBUG, "udev_getdevs_ioctl: prop_array_create() failed\n");
712 		return EINVAL;
713 	}
714 
715 	/* XXX: need devfs_scan_alias_callback() */
716 	devfs_scan_callback(udev_getdevs_scan_callback, &ctx);
717 
718 	if (ctx.error != 0) {
719 		prop_object_release(ctx.cdevs);
720 		return (ctx.error);
721 	}
722 	udevctx.initiated = 1;
723 
724 	odict = prop_dictionary_create();
725 	if (odict == NULL) {
726 		return ENOMEM;
727 	}
728 
729 	if ((prop_dictionary_set(odict, "array", ctx.cdevs)) == 0) {
730 		log(LOG_DEBUG, "udev_getdevs_ioctl: prop_dictionary_set failed\n");
731 		prop_object_release(odict);
732 		return ENOMEM;
733 	}
734 
735 	error = prop_dictionary_copyout_ioctl(pref, cmd, odict);
736 
737 	prop_object_release(odict);
738 	return error;
739 }
740 
741 
742 /*
743  * SYSINIT stuff
744  */
745 static void
746 udev_init(void)
747 {
748 	lockinit(&udevctx.lock, "udevevq", 0, LK_CANRECURSE);
749 	TAILQ_INIT(&udevctx.ev_queue);
750 }
751 
752 static void
753 udev_uninit(void)
754 {
755 }
756 
757 static void
758 udev_dev_init(void)
759 {
760 	udev_dev = make_dev(&udev_dev_ops,
761             0,
762             UID_ROOT,
763             GID_WHEEL,
764             0600,
765             "udev");
766 }
767 
768 static void
769 udev_dev_uninit(void)
770 {
771 	destroy_dev(udev_dev);
772 }
773 
774 SYSINIT(subr_udev_register, SI_SUB_CREATE_INIT, SI_ORDER_ANY, udev_init, NULL);
775 SYSUNINIT(subr_udev_register, SI_SUB_CREATE_INIT, SI_ORDER_ANY, udev_uninit, NULL);
776 SYSINIT(subr_udev_dev_register, SI_SUB_DRIVERS, SI_ORDER_ANY, udev_dev_init, NULL);
777 SYSUNINIT(subr_udev_dev_register, SI_SUB_DRIVERS, SI_ORDER_ANY, udev_dev_uninit, NULL);
778