/* SPDX-License-Identifier: GPL-2.0+ */
#include <errno.h>
-#include <fcntl.h>
#include <getopt.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/epoll.h>
-#include <unistd.h>
+
+#include "sd-device.h"
+#include "sd-event.h"
#include "device-enumerator-private.h"
-#include "device-monitor-private.h"
+#include "device-private.h"
#include "fd-util.h"
+#include "fileio.h"
#include "path-util.h"
+#include "process-util.h"
#include "set.h"
#include "string-util.h"
#include "strv.h"
#include "udevadm.h"
#include "udevadm-util.h"
+#include "udev-ctrl.h"
+#include "virt.h"
static bool arg_verbose = false;
static bool arg_dry_run = false;
static int exec_list(sd_device_enumerator *e, const char *action, Set *settle_set) {
sd_device *d;
- int r;
+ int r, ret = 0;
FOREACH_DEVICE_AND_SUBSYSTEM(e, d) {
_cleanup_free_ char *filename = NULL;
- _cleanup_close_ int fd = -1;
const char *syspath;
if (sd_device_get_syspath(d, &syspath) < 0)
if (arg_dry_run)
continue;
- filename = path_join(NULL, syspath, "uevent");
+ filename = path_join(syspath, "uevent");
if (!filename)
return log_oom();
- fd = open(filename, O_WRONLY|O_CLOEXEC);
- if (fd < 0)
+ r = write_string_file(filename, action, WRITE_STRING_FILE_DISABLE_BUFFER);
+ if (r < 0) {
+ log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_ERR, r,
+ "Failed to write '%s' to '%s': %m", action, filename);
+ if (ret == 0 && r != -ENOENT)
+ ret = r;
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, "Failed to write '%s' to '%s', ignoring: %m", action, filename);
}
+ return ret;
+}
+
+static int device_monitor_handler(sd_device_monitor *m, sd_device *dev, void *userdata) {
+ _cleanup_free_ char *val = NULL;
+ Set *settle_set = userdata;
+ const char *syspath;
+
+ assert(dev);
+ assert(settle_set);
+
+ if (sd_device_get_syspath(dev, &syspath) < 0)
+ return 0;
+
+ if (arg_verbose)
+ printf("settle %s\n", syspath);
+
+ val = set_remove(settle_set, syspath);
+ if (!val)
+ log_debug("Got epoll event on syspath %s not present in syspath set", syspath);
+
+ if (set_isempty(settle_set))
+ return sd_event_exit(sd_device_monitor_get_event(m), 0);
+
return 0;
}
" --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"
+ " --wait-daemon[=SECONDS] Wait for udevd daemon to be initialized\n"
+ " before triggering uevents\n"
, program_invocation_short_name);
return 0;
int trigger_main(int argc, char *argv[], void *userdata) {
enum {
ARG_NAME = 0x100,
+ ARG_PING,
};
static const struct option options[] = {
{ "name-match", required_argument, NULL, ARG_NAME },
{ "parent-match", required_argument, NULL, 'b' },
{ "settle", no_argument, NULL, 'w' },
+ { "wait-daemon", optional_argument, NULL, ARG_PING },
{ "version", no_argument, NULL, 'V' },
{ "help", no_argument, NULL, 'h' },
{}
const char *action = "change";
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *m = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_set_free_free_ Set *settle_set = NULL;
- _cleanup_close_ int fd_ep = -1;
- struct epoll_event ep_monitor;
- int c, r, fd_monitor = -1;
- bool settle = false;
+ usec_t ping_timeout_usec = 5 * USEC_PER_SEC;
+ bool settle = false, ping = false;
+ int c, r;
+
+ if (running_in_chroot() > 0) {
+ log_info("Running in chroot, ignoring request.");
+ return 0;
+ }
r = sd_device_enumerator_new(&e);
if (r < 0)
device_type = TYPE_DEVICES;
else if (streq(optarg, "subsystems"))
device_type = TYPE_SUBSYSTEMS;
- else {
- log_error("Unknown type --type=%s", optarg);
- return -EINVAL;
- }
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown type --type=%s", optarg);
break;
case 'c':
- if (STR_IN_SET(optarg, "add", "remove", "change"))
- action = optarg;
- else {
- log_error("Unknown action '%s'", optarg);
- return -EINVAL;
- }
+ if (device_action_from_string(optarg) < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown action '%s'", optarg);
+ action = optarg;
break;
case 's':
r = sd_device_enumerator_add_match_subsystem(e, optarg, true);
if (r < 0)
return log_error_errno(r, "Failed to open the device '%s': %m", optarg);
- r = sd_device_enumerator_add_match_parent(e, dev);
+ r = device_enumerator_add_match_parent_incremental(e, dev);
if (r < 0)
return log_error_errno(r, "Failed to add parent match '%s': %m", optarg);
break;
if (r < 0)
return log_error_errno(r, "Failed to open the device '%s': %m", optarg);
- r = sd_device_enumerator_add_match_parent(e, dev);
+ r = device_enumerator_add_match_parent_incremental(e, dev);
if (r < 0)
return log_error_errno(r, "Failed to add parent match '%s': %m", optarg);
break;
}
+ case ARG_PING: {
+ ping = true;
+ if (optarg) {
+ r = parse_sec(optarg, &ping_timeout_usec);
+ if (r < 0)
+ log_error_errno(r, "Failed to parse timeout value '%s', ignoring: %m", optarg);
+ }
+ break;
+ }
+
case 'V':
return print_version();
case 'h':
}
}
+ if (ping) {
+ _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL;
+
+ r = udev_ctrl_new(&uctrl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to initialize udev control: %m");
+
+ r = udev_ctrl_send_ping(uctrl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to udev daemon: %m");
+
+ r = udev_ctrl_wait(uctrl, ping_timeout_usec);
+ if (r < 0)
+ return log_error_errno(r, "Failed to wait for daemon to reply: %m");
+ }
+
for (; optind < argc; optind++) {
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
if (r < 0)
return log_error_errno(r, "Failed to open the device '%s': %m", argv[optind]);
- r = sd_device_enumerator_add_match_parent(e, dev);
+ r = device_enumerator_add_match_parent_incremental(e, dev);
if (r < 0)
return log_error_errno(r, "Failed to add parent match '%s': %m", argv[optind]);
}
if (settle) {
- fd_ep = epoll_create1(EPOLL_CLOEXEC);
- if (fd_ep < 0)
- return log_error_errno(errno, "Failed to create epoll fd: %m");
+ settle_set = set_new(&string_hash_ops);
+ if (!settle_set)
+ return log_oom();
+
+ r = sd_event_default(&event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get default event: %m");
r = sd_device_monitor_new(&m);
if (r < 0)
return log_error_errno(r, "Failed to create device monitor object: %m");
- fd_monitor = device_monitor_get_fd(m);
- if (fd_monitor < 0)
- return log_error_errno(fd_monitor, "Failed to get monitor fd: %m");
-
- r = device_monitor_enable_receiving(m);
+ r = sd_device_monitor_attach_event(m, event);
if (r < 0)
- return log_error_errno(r, "Failed to subscribe udev events: %m");
-
- ep_monitor = (struct epoll_event) {
- .events = EPOLLIN,
- .data.fd = fd_monitor,
- };
- if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0)
- return log_error_errno(errno, "Failed to add fd to epoll: %m");
+ return log_error_errno(r, "Failed to attach event to device monitor: %m");
- settle_set = set_new(&string_hash_ops);
- if (!settle_set)
- return log_oom();
+ r = sd_device_monitor_start(m, device_monitor_handler, settle_set);
+ if (r < 0)
+ return log_error_errno(r, "Failed to start device monitor: %m");
}
switch (device_type) {
if (r < 0)
return r;
- 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, "Failed to receive uevent message: %m");
- continue;
- }
-
- for (i = 0; i < fdcount; i++) {
- _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
- const char *syspath;
-
- if (!(ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN))
- continue;
-
- if (device_monitor_receive_device(m, &dev) <= 0)
- continue;
-
- if (sd_device_get_syspath(dev, &syspath) < 0)
- continue;
-
- if (arg_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);
- }
+ if (event && !set_isempty(settle_set)) {
+ r = sd_event_loop(event);
+ if (r < 0)
+ return log_error_errno(r, "Event loop failed: %m");
}
return 0;