diff options
Diffstat (limited to 'src/udev/udevadm-trigger.c')
-rw-r--r-- | src/udev/udevadm-trigger.c | 97 |
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 = { |