--- /dev/null
+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>
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+#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;
+}
--- /dev/null
+DEBUG_FLAGS=-g
+PROG= devattr_test
+SRCS= devattr_test.c
+LDADD= -lprop -L. -ldevattr
+NOMAN=
+
+.include <bsd.prog.mk>
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
LOCK_TEST_WITH_RETURN(dev, file_priv); \
} while (0)
-#if defined(__FreeBSD__) || defined(__DragonFly__)
+#if defined(__FreeBSD__)
typedef boolean_t bool;
#endif
--- /dev/null
+/*
+ * 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);
}
-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)
{
} 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) {
}
-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)
{
#ifndef _SYS_SYSREF_H_
#include <sys/sysref.h>
#endif
+#include <libprop/proplib.h>
#define SPECNAMELEN 63
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
} __m_chandler;
struct {
void *load;
+ void *load2;
} __m_gen;
struct {
void *resp;
#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
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))
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);
--- /dev/null
+/*
+ * 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_ */
#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);
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);
* 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;
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);
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,
devfs_link_dev(dev);
devfs_propagate_dev(dev, 1);
+ udev_event_attach(dev, NULL, 0);
+
return 0;
}
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);
*/
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",
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);
}
}
* 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;
--- /dev/null
+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>
--- /dev/null
+DEBUG_FLAGS=-g
+PROG= test_udevd
+SRCS= test_udevd.c
+LDADD= -lprop
+NOMAN=
+
+.include <bsd.prog.mk>
--- /dev/null
+#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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}