mirror of
https://github.com/systemd/systemd.git
synced 2025-03-25 18:50:18 +03:00
Merge pull request #8066 from LittleCVR/udevadm-trigger-and-settle
udevadm: allow trigger command to be synchronous
This commit is contained in:
commit
9e42c9373c
@ -335,6 +335,17 @@
|
||||
device.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>-w</option></term>
|
||||
<term><option>--settle</option></term>
|
||||
<listitem>
|
||||
<para>Apart from triggering events, also waits for those events to
|
||||
finish. Note that this is different from calling <command>udevadm
|
||||
settle</command>. <command>udevadm settle</command> waits for all
|
||||
events to finish. This option only waits for events triggered by
|
||||
the same command to finish.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
|
@ -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,25 +35,36 @@
|
||||
static int verbose;
|
||||
static int dry_run;
|
||||
|
||||
static void exec_list(struct udev_enumerate *udev_enumerate, const char *action) {
|
||||
static int exec_list(struct udev_enumerate *udev_enumerate, const char *action, Set *settle_set) {
|
||||
struct udev_list_entry *entry;
|
||||
int r;
|
||||
|
||||
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) {
|
||||
r = set_put_strdup(settle_set, syspath);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
if (write(fd, action, strlen(action)) < 0)
|
||||
log_debug_errno(errno, "error writing '%s' to '%s': %m", action, filename);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *keyval(const char *str, const char **val, char *buf, size_t size) {
|
||||
@ -87,6 +100,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 +123,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 +134,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)
|
||||
if (!udev_enumerate)
|
||||
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];
|
||||
@ -211,7 +232,7 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
|
||||
_cleanup_udev_device_unref_ struct udev_device *dev;
|
||||
|
||||
dev = find_device(udev, optarg, "/sys");
|
||||
if (dev == NULL) {
|
||||
if (!dev) {
|
||||
log_error("unable to open the device '%s'", optarg);
|
||||
return 2;
|
||||
}
|
||||
@ -223,12 +244,15 @@ 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;
|
||||
|
||||
dev = find_device(udev, optarg, "/dev/");
|
||||
if (dev == NULL) {
|
||||
if (!dev) {
|
||||
log_error("unable to open the device '%s'", optarg);
|
||||
return 2;
|
||||
}
|
||||
@ -258,7 +282,7 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
|
||||
_cleanup_udev_device_unref_ struct udev_device *dev;
|
||||
|
||||
dev = find_device(udev, argv[optind], NULL);
|
||||
if (dev == NULL) {
|
||||
if (!dev) {
|
||||
log_error("unable to open the device '%s'", argv[optind]);
|
||||
return 2;
|
||||
}
|
||||
@ -270,18 +294,83 @@ 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) {
|
||||
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) {
|
||||
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");
|
||||
}
|
||||
r = exec_list(udev_enumerate, action, settle_set);
|
||||
if (r < 0)
|
||||
return 1;
|
||||
|
||||
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)
|
||||
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 = {
|
||||
|
Loading…
x
Reference in New Issue
Block a user