X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=udev%2Fudevadm-monitor.c;h=64913dbd55cbfa9acd9067b48b25d04d7b06d957;hp=caaf84ffa7b82136006f93b565ca88516fdd89fd;hb=aa29418a13dc1e1eedca702663e733cc8d9f9859;hpb=d59f11e1407ec6fa26e3a6f20b2d404af6978199 diff --git a/udev/udevadm-monitor.c b/udev/udevadm-monitor.c index caaf84ffa..64913dbd5 100644 --- a/udev/udevadm-monitor.c +++ b/udev/udevadm-monitor.c @@ -1,19 +1,18 @@ /* - * Copyright (C) 2004-2006 Kay Sievers + * Copyright (C) 2004-2010 Kay Sievers * - * 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 version 2 of the License. - * - * 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * 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 . */ #include @@ -25,264 +24,274 @@ #include #include #include +#include #include #include #include -#include +#include #include #include #include "udev.h" -static int uevent_netlink_sock = -1; -static int udev_monitor_sock = -1; -static volatile int udev_exit; +static bool udev_exit; -static int init_udev_monitor_socket(void) -{ - struct sockaddr_un saddr; - socklen_t addrlen; - int retval; - - memset(&saddr, 0x00, sizeof(saddr)); - saddr.sun_family = AF_LOCAL; - /* use abstract namespace for socket path */ - strcpy(&saddr.sun_path[1], "/org/kernel/udev/monitor"); - addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); - - udev_monitor_sock = socket(AF_LOCAL, SOCK_DGRAM, 0); - if (udev_monitor_sock == -1) { - fprintf(stderr, "error getting socket: %s\n", strerror(errno)); - return -1; - } - - /* the bind takes care of ensuring only one copy running */ - retval = bind(udev_monitor_sock, (struct sockaddr *) &saddr, addrlen); - if (retval < 0) { - fprintf(stderr, "bind failed: %s\n", strerror(errno)); - close(udev_monitor_sock); - udev_monitor_sock = -1; - return -1; - } - - return 0; -} - -static int init_uevent_netlink_sock(void) -{ - struct sockaddr_nl snl; - int retval; - - memset(&snl, 0x00, sizeof(struct sockaddr_nl)); - snl.nl_family = AF_NETLINK; - snl.nl_pid = getpid(); - snl.nl_groups = 1; - - uevent_netlink_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); - if (uevent_netlink_sock == -1) { - fprintf(stderr, "error getting socket: %s\n", strerror(errno)); - return -1; - } - - retval = bind(uevent_netlink_sock, (struct sockaddr *) &snl, - sizeof(struct sockaddr_nl)); - if (retval < 0) { - fprintf(stderr, "bind failed: %s\n", strerror(errno)); - close(uevent_netlink_sock); - uevent_netlink_sock = -1; - return -1; - } - - return 0; -} - -static void asmlinkage sig_handler(int signum) +static void sig_handler(int signum) { if (signum == SIGINT || signum == SIGTERM) - udev_exit = 1; + udev_exit = true; } -static const char *search_key(const char *searchkey, const char *buf, size_t buflen) +static void print_device(struct udev_device *device, const char *source, int prop) { - size_t bufpos = 0; - size_t searchkeylen = strlen(searchkey); - - while (bufpos < buflen) { - const char *key; - int keylen; - - key = &buf[bufpos]; - keylen = strlen(key); - if (keylen == 0) - break; - if ((strncmp(searchkey, key, searchkeylen) == 0) && key[searchkeylen] == '=') - return &key[searchkeylen + 1]; - bufpos += keylen + 1; + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + printf("%-6s[%llu.%06u] %-8s %s (%s)\n", + source, + (unsigned long long) ts.tv_sec, (unsigned int) ts.tv_nsec/1000, + udev_device_get_action(device), + udev_device_get_devpath(device), + udev_device_get_subsystem(device)); + if (prop) { + struct udev_list_entry *list_entry; + + udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) + printf("%s=%s\n", + udev_list_entry_get_name(list_entry), + udev_list_entry_get_value(list_entry)); + printf("\n"); } - return NULL; } -int udevadm_monitor(struct udev *udev, int argc, char *argv[]) +static int adm_monitor(struct udev *udev, int argc, char *argv[]) { struct sigaction act; + sigset_t mask; int option; - int env = 0; - int print_kernel = 0; - int print_udev = 0; - fd_set readfds; - int retval = 0; + bool prop = false; + bool print_kernel = false; + bool print_udev = false; + struct udev_list subsystem_match_list; + struct udev_list tag_match_list; + struct udev_monitor *udev_monitor = NULL; + struct udev_monitor *kernel_monitor = NULL; + 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[] = { - { "environment", 0, NULL, 'e' }, - { "kernel", 0, NULL, 'k' }, - { "udev", 0, NULL, 'u' }, - { "help", 0, NULL, 'h' }, + { "property", no_argument, NULL, 'p' }, + { "environment", no_argument, NULL, 'e' }, + { "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' }, {} }; - while (1) { - option = getopt_long(argc, argv, "ekuh", options, NULL); + udev_list_init(udev, &subsystem_match_list, true); + udev_list_init(udev, &tag_match_list, true); + + for (;;) { + option = getopt_long(argc, argv, "pekus:t:h", options, NULL); if (option == -1) break; switch (option) { + case 'p': case 'e': - env = 1; + prop = true; break; case 'k': - print_kernel = 1; + print_kernel = true; break; case 'u': - print_udev = 1; + print_udev = true; + break; + case 's': + { + char subsys[UTIL_NAME_SIZE]; + char *devtype; + + util_strscpy(subsys, sizeof(subsys), optarg); + devtype = strchr(subsys, '/'); + if (devtype != NULL) { + devtype[0] = '\0'; + devtype++; + } + udev_list_entry_add(&subsystem_match_list, subsys, devtype); + break; + } + case 't': + udev_list_entry_add(&tag_match_list, optarg, NULL); break; case 'h': - printf("Usage: udevadm monitor [--environment] [--kernel] [--udev] [--help]\n" - " --env print the whole event environment\n" - " --kernel print kernel uevents\n" - " --udev print udev events\n" - " --help print this help text\n\n"); + 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= filter events by subsystem\n" + " --tag-match= filter events by tag\n" + " --help\n\n"); + goto out; default: + rc = 1; goto out; } } if (!print_kernel && !print_udev) { - print_kernel = 1; - print_udev =1; - } - - if (getuid() != 0 && print_kernel) { - fprintf(stderr, "root privileges needed to subscribe to kernel events\n"); - goto out; + print_kernel = true; + print_udev = true; } /* set signal handlers */ memset(&act, 0x00, sizeof(struct sigaction)); - act.sa_handler = (void (*)(int)) sig_handler; + act.sa_handler = sig_handler; sigemptyset(&act.sa_mask); 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) { - retval = init_udev_monitor_socket(); - if (retval) + struct udev_list_entry *entry; + + udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); + if (udev_monitor == NULL) { + fprintf(stderr, "error: unable to create netlink socket\n"); + 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); + const char *devtype = udev_list_entry_get_value(entry); + + if (udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, subsys, devtype) < 0) + 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; - printf("UDEV the event which udev sends out after rule processing\n"); + } + + 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) { - retval = init_uevent_netlink_sock(); - if (retval) + struct udev_list_entry *entry; + + kernel_monitor = udev_monitor_new_from_netlink(udev, "kernel"); + if (kernel_monitor == NULL) { + fprintf(stderr, "error: unable to create netlink socket\n"); + 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); + + if (udev_monitor_filter_add_match_subsystem_devtype(kernel_monitor, subsys, NULL) < 0) + fprintf(stderr, "error: unable to apply subsystem filter '%s'\n", subsys); + } + + if (udev_monitor_enable_receiving(kernel_monitor) < 0) { + fprintf(stderr, "error: unable to subscribe to kernel events\n"); + rc = 4; goto out; - printf("UEVENT the kernel uevent\n"); + } + + 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) { - char buf[UEVENT_BUFFER_SIZE*2]; - ssize_t buflen; - ssize_t bufpos; - ssize_t keys; int fdcount; - struct timeval tv; - struct timezone tz; - char timestr[64]; - const char *source = NULL; - const char *devpath, *action, *subsys; - - buflen = 0; - FD_ZERO(&readfds); - if (uevent_netlink_sock >= 0) - FD_SET(uevent_netlink_sock, &readfds); - if (udev_monitor_sock >= 0) - FD_SET(udev_monitor_sock, &readfds); - - fdcount = select(UDEV_MAX(uevent_netlink_sock, udev_monitor_sock)+1, &readfds, NULL, NULL, NULL); + struct epoll_event ev[4]; + int i; + + fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1); if (fdcount < 0) { if (errno != EINTR) - fprintf(stderr, "error receiving uevent message: %s\n", strerror(errno)); + fprintf(stderr, "error receiving uevent message: %m\n"); continue; } - if (gettimeofday(&tv, &tz) == 0) { - snprintf(timestr, sizeof(timestr), "%llu.%06u", - (unsigned long long) tv.tv_sec, (unsigned int) tv.tv_usec); - } else - timestr[0] = '\0'; - - if ((uevent_netlink_sock >= 0) && FD_ISSET(uevent_netlink_sock, &readfds)) { - buflen = recv(uevent_netlink_sock, &buf, sizeof(buf), 0); - if (buflen <= 0) { - fprintf(stderr, "error receiving uevent message: %s\n", strerror(errno)); - continue; - } - source = "UEVENT"; - } - - if ((udev_monitor_sock >= 0) && FD_ISSET(udev_monitor_sock, &readfds)) { - buflen = recv(udev_monitor_sock, &buf, sizeof(buf), 0); - if (buflen <= 0) { - fprintf(stderr, "error receiving udev message: %s\n", strerror(errno)); - continue; - } - source = "UDEV "; - } - - if (buflen == 0) - continue; - - keys = strlen(buf) + 1; /* start of payload */ - devpath = search_key("DEVPATH", &buf[keys], buflen); - action = search_key("ACTION", &buf[keys], buflen); - subsys = search_key("SUBSYSTEM", &buf[keys], buflen); - printf("%s[%s] %-8s %s (%s)\n", source, timestr, action, devpath, subsys); - - /* print environment */ - bufpos = keys; - if (env) { - while (bufpos < buflen) { - int keylen; - char *key; - - key = &buf[bufpos]; - keylen = strlen(key); - if (keylen == 0) - break; - printf("%s\n", key); - bufpos += keylen + 1; + 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); + } else 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); } - printf("\n"); } } - out: - if (uevent_netlink_sock >= 0) - close(uevent_netlink_sock); - if (udev_monitor_sock >= 0) - close(udev_monitor_sock); - - if (retval) - return 1; - return 0; + if (fd_ep >= 0) + close(fd_ep); + udev_monitor_unref(udev_monitor); + udev_monitor_unref(kernel_monitor); + udev_list_cleanup(&subsystem_match_list); + udev_list_cleanup(&tag_match_list); + return rc; } + +const struct udevadm_cmd udevadm_monitor = { + .name = "monitor", + .cmd = adm_monitor, + .help = "listen to kernel and udev events", +};