]>
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" |
dcee4a13 | 10 | #include "device-private.h" |
792cc203 | 11 | #include "fd-util.h" |
92c40e1d | 12 | #include "fileio.h" |
c7d942d6 | 13 | #include "path-util.h" |
2001622c | 14 | #include "process-util.h" |
792cc203 | 15 | #include "set.h" |
07630cea | 16 | #include "string-util.h" |
13aca847 | 17 | #include "strv.h" |
3d05193e | 18 | #include "udevadm.h" |
d6170d27 | 19 | #include "udevadm-util.h" |
2001622c | 20 | #include "udev-ctrl.h" |
c494b739 | 21 | #include "virt.h" |
0d5be398 | 22 | |
13aca847 YW |
23 | static bool arg_verbose = false; |
24 | static bool arg_dry_run = false; | |
c48622cc | 25 | |
13aca847 YW |
26 | static int exec_list(sd_device_enumerator *e, const char *action, Set *settle_set) { |
27 | sd_device *d; | |
97afc035 | 28 | int r, ret = 0; |
912541b0 | 29 | |
13aca847 | 30 | FOREACH_DEVICE_AND_SUBSYSTEM(e, d) { |
c7d942d6 | 31 | _cleanup_free_ char *filename = NULL; |
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 | |
62a85ee0 | 42 | filename = path_join(syspath, "uevent"); |
c7d942d6 YW |
43 | if (!filename) |
44 | return log_oom(); | |
45 | ||
92c40e1d YW |
46 | r = write_string_file(filename, action, WRITE_STRING_FILE_DISABLE_BUFFER); |
47 | if (r < 0) { | |
dc11d2bf | 48 | bool ignore = IN_SET(r, -ENOENT, -EACCES, -ENODEV, -EROFS); |
67acde48 YW |
49 | |
50 | log_full_errno(ignore ? LOG_DEBUG : LOG_ERR, r, | |
dc11d2bf YW |
51 | "Failed to write '%s' to '%s'%s: %m", |
52 | action, filename, ignore ? ", ignoring" : ""); | |
53 | if (r == -EROFS) | |
54 | return 0; /* Read only filesystem. Return earlier. */ | |
67acde48 | 55 | if (ret == 0 && !ignore) |
97afc035 | 56 | ret = r; |
912541b0 | 57 | continue; |
92c40e1d | 58 | } |
1b113391 | 59 | |
6bcc09be ZJS |
60 | if (settle_set) { |
61 | r = set_put_strdup(settle_set, syspath); | |
62 | if (r < 0) | |
63 | return log_oom(); | |
64 | } | |
912541b0 | 65 | } |
6bcc09be | 66 | |
97afc035 | 67 | return ret; |
0d5be398 KS |
68 | } |
69 | ||
fb3d8e9f | 70 | static int device_monitor_handler(sd_device_monitor *m, sd_device *dev, void *userdata) { |
1f682e24 | 71 | _cleanup_free_ char *val = NULL; |
fb3d8e9f YW |
72 | Set *settle_set = userdata; |
73 | const char *syspath; | |
74 | ||
75 | assert(dev); | |
76 | assert(settle_set); | |
77 | ||
78 | if (sd_device_get_syspath(dev, &syspath) < 0) | |
79 | return 0; | |
80 | ||
81 | if (arg_verbose) | |
82 | printf("settle %s\n", syspath); | |
83 | ||
1f682e24 YW |
84 | val = set_remove(settle_set, syspath); |
85 | if (!val) | |
fb3d8e9f YW |
86 | log_debug("Got epoll event on syspath %s not present in syspath set", syspath); |
87 | ||
88 | if (set_isempty(settle_set)) | |
89 | return sd_event_exit(sd_device_monitor_get_event(m), 0); | |
90 | ||
91 | return 0; | |
92 | } | |
93 | ||
c7d942d6 YW |
94 | static char* keyval(const char *str, const char **key, const char **val) { |
95 | char *buf, *pos; | |
96 | ||
97 | buf = strdup(str); | |
98 | if (!buf) | |
99 | return NULL; | |
912541b0 | 100 | |
912541b0 | 101 | pos = strchr(buf, '='); |
c7d942d6 | 102 | if (pos) { |
912541b0 KS |
103 | pos[0] = 0; |
104 | pos++; | |
105 | } | |
c7d942d6 YW |
106 | |
107 | *key = buf; | |
912541b0 | 108 | *val = pos; |
c7d942d6 | 109 | |
912541b0 | 110 | return buf; |
80381823 KS |
111 | } |
112 | ||
bb084d42 | 113 | static int help(void) { |
5639df9a | 114 | printf("%s trigger [OPTIONS] DEVPATH\n\n" |
5ac0162c LP |
115 | "Request events from the kernel.\n\n" |
116 | " -h --help Show this help\n" | |
5639df9a | 117 | " -V --version Show package version\n" |
5ac0162c LP |
118 | " -v --verbose Print the list of devices while running\n" |
119 | " -n --dry-run Do not actually trigger the events\n" | |
120 | " -t --type= Type of events to trigger\n" | |
121 | " devices sysfs devices (default)\n" | |
122 | " subsystems sysfs subsystems and drivers\n" | |
6d22bd87 | 123 | " -c --action=ACTION|help Event action value, default is \"change\"\n" |
5ac0162c LP |
124 | " -s --subsystem-match=SUBSYSTEM Trigger devices from a matching subsystem\n" |
125 | " -S --subsystem-nomatch=SUBSYSTEM Exclude devices from a matching subsystem\n" | |
126 | " -a --attr-match=FILE[=VALUE] Trigger devices with a matching attribute\n" | |
127 | " -A --attr-nomatch=FILE[=VALUE] Exclude devices with a matching attribute\n" | |
128 | " -p --property-match=KEY=VALUE Trigger devices with a matching property\n" | |
129 | " -g --tag-match=KEY=VALUE Trigger devices with a matching property\n" | |
130 | " -y --sysname-match=NAME Trigger devices with this /sys path\n" | |
131 | " --name-match=NAME Trigger devices with this /dev name\n" | |
132 | " -b --parent-match=NAME Trigger devices with that parent device\n" | |
792cc203 | 133 | " -w --settle Wait for the triggered events to complete\n" |
2001622c YW |
134 | " --wait-daemon[=SECONDS] Wait for udevd daemon to be initialized\n" |
135 | " before triggering uevents\n" | |
5ac0162c | 136 | , program_invocation_short_name); |
bb084d42 YW |
137 | |
138 | return 0; | |
7643ac9a ZJS |
139 | } |
140 | ||
3d05193e | 141 | int trigger_main(int argc, char *argv[], void *userdata) { |
80877656 ZJS |
142 | enum { |
143 | ARG_NAME = 0x100, | |
2001622c | 144 | ARG_PING, |
80877656 ZJS |
145 | }; |
146 | ||
912541b0 | 147 | static const struct option options[] = { |
80877656 ZJS |
148 | { "verbose", no_argument, NULL, 'v' }, |
149 | { "dry-run", no_argument, NULL, 'n' }, | |
150 | { "type", required_argument, NULL, 't' }, | |
151 | { "action", required_argument, NULL, 'c' }, | |
152 | { "subsystem-match", required_argument, NULL, 's' }, | |
153 | { "subsystem-nomatch", required_argument, NULL, 'S' }, | |
154 | { "attr-match", required_argument, NULL, 'a' }, | |
155 | { "attr-nomatch", required_argument, NULL, 'A' }, | |
156 | { "property-match", required_argument, NULL, 'p' }, | |
157 | { "tag-match", required_argument, NULL, 'g' }, | |
158 | { "sysname-match", required_argument, NULL, 'y' }, | |
159 | { "name-match", required_argument, NULL, ARG_NAME }, | |
160 | { "parent-match", required_argument, NULL, 'b' }, | |
792cc203 | 161 | { "settle", no_argument, NULL, 'w' }, |
2001622c | 162 | { "wait-daemon", optional_argument, NULL, ARG_PING }, |
5639df9a | 163 | { "version", no_argument, NULL, 'V' }, |
80877656 | 164 | { "help", no_argument, NULL, 'h' }, |
912541b0 KS |
165 | {} |
166 | }; | |
167 | enum { | |
168 | TYPE_DEVICES, | |
169 | TYPE_SUBSYSTEMS, | |
170 | } device_type = TYPE_DEVICES; | |
171 | const char *action = "change"; | |
13aca847 | 172 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; |
f8d596cd | 173 | _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *m = NULL; |
fb3d8e9f | 174 | _cleanup_(sd_event_unrefp) sd_event *event = NULL; |
f8d596cd | 175 | _cleanup_set_free_free_ Set *settle_set = NULL; |
2001622c YW |
176 | usec_t ping_timeout_usec = 5 * USEC_PER_SEC; |
177 | bool settle = false, ping = false; | |
fb3d8e9f | 178 | int c, r; |
912541b0 | 179 | |
c494b739 YW |
180 | if (running_in_chroot() > 0) { |
181 | log_info("Running in chroot, ignoring request."); | |
182 | return 0; | |
183 | } | |
184 | ||
13aca847 YW |
185 | r = sd_device_enumerator_new(&e); |
186 | if (r < 0) | |
187 | return r; | |
188 | ||
189 | r = sd_device_enumerator_allow_uninitialized(e); | |
190 | if (r < 0) | |
191 | return r; | |
912541b0 | 192 | |
792cc203 | 193 | while ((c = getopt_long(argc, argv, "vnt:c:s:S:a:A:p:g:y:b:wVh", options, NULL)) >= 0) { |
c7d942d6 YW |
194 | _cleanup_free_ char *buf = NULL; |
195 | const char *key, *val; | |
912541b0 | 196 | |
7643ac9a | 197 | switch (c) { |
912541b0 | 198 | case 'v': |
13aca847 | 199 | arg_verbose = true; |
912541b0 KS |
200 | break; |
201 | case 'n': | |
13aca847 | 202 | arg_dry_run = true; |
912541b0 KS |
203 | break; |
204 | case 't': | |
44433ebd | 205 | if (streq(optarg, "devices")) |
912541b0 | 206 | device_type = TYPE_DEVICES; |
44433ebd | 207 | else if (streq(optarg, "subsystems")) |
912541b0 | 208 | device_type = TYPE_SUBSYSTEMS; |
47c8fcbe YW |
209 | else |
210 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown type --type=%s", optarg); | |
912541b0 KS |
211 | break; |
212 | case 'c': | |
6d22bd87 YW |
213 | if (streq(optarg, "help")) { |
214 | dump_device_action_table(); | |
215 | return 0; | |
216 | } | |
dcee4a13 YW |
217 | if (device_action_from_string(optarg) < 0) |
218 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown action '%s'", optarg); | |
44433ebd | 219 | |
dcee4a13 | 220 | action = optarg; |
912541b0 KS |
221 | break; |
222 | case 's': | |
13aca847 | 223 | r = sd_device_enumerator_add_match_subsystem(e, optarg, true); |
bb084d42 | 224 | if (r < 0) |
c7d942d6 | 225 | return log_error_errno(r, "Failed to add subsystem match '%s': %m", optarg); |
912541b0 KS |
226 | break; |
227 | case 'S': | |
13aca847 | 228 | r = sd_device_enumerator_add_match_subsystem(e, optarg, false); |
bb084d42 | 229 | if (r < 0) |
c7d942d6 | 230 | return log_error_errno(r, "Failed to add negative subsystem match '%s': %m", optarg); |
912541b0 KS |
231 | break; |
232 | case 'a': | |
c7d942d6 YW |
233 | buf = keyval(optarg, &key, &val); |
234 | if (!buf) | |
235 | return log_oom(); | |
13aca847 | 236 | r = sd_device_enumerator_add_match_sysattr(e, key, val, true); |
bb084d42 | 237 | if (r < 0) |
c7d942d6 | 238 | return log_error_errno(r, "Failed to add sysattr match '%s=%s': %m", key, val); |
912541b0 KS |
239 | break; |
240 | case 'A': | |
c7d942d6 YW |
241 | buf = keyval(optarg, &key, &val); |
242 | if (!buf) | |
243 | return log_oom(); | |
13aca847 | 244 | r = sd_device_enumerator_add_match_sysattr(e, key, val, false); |
bb084d42 | 245 | if (r < 0) |
c7d942d6 | 246 | return log_error_errno(r, "Failed to add negative sysattr match '%s=%s': %m", key, val); |
912541b0 KS |
247 | break; |
248 | case 'p': | |
c7d942d6 YW |
249 | buf = keyval(optarg, &key, &val); |
250 | if (!buf) | |
251 | return log_oom(); | |
13aca847 | 252 | r = sd_device_enumerator_add_match_property(e, key, val); |
bb084d42 | 253 | if (r < 0) |
c7d942d6 | 254 | return log_error_errno(r, "Failed to add property match '%s=%s': %m", key, val); |
912541b0 KS |
255 | break; |
256 | case 'g': | |
13aca847 | 257 | r = sd_device_enumerator_add_match_tag(e, optarg); |
bb084d42 | 258 | if (r < 0) |
c7d942d6 | 259 | return log_error_errno(r, "Failed to add tag match '%s': %m", optarg); |
912541b0 KS |
260 | break; |
261 | case 'y': | |
13aca847 | 262 | r = sd_device_enumerator_add_match_sysname(e, optarg); |
bb084d42 | 263 | if (r < 0) |
c7d942d6 | 264 | return log_error_errno(r, "Failed to add sysname match '%s': %m", optarg); |
912541b0 KS |
265 | break; |
266 | case 'b': { | |
13aca847 | 267 | _cleanup_(sd_device_unrefp) sd_device *dev = NULL; |
d6170d27 | 268 | |
13aca847 YW |
269 | r = find_device(optarg, "/sys", &dev); |
270 | if (r < 0) | |
c7d942d6 | 271 | return log_error_errno(r, "Failed to open the device '%s': %m", optarg); |
d6170d27 | 272 | |
5b3b0733 | 273 | r = device_enumerator_add_match_parent_incremental(e, dev); |
bb084d42 | 274 | if (r < 0) |
c7d942d6 | 275 | return log_error_errno(r, "Failed to add parent match '%s': %m", optarg); |
912541b0 KS |
276 | break; |
277 | } | |
792cc203 MH |
278 | case 'w': |
279 | settle = true; | |
280 | break; | |
d6170d27 | 281 | |
80877656 | 282 | case ARG_NAME: { |
13aca847 | 283 | _cleanup_(sd_device_unrefp) sd_device *dev = NULL; |
80877656 | 284 | |
13aca847 YW |
285 | r = find_device(optarg, "/dev/", &dev); |
286 | if (r < 0) | |
c7d942d6 | 287 | return log_error_errno(r, "Failed to open the device '%s': %m", optarg); |
80877656 | 288 | |
5b3b0733 | 289 | r = device_enumerator_add_match_parent_incremental(e, dev); |
bb084d42 | 290 | if (r < 0) |
c7d942d6 | 291 | return log_error_errno(r, "Failed to add parent match '%s': %m", optarg); |
80877656 ZJS |
292 | break; |
293 | } | |
294 | ||
2001622c YW |
295 | case ARG_PING: { |
296 | ping = true; | |
297 | if (optarg) { | |
298 | r = parse_sec(optarg, &ping_timeout_usec); | |
299 | if (r < 0) | |
300 | log_error_errno(r, "Failed to parse timeout value '%s', ignoring: %m", optarg); | |
301 | } | |
302 | break; | |
303 | } | |
304 | ||
5639df9a | 305 | case 'V': |
51b006e1 | 306 | return print_version(); |
912541b0 | 307 | case 'h': |
bb084d42 | 308 | return help(); |
7643ac9a | 309 | case '?': |
bb084d42 | 310 | return -EINVAL; |
7643ac9a ZJS |
311 | default: |
312 | assert_not_reached("Unknown option"); | |
912541b0 KS |
313 | } |
314 | } | |
315 | ||
2001622c YW |
316 | if (ping) { |
317 | _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL; | |
318 | ||
100bc5bf YW |
319 | r = udev_ctrl_new(&uctrl); |
320 | if (r < 0) | |
321 | return log_error_errno(r, "Failed to initialize udev control: %m"); | |
2001622c | 322 | |
78467aeb | 323 | r = udev_ctrl_send_ping(uctrl); |
2001622c YW |
324 | if (r < 0) |
325 | return log_error_errno(r, "Failed to connect to udev daemon: %m"); | |
78467aeb YW |
326 | |
327 | r = udev_ctrl_wait(uctrl, ping_timeout_usec); | |
328 | if (r < 0) | |
329 | return log_error_errno(r, "Failed to wait for daemon to reply: %m"); | |
2001622c YW |
330 | } |
331 | ||
80877656 | 332 | for (; optind < argc; optind++) { |
13aca847 | 333 | _cleanup_(sd_device_unrefp) sd_device *dev = NULL; |
80877656 | 334 | |
13aca847 YW |
335 | r = find_device(argv[optind], NULL, &dev); |
336 | if (r < 0) | |
c7d942d6 | 337 | return log_error_errno(r, "Failed to open the device '%s': %m", argv[optind]); |
80877656 | 338 | |
5b3b0733 | 339 | r = device_enumerator_add_match_parent_incremental(e, dev); |
bb084d42 | 340 | if (r < 0) |
c7d942d6 | 341 | return log_error_errno(r, "Failed to add parent match '%s': %m", argv[optind]); |
7643ac9a ZJS |
342 | } |
343 | ||
792cc203 | 344 | if (settle) { |
fb3d8e9f YW |
345 | settle_set = set_new(&string_hash_ops); |
346 | if (!settle_set) | |
347 | return log_oom(); | |
348 | ||
349 | r = sd_event_default(&event); | |
350 | if (r < 0) | |
351 | return log_error_errno(r, "Failed to get default event: %m"); | |
792cc203 | 352 | |
f8d596cd YW |
353 | r = sd_device_monitor_new(&m); |
354 | if (r < 0) | |
355 | return log_error_errno(r, "Failed to create device monitor object: %m"); | |
bb084d42 | 356 | |
deb2b734 | 357 | r = sd_device_monitor_attach_event(m, event); |
bb084d42 | 358 | if (r < 0) |
fb3d8e9f | 359 | return log_error_errno(r, "Failed to attach event to device monitor: %m"); |
792cc203 | 360 | |
deb2b734 | 361 | r = sd_device_monitor_start(m, device_monitor_handler, settle_set); |
fb3d8e9f YW |
362 | if (r < 0) |
363 | return log_error_errno(r, "Failed to start device monitor: %m"); | |
792cc203 MH |
364 | } |
365 | ||
912541b0 KS |
366 | switch (device_type) { |
367 | case TYPE_SUBSYSTEMS: | |
13aca847 YW |
368 | r = device_enumerator_scan_subsystems(e); |
369 | if (r < 0) | |
370 | return log_error_errno(r, "Failed to scan subsystems: %m"); | |
792cc203 | 371 | break; |
912541b0 | 372 | case TYPE_DEVICES: |
13aca847 YW |
373 | r = device_enumerator_scan_devices(e); |
374 | if (r < 0) | |
375 | return log_error_errno(r, "Failed to scan devices: %m"); | |
792cc203 | 376 | break; |
912541b0 | 377 | default: |
c7d942d6 | 378 | assert_not_reached("Unknown device type"); |
912541b0 | 379 | } |
13aca847 | 380 | r = exec_list(e, action, settle_set); |
6bcc09be | 381 | if (r < 0) |
bb084d42 | 382 | return r; |
792cc203 | 383 | |
fb3d8e9f YW |
384 | if (event && !set_isempty(settle_set)) { |
385 | r = sd_event_loop(event); | |
386 | if (r < 0) | |
387 | return log_error_errno(r, "Event loop failed: %m"); | |
792cc203 MH |
388 | } |
389 | ||
390 | return 0; | |
0d5be398 | 391 | } |