chiark / gitweb /
libudev: add udev event monitor API
authorKay Sievers <kay.sievers@vrfy.org>
Thu, 28 Aug 2008 21:05:01 +0000 (23:05 +0200)
committerKay Sievers <kay.sievers@vrfy.org>
Thu, 28 Aug 2008 21:05:01 +0000 (23:05 +0200)
NEWS
udev/lib/Makefile.am
udev/lib/exported_symbols
udev/lib/libudev-device.c
udev/lib/libudev-monitor.c [new file with mode: 0644]
udev/lib/libudev-private.h
udev/lib/libudev.c
udev/lib/libudev.h
udev/lib/test-libudev.c

diff --git a/NEWS b/NEWS
index 016ec19..1a9aa69 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,13 @@ udev 127
 ========
 Bugfixes.
 
+A shared library "libudev" will be installed now, to access udev
+device information. DeviceKit, the successor of HAL, will need this
+library to access the udev database and search sysfs for devices.
+The library is currently in an experimental state, also the API is
+expected to change, as long as the DeviceKit integration isn't
+finished.
+
 udev 126
 ========
 We use ./configure now. See INSTALL for details. Current
index 2e5b81d..a1fd77b 100644 (file)
@@ -24,6 +24,7 @@ libudev_la_SOURCES =\
        libudev-utils.c \
        libudev-device.c \
        libudev-enumerate.c \
+       libudev-monitor.c \
        ../list.h \
        ../udev.h \
        ../udev_utils.c \
index c3a7caf..0826e6f 100644 (file)
@@ -15,3 +15,9 @@ udev_device_get_subsystem
 udev_device_get_devlinks
 udev_device_get_properties
 udev_devices_enumerate
+udev_monitor_new_from_socket
+udev_monitor_ref
+udev_monitor_unref
+udev_monitor_get_udev
+udev_monitor_get_fd
+udev_monitor_get_device
index 805eef9..f6ad541 100644 (file)
 #include "libudev-private.h"
 #include "../udev.h"
 
-static struct udev_device *device_init(struct udev *udev)
+struct udev_device *device_init(struct udev *udev)
 {
        struct udev_device *udev_device;
 
+       if (udev == NULL)
+               return NULL;
+
        udev_device = malloc(sizeof(struct udev_device));
        if (udev_device == NULL)
                return NULL;
        memset(udev_device, 0x00, sizeof(struct udev_device));
        udev_device->refcount = 1;
        udev_device->udev = udev;
+       INIT_LIST_HEAD(&udev_device->link_list);
+       INIT_LIST_HEAD(&udev_device->env_list);
+       log_info(udev_device->udev, "udev_device: %p created\n", udev_device);
        return udev_device;
 }
 
@@ -64,8 +70,15 @@ struct udev_device *udev_device_new_from_devpath(struct udev *udev, const char *
        char path[PATH_SIZE];
        struct stat statbuf;
        struct udev_device *udev_device;
+       struct udevice *udevice;
+       struct name_entry *name_loop;
        int err;
 
+       if (udev == NULL)
+               return NULL;
+       if (devpath == NULL)
+               return NULL;
+
        strlcpy(path, udev_get_sys_path(udev), sizeof(path));
        strlcat(path, devpath, sizeof(path));
        if (stat(path, &statbuf) != 0)
@@ -77,20 +90,38 @@ struct udev_device *udev_device_new_from_devpath(struct udev *udev, const char *
        if (udev_device == NULL)
                return NULL;
 
-       udev_device->udevice = udev_device_init(NULL);
-       if (udev_device->udevice == NULL) {
+       udevice = udev_device_init(NULL);
+       if (udevice == NULL) {
                free(udev_device);
                return NULL;
        }
-       log_info(udev, "device %p created\n", udev_device);
 
+       /* resolve possible symlink to real path */
        strlcpy(path, devpath, sizeof(path));
        sysfs_resolve_link(path, sizeof(path));
+       udev_device->devpath = strdup(path);
+       log_info(udev, "device %p has devpath '%s'\n", udev_device, udev_device_get_devpath(udev_device));
 
-       err = udev_db_get_device(udev_device->udevice, path);
+       err = udev_db_get_device(udevice, path);
        if (err >= 0)
                log_info(udev, "device %p filled with udev database data\n", udev_device);
-       log_info(udev, "device %p filled with %s data\n", udev_device, udev_device_get_devpath(udev_device));
+
+       if (udevice->name[0] != '\0')
+               asprintf(&udev_device->devname, "%s/%s", udev_get_dev_path(udev), udevice->name);
+
+       list_for_each_entry(name_loop, &udevice->symlink_list, node) {
+               char name[PATH_SIZE];
+
+               strlcpy(name, udev_get_dev_path(udev), sizeof(name));
+               strlcat(name, "/", sizeof(name));
+               strlcat(name, name_loop->name, sizeof(name));
+               name_list_add(&udev_device->link_list, name, 0);
+       }
+
+       list_for_each_entry(name_loop, &udevice->env_list, node)
+               name_list_add(&udev_device->env_list, name_loop->name, 0);
+
+       udev_device_cleanup(udevice);
        return udev_device;
 }
 
@@ -103,6 +134,8 @@ struct udev_device *udev_device_new_from_devpath(struct udev *udev, const char *
  **/
 struct udev *udev_device_get_udev(struct udev_device *udev_device)
 {
+       if (udev_device == NULL)
+               return NULL;
        return udev_device->udev;
 }
 
@@ -116,6 +149,8 @@ struct udev *udev_device_get_udev(struct udev_device *udev_device)
  **/
 struct udev_device *udev_device_ref(struct udev_device *udev_device)
 {
+       if (udev_device == NULL)
+               return NULL;
        udev_device->refcount++;
        return udev_device;
 }
@@ -130,10 +165,17 @@ struct udev_device *udev_device_ref(struct udev_device *udev_device)
  **/
 void udev_device_unref(struct udev_device *udev_device)
 {
+       if (udev_device == NULL)
+               return;
        udev_device->refcount--;
        if (udev_device->refcount > 0)
                return;
-       udev_device_cleanup(udev_device->udevice);
+       free(udev_device->devpath);
+       free(udev_device->devname);
+       free(udev_device->subsystem);
+       name_list_cleanup(&udev_device->link_list);
+       name_list_cleanup(&udev_device->env_list);
+       log_info(udev_device->udev, "udev_device: %p released\n", udev_device);
        free(udev_device);
 }
 
@@ -148,7 +190,9 @@ void udev_device_unref(struct udev_device *udev_device)
  **/
 const char *udev_device_get_devpath(struct udev_device *udev_device)
 {
-       return udev_device->udevice->dev->devpath;
+       if (udev_device == NULL)
+               return NULL;
+       return udev_device->devpath;
 }
 
 /**
@@ -156,16 +200,15 @@ const char *udev_device_get_devpath(struct udev_device *udev_device)
  * @udev_device: udev device
  *
  * Retrieve the device node file name belonging to the udev device.
- * The path does not contain the device directory, and does not contain
- * a leading '/'.
+ * The path is an absolute path, and starts with the device directory.
  *
  * Returns: the device node file name of the udev device, or #NULL if no device node exists
  **/
 const char *udev_device_get_devname(struct udev_device *udev_device)
 {
-       if (udev_device->udevice->name[0] == '\0')
+       if (udev_device == NULL)
                return NULL;
-       return udev_device->udevice->name;
+       return udev_device->devname;
 }
 
 /**
@@ -179,13 +222,16 @@ const char *udev_device_get_devname(struct udev_device *udev_device)
  **/
 const char *udev_device_get_subsystem(struct udev_device *udev_device)
 {
-       struct sysfs_device *dev = udev_device->udevice->dev;
-       if (dev->subsystem[0] != '\0')
-               return dev->subsystem;
-       if (util_get_sys_subsystem(udev_device->udev, dev->devpath,
-                         dev->subsystem, sizeof(dev->subsystem)) < 2)
+       char subsystem[NAME_SIZE];
+
+       if (udev_device == NULL)
+               return NULL;
+       if (udev_device->subsystem != NULL)
+               return udev_device->subsystem;
+       if (util_get_sys_subsystem(udev_device->udev, udev_device->devpath, subsystem, sizeof(subsystem)) < 2)
                return NULL;
-       return dev->subsystem;
+       udev_device->subsystem = strdup(subsystem);
+       return udev_device->subsystem;
 }
 
 /**
@@ -196,10 +242,9 @@ const char *udev_device_get_subsystem(struct udev_device *udev_device)
  *
  * Retrieve the device links pointing to the device file of the
  * udev device. For every device link, the passed function will be
- * called with the device link string. If the function returns 1,
- * remaning device links will be ignored. The device link path
- * does not contain the device directory, and does not contain
- * a leading '/'.
+ * called with the device link string.
+ * The path is an absolute path, and starts with the device directory.
+ * If the function returns 1, remaning device links will be ignored.
  *
  * Returns: the number of device links passed to the caller, or a negative value on error
  **/
@@ -210,7 +255,9 @@ int udev_device_get_devlinks(struct udev_device *udev_device,
        struct name_entry *name_loop;
        int count = 0;
 
-       list_for_each_entry(name_loop, &udev_device->udevice->symlink_list, node) {
+       if (udev_device == NULL)
+               return -1;
+       list_for_each_entry(name_loop, &udev_device->link_list, node) {
                count++;
                if (cb(udev_device, name_loop->name, data) != 0)
                        break;
@@ -238,7 +285,9 @@ int udev_device_get_properties(struct udev_device *udev_device,
        struct name_entry *name_loop;
        int count = 0;
 
-       list_for_each_entry(name_loop, &udev_device->udevice->env_list, node) {
+       if (udev_device == NULL)
+               return -1;
+       list_for_each_entry(name_loop, &udev_device->env_list, node) {
                char name[PATH_SIZE];
                char *val;
 
@@ -249,6 +298,7 @@ int udev_device_get_properties(struct udev_device *udev_device,
                        continue;
                val[0] = '\0';
                val = &val[1];
+               count++;
                if (cb(udev_device, name, val, data) != 0)
                        break;
        }
diff --git a/udev/lib/libudev-monitor.c b/udev/lib/libudev-monitor.c
new file mode 100644 (file)
index 0000000..fbb21d0
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * libudev - interface to udev device information
+ *
+ * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "libudev.h"
+#include "libudev-private.h"
+#include "../udev.h"
+
+struct udev_monitor {
+       struct udev *udev;
+       int refcount;
+       int socket;
+};
+
+struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path)
+{
+       struct udev_monitor *udev_monitor;
+       struct sockaddr_un saddr;
+       socklen_t addrlen;
+       const int on = 1;
+
+       if (udev == NULL)
+               return NULL;
+       if (socket_path == NULL)
+               return NULL;
+       udev_monitor = malloc(sizeof(struct udev_monitor));
+       if (udev_monitor == NULL)
+               return NULL;
+       memset(udev_monitor, 0x00, sizeof(struct udev_monitor));
+       udev_monitor->refcount = 1;
+       udev_monitor->udev = udev;
+
+       memset(&saddr, 0x00, sizeof(saddr));
+       saddr.sun_family = AF_LOCAL;
+       strcpy(saddr.sun_path, socket_path);
+       addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path);
+
+       /* translate leading '@' to abstract namespace */
+       if (saddr.sun_path[0] == '@')
+               saddr.sun_path[0] = '\0';
+
+       udev_monitor->socket = socket(AF_LOCAL, SOCK_DGRAM, 0);
+       if (udev_monitor->socket == -1) {
+               log_err(udev, "error getting socket: %s\n", strerror(errno));
+               free(udev_monitor);
+               return NULL;
+       }
+
+       if (bind(udev_monitor->socket, (struct sockaddr *) &saddr, addrlen) < 0) {
+               log_err(udev, "bind failed: %s\n", strerror(errno));
+               close(udev_monitor->socket);
+               free(udev_monitor);
+               return NULL;
+       }
+
+       /* enable receiving of the sender credentials */
+       setsockopt(udev_monitor->socket, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+       log_info(udev_monitor->udev, "udev_monitor: %p created\n", udev_monitor);
+
+       return udev_monitor;
+}
+
+struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor)
+{
+       if (udev_monitor == NULL)
+               return NULL;
+       udev_monitor->refcount++;
+       return udev_monitor;
+}
+
+void udev_monitor_unref(struct udev_monitor *udev_monitor)
+{
+       if (udev_monitor == NULL)
+               return;
+       udev_monitor->refcount--;
+       if (udev_monitor->refcount > 0)
+               return;
+       close(udev_monitor->socket);
+       log_info(udev_monitor->udev, "udev_monitor: %p released\n", udev_monitor);
+       free(udev_monitor);
+}
+
+struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor)
+{
+       if (udev_monitor == NULL)
+               return NULL;
+       return udev_monitor->udev;
+}
+
+int udev_monitor_get_fd(struct udev_monitor *udev_monitor)
+{
+       if (udev_monitor == NULL)
+               return -1;
+       return udev_monitor->socket;
+}
+
+struct udev_device *udev_monitor_get_device(struct udev_monitor *udev_monitor)
+{
+       struct udev_device *udev_device;
+       struct msghdr smsg;
+       struct cmsghdr *cmsg;
+       struct iovec iov;
+       struct ucred *cred;
+       char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
+       char buf[4096];
+       size_t bufpos;
+
+       if (udev_monitor == NULL)
+               return NULL;
+       memset(buf, 0x00, sizeof(buf));
+       iov.iov_base = &buf;
+       iov.iov_len = sizeof(buf);
+       memset (&smsg, 0x00, sizeof(struct msghdr));
+       smsg.msg_iov = &iov;
+       smsg.msg_iovlen = 1;
+       smsg.msg_control = cred_msg;
+       smsg.msg_controllen = sizeof(cred_msg);
+
+       if (recvmsg(udev_monitor->socket, &smsg, 0) < 0) {
+               if (errno != EINTR)
+                       log_info(udev_monitor->udev, "unable to receive message");
+               return NULL;
+       }
+       cmsg = CMSG_FIRSTHDR(&smsg);
+       cred = (struct ucred *)CMSG_DATA (cmsg);
+
+       if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
+               log_info(udev_monitor->udev, "no sender credentials received, message ignored");
+               return NULL;
+       }
+
+       if (cred->uid != 0) {
+               log_info(udev_monitor->udev, "sender uid=%d, message ignored", cred->uid);
+               return NULL;
+       }
+
+       /* skip header */
+       bufpos = strlen(buf) + 1;
+       if (bufpos < sizeof("a@/d") || bufpos >= sizeof(buf)) {
+               log_info(udev_monitor->udev, "invalid message length");
+               return NULL;
+       }
+
+       /* check message header */
+       if (strstr(buf, "@/") == NULL) {
+               log_info(udev_monitor->udev, "unrecognized message header");
+               return NULL;
+       }
+
+       udev_device = device_init(udev_monitor->udev);
+       if (udev_device == NULL) {
+               return NULL;
+       }
+
+       while (bufpos < sizeof(buf)) {
+               char *key;
+               size_t keylen;
+
+               key = &buf[bufpos];
+               keylen = strlen(key);
+               if (keylen == 0)
+                       break;
+               bufpos += keylen + 1;
+
+               if (strncmp(key, "DEVPATH=", 8) == 0) {
+                       udev_device->devpath = strdup(&key[8]);
+               } else if (strncmp(key, "SUBSYSTEM=", 10) == 0) {
+                       udev_device->subsystem = strdup(&key[10]);
+               } else if (strncmp(key, "DEVNAME=", 8) == 0) {
+                       udev_device->devname = strdup(&key[8]);
+               } else if (strncmp(key, "DEVLINKS=", 9) == 0) {
+                       char *slink = &key[9];
+                       char *next = strchr(slink, ' ');
+
+                       while (next != NULL) {
+                               next[0] = '\0';
+                               name_list_add(&udev_device->link_list, slink, 0);
+                               slink = &next[1];
+                               next = strchr(slink, ' ');
+                       }
+                       if (slink[0] != '\0')
+                               name_list_add(&udev_device->link_list, slink, 0);
+               }
+               name_list_add(&udev_device->env_list, key, 0);
+       }
+
+       return udev_device;
+}
index 4697f84..7e7d1c6 100644 (file)
 #include "libudev.h"
 #include "../udev.h"
 
-struct udev {
-       int refcount;
-       void (*log_fn)(struct udev *udev,
-                      int priority, const char *file, int line, const char *fn,
-                      const char *format, va_list args);
-};
-
 struct udev_device {
        int refcount;
        struct udev *udev;
-       struct udevice *udevice;
+       char *devpath;
+       char *devname;
+       char *subsystem;
+       struct list_head link_list;
+       struct list_head env_list;
 };
 
 #ifdef USE_LOG
@@ -57,6 +54,7 @@ static inline void udev_log(struct udev *udev,
              __attribute__ ((format(printf, 6, 7))) {}
 #endif
 
+extern struct udev_device *device_init(struct udev *udev);
 extern ssize_t util_get_sys_subsystem(struct udev *udev, const char *devpath, char *subsystem, size_t size);
 
 #endif
index 56bd477..a5f33eb 100644 (file)
 #include "libudev-private.h"
 #include "../udev.h"
 
+struct udev {
+       int refcount;
+       void (*log_fn)(struct udev *udev,
+                      int priority, const char *file, int line, const char *fn,
+                      const char *format, va_list args);
+};
+
 void udev_log(struct udev *udev,
              int priority, const char *file, int line, const char *fn,
              const char *format, ...)
@@ -110,6 +117,8 @@ struct udev *udev_new(void)
  **/
 struct udev *udev_ref(struct udev *udev)
 {
+       if (udev == NULL)
+               return NULL;
        udev->refcount++;
        return udev;
 }
@@ -124,6 +133,8 @@ struct udev *udev_ref(struct udev *udev)
  **/
 void udev_unref(struct udev *udev)
 {
+       if (udev == NULL)
+               return;
        udev->refcount--;
        if (udev->refcount > 0)
                return;
@@ -164,6 +175,8 @@ void udev_set_log_fn(struct udev *udev,
  **/
 const char *udev_get_sys_path(struct udev *udev)
 {
+       if (udev == NULL)
+               return NULL;
        return sysfs_path;
 }
 
@@ -179,5 +192,7 @@ const char *udev_get_sys_path(struct udev *udev)
  **/
 const char *udev_get_dev_path(struct udev *udev)
 {
+       if (udev == NULL)
+               return NULL;
        return udev_root;
 }
index e740f36..1eb3745 100644 (file)
@@ -22,6 +22,7 @@
 
 struct udev;
 struct udev_device;
+struct udev_monitor;
 
 extern struct udev *udev_new(void);
 extern struct udev *udev_ref(struct udev *udev);
@@ -54,4 +55,11 @@ extern int udev_devices_enumerate(struct udev *udev, const char *subsystem,
                                            const char *devpath, const char *subsystem, const char *name, void *data),
                                  void *data);
 
+extern struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path);
+extern struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor);
+extern void udev_monitor_unref(struct udev_monitor *udev_monitor);
+extern struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor);
+extern int udev_monitor_get_fd(struct udev_monitor *udev_monitor);
+extern struct udev_device *udev_monitor_get_device(struct udev_monitor *udev_monitor);
+
 #endif
index e86c660..3c38600 100644 (file)
 
 #include <stdio.h>
 #include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/select.h>
+
 #include "libudev.h"
 
 static void log_fn(struct udev *udev,
@@ -31,9 +36,49 @@ static void log_fn(struct udev *udev,
        vprintf(format, args);
 }
 
-static int devlinks_cb(struct udev_device *udev_device, const char *value, void *data)
+static int print_devlinks_cb(struct udev_device *udev_device, const char *value, void *data)
 {
-       printf("link: %s\n", value);
+       printf("link:      '%s'\n", value);
+       return 0;
+}
+
+static int print_properties_cb(struct udev_device *udev_device, const char *key, const char *value, void *data)
+{
+       printf("property:  '%s=%s'\n", key, value);
+       return 0;
+}
+
+static void print_device(struct udev_device *device)
+{
+       const char *str;
+       int count;
+
+       printf("*** device: %p ***\n", device);
+       str = udev_device_get_devpath(device);
+       printf("devpath:   '%s'\n", str);
+       str = udev_device_get_subsystem(device);
+       printf("subsystem: '%s'\n", str);
+       str = udev_device_get_devname(device);
+       printf("devname:   '%s'\n", str);
+       count = udev_device_get_devlinks(device, print_devlinks_cb, NULL);
+       printf("found %i links\n", count);
+       count = udev_device_get_properties(device, print_properties_cb, NULL);
+       printf("found %i properties\n", count);
+       printf("\n");
+}
+
+static int test_device(struct udev *udev, const char *devpath)
+{
+       struct udev_device *device;
+
+       printf("looking at device: %s\n", devpath);
+       device = udev_device_new_from_devpath(udev, devpath);
+       if (device == NULL) {
+               printf("no device\n");
+               return -1;
+       }
+       print_device(device);
+       udev_device_unref(device);
        return 0;
 }
 
@@ -41,28 +86,79 @@ static int devices_enum_cb(struct udev *udev,
                           const char *devpath, const char *subsystem, const char *name,
                           void *data)
 {
-       printf("device: %s (%s) %s\n", devpath, subsystem, name);
+       printf("device:    '%s' (%s) '%s'\n", devpath, subsystem, name);
        return 0;
 }
 
-static int properties_cb(struct udev_device *udev_device, const char *key, const char *value, void *data)
+static int test_enumerate(struct udev *udev, const char *subsystem)
 {
-       printf("property: %s=%s\n", key, value);
+       int count;
+
+       count = udev_devices_enumerate(udev, subsystem, devices_enum_cb, NULL);
+       printf("found %i devices\n\n", count);
+       return count;
+}
+
+static int test_monitor(struct udev *udev, const char *socket_path)
+{
+       struct udev_monitor *udev_monitor;
+       fd_set readfds;
+       int fd;
+
+       udev_monitor = udev_monitor_new_from_socket(udev, socket_path);
+       if (udev_monitor == NULL) {
+               printf("no socket\n");
+               return -1;
+       }
+
+       fd = udev_monitor_get_fd(udev_monitor);
+       FD_ZERO(&readfds);
+
+       while (1) {
+               struct udev_device *device;
+               int fdcount;
+
+               FD_SET(STDIN_FILENO, &readfds);
+               FD_SET(fd, &readfds);
+
+               printf("waiting for events on %s, press ENTER to exit\n", socket_path);
+               fdcount = select(fd+1, &readfds, NULL, NULL, NULL);
+               printf("select fd count: %i\n", fdcount);
+
+               if (FD_ISSET(fd, &readfds)) {
+                       device = udev_monitor_get_device(udev_monitor);
+                       if (device == NULL) {
+                               printf("no device from socket\n");
+                               continue;
+                       }
+                       print_device(device);
+                       udev_device_unref(device);
+               }
+
+               if (FD_ISSET(STDIN_FILENO, &readfds)) {
+                       printf("exiting loop\n");
+                       break;
+               }
+       }
+
+       udev_monitor_unref(udev_monitor);
        return 0;
 }
 
 int main(int argc, char *argv[], char *envp[])
 {
        struct udev *udev;
-       struct udev_device *device;
-       const char *str;
        const char *devpath = "/devices/virtual/mem/null";
        const char *subsystem = NULL;
+       const char *socket = "@/org/kernel/udev/monitor";
+       const char *str;
 
        if (argv[1] != NULL) {
                devpath = argv[1];
                if (argv[2] != NULL)
                        subsystem = argv[2];
+                       if (argv[3] != NULL)
+                               socket = argv[3];
        }
 
        udev = udev_new();
@@ -75,32 +171,13 @@ int main(int argc, char *argv[], char *envp[])
        printf("set log: %p\n", log_fn);
 
        str = udev_get_sys_path(udev);
-       printf("sys_path: %s\n", str);
+       printf("sys_path: '%s'\n", str);
        str = udev_get_dev_path(udev);
-       printf("dev_path: %s\n", str);
-
-       printf("looking at device: %s\n", devpath);
-       device = udev_device_new_from_devpath(udev, devpath);
-       printf("device: %p\n", device);
-       if (device == NULL) {
-               printf("no device\n");
-               return 1;
-       }
-       str = udev_device_get_devpath(device);
-       printf("devpath: %s\n", str);
-       str = udev_device_get_subsystem(device);
-       printf("subsystem: %s\n", str);
-       str = udev_device_get_devname(device);
-       printf("devname: %s\n", str);
-       udev_device_get_devlinks(device, devlinks_cb, NULL);
-       udev_device_get_properties(device, properties_cb, NULL);
-       udev_device_unref(device);
+       printf("dev_path: '%s'\n", str);
 
-       if (subsystem == NULL)
-               printf("enumerating devices from all subsystems\n");
-       else
-               printf("enumerating devices from subsystem: %s\n", subsystem);
-       udev_devices_enumerate(udev, subsystem, devices_enum_cb, NULL);
+       test_device(udev, devpath);
+       test_enumerate(udev, subsystem);
+       test_monitor(udev, socket);
 
        udev_unref(udev);
        return 0;