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