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