Skip to content

Commit 9e42c93

Browse files
authored
Merge pull request systemd#8066 from LittleCVR/udevadm-trigger-and-settle
udevadm: allow trigger command to be synchronous
2 parents 91761b1 + 6bcc09b commit 9e42c93

File tree

2 files changed

+112
-12
lines changed

2 files changed

+112
-12
lines changed

man/udevadm.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,17 @@
335335
device.</para>
336336
</listitem>
337337
</varlistentry>
338+
<varlistentry>
339+
<term><option>-w</option></term>
340+
<term><option>--settle</option></term>
341+
<listitem>
342+
<para>Apart from triggering events, also waits for those events to
343+
finish. Note that this is different from calling <command>udevadm
344+
settle</command>. <command>udevadm settle</command> waits for all
345+
events to finish. This option only waits for events triggered by
346+
the same command to finish.</para>
347+
</listitem>
348+
</varlistentry>
338349

339350
<xi:include href="standard-options.xml" xpointer="version" />
340351
<xi:include href="standard-options.xml" xpointer="help" />

src/udev/udevadm-trigger.c

Lines changed: 101 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#include <string.h>
2525
#include <unistd.h>
2626

27+
#include "fd-util.h"
28+
#include "set.h"
2729
#include "string-util.h"
2830
#include "udev-util.h"
2931
#include "udev.h"
@@ -33,25 +35,36 @@
3335
static int verbose;
3436
static int dry_run;
3537

36-
static void exec_list(struct udev_enumerate *udev_enumerate, const char *action) {
38+
static int exec_list(struct udev_enumerate *udev_enumerate, const char *action, Set *settle_set) {
3739
struct udev_list_entry *entry;
40+
int r;
3841

3942
udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_enumerate)) {
4043
char filename[UTIL_PATH_SIZE];
44+
const char *syspath;
4145
int fd;
4246

47+
syspath = udev_list_entry_get_name(entry);
4348
if (verbose)
44-
printf("%s\n", udev_list_entry_get_name(entry));
49+
printf("%s\n", syspath);
4550
if (dry_run)
4651
continue;
47-
strscpyl(filename, sizeof(filename), udev_list_entry_get_name(entry), "/uevent", NULL);
52+
53+
strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL);
4854
fd = open(filename, O_WRONLY|O_CLOEXEC);
4955
if (fd < 0)
5056
continue;
57+
if (settle_set) {
58+
r = set_put_strdup(settle_set, syspath);
59+
if (r < 0)
60+
return log_oom();
61+
}
5162
if (write(fd, action, strlen(action)) < 0)
5263
log_debug_errno(errno, "error writing '%s' to '%s': %m", action, filename);
5364
close(fd);
5465
}
66+
67+
return 0;
5568
}
5669

5770
static const char *keyval(const char *str, const char **val, char *buf, size_t size) {
@@ -87,6 +100,7 @@ static void help(void) {
87100
" -y --sysname-match=NAME Trigger devices with this /sys path\n"
88101
" --name-match=NAME Trigger devices with this /dev name\n"
89102
" -b --parent-match=NAME Trigger devices with that parent device\n"
103+
" -w --settle Wait for the triggered events to complete\n"
90104
, program_invocation_short_name);
91105
}
92106

@@ -109,6 +123,7 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
109123
{ "sysname-match", required_argument, NULL, 'y' },
110124
{ "name-match", required_argument, NULL, ARG_NAME },
111125
{ "parent-match", required_argument, NULL, 'b' },
126+
{ "settle", no_argument, NULL, 'w' },
112127
{ "version", no_argument, NULL, 'V' },
113128
{ "help", no_argument, NULL, 'h' },
114129
{}
@@ -119,13 +134,19 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
119134
} device_type = TYPE_DEVICES;
120135
const char *action = "change";
121136
_cleanup_udev_enumerate_unref_ struct udev_enumerate *udev_enumerate = NULL;
137+
_cleanup_udev_monitor_unref_ struct udev_monitor *udev_monitor = NULL;
138+
_cleanup_close_ int fd_ep = -1;
139+
int fd_udev = -1;
140+
struct epoll_event ep_udev;
141+
bool settle = false;
142+
_cleanup_set_free_free_ Set *settle_set = NULL;
122143
int c, r;
123144

124145
udev_enumerate = udev_enumerate_new(udev);
125-
if (udev_enumerate == NULL)
146+
if (!udev_enumerate)
126147
return 1;
127148

128-
while ((c = getopt_long(argc, argv, "vnt:c:s:S:a:A:p:g:y:b:Vh", options, NULL)) >= 0) {
149+
while ((c = getopt_long(argc, argv, "vnt:c:s:S:a:A:p:g:y:b:wVh", options, NULL)) >= 0) {
129150
const char *key;
130151
const char *val;
131152
char buf[UTIL_PATH_SIZE];
@@ -211,7 +232,7 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
211232
_cleanup_udev_device_unref_ struct udev_device *dev;
212233

213234
dev = find_device(udev, optarg, "/sys");
214-
if (dev == NULL) {
235+
if (!dev) {
215236
log_error("unable to open the device '%s'", optarg);
216237
return 2;
217238
}
@@ -223,12 +244,15 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
223244
}
224245
break;
225246
}
247+
case 'w':
248+
settle = true;
249+
break;
226250

227251
case ARG_NAME: {
228252
_cleanup_udev_device_unref_ struct udev_device *dev;
229253

230254
dev = find_device(udev, optarg, "/dev/");
231-
if (dev == NULL) {
255+
if (!dev) {
232256
log_error("unable to open the device '%s'", optarg);
233257
return 2;
234258
}
@@ -258,7 +282,7 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
258282
_cleanup_udev_device_unref_ struct udev_device *dev;
259283

260284
dev = find_device(udev, argv[optind], NULL);
261-
if (dev == NULL) {
285+
if (!dev) {
262286
log_error("unable to open the device '%s'", argv[optind]);
263287
return 2;
264288
}
@@ -270,18 +294,83 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
270294
}
271295
}
272296

297+
if (settle) {
298+
fd_ep = epoll_create1(EPOLL_CLOEXEC);
299+
if (fd_ep < 0) {
300+
log_error_errno(errno, "error creating epoll fd: %m");
301+
return 1;
302+
}
303+
304+
udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
305+
if (!udev_monitor) {
306+
log_error("error: unable to create netlink socket");
307+
return 3;
308+
}
309+
fd_udev = udev_monitor_get_fd(udev_monitor);
310+
311+
if (udev_monitor_enable_receiving(udev_monitor) < 0) {
312+
log_error("error: unable to subscribe to udev events");
313+
return 4;
314+
}
315+
316+
ep_udev = (struct epoll_event) { .events = EPOLLIN, .data.fd = fd_udev };
317+
if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) {
318+
log_error_errno(errno, "fail to add fd to epoll: %m");
319+
return 5;
320+
}
321+
322+
settle_set = set_new(&string_hash_ops);
323+
if (!settle_set) {
324+
log_oom();
325+
return 1;
326+
}
327+
}
328+
273329
switch (device_type) {
274330
case TYPE_SUBSYSTEMS:
275331
udev_enumerate_scan_subsystems(udev_enumerate);
276-
exec_list(udev_enumerate, action);
277-
return 0;
332+
break;
278333
case TYPE_DEVICES:
279334
udev_enumerate_scan_devices(udev_enumerate);
280-
exec_list(udev_enumerate, action);
281-
return 0;
335+
break;
282336
default:
283337
assert_not_reached("device_type");
284338
}
339+
r = exec_list(udev_enumerate, action, settle_set);
340+
if (r < 0)
341+
return 1;
342+
343+
while (!set_isempty(settle_set)) {
344+
int fdcount;
345+
struct epoll_event ev[4];
346+
int i;
347+
348+
fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1);
349+
if (fdcount < 0) {
350+
if (errno != EINTR)
351+
log_error_errno(errno, "error receiving uevent message: %m");
352+
continue;
353+
}
354+
355+
for (i = 0; i < fdcount; i++) {
356+
if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) {
357+
_cleanup_udev_device_unref_ struct udev_device *device;
358+
const char *syspath = NULL;
359+
360+
device = udev_monitor_receive_device(udev_monitor);
361+
if (!device)
362+
continue;
363+
364+
syspath = udev_device_get_syspath(device);
365+
if (verbose)
366+
printf("settle %s\n", syspath);
367+
if (!set_remove(settle_set, syspath))
368+
log_debug("Got epoll event on syspath %s not present in syspath set", syspath);
369+
}
370+
}
371+
}
372+
373+
return 0;
285374
}
286375

287376
const struct udevadm_cmd udevadm_trigger = {

0 commit comments

Comments
 (0)