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