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