]>
Commit | Line | Data |
---|---|---|
e7145211 | 1 | /* SPDX-License-Identifier: GPL-2.0+ */ |
0d5be398 | 2 | |
07630cea LP |
3 | #include <errno.h> |
4 | #include <fcntl.h> | |
5 | #include <getopt.h> | |
0d5be398 | 6 | #include <stddef.h> |
0d5be398 | 7 | #include <stdio.h> |
07630cea | 8 | #include <string.h> |
13aca847 | 9 | #include <sys/epoll.h> |
0d5be398 | 10 | #include <unistd.h> |
0d5be398 | 11 | |
13aca847 | 12 | #include "device-enumerator-private.h" |
f8d596cd | 13 | #include "device-monitor-private.h" |
792cc203 | 14 | #include "fd-util.h" |
c7d942d6 | 15 | #include "path-util.h" |
792cc203 | 16 | #include "set.h" |
07630cea | 17 | #include "string-util.h" |
13aca847 | 18 | #include "strv.h" |
3d05193e | 19 | #include "udevadm.h" |
d6170d27 | 20 | #include "udevadm-util.h" |
0d5be398 | 21 | |
13aca847 YW |
22 | static bool arg_verbose = false; |
23 | static bool arg_dry_run = false; | |
c48622cc | 24 | |
13aca847 YW |
25 | static int exec_list(sd_device_enumerator *e, const char *action, Set *settle_set) { |
26 | sd_device *d; | |
6bcc09be | 27 | int r; |
912541b0 | 28 | |
13aca847 | 29 | FOREACH_DEVICE_AND_SUBSYSTEM(e, d) { |
c7d942d6 | 30 | _cleanup_free_ char *filename = NULL; |
1b113391 | 31 | _cleanup_close_ int fd = -1; |
c7d942d6 | 32 | const char *syspath; |
912541b0 | 33 | |
13aca847 YW |
34 | if (sd_device_get_syspath(d, &syspath) < 0) |
35 | continue; | |
36 | ||
37 | if (arg_verbose) | |
792cc203 | 38 | printf("%s\n", syspath); |
13aca847 | 39 | if (arg_dry_run) |
912541b0 | 40 | continue; |
792cc203 | 41 | |
c7d942d6 YW |
42 | filename = path_join(NULL, syspath, "uevent"); |
43 | if (!filename) | |
44 | return log_oom(); | |
45 | ||
c8a202b7 | 46 | fd = open(filename, O_WRONLY|O_CLOEXEC); |
baa30fbc | 47 | if (fd < 0) |
912541b0 | 48 | continue; |
1b113391 | 49 | |
6bcc09be ZJS |
50 | if (settle_set) { |
51 | r = set_put_strdup(settle_set, syspath); | |
52 | if (r < 0) | |
53 | return log_oom(); | |
54 | } | |
1b113391 | 55 | |
912541b0 | 56 | if (write(fd, action, strlen(action)) < 0) |
c7d942d6 | 57 | log_debug_errno(errno, "Failed to write '%s' to '%s', ignoring: %m", action, filename); |
912541b0 | 58 | } |
6bcc09be ZJS |
59 | |
60 | return 0; | |
0d5be398 KS |
61 | } |
62 | ||
c7d942d6 YW |
63 | static char* keyval(const char *str, const char **key, const char **val) { |
64 | char *buf, *pos; | |
65 | ||
66 | buf = strdup(str); | |
67 | if (!buf) | |
68 | return NULL; | |
912541b0 | 69 | |
912541b0 | 70 | pos = strchr(buf, '='); |
c7d942d6 | 71 | if (pos) { |
912541b0 KS |
72 | pos[0] = 0; |
73 | pos++; | |
74 | } | |
c7d942d6 YW |
75 | |
76 | *key = buf; | |
912541b0 | 77 | *val = pos; |
c7d942d6 | 78 | |
912541b0 | 79 | return buf; |
80381823 KS |
80 | } |
81 | ||
bb084d42 | 82 | static int help(void) { |
5639df9a | 83 | printf("%s trigger [OPTIONS] DEVPATH\n\n" |
5ac0162c LP |
84 | "Request events from the kernel.\n\n" |
85 | " -h --help Show this help\n" | |
5639df9a | 86 | " -V --version Show package version\n" |
5ac0162c LP |
87 | " -v --verbose Print the list of devices while running\n" |
88 | " -n --dry-run Do not actually trigger the events\n" | |
89 | " -t --type= Type of events to trigger\n" | |
90 | " devices sysfs devices (default)\n" | |
91 | " subsystems sysfs subsystems and drivers\n" | |
92 | " -c --action=ACTION Event action value, default is \"change\"\n" | |
93 | " -s --subsystem-match=SUBSYSTEM Trigger devices from a matching subsystem\n" | |
94 | " -S --subsystem-nomatch=SUBSYSTEM Exclude devices from a matching subsystem\n" | |
95 | " -a --attr-match=FILE[=VALUE] Trigger devices with a matching attribute\n" | |
96 | " -A --attr-nomatch=FILE[=VALUE] Exclude devices with a matching attribute\n" | |
97 | " -p --property-match=KEY=VALUE Trigger devices with a matching property\n" | |
98 | " -g --tag-match=KEY=VALUE Trigger devices with a matching property\n" | |
99 | " -y --sysname-match=NAME Trigger devices with this /sys path\n" | |
100 | " --name-match=NAME Trigger devices with this /dev name\n" | |
101 | " -b --parent-match=NAME Trigger devices with that parent device\n" | |
792cc203 | 102 | " -w --settle Wait for the triggered events to complete\n" |
5ac0162c | 103 | , program_invocation_short_name); |
bb084d42 YW |
104 | |
105 | return 0; | |
7643ac9a ZJS |
106 | } |
107 | ||
3d05193e | 108 | int trigger_main(int argc, char *argv[], void *userdata) { |
80877656 ZJS |
109 | enum { |
110 | ARG_NAME = 0x100, | |
111 | }; | |
112 | ||
912541b0 | 113 | static const struct option options[] = { |
80877656 ZJS |
114 | { "verbose", no_argument, NULL, 'v' }, |
115 | { "dry-run", no_argument, NULL, 'n' }, | |
116 | { "type", required_argument, NULL, 't' }, | |
117 | { "action", required_argument, NULL, 'c' }, | |
118 | { "subsystem-match", required_argument, NULL, 's' }, | |
119 | { "subsystem-nomatch", required_argument, NULL, 'S' }, | |
120 | { "attr-match", required_argument, NULL, 'a' }, | |
121 | { "attr-nomatch", required_argument, NULL, 'A' }, | |
122 | { "property-match", required_argument, NULL, 'p' }, | |
123 | { "tag-match", required_argument, NULL, 'g' }, | |
124 | { "sysname-match", required_argument, NULL, 'y' }, | |
125 | { "name-match", required_argument, NULL, ARG_NAME }, | |
126 | { "parent-match", required_argument, NULL, 'b' }, | |
792cc203 | 127 | { "settle", no_argument, NULL, 'w' }, |
5639df9a | 128 | { "version", no_argument, NULL, 'V' }, |
80877656 | 129 | { "help", no_argument, NULL, 'h' }, |
912541b0 KS |
130 | {} |
131 | }; | |
132 | enum { | |
133 | TYPE_DEVICES, | |
134 | TYPE_SUBSYSTEMS, | |
135 | } device_type = TYPE_DEVICES; | |
136 | const char *action = "change"; | |
13aca847 | 137 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; |
f8d596cd YW |
138 | _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *m = NULL; |
139 | _cleanup_set_free_free_ Set *settle_set = NULL; | |
792cc203 | 140 | _cleanup_close_ int fd_ep = -1; |
f8d596cd YW |
141 | struct epoll_event ep_monitor; |
142 | int c, r, fd_monitor = -1; | |
792cc203 | 143 | bool settle = false; |
912541b0 | 144 | |
13aca847 YW |
145 | r = sd_device_enumerator_new(&e); |
146 | if (r < 0) | |
147 | return r; | |
148 | ||
149 | r = sd_device_enumerator_allow_uninitialized(e); | |
150 | if (r < 0) | |
151 | return r; | |
912541b0 | 152 | |
792cc203 | 153 | while ((c = getopt_long(argc, argv, "vnt:c:s:S:a:A:p:g:y:b:wVh", options, NULL)) >= 0) { |
c7d942d6 YW |
154 | _cleanup_free_ char *buf = NULL; |
155 | const char *key, *val; | |
912541b0 | 156 | |
7643ac9a | 157 | switch (c) { |
912541b0 | 158 | case 'v': |
13aca847 | 159 | arg_verbose = true; |
912541b0 KS |
160 | break; |
161 | case 'n': | |
13aca847 | 162 | arg_dry_run = true; |
912541b0 KS |
163 | break; |
164 | case 't': | |
44433ebd | 165 | if (streq(optarg, "devices")) |
912541b0 | 166 | device_type = TYPE_DEVICES; |
44433ebd | 167 | else if (streq(optarg, "subsystems")) |
912541b0 | 168 | device_type = TYPE_SUBSYSTEMS; |
44433ebd | 169 | else { |
c7d942d6 | 170 | log_error("Unknown type --type=%s", optarg); |
bb084d42 | 171 | return -EINVAL; |
912541b0 KS |
172 | } |
173 | break; | |
174 | case 'c': | |
bb084d42 | 175 | if (STR_IN_SET(optarg, "add", "remove", "change")) |
c5383e79 | 176 | action = optarg; |
bb084d42 | 177 | else { |
c7d942d6 | 178 | log_error("Unknown action '%s'", optarg); |
bb084d42 YW |
179 | return -EINVAL; |
180 | } | |
44433ebd | 181 | |
912541b0 KS |
182 | break; |
183 | case 's': | |
13aca847 | 184 | r = sd_device_enumerator_add_match_subsystem(e, optarg, true); |
bb084d42 | 185 | if (r < 0) |
c7d942d6 | 186 | return log_error_errno(r, "Failed to add subsystem match '%s': %m", optarg); |
912541b0 KS |
187 | break; |
188 | case 'S': | |
13aca847 | 189 | r = sd_device_enumerator_add_match_subsystem(e, optarg, false); |
bb084d42 | 190 | if (r < 0) |
c7d942d6 | 191 | return log_error_errno(r, "Failed to add negative subsystem match '%s': %m", optarg); |
912541b0 KS |
192 | break; |
193 | case 'a': | |
c7d942d6 YW |
194 | buf = keyval(optarg, &key, &val); |
195 | if (!buf) | |
196 | return log_oom(); | |
13aca847 | 197 | r = sd_device_enumerator_add_match_sysattr(e, key, val, true); |
bb084d42 | 198 | if (r < 0) |
c7d942d6 | 199 | return log_error_errno(r, "Failed to add sysattr match '%s=%s': %m", key, val); |
912541b0 KS |
200 | break; |
201 | case 'A': | |
c7d942d6 YW |
202 | buf = keyval(optarg, &key, &val); |
203 | if (!buf) | |
204 | return log_oom(); | |
13aca847 | 205 | r = sd_device_enumerator_add_match_sysattr(e, key, val, false); |
bb084d42 | 206 | if (r < 0) |
c7d942d6 | 207 | return log_error_errno(r, "Failed to add negative sysattr match '%s=%s': %m", key, val); |
912541b0 KS |
208 | break; |
209 | case 'p': | |
c7d942d6 YW |
210 | buf = keyval(optarg, &key, &val); |
211 | if (!buf) | |
212 | return log_oom(); | |
13aca847 | 213 | r = sd_device_enumerator_add_match_property(e, key, val); |
bb084d42 | 214 | if (r < 0) |
c7d942d6 | 215 | return log_error_errno(r, "Failed to add property match '%s=%s': %m", key, val); |
912541b0 KS |
216 | break; |
217 | case 'g': | |
13aca847 | 218 | r = sd_device_enumerator_add_match_tag(e, optarg); |
bb084d42 | 219 | if (r < 0) |
c7d942d6 | 220 | return log_error_errno(r, "Failed to add tag match '%s': %m", optarg); |
912541b0 KS |
221 | break; |
222 | case 'y': | |
13aca847 | 223 | r = sd_device_enumerator_add_match_sysname(e, optarg); |
bb084d42 | 224 | if (r < 0) |
c7d942d6 | 225 | return log_error_errno(r, "Failed to add sysname match '%s': %m", optarg); |
912541b0 KS |
226 | break; |
227 | case 'b': { | |
13aca847 | 228 | _cleanup_(sd_device_unrefp) sd_device *dev = NULL; |
d6170d27 | 229 | |
13aca847 YW |
230 | r = find_device(optarg, "/sys", &dev); |
231 | if (r < 0) | |
c7d942d6 | 232 | return log_error_errno(r, "Failed to open the device '%s': %m", optarg); |
d6170d27 | 233 | |
13aca847 | 234 | r = sd_device_enumerator_add_match_parent(e, dev); |
bb084d42 | 235 | if (r < 0) |
c7d942d6 | 236 | return log_error_errno(r, "Failed to add parent match '%s': %m", optarg); |
912541b0 KS |
237 | break; |
238 | } | |
792cc203 MH |
239 | case 'w': |
240 | settle = true; | |
241 | break; | |
d6170d27 | 242 | |
80877656 | 243 | case ARG_NAME: { |
13aca847 | 244 | _cleanup_(sd_device_unrefp) sd_device *dev = NULL; |
80877656 | 245 | |
13aca847 YW |
246 | r = find_device(optarg, "/dev/", &dev); |
247 | if (r < 0) | |
c7d942d6 | 248 | return log_error_errno(r, "Failed to open the device '%s': %m", optarg); |
80877656 | 249 | |
13aca847 | 250 | r = sd_device_enumerator_add_match_parent(e, dev); |
bb084d42 | 251 | if (r < 0) |
c7d942d6 | 252 | return log_error_errno(r, "Failed to add parent match '%s': %m", optarg); |
80877656 ZJS |
253 | break; |
254 | } | |
255 | ||
5639df9a | 256 | case 'V': |
51b006e1 | 257 | return print_version(); |
912541b0 | 258 | case 'h': |
bb084d42 | 259 | return help(); |
7643ac9a | 260 | case '?': |
bb084d42 | 261 | return -EINVAL; |
7643ac9a ZJS |
262 | default: |
263 | assert_not_reached("Unknown option"); | |
912541b0 KS |
264 | } |
265 | } | |
266 | ||
80877656 | 267 | for (; optind < argc; optind++) { |
13aca847 | 268 | _cleanup_(sd_device_unrefp) sd_device *dev = NULL; |
80877656 | 269 | |
13aca847 YW |
270 | r = find_device(argv[optind], NULL, &dev); |
271 | if (r < 0) | |
c7d942d6 | 272 | return log_error_errno(r, "Failed to open the device '%s': %m", argv[optind]); |
80877656 | 273 | |
13aca847 | 274 | r = sd_device_enumerator_add_match_parent(e, dev); |
bb084d42 | 275 | if (r < 0) |
c7d942d6 | 276 | return log_error_errno(r, "Failed to add parent match '%s': %m", argv[optind]); |
7643ac9a ZJS |
277 | } |
278 | ||
792cc203 MH |
279 | if (settle) { |
280 | fd_ep = epoll_create1(EPOLL_CLOEXEC); | |
bb084d42 | 281 | if (fd_ep < 0) |
c7d942d6 | 282 | return log_error_errno(errno, "Failed to create epoll fd: %m"); |
792cc203 | 283 | |
f8d596cd YW |
284 | r = sd_device_monitor_new(&m); |
285 | if (r < 0) | |
286 | return log_error_errno(r, "Failed to create device monitor object: %m"); | |
bb084d42 | 287 | |
f8d596cd YW |
288 | fd_monitor = device_monitor_get_fd(m); |
289 | if (fd_monitor < 0) | |
290 | return log_error_errno(fd_monitor, "Failed to get monitor fd: %m"); | |
792cc203 | 291 | |
f8d596cd | 292 | r = device_monitor_enable_receiving(m); |
bb084d42 | 293 | if (r < 0) |
f8d596cd | 294 | return log_error_errno(r, "Failed to subscribe udev events: %m"); |
792cc203 | 295 | |
f8d596cd | 296 | ep_monitor = (struct epoll_event) { |
13aca847 | 297 | .events = EPOLLIN, |
f8d596cd | 298 | .data.fd = fd_monitor, |
13aca847 | 299 | }; |
f8d596cd YW |
300 | if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) |
301 | return log_error_errno(errno, "Failed to add fd to epoll: %m"); | |
792cc203 MH |
302 | |
303 | settle_set = set_new(&string_hash_ops); | |
bb084d42 YW |
304 | if (!settle_set) |
305 | return log_oom(); | |
792cc203 MH |
306 | } |
307 | ||
912541b0 KS |
308 | switch (device_type) { |
309 | case TYPE_SUBSYSTEMS: | |
13aca847 YW |
310 | r = device_enumerator_scan_subsystems(e); |
311 | if (r < 0) | |
312 | return log_error_errno(r, "Failed to scan subsystems: %m"); | |
792cc203 | 313 | break; |
912541b0 | 314 | case TYPE_DEVICES: |
13aca847 YW |
315 | r = device_enumerator_scan_devices(e); |
316 | if (r < 0) | |
317 | return log_error_errno(r, "Failed to scan devices: %m"); | |
792cc203 | 318 | break; |
912541b0 | 319 | default: |
c7d942d6 | 320 | assert_not_reached("Unknown device type"); |
912541b0 | 321 | } |
13aca847 | 322 | r = exec_list(e, action, settle_set); |
6bcc09be | 323 | if (r < 0) |
bb084d42 | 324 | return r; |
792cc203 MH |
325 | |
326 | while (!set_isempty(settle_set)) { | |
327 | int fdcount; | |
328 | struct epoll_event ev[4]; | |
329 | int i; | |
330 | ||
331 | fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1); | |
332 | if (fdcount < 0) { | |
333 | if (errno != EINTR) | |
c7d942d6 | 334 | log_error_errno(errno, "Failed to receive uevent message: %m"); |
792cc203 MH |
335 | continue; |
336 | } | |
337 | ||
338 | for (i = 0; i < fdcount; i++) { | |
13aca847 | 339 | _cleanup_(sd_device_unrefp) sd_device *dev = NULL; |
c7d942d6 | 340 | const char *syspath; |
13aca847 | 341 | |
f8d596cd | 342 | if (!(ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN)) |
13aca847 YW |
343 | continue; |
344 | ||
f8d596cd | 345 | if (device_monitor_receive_device(m, &dev) <= 0) |
13aca847 YW |
346 | continue; |
347 | ||
348 | if (sd_device_get_syspath(dev, &syspath) < 0) | |
349 | continue; | |
350 | ||
351 | if (arg_verbose) | |
352 | printf("settle %s\n", syspath); | |
353 | ||
354 | if (!set_remove(settle_set, syspath)) | |
355 | log_debug("Got epoll event on syspath %s not present in syspath set", syspath); | |
792cc203 MH |
356 | } |
357 | } | |
358 | ||
359 | return 0; | |
0d5be398 | 360 | } |