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