]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udevadm-monitor.c
f7737d079089db6f347c4f2bf51d6856b7db25a7
[thirdparty/systemd.git] / src / udev / udevadm-monitor.c
1 /* SPDX-License-Identifier: GPL-2.0+ */
2
3 #include <errno.h>
4 #include <getopt.h>
5 #include <signal.h>
6
7 #include "sd-device.h"
8 #include "sd-event.h"
9
10 #include "alloc-util.h"
11 #include "device-monitor-private.h"
12 #include "device-util.h"
13 #include "fd-util.h"
14 #include "format-util.h"
15 #include "hashmap.h"
16 #include "set.h"
17 #include "signal-util.h"
18 #include "string-util.h"
19 #include "udevadm.h"
20
21 static bool arg_show_property = false;
22 static bool arg_print_kernel = false;
23 static bool arg_print_udev = false;
24 static Set *arg_tag_filter = NULL;
25 static Hashmap *arg_subsystem_filter = NULL;
26
27 static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, void *userdata) {
28 const char *action = NULL, *devpath = NULL, *subsystem = NULL;
29 MonitorNetlinkGroup group = PTR_TO_INT(userdata);
30 struct timespec ts;
31
32 assert(device);
33 assert(IN_SET(group, MONITOR_GROUP_UDEV, MONITOR_GROUP_KERNEL));
34
35 (void) sd_device_get_property_value(device, "ACTION", &action);
36 (void) sd_device_get_devpath(device, &devpath);
37 (void) sd_device_get_subsystem(device, &subsystem);
38
39 assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
40
41 printf("%-6s[%"PRI_TIME".%06"PRI_NSEC"] %-8s %s (%s)\n",
42 group == MONITOR_GROUP_UDEV ? "UDEV" : "KERNEL",
43 ts.tv_sec, (nsec_t)ts.tv_nsec/1000,
44 action, devpath, subsystem);
45
46 if (arg_show_property) {
47 const char *key, *value;
48
49 FOREACH_DEVICE_PROPERTY(device, key, value)
50 printf("%s=%s\n", key, value);
51
52 printf("\n");
53 }
54
55 return 0;
56 }
57
58 static int setup_monitor(MonitorNetlinkGroup sender, sd_event *event, sd_device_monitor **ret) {
59 _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
60 const char *subsystem, *devtype, *tag;
61 Iterator i;
62 int r;
63
64 r = device_monitor_new_full(&monitor, sender, -1);
65 if (r < 0)
66 return log_error_errno(r, "Failed to create netlink socket: %m");
67
68 (void) sd_device_monitor_set_receive_buffer_size(monitor, 128*1024*1024);
69
70 r = sd_device_monitor_attach_event(monitor, event);
71 if (r < 0)
72 return log_error_errno(r, "Failed to attach event: %m");
73
74 HASHMAP_FOREACH_KEY(devtype, subsystem, arg_subsystem_filter, i) {
75 r = sd_device_monitor_filter_add_match_subsystem_devtype(monitor, subsystem, devtype);
76 if (r < 0)
77 return log_error_errno(r, "Failed to apply subsystem filter '%s%s%s': %m",
78 subsystem, devtype ? "/" : "", strempty(devtype));
79 }
80
81 SET_FOREACH(tag, arg_tag_filter, i) {
82 r = sd_device_monitor_filter_add_match_tag(monitor, tag);
83 if (r < 0)
84 return log_error_errno(r, "Failed to apply tag filter '%s': %m", tag);
85 }
86
87 r = sd_device_monitor_start(monitor, device_monitor_handler, INT_TO_PTR(sender));
88 if (r < 0)
89 return log_error_errno(r, "Failed to start device monitor: %m");
90
91 (void) sd_event_source_set_description(sd_device_monitor_get_event_source(monitor),
92 sender == MONITOR_GROUP_UDEV ? "device-monitor-udev" : "device-monitor-kernel");
93
94 *ret = TAKE_PTR(monitor);
95 return 0;
96 }
97
98 static int help(void) {
99 printf("%s monitor [OPTIONS]\n\n"
100 "Listen to kernel and udev events.\n\n"
101 " -h --help Show this help\n"
102 " -V --version Show package version\n"
103 " -p --property Print the event properties\n"
104 " -k --kernel Print kernel uevents\n"
105 " -u --udev Print udev events\n"
106 " -s --subsystem-match=SUBSYSTEM[/DEVTYPE] Filter events by subsystem\n"
107 " -t --tag-match=TAG Filter events by tag\n"
108 , program_invocation_short_name);
109
110 return 0;
111 }
112
113 static int parse_argv(int argc, char *argv[]) {
114 static const struct option options[] = {
115 { "property", no_argument, NULL, 'p' },
116 { "environment", no_argument, NULL, 'e' }, /* alias for -p */
117 { "kernel", no_argument, NULL, 'k' },
118 { "udev", no_argument, NULL, 'u' },
119 { "subsystem-match", required_argument, NULL, 's' },
120 { "tag-match", required_argument, NULL, 't' },
121 { "version", no_argument, NULL, 'V' },
122 { "help", no_argument, NULL, 'h' },
123 {}
124 };
125
126 int r, c;
127
128 while ((c = getopt_long(argc, argv, "pekus:t:Vh", options, NULL)) >= 0)
129 switch (c) {
130 case 'p':
131 case 'e':
132 arg_show_property = true;
133 break;
134 case 'k':
135 arg_print_kernel = true;
136 break;
137 case 'u':
138 arg_print_udev = true;
139 break;
140 case 's': {
141 _cleanup_free_ char *subsystem = NULL, *devtype = NULL;
142 const char *slash;
143
144 slash = strchr(optarg, '/');
145 if (slash) {
146 devtype = strdup(devtype + 1);
147 if (!devtype)
148 return -ENOMEM;
149
150 subsystem = strndup(optarg, devtype - optarg);
151 } else
152 subsystem = strdup(optarg);
153
154 if (!subsystem)
155 return -ENOMEM;
156
157 r = hashmap_ensure_allocated(&arg_subsystem_filter, NULL);
158 if (r < 0)
159 return r;
160
161 r = hashmap_put(arg_subsystem_filter, subsystem, devtype);
162 if (r < 0)
163 return r;
164
165 subsystem = devtype = NULL;
166 break;
167 }
168 case 't': {
169 _cleanup_free_ char *tag = NULL;
170
171 r = set_ensure_allocated(&arg_tag_filter, &string_hash_ops);
172 if (r < 0)
173 return r;
174
175 tag = strdup(optarg);
176 if (!tag)
177 return -ENOMEM;
178
179 r = set_put(arg_tag_filter, tag);
180 if (r < 0)
181 return r;
182
183 tag = NULL;
184 break;
185 }
186 case 'V':
187 return print_version();
188 case 'h':
189 return help();
190 case '?':
191 return -EINVAL;
192 default:
193 assert_not_reached("Unknown option.");
194 }
195
196 if (!arg_print_kernel && !arg_print_udev) {
197 arg_print_kernel = true;
198 arg_print_udev = true;
199 }
200
201 return 1;
202 }
203
204 int monitor_main(int argc, char *argv[], void *userdata) {
205 _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *kernel_monitor = NULL, *udev_monitor = NULL;
206 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
207 int r;
208
209 r = parse_argv(argc, argv);
210 if (r <= 0)
211 goto finalize;
212
213 /* Callers are expecting to see events as they happen: Line buffering */
214 setlinebuf(stdout);
215
216 r = sd_event_default(&event);
217 if (r < 0) {
218 log_error_errno(r, "Failed to initialize event: %m");
219 goto finalize;
220 }
221
222 assert_se(sigprocmask_many(SIG_UNBLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
223 (void) sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
224 (void) sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
225
226 printf("monitor will print the received events for:\n");
227 if (arg_print_udev) {
228 r = setup_monitor(MONITOR_GROUP_UDEV, event, &udev_monitor);
229 if (r < 0)
230 goto finalize;
231
232 printf("UDEV - the event which udev sends out after rule processing\n");
233 }
234
235 if (arg_print_kernel) {
236 r = setup_monitor(MONITOR_GROUP_KERNEL, event, &kernel_monitor);
237 if (r < 0)
238 goto finalize;
239
240 printf("KERNEL - the kernel uevent\n");
241 }
242 printf("\n");
243
244 r = sd_event_loop(event);
245 if (r < 0) {
246 log_error_errno(r, "Failed to run event loop: %m");
247 goto finalize;
248 }
249
250 r = 0;
251
252 finalize:
253 hashmap_free_free_free(arg_subsystem_filter);
254 set_free_free(arg_tag_filter);
255
256 return r;
257 }