summaryrefslogtreecommitdiff
path: root/src/udev/udevadm-trigger.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/udev/udevadm-trigger.c')
-rw-r--r--src/udev/udevadm-trigger.c97
1 files changed, 89 insertions, 8 deletions
diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c
index f78a2ba437..c5f9797fcc 100644
--- a/src/udev/udevadm-trigger.c
+++ b/src/udev/udevadm-trigger.c
@@ -24,6 +24,8 @@
#include <string.h>
#include <unistd.h>
+#include "fd-util.h"
+#include "set.h"
#include "string-util.h"
#include "udev-util.h"
#include "udev.h"
@@ -33,21 +35,26 @@
static int verbose;
static int dry_run;
-static void exec_list(struct udev_enumerate *udev_enumerate, const char *action) {
+static void exec_list(struct udev_enumerate *udev_enumerate, const char *action, Set *settle_set) {
struct udev_list_entry *entry;
udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_enumerate)) {
char filename[UTIL_PATH_SIZE];
+ const char *syspath;
int fd;
+ syspath = udev_list_entry_get_name(entry);
if (verbose)
- printf("%s\n", udev_list_entry_get_name(entry));
+ printf("%s\n", syspath);
if (dry_run)
continue;
- strscpyl(filename, sizeof(filename), udev_list_entry_get_name(entry), "/uevent", NULL);
+
+ strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL);
fd = open(filename, O_WRONLY|O_CLOEXEC);
if (fd < 0)
continue;
+ if (settle_set != NULL)
+ set_put_strdup(settle_set, syspath);
if (write(fd, action, strlen(action)) < 0)
log_debug_errno(errno, "error writing '%s' to '%s': %m", action, filename);
close(fd);
@@ -87,6 +94,7 @@ static void help(void) {
" -y --sysname-match=NAME Trigger devices with this /sys path\n"
" --name-match=NAME Trigger devices with this /dev name\n"
" -b --parent-match=NAME Trigger devices with that parent device\n"
+ " -w --settle Wait for the triggered events to complete\n"
, program_invocation_short_name);
}
@@ -109,6 +117,7 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
{ "sysname-match", required_argument, NULL, 'y' },
{ "name-match", required_argument, NULL, ARG_NAME },
{ "parent-match", required_argument, NULL, 'b' },
+ { "settle", no_argument, NULL, 'w' },
{ "version", no_argument, NULL, 'V' },
{ "help", no_argument, NULL, 'h' },
{}
@@ -119,13 +128,19 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
} device_type = TYPE_DEVICES;
const char *action = "change";
_cleanup_udev_enumerate_unref_ struct udev_enumerate *udev_enumerate = NULL;
+ _cleanup_udev_monitor_unref_ struct udev_monitor *udev_monitor = NULL;
+ _cleanup_close_ int fd_ep = -1;
+ int fd_udev = -1;
+ struct epoll_event ep_udev;
+ bool settle = false;
+ _cleanup_set_free_free_ Set *settle_set = NULL;
int c, r;
udev_enumerate = udev_enumerate_new(udev);
if (udev_enumerate == NULL)
return 1;
- while ((c = getopt_long(argc, argv, "vnt:c:s:S:a:A:p:g:y:b:Vh", options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "vnt:c:s:S:a:A:p:g:y:b:wVh", options, NULL)) >= 0) {
const char *key;
const char *val;
char buf[UTIL_PATH_SIZE];
@@ -223,6 +238,9 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
}
break;
}
+ case 'w':
+ settle = true;
+ break;
case ARG_NAME: {
_cleanup_udev_device_unref_ struct udev_device *dev;
@@ -270,18 +288,81 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
}
}
+ if (settle) {
+ fd_ep = epoll_create1(EPOLL_CLOEXEC);
+ if (fd_ep < 0) {
+ log_error_errno(errno, "error creating epoll fd: %m");
+ return 1;
+ }
+
+ udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
+ if (udev_monitor == NULL) {
+ log_error("error: unable to create netlink socket");
+ return 3;
+ }
+ fd_udev = udev_monitor_get_fd(udev_monitor);
+
+ if (udev_monitor_enable_receiving(udev_monitor) < 0) {
+ log_error("error: unable to subscribe to udev events");
+ return 4;
+ }
+
+ ep_udev = (struct epoll_event) { .events = EPOLLIN, .data.fd = fd_udev };
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) {
+ log_error_errno(errno, "fail to add fd to epoll: %m");
+ return 5;
+ }
+
+ settle_set = set_new(&string_hash_ops);
+ if (settle_set == NULL) {
+ log_oom();
+ return 1;
+ }
+ }
+
switch (device_type) {
case TYPE_SUBSYSTEMS:
udev_enumerate_scan_subsystems(udev_enumerate);
- exec_list(udev_enumerate, action);
- return 0;
+ break;
case TYPE_DEVICES:
udev_enumerate_scan_devices(udev_enumerate);
- exec_list(udev_enumerate, action);
- return 0;
+ break;
default:
assert_not_reached("device_type");
}
+ exec_list(udev_enumerate, action, settle_set);
+
+ while (!set_isempty(settle_set)) {
+ int fdcount;
+ struct epoll_event ev[4];
+ int i;
+
+ fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1);
+ if (fdcount < 0) {
+ if (errno != EINTR)
+ log_error_errno(errno, "error receiving uevent message: %m");
+ continue;
+ }
+
+ for (i = 0; i < fdcount; i++) {
+ if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) {
+ _cleanup_udev_device_unref_ struct udev_device *device;
+ const char *syspath = NULL;
+
+ device = udev_monitor_receive_device(udev_monitor);
+ if (device == NULL)
+ continue;
+
+ syspath = udev_device_get_syspath(device);
+ if (verbose)
+ printf("settle %s\n", syspath);
+ if (!set_remove(settle_set, syspath))
+ log_debug("Got epoll event on syspath %s not present in syspath set", syspath);
+ }
+ }
+ }
+
+ return 0;
}
const struct udevadm_cmd udevadm_trigger = {