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