2017-11-18 17:14:42 +01:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0+ */
|
2005-08-01 20:22:46 +02:00
|
|
|
|
|
|
|
#include <errno.h>
|
2007-03-10 15:12:42 +01:00
|
|
|
#include <getopt.h>
|
2015-11-16 22:09:36 +01:00
|
|
|
#include <signal.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
2011-04-15 11:58:17 +02:00
|
|
|
#include <sys/epoll.h>
|
2015-11-16 22:09:36 +01:00
|
|
|
#include <sys/time.h>
|
|
|
|
#include <time.h>
|
2005-08-01 20:22:46 +02:00
|
|
|
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "fd-util.h"
|
2016-11-07 16:14:59 +01:00
|
|
|
#include "format-util.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "udev.h"
|
2017-12-05 15:30:10 +01:00
|
|
|
#include "udevadm-util.h"
|
2005-08-01 20:22:46 +02:00
|
|
|
|
2011-04-15 11:58:17 +02:00
|
|
|
static bool udev_exit;
|
2005-08-01 20:22:46 +02:00
|
|
|
|
2014-07-29 15:47:41 +02:00
|
|
|
static void sig_handler(int signum) {
|
2017-09-29 00:37:23 +02:00
|
|
|
if (IN_SET(signum, SIGINT, SIGTERM))
|
2012-01-10 01:34:15 +01:00
|
|
|
udev_exit = true;
|
2005-08-25 00:38:25 +02:00
|
|
|
}
|
|
|
|
|
2014-07-29 15:47:41 +02:00
|
|
|
static void print_device(struct udev_device *device, const char *source, int prop) {
|
2012-01-10 01:34:15 +01:00
|
|
|
struct timespec ts;
|
|
|
|
|
2016-04-09 03:08:21 +02:00
|
|
|
assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
|
2017-03-22 21:40:51 +01:00
|
|
|
printf("%-6s[%"PRI_TIME".%06"PRI_NSEC"] %-8s %s (%s)\n",
|
2012-01-10 01:34:15 +01:00
|
|
|
source,
|
2017-03-22 21:40:51 +01:00
|
|
|
ts.tv_sec, (nsec_t)ts.tv_nsec/1000,
|
2012-01-10 01:34:15 +01:00
|
|
|
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");
|
|
|
|
}
|
2008-09-28 01:34:55 +02:00
|
|
|
}
|
|
|
|
|
2013-12-18 03:48:14 +01:00
|
|
|
static void help(void) {
|
2017-12-05 15:30:10 +01:00
|
|
|
printf("%s monitor [OPTIONS]\n\n"
|
2015-01-05 13:19:55 +01:00
|
|
|
"Listen to kernel and udev events.\n\n"
|
|
|
|
" -h --help Show this help\n"
|
2017-12-05 15:30:10 +01:00
|
|
|
" -V --version Show package version\n"
|
2015-01-05 13:19:55 +01:00
|
|
|
" -p --property Print the event properties\n"
|
|
|
|
" -k --kernel Print kernel uevents\n"
|
|
|
|
" -u --udev Print udev events\n"
|
|
|
|
" -s --subsystem-match=SUBSYSTEM[/DEVTYPE] Filter events by subsystem\n"
|
|
|
|
" -t --tag-match=TAG Filter events by tag\n"
|
|
|
|
, program_invocation_short_name);
|
2013-12-18 03:48:14 +01:00
|
|
|
}
|
|
|
|
|
2014-07-29 15:47:41 +02:00
|
|
|
static int adm_monitor(struct udev *udev, int argc, char *argv[]) {
|
2013-11-08 13:57:18 +01:00
|
|
|
struct sigaction act = {};
|
2012-01-10 01:34:15 +01:00
|
|
|
sigset_t mask;
|
|
|
|
bool prop = false;
|
|
|
|
bool print_kernel = false;
|
|
|
|
bool print_udev = false;
|
tree-wide: drop redundant _cleanup_ macros (#8810)
This drops a good number of type-specific _cleanup_ macros, and patches
all users to just use the generic ones.
In most recent code we abstained from defining type-specific macros, and
this basically removes all those added already, with the exception of
the really low-level ones.
Having explicit macros for this is not too useful, as the expression
without the extra macro is generally just 2ch wider. We should generally
emphesize generic code, unless there are really good reasons for
specific code, hence let's follow this in this case too.
Note that _cleanup_free_ and similar really low-level, libc'ish, Linux
API'ish macros continue to be defined, only the really high-level OO
ones are dropped. From now on this should really be the rule: for really
low-level stuff, such as memory allocation, fd handling and so one, go
ahead and define explicit per-type macros, but for high-level, specific
program code, just use the generic _cleanup_() macro directly, in order
to keep things simple and as readable as possible for the uninitiated.
Note that before this patch some of the APIs (notable libudev ones) were
already used with the high-level macros at some places and with the
generic _cleanup_ macro at others. With this patch we hence unify on the
latter.
2018-04-25 12:31:45 +02:00
|
|
|
_cleanup_(udev_list_cleanup) struct udev_list subsystem_match_list;
|
|
|
|
_cleanup_(udev_list_cleanup) struct udev_list tag_match_list;
|
|
|
|
_cleanup_(udev_monitor_unrefp) struct udev_monitor *udev_monitor = NULL;
|
|
|
|
_cleanup_(udev_monitor_unrefp) struct udev_monitor *kernel_monitor = NULL;
|
2013-12-18 03:49:07 +01:00
|
|
|
_cleanup_close_ int fd_ep = -1;
|
2012-01-10 01:34:15 +01:00
|
|
|
int fd_kernel = -1, fd_udev = -1;
|
|
|
|
struct epoll_event ep_kernel, ep_udev;
|
2013-12-18 03:49:07 +01:00
|
|
|
int c;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
static const struct option options[] = {
|
2013-12-18 03:48:14 +01:00
|
|
|
{ "property", no_argument, NULL, 'p' },
|
|
|
|
{ "environment", no_argument, NULL, 'e' }, /* alias for -p */
|
|
|
|
{ "kernel", no_argument, NULL, 'k' },
|
|
|
|
{ "udev", no_argument, NULL, 'u' },
|
2012-01-10 01:34:15 +01:00
|
|
|
{ "subsystem-match", required_argument, NULL, 's' },
|
2013-12-18 03:48:14 +01:00
|
|
|
{ "tag-match", required_argument, NULL, 't' },
|
2017-12-05 15:30:10 +01:00
|
|
|
{ "version", no_argument, NULL, 'V' },
|
2013-12-18 03:48:14 +01:00
|
|
|
{ "help", no_argument, NULL, 'h' },
|
2012-01-10 01:34:15 +01:00
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
udev_list_init(udev, &subsystem_match_list, true);
|
|
|
|
udev_list_init(udev, &tag_match_list, true);
|
|
|
|
|
2017-12-05 15:30:10 +01:00
|
|
|
while ((c = getopt_long(argc, argv, "pekus:t:Vh", options, NULL)) >= 0)
|
2013-12-18 03:48:14 +01:00
|
|
|
switch (c) {
|
2012-01-10 01:34:15 +01:00
|
|
|
case 'p':
|
|
|
|
case 'e':
|
|
|
|
prop = true;
|
|
|
|
break;
|
|
|
|
case 'k':
|
|
|
|
print_kernel = true;
|
|
|
|
break;
|
|
|
|
case 'u':
|
|
|
|
print_udev = true;
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
{
|
|
|
|
char subsys[UTIL_NAME_SIZE];
|
|
|
|
char *devtype;
|
|
|
|
|
2013-01-09 19:06:46 +01:00
|
|
|
strscpy(subsys, sizeof(subsys), optarg);
|
2012-01-10 01:34:15 +01:00
|
|
|
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;
|
2017-12-05 15:30:10 +01:00
|
|
|
case 'V':
|
|
|
|
print_version();
|
|
|
|
return 0;
|
2012-01-10 01:34:15 +01:00
|
|
|
case 'h':
|
2013-12-18 03:48:14 +01:00
|
|
|
help();
|
2013-12-18 03:49:07 +01:00
|
|
|
return 0;
|
2012-01-10 01:34:15 +01:00
|
|
|
default:
|
2013-12-18 03:49:07 +01:00
|
|
|
return 1;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!print_kernel && !print_udev) {
|
|
|
|
print_kernel = true;
|
|
|
|
print_udev = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set signal handlers */
|
|
|
|
act.sa_handler = sig_handler;
|
2016-12-05 23:55:18 +01:00
|
|
|
act.sa_flags = SA_RESTART;
|
2017-11-27 22:48:46 +01:00
|
|
|
assert_se(sigaction(SIGINT, &act, NULL) == 0);
|
|
|
|
assert_se(sigaction(SIGTERM, &act, NULL) == 0);
|
|
|
|
assert_se(sigemptyset(&mask) == 0);
|
|
|
|
assert_se(sigaddset(&mask, SIGINT) == 0);
|
|
|
|
assert_se(sigaddset(&mask, SIGTERM) == 0);
|
|
|
|
assert_se(sigprocmask(SIG_UNBLOCK, &mask, NULL) == 0);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2016-07-15 12:24:34 +02:00
|
|
|
/* Callers are expecting to see events as they happen: Line buffering */
|
|
|
|
setlinebuf(stdout);
|
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
fd_ep = epoll_create1(EPOLL_CLOEXEC);
|
|
|
|
if (fd_ep < 0) {
|
2014-11-28 19:29:59 +01:00
|
|
|
log_error_errno(errno, "error creating epoll fd: %m");
|
2013-12-18 03:49:07 +01:00
|
|
|
return 1;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
printf("monitor will print the received events for:\n");
|
|
|
|
if (print_udev) {
|
|
|
|
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");
|
2013-12-18 03:49:07 +01:00
|
|
|
return 1;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
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");
|
2013-12-18 03:49:07 +01:00
|
|
|
return 2;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
2014-01-31 06:51:32 +01:00
|
|
|
memzero(&ep_udev, sizeof(struct epoll_event));
|
2012-01-10 01:34:15 +01:00
|
|
|
ep_udev.events = EPOLLIN;
|
|
|
|
ep_udev.data.fd = fd_udev;
|
|
|
|
if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) {
|
2014-11-28 19:29:59 +01:00
|
|
|
log_error_errno(errno, "fail to add fd to epoll: %m");
|
2013-12-18 03:49:07 +01:00
|
|
|
return 2;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
printf("UDEV - the event which udev sends out after rule processing\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (print_kernel) {
|
|
|
|
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");
|
2013-12-18 03:49:07 +01:00
|
|
|
return 3;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
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");
|
2013-12-18 03:49:07 +01:00
|
|
|
return 4;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
2014-01-31 06:51:32 +01:00
|
|
|
memzero(&ep_kernel, sizeof(struct epoll_event));
|
2012-01-10 01:34:15 +01:00
|
|
|
ep_kernel.events = EPOLLIN;
|
|
|
|
ep_kernel.data.fd = fd_kernel;
|
|
|
|
if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_kernel, &ep_kernel) < 0) {
|
2014-11-28 19:29:59 +01:00
|
|
|
log_error_errno(errno, "fail to add fd to epoll: %m");
|
2013-12-18 03:49:07 +01:00
|
|
|
return 5;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
printf("KERNEL - the kernel uevent\n");
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
while (!udev_exit) {
|
|
|
|
int fdcount;
|
|
|
|
struct epoll_event ev[4];
|
|
|
|
int i;
|
|
|
|
|
2012-04-16 03:13:22 +02:00
|
|
|
fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1);
|
2012-01-10 01:34:15 +01:00
|
|
|
if (fdcount < 0) {
|
|
|
|
if (errno != EINTR)
|
|
|
|
fprintf(stderr, "error receiving uevent message: %m\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-12-18 03:49:07 +01:00
|
|
|
|
|
|
|
return 0;
|
2005-08-01 20:22:46 +02:00
|
|
|
}
|
2011-07-14 01:53:23 +02:00
|
|
|
|
|
|
|
const struct udevadm_cmd udevadm_monitor = {
|
2012-01-10 01:34:15 +01:00
|
|
|
.name = "monitor",
|
|
|
|
.cmd = adm_monitor,
|
2015-01-05 13:19:55 +01:00
|
|
|
.help = "Listen to kernel and udev events",
|
2011-07-14 01:53:23 +02:00
|
|
|
};
|