-/* SPDX-License-Identifier: GPL-2.0+ */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <ctype.h>
#include <errno.h>
pid_t pid;
usec_t timeout_warn_usec;
usec_t timeout_usec;
+ int timeout_signal;
usec_t event_birth_usec;
bool accept_failure;
int fd_stdout;
}
case FORMAT_SUBST_PARENT:
r = sd_device_get_parent(dev, &parent);
- if (r == -ENODEV)
+ if (r == -ENOENT)
goto null_terminate;
if (r < 0)
return r;
return 0;
}
-ssize_t udev_event_apply_format(UdevEvent *event,
- const char *src, char *dest, size_t size,
- bool replace_whitespace) {
+size_t udev_event_apply_format(UdevEvent *event,
+ const char *src, char *dest, size_t size,
+ bool replace_whitespace) {
const char *s = src;
int r;
ssize_t subst_len;
r = get_subst_type(&s, false, &type, attr);
- if (r < 0)
- return log_device_warning_errno(event->dev, r, "Invalid format string, ignoring: %s", src);
- if (r == 0) {
+ if (r < 0) {
+ log_device_warning_errno(event->dev, r, "Invalid format string, ignoring: %s", src);
+ break;
+ } else if (r == 0) {
if (size < 2) /* need space for this char and the terminating NUL */
break;
*dest++ = *s++;
}
subst_len = udev_event_subst_format(event, type, attr, dest, size);
- if (subst_len < 0)
- return log_device_warning_errno(event->dev, subst_len,
- "Failed to substitute variable '$%s' or apply format '%%%c', ignoring: %m",
- format_type_to_string(type), format_type_to_char(type));
+ if (subst_len < 0) {
+ log_device_warning_errno(event->dev, subst_len,
+ "Failed to substitute variable '$%s' or apply format '%%%c', ignoring: %m",
+ format_type_to_string(type), format_type_to_char(type));
+ break;
+ }
/* FORMAT_SUBST_RESULT handles spaces itself */
if (replace_whitespace && type != FORMAT_SUBST_RESULT)
char buf[4096], *p;
size_t size;
ssize_t l;
+ int r;
assert(spawn);
assert(fd == spawn->fd_stdout || fd == spawn->fd_stderr);
l = read(fd, p, size - 1);
if (l < 0) {
- if (errno != EAGAIN)
- log_device_error_errno(spawn->device, errno,
- "Failed to read stdout of '%s': %m", spawn->cmd);
+ if (errno == EAGAIN)
+ goto reenable;
+
+ log_device_error_errno(spawn->device, errno,
+ "Failed to read stdout of '%s': %m", spawn->cmd);
return 0;
}
fd == spawn->fd_stdout ? "out" : "err", *q);
}
+
+ if (l == 0)
+ return 0;
+
+ /* Re-enable the event source if we did not encounter EOF */
+reenable:
+ r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
+ if (r < 0)
+ log_device_error_errno(spawn->device, r,
+ "Failed to reactivate IO source of '%s'", spawn->cmd);
return 0;
}
assert(spawn);
- kill_and_sigcont(spawn->pid, SIGKILL);
+ kill_and_sigcont(spawn->pid, spawn->timeout_signal);
log_device_error(spawn->device, "Spawned process '%s' ["PID_FMT"] timed out after %s, killing",
spawn->cmd, spawn->pid,
if (si->si_status == 0)
log_device_debug(spawn->device, "Process '%s' succeeded.", spawn->cmd);
else
- log_device_full(spawn->device, spawn->accept_failure ? LOG_DEBUG : LOG_WARNING, 0,
+ log_device_full(spawn->device, spawn->accept_failure ? LOG_DEBUG : LOG_WARNING,
"Process '%s' failed with exit code %i.", spawn->cmd, si->si_status);
ret = si->si_status;
break;
static int spawn_wait(Spawn *spawn) {
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_(sd_event_source_unrefp) sd_event_source *sigchld_source = NULL;
+ _cleanup_(sd_event_source_unrefp) sd_event_source *stdout_source = NULL;
+ _cleanup_(sd_event_source_unrefp) sd_event_source *stderr_source = NULL;
int r;
assert(spawn);
}
if (spawn->fd_stdout >= 0) {
- r = sd_event_add_io(e, NULL, spawn->fd_stdout, EPOLLIN, on_spawn_io, spawn);
+ r = sd_event_add_io(e, &stdout_source, spawn->fd_stdout, EPOLLIN, on_spawn_io, spawn);
+ if (r < 0)
+ return r;
+ r = sd_event_source_set_enabled(stdout_source, SD_EVENT_ONESHOT);
if (r < 0)
return r;
}
if (spawn->fd_stderr >= 0) {
- r = sd_event_add_io(e, NULL, spawn->fd_stderr, EPOLLIN, on_spawn_io, spawn);
+ r = sd_event_add_io(e, &stderr_source, spawn->fd_stderr, EPOLLIN, on_spawn_io, spawn);
+ if (r < 0)
+ return r;
+ r = sd_event_source_set_enabled(stderr_source, SD_EVENT_ONESHOT);
if (r < 0)
return r;
}
- r = sd_event_add_child(e, NULL, spawn->pid, WEXITED, on_spawn_sigchld, spawn);
+ r = sd_event_add_child(e, &sigchld_source, spawn->pid, WEXITED, on_spawn_sigchld, spawn);
+ if (r < 0)
+ return r;
+ /* SIGCHLD should be processed after IO is complete */
+ r = sd_event_source_set_priority(sigchld_source, SD_EVENT_PRIORITY_NORMAL + 1);
if (r < 0)
return r;
+
return sd_event_loop(e);
}
int udev_event_spawn(UdevEvent *event,
usec_t timeout_usec,
+ int timeout_signal,
bool accept_failure,
const char *cmd,
char *result, size_t ressize) {
return log_device_error_errno(event->dev, errno,
"Failed to create pipe for command '%s': %m", cmd);
- argv = strv_split_full(cmd, NULL, SPLIT_QUOTES|SPLIT_RELAX);
- if (!argv)
- return log_oom();
+ r = strv_split_full(&argv, cmd, NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_RETAIN_ESCAPE);
+ if (r < 0)
+ return log_device_error_errno(event->dev, r, "Failed to split command: %m");
if (isempty(argv[0]))
return log_device_error_errno(event->dev, SYNTHETIC_ERRNO(EINVAL),
.accept_failure = accept_failure,
.timeout_warn_usec = udev_warn_timeout(timeout_usec),
.timeout_usec = timeout_usec,
+ .timeout_signal = timeout_signal,
.event_birth_usec = event->birth_usec,
.fd_stdout = outpipe[READ_END],
.fd_stderr = errpipe[READ_END],
if (r < 0)
return log_device_error_errno(dev, r, "Failed to get ifindex: %m");
- r = rtnl_set_link_name(&event->rtnl, ifindex, event->name);
- if (r < 0)
- return log_device_error_errno(dev, r, "Failed to rename network interface %i from '%s' to '%s': %m",
- ifindex, oldname, event->name);
-
/* Set ID_RENAMING boolean property here, and drop it in the corresponding move uevent later. */
r = device_add_property(dev, "ID_RENAMING", "1");
if (r < 0)
if (r < 0)
return log_device_warning_errno(dev, r, "Failed to update properties with new name '%s': %m", event->name);
+ /* Also set ID_RENAMING boolean property to cloned sd_device object and save it to database
+ * before calling rtnl_set_link_name(). Otherwise, clients (e.g., systemd-networkd) may receive
+ * RTM_NEWLINK netlink message before the database is updated. */
+ r = device_add_property(event->dev_db_clone, "ID_RENAMING", "1");
+ if (r < 0)
+ return log_device_warning_errno(event->dev_db_clone, r, "Failed to add 'ID_RENAMING' property: %m");
+
+ r = device_update_db(event->dev_db_clone);
+ if (r < 0)
+ return log_device_debug_errno(event->dev_db_clone, r, "Failed to update database under /run/udev/data/: %m");
+
+ r = rtnl_set_link_name(&event->rtnl, ifindex, event->name);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to rename network interface %i from '%s' to '%s': %m",
+ ifindex, oldname, event->name);
+
log_device_debug(dev, "Network interface %i is renamed from '%s' to '%s'", ifindex, oldname, event->name);
return 1;
return log_device_error_errno(dev, r, "Failed to get devnum: %m");
/* remove/update possible left-over symlinks from old database entry */
- if (event->dev_db_clone)
- (void) udev_node_update_old_links(dev, event->dev_db_clone);
+ (void) udev_node_update_old_links(dev, event->dev_db_clone);
if (!uid_is_valid(event->uid)) {
r = device_get_devnode_uid(dev, &event->uid);
static void event_execute_rules_on_remove(
UdevEvent *event,
usec_t timeout_usec,
+ int timeout_signal,
Hashmap *properties_list,
UdevRules *rules) {
if (sd_device_get_devnum(dev, NULL) >= 0)
(void) udev_watch_end(dev);
- (void) udev_rules_apply_to_event(rules, event, timeout_usec, properties_list);
+ (void) udev_rules_apply_to_event(rules, event, timeout_usec, timeout_signal, properties_list);
if (sd_device_get_devnum(dev, NULL) >= 0)
(void) udev_node_remove(dev);
}
-static int udev_event_on_move(UdevEvent *event) {
- sd_device *dev = event->dev;
+static int udev_event_on_move(sd_device *dev) {
int r;
- if (event->dev_db_clone &&
- sd_device_get_devnum(dev, NULL) < 0) {
- r = device_copy_properties(dev, event->dev_db_clone);
- if (r < 0)
- log_device_debug_errno(dev, r, "Failed to copy properties from cloned sd_device object, ignoring: %m");
- }
-
/* Drop previously added property */
r = device_add_property(dev, "ID_RENAMING", NULL);
if (r < 0)
return 0;
}
+static int copy_all_tags(sd_device *d, sd_device *s) {
+ const char *tag;
+ int r;
+
+ assert(d);
+
+ if (!s)
+ return 0;
+
+ for (tag = sd_device_get_tag_first(s); tag; tag = sd_device_get_tag_next(s)) {
+ r = device_add_tag(d, tag, false);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
int udev_event_execute_rules(UdevEvent *event,
usec_t timeout_usec,
+ int timeout_signal,
Hashmap *properties_list,
UdevRules *rules) {
const char *subsystem;
return log_device_error_errno(dev, r, "Failed to get ACTION: %m");
if (action == DEVICE_ACTION_REMOVE) {
- event_execute_rules_on_remove(event, timeout_usec, properties_list, rules);
+ event_execute_rules_on_remove(event, timeout_usec, timeout_signal, properties_list, rules);
return 0;
}
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to clone sd_device object: %m");
- if (event->dev_db_clone && sd_device_get_devnum(dev, NULL) >= 0)
+ r = copy_all_tags(dev, event->dev_db_clone);
+ if (r < 0)
+ log_device_warning_errno(dev, r, "Failed to copy all tags from old database entry, ignoring: %m");
+
+ if (sd_device_get_devnum(dev, NULL) >= 0)
/* Disable watch during event processing. */
(void) udev_watch_end(event->dev_db_clone);
if (action == DEVICE_ACTION_MOVE) {
- r = udev_event_on_move(event);
+ r = udev_event_on_move(event->dev);
if (r < 0)
return r;
}
- r = udev_rules_apply_to_event(rules, event, timeout_usec, properties_list);
+ r = udev_rules_apply_to_event(rules, event, timeout_usec, timeout_signal, properties_list);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to apply udev rules: %m");
device_set_is_initialized(dev);
- event->dev_db_clone = sd_device_unref(event->dev_db_clone);
-
return 0;
}
-void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec) {
+void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec, int timeout_signal) {
const char *command;
void *val;
- Iterator i;
int r;
- ORDERED_HASHMAP_FOREACH_KEY(val, command, event->run_list, i) {
+ ORDERED_HASHMAP_FOREACH_KEY(val, command, event->run_list) {
UdevBuiltinCommand builtin_cmd = PTR_TO_UDEV_BUILTIN_CMD(val);
if (builtin_cmd != _UDEV_BUILTIN_INVALID) {
}
log_device_debug(event->dev, "Running command \"%s\"", command);
- r = udev_event_spawn(event, timeout_usec, false, command, NULL, 0);
+
+ r = udev_event_spawn(event, timeout_usec, timeout_signal, false, command, NULL, 0);
if (r < 0)
log_device_warning_errno(event->dev, r, "Failed to execute '%s', ignoring: %m", command);
else if (r > 0) /* returned value is positive when program fails */