From 3a3826b3871c8c2f480bcba820c6da8f86700143 Mon Sep 17 00:00:00 2001 From: Alex Hornung Date: Sat, 12 Jun 2010 16:12:07 +0000 Subject: [PATCH] Bring in udev & libdevattr * Bring in kern_udev, libdevattr and udevd from my personal repo. This is still WIP, but basic functionality is available and the API is stabilizing. * kern_udev allows the association of certain parameters in form of a dictionary to each device in the system and provides notification of attach and detach events. * udevd is a userland daemon which keeps an up to date list of all devices and their dictionaries and provides an interface for libdevattr to acccess and manipulate these devices and related events. * libdevattr provides a mostly Linux' libudev compatible API to access device dictionaries and events. Beware that it is NOT a full drop-in replacement! --- lib/libdevattr/Makefile | 11 + lib/libdevattr/devattr.c | 319 ++++++++++++ lib/libdevattr/devattr.h | 151 ++++++ lib/libdevattr/devattr_device.c | 282 +++++++++++ lib/libdevattr/devattr_enumerate.c | 331 +++++++++++++ lib/libdevattr/devattr_monitor.c | 365 ++++++++++++++ lib/libdevattr/devattr_test.c | 83 ++++ lib/libdevattr/mktest | 7 + sys/conf/files | 1 + sys/dev/drm/i915_drv.h | 2 +- sys/kern/kern_udev.c | 747 ++++++++++++++++++++++++++++ sys/kern/vfs_conf.c | 19 +- sys/sys/conf.h | 2 + sys/sys/devfs.h | 6 +- sys/sys/udev.h | 75 +++ sys/vfs/devfs/devfs_core.c | 20 +- usr.sbin/udevd/Makefile | 8 + usr.sbin/udevd/mktest | 7 + usr.sbin/udevd/test_udevd.c | 770 +++++++++++++++++++++++++++++ usr.sbin/udevd/udevd.c | 365 ++++++++++++++ usr.sbin/udevd/udevd.h | 119 +++++ usr.sbin/udevd/udevd_client.c | 231 +++++++++ usr.sbin/udevd/udevd_monitor.c | 485 ++++++++++++++++++ usr.sbin/udevd/udevd_pdev.c | 138 ++++++ usr.sbin/udevd/udevd_socket.c | 157 ++++++ 25 files changed, 4682 insertions(+), 19 deletions(-) create mode 100644 lib/libdevattr/Makefile create mode 100644 lib/libdevattr/devattr.c create mode 100644 lib/libdevattr/devattr.h create mode 100644 lib/libdevattr/devattr_device.c create mode 100644 lib/libdevattr/devattr_enumerate.c create mode 100644 lib/libdevattr/devattr_monitor.c create mode 100644 lib/libdevattr/devattr_test.c create mode 100644 lib/libdevattr/mktest create mode 100644 sys/kern/kern_udev.c create mode 100644 sys/sys/udev.h create mode 100644 usr.sbin/udevd/Makefile create mode 100644 usr.sbin/udevd/mktest create mode 100644 usr.sbin/udevd/test_udevd.c create mode 100644 usr.sbin/udevd/udevd.c create mode 100644 usr.sbin/udevd/udevd.h create mode 100644 usr.sbin/udevd/udevd_client.c create mode 100644 usr.sbin/udevd/udevd_monitor.c create mode 100644 usr.sbin/udevd/udevd_pdev.c create mode 100644 usr.sbin/udevd/udevd_socket.c diff --git a/lib/libdevattr/Makefile b/lib/libdevattr/Makefile new file mode 100644 index 0000000000..d4e0ba1050 --- /dev/null +++ b/lib/libdevattr/Makefile @@ -0,0 +1,11 @@ +LIB= devattr +SRCS= devattr.c devattr_device.c devattr_enumerate.c devattr_monitor.c + +WARNS?= 6 +DEBUG_FLAGS=-g + +LDADD+= -lprop + +NOMAN= + +.include diff --git a/lib/libdevattr/devattr.c b/lib/libdevattr/devattr.c new file mode 100644 index 0000000000..8cf2c7a9e0 --- /dev/null +++ b/lib/libdevattr/devattr.c @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2010 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Alex Hornung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "devattr.h" + +struct udev { + int gp_fd; + int monitor_fd; + int refs; + + void *userdata; +}; + +struct udev * +udev_ref(struct udev *udev_ctx) +{ + atomic_add_int(&udev_ctx->refs, 1); + + return udev_ctx; +} + +void +udev_unref(struct udev *udev_ctx) +{ + int refcount; + + refcount = atomic_fetchadd_int(&udev_ctx->refs, -1); + + if (refcount == 1) { + atomic_subtract_int(&udev_ctx->refs, 0x400); /* in destruction */ + if (udev_ctx->gp_fd != -1) + close (udev_ctx->gp_fd); + if (udev_ctx->monitor_fd != -1) + close (udev_ctx->monitor_fd); + + free(udev_ctx); + } +} + +struct udev * +udev_new(void) +{ + struct udev *udev_ctx; + int ret, s; + + ret = conn_local_server(LISTEN_SOCKET_FILE, SOCK_STREAM, 0, &s); + if (ret < 0) + return NULL; + + udev_ctx = malloc(sizeof(struct udev)); + + udev_ctx->refs = 1; + udev_ctx->gp_fd = s; + udev_ctx->monitor_fd = -1; + udev_ctx->userdata = NULL; + + return udev_ctx; +} + +const char *udev_get_dev_path(struct udev *udev_ctx __unused) +{ + return "/dev"; +} + +void * +udev_get_userdata(struct udev *udev_ctx) +{ + return udev_ctx->userdata; +} + +void +udev_set_userdata(struct udev *udev_ctx, void *userdata) +{ + udev_ctx->userdata = userdata; +} + +int +udev_get_fd(struct udev *udev_ctx) +{ + return udev_ctx->gp_fd; +} + +int +send_xml(int s, char *xml) +{ + ssize_t r,n; + size_t sz; + + sz = strlen(xml) + 1; + + r = send(s, &sz, sizeof(sz), 0); + if (r <= 0) + return r; + + r = 0; + while (r < (ssize_t)sz) { + n = send(s, xml+r, sz-r, 0); + if (n <= 0) + return n; + r += n; + } + + return r; +} + +int +read_xml(int s, char *buf, size_t buf_sz) +{ + size_t sz; + int n, r; + + n = recv(s, &sz, sizeof(sz), MSG_WAITALL); + if (n <= 0) + return n; + + r = 0; + while ((r < (ssize_t)sz) && (r < (ssize_t)buf_sz)) { + n = recv(s, buf+r, sz-r, MSG_WAITALL); + if (n <= 0) + return n; + r += n; + } + + return r; +} + +int +_udev_dict_set_cstr(prop_dictionary_t dict, const char *key, char *str) +{ + prop_string_t ps; + + ps = prop_string_create_cstring(str); + if (ps == NULL) + return ENOMEM; + + if (prop_dictionary_set(dict, key, ps) == false) { + prop_object_release(ps); + return ENOMEM; + } + + prop_object_release(ps); + return 0; +} + +int +_udev_dict_set_int(prop_dictionary_t dict, const char *key, int64_t val) +{ + prop_number_t pn; + + pn = prop_number_create_integer(val); + if (pn == NULL) + return ENOMEM; + + if (prop_dictionary_set(dict, key, pn) == false) { + prop_object_release(pn); + return ENOMEM; + } + + prop_object_release(pn); + return 0; +} + +int +_udev_dict_set_uint(prop_dictionary_t dict, const char *key, uint64_t val) +{ + prop_number_t pn; + + pn = prop_number_create_unsigned_integer(val); + if (pn == NULL) + return ENOMEM; + + if (prop_dictionary_set(dict, key, pn) == false) { + prop_object_release(pn); + return ENOMEM; + } + + prop_object_release(pn); + return 0; +} + +int +conn_local_server(const char *sockfile, int socktype, int nonblock __unused, + int *retsock) +{ + int s; + struct sockaddr_un serv_addr; + + *retsock = -1; + if ((s = socket(AF_UNIX, socktype, 0)) < 0) + return -1; + + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sun_family = AF_UNIX; + strncpy(serv_addr.sun_path, sockfile, SOCKFILE_NAMELEN); + serv_addr.sun_path[SOCKFILE_NAMELEN - 1] = '\0'; + + *retsock = s; + return connect(s, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); +} + +prop_dictionary_t +udevd_get_command_dict(char *command) +{ + prop_dictionary_t dict; + int error; + + dict = prop_dictionary_create(); + if (dict == NULL) + return NULL; + + if ((error = _udev_dict_set_cstr(dict, "command", command))) + goto error_out; + + return dict; + +error_out: + prop_object_release(dict); + return NULL; +} + +prop_array_t +udevd_request_devs(int s, prop_array_t filters) +{ + prop_array_t pa; + prop_dictionary_t dict; + char *xml; + + int n; + + dict = udevd_get_command_dict(__DECONST(char *, "getdevs")); + if (dict == NULL) + return NULL; + + /* Add filters to message, if available */ + if (filters != NULL) { + if (prop_dictionary_set(dict, "filters", filters) == false) { + prop_object_release(dict); + return NULL; + } + } + + xml = prop_dictionary_externalize(dict); + prop_object_release(dict); + if (xml == NULL) + return NULL; + + n = send_xml(s, xml); + free(xml); + + if (n <= 0) + return NULL; + + xml = malloc(12*1024*1024); /* generous 12 MB */ + if ((n = read_xml(s, xml, 12*1024*1024)) <= 0) { + free(xml); + return NULL; + } + + xml[n+1] = '\0'; + pa = prop_array_internalize(xml); + free(xml); + return (pa); +} diff --git a/lib/libdevattr/devattr.h b/lib/libdevattr/devattr.h new file mode 100644 index 0000000000..21bd7f8b2d --- /dev/null +++ b/lib/libdevattr/devattr.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2010 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Alex Hornung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include + +struct udev; +struct udev_enumerate; +struct udev_list_entry; +struct udev_monitor; +struct udev_device; + +struct udev *udev_new(void); +struct udev *udev_ref(struct udev *udev_ctx); +void udev_unref(struct udev *udev_ctx); +const char *udev_get_dev_path(struct udev *udev_ctx); +void *udev_get_userdata(struct udev *udev_ctx); +void udev_set_userdata(struct udev *udev_ctx, void *userdata); +int udev_get_fd(struct udev *udev_ctx); + +struct udev_device *udev_device_new_from_dictionary(struct udev *udev_ctx, prop_dictionary_t dict); +struct udev_device *udev_device_ref(struct udev_device *udev_device); +void udev_device_unref(struct udev_device *udev_device); +prop_dictionary_t udev_device_get_dictionary(struct udev_device *udev_device); +struct udev *udev_device_get_udev(struct udev_device *udev_device); +void udev_device_set_action(struct udev_device *udev_device, int action); +const char *udev_device_get_action(struct udev_device *udev_device); +dev_t udev_device_get_devnum(struct udev_device *udev_device); +const char *udev_device_get_devnode(struct udev_device *udev_device); +const char *udev_device_get_property_value(struct udev_device *udev_device, + const char *key); +const char *udev_device_get_subsystem(struct udev_device *udev_device); +const char *udev_device_get_driver(struct udev_device *udev_device); +uint64_t udev_device_get_kptr(struct udev_device *udev_device); +int32_t udev_device_get_major(struct udev_device *udev_device); +int32_t udev_device_get_minor(struct udev_device *udev_device); + +struct udev_enumerate *udev_enumerate_new(struct udev *udev_ctx); +struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enum); +void udev_enumerate_unref(struct udev_enumerate *udev_enum); +struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enum); +int udev_enumerate_scan_devices(struct udev_enumerate *udev_enum); +struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enum); +#define udev_list_entry_foreach(list_entry, first_entry) \ + for(list_entry = first_entry; \ + list_entry != NULL; \ + list_entry = udev_list_entry_get_next(list_entry)) +prop_array_t udev_enumerate_get_array(struct udev_enumerate *udev_enum); +struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry); +prop_dictionary_t udev_list_entry_get_dictionary(struct udev_list_entry *list_entry); +struct udev_device *udev_list_entry_get_device(struct udev_list_entry *list_entry); +int _udev_enumerate_filter_add_match_gen(struct udev_enumerate *udev_enum, + int type, + int neg, + const char *key, + char *expr); +int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enum, + const char *subsystem); +int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enum, + const char *subsystem); +int udev_enumerate_add_match_expr(struct udev_enumerate *udev_enum, + const char *key, + char *expr); +int udev_enumerate_add_nomatch_expr(struct udev_enumerate *udev_enum, + const char *key, + char *expr); +int udev_enumerate_add_match_regex(struct udev_enumerate *udev_enum, + const char *key, + char *expr); +int udev_enumerate_add_nomatch_regex(struct udev_enumerate *udev_enum, + const char *key, + char *expr); +#define udev_enumerate_add_match_property(a, b) \ + udev_enumerate_add_match_expr((a), __DECONST(char *, (b))) + +#define udev_enumerate_add_nomatch_property(a, b) \ + udev_enumerate_add_nomatch_expr((a), __DECONST(char *, (b))) + +struct udev_monitor *udev_monitor_new(struct udev *udev_ctx); +struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor); +void udev_monitor_unref(struct udev_monitor *udev_monitor); +struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor); +int udev_monitor_get_fd(struct udev_monitor *udev_monitor); +struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor); +int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor); +int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, + const char *subsystem, + const char *devtype); +int udev_monitor_filter_add_match_expr(struct udev_monitor *udev_monitor, + const char *key, + char *expr); +int udev_monitor_filter_add_nomatch_expr(struct udev_monitor *udev_monitor, + const char *key, + char *expr); +int udev_monitor_filter_add_match_regex(struct udev_monitor *udev_monitor, + const char *key, + char *expr); +int udev_monitor_filter_add_nomatch_regex(struct udev_monitor *udev_monitor, + const char *key, + char *expr); +int _udev_monitor_filter_add_match_gen(struct udev_monitor *udev_monitor, + int type, + int neg, + const char *key, + char *expr); +int _udev_filter_add_match_gen(prop_array_t filters, + int type, + int neg, + const char *key, + char *expr); + +int send_xml(int s, char *xml); +int read_xml(int s, char *buf, size_t buf_sz); +int _udev_dict_set_cstr(prop_dictionary_t dict, const char *key, char *str); +int _udev_dict_set_int(prop_dictionary_t dict, const char *key, int64_t val); +int _udev_dict_set_uint(prop_dictionary_t dict, const char *key, uint64_t val); +int conn_local_server(const char *sockfile, int socktype, int nonblock, + int *retsock); +prop_dictionary_t udevd_get_command_dict(char *command); +prop_array_t udevd_request_devs(int s, prop_array_t filters); diff --git a/lib/libdevattr/devattr_device.c b/lib/libdevattr/devattr_device.c new file mode 100644 index 0000000000..ae754b4367 --- /dev/null +++ b/lib/libdevattr/devattr_device.c @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2010 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Alex Hornung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "devattr.h" + +struct udev_device { + struct udev *udev_ctx; + prop_dictionary_t dict; + int ev_type; + int refs; +}; + +struct udev_device * +udev_device_new_from_dictionary(struct udev *udev_ctx, prop_dictionary_t dict) +{ + struct udev_device *udev_dev; + + udev_dev = malloc(sizeof(struct udev_device)); + if (udev_dev == NULL) + return NULL; + + udev_dev->refs = 1; + udev_dev->ev_type = UDEV_EVENT_NONE; + + if (dict != NULL) + prop_object_retain(dict); + + udev_dev->dict = dict; + udev_dev->udev_ctx = udev_ref(udev_ctx); + + return udev_dev; +} + +struct udev_device * +udev_device_ref(struct udev_device *udev_device) +{ + atomic_add_int(&udev_device->refs, 1); + + return udev_device; +} + +void +udev_device_unref(struct udev_device *udev_device) +{ + int refcount; + + refcount = atomic_fetchadd_int(&udev_device->refs, -1); + + if (refcount == 1) { + atomic_subtract_int(&udev_device->refs, 0x400); /* in destruction */ + if (udev_device->dict != NULL) + prop_object_release(udev_device->dict); + + udev_unref(udev_device->udev_ctx); + free(udev_device); + } +} + +void +udev_device_set_action(struct udev_device *udev_device, int action) +{ + udev_device->ev_type = action; +} + +const char * +udev_device_get_action(struct udev_device *udev_device) +{ + const char *action; + + switch (udev_device->ev_type) { + case UDEV_EVENT_ATTACH: + action = "add"; + break; + + case UDEV_EVENT_DETACH: + action = "remove"; + break; + + default: + action = "none"; + break; + } + + return action; +} + +dev_t +udev_device_get_devnum(struct udev_device *udev_device) +{ + prop_number_t pn; + dev_t devnum; + + if (udev_device->dict == NULL) + return 0; + + pn = prop_dictionary_get(udev_device->dict, "devnum"); + if (pn == NULL) + return 0; + + devnum = prop_number_unsigned_integer_value(pn); + + return devnum; +} + +uint64_t +udev_device_get_kptr(struct udev_device *udev_device) +{ + prop_number_t pn; + uint64_t kptr; + + if (udev_device->dict == NULL) + return 0; + + pn = prop_dictionary_get(udev_device->dict, "kptr"); + if (pn == NULL) + return 0; + + kptr = prop_number_unsigned_integer_value(pn); + + return kptr; +} + +int32_t +udev_device_get_major(struct udev_device *udev_device) +{ + prop_number_t pn; + int32_t major; + + if (udev_device->dict == NULL) + return 0; + + pn = prop_dictionary_get(udev_device->dict, "major"); + if (pn == NULL) + return 0; + + major = (int32_t)prop_number_integer_value(pn); + + return major; +} + +int32_t +udev_device_get_minor(struct udev_device *udev_device) +{ + prop_number_t pn; + int32_t minor; + + if (udev_device->dict == NULL) + return 0; + + pn = prop_dictionary_get(udev_device->dict, "minor"); + if (pn == NULL) + return 0; + + minor = (int32_t)prop_number_integer_value(pn); + + return minor; +} + +const char * +udev_device_get_devnode(struct udev_device *udev_device) +{ + dev_t devnum; + + devnum = udev_device_get_devnum(udev_device); + if (devnum == 0) + return 0; + + return devname(devnum, S_IFCHR); +} + +const char * +udev_device_get_property_value(struct udev_device *udev_device, + const char *key) +{ + prop_object_t po; + prop_number_t pn; + prop_string_t ps; + static char buf[128]; /* XXX: might cause trouble */ + const char *str = NULL; + + if (udev_device->dict == NULL) + return NULL; + + if ((po = prop_dictionary_get(udev_device->dict, key)) == NULL) + return NULL; + + if (prop_object_type(po) == PROP_TYPE_STRING) { + ps = po; + str = __DECONST(char *, prop_string_cstring_nocopy(ps)); + } else if (prop_object_type(po) == PROP_TYPE_NUMBER) { + pn = po; + if (prop_number_unsigned(pn)) { + snprintf(buf, sizeof(buf), "%" PRIu64, prop_number_unsigned_integer_value(pn)); + } else { + snprintf(buf, sizeof(buf), "%" PRIi64, prop_number_integer_value(pn)); + } + str = buf; + } + return str; +} + +const char * +udev_device_get_subsystem(struct udev_device *udev_device) +{ + return udev_device_get_property_value(udev_device, "subsystem"); +} + +const char * +udev_device_get_driver(struct udev_device *udev_device) +{ + return udev_device_get_property_value(udev_device, "driver"); +} + +prop_dictionary_t +udev_device_get_dictionary(struct udev_device *udev_device) +{ + return udev_device->dict; +} + +struct udev * +udev_device_get_udev(struct udev_device *udev_device) +{ + return udev_device->udev_ctx; +} + diff --git a/lib/libdevattr/devattr_enumerate.c b/lib/libdevattr/devattr_enumerate.c new file mode 100644 index 0000000000..34921923fd --- /dev/null +++ b/lib/libdevattr/devattr_enumerate.c @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2010 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Alex Hornung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "devattr.h" + +struct udev_enumerate { + struct udev *udev_ctx; + prop_array_t ev_filt; + prop_array_t pa; + int refs; + TAILQ_HEAD(, udev_list_entry) list_entries; +}; + +struct udev_list_entry { + struct udev *udev_ctx; + prop_dictionary_t dict; + TAILQ_ENTRY(udev_list_entry) link; +}; + +struct udev_enumerate * +udev_enumerate_new(struct udev *udev_ctx) +{ + struct udev_enumerate *udev_enum; + + udev_enum = malloc(sizeof(struct udev_enumerate)); + + udev_enum->refs = 1; + udev_enum->ev_filt = NULL; + udev_enum->pa = NULL; + TAILQ_INIT(&udev_enum->list_entries); + udev_enum->udev_ctx = udev_ref(udev_ctx); + + return udev_enum; +} + +struct udev_enumerate * +udev_enumerate_ref(struct udev_enumerate *udev_enum) +{ + atomic_add_int(&udev_enum->refs, 1); + + return udev_enum; +} + +void +udev_enumerate_unref(struct udev_enumerate *udev_enum) +{ + struct udev_list_entry *le; + int refcount; + + refcount = atomic_fetchadd_int(&udev_enum->refs, -1); + + if (refcount == 1) { + atomic_subtract_int(&udev_enum->refs, 0x400); /* in destruction */ + if (udev_enum->pa != NULL) + prop_object_release(udev_enum->pa); + if (udev_enum->ev_filt != NULL) + prop_object_release(udev_enum->ev_filt); + + while (!TAILQ_EMPTY(&udev_enum->list_entries)) { + le = TAILQ_FIRST(&udev_enum->list_entries); + TAILQ_REMOVE(&udev_enum->list_entries, le, link); + prop_object_release(le->dict); + free(le); + } + udev_unref(udev_enum->udev_ctx); + free(udev_enum); + } +} + +struct udev * +udev_enumerate_get_udev(struct udev_enumerate *udev_enum) +{ + return udev_enum->udev_ctx; +} + +int +udev_enumerate_scan_devices(struct udev_enumerate *udev_enum) +{ + prop_array_t pa; + + if (udev_get_fd(udev_enum->udev_ctx) == -1) + return -1; + + pa = udevd_request_devs(udev_get_fd(udev_enum->udev_ctx), udev_enum->ev_filt); + if (pa == NULL) + return -1; + + prop_object_retain(pa); + + if (udev_enum->pa != NULL) + prop_object_release(udev_enum->pa); + + udev_enum->pa = pa; + + return 0; +} + +struct udev_list_entry * +udev_enumerate_get_list_entry(struct udev_enumerate *udev_enum) +{ + struct udev_list_entry *le; + prop_object_iterator_t iter; + prop_dictionary_t dict; + + /* If the list is not empty, assume it was populated in an earlier call */ + if (!TAILQ_EMPTY(&udev_enum->list_entries)) + return TAILQ_FIRST(&udev_enum->list_entries); + + iter = prop_array_iterator(udev_enum->pa); + if (iter == NULL) + return NULL; + + while ((dict = prop_object_iterator_next(iter)) != NULL) { + le = malloc(sizeof(struct udev_list_entry)); + if (le == NULL) + goto out; + + prop_object_retain(dict); + le->dict = dict; + le->udev_ctx = udev_enum->udev_ctx; + TAILQ_INSERT_TAIL(&udev_enum->list_entries, le, link); + } + + le = TAILQ_FIRST(&udev_enum->list_entries); + +out: + prop_object_iterator_release(iter); + return le; +} + +prop_array_t +udev_enumerate_get_array(struct udev_enumerate *udev_enum) +{ + return udev_enum->pa; +} + +struct udev_list_entry * +udev_list_entry_get_next(struct udev_list_entry *list_entry) +{ + return TAILQ_NEXT(list_entry, link); +} + +prop_dictionary_t +udev_list_entry_get_dictionary(struct udev_list_entry *list_entry) +{ + return list_entry->dict; +} + +struct udev_device * +udev_list_entry_get_device(struct udev_list_entry *list_entry) +{ + struct udev_device *udev_dev; + + udev_dev = udev_device_new_from_dictionary(list_entry->udev_ctx, + list_entry->dict); + + return udev_dev; +} + +int +udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enum, + const char *subsystem) +{ + int ret; + + ret = _udev_enumerate_filter_add_match_gen(udev_enum, + EVENT_FILTER_TYPE_WILDCARD, + 0, + "subsystem", + __DECONST(char *, subsystem)); + + return ret; +} + +int +udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enum, + const char *subsystem) +{ + int ret; + + ret = _udev_enumerate_filter_add_match_gen(udev_enum, + EVENT_FILTER_TYPE_WILDCARD, + 1, + "subsystem", + __DECONST(char *, subsystem)); + + return ret; +} + +int +udev_enumerate_add_match_expr(struct udev_enumerate *udev_enum, + const char *key, + char *expr) +{ + int ret; + + ret = _udev_enumerate_filter_add_match_gen(udev_enum, + EVENT_FILTER_TYPE_WILDCARD, + 0, + key, + expr); + + return ret; +} + +int +udev_enumerate_add_nomatch_expr(struct udev_enumerate *udev_enum, + const char *key, + char *expr) +{ + int ret; + + ret = _udev_enumerate_filter_add_match_gen(udev_enum, + EVENT_FILTER_TYPE_WILDCARD, + 1, + key, + expr); + + return ret; +} + +int +udev_enumerate_add_match_regex(struct udev_enumerate *udev_enum, + const char *key, + char *expr) +{ + int ret; + + ret = _udev_enumerate_filter_add_match_gen(udev_enum, + EVENT_FILTER_TYPE_REGEX, + 0, + key, + expr); + + return ret; +} + +int +udev_enumerate_add_nomatch_regex(struct udev_enumerate *udev_enum, + const char *key, + char *expr) +{ + int ret; + + ret = _udev_enumerate_filter_add_match_gen(udev_enum, + EVENT_FILTER_TYPE_REGEX, + 1, + key, + expr); + + return ret; +} + +int +_udev_enumerate_filter_add_match_gen(struct udev_enumerate *udev_enum, + int type, + int neg, + const char *key, + char *expr) +{ + prop_array_t pa; + int error; + + if (udev_enum->ev_filt == NULL) { + pa = prop_array_create_with_capacity(5); + if (pa == NULL) + return -1; + + udev_enum->ev_filt = pa; + } + + error = _udev_filter_add_match_gen(udev_enum->ev_filt, type, neg, key, expr); + + return error; +} diff --git a/lib/libdevattr/devattr_monitor.c b/lib/libdevattr/devattr_monitor.c new file mode 100644 index 0000000000..b6dded5d1a --- /dev/null +++ b/lib/libdevattr/devattr_monitor.c @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2010 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Alex Hornung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "devattr.h" + +struct udev_monitor { + struct udev *udev_ctx; + prop_array_t ev_filt; + int socket; + int user_socket; /* maybe... one day... */ + int refs; +}; + +struct udev_monitor * +udev_monitor_new(struct udev *udev_ctx) +{ + struct udev_monitor *udev_monitor; + int ret, s; + + ret = conn_local_server(LISTEN_SOCKET_FILE, SOCK_STREAM, 0, &s); + if (ret < 0) + return NULL; + + udev_monitor = malloc(sizeof(struct udev_monitor)); + if (udev_monitor == NULL) + return NULL; + + udev_monitor->refs = 1; + udev_monitor->ev_filt = NULL; + udev_monitor->socket = s; + udev_monitor->user_socket = 1; + udev_monitor->udev_ctx = udev_ref(udev_ctx); + + return udev_monitor; +} + + +struct udev_monitor * +udev_monitor_ref(struct udev_monitor *udev_monitor) +{ + atomic_add_int(&udev_monitor->refs, 1); + + return udev_monitor; +} + +void +udev_monitor_unref(struct udev_monitor *udev_monitor) +{ + int refcount; + + refcount = atomic_fetchadd_int(&udev_monitor->refs, -1); + + if (refcount == 1) { + atomic_subtract_int(&udev_monitor->refs, 0x400); /* in destruction */ + if (udev_monitor->ev_filt != NULL) + prop_object_release(udev_monitor->ev_filt); + + if (udev_monitor->socket != -1) + close(udev_monitor->socket); + if (udev_monitor->user_socket != -1) + close(udev_monitor->user_socket); + + udev_unref(udev_monitor->udev_ctx); + free(udev_monitor); + } +} + +struct udev * +udev_monitor_get_udev(struct udev_monitor *udev_monitor) +{ + return udev_monitor->udev_ctx; +} + +int +udev_monitor_get_fd(struct udev_monitor *udev_monitor) +{ + return udev_monitor->socket; +} + +struct udev_device * +udev_monitor_receive_device(struct udev_monitor *udev_monitor) +{ + struct udev_device *udev_dev; + prop_dictionary_t dict, evdict; + prop_number_t pn; + char *xml; + int n; + + xml = malloc(12*1024*1024); + if (xml == NULL) + return NULL; + + if ((n = read_xml(udev_monitor->socket, xml, 12*1024*1024)) <= 0) { + free(xml); + return NULL; + } + + xml[n+1] = '\0'; + dict = prop_dictionary_internalize(xml); + free(xml); + if (dict == NULL) + return NULL; + + pn = prop_dictionary_get(dict, "evtype"); + if (pn == NULL) { + prop_object_release(dict); + return NULL; + } + + evdict = prop_dictionary_get(dict, "evdict"); + if (evdict == NULL) { + prop_object_release(dict); + return NULL; + } + + udev_dev = udev_device_new_from_dictionary(udev_monitor->udev_ctx, evdict); + if (udev_dev == NULL) { + prop_object_release(dict); + return NULL; + } + + udev_device_set_action(udev_dev, prop_number_integer_value(pn)); + + prop_object_release(dict); + return udev_dev; +} + +int +udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) +{ + prop_dictionary_t dict; + char *xml; + int n; + /* ->socket, ->user_socket, ->ev_filt */ + + dict = udevd_get_command_dict(__DECONST(char *, "monitor")); + if (dict == NULL) + return -1; + + /* Add event filters to message, if available */ + if (udev_monitor->ev_filt != NULL) { + if (prop_dictionary_set(dict, "filters", + udev_monitor->ev_filt) == false) { + prop_object_release(dict); + return -1; + } + } + + xml = prop_dictionary_externalize(dict); + prop_object_release(dict); + if (xml == NULL) + return -1; + + n = send_xml(udev_monitor->socket, xml); + free(xml); + if (n <= 0) + return -1; + + return 0; +} + +int +udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, + const char *subsystem, + const char *devtype __unused) +{ + int ret; + + ret = _udev_monitor_filter_add_match_gen(udev_monitor, + EVENT_FILTER_TYPE_WILDCARD, + 0, + "subsystem", + __DECONST(char *, subsystem)); + + return ret; +} + +int +udev_monitor_filter_add_match_expr(struct udev_monitor *udev_monitor, + const char *key, + char *expr) +{ + int ret; + + ret = _udev_monitor_filter_add_match_gen(udev_monitor, + EVENT_FILTER_TYPE_WILDCARD, + 0, + key, + expr); + + return ret; +} + +int +udev_monitor_filter_add_nomatch_expr(struct udev_monitor *udev_monitor, + const char *key, + char *expr) +{ + int ret; + + ret = _udev_monitor_filter_add_match_gen(udev_monitor, + EVENT_FILTER_TYPE_WILDCARD, + 1, + key, + expr); + + return ret; +} + +int +udev_monitor_filter_add_match_regex(struct udev_monitor *udev_monitor, + const char *key, + char *expr) +{ + int ret; + + ret = _udev_monitor_filter_add_match_gen(udev_monitor, + EVENT_FILTER_TYPE_REGEX, + 0, + key, + expr); + + return ret; +} + +int +udev_monitor_filter_add_nomatch_regex(struct udev_monitor *udev_monitor, + const char *key, + char *expr) +{ + int ret; + + ret = _udev_monitor_filter_add_match_gen(udev_monitor, + EVENT_FILTER_TYPE_REGEX, + 1, + key, + expr); + + return ret; +} + +int +_udev_filter_add_match_gen(prop_array_t filters, + int type, + int neg, + const char *key, + char *expr) +{ + prop_dictionary_t dict; + int error; + + if (key == NULL) + return -1; + if (expr == NULL) + return -1; + + dict = prop_dictionary_create(); + if (dict == NULL) + return -1; + + error = _udev_dict_set_cstr(dict, "key", __DECONST(char *, key)); + if (error != 0) + goto error_out; + error = _udev_dict_set_int(dict, "type", type); + if (error != 0) + goto error_out; + error = _udev_dict_set_cstr(dict, "expr", expr); + if (error != 0) + goto error_out; + + if (neg) { + error = _udev_dict_set_int(dict, "negative", 1); + if (error != 0) + goto error_out; + } + + if (prop_array_add(filters, dict) == false) + goto error_out; + + return 0; + +error_out: + prop_object_release(dict); + return -1; +} + +int +_udev_monitor_filter_add_match_gen(struct udev_monitor *udev_monitor, + int type, + int neg, + const char *key, + char *expr) +{ + prop_array_t pa; + int error; + + if (udev_monitor->ev_filt == NULL) { + pa = prop_array_create_with_capacity(5); + if (pa == NULL) + return -1; + + udev_monitor->ev_filt = pa; + } + + error = _udev_filter_add_match_gen(udev_monitor->ev_filt, type, neg, key, expr); + + return error; +} + diff --git a/lib/libdevattr/devattr_test.c b/lib/libdevattr/devattr_test.c new file mode 100644 index 0000000000..3d543ce46b --- /dev/null +++ b/lib/libdevattr/devattr_test.c @@ -0,0 +1,83 @@ +#include +#include +#include "devattr.h" + +int main(void) +{ + struct udev *udev; + struct udev_enumerate *udev_enum; + struct udev_list_entry *udev_le, *udev_le_first; + struct udev_monitor *udev_monitor; + struct udev_device *udev_dev; + prop_array_t pa; + prop_dictionary_t dict; + int ret; + + printf("1\n"); + udev = udev_new(); + printf("2\n"); + udev_enum = udev_enumerate_new(udev); + printf("3\n"); + + ret = udev_enumerate_add_match_expr(udev_enum, "name", "da*"); + if (ret != 0) + err(1, "udev_enumerate_add_match_expr, out, ret=%d\n", ret); + + ret = udev_enumerate_add_match_regex(udev_enum, "name", "ad.*"); + if (ret != 0) + err(1, "udev_enumerate_add_match_regex, out, ret=%d\n", ret); + + ret = udev_enumerate_scan_devices(udev_enum); + printf("4\n"); + if (ret != 0) { + printf("4 out...\n"); + err(1, "udev_enumerate_scan_device ret = %d", ret); + } + + printf("5\n"); + udev_le_first = udev_enumerate_get_list_entry(udev_enum); + printf("6\n"); + if (udev_le_first == NULL) + err(1, "udev_enumerate_get_list_entry error"); + + printf("7\n"); + pa = udev_enumerate_get_array(udev_enum); + printf("8\n"); + prop_array_externalize_to_file(pa, "array_out.xml"); + printf("9\n"); + udev_list_entry_foreach(udev_le, udev_le_first) { + dict = udev_list_entry_get_dictionary(udev_le); + printf("xml: %s\n\n\n", prop_dictionary_externalize(dict)); + } + + udev_enumerate_unref(udev_enum); + + udev_monitor = udev_monitor_new(udev); +#if 1 + ret = udev_monitor_filter_add_match_regex(udev_monitor, "name", "vn.*"); + if (ret != 0) + err(1, "udev_monitor_filter_add_match_expr ret = %d", ret); +#endif +#if 0 + ret = udev_monitor_filter_add_nomatch_expr(udev_monitor, "name", "vn*"); + if (ret != 0) + err(1, "udev_monitor_filter_add_match_expr2 ret = %d", ret); +#endif + ret = udev_monitor_enable_receiving(udev_monitor); + if (ret != 0) + err(1, "udev_monitor_enable_receiving ret = %d", ret); + + printf("meeh\n"); + while ((udev_dev = udev_monitor_receive_device(udev_monitor))) { + printf("mooh\n"); + if (udev_dev == NULL) + err(1, "udev_monitor_receive_device failed"); + + printf("foo\n"); + dict = udev_device_get_dictionary(udev_dev); + printf("xml of new device: %s\n", prop_dictionary_externalize(dict)); + } + udev_monitor_unref(udev_monitor); + udev_unref(udev); + return 0; +} diff --git a/lib/libdevattr/mktest b/lib/libdevattr/mktest new file mode 100644 index 0000000000..df633c42a3 --- /dev/null +++ b/lib/libdevattr/mktest @@ -0,0 +1,7 @@ +DEBUG_FLAGS=-g +PROG= devattr_test +SRCS= devattr_test.c +LDADD= -lprop -L. -ldevattr +NOMAN= + +.include diff --git a/sys/conf/files b/sys/conf/files index 18fe2fbd7b..802f183b53 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -691,6 +691,7 @@ kern/kern_mpipe.c standard kern/kern_shutdown.c standard kern/kern_sig.c standard kern/kern_memio.c standard +kern/kern_udev.c standard kern/kern_upcall.c standard kern/kern_sfbuf.c standard kern/kern_mplock.c standard diff --git a/sys/dev/drm/i915_drv.h b/sys/dev/drm/i915_drv.h index 8d907bfdff..711fc689ec 100644 --- a/sys/dev/drm/i915_drv.h +++ b/sys/dev/drm/i915_drv.h @@ -557,7 +557,7 @@ extern void opregion_enable_asle(struct drm_device *dev); LOCK_TEST_WITH_RETURN(dev, file_priv); \ } while (0) -#if defined(__FreeBSD__) || defined(__DragonFly__) +#if defined(__FreeBSD__) typedef boolean_t bool; #endif diff --git a/sys/kern/kern_udev.c b/sys/kern/kern_udev.c new file mode 100644 index 0000000000..e6a0579f42 --- /dev/null +++ b/sys/kern/kern_udev.c @@ -0,0 +1,747 @@ +/* + * Copyright (c) 2010 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Alex Hornung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MALLOC_DEFINE(M_UDEV, "udev", "udev allocs"); + +/* XXX: use UUIDs for identification; would need help from devfs */ + +static cdev_t udev_dev; +static d_open_t udev_dev_open; +static d_close_t udev_dev_close; +static d_read_t udev_dev_read; +static d_poll_t udev_dev_poll; +static d_ioctl_t udev_dev_ioctl; + +static int _udev_dict_set_cstr(prop_dictionary_t, const char *, char *); +static int _udev_dict_set_int(prop_dictionary_t, const char *, int64_t); +static int _udev_dict_set_uint(prop_dictionary_t, const char *, uint64_t); +static int _udev_dict_delete_key(prop_dictionary_t, const char *); +static prop_dictionary_t udev_init_dict_event(cdev_t, const char *); +static int udev_init_dict(cdev_t); +static int udev_destroy_dict(cdev_t); +static void udev_event_insert(int, prop_dictionary_t); +static struct udev_event_kernel *udev_event_remove(void); +static void udev_event_free(struct udev_event_kernel *); +static char *udev_event_externalize(struct udev_event_kernel *); +static void udev_getdevs_scan_callback(cdev_t, void *); +static int udev_getdevs_ioctl(struct plistref *, u_long, prop_dictionary_t); + +struct cmd_function { + const char *cmd; + int (*fn)(struct plistref *, u_long, prop_dictionary_t); +}; + +struct udev_prop_ctx { + prop_array_t cdevs; + int error; +}; + +struct udev_event_kernel { + struct udev_event ev; + TAILQ_ENTRY(udev_event_kernel) link; +}; + +struct udev_softc { + int opened; + int initiated; + + struct selinfo sel; + + int qlen; + struct lock lock; + TAILQ_HEAD(, udev_event_kernel) ev_queue; /* list of thread_io */ +} udevctx; + +static struct dev_ops udev_dev_ops = { + { "udev", 0, 0 }, + .d_open = udev_dev_open, + .d_close = udev_dev_close, + .d_read = udev_dev_read, + .d_poll = udev_dev_poll, + .d_ioctl = udev_dev_ioctl +}; + +struct cmd_function cmd_fn[] = { + { .cmd = "getdevs", .fn = udev_getdevs_ioctl}, + {NULL, NULL} +}; + +static int +_udev_dict_set_cstr(prop_dictionary_t dict, const char *key, char *str) +{ + prop_string_t ps; + + KKASSERT(dict != NULL); + + ps = prop_string_create_cstring(str); + if (ps == NULL) + return ENOMEM; + + if (prop_dictionary_set(dict, key, ps) == false) { + prop_object_release(ps); + return ENOMEM; + } + + prop_object_release(ps); + return 0; +} + +static int +_udev_dict_set_int(prop_dictionary_t dict, const char *key, int64_t val) +{ + prop_number_t pn; + + KKASSERT(dict != NULL); + + pn = prop_number_create_integer(val); + if (pn == NULL) + return ENOMEM; + + if (prop_dictionary_set(dict, key, pn) == false) { + prop_object_release(pn); + return ENOMEM; + } + + prop_object_release(pn); + return 0; +} + +static int +_udev_dict_set_uint(prop_dictionary_t dict, const char *key, uint64_t val) +{ + prop_number_t pn; + + KKASSERT(dict != NULL); + + pn = prop_number_create_unsigned_integer(val); + if (pn == NULL) + return ENOMEM; + + if (prop_dictionary_set(dict, key, pn) == false) { + prop_object_release(pn); + return ENOMEM; + } + + prop_object_release(pn); + return 0; +} + +static int +_udev_dict_delete_key(prop_dictionary_t dict, const char *key) +{ + KKASSERT(dict != NULL); + + prop_dictionary_remove(dict, key); + + return 0; +} + +/* + * Initialize an event dictionary, which contains three parameters to + * identify the device referred to (name, devnum, kptr) and the affected key. + */ +static prop_dictionary_t +udev_init_dict_event(cdev_t dev, const char *key) +{ + prop_dictionary_t dict; + uint64_t kptr; + int error; + + kptr = (uint64_t)(uintptr_t)dev; + KKASSERT(dev != NULL); + + dict = prop_dictionary_create(); + if (dict == NULL) { + log(LOG_DEBUG, "udev_init_dict_event: prop_dictionary_create() failed\n"); + return NULL; + } + + if ((error = _udev_dict_set_cstr(dict, "name", dev->si_name))) + goto error_out; + if ((error = _udev_dict_set_uint(dict, "devnum", dev->si_inode))) + goto error_out; + if ((error = _udev_dict_set_uint(dict, "kptr", kptr))) + goto error_out; + if ((error = _udev_dict_set_cstr(dict, "key", __DECONST(char *, key)))) + goto error_out; + +error_out: + prop_object_release(dict); + return NULL; +} + +int +udev_dict_set_cstr(cdev_t dev, const char *key, char *str) +{ + prop_dictionary_t dict; + int error; + + KKASSERT(dev != NULL); + + /* Queue a key update event */ + dict = udev_init_dict_event(dev, key); + if (dict == NULL) + return ENOMEM; + if ((error = _udev_dict_set_cstr(dict, "value", str))) { + prop_object_release(dict); + return error; + } + udev_event_insert(UDEV_EV_KEY_UPDATE, dict); + prop_object_release(dict); + + return _udev_dict_set_cstr(dev->si_dict, key, str); +} + +int +udev_dict_set_int(cdev_t dev, const char *key, int64_t val) +{ + prop_dictionary_t dict; + int error; + + KKASSERT(dev != NULL); + + /* Queue a key update event */ + dict = udev_init_dict_event(dev, key); + if (dict == NULL) + return ENOMEM; + if ((error = _udev_dict_set_int(dict, "value", val))) { + prop_object_release(dict); + return error; + } + udev_event_insert(UDEV_EV_KEY_UPDATE, dict); + prop_object_release(dict); + + return _udev_dict_set_int(dev->si_dict, key, val); +} + +int +udev_dict_set_uint(cdev_t dev, const char *key, uint64_t val) +{ + prop_dictionary_t dict; + int error; + + KKASSERT(dev != NULL); + + /* Queue a key update event */ + dict = udev_init_dict_event(dev, key); + if (dict == NULL) + return ENOMEM; + if ((error = _udev_dict_set_uint(dict, "value", val))) { + prop_object_release(dict); + return error; + } + udev_event_insert(UDEV_EV_KEY_UPDATE, dict); + prop_object_release(dict); + + return _udev_dict_set_uint(dev->si_dict, key, val); +} + +int +udev_dict_delete_key(cdev_t dev, const char *key) +{ + prop_dictionary_t dict; + + KKASSERT(dev != NULL); + + /* Queue a key removal event */ + dict = udev_init_dict_event(dev, key); + if (dict == NULL) + return ENOMEM; + udev_event_insert(UDEV_EV_KEY_REMOVE, dict); + prop_object_release(dict); + + return _udev_dict_delete_key(dev->si_dict, key); +} + +static int +udev_init_dict(cdev_t dev) +{ + prop_dictionary_t dict; + uint64_t kptr; + int error; + + kptr = (uint64_t)(uintptr_t)dev; + + KKASSERT(dev != NULL); + dict = prop_dictionary_create(); + if (dict == NULL) { + log(LOG_DEBUG, "udev_init_dict: prop_dictionary_create() failed\n"); + return ENOMEM; + } + + if ((error = _udev_dict_set_cstr(dict, "name", dev->si_name))) + goto error_out; + if ((error = _udev_dict_set_uint(dict, "devnum", dev->si_inode))) + goto error_out; + if ((error = _udev_dict_set_uint(dict, "kptr", kptr))) + goto error_out; + + /* XXX: The next 3 are marginallly useful, if at all */ + if ((error = _udev_dict_set_uint(dict, "uid", dev->si_uid))) + goto error_out; + if ((error = _udev_dict_set_uint(dict, "gid", dev->si_gid))) + goto error_out; + if ((error = _udev_dict_set_int(dict, "mode", dev->si_perms))) + goto error_out; + + if ((error = _udev_dict_set_int(dict, "major", umajor(dev->si_inode)))) + goto error_out; + if ((error = _udev_dict_set_int(dict, "minor", dev->si_uminor))) + goto error_out; + + dev->si_dict = dict; + return 0; + +error_out: + dev->si_dict = NULL; + prop_object_release(dict); + return error; +} + +static int +udev_destroy_dict(cdev_t dev) +{ + KKASSERT(dev != NULL); + + if (dev->si_dict != NULL) { + prop_object_release(dev->si_dict); + dev->si_dict = NULL; + } + + return 0; +} + +static void +udev_event_insert(int ev_type, prop_dictionary_t dict) +{ + struct udev_event_kernel *ev; + + /* Only start queing events after client has initiated properly */ + if (!udevctx.initiated) + return; + + /* XXX: use objcache eventually */ + ev = kmalloc(sizeof(*ev), M_UDEV, M_WAITOK); + ev->ev.ev_dict = prop_dictionary_copy(dict); + if (ev->ev.ev_dict == NULL) { + kfree(ev, M_UDEV); + return; + } + ev->ev.ev_type = ev_type; + + lockmgr(&udevctx.lock, LK_EXCLUSIVE); + TAILQ_INSERT_TAIL(&udevctx.ev_queue, ev, link); + ++udevctx.qlen; + lockmgr(&udevctx.lock, LK_RELEASE); + + wakeup(&udevctx); + selwakeup(&udevctx.sel); +} + +static struct udev_event_kernel * +udev_event_remove() +{ + struct udev_event_kernel *ev; + + lockmgr(&udevctx.lock, LK_EXCLUSIVE); + if (TAILQ_EMPTY(&udevctx.ev_queue)) { + lockmgr(&udevctx.lock, LK_RELEASE); + return NULL; + } + + ev = TAILQ_FIRST(&udevctx.ev_queue); + TAILQ_REMOVE(&udevctx.ev_queue, ev, link); + --udevctx.qlen; + lockmgr(&udevctx.lock, LK_RELEASE); + + return ev; +} + +static void +udev_event_free(struct udev_event_kernel *ev) +{ + /* XXX: use objcache eventually */ + kfree(ev, M_UDEV); +} + +static char * +udev_event_externalize(struct udev_event_kernel *ev) +{ + prop_dictionary_t dict; + char *xml; + int error; + + + dict = prop_dictionary_create(); + if (dict == NULL) { + log(LOG_DEBUG, "udev_event_externalize: prop_dictionary_create() failed\n"); + return NULL; + } + + if ((error = _udev_dict_set_int(dict, "evtype", ev->ev.ev_type))) { + prop_object_release(dict); + return NULL; + } + + if (prop_dictionary_set(dict, "evdict", ev->ev.ev_dict) == false) { + prop_object_release(dict); + return NULL; + } + + prop_object_release(ev->ev.ev_dict); + + xml = prop_dictionary_externalize(dict); + + prop_object_release(dict); + + return xml; +} + +int +udev_event_attach(cdev_t dev, char *name, int alias) +{ + prop_dictionary_t dict; + int error; + + KKASSERT(dev != NULL); + + reference_dev(dev); + + error = udev_init_dict(dev); + if (error) + goto error_out; + + if (alias) { + dict = prop_dictionary_copy(dev->si_dict); + if (dict == NULL) + goto error_out; + + if ((error = _udev_dict_set_cstr(dict, "name", name))) { + prop_object_release(dict); + goto error_out; + } + + _udev_dict_set_int(dict, "alias", 1); + + udev_event_insert(UDEV_EVENT_ATTACH, dict); + prop_object_release(dict); + } else { + _udev_dict_set_int(dev->si_dict, "alias", 0); + udev_event_insert(UDEV_EVENT_ATTACH, dev->si_dict); + } + +error_out: + release_dev(dev); + return error; +} + +int +udev_event_detach(cdev_t dev, char *name, int alias) +{ + prop_dictionary_t dict; + + KKASSERT(dev != NULL); + + reference_dev(dev); + + if (alias) { + dict = prop_dictionary_copy(dev->si_dict); + if (dict == NULL) + goto error_out; + + if (_udev_dict_set_cstr(dict, "name", name)) { + prop_object_release(dict); + goto error_out; + } + + _udev_dict_set_int(dict, "alias", 1); + + udev_event_insert(UDEV_EVENT_DETACH, dict); + prop_object_release(dict); + } else { + udev_event_insert(UDEV_EVENT_DETACH, dev->si_dict); + } + +error_out: + udev_destroy_dict(dev); + + release_dev(dev); + + return 0; +} + +/* + * dev stuff + */ +static int +udev_dev_open(struct dev_open_args *ap) +{ + if (udevctx.opened) + return EBUSY; + + udevctx.opened = 1; + + return 0; +} + +static int +udev_dev_close(struct dev_close_args *ap) +{ + udevctx.opened = 0; + udevctx.initiated = 0; + wakeup(&udevctx); + + return 0; +} + +static int +udev_dev_poll(struct dev_poll_args *ap) +{ + int revents = 0; + + lockmgr(&udevctx.lock, LK_EXCLUSIVE); + if (ap->a_events & (POLLIN | POLLRDNORM)) { + if (!TAILQ_EMPTY(&udevctx.ev_queue)) + revents = ap->a_events & (POLLIN | POLLRDNORM); + else + selrecord(curthread, &udevctx.sel); + } + lockmgr(&udevctx.lock, LK_RELEASE); + + ap->a_events = revents; + return 0; +} + +static int +udev_dev_read(struct dev_read_args *ap) +{ + struct udev_event_kernel *ev; + struct uio *uio = ap->a_uio; + char *xml; + size_t len; + int error; + + + lockmgr(&udevctx.lock, LK_EXCLUSIVE); + + for (;;) { + if ((ev = udev_event_remove()) != NULL) { + if ((xml = udev_event_externalize(ev)) == NULL) { + lockmgr(&udevctx.lock, LK_RELEASE); + return ENOMEM; + } + + len = strlen(xml) + 1; /* account for NULL-termination */ + if (uio->uio_resid < len) { + error = ENOMEM; + } else { + error = uiomove((caddr_t)xml, len, uio); + } + + kfree(xml, M_TEMP); + udev_event_free(ev); + lockmgr(&udevctx.lock, LK_RELEASE); + return error; + } + + if ((error = lksleep(&udevctx, &udevctx.lock, 0, "udevq", 0))) { + lockmgr(&udevctx.lock, LK_RELEASE); + return error; + } + } + + lockmgr(&udevctx.lock, LK_RELEASE); + +} + +static int +udev_dev_ioctl(struct dev_ioctl_args *ap) +{ + prop_dictionary_t dict; + prop_object_t po; + prop_string_t ps; + struct plistref *pref; + int i, error; + + error = 0; + + switch(ap->a_cmd) { + case UDEVPROP: + /* Use proplib(3) for userspace/kernel communication */ + pref = (struct plistref *)ap->a_data; + error = prop_dictionary_copyin_ioctl(pref, ap->a_cmd, &dict); + if (error) + return error; + + po = prop_dictionary_get(dict, "command"); + if (po == NULL || prop_object_type(po) != PROP_TYPE_STRING) { + log(LOG_DEBUG, "udev: prop_dictionary_get() failed\n"); + prop_object_release(dict); + return EINVAL; + } + + ps = po; + /* Handle cmd */ + for(i = 0; cmd_fn[i].cmd != NULL; i++) { + if (prop_string_equals_cstring(ps, cmd_fn[i].cmd)) + break; + } + + if (cmd_fn[i].cmd != NULL) { + log(LOG_DEBUG, "udev: ioctl %s called\n", cmd_fn[i].cmd); + error = cmd_fn[i].fn(pref, ap->a_cmd, dict); + } else { + error = EINVAL; + } + + //prop_object_release(po); + kprintf("foo\n"); + prop_object_release(dict); + break; + default: + error = ENOTTY; /* Inappropriate ioctl for device */ + break; + } + + return(error); +} + +static void +udev_getdevs_scan_callback(cdev_t cdev, void *arg) +{ + struct udev_prop_ctx *ctx = arg; + + KKASSERT(arg != NULL); + + if (cdev->si_dict == NULL) + return; + + if (prop_array_add(ctx->cdevs, cdev->si_dict) == false) { + ctx->error = EINVAL; + return; + } +} + +static int +udev_getdevs_ioctl(struct plistref *pref, u_long cmd, prop_dictionary_t dict) +{ + prop_dictionary_t odict; + struct udev_prop_ctx ctx; + int error; + + ctx.error = 0; + ctx.cdevs = prop_array_create(); + if (ctx.cdevs == NULL) { + log(LOG_DEBUG, "udev_getdevs_ioctl: prop_array_create() failed\n"); + return EINVAL; + } + + /* XXX: need devfs_scan_alias_callback() */ + devfs_scan_callback(udev_getdevs_scan_callback, &ctx); + + if (ctx.error != 0) { + prop_object_release(ctx.cdevs); + return (ctx.error); + } + udevctx.initiated = 1; + + odict = prop_dictionary_create(); + if (odict == NULL) { + return ENOMEM; + } + + if ((prop_dictionary_set(odict, "array", ctx.cdevs)) == 0) { + log(LOG_DEBUG, "udev_getdevs_ioctl: prop_dictionary_set failed\n"); + prop_object_release(odict); + return ENOMEM; + } + + error = prop_dictionary_copyout_ioctl(pref, cmd, odict); + + /* XXX: need to release ctx.cdevs? */ + prop_object_release(odict); + return error; +} + + +/* + * SYSINIT stuff + */ +static void +udev_init(void) +{ + lockinit(&udevctx.lock, "udevevq", 0, LK_CANRECURSE); + TAILQ_INIT(&udevctx.ev_queue); +} + +static void +udev_uninit(void) +{ +} + +static void +udev_dev_init(void) +{ + udev_dev = make_dev(&udev_dev_ops, + 0, + UID_ROOT, + GID_WHEEL, + 0600, + "udev"); +} + +static void +udev_dev_uninit(void) +{ + destroy_dev(udev_dev); +} + +SYSINIT(subr_udev_register, SI_SUB_CREATE_INIT, SI_ORDER_ANY, udev_init, NULL); +SYSUNINIT(subr_udev_register, SI_SUB_CREATE_INIT, SI_ORDER_ANY, udev_uninit, NULL); +SYSINIT(subr_udev_dev_register, SI_SUB_DRIVERS, SI_ORDER_ANY, udev_dev_init, NULL); +SYSUNINIT(subr_udev_dev_register, SI_SUB_DRIVERS, SI_ORDER_ANY, udev_dev_uninit, NULL); diff --git a/sys/kern/vfs_conf.c b/sys/kern/vfs_conf.c index 5628474398..626a22c374 100644 --- a/sys/kern/vfs_conf.c +++ b/sys/kern/vfs_conf.c @@ -456,12 +456,17 @@ end: } -static void vfs_mountroot_ask_callback(cdev_t); +static void +vfs_mountroot_ask_callback(cdev_t dev, void *arg __unused) +{ + if (dev_is_good(dev) && (dev_dflags(dev) & D_DISK)) + kprintf(" \"%s\" ", dev->si_name); +} + /* * Spin prompting on the console for a suitable root filesystem */ - static int vfs_mountroot_ask(void) { @@ -483,7 +488,7 @@ vfs_mountroot_ask(void) } else if (name[0] == '?') { kprintf("Possibly valid devices for root FS:\n"); //enumerate all disk devices - devfs_scan_callback(vfs_mountroot_ask_callback); + devfs_scan_callback(vfs_mountroot_ask_callback, NULL); kprintf("\n"); continue; } else if (strcmp(name, "panic") == 0) { @@ -498,14 +503,6 @@ vfs_mountroot_ask(void) } -static void -vfs_mountroot_ask_callback(cdev_t dev) -{ - if (dev_is_good(dev) && (dev_dflags(dev) & D_DISK)) - kprintf(" \"%s\" ", dev->si_name); -} - - static int getline(char *cp, int limit) { diff --git a/sys/sys/conf.h b/sys/sys/conf.h index 7990156a13..b69c04fe7f 100644 --- a/sys/sys/conf.h +++ b/sys/sys/conf.h @@ -57,6 +57,7 @@ #ifndef _SYS_SYSREF_H_ #include #endif +#include #define SPECNAMELEN 63 @@ -100,6 +101,7 @@ struct cdev { time_t si_lastread; /* time_second */ time_t si_lastwrite; /* time_second */ struct vm_object *si_object; /* vm_pager support */ + prop_dictionary_t si_dict; }; #define SI_UNUSED01 0x0001 diff --git a/sys/sys/devfs.h b/sys/sys/devfs.h index 63a5f00e6e..03c8a97a5f 100644 --- a/sys/sys/devfs.h +++ b/sys/sys/devfs.h @@ -173,6 +173,7 @@ typedef struct devfs_msg { } __m_chandler; struct { void *load; + void *load2; } __m_gen; struct { void *resp; @@ -218,6 +219,7 @@ typedef struct devfs_msg { #define mdv_chandler __m_u.__m_chandler #define mdv_mnt __m_u.__m_mnt.mnt #define mdv_load __m_u.__m_gen.load +#define mdv_load2 __m_u.__m_gen.load2 #define mdv_response __m_u.__m_resp.resp #define mdv_dev __m_u.__m_dev #define mdv_link __m_u.__m_link @@ -242,7 +244,7 @@ TAILQ_HEAD(devfs_chandler_head, devfs_clone_handler); TAILQ_HEAD(devfs_alias_head, devfs_alias); TAILQ_HEAD(devfs_dev_ops_head, devfs_dev_ops); -typedef void (devfs_scan_t)(cdev_t); +typedef void (devfs_scan_t)(cdev_t, void *); typedef void* (devfs_iterate_callback_t)(struct devfs_node *, void *); #define DEVFS_NODE(x) ((struct devfs_node *)((x)->v_data)) @@ -411,7 +413,7 @@ int devfs_alias_create(char *, struct devfs_node *, int); int devfs_apply_rules(char *); int devfs_reset_rules(char *); -int devfs_scan_callback(devfs_scan_t *); +int devfs_scan_callback(devfs_scan_t *, void *); int devfs_clr_subnames_flag(char *, uint32_t); int devfs_destroy_subnames_without_flag(char *, uint32_t); diff --git a/sys/sys/udev.h b/sys/sys/udev.h new file mode 100644 index 0000000000..13f87a9073 --- /dev/null +++ b/sys/sys/udev.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2010 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Alex Hornung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef _SYS_UDEV_H_ +#define _SYS_UDEV_H_ + +#if defined(_KERNEL) + +#ifndef _SYS_QUEUE_H_ +#include +#endif +#ifndef _SYS_CONF_H_ +#include +#endif + +int udev_dict_set_cstr(cdev_t dev, const char *key, char *str); +int udev_dict_set_int(cdev_t dev, const char *key, int64_t val); +int udev_dict_set_uint(cdev_t dev, const char *key, uint64_t val); +int udev_dict_delete_key(cdev_t dev, const char *key); + +int udev_event_attach(cdev_t dev, char *name, int alias); +int udev_event_detach(cdev_t dev, char *name, int alias); + +#endif /* _KERNEL */ + +#define UDEVPROP _IOWR('U', 0xBA, struct plistref) +#define UDEV_EVENT_NONE 0x00 +#define UDEV_EVENT_ATTACH 0x01 +#define UDEV_EVENT_DETACH 0x02 + +#define UDEV_EV_KEY_UPDATE 0x11 +#define UDEV_EV_KEY_REMOVE 0x12 + +#define EVENT_FILTER_TYPE_WILDCARD 0 +#define EVENT_FILTER_TYPE_REGEX 1 + +#define LISTEN_SOCKET_FILE "/tmp/udevd.socket" +#define SOCKFILE_NAMELEN strlen(LISTEN_SOCKET_FILE)+1 + +struct udev_event { + int ev_type; + prop_dictionary_t ev_dict; +}; + +#endif /* _SYS_DSCHED_H_ */ diff --git a/sys/vfs/devfs/devfs_core.c b/sys/vfs/devfs/devfs_core.c index 9de7aba00b..3aea6efaf8 100644 --- a/sys/vfs/devfs/devfs_core.c +++ b/sys/vfs/devfs/devfs_core.c @@ -49,6 +49,7 @@ #include #include #include +#include MALLOC_DEFINE(M_DEVFS, "devfs", "Device File System (devfs) allocations"); DEVFS_DECLARE_CLONE_BITMAP(ops_id); @@ -127,7 +128,7 @@ static int devfs_find_device_by_udev_worker(devfs_msg_t); static int devfs_apply_reset_rules_caller(char *, int); -static int devfs_scan_callback_worker(devfs_scan_t *); +static int devfs_scan_callback_worker(devfs_scan_t *, void *); static struct devfs_node *devfs_resolve_or_create_dir(struct devfs_node *, char *, size_t, int); @@ -857,7 +858,7 @@ devfs_reset_rules(char *mntto) * It just sends a message with the relevant details to the devfs core. */ int -devfs_scan_callback(devfs_scan_t *callback) +devfs_scan_callback(devfs_scan_t *callback, void *arg) { devfs_msg_t msg; @@ -865,6 +866,7 @@ devfs_scan_callback(devfs_scan_t *callback) msg = devfs_msg_get(); msg->mdv_load = callback; + msg->mdv_load2 = arg; msg = devfs_msg_send_sync(DEVFS_SCAN_CALLBACK, msg); devfs_msg_put(msg); @@ -1140,7 +1142,8 @@ devfs_msg_exec(devfs_msg_t msg) devfs_apply_reset_rules_caller(msg->mdv_name, 0); break; case DEVFS_SCAN_CALLBACK: - devfs_scan_callback_worker((devfs_scan_t *)msg->mdv_load); + devfs_scan_callback_worker((devfs_scan_t *)msg->mdv_load, + msg->mdv_load2); break; case DEVFS_CLR_SUBNAMES_FLAG: devfs_clr_subnames_flag_worker(msg->mdv_flags.name, @@ -1190,6 +1193,8 @@ devfs_create_dev_worker(cdev_t dev, uid_t uid, gid_t gid, int perms) devfs_link_dev(dev); devfs_propagate_dev(dev, 1); + udev_event_attach(dev, NULL, 0); + return 0; } @@ -1208,6 +1213,9 @@ devfs_destroy_dev_worker(cdev_t dev) error = devfs_unlink_dev(dev); devfs_propagate_dev(dev, 0); + + udev_event_detach(dev, NULL, 0); + if (error == 0) release_dev(dev); /* link ref */ release_dev(dev); @@ -1450,6 +1458,7 @@ devfs_make_alias_worker(struct devfs_alias *alias) */ TAILQ_INSERT_TAIL(&devfs_alias_list, alias, link); devfs_alias_propagate(alias); + udev_event_attach(alias->dev_target, alias->name, 1); } else { devfs_debug(DEVFS_DEBUG_WARNING, "Warning: duplicate devfs_make_alias for %s\n", @@ -1488,6 +1497,7 @@ devfs_alias_remove(cdev_t dev) TAILQ_FOREACH_MUTABLE(alias, &devfs_alias_list, link, alias2) { if (alias->dev_target == dev) { TAILQ_REMOVE(&devfs_alias_list, alias, link); + udev_event_detach(alias->dev_target, alias->name, 1); kfree(alias, M_DEVFS); } } @@ -1646,12 +1656,12 @@ devfs_apply_reset_rules_caller(char *mountto, int apply) * every dev node in the devfs dev list. */ static int -devfs_scan_callback_worker(devfs_scan_t *callback) +devfs_scan_callback_worker(devfs_scan_t *callback, void *arg) { cdev_t dev, dev1; TAILQ_FOREACH_MUTABLE(dev, &devfs_dev_list, link, dev1) { - callback(dev); + callback(dev, arg); } return 0; diff --git a/usr.sbin/udevd/Makefile b/usr.sbin/udevd/Makefile new file mode 100644 index 0000000000..9e1c3f5b79 --- /dev/null +++ b/usr.sbin/udevd/Makefile @@ -0,0 +1,8 @@ +DEBUG_FLAGS=-g +PROG= udevd +SRCS= udevd.c udevd_client.c udevd_monitor.c udevd_pdev.c +SRCS+= udevd_socket.c +LDADD= -lprop -lpthread +NOMAN= + +.include diff --git a/usr.sbin/udevd/mktest b/usr.sbin/udevd/mktest new file mode 100644 index 0000000000..9a39224ef2 --- /dev/null +++ b/usr.sbin/udevd/mktest @@ -0,0 +1,7 @@ +DEBUG_FLAGS=-g +PROG= test_udevd +SRCS= test_udevd.c +LDADD= -lprop +NOMAN= + +.include diff --git a/usr.sbin/udevd/test_udevd.c b/usr.sbin/udevd/test_udevd.c new file mode 100644 index 0000000000..6564dfcd0b --- /dev/null +++ b/usr.sbin/udevd/test_udevd.c @@ -0,0 +1,770 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define LISTEN_SOCKET_FILE "/tmp/udevd.socket" +#define SOCKFILE_NAMELEN strlen(LISTEN_SOCKET_FILE)+1 + +int conn_local_server(const char *sockfile, int socktype, int nonblock, + int *retsock); +prop_dictionary_t udevd_get_command_dict(char *command); +void udevd_request_devs(int s); + +struct udev { + int gp_fd; + int monitor_fd; + int refs; + + void *userdata; +}; + +struct udev_enumerate { + struct udev *udev_ctx; + prop_array_t pa; + int refs; + TAILQ_HEAD(, udev_list_entry) list_entries; +}; + +struct udev_list_entry { + prop_dictionary_t dict; + TAILQ_ENTRY(udev_list_entry) link; +}; + +struct udev_monitor { + struct udev *udev_ctx; + prop_array_t ev_filt; + int socket; + int user_socket; /* maybe... one day... */ + int refs; +}; + +struct udev_device { + struct udev *udev_ctx; + prop_dictionary_t dict; + int ev_type; + int refs; +}; + +struct udev * +udev_ref(struct udev *udev_ctx) +{ + atomic_add_int(&udev_ctx->refs, 1); + + return udev_ctx; +} + +void +udev_unref(struct udev *udev_ctx) +{ + int refcount; + + refcount = atomic_fetchadd_int(&udev_ctx->refs, -1); + + if (refcount == 1) { + atomic_subtract_int(&udev_ctx->refs, 0x400); /* in destruction */ + if (udev_ctx->gp_fd != -1) + close (udev_ctx->gp_fd); + if (udev_ctx->monitor_fd != -1) + close (udev_ctx->monitor_fd); + + free(udev_ctx); + } +} + +struct udev * +udev_new() +{ + struct udev *udev_ctx; + + udev_ctx = malloc(sizeof(struct udev)); + + udev_ctx->refs = 1; + udev_ctx->gp_fd = -1; + udev_ctx->monitor_fd = -1; + udev_ctx->userdata = NULL; + + return udev_ctx; +} + +const char *udev_get_dev_path(struct udev *udev_ctx __unused) +{ + return "/dev"; +} + +void * +udev_get_userdata(struct udev *udev_ctx) +{ + return udev_ctx->userdata; +} + +void +udev_set_userdata(struct udev *udev_ctx, void *userdata) +{ + udev_ctx->userdata = userdata; +} + +struct udev_enumerate * +udev_enumerate_new(struct udev *udev_ctx) +{ + struct udev_enumerate *udev_enum; + + udev_enum = malloc(sizeof(struct udev_enumerate)); + + udev_enum->refs = 1; + udev_enum->pa = NULL; + TAILQ_INIT(&udev_enum->list_entries); + udev_enum->udev_ctx = udev_ref(udev_ctx); +} + +struct udev_enumerate * +udev_enumerate_ref(struct udev_enumerate *udev_enum) +{ + atomic_add_int(&udev_enum->refs, 1); + + return udev_enum; +} + +void +udev_enumerate_unref(struct udev_enumerate *udev_enum) +{ + struct udev_list_entry *le; + int refcount; + + refcount = atomic_fetchadd_int(&udev_enum->refs, -1); + + if (refcount == 1) { + atomic_subtract_int(&udev_enum->refs, 0x400); /* in destruction */ + if (udev_enum->pa != NULL) + prop_object_release(udev_enum->pa); + + while (!TAILQ_EMPTY(&udev_enum->list_entries)) { + le = TAILQ_FIRST(&udev_enum->list_entries); + TAILQ_REMOVE(&udev_enum->list_entries, le, link); + prop_object_release(le->dict); + free(le); + } + udev_unref(udev_enum->udev_ctx); + free(udev_enum); + } +} + +struct udev * +udev_enumerate_get_udev(struct udev_enumerate *udev_enum) +{ + return udev_enum->udev_ctx; +} + +int +udev_enumerate_scan_devices(struct udev_enumerate *udev_enum) +{ + prop_array_t pa; + + if (udev_enum->udev_ctx->gp_fd == -1) + return -1; + + pa = udevd_request_devs(udev_enum->udev_ctx->gp_fd); + if (pa == NULL) + return -1; + + prop_object_retain(pa); + + if (udev_enum->pa != NULL) + prop_object_release(udev_enum->pa); + + udev_enum->iter = NULL; + udev_enum->pa = pa; + + return 0; +} + +struct udev_list_entry * +udev_enumerate_get_list_entry(struct udev_enumerate *udev_enum) +{ + struct udev_list_entry *le; + prop_object_iterator_t iter; + + /* If the list is not empty, assume it was populated in an earlier call */ + if (!TAILQ_EMPTY(&udev_enum->list_entries)) + return TAILQ_FIRST(&udev_enum->list_entries); + + iter = prop_array_iterator(udev_enum->pa); + if (iter == NULL) + return NULL; + + while ((dict = prop_object_iterator_next(iter)) != NULL) { + le = malloc(sizeof(struct udev_list_entry)); + if (le == NULL) + goto out; + + prop_object_retain(dict); + le->dict = dict; + TAILQ_INSERT_TAIL(&udev_enum->list_entries, le, link); + } + + le = TAILQ_FIRST(&udev_enum->list_entries); + +out: + prop_object_iterator_release(iter); + return le; +} + +prop_array_t +udev_enumerate_get_array(struct udev_enumerate *udev_enum) +{ + return udev_enum->pa; +} + +struct udev_list_entry * +udev_list_entry_get_next(struct udev_list_entry *list_entry) +{ + return TAILQ_NEXT(list_entry, link); +} + +prop_dictionary_t +udev_list_entry_get_dictionary(struct udev_list_entry *list_entry) +{ + return list_entry->dict; +} + +#define udev_list_entry_foreach(list_entry, first_entry) \ + for(list_entry = first_entry; \ + list_entry != NULL; \ + list_entry = udev_list_entry_get_next(list_entry)) + + + + + + +struct udev_monitor * +udev_monitor_new(struct udev *udev_ctx) +{ + struct udev_monitor *udev_monitor; + int ret, s; + + ret = conn_local_server(LISTEN_SOCKET_FILE, SOCK_STREAM, 0, &s); + if (ret < 0) + return NULL; + + udev_monitor = malloc(sizeof(struct udev_monitor)); + if (udev_monitor == NULL) + return NULL; + + udev_monitor->refs = 1; + udev_monitor->ev_filt = NULL; + udev_monitor->socket = s; + udev_monitor->user_socket = 1; + udev_monitor->udev_ctx = udev_ref(udev_ctx); + + return udev_monitor; +} + + +struct udev_monitor * +udev_monitor_ref(struct udev_monitor *udev_monitor) +{ + atomic_add_int(&udev_monitor->refs, 1); + + return udev_monitor; +} + +void +udev_monitor_unref(struct udev_monitor *udev_monitor) +{ + int refcount; + + refcount = atomic_fetchadd_int(&udev_monitor->refs, -1); + + if (refcount == 1) { + atomic_subtract_int(&udev_monitor->refs, 0x400); /* in destruction */ + if (udev_monitor->ev_filt != NULL) + prop_object_release(udev_monitor->ev_filt); + + if (udev_monitor->socket != -1) + close(udev_monitor->socket); + if (udev_monitor->user_socket != -1) + close(udev_monitor->user_socket); + + udev_unref(udev_monitor->udev_ctx); + free(udev_monitor); + } +} + +struct udev * +udev_monitor_get_udev(struct udev_monitor *udev_monitor) +{ + return udev_monitor->udev_ctx; +} + +int +udev_monitor_get_fd(struct udev_monitor *udev_monitor) +{ + return udev_monitor->socket; +} + +struct udev_device * +udev_monitor_receive_device(struct udev_monitor *udev_monitor) +{ + struct udev_device *udev_dev; + prop_dictionary_t dict; + prop_number_t pn; + char *xml; + int n, evtype; + + xml = malloc(12*1024*1024); + if (xml == NULL) + return NULL; + + if ((n = read_xml(udev_monitor->socket, xml, 12*1024*1024)) <= 0) { + free(xml); + return NULL; + } + + xml[n+1] = '\0'; + dict = prop_dictionary_internalize(xml); + free(xml); + if (dict == NULL) + return NULL; + + pn = prop_dictionary_get(dict, "evtype"); + if (pn == NULL) { + prop_object_release(dict); + return NULL; + } + + udev_dev = malloc(sizeof(struct udev_dev)); + if (udev_dev == NULL) { + prop_object_release(dict); + return NULL; + } + + udev_dev->refs = 1; + udev_dev->ev_type = prop_number_integer_value(pn); + udev_dev->dict = prop_dictionary_get(dict, "evdict"); + if (udev_dev->dict == NULL) { + free(udev_dev); + return NULL; + } + udev_dev->udev_ctx = udev_ref(udev_monitor->udev_ctx); + +out: + return udev_dev; +} + +int +udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) +{ + prop_dictionary_t dict; + char *xml; + /* ->socket, ->user_socket, ->ev_filt */ + + dict = udevd_get_command_dict(__DECONST(char *, "monitor")); + if (dict == NULL) + return -1; + + /* Add event filters to message, if available */ + if (udev_monitor->ev_filt != NULL) { + if (prop_dictionary_set(dict, "filters", + udev_monitor->ev_filt) == false) { + prop_object_release(dict); + return -1; + } + } + + xml = prop_dictionary_externalize(dict); + prop_object_release(dict); + if (xml == NULL) + return -1; + + n = send_xml(udev_monitor->socket, xml); + free(xml); + if (n <= 0) + return NULL; + + return 0; +} + +int +udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, + const char *subsystem, + const char *devtype __unused) +{ + int ret; + + ret = _udev_monitor_filter_add_match_gen(udev_monitor, + EVENT_FILTER_TYPE_WILDCARD, + 0, + "subsystem", + subsystem); + + return ret; +} + +int +udev_monitor_filter_add_match_expr(struct udev_monitor *udev_monitor, + const char *key, + char *expr) +{ + int ret; + + ret = _udev_monitor_filter_add_match_gen(udev_monitor, + EVENT_FILTER_TYPE_WILDCARD, + 0, + key, + expr); + + return ret; +} + +int +udev_monitor_filter_add_nomatch_expr(struct udev_monitor *udev_monitor, + const char *key, + char *expr) +{ + int ret; + + ret = _udev_monitor_filter_add_match_gen(udev_monitor, + EVENT_FILTER_TYPE_WILDCARD, + 1, + key, + expr); + + return ret; +} + +int +udev_monitor_filter_add_match_regex(struct udev_monitor *udev_monitor, + const char *key, + char *expr) +{ + int ret; + + ret = _udev_monitor_filter_add_match_gen(udev_monitor, + EVENT_FILTER_TYPE_REGEX, + 0, + key, + expr); + + return ret; +} + +int +udev_monitor_filter_add_nomatch_regex(struct udev_monitor *udev_monitor, + const char *key, + char *expr) +{ + int ret; + + ret = _udev_monitor_filter_add_match_gen(udev_monitor, + EVENT_FILTER_TYPE_REGEX, + 1, + key, + expr); + + return ret; +} + +int +_udev_monitor_filter_add_match_gen(struct udev_monitor *udev_monitor, + int type, + int neg, + const char *key, + char *expr) +{ + prop_array_t pa; + prop_dictionary_t dict; + int error; + + if (subsystem == NULL) + return NULL; + + dict = prop_dictionary_create(); + if (dict == NULL) + return -1; + + error = _udev_dict_set_cstr(dict, "key", key); + if (error != 0) + goto error_out; + error = _udev_dict_set_int(dict, "type", type); + if (error != 0) + goto error_out; + error = _udev_dict_set_int(dict, "expr", expr); + if (error != 0) + goto error_out; + + if (neg) { + error = _udev_dict_set_int(dict, "negative", 1); + if (error != 0) + goto error_out; + } + + if (udev_monitor->ev_filt == NULL) { + pa = prop_array_create(); + if (pa == NULL) + goto error_out; + + udev_monitor->ev_filt = pa; + } + + if (prop_array_add(udev_monitor->ev_filt, dict) == false) + goto error_out; + + return 0; + +error_out: + prop_object_release(dict); + return -1; +} + +struct udev_device * +udev_device_ref(struct udev_device *udev_device) +{ + atomic_add_int(&udev_device->refs, 1); + + return udev_device; +} + +void +udev_device_unref(struct udev_device *udev_device) +{ + int refcount; + + refcount = atomic_fetchadd_int(&udev_device->refs, -1); + + if (refcount == 1) { + atomic_subtract_int(&udev_device->refs, 0x400); /* in destruction */ + if (udev_device->dict != NULL) + prop_object_release(udev_device->dict); + + udev_unref(udev_device->udev_ctx); + free(udev_device); + } +} + +prop_dictionary_t +udev_device_get_dictionary(struct udev_device *udev_device) +{ + return udev_device->dict; +} + +struct udev * +udev_device_get_udev(struct udev_device *udev_device) +{ + return udev_device->udev_ctx; +} + +int +send_xml(int s, char *xml) +{ + ssize_t r,n; + size_t sz; + + sz = strlen(xml) + 1; + + r = send(s, &sz, sizeof(sz), 0); + if (r <= 0) + return r; + + r = 0; + while (r < (ssize_t)sz) { + n = send(s, xml+r, sz-r, 0); + if (n <= 0) + return n; + r += n; + } + + return r; +} + +int +read_xml(int s, char *buf, size_t buf_sz) +{ + size_t sz; + int n, r; + + n = recv(s, &sz, sizeof(sz), MSG_WAITALL); + if (n <= 0) + return n; + + r = 0; + while ((r < (ssize_t)sz) && (r < (ssize_t)buf_sz)) { + n = recv(s, buf+r, sz-r, MSG_WAITALL); + if (n <= 0) + return n; + r += n; + } + + return r; +} + + + +static int +_udev_dict_set_cstr(prop_dictionary_t dict, const char *key, char *str) +{ + prop_string_t ps; + + ps = prop_string_create_cstring(str); + if (ps == NULL) + return ENOMEM; + + if (prop_dictionary_set(dict, key, ps) == false) { + prop_object_release(ps); + return ENOMEM; + } + + prop_object_release(ps); + return 0; +} + +static int +_udev_dict_set_int(prop_dictionary_t dict, const char *key, int64_t val) +{ + prop_number_t pn; + + pn = prop_number_create_integer(val); + if (pn == NULL) + return ENOMEM; + + if (prop_dictionary_set(dict, key, pn) == false) { + prop_object_release(pn); + return ENOMEM; + } + + prop_object_release(pn); + return 0; +} + +static int +_udev_dict_set_uint(prop_dictionary_t dict, const char *key, uint64_t val) +{ + prop_number_t pn; + + pn = prop_number_create_unsigned_integer(val); + if (pn == NULL) + return ENOMEM; + + if (prop_dictionary_set(dict, key, pn) == false) { + prop_object_release(pn); + return ENOMEM; + } + + prop_object_release(pn); + return 0; +} + +int +conn_local_server(const char *sockfile, int socktype, int nonblock, + int *retsock) +{ + int s; + struct sockaddr_un serv_addr; + + *retsock = -1; + if ((s = socket(AF_UNIX, socktype, 0)) < 0) + return -1; + + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sun_family = AF_UNIX; + strncpy(serv_addr.sun_path, sockfile, SOCKFILE_NAMELEN); + serv_addr.sun_path[SOCKFILE_NAMELEN - 1] = '\0'; + + if (nonblock && unblock_descriptor(s) < 0) { + close(s); + return -1; + } + + *retsock = s; + return connect(s, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); +} + +prop_dictionary_t +udevd_get_command_dict(char *command) +{ + prop_dictionary_t dict; + int error; + + dict = prop_dictionary_create(); + if (dict == NULL) + return NULL; + + if ((error = _udev_dict_set_cstr(dict, "command", command))) + goto error_out; + + return dict; + +error_out: + prop_object_release(dict); + return NULL; +} + +prop_array_t +udevd_request_devs(int s) +{ + prop_array_t pa; + prop_dictionary_t dict; + char *xml; + + int n, t; + + dict = udevd_get_command_dict(__DECONST(char *, "getdevs")); + if (dict == NULL) + return NULL; + + xml = prop_dictionary_externalize(dict); + prop_object_release(dict); + if (xml == NULL) + return NULL; + + n = send_xml(s, xml); + free(xml); + + if (n <= 0) + return NULL; + + xml = malloc(12*1024*1024); /* generous 12 MB */ + if ((n = read_xml(s, xml, 12*1024*1024)) <= 0) { + free(xml); + return NULL; + } + + xml[n+1] = '\0'; + pa = prop_array_internalize(xml); + free(xml); + return (pa); +} + + + +int +main(void) +{ + int ret, s; + + ret = conn_local_server(LISTEN_SOCKET_FILE, SOCK_STREAM, 0, &s); + if (ret < 0) + err(1, "conn_local_server"); + + udevd_request_devs(s); + + return 0; +} diff --git a/usr.sbin/udevd/udevd.c b/usr.sbin/udevd/udevd.c new file mode 100644 index 0000000000..bed883c755 --- /dev/null +++ b/usr.sbin/udevd/udevd.c @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2010 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Alex Hornung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "udevd.h" + +static int udevfd; + +extern pthread_mutex_t monitor_lock; +extern TAILQ_HEAD(udev_monitor_list_head, udev_monitor) udev_monitor_list; +extern TAILQ_HEAD(pdev_array_list_head, pdev_array_entry) pdev_array_list; + +int match_dev_dict(prop_dictionary_t, prop_dictionary_t); +prop_dictionary_t find_dev_dict(int64_t, prop_dictionary_t, int *); + +void udev_read_event(int); +prop_array_t udev_getdevs(int); + +int +match_dev_dict(prop_dictionary_t dict, prop_dictionary_t match_dict) +{ + prop_number_t pn, pn2; + prop_string_t ps, ps2; + + if (dict == NULL) + return 0; + + if ((ps = prop_dictionary_get(dict, "name")) == NULL) + return 0; + if ((ps2 = prop_dictionary_get(match_dict, "name")) == NULL) + return 0; + if (!prop_string_equals(ps, ps2)) + return 0; + + if ((pn = prop_dictionary_get(dict, "devnum")) == NULL) + return 0; + if ((pn2 = prop_dictionary_get(match_dict, "devnum")) == NULL) + return 0; + if (!prop_number_equals(pn, pn2)) + return 0; + + if ((pn = prop_dictionary_get(dict, "kptr")) == NULL) + return 0; + if ((pn2 = prop_dictionary_get(match_dict, "kptr")) == NULL) + return 0; + if (!prop_number_equals(pn, pn2)) + return 0; + + return 1; +} + +prop_dictionary_t +find_dev_dict(int64_t generation, prop_dictionary_t match_dict, int *idx) +{ + struct pdev_array_entry *pae; + prop_array_t pa; + prop_object_iterator_t iter; + prop_dictionary_t dict; + int i = 0; + + if (generation == -1) + pae = pdev_array_entry_get_last(); + else + pae = pdev_array_entry_get(generation); + + if (pae == NULL) + return NULL; + + pa = pae->pdev_array; + + iter = prop_array_iterator(pa); + if (iter == NULL) { + pdev_array_entry_unref(pae); + return NULL; + } + + while ((dict = prop_object_iterator_next(iter)) != NULL) { + if (match_dev_dict(dict, match_dict)) + break; + ++i; + } + + prop_object_iterator_release(iter); + + if (idx != NULL) + *idx = i; + + pdev_array_entry_unref(pae); + return dict; +} + +void +udev_read_event(int fd) +{ + struct pdev_array_entry *pae; + prop_dictionary_t dict, evdict, devdict; + prop_number_t pn; + prop_string_t ps; + prop_object_t po; + prop_array_t pa; + char *xml; + int n, idx, evtype; + size_t sz; + + sz = 4096 * 1024; + + xml = malloc(sz); /* 4 MB */ +again: + if ((n = read(fd, xml, sz)) <= 0) { + if (errno == ENOMEM) { + sz <<= 2; + realloc(xml, sz); + goto again; + } + free(xml); + return; + } + + dict = prop_dictionary_internalize(xml); + free(xml); + if (dict == NULL) { + syslog(LOG_ERR, "internalization of xml failed"); + return; + } + + pn = prop_dictionary_get(dict, "evtype"); + if (pn == NULL) { + syslog(LOG_ERR, "read_event: no key evtype"); + goto out; + } + + evtype = prop_number_integer_value(pn); + + evdict = prop_dictionary_get(dict, "evdict"); + if (evdict == NULL) { + syslog(LOG_ERR, "read_event: no key evdict"); + goto out; + } + + switch (evtype) { + case UDEV_EVENT_ATTACH: + monitor_queue_event(dict); + pae = pdev_array_entry_get_last(); + pa = prop_array_copy(pae->pdev_array); + pdev_array_entry_unref(pae); + if (pa == NULL) + goto out; + prop_array_add(pa, evdict); + pdev_array_entry_insert(pa); + break; + + case UDEV_EVENT_DETACH: + monitor_queue_event(dict); + if ((devdict = find_dev_dict(-1, evdict, &idx)) == NULL) + goto out; + pae = pdev_array_entry_get_last(); + pa = prop_array_copy(pae->pdev_array); + pdev_array_entry_unref(pae); + if (pa == NULL) + goto out; + prop_array_remove(pa, idx); + //pdev_array_entry_insert(pa); + break; + + case UDEV_EV_KEY_UPDATE: + if ((devdict = find_dev_dict(-1, evdict, NULL)) == NULL) + goto out; + if ((ps = prop_dictionary_get(evdict, "key")) == NULL) + goto out; + if ((po = prop_dictionary_get(evdict, "value")) == NULL) + goto out; + /* prop_object_retain(po); */ /* not necessary afaik */ + prop_dictionary_set(devdict, prop_string_cstring_nocopy(ps), po); + break; + + case UDEV_EV_KEY_REMOVE: + if ((devdict = find_dev_dict(-1, evdict, NULL)) == NULL) + goto out; + if ((ps = prop_dictionary_get(evdict, "key")) == NULL) + goto out; + prop_dictionary_remove(devdict, prop_string_cstring_nocopy(ps)); + break; + + default: + syslog(LOG_ERR, "read_event: unknown evtype %d", evtype); + } + +out: + prop_object_release(dict); + return; +} + +prop_array_t +udev_getdevs(int devfd) +{ + prop_dictionary_t pd, rpd; + prop_string_t ps; + prop_array_t pa; + + pd = prop_dictionary_create(); + if (pd == NULL) { + err(1, "prop_dictionary_create()"); + } + + ps = prop_string_create_cstring("getdevs"); + if (ps == NULL) { + prop_object_release(pd); + err(1, "prop_string_create_cstring()"); + } + + if (prop_dictionary_set(pd, "command", ps) == false) { + prop_object_release(ps); + prop_object_release(pd); + err(1, "prop_dictionary_set()"); + } + + prop_object_release(ps); + + /* Send dictionary to kernel space */ + if (prop_dictionary_sendrecv_ioctl(pd, devfd, UDEVPROP, &rpd) != 0) + err(1, "prop_array_recv_ioctl()"); + + prop_object_release(pd); + + pa = prop_dictionary_get(rpd, "array"); + if (pa == NULL) + goto out; + prop_object_retain(pa); + +out: + prop_object_release(rpd); + return pa; +} + +int +ignore_signal(int signum) +{ + struct sigaction act; + int ret; + + act.sa_handler = SIG_IGN; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + + ret = sigaction(signum, &act, NULL); + return ret; +} + +int main(int argc __unused, char *argv[] __unused) +{ + int error __unused, i, r, s; + struct pollfd fds[NFDS]; + + TAILQ_INIT(&pdev_array_list); + TAILQ_INIT(&udev_monitor_list); + + r = ignore_signal(SIGPIPE); + if (r != 0) + err(1, "could not ignore_signal SIGPIPE"); + + r = pthread_mutex_init(&(monitor_lock), NULL); + if (r != 0) + err(1, "could not allocate a pthread_mutex"); + + if ((udevfd = open(UDEV_DEVICE_PATH, O_RDWR | O_NONBLOCK)) == -1) + err(1, "%s", UDEV_DEVICE_PATH); + unblock_descriptor(udevfd); + + //if (daemon(0, 0) == -1) + // err(1, "daemon"); + + syslog(LOG_ERR, "udevd started"); + + pdev_array_entry_insert(udev_getdevs(udevfd)); + + s = init_local_server(LISTEN_SOCKET_FILE, SOCK_STREAM, 0); + if (s < 0) + err(1, "init_local_server"); + + memset(fds, 0 , sizeof(fds)); + fds[UDEV_DEVICE_FD_IDX].fd = udevfd; + fds[UDEV_DEVICE_FD_IDX].events = POLLIN; + fds[UDEV_SOCKET_FD_IDX].fd = s; + fds[UDEV_SOCKET_FD_IDX].events = POLLIN | POLLPRI; + + for (;;) { + r = poll(fds, NFDS, -1); + if (r < 0) + err(1, "polling..."); + + for (i = 0; (i < NFDS) && (r > 0); i++) { + if (fds[i].revents == 0) + continue; + + --r; + switch (i) { + case UDEV_DEVICE_FD_IDX: + udev_read_event(udevfd); + break; + case UDEV_SOCKET_FD_IDX: + handle_new_connection(s); + break; + default: + break; + } + } + } + + return 0; +} diff --git a/usr.sbin/udevd/udevd.h b/usr.sbin/udevd/udevd.h new file mode 100644 index 0000000000..cc19122b08 --- /dev/null +++ b/usr.sbin/udevd/udevd.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2010 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Alex Hornung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#define UDEV_DEVICE_PATH "/dev/udev" +#define LOCAL_BACKLOG 5 + +#define UDEV_DEVICE_FD_IDX 0 +#define UDEV_SOCKET_FD_IDX 1 +#define NFDS 2 + + +struct pdev_array_entry { + int32_t refs; + int64_t generation; + prop_array_t pdev_array; + TAILQ_ENTRY(pdev_array_entry) link; +}; + +struct event_filter { + int id; + int neg; + int type; + char *key; + char *wildcard_match; + regex_t regex_match; + + TAILQ_ENTRY(event_filter) link; +}; + +struct udev_monitor; + +struct client_info { + pthread_t tid; + int fd; + struct udev_monitor *udm; + struct event_filter ev_filt; +}; + +struct udev_monitor_event { + prop_dictionary_t ev_dict; + TAILQ_ENTRY(udev_monitor_event) link; +}; + +struct udev_monitor { + pthread_mutex_t q_lock; + pthread_cond_t cond; + struct client_info *cli; + TAILQ_HEAD(, event_filter) ev_filt; + TAILQ_HEAD(, udev_monitor_event) ev_queue; + TAILQ_ENTRY(udev_monitor) link; +}; + +struct cmd_function { + const char *cmd; + int (*fn)(struct client_info *, prop_dictionary_t); +}; + +/* From udevd_socket.c */ +int init_local_server(const char *sockfile, int socktype, int nonblock); +int block_descriptor(int s); +int unblock_descriptor(int s); +int read_xml(int s, char *buf, size_t buf_sz); +int send_xml(int s, char *xml); + +/* From udevd_pdev.c */ +void pdev_array_entry_ref(struct pdev_array_entry *pae); +void pdev_array_entry_unref(struct pdev_array_entry *pae); +void pdev_array_entry_insert(prop_array_t pa); +struct pdev_array_entry *pdev_array_entry_get(int64_t generation); +struct pdev_array_entry *pdev_array_entry_get_last(void); + +/* From udevd_client.c */ +void handle_new_connection(int s); +int client_cmd_getdevs(struct client_info *cli, prop_dictionary_t dict); + + +/* From udevd_monitor.c */ +void monitor_queue_event(prop_dictionary_t ev_dict); +int client_cmd_monitor(struct client_info *cli, prop_dictionary_t dict); +int match_event_filter(struct udev_monitor *udm, prop_dictionary_t ev_dict); +struct udev_monitor *udev_monitor_init(struct client_info *cli, prop_array_t filters); +void udev_monitor_free(struct udev_monitor *udm); + +/* From udevd.c */ +int ignore_signal(int signum); diff --git a/usr.sbin/udevd/udevd_client.c b/usr.sbin/udevd/udevd_client.c new file mode 100644 index 0000000000..b53983b5fe --- /dev/null +++ b/usr.sbin/udevd/udevd_client.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2010 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Alex Hornung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "udevd.h" + +struct cmd_function cmd_fn[] = { + { .cmd = "getdevs", .fn = client_cmd_getdevs}, + { .cmd = "monitor", .fn = client_cmd_monitor}, + {NULL, NULL} +}; + +static void *client_thread(void *arg); + +void +handle_new_connection(int s) +{ + struct client_info *cli_info; + struct sockaddr_un addr; + int fd; + size_t saddr_len = sizeof(struct sockaddr_un); + + fd = accept(s, (struct sockaddr *)&addr, &saddr_len); + if (fd < 0) { + syslog(LOG_ERR, "uh, oh, accept failed with %d", errno); + return; + } + + block_descriptor(fd); + cli_info = malloc(sizeof(struct client_info)); + memset(cli_info, 0, sizeof(struct client_info)); + + cli_info->fd = fd; + pthread_create(&cli_info->tid, NULL, client_thread, (void *)cli_info); +} + + +static void * +client_thread(void *arg) +{ + prop_dictionary_t dict; + prop_string_t ps; + prop_object_t po; + struct client_info *cli; + char *xml; + int r, n, error; + + r = ignore_signal(SIGPIPE); + if (r != 0) + err(1, "could not ignore_signal SIGPIPE"); + + cli = (struct client_info *)arg; + xml = malloc(12*1024*1024); /* generous 12 MB */ + for (;;) { + n = read_xml(cli->fd, xml, 12*1024*1024); + if (n == 0) + goto cli_disconnect; + else if (n < 0) + goto error_out; + + xml[n+1] = '\0'; + + dict = prop_dictionary_internalize(xml); + if (dict == NULL) { + syslog(LOG_ERR, "internalization of received XML failed"); + goto error_out; + } + + po = prop_dictionary_get(dict, "command"); + if (po == NULL || prop_object_type(po) != PROP_TYPE_STRING) { + syslog(LOG_ERR, "received dictionary doesn't contain a key 'command'"); + prop_object_release(dict); + continue; + } + + ps = po; + + syslog(LOG_DEBUG, "Received command: %s (from fd = %d)\n", prop_string_cstring_nocopy(ps), cli->fd); + for(n = 0; cmd_fn[n].cmd != NULL; n++) { + if (prop_string_equals_cstring(ps, cmd_fn[n].cmd)) + break; + } + + if (cmd_fn[n].cmd != NULL) { + error = cmd_fn[n].fn(cli, dict); + if (error) { + prop_object_release(dict); + goto error_out; + } + } + prop_object_release(dict); + } + +error_out: + +cli_disconnect: + close(cli->fd); + free(xml); + cli->fd = -1; + free(cli); + return NULL; +} + +int +client_cmd_getdevs(struct client_info *cli, prop_dictionary_t cli_dict) +{ + struct pdev_array_entry *pae; + struct udev_monitor *udm; + prop_object_iterator_t iter; + prop_dictionary_t dict; + prop_object_t po; + prop_array_t pa; + char *xml; + ssize_t r; + int filters; + + + pa = NULL; + po = prop_dictionary_get(cli_dict, "filters"); + if ((po != NULL) && prop_object_type(po) == PROP_TYPE_ARRAY) { + pa = po; + filters = 1; + } else { + filters = 0; + } + + pae = pdev_array_entry_get_last(); + if (pae == NULL) + return 1; + + if (filters) { + udm = udev_monitor_init(cli, pa); + if (udm == NULL) { + pdev_array_entry_unref(pae); + return 1; + } + + pa = prop_array_create_with_capacity(10); + + iter = prop_array_iterator(pae->pdev_array); + if (iter == NULL) { + pdev_array_entry_unref(pae); + udev_monitor_free(udm); + return 1; + } + + while ((dict = prop_object_iterator_next(iter)) != NULL) { + if (match_event_filter(udm, dict)) { + prop_array_add(pa, dict); + } + } + + udev_monitor_free(udm); + } else { + pa = pae->pdev_array; + } + + xml = prop_array_externalize(pa); + if (filters) + prop_object_release(pa); + + pdev_array_entry_unref(pae); + + if (xml == NULL) + return 1; + + r = send_xml(cli->fd, xml); + if (r < 0) + syslog(LOG_DEBUG, "error while send_xml (cmd_getdevs)\n"); + if (r == 0) + syslog(LOG_DEBUG, "EOF while send_xml (cmd_getdevs)\n"); + + free(xml); + + return 0; +} diff --git a/usr.sbin/udevd/udevd_monitor.c b/usr.sbin/udevd/udevd_monitor.c new file mode 100644 index 0000000000..4dfcd4e495 --- /dev/null +++ b/usr.sbin/udevd/udevd_monitor.c @@ -0,0 +1,485 @@ +/* + * Copyright (c) 2010 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Alex Hornung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "udevd.h" + +#define MONITOR_LOCK() pthread_mutex_lock(&monitor_lock) +#define MONITOR_UNLOCK() pthread_mutex_unlock(&monitor_lock) + +static int _parse_filter_prop(struct udev_monitor *udm, prop_array_t pa); +static int match_filter(struct event_filter *evf, prop_dictionary_t dict); + +static int WildCaseCmp(const char *w, const char *s); +static int wildCaseCmp(const char **mary, int d, const char *w, const char *s); + +TAILQ_HEAD(udev_monitor_list_head, udev_monitor) udev_monitor_list; +pthread_mutex_t monitor_lock; + + +void +monitor_queue_event(prop_dictionary_t ev_dict) +{ + struct udev_monitor *udm; + struct udev_monitor_event *udm_ev; + + MONITOR_LOCK(); + + TAILQ_FOREACH(udm, &udev_monitor_list, link) { + udm_ev = malloc(sizeof(struct udev_monitor_event)); + if (udm_ev == NULL) + continue; + + prop_object_retain(ev_dict); + udm_ev->ev_dict = ev_dict; + + if (match_event_filter(udm, + prop_dictionary_get(udm_ev->ev_dict, "evdict")) == 0) { + prop_object_release(ev_dict); + free(udm_ev); + continue; + } + + pthread_mutex_lock(&udm->q_lock); + TAILQ_INSERT_TAIL(&udm->ev_queue, udm_ev, link); + pthread_cond_signal(&udm->cond); + pthread_mutex_unlock(&udm->q_lock); + } + + MONITOR_UNLOCK(); +} + +struct udev_monitor * +udev_monitor_init(struct client_info *cli, prop_array_t filters) +{ + struct udev_monitor *udm; + int error; + + udm = malloc(sizeof(struct udev_monitor)); + if (udm == NULL) + return NULL; + + TAILQ_INIT(&udm->ev_queue); + TAILQ_INIT(&udm->ev_filt); + + pthread_mutex_init(&udm->q_lock, NULL); + pthread_cond_init(&udm->cond, NULL); + udm->cli = cli; + + if (filters != NULL) { + error = _parse_filter_prop(udm, filters); + /* XXX: ignore error for now */ + } + + return udm; +} + +void +udev_monitor_free(struct udev_monitor *udm) +{ + struct event_filter *evf; + struct udev_monitor_event *udm_ev; + + pthread_mutex_lock(&udm->q_lock); + + while ((udm_ev = TAILQ_FIRST(&udm->ev_queue)) != NULL) { + prop_object_release(udm_ev->ev_dict); + udm_ev->ev_dict = NULL; + TAILQ_REMOVE(&udm->ev_queue, udm_ev, link); + free(udm_ev); + } + + while ((evf = TAILQ_FIRST(&udm->ev_filt)) != NULL) { + TAILQ_REMOVE(&udm->ev_filt, evf, link); + free(evf); + } + + pthread_mutex_unlock(&udm->q_lock); + free(udm); +} + +int +client_cmd_monitor(struct client_info *cli, prop_dictionary_t dict) +{ + prop_array_t pa; + prop_object_t po; + struct udev_monitor *udm; + struct udev_monitor_event *udm_ev; + char *xml; + ssize_t r; + int ok = 1; + + pa = NULL; + po = prop_dictionary_get(dict, "filters"); + if ((po != NULL) && prop_object_type(po) == PROP_TYPE_ARRAY) { + pa = po; + } + + udm = udev_monitor_init(cli, pa); + if (udm == NULL) + return 1; + + MONITOR_LOCK(); + TAILQ_INSERT_TAIL(&udev_monitor_list, udm, link); + MONITOR_UNLOCK(); + + pthread_mutex_lock(&udm->q_lock); + while (ok) { + pthread_cond_wait(&udm->cond, &udm->q_lock); + + udm_ev = TAILQ_FIRST(&udm->ev_queue); + if (udm_ev == NULL) + continue; + + assert(udm_ev->ev_dict != NULL); + xml = prop_dictionary_externalize(udm_ev->ev_dict); + if (xml == NULL) + continue; + + prop_object_release(udm_ev->ev_dict); + udm_ev->ev_dict = NULL; + TAILQ_REMOVE(&udm->ev_queue, udm_ev, link); + free(udm_ev); + + r = send_xml(cli->fd, xml); + if (r <= 0) + goto end; + + free(xml); + continue; +end: + pthread_mutex_unlock(&udm->q_lock); + close(cli->fd); + ok = 0; + free(xml); + } + + MONITOR_LOCK(); + TAILQ_REMOVE(&udev_monitor_list, udm, link); + MONITOR_UNLOCK(); + + udev_monitor_free(udm); + + return 1; +} + +static int +_parse_filter_prop(struct udev_monitor *udm, prop_array_t pa) +{ + prop_string_t ps; + prop_number_t pn; + prop_object_iterator_t iter; + prop_dictionary_t dict; + struct event_filter *evf; + int error; + + iter = prop_array_iterator(pa); + if (iter == NULL) + return -1; + + while ((dict = prop_object_iterator_next(iter)) != NULL) { + evf = malloc(sizeof(struct event_filter)); + if (evf == NULL) + goto error_alloc; + + ps = prop_dictionary_get(dict, "key"); + if (ps == NULL) + goto error_out; + evf->key = prop_string_cstring(ps); + if (evf->key == NULL) + goto error_out; + + pn = prop_dictionary_get(dict, "type"); + if (pn == NULL) + goto error_out_ps; + + ps = prop_dictionary_get(dict, "expr"); + if (ps == NULL) + goto error_out_ps; + + if (prop_dictionary_get(dict, "negative")) + evf->neg = 1; + else + evf->neg = 0; + + evf->type = prop_number_integer_value(pn); + switch (evf->type) { + case EVENT_FILTER_TYPE_WILDCARD: + evf->wildcard_match = prop_string_cstring(ps); + if (evf->wildcard_match == NULL) + goto error_out_ps; + break; + + case EVENT_FILTER_TYPE_REGEX: + error = regcomp(&evf->regex_match, prop_string_cstring_nocopy(ps), REG_ICASE | REG_NOSUB); + if (error) + goto error_out_ps; + break; + + default: + goto error_out_ps; + } + + pthread_mutex_lock(&udm->q_lock); + TAILQ_INSERT_TAIL(&udm->ev_filt, evf, link); + pthread_mutex_unlock(&udm->q_lock); + + } + + prop_object_iterator_release(iter); + return 0; + +error_out_ps: + free(evf->key); +error_out: + free(evf); +error_alloc: + prop_object_iterator_release(iter); + return -1; +} + +/* +Event filter format: + + +key +(e.g. kptr, devnum, ...) +type +(e.g. wildcard or regex) +expr +(regex) + +... repeat ... + +*/ + +static int +match_filter(struct event_filter *evf, prop_dictionary_t ev_dict) +{ + prop_object_t po; + prop_string_t ps; + prop_number_t pn; + char *str; + char buf[128]; + int ret; + + if (ev_dict == NULL) + return 0; + + prop_object_retain(ev_dict); + + assert(prop_dictionary_externalize(ev_dict) != NULL); + if ((po = prop_dictionary_get(ev_dict, evf->key)) == NULL) + goto no_match; + + if (prop_object_type(po) == PROP_TYPE_STRING) { + ps = po; + str = __DECONST(char *, prop_string_cstring_nocopy(ps)); + } else if (prop_object_type(po) == PROP_TYPE_NUMBER) { + pn = po; + if (prop_number_unsigned(pn)) { + snprintf(buf, sizeof(buf), "%" PRIu64, prop_number_unsigned_integer_value(pn)); + } else { + snprintf(buf, sizeof(buf), "%" PRIi64, prop_number_integer_value(pn)); + } + str = buf; + } else { + syslog(LOG_DEBUG, "Unexpected type in match_filter: %d\n", prop_object_type(po)); + /* Unexpected type */ + goto no_match; + } + + switch (evf->type) { + case EVENT_FILTER_TYPE_WILDCARD: + ret = WildCaseCmp(evf->wildcard_match, str); + + if (ret != 0) + goto no_match; + + break; + case EVENT_FILTER_TYPE_REGEX: + ret = regexec(&evf->regex_match, str, 0, NULL, 0); + + if (ret != 0) + goto no_match; + break; + default: + goto no_match; + } + + prop_object_release(ev_dict); + return 1; + +no_match: + prop_object_release(ev_dict); + return 0; +} + +int +match_event_filter(struct udev_monitor *udm, prop_dictionary_t ev_dict) +{ + struct event_filter *evf; + int all_negative = 1; + + pthread_mutex_lock(&udm->q_lock); + + if (TAILQ_EMPTY(&udm->ev_filt)) + return 1; + + TAILQ_FOREACH(evf, &udm->ev_filt, link) { + //printf("match_event_filter 3\n"); + if (evf->neg == 0) + all_negative = 0; + + if (match_filter(evf, ev_dict)) { + pthread_mutex_unlock(&udm->q_lock); + return (1 ^ evf->neg); /* return 1; or 0 for 'nomatch' hit */ + } + //printf("match_event_filter 5\n"); + } + + pthread_mutex_unlock(&udm->q_lock); + return (all_negative == 1)?1:0; +} + +static int +WildCaseCmp(const char *w, const char *s) +{ + int i; + int c; + int slen = strlen(s); + const char **mary; + + for (i = c = 0; w[i]; ++i) { + if (w[i] == '*') + ++c; + } + mary = malloc(sizeof(char *) * (c + 1)); + if (mary == NULL) + return -1; + + for (i = 0; i < c; ++i) + mary[i] = s + slen; + i = wildCaseCmp(mary, 0, w, s); + free(mary); + return(i); +} + +/* + * WildCaseCmp() - compare wild string to sane string, case insensitive + * + * Returns 0 on success, -1 on failure. + */ +static int +wildCaseCmp(const char **mary, int d, const char *w, const char *s) +{ + int i; + + /* + * skip fixed portion + */ + for (;;) { + switch(*w) { + case '*': + /* + * optimize terminator + */ + if (w[1] == 0) + return(0); + if (w[1] != '?' && w[1] != '*') { + /* + * optimize * followed by non-wild + */ + for (i = 0; s + i < mary[d]; ++i) { + if (s[i] == w[1] && wildCaseCmp(mary, d + 1, w + 1, s + i) == 0) + return(0); + } + } else { + /* + * less-optimal + */ + for (i = 0; s + i < mary[d]; ++i) { + if (wildCaseCmp(mary, d + 1, w + 1, s + i) == 0) + return(0); + } + } + mary[d] = s; + return(-1); + case '?': + if (*s == 0) + return(-1); + ++w; + ++s; + break; + default: + if (*w != *s) { + if (tolower(*w) != tolower(*s)) + return(-1); + } + if (*w == 0) /* terminator */ + return(0); + ++w; + ++s; + break; + } + } + /* not reached */ + return(-1); +} diff --git a/usr.sbin/udevd/udevd_pdev.c b/usr.sbin/udevd/udevd_pdev.c new file mode 100644 index 0000000000..5992a13ac1 --- /dev/null +++ b/usr.sbin/udevd/udevd_pdev.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2010 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Alex Hornung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "udevd.h" + +static int64_t udev_generation; +TAILQ_HEAD(pdev_array_list_head, pdev_array_entry) pdev_array_list; + +void +pdev_array_entry_ref(struct pdev_array_entry *pae) +{ + ++pae->refs; +} + +void +pdev_array_entry_unref(struct pdev_array_entry *pae) +{ + if (--pae->refs == 0) { + TAILQ_REMOVE(&pdev_array_list, pae, link); + prop_object_release(pae->pdev_array); /* XXX */ + free(pae); + } +} + +void +pdev_array_entry_insert(prop_array_t pa) +{ + struct pdev_array_entry *pae, *opae = NULL; + + if (pa == NULL) + errx(1, "null prop_array in insert_pdev_array"); + pae = malloc(sizeof(struct pdev_array_entry)); + if (pae == NULL) + errx(1, "insert_pdev_array could not allocate mem"); + memset(pae, 0, sizeof(struct pdev_array_entry)); + pae->pdev_array = pa; + pae->generation = udev_generation++; + pae->refs = 1; /* One ref because it's the most recent one */ + + /* + * If the TAILQ is not empty, unref the last entry, + * as it isn't needed anymore. + */ + if (!TAILQ_EMPTY(&pdev_array_list)) + opae = TAILQ_LAST(&pdev_array_list, pdev_array_list_head); + + TAILQ_INSERT_TAIL(&pdev_array_list, pae, link); + + if (opae != NULL) + pdev_array_entry_unref(opae); +} + +struct pdev_array_entry * +pdev_array_entry_get(int64_t generation) +{ + struct pdev_array_entry *pae; + int found = 0; + + TAILQ_FOREACH(pae, &pdev_array_list, link) { + if (pae->generation == generation) { + found = 1; + break; + } + } + + if (!found) + return NULL; + + pdev_array_entry_ref(pae); + return pae; +} + +struct pdev_array_entry * +pdev_array_entry_get_last(void) +{ + struct pdev_array_entry *pae; + + pae = TAILQ_LAST(&pdev_array_list, pdev_array_list_head); + + pdev_array_entry_ref(pae); + return pae; +} diff --git a/usr.sbin/udevd/udevd_socket.c b/usr.sbin/udevd/udevd_socket.c new file mode 100644 index 0000000000..f6a4bfd841 --- /dev/null +++ b/usr.sbin/udevd/udevd_socket.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2010 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Alex Hornung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "udevd.h" + +int +send_xml(int s, char *xml) +{ + ssize_t r,n; + size_t sz; + + sz = strlen(xml) + 1; + + r = send(s, &sz, sizeof(sz), 0); + if (r <= 0) + return r; + + r = 0; + while (r < (ssize_t)sz) { + n = send(s, xml+r, sz-r, 0); + if (n <= 0) + return n; + r += n; + } + + return r; +} + +int +read_xml(int s, char *buf, size_t buf_sz) +{ + size_t sz; + int n, r; + + n = recv(s, &sz, sizeof(sz), MSG_WAITALL); + if (n <= 0) + return n; + + r = 0; + while ((r < (ssize_t)sz) && (r < (ssize_t)buf_sz)) { + n = recv(s, buf+r, sz-r, MSG_WAITALL); + if (n <= 0) + return n; + r += n; + } + + return r; +} + +int +unblock_descriptor(int s) +{ + int flags, ret; + + flags = fcntl(s, F_GETFL, 0); + ret = fcntl(s, F_SETFL, flags | O_NONBLOCK); + return ret; +} + +int +block_descriptor(int s) +{ + int flags, ret; + + flags = fcntl(s, F_GETFL, 0); + ret = fcntl(s, F_SETFL, flags & ~O_NONBLOCK); + return ret; +} + +int +init_local_server(const char *sockfile, int socktype, int nonblock) +{ + int s; + struct sockaddr_un un_addr; + + if ((s = socket(AF_UNIX, socktype, 0)) < 0) + return -1; + + memset(&un_addr, 0, sizeof(un_addr)); + un_addr.sun_family = AF_UNIX; + strncpy(un_addr.sun_path, sockfile, SOCKFILE_NAMELEN); + un_addr.sun_path[SOCKFILE_NAMELEN - 1] = '\0'; + + /* + * DO NOT change `un_addr.sun_path' to `sockfile' here, + * since `sockfile' may have been truncated by above strncpy(3). + */ + unlink(un_addr.sun_path); + + if (nonblock && unblock_descriptor(s) < 0) { + close(s); + return -1; + } + + if (bind(s, (struct sockaddr *)&un_addr, sizeof(un_addr)) < 0) { + close(s); + return -1; + } + + if (socktype == SOCK_STREAM && listen(s, LOCAL_BACKLOG) < 0) { + close(s); + return -1; + } + + return s; +} -- 2.41.0