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