/* 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 <unistd.h>
+#include "sd-device.h"
+#include "sd-event.h"
+
+#include "device-enumerator-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 "udev.h"
+#include "strv.h"
#include "udevadm.h"
#include "udevadm-util.h"
-#include "util.h"
+#include "udev-ctrl.h"
+#include "virt.h"
-static int verbose;
-static int dry_run;
+static bool arg_verbose = false;
+static bool arg_dry_run = false;
-static int exec_list(struct udev_enumerate *udev_enumerate, const char *action, Set *settle_set) {
- struct udev_list_entry *entry;
- int r;
+static int exec_list(sd_device_enumerator *e, const char *action, Set *settle_set) {
+ sd_device *d;
+ int r, ret = 0;
- udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_enumerate)) {
- char filename[UTIL_PATH_SIZE];
+ FOREACH_DEVICE_AND_SUBSYSTEM(e, d) {
+ _cleanup_free_ char *filename = NULL;
const char *syspath;
- _cleanup_close_ int fd = -1;
- syspath = udev_list_entry_get_name(entry);
- if (verbose)
+ if (sd_device_get_syspath(d, &syspath) < 0)
+ continue;
+
+ if (arg_verbose)
printf("%s\n", syspath);
- if (dry_run)
+ if (arg_dry_run)
continue;
- strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL);
- fd = open(filename, O_WRONLY|O_CLOEXEC);
- if (fd < 0)
+ filename = path_join(syspath, "uevent");
+ if (!filename)
+ return log_oom();
+
+ 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, "error writing '%s' to '%s': %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;
}
-static const char *keyval(const char *str, const char **val, char *buf, size_t size) {
- char *pos;
+static char* keyval(const char *str, const char **key, const char **val) {
+ char *buf, *pos;
+
+ buf = strdup(str);
+ if (!buf)
+ return NULL;
- strscpy(buf, size,str);
pos = strchr(buf, '=');
- if (pos != NULL) {
+ if (pos) {
pos[0] = 0;
pos++;
}
+
+ *key = buf;
*val = pos;
+
return buf;
}
" --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' },
{}
TYPE_SUBSYSTEMS,
} device_type = TYPE_DEVICES;
const char *action = "change";
- _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *udev_enumerate = NULL;
- _cleanup_(udev_monitor_unrefp) 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_(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;
+ usec_t ping_timeout_usec = 5 * USEC_PER_SEC;
+ bool settle = false, ping = false;
int c, r;
- udev_enumerate = udev_enumerate_new(NULL);
- if (!udev_enumerate)
- return -errno;
+ if (running_in_chroot() > 0) {
+ log_info("Running in chroot, ignoring request.");
+ return 0;
+ }
+
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_allow_uninitialized(e);
+ if (r < 0)
+ return r;
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];
+ _cleanup_free_ char *buf = NULL;
+ const char *key, *val;
switch (c) {
case 'v':
- verbose = 1;
+ arg_verbose = true;
break;
case 'n':
- dry_run = 1;
+ arg_dry_run = true;
break;
case 't':
if (streq(optarg, "devices"))
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 = udev_enumerate_add_match_subsystem(udev_enumerate, optarg);
+ r = sd_device_enumerator_add_match_subsystem(e, optarg, true);
if (r < 0)
- return log_error_errno(r, "could not add subsystem match '%s': %m", optarg);
+ return log_error_errno(r, "Failed to add subsystem match '%s': %m", optarg);
break;
case 'S':
- r = udev_enumerate_add_nomatch_subsystem(udev_enumerate, optarg);
+ r = sd_device_enumerator_add_match_subsystem(e, optarg, false);
if (r < 0)
- return log_error_errno(r, "could not add negative subsystem match '%s': %m", optarg);
+ return log_error_errno(r, "Failed to add negative subsystem match '%s': %m", optarg);
break;
case 'a':
- key = keyval(optarg, &val, buf, sizeof(buf));
- r = udev_enumerate_add_match_sysattr(udev_enumerate, key, val);
+ buf = keyval(optarg, &key, &val);
+ if (!buf)
+ return log_oom();
+ r = sd_device_enumerator_add_match_sysattr(e, key, val, true);
if (r < 0)
- return log_error_errno(r, "could not add sysattr match '%s=%s': %m", key, val);
+ return log_error_errno(r, "Failed to add sysattr match '%s=%s': %m", key, val);
break;
case 'A':
- key = keyval(optarg, &val, buf, sizeof(buf));
- r = udev_enumerate_add_nomatch_sysattr(udev_enumerate, key, val);
+ buf = keyval(optarg, &key, &val);
+ if (!buf)
+ return log_oom();
+ r = sd_device_enumerator_add_match_sysattr(e, key, val, false);
if (r < 0)
- return log_error_errno(r, "could not add negative sysattr match '%s=%s': %m", key, val);
+ return log_error_errno(r, "Failed to add negative sysattr match '%s=%s': %m", key, val);
break;
case 'p':
- key = keyval(optarg, &val, buf, sizeof(buf));
- r = udev_enumerate_add_match_property(udev_enumerate, key, val);
+ buf = keyval(optarg, &key, &val);
+ if (!buf)
+ return log_oom();
+ r = sd_device_enumerator_add_match_property(e, key, val);
if (r < 0)
- return log_error_errno(r, "could not add property match '%s=%s': %m", key, val);
+ return log_error_errno(r, "Failed to add property match '%s=%s': %m", key, val);
break;
case 'g':
- r = udev_enumerate_add_match_tag(udev_enumerate, optarg);
+ r = sd_device_enumerator_add_match_tag(e, optarg);
if (r < 0)
- return log_error_errno(r, "could not add tag match '%s': %m", optarg);
+ return log_error_errno(r, "Failed to add tag match '%s': %m", optarg);
break;
case 'y':
- r = udev_enumerate_add_match_sysname(udev_enumerate, optarg);
+ r = sd_device_enumerator_add_match_sysname(e, optarg);
if (r < 0)
- return log_error_errno(r, "could not add sysname match '%s': %m", optarg);
+ return log_error_errno(r, "Failed to add sysname match '%s': %m", optarg);
break;
case 'b': {
- _cleanup_(udev_device_unrefp) struct udev_device *dev;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
- dev = find_device(optarg, "/sys");
- if (!dev)
- return log_error_errno(errno, "unable to open the device '%s'", optarg);
+ r = find_device(optarg, "/sys", &dev);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open the device '%s': %m", optarg);
- r = udev_enumerate_add_match_parent(udev_enumerate, dev);
+ r = device_enumerator_add_match_parent_incremental(e, dev);
if (r < 0)
- return log_error_errno(r, "could not add parent match '%s': %m", optarg);
+ return log_error_errno(r, "Failed to add parent match '%s': %m", optarg);
break;
}
case 'w':
break;
case ARG_NAME: {
- _cleanup_(udev_device_unrefp) struct udev_device *dev;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
- dev = find_device(optarg, "/dev/");
- if (!dev)
- return log_error_errno(errno, "unable to open the device '%s'", optarg);
+ r = find_device(optarg, "/dev/", &dev);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open the device '%s': %m", optarg);
- r = udev_enumerate_add_match_parent(udev_enumerate, dev);
+ r = device_enumerator_add_match_parent_incremental(e, dev);
if (r < 0)
- return log_error_errno(r, "could not add parent match '%s': %m", optarg);
+ 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;
}
}
}
- for (; optind < argc; optind++) {
- _cleanup_(udev_device_unrefp) struct udev_device *dev;
+ if (ping) {
+ _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL;
- dev = find_device(argv[optind], NULL);
- if (!dev) {
- log_error("unable to open the device '%s'", argv[optind]);
- return -EINVAL;
- }
-
- r = udev_enumerate_add_match_parent(udev_enumerate, dev);
+ r = udev_ctrl_new(&uctrl);
if (r < 0)
- return log_error_errno(r, "could not add tag match '%s': %m", optarg);
- }
+ return log_error_errno(r, "Failed to initialize udev control: %m");
- if (settle) {
- fd_ep = epoll_create1(EPOLL_CLOEXEC);
- if (fd_ep < 0)
- return log_error_errno(errno, "error creating epoll fd: %m");
+ r = udev_ctrl_send_ping(uctrl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to udev daemon: %m");
- udev_monitor = udev_monitor_new_from_netlink(NULL, "udev");
- if (!udev_monitor)
- return log_error_errno(errno, "error: unable to create netlink socket: %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");
+ }
- fd_udev = udev_monitor_get_fd(udev_monitor);
+ for (; optind < argc; optind++) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
- r = udev_monitor_enable_receiving(udev_monitor);
+ r = find_device(argv[optind], NULL, &dev);
if (r < 0)
- return log_error_errno(r, "error: unable to subscribe to udev events: %m");
+ return log_error_errno(r, "Failed to open the device '%s': %m", argv[optind]);
- ep_udev = (struct epoll_event) { .events = EPOLLIN, .data.fd = fd_udev };
- if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0)
- return log_error_errno(errno, "fail to add fd to epoll: %m");
+ 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) {
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");
+
+ r = sd_device_monitor_attach_event(m, event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach event to device monitor: %m");
+
+ 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) {
case TYPE_SUBSYSTEMS:
- udev_enumerate_scan_subsystems(udev_enumerate);
+ r = device_enumerator_scan_subsystems(e);
+ if (r < 0)
+ return log_error_errno(r, "Failed to scan subsystems: %m");
break;
case TYPE_DEVICES:
- udev_enumerate_scan_devices(udev_enumerate);
+ r = device_enumerator_scan_devices(e);
+ if (r < 0)
+ return log_error_errno(r, "Failed to scan devices: %m");
break;
default:
- assert_not_reached("device_type");
+ assert_not_reached("Unknown device type");
}
- r = exec_list(udev_enumerate, action, settle_set);
+ r = exec_list(e, action, settle_set);
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, "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_unrefp) 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);
- }
- }
+ 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;