1 /* SPDX-License-Identifier: GPL-2.0-or-later */
9 #include "alloc-util.h"
10 #include "device-monitor-private.h"
11 #include "device-private.h"
12 #include "device-util.h"
14 #include "format-util.h"
17 #include "signal-util.h"
18 #include "string-util.h"
21 #include "time-util.h"
23 static bool arg_show_property
= false;
24 static bool arg_print_kernel
= false;
25 static bool arg_print_udev
= false;
26 static Set
*arg_tag_filter
= NULL
;
27 static Hashmap
*arg_subsystem_filter
= NULL
;
29 static int device_monitor_handler(sd_device_monitor
*monitor
, sd_device
*device
, void *userdata
) {
30 sd_device_action_t action
= _SD_DEVICE_ACTION_INVALID
;
31 const char *devpath
= NULL
, *subsystem
= NULL
;
32 MonitorNetlinkGroup group
= PTR_TO_INT(userdata
);
36 assert(IN_SET(group
, MONITOR_GROUP_UDEV
, MONITOR_GROUP_KERNEL
));
38 (void) sd_device_get_action(device
, &action
);
39 (void) sd_device_get_devpath(device
, &devpath
);
40 (void) sd_device_get_subsystem(device
, &subsystem
);
42 assert_se(clock_gettime(CLOCK_MONOTONIC
, &ts
) == 0);
44 printf("%-6s[%"PRI_TIME
".%06"PRI_NSEC
"] %-8s %s (%s)\n",
45 group
== MONITOR_GROUP_UDEV
? "UDEV" : "KERNEL",
46 ts
.tv_sec
, (nsec_t
)ts
.tv_nsec
/1000,
47 strna(device_action_to_string(action
)),
50 if (arg_show_property
) {
51 FOREACH_DEVICE_PROPERTY(device
, key
, value
)
52 printf("%s=%s\n", key
, value
);
60 static int setup_monitor(MonitorNetlinkGroup sender
, sd_event
*event
, sd_device_monitor
**ret
) {
61 _cleanup_(sd_device_monitor_unrefp
) sd_device_monitor
*monitor
= NULL
;
62 const char *subsystem
, *devtype
, *tag
;
65 r
= device_monitor_new_full(&monitor
, sender
, -1);
67 return log_error_errno(r
, "Failed to create netlink socket: %m");
69 r
= sd_device_monitor_attach_event(monitor
, event
);
71 return log_error_errno(r
, "Failed to attach event: %m");
73 HASHMAP_FOREACH_KEY(devtype
, subsystem
, arg_subsystem_filter
) {
74 r
= sd_device_monitor_filter_add_match_subsystem_devtype(monitor
, subsystem
, devtype
);
76 return log_error_errno(r
, "Failed to apply subsystem filter '%s%s%s': %m",
77 subsystem
, devtype
? "/" : "", strempty(devtype
));
80 SET_FOREACH(tag
, arg_tag_filter
) {
81 r
= sd_device_monitor_filter_add_match_tag(monitor
, tag
);
83 return log_error_errno(r
, "Failed to apply tag filter '%s': %m", tag
);
86 r
= sd_device_monitor_start(monitor
, device_monitor_handler
, INT_TO_PTR(sender
));
88 return log_error_errno(r
, "Failed to start device monitor: %m");
90 (void) sd_device_monitor_set_description(monitor
, sender
== MONITOR_GROUP_UDEV
? "udev" : "kernel");
92 *ret
= TAKE_PTR(monitor
);
96 static int help(void) {
97 printf("%s monitor [OPTIONS]\n\n"
98 "Listen to kernel and udev events.\n\n"
99 " -h --help Show this help\n"
100 " -V --version Show package version\n"
101 " -p --property Print the event properties\n"
102 " -k --kernel Print kernel uevents\n"
103 " -u --udev Print udev events\n"
104 " -s --subsystem-match=SUBSYSTEM[/DEVTYPE] Filter events by subsystem\n"
105 " -t --tag-match=TAG Filter events by tag\n",
106 program_invocation_short_name
);
111 static int parse_argv(int argc
, char *argv
[]) {
112 static const struct option options
[] = {
113 { "property", no_argument
, NULL
, 'p' },
114 { "environment", no_argument
, NULL
, 'e' }, /* alias for -p */
115 { "kernel", no_argument
, NULL
, 'k' },
116 { "udev", no_argument
, NULL
, 'u' },
117 { "subsystem-match", required_argument
, NULL
, 's' },
118 { "tag-match", required_argument
, NULL
, 't' },
119 { "version", no_argument
, NULL
, 'V' },
120 { "help", no_argument
, NULL
, 'h' },
126 while ((c
= getopt_long(argc
, argv
, "pekus:t:Vh", options
, NULL
)) >= 0)
130 arg_show_property
= true;
133 arg_print_kernel
= true;
136 arg_print_udev
= true;
139 _cleanup_free_
char *subsystem
= NULL
, *devtype
= NULL
;
142 slash
= strchr(optarg
, '/');
144 devtype
= strdup(slash
+ 1);
148 subsystem
= strndup(optarg
, slash
- optarg
);
150 subsystem
= strdup(optarg
);
155 r
= hashmap_ensure_put(&arg_subsystem_filter
, NULL
, subsystem
, devtype
);
164 /* optarg is stored in argv[], so we don't need to copy it */
165 r
= set_ensure_put(&arg_tag_filter
, &string_hash_ops
, optarg
);
171 return print_version();
177 assert_not_reached();
180 if (!arg_print_kernel
&& !arg_print_udev
) {
181 arg_print_kernel
= true;
182 arg_print_udev
= true;
188 int monitor_main(int argc
, char *argv
[], void *userdata
) {
189 _cleanup_(sd_device_monitor_unrefp
) sd_device_monitor
*kernel_monitor
= NULL
, *udev_monitor
= NULL
;
190 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
193 r
= parse_argv(argc
, argv
);
197 if (running_in_chroot() > 0) {
198 log_info("Running in chroot, ignoring request.");
202 /* Callers are expecting to see events as they happen: Line buffering */
205 r
= sd_event_default(&event
);
207 log_error_errno(r
, "Failed to initialize event: %m");
211 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
212 (void) sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
213 (void) sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
215 printf("monitor will print the received events for:\n");
216 if (arg_print_udev
) {
217 r
= setup_monitor(MONITOR_GROUP_UDEV
, event
, &udev_monitor
);
221 printf("UDEV - the event which udev sends out after rule processing\n");
224 if (arg_print_kernel
) {
225 r
= setup_monitor(MONITOR_GROUP_KERNEL
, event
, &kernel_monitor
);
229 printf("KERNEL - the kernel uevent\n");
233 r
= sd_event_loop(event
);
235 log_error_errno(r
, "Failed to run event loop: %m");
242 hashmap_free_free_free(arg_subsystem_filter
);
243 set_free(arg_tag_filter
);