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