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