]>
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" |
92c40e1d | 11 | #include "fileio.h" |
c7d942d6 | 12 | #include "path-util.h" |
792cc203 | 13 | #include "set.h" |
07630cea | 14 | #include "string-util.h" |
13aca847 | 15 | #include "strv.h" |
3d05193e | 16 | #include "udevadm.h" |
d6170d27 | 17 | #include "udevadm-util.h" |
c494b739 | 18 | #include "virt.h" |
0d5be398 | 19 | |
13aca847 YW |
20 | static bool arg_verbose = false; |
21 | static bool arg_dry_run = false; | |
c48622cc | 22 | |
13aca847 YW |
23 | static int exec_list(sd_device_enumerator *e, const char *action, Set *settle_set) { |
24 | sd_device *d; | |
6bcc09be | 25 | int r; |
912541b0 | 26 | |
13aca847 | 27 | FOREACH_DEVICE_AND_SUBSYSTEM(e, d) { |
c7d942d6 | 28 | _cleanup_free_ char *filename = NULL; |
c7d942d6 | 29 | const char *syspath; |
912541b0 | 30 | |
13aca847 YW |
31 | if (sd_device_get_syspath(d, &syspath) < 0) |
32 | continue; | |
33 | ||
34 | if (arg_verbose) | |
792cc203 | 35 | printf("%s\n", syspath); |
13aca847 | 36 | if (arg_dry_run) |
912541b0 | 37 | continue; |
792cc203 | 38 | |
62a85ee0 | 39 | filename = path_join(syspath, "uevent"); |
c7d942d6 YW |
40 | if (!filename) |
41 | return log_oom(); | |
42 | ||
92c40e1d YW |
43 | r = write_string_file(filename, action, WRITE_STRING_FILE_DISABLE_BUFFER); |
44 | if (r < 0) { | |
45 | log_debug_errno(r, "Failed to write '%s' to '%s', ignoring: %m", action, filename); | |
912541b0 | 46 | continue; |
92c40e1d | 47 | } |
1b113391 | 48 | |
6bcc09be ZJS |
49 | if (settle_set) { |
50 | r = set_put_strdup(settle_set, syspath); | |
51 | if (r < 0) | |
52 | return log_oom(); | |
53 | } | |
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 | |
c494b739 YW |
162 | if (running_in_chroot() > 0) { |
163 | log_info("Running in chroot, ignoring request."); | |
164 | return 0; | |
165 | } | |
166 | ||
13aca847 YW |
167 | r = sd_device_enumerator_new(&e); |
168 | if (r < 0) | |
169 | return r; | |
170 | ||
171 | r = sd_device_enumerator_allow_uninitialized(e); | |
172 | if (r < 0) | |
173 | return r; | |
912541b0 | 174 | |
792cc203 | 175 | while ((c = getopt_long(argc, argv, "vnt:c:s:S:a:A:p:g:y:b:wVh", options, NULL)) >= 0) { |
c7d942d6 YW |
176 | _cleanup_free_ char *buf = NULL; |
177 | const char *key, *val; | |
912541b0 | 178 | |
7643ac9a | 179 | switch (c) { |
912541b0 | 180 | case 'v': |
13aca847 | 181 | arg_verbose = true; |
912541b0 KS |
182 | break; |
183 | case 'n': | |
13aca847 | 184 | arg_dry_run = true; |
912541b0 KS |
185 | break; |
186 | case 't': | |
44433ebd | 187 | if (streq(optarg, "devices")) |
912541b0 | 188 | device_type = TYPE_DEVICES; |
44433ebd | 189 | else if (streq(optarg, "subsystems")) |
912541b0 | 190 | device_type = TYPE_SUBSYSTEMS; |
44433ebd | 191 | else { |
c7d942d6 | 192 | log_error("Unknown type --type=%s", optarg); |
bb084d42 | 193 | return -EINVAL; |
912541b0 KS |
194 | } |
195 | break; | |
196 | case 'c': | |
bb084d42 | 197 | if (STR_IN_SET(optarg, "add", "remove", "change")) |
c5383e79 | 198 | action = optarg; |
bb084d42 | 199 | else { |
c7d942d6 | 200 | log_error("Unknown action '%s'", optarg); |
bb084d42 YW |
201 | return -EINVAL; |
202 | } | |
44433ebd | 203 | |
912541b0 KS |
204 | break; |
205 | case 's': | |
13aca847 | 206 | r = sd_device_enumerator_add_match_subsystem(e, optarg, true); |
bb084d42 | 207 | if (r < 0) |
c7d942d6 | 208 | return log_error_errno(r, "Failed to add subsystem match '%s': %m", optarg); |
912541b0 KS |
209 | break; |
210 | case 'S': | |
13aca847 | 211 | r = sd_device_enumerator_add_match_subsystem(e, optarg, false); |
bb084d42 | 212 | if (r < 0) |
c7d942d6 | 213 | return log_error_errno(r, "Failed to add negative subsystem match '%s': %m", optarg); |
912541b0 KS |
214 | break; |
215 | case 'a': | |
c7d942d6 YW |
216 | buf = keyval(optarg, &key, &val); |
217 | if (!buf) | |
218 | return log_oom(); | |
13aca847 | 219 | r = sd_device_enumerator_add_match_sysattr(e, key, val, true); |
bb084d42 | 220 | if (r < 0) |
c7d942d6 | 221 | return log_error_errno(r, "Failed to add sysattr match '%s=%s': %m", key, val); |
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, false); |
bb084d42 | 228 | if (r < 0) |
c7d942d6 | 229 | return log_error_errno(r, "Failed to add negative sysattr match '%s=%s': %m", key, val); |
912541b0 KS |
230 | break; |
231 | case 'p': | |
c7d942d6 YW |
232 | buf = keyval(optarg, &key, &val); |
233 | if (!buf) | |
234 | return log_oom(); | |
13aca847 | 235 | r = sd_device_enumerator_add_match_property(e, key, val); |
bb084d42 | 236 | if (r < 0) |
c7d942d6 | 237 | return log_error_errno(r, "Failed to add property match '%s=%s': %m", key, val); |
912541b0 KS |
238 | break; |
239 | case 'g': | |
13aca847 | 240 | r = sd_device_enumerator_add_match_tag(e, optarg); |
bb084d42 | 241 | if (r < 0) |
c7d942d6 | 242 | return log_error_errno(r, "Failed to add tag match '%s': %m", optarg); |
912541b0 KS |
243 | break; |
244 | case 'y': | |
13aca847 | 245 | r = sd_device_enumerator_add_match_sysname(e, optarg); |
bb084d42 | 246 | if (r < 0) |
c7d942d6 | 247 | return log_error_errno(r, "Failed to add sysname match '%s': %m", optarg); |
912541b0 KS |
248 | break; |
249 | case 'b': { | |
13aca847 | 250 | _cleanup_(sd_device_unrefp) sd_device *dev = NULL; |
d6170d27 | 251 | |
13aca847 YW |
252 | r = find_device(optarg, "/sys", &dev); |
253 | if (r < 0) | |
c7d942d6 | 254 | return log_error_errno(r, "Failed to open the device '%s': %m", optarg); |
d6170d27 | 255 | |
13aca847 | 256 | r = sd_device_enumerator_add_match_parent(e, dev); |
bb084d42 | 257 | if (r < 0) |
c7d942d6 | 258 | return log_error_errno(r, "Failed to add parent match '%s': %m", optarg); |
912541b0 KS |
259 | break; |
260 | } | |
792cc203 MH |
261 | case 'w': |
262 | settle = true; | |
263 | break; | |
d6170d27 | 264 | |
80877656 | 265 | case ARG_NAME: { |
13aca847 | 266 | _cleanup_(sd_device_unrefp) sd_device *dev = NULL; |
80877656 | 267 | |
13aca847 YW |
268 | r = find_device(optarg, "/dev/", &dev); |
269 | if (r < 0) | |
c7d942d6 | 270 | return log_error_errno(r, "Failed to open the device '%s': %m", optarg); |
80877656 | 271 | |
13aca847 | 272 | r = sd_device_enumerator_add_match_parent(e, dev); |
bb084d42 | 273 | if (r < 0) |
c7d942d6 | 274 | return log_error_errno(r, "Failed to add parent match '%s': %m", optarg); |
80877656 ZJS |
275 | break; |
276 | } | |
277 | ||
5639df9a | 278 | case 'V': |
51b006e1 | 279 | return print_version(); |
912541b0 | 280 | case 'h': |
bb084d42 | 281 | return help(); |
7643ac9a | 282 | case '?': |
bb084d42 | 283 | return -EINVAL; |
7643ac9a ZJS |
284 | default: |
285 | assert_not_reached("Unknown option"); | |
912541b0 KS |
286 | } |
287 | } | |
288 | ||
80877656 | 289 | for (; optind < argc; optind++) { |
13aca847 | 290 | _cleanup_(sd_device_unrefp) sd_device *dev = NULL; |
80877656 | 291 | |
13aca847 YW |
292 | r = find_device(argv[optind], NULL, &dev); |
293 | if (r < 0) | |
c7d942d6 | 294 | return log_error_errno(r, "Failed to open the device '%s': %m", argv[optind]); |
80877656 | 295 | |
13aca847 | 296 | r = sd_device_enumerator_add_match_parent(e, dev); |
bb084d42 | 297 | if (r < 0) |
c7d942d6 | 298 | return log_error_errno(r, "Failed to add parent match '%s': %m", argv[optind]); |
7643ac9a ZJS |
299 | } |
300 | ||
792cc203 | 301 | if (settle) { |
fb3d8e9f YW |
302 | settle_set = set_new(&string_hash_ops); |
303 | if (!settle_set) | |
304 | return log_oom(); | |
305 | ||
306 | r = sd_event_default(&event); | |
307 | if (r < 0) | |
308 | return log_error_errno(r, "Failed to get default event: %m"); | |
792cc203 | 309 | |
f8d596cd YW |
310 | r = sd_device_monitor_new(&m); |
311 | if (r < 0) | |
312 | return log_error_errno(r, "Failed to create device monitor object: %m"); | |
bb084d42 | 313 | |
deb2b734 | 314 | r = sd_device_monitor_attach_event(m, event); |
bb084d42 | 315 | if (r < 0) |
fb3d8e9f | 316 | return log_error_errno(r, "Failed to attach event to device monitor: %m"); |
792cc203 | 317 | |
deb2b734 | 318 | r = sd_device_monitor_start(m, device_monitor_handler, settle_set); |
fb3d8e9f YW |
319 | if (r < 0) |
320 | return log_error_errno(r, "Failed to start device monitor: %m"); | |
792cc203 MH |
321 | } |
322 | ||
912541b0 KS |
323 | switch (device_type) { |
324 | case TYPE_SUBSYSTEMS: | |
13aca847 YW |
325 | r = device_enumerator_scan_subsystems(e); |
326 | if (r < 0) | |
327 | return log_error_errno(r, "Failed to scan subsystems: %m"); | |
792cc203 | 328 | break; |
912541b0 | 329 | case TYPE_DEVICES: |
13aca847 YW |
330 | r = device_enumerator_scan_devices(e); |
331 | if (r < 0) | |
332 | return log_error_errno(r, "Failed to scan devices: %m"); | |
792cc203 | 333 | break; |
912541b0 | 334 | default: |
c7d942d6 | 335 | assert_not_reached("Unknown device type"); |
912541b0 | 336 | } |
13aca847 | 337 | r = exec_list(e, action, settle_set); |
6bcc09be | 338 | if (r < 0) |
bb084d42 | 339 | return r; |
792cc203 | 340 | |
fb3d8e9f YW |
341 | if (event && !set_isempty(settle_set)) { |
342 | r = sd_event_loop(event); | |
343 | if (r < 0) | |
344 | return log_error_errno(r, "Event loop failed: %m"); | |
792cc203 MH |
345 | } |
346 | ||
347 | return 0; | |
0d5be398 | 348 | } |