chiark / gitweb /
udevadm: monitor - use epoll
[elogind.git] / udev / udevadm-monitor.c
index df1459ae17d5f8bb7b4379fd123efd4330c0fd5c..8fe9e0527caec281419faead86fd2c475ad224ab 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2004-2008 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2004-2010 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
 #include <sys/time.h>
 #include <sys/socket.h>
 #include <sys/un.h>
-#include <sys/select.h>
+#include <sys/epoll.h>
 #include <linux/types.h>
 #include <linux/netlink.h>
 
 #include "udev.h"
 
-static int udev_exit;
+static bool udev_exit;
 
 static void sig_handler(int signum)
 {
        if (signum == SIGINT || signum == SIGTERM)
-               udev_exit = 1;
+               udev_exit = true;
 }
 
 static void print_device(struct udev_device *device, const char *source, int prop)
@@ -67,14 +67,18 @@ static void print_device(struct udev_device *device, const char *source, int pro
 int udevadm_monitor(struct udev *udev, int argc, char *argv[])
 {
        struct sigaction act;
+       sigset_t mask;
        int option;
-       int prop = 0;
-       int print_kernel = 0;
-       int print_udev = 0;
+       bool prop = false;
+       bool print_kernel = false;
+       bool print_udev = false;
        struct udev_list_node subsystem_match_list;
+       struct udev_list_node tag_match_list;
        struct udev_monitor *udev_monitor = NULL;
        struct udev_monitor *kernel_monitor = NULL;
-       fd_set readfds;
+       int fd_ep = -1;
+       int fd_kernel = -1, fd_udev = -1;
+       struct epoll_event ep_kernel, ep_udev;
        int rc = 0;
 
        static const struct option options[] = {
@@ -83,26 +87,29 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
                { "kernel", no_argument, NULL, 'k' },
                { "udev", no_argument, NULL, 'u' },
                { "subsystem-match", required_argument, NULL, 's' },
+               { "tag-match", required_argument, NULL, 't' },
                { "help", no_argument, NULL, 'h' },
                {}
        };
 
        udev_list_init(&subsystem_match_list);
-       while (1) {
-               option = getopt_long(argc, argv, "epkus:h", options, NULL);
+       udev_list_init(&tag_match_list);
+
+       for (;;) {
+               option = getopt_long(argc, argv, "pekus:t:h", options, NULL);
                if (option == -1)
                        break;
 
                switch (option) {
                case 'p':
                case 'e':
-                       prop = 1;
+                       prop = true;
                        break;
                case 'k':
-                       print_kernel = 1;
+                       print_kernel = true;
                        break;
                case 'u':
-                       print_udev = 1;
+                       print_udev = true;
                        break;
                case 's':
                        {
@@ -110,7 +117,7 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
                                char *devtype;
 
                                util_strscpy(subsys, sizeof(subsys), optarg);
-                               devtype = strchr(subsys, ':');
+                               devtype = strchr(subsys, '/');
                                if (devtype != NULL) {
                                        devtype[0] = '\0';
                                        devtype++;
@@ -118,21 +125,24 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
                                udev_list_entry_add(udev, &subsystem_match_list, subsys, devtype, 0, 0);
                                break;
                        }
+               case 't':
+                       udev_list_entry_add(udev, &tag_match_list, optarg, NULL, 0, 0);
+                       break;
                case 'h':
                        printf("Usage: udevadm monitor [--property] [--kernel] [--udev] [--help]\n"
-                              "  --property                    print the event properties\n"
-                              "  --kernel                      print kernel uevents\n"
-                              "  --udev                        print udev events\n"
-                              "  --subsystem-match=<subsystem> filter events\n"
+                              "  --property                              print the event properties\n"
+                              "  --kernel                                print kernel uevents\n"
+                              "  --udev                                  print udev events\n"
+                              "  --subsystem-match=<subsystem[/devtype]> filter events by subsystem\n"
+                              "  --tag-match=<tag>                       filter events by tag\n"
                               "  --help\n\n");
-               default:
                        goto out;
                }
        }
 
        if (!print_kernel && !print_udev) {
-               print_kernel = 1;
-               print_udev =1;
+               print_kernel = true;
+               print_udev = true;
        }
 
        /* set signal handlers */
@@ -142,6 +152,16 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
        act.sa_flags = SA_RESTART;
        sigaction(SIGINT, &act, NULL);
        sigaction(SIGTERM, &act, NULL);
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGTERM);
+       sigprocmask(SIG_UNBLOCK, &mask, NULL);
+
+       fd_ep = epoll_create1(EPOLL_CLOEXEC);
+       if (fd_ep < 0) {
+               err(udev, "error creating epoll fd: %m\n");
+               goto out;
+       }
 
        printf("monitor will print the received events for:\n");
        if (print_udev) {
@@ -153,6 +173,8 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
                        rc = 1;
                        goto out;
                }
+               udev_monitor_set_receive_buffer_size(udev_monitor, 128*1024*1024);
+               fd_udev = udev_monitor_get_fd(udev_monitor);
 
                udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) {
                        const char *subsys = udev_list_entry_get_name(entry);
@@ -162,13 +184,30 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
                                fprintf(stderr, "error: unable to apply subsystem filter '%s'\n", subsys);
                }
 
+               udev_list_entry_foreach(entry, udev_list_get_entry(&tag_match_list)) {
+                       const char *tag = udev_list_entry_get_name(entry);
+
+                       if (udev_monitor_filter_add_match_tag(udev_monitor, tag) < 0)
+                               fprintf(stderr, "error: unable to apply tag filter '%s'\n", tag);
+               }
+
                if (udev_monitor_enable_receiving(udev_monitor) < 0) {
                        fprintf(stderr, "error: unable to subscribe to udev events\n");
                        rc = 2;
                        goto out;
                }
+
+               memset(&ep_udev, 0, sizeof(struct epoll_event));
+               ep_udev.events = EPOLLIN;
+               ep_udev.data.fd = fd_udev;
+               if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) {
+                       err(udev, "fail to add fd to epoll: %m\n");
+                       goto out;
+               }
+
                printf("UDEV - the event which udev sends out after rule processing\n");
        }
+
        if (print_kernel) {
                struct udev_list_entry *entry;
 
@@ -178,6 +217,8 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
                        rc = 3;
                        goto out;
                }
+               udev_monitor_set_receive_buffer_size(kernel_monitor, 128*1024*1024);
+               fd_kernel = udev_monitor_get_fd(kernel_monitor);
 
                udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) {
                        const char *subsys = udev_list_entry_get_name(entry);
@@ -191,51 +232,59 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
                        rc = 4;
                        goto out;
                }
+
+               memset(&ep_kernel, 0, sizeof(struct epoll_event));
+               ep_kernel.events = EPOLLIN;
+               ep_kernel.data.fd = fd_kernel;
+               if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_kernel, &ep_kernel) < 0) {
+                       err(udev, "fail to add fd to epoll: %m\n");
+                       goto out;
+               }
+
                printf("KERNEL - the kernel uevent\n");
        }
        printf("\n");
 
        while (!udev_exit) {
                int fdcount;
+               struct epoll_event ev[4];
+               int i;
 
-               FD_ZERO(&readfds);
-               if (kernel_monitor != NULL)
-                       FD_SET(udev_monitor_get_fd(kernel_monitor), &readfds);
-               if (udev_monitor != NULL)
-                       FD_SET(udev_monitor_get_fd(udev_monitor), &readfds);
-
-               fdcount = select(MAX(udev_monitor_get_fd(kernel_monitor), udev_monitor_get_fd(udev_monitor))+1,
-                                &readfds, NULL, NULL, NULL);
+               fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1);
                if (fdcount < 0) {
                        if (errno != EINTR)
                                fprintf(stderr, "error receiving uevent message: %m\n");
                        continue;
                }
 
-               if ((kernel_monitor != NULL) && FD_ISSET(udev_monitor_get_fd(kernel_monitor), &readfds)) {
-                       struct udev_device *device;
+               for (i = 0; i < fdcount; i++) {
+                       if (ev[i].data.fd == fd_kernel && ev[i].events & EPOLLIN) {
+                               struct udev_device *device;
 
-                       device = udev_monitor_receive_device(kernel_monitor);
-                       if (device == NULL)
-                               continue;
-                       print_device(device, "KERNEL", prop);
-                       udev_device_unref(device);
-               }
+                               device = udev_monitor_receive_device(kernel_monitor);
+                               if (device == NULL)
+                                       continue;
+                               print_device(device, "KERNEL", prop);
+                               udev_device_unref(device);
+                       }
 
-               if ((udev_monitor != NULL) && FD_ISSET(udev_monitor_get_fd(udev_monitor), &readfds)) {
-                       struct udev_device *device;
+                       if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) {
+                               struct udev_device *device;
 
-                       device = udev_monitor_receive_device(udev_monitor);
-                       if (device == NULL)
-                               continue;
-                       print_device(device, "UDEV", prop);
-                       udev_device_unref(device);
+                               device = udev_monitor_receive_device(udev_monitor);
+                               if (device == NULL)
+                                       continue;
+                               print_device(device, "UDEV", prop);
+                               udev_device_unref(device);
+                       }
                }
        }
-
 out:
+       if (fd_ep >= 0)
+               close(fd_ep);
        udev_monitor_unref(udev_monitor);
        udev_monitor_unref(kernel_monitor);
        udev_list_cleanup_entries(udev, &subsystem_match_list);
+       udev_list_cleanup_entries(udev, &tag_match_list);
        return rc;
 }