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