]>
Commit | Line | Data |
---|---|---|
e7145211 | 1 | /* SPDX-License-Identifier: GPL-2.0+ */ |
0d5be398 | 2 | /* |
1298001e | 3 | * Copyright (C) 2008-2009 Kay Sievers <kay@vrfy.org> |
0d5be398 | 4 | * |
55e9959b KS |
5 | * This program is free software: you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation, either version 2 of the License, or | |
8 | * (at your option) any later version. | |
0d5be398 | 9 | * |
55e9959b KS |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
0d5be398 KS |
17 | */ |
18 | ||
07630cea LP |
19 | #include <errno.h> |
20 | #include <fcntl.h> | |
21 | #include <getopt.h> | |
0d5be398 | 22 | #include <stddef.h> |
0d5be398 | 23 | #include <stdio.h> |
07630cea | 24 | #include <string.h> |
0d5be398 | 25 | #include <unistd.h> |
0d5be398 | 26 | |
792cc203 MH |
27 | #include "fd-util.h" |
28 | #include "set.h" | |
07630cea | 29 | #include "string-util.h" |
44433ebd | 30 | #include "udev-util.h" |
07630cea | 31 | #include "udev.h" |
d6170d27 | 32 | #include "udevadm-util.h" |
c5383e79 | 33 | #include "util.h" |
0d5be398 | 34 | |
c48622cc KS |
35 | static int verbose; |
36 | static int dry_run; | |
c48622cc | 37 | |
792cc203 | 38 | static void exec_list(struct udev_enumerate *udev_enumerate, const char *action, Set *settle_set) { |
912541b0 KS |
39 | struct udev_list_entry *entry; |
40 | ||
41 | udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_enumerate)) { | |
42 | char filename[UTIL_PATH_SIZE]; | |
792cc203 | 43 | const char *syspath; |
912541b0 KS |
44 | int fd; |
45 | ||
792cc203 | 46 | syspath = udev_list_entry_get_name(entry); |
912541b0 | 47 | if (verbose) |
792cc203 | 48 | printf("%s\n", syspath); |
912541b0 KS |
49 | if (dry_run) |
50 | continue; | |
792cc203 MH |
51 | |
52 | strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL); | |
c8a202b7 | 53 | fd = open(filename, O_WRONLY|O_CLOEXEC); |
baa30fbc | 54 | if (fd < 0) |
912541b0 | 55 | continue; |
792cc203 MH |
56 | if (settle_set != NULL) |
57 | set_put_strdup(settle_set, syspath); | |
912541b0 | 58 | if (write(fd, action, strlen(action)) < 0) |
56f64d95 | 59 | log_debug_errno(errno, "error writing '%s' to '%s': %m", action, filename); |
912541b0 KS |
60 | close(fd); |
61 | } | |
0d5be398 KS |
62 | } |
63 | ||
9ec6e95b | 64 | static const char *keyval(const char *str, const char **val, char *buf, size_t size) { |
912541b0 KS |
65 | char *pos; |
66 | ||
d5a89d7d | 67 | strscpy(buf, size,str); |
912541b0 KS |
68 | pos = strchr(buf, '='); |
69 | if (pos != NULL) { | |
70 | pos[0] = 0; | |
71 | pos++; | |
72 | } | |
73 | *val = pos; | |
74 | return buf; | |
80381823 KS |
75 | } |
76 | ||
7643ac9a | 77 | static void help(void) { |
5639df9a | 78 | printf("%s trigger [OPTIONS] DEVPATH\n\n" |
5ac0162c LP |
79 | "Request events from the kernel.\n\n" |
80 | " -h --help Show this help\n" | |
5639df9a | 81 | " -V --version Show package version\n" |
5ac0162c LP |
82 | " -v --verbose Print the list of devices while running\n" |
83 | " -n --dry-run Do not actually trigger the events\n" | |
84 | " -t --type= Type of events to trigger\n" | |
85 | " devices sysfs devices (default)\n" | |
86 | " subsystems sysfs subsystems and drivers\n" | |
87 | " -c --action=ACTION Event action value, default is \"change\"\n" | |
88 | " -s --subsystem-match=SUBSYSTEM Trigger devices from a matching subsystem\n" | |
89 | " -S --subsystem-nomatch=SUBSYSTEM Exclude devices from a matching subsystem\n" | |
90 | " -a --attr-match=FILE[=VALUE] Trigger devices with a matching attribute\n" | |
91 | " -A --attr-nomatch=FILE[=VALUE] Exclude devices with a matching attribute\n" | |
92 | " -p --property-match=KEY=VALUE Trigger devices with a matching property\n" | |
93 | " -g --tag-match=KEY=VALUE Trigger devices with a matching property\n" | |
94 | " -y --sysname-match=NAME Trigger devices with this /sys path\n" | |
95 | " --name-match=NAME Trigger devices with this /dev name\n" | |
96 | " -b --parent-match=NAME Trigger devices with that parent device\n" | |
792cc203 | 97 | " -w --settle Wait for the triggered events to complete\n" |
5ac0162c | 98 | , program_invocation_short_name); |
7643ac9a ZJS |
99 | } |
100 | ||
9ec6e95b | 101 | static int adm_trigger(struct udev *udev, int argc, char *argv[]) { |
80877656 ZJS |
102 | enum { |
103 | ARG_NAME = 0x100, | |
104 | }; | |
105 | ||
912541b0 | 106 | static const struct option options[] = { |
80877656 ZJS |
107 | { "verbose", no_argument, NULL, 'v' }, |
108 | { "dry-run", no_argument, NULL, 'n' }, | |
109 | { "type", required_argument, NULL, 't' }, | |
110 | { "action", required_argument, NULL, 'c' }, | |
111 | { "subsystem-match", required_argument, NULL, 's' }, | |
112 | { "subsystem-nomatch", required_argument, NULL, 'S' }, | |
113 | { "attr-match", required_argument, NULL, 'a' }, | |
114 | { "attr-nomatch", required_argument, NULL, 'A' }, | |
115 | { "property-match", required_argument, NULL, 'p' }, | |
116 | { "tag-match", required_argument, NULL, 'g' }, | |
117 | { "sysname-match", required_argument, NULL, 'y' }, | |
118 | { "name-match", required_argument, NULL, ARG_NAME }, | |
119 | { "parent-match", required_argument, NULL, 'b' }, | |
792cc203 | 120 | { "settle", no_argument, NULL, 'w' }, |
5639df9a | 121 | { "version", no_argument, NULL, 'V' }, |
80877656 | 122 | { "help", no_argument, NULL, 'h' }, |
912541b0 KS |
123 | {} |
124 | }; | |
125 | enum { | |
126 | TYPE_DEVICES, | |
127 | TYPE_SUBSYSTEMS, | |
128 | } device_type = TYPE_DEVICES; | |
129 | const char *action = "change"; | |
44433ebd | 130 | _cleanup_udev_enumerate_unref_ struct udev_enumerate *udev_enumerate = NULL; |
792cc203 MH |
131 | _cleanup_udev_monitor_unref_ struct udev_monitor *udev_monitor = NULL; |
132 | _cleanup_close_ int fd_ep = -1; | |
133 | int fd_udev = -1; | |
134 | struct epoll_event ep_udev; | |
135 | bool settle = false; | |
136 | _cleanup_set_free_free_ Set *settle_set = NULL; | |
56b13bcc | 137 | int c, r; |
912541b0 | 138 | |
912541b0 | 139 | udev_enumerate = udev_enumerate_new(udev); |
44433ebd ZJS |
140 | if (udev_enumerate == NULL) |
141 | return 1; | |
912541b0 | 142 | |
792cc203 | 143 | while ((c = getopt_long(argc, argv, "vnt:c:s:S:a:A:p:g:y:b:wVh", options, NULL)) >= 0) { |
912541b0 KS |
144 | const char *key; |
145 | const char *val; | |
146 | char buf[UTIL_PATH_SIZE]; | |
147 | ||
7643ac9a | 148 | switch (c) { |
912541b0 KS |
149 | case 'v': |
150 | verbose = 1; | |
151 | break; | |
152 | case 'n': | |
153 | dry_run = 1; | |
154 | break; | |
155 | case 't': | |
44433ebd | 156 | if (streq(optarg, "devices")) |
912541b0 | 157 | device_type = TYPE_DEVICES; |
44433ebd | 158 | else if (streq(optarg, "subsystems")) |
912541b0 | 159 | device_type = TYPE_SUBSYSTEMS; |
44433ebd | 160 | else { |
9f6445e3 | 161 | log_error("unknown type --type=%s", optarg); |
44433ebd | 162 | return 2; |
912541b0 KS |
163 | } |
164 | break; | |
165 | case 'c': | |
5991ce44 | 166 | if (!STR_IN_SET(optarg, "add", "remove", "change")) { |
9f6445e3 | 167 | log_error("unknown action '%s'", optarg); |
44433ebd ZJS |
168 | return 2; |
169 | } else | |
c5383e79 | 170 | action = optarg; |
44433ebd | 171 | |
912541b0 KS |
172 | break; |
173 | case 's': | |
56b13bcc TG |
174 | r = udev_enumerate_add_match_subsystem(udev_enumerate, optarg); |
175 | if (r < 0) { | |
176 | log_error_errno(r, "could not add subsystem match '%s': %m", optarg); | |
177 | return 2; | |
178 | } | |
912541b0 KS |
179 | break; |
180 | case 'S': | |
56b13bcc TG |
181 | r = udev_enumerate_add_nomatch_subsystem(udev_enumerate, optarg); |
182 | if (r < 0) { | |
183 | log_error_errno(r, "could not add negative subsystem match '%s': %m", optarg); | |
184 | return 2; | |
185 | } | |
912541b0 KS |
186 | break; |
187 | case 'a': | |
188 | key = keyval(optarg, &val, buf, sizeof(buf)); | |
56b13bcc TG |
189 | r = udev_enumerate_add_match_sysattr(udev_enumerate, key, val); |
190 | if (r < 0) { | |
191 | log_error_errno(r, "could not add sysattr match '%s=%s': %m", key, val); | |
192 | return 2; | |
193 | } | |
912541b0 KS |
194 | break; |
195 | case 'A': | |
196 | key = keyval(optarg, &val, buf, sizeof(buf)); | |
56b13bcc TG |
197 | r = udev_enumerate_add_nomatch_sysattr(udev_enumerate, key, val); |
198 | if (r < 0) { | |
199 | log_error_errno(r, "could not add negative sysattr match '%s=%s': %m", key, val); | |
200 | return 2; | |
201 | } | |
912541b0 KS |
202 | break; |
203 | case 'p': | |
204 | key = keyval(optarg, &val, buf, sizeof(buf)); | |
56b13bcc TG |
205 | r = udev_enumerate_add_match_property(udev_enumerate, key, val); |
206 | if (r < 0) { | |
207 | log_error_errno(r, "could not add property match '%s=%s': %m", key, val); | |
208 | return 2; | |
209 | } | |
912541b0 KS |
210 | break; |
211 | case 'g': | |
56b13bcc TG |
212 | r = udev_enumerate_add_match_tag(udev_enumerate, optarg); |
213 | if (r < 0) { | |
214 | log_error_errno(r, "could not add tag match '%s': %m", optarg); | |
215 | return 2; | |
216 | } | |
912541b0 KS |
217 | break; |
218 | case 'y': | |
56b13bcc TG |
219 | r = udev_enumerate_add_match_sysname(udev_enumerate, optarg); |
220 | if (r < 0) { | |
221 | log_error_errno(r, "could not add sysname match '%s': %m", optarg); | |
222 | return 2; | |
223 | } | |
912541b0 KS |
224 | break; |
225 | case 'b': { | |
d6170d27 ZJS |
226 | _cleanup_udev_device_unref_ struct udev_device *dev; |
227 | ||
228 | dev = find_device(udev, optarg, "/sys"); | |
912541b0 | 229 | if (dev == NULL) { |
9f6445e3 | 230 | log_error("unable to open the device '%s'", optarg); |
44433ebd | 231 | return 2; |
912541b0 | 232 | } |
d6170d27 | 233 | |
56b13bcc TG |
234 | r = udev_enumerate_add_match_parent(udev_enumerate, dev); |
235 | if (r < 0) { | |
236 | log_error_errno(r, "could not add parent match '%s': %m", optarg); | |
237 | return 2; | |
238 | } | |
912541b0 KS |
239 | break; |
240 | } | |
792cc203 MH |
241 | case 'w': |
242 | settle = true; | |
243 | break; | |
d6170d27 | 244 | |
80877656 ZJS |
245 | case ARG_NAME: { |
246 | _cleanup_udev_device_unref_ struct udev_device *dev; | |
247 | ||
248 | dev = find_device(udev, optarg, "/dev/"); | |
249 | if (dev == NULL) { | |
250 | log_error("unable to open the device '%s'", optarg); | |
251 | return 2; | |
252 | } | |
253 | ||
56b13bcc TG |
254 | r = udev_enumerate_add_match_parent(udev_enumerate, dev); |
255 | if (r < 0) { | |
256 | log_error_errno(r, "could not add parent match '%s': %m", optarg); | |
257 | return 2; | |
258 | } | |
80877656 ZJS |
259 | break; |
260 | } | |
261 | ||
5639df9a YW |
262 | case 'V': |
263 | print_version(); | |
264 | return 0; | |
912541b0 | 265 | case 'h': |
7643ac9a | 266 | help(); |
44433ebd | 267 | return 0; |
7643ac9a | 268 | case '?': |
44433ebd | 269 | return 1; |
7643ac9a ZJS |
270 | default: |
271 | assert_not_reached("Unknown option"); | |
912541b0 KS |
272 | } |
273 | } | |
274 | ||
80877656 ZJS |
275 | for (; optind < argc; optind++) { |
276 | _cleanup_udev_device_unref_ struct udev_device *dev; | |
277 | ||
278 | dev = find_device(udev, argv[optind], NULL); | |
279 | if (dev == NULL) { | |
280 | log_error("unable to open the device '%s'", argv[optind]); | |
281 | return 2; | |
282 | } | |
283 | ||
56b13bcc TG |
284 | r = udev_enumerate_add_match_parent(udev_enumerate, dev); |
285 | if (r < 0) { | |
286 | log_error_errno(r, "could not add tag match '%s': %m", optarg); | |
287 | return 2; | |
288 | } | |
7643ac9a ZJS |
289 | } |
290 | ||
792cc203 MH |
291 | if (settle) { |
292 | fd_ep = epoll_create1(EPOLL_CLOEXEC); | |
293 | if (fd_ep < 0) { | |
294 | log_error_errno(errno, "error creating epoll fd: %m"); | |
295 | return 1; | |
296 | } | |
297 | ||
298 | udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); | |
299 | if (udev_monitor == NULL) { | |
300 | log_error("error: unable to create netlink socket"); | |
301 | return 3; | |
302 | } | |
303 | fd_udev = udev_monitor_get_fd(udev_monitor); | |
304 | ||
305 | if (udev_monitor_enable_receiving(udev_monitor) < 0) { | |
306 | log_error("error: unable to subscribe to udev events"); | |
307 | return 4; | |
308 | } | |
309 | ||
310 | ep_udev = (struct epoll_event) { .events = EPOLLIN, .data.fd = fd_udev }; | |
311 | if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) { | |
312 | log_error_errno(errno, "fail to add fd to epoll: %m"); | |
313 | return 5; | |
314 | } | |
315 | ||
316 | settle_set = set_new(&string_hash_ops); | |
317 | if (settle_set == NULL) { | |
318 | log_oom(); | |
319 | return 1; | |
320 | } | |
321 | } | |
322 | ||
912541b0 KS |
323 | switch (device_type) { |
324 | case TYPE_SUBSYSTEMS: | |
325 | udev_enumerate_scan_subsystems(udev_enumerate); | |
792cc203 | 326 | break; |
912541b0 KS |
327 | case TYPE_DEVICES: |
328 | udev_enumerate_scan_devices(udev_enumerate); | |
792cc203 | 329 | break; |
912541b0 | 330 | default: |
4dd1de72 | 331 | assert_not_reached("device_type"); |
912541b0 | 332 | } |
792cc203 MH |
333 | exec_list(udev_enumerate, action, settle_set); |
334 | ||
335 | while (!set_isempty(settle_set)) { | |
336 | int fdcount; | |
337 | struct epoll_event ev[4]; | |
338 | int i; | |
339 | ||
340 | fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1); | |
341 | if (fdcount < 0) { | |
342 | if (errno != EINTR) | |
343 | log_error_errno(errno, "error receiving uevent message: %m"); | |
344 | continue; | |
345 | } | |
346 | ||
347 | for (i = 0; i < fdcount; i++) { | |
348 | if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) { | |
349 | _cleanup_udev_device_unref_ struct udev_device *device; | |
350 | const char *syspath = NULL; | |
351 | ||
352 | device = udev_monitor_receive_device(udev_monitor); | |
353 | if (device == NULL) | |
354 | continue; | |
355 | ||
356 | syspath = udev_device_get_syspath(device); | |
357 | if (verbose) | |
358 | printf("settle %s\n", syspath); | |
359 | if (!set_remove(settle_set, syspath)) | |
360 | log_debug("Got epoll event on syspath %s not present in syspath set", syspath); | |
361 | } | |
362 | } | |
363 | } | |
364 | ||
365 | return 0; | |
0d5be398 | 366 | } |
1985c76e KS |
367 | |
368 | const struct udevadm_cmd udevadm_trigger = { | |
912541b0 KS |
369 | .name = "trigger", |
370 | .cmd = adm_trigger, | |
5ac0162c | 371 | .help = "Request events from the kernel", |
1985c76e | 372 | }; |