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