Bring in udev & libdevattr
authorAlex Hornung <ahornung@gmail.com>
Sat, 12 Jun 2010 16:12:07 +0000 (16:12 +0000)
committerAlex Hornung <ahornung@gmail.com>
Sun, 13 Jun 2010 07:46:38 +0000 (08:46 +0100)
* 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!

25 files changed:
lib/libdevattr/Makefile [new file with mode: 0644]
lib/libdevattr/devattr.c [new file with mode: 0644]
lib/libdevattr/devattr.h [new file with mode: 0644]
lib/libdevattr/devattr_device.c [new file with mode: 0644]
lib/libdevattr/devattr_enumerate.c [new file with mode: 0644]
lib/libdevattr/devattr_monitor.c [new file with mode: 0644]
lib/libdevattr/devattr_test.c [new file with mode: 0644]
lib/libdevattr/mktest [new file with mode: 0644]
sys/conf/files
sys/dev/drm/i915_drv.h
sys/kern/kern_udev.c [new file with mode: 0644]
sys/kern/vfs_conf.c
sys/sys/conf.h
sys/sys/devfs.h
sys/sys/udev.h [new file with mode: 0644]
sys/vfs/devfs/devfs_core.c
usr.sbin/udevd/Makefile [new file with mode: 0644]
usr.sbin/udevd/mktest [new file with mode: 0644]
usr.sbin/udevd/test_udevd.c [new file with mode: 0644]
usr.sbin/udevd/udevd.c [new file with mode: 0644]
usr.sbin/udevd/udevd.h [new file with mode: 0644]
usr.sbin/udevd/udevd_client.c [new file with mode: 0644]
usr.sbin/udevd/udevd_monitor.c [new file with mode: 0644]
usr.sbin/udevd/udevd_pdev.c [new file with mode: 0644]
usr.sbin/udevd/udevd_socket.c [new file with mode: 0644]

diff --git a/lib/libdevattr/Makefile b/lib/libdevattr/Makefile
new file mode 100644 (file)
index 0000000..d4e0ba1
--- /dev/null
@@ -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 <bsd.lib.mk>
diff --git a/lib/libdevattr/devattr.c b/lib/libdevattr/devattr.c
new file mode 100644 (file)
index 0000000..8cf2c7a
--- /dev/null
@@ -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 <ahornung@gmail.com>
+ *
+ * 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 <sys/types.h>
+#include <sys/device.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/queue.h>
+#include <sys/un.h>
+#include <cpu/inttypes.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <libprop/proplib.h>
+#include <sys/udev.h>
+#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 (file)
index 0000000..21bd7f8
--- /dev/null
@@ -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 <ahornung@gmail.com>
+ *
+ * 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 <sys/queue.h>
+#include <libprop/proplib.h>
+#include <cpu/atomic.h>
+
+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 (file)
index 0000000..ae754b4
--- /dev/null
@@ -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 <ahornung@gmail.com>
+ *
+ * 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 <sys/types.h>
+#include <sys/device.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <cpu/inttypes.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <libprop/proplib.h>
+#include <sys/udev.h>
+#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 (file)
index 0000000..3492192
--- /dev/null
@@ -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 <ahornung@gmail.com>
+ *
+ * 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 <sys/types.h>
+#include <sys/device.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/queue.h>
+#include <sys/un.h>
+#include <cpu/inttypes.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <libprop/proplib.h>
+#include <sys/udev.h>
+#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 (file)
index 0000000..b6dded5
--- /dev/null
@@ -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 <ahornung@gmail.com>
+ *
+ * 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 <sys/types.h>
+#include <sys/device.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/queue.h>
+#include <sys/un.h>
+#include <cpu/inttypes.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <libprop/proplib.h>
+#include <sys/udev.h>
+#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 (file)
index 0000000..3d543ce
--- /dev/null
@@ -0,0 +1,83 @@
+#include <stdlib.h>
+#include <stdio.h>
+#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 (file)
index 0000000..df633c4
--- /dev/null
@@ -0,0 +1,7 @@
+DEBUG_FLAGS=-g
+PROG=  devattr_test
+SRCS=  devattr_test.c
+LDADD= -lprop -L. -ldevattr
+NOMAN=
+
+.include <bsd.prog.mk>
index 18fe2fb..802f183 100644 (file)
@@ -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
index 8d907bf..711fc68 100644 (file)
@@ -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 (file)
index 0000000..e6a0579
--- /dev/null
@@ -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 <ahornung@gmail.com>
+ *
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/poll.h>
+#include <sys/ioccom.h>
+#include <sys/malloc.h>
+#include <sys/ctype.h>
+#include <sys/syslog.h>
+#include <sys/udev.h>
+#include <sys/devfs.h>
+#include <libprop/proplib.h>
+
+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);
index 5628474..626a22c 100644 (file)
@@ -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)
 {
index 7990156..b69c04f 100644 (file)
@@ -57,6 +57,7 @@
 #ifndef _SYS_SYSREF_H_
 #include <sys/sysref.h>
 #endif
+#include <libprop/proplib.h>
 
 #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
index 63a5f00..03c8a97 100644 (file)
@@ -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 (file)
index 0000000..13f87a9
--- /dev/null
@@ -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 <ahornung@gmail.com>
+ *
+ * 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 <sys/queue.h>
+#endif
+#ifndef _SYS_CONF_H_
+#include <sys/conf.h>
+#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_ */
index 9de7aba..3aea6ef 100644 (file)
@@ -49,6 +49,7 @@
 #include <sys/devfs.h>
 #include <sys/devfs_rules.h>
 #include <sys/hotplug.h>
+#include <sys/udev.h>
 
 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 (file)
index 0000000..9e1c3f5
--- /dev/null
@@ -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 <bsd.prog.mk>
diff --git a/usr.sbin/udevd/mktest b/usr.sbin/udevd/mktest
new file mode 100644 (file)
index 0000000..9a39224
--- /dev/null
@@ -0,0 +1,7 @@
+DEBUG_FLAGS=-g
+PROG=  test_udevd
+SRCS=  test_udevd.c
+LDADD= -lprop
+NOMAN=
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/udevd/test_udevd.c b/usr.sbin/udevd/test_udevd.c
new file mode 100644 (file)
index 0000000..6564dfc
--- /dev/null
@@ -0,0 +1,770 @@
+#include <sys/types.h>
+#include <sys/device.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/queue.h>
+#include <sys/un.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <libprop/proplib.h>
+#include <sys/udev.h>
+
+#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 (file)
index 0000000..bed883c
--- /dev/null
@@ -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 <ahornung@gmail.com>
+ *
+ * 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 <sys/types.h>
+#include <sys/device.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/queue.h>
+#include <sys/un.h>
+#include <cpu/inttypes.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <libprop/proplib.h>
+#include <sys/udev.h>
+#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 (file)
index 0000000..cc19122
--- /dev/null
@@ -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 <ahornung@gmail.com>
+ *
+ * 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 <sys/queue.h>
+#include <libprop/proplib.h>
+
+#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 (file)
index 0000000..b53983b
--- /dev/null
@@ -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 <ahornung@gmail.com>
+ *
+ * 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 <sys/types.h>
+#include <sys/device.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/queue.h>
+#include <sys/un.h>
+#include <cpu/inttypes.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <libprop/proplib.h>
+#include <sys/udev.h>
+#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 (file)
index 0000000..4dfcd4e
--- /dev/null
@@ -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 <ahornung@gmail.com>
+ *
+ * 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 <sys/types.h>
+#include <sys/device.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/queue.h>
+#include <sys/un.h>
+#include <cpu/inttypes.h>
+#include <assert.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <libprop/proplib.h>
+#include <sys/udev.h>
+#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:
+<array>
+<dictionary>
+<key>key</key>
+<value>(e.g. kptr, devnum, ...)</value>
+<key>type</key>
+<value>(e.g. wildcard or regex)</value>
+<key>expr</key>
+<value>(regex)</value>
+</dictionary>
+... repeat ...
+</array>
+*/
+
+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 (file)
index 0000000..5992a13
--- /dev/null
@@ -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 <ahornung@gmail.com>
+ *
+ * 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 <sys/types.h>
+#include <sys/device.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/queue.h>
+#include <sys/un.h>
+#include <cpu/inttypes.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <libprop/proplib.h>
+#include <sys/udev.h>
+#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 (file)
index 0000000..f6a4bfd
--- /dev/null
@@ -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 <ahornung@gmail.com>
+ *
+ * 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 <sys/types.h>
+#include <sys/device.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <libprop/proplib.h>
+#include <sys/udev.h>
+#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;
+}