| From 651c9d55e16b36846bcd633ee336da7939cff5b5 Mon Sep 17 00:00:00 2001 |
| From: Yu Watanabe <watanabe.yu+github@gmail.com> |
| Date: Wed, 1 Sep 2021 09:22:15 +0900 |
| Subject: [PATCH 01/12] sd-device: introduce device_has_devlink() |
| |
| --- |
| src/libsystemd/sd-device/device-private.h | 1 + |
| src/libsystemd/sd-device/sd-device.c | 7 +++++++ |
| 2 files changed, 8 insertions(+) |
| |
| diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h |
| index fe268d7f2f..9bb5eff208 100644 |
| --- a/src/libsystemd/sd-device/device-private.h |
| +++ b/src/libsystemd/sd-device/device-private.h |
| @@ -32,6 +32,7 @@ void device_set_db_persist(sd_device *device); |
| void device_set_devlink_priority(sd_device *device, int priority); |
| int device_ensure_usec_initialized(sd_device *device, sd_device *device_old); |
| int device_add_devlink(sd_device *device, const char *devlink); |
| +bool device_has_devlink(sd_device *device, const char *devlink); |
| int device_add_property(sd_device *device, const char *property, const char *value); |
| int device_add_tag(sd_device *device, const char *tag, bool both); |
| void device_remove_tag(sd_device *device, const char *tag); |
| diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c |
| index 388128bf33..8a9e4a33a1 100644 |
| --- a/src/libsystemd/sd-device/sd-device.c |
| +++ b/src/libsystemd/sd-device/sd-device.c |
| @@ -1193,6 +1193,13 @@ int device_add_devlink(sd_device *device, const char *devlink) { |
| return 0; |
| } |
| |
| +bool device_has_devlink(sd_device *device, const char *devlink) { |
| + assert(device); |
| + assert(devlink); |
| + |
| + return set_contains(device->devlinks, devlink); |
| +} |
| + |
| static int device_add_property_internal_from_string(sd_device *device, const char *str) { |
| _cleanup_free_ char *key = NULL; |
| char *value; |
| -- |
| 2.36.1.124.g0e6072fb45-goog |
| |
| |
| From fdeaaff253022b9e35a27656dda8ed500f6dc613 Mon Sep 17 00:00:00 2001 |
| From: Yu Watanabe <watanabe.yu+github@gmail.com> |
| Date: Wed, 1 Sep 2021 09:24:15 +0900 |
| Subject: [PATCH 02/12] udev-node: split out permission handling from |
| udev_node_add() |
| |
| And then merge udev_node_add() and udev_node_update_old_links(). |
| --- |
| src/udev/udev-event.c | 9 +- |
| src/udev/udev-node.c | 204 +++++++++++++++++++----------------------- |
| src/udev/udev-node.h | 12 ++- |
| 3 files changed, 106 insertions(+), 119 deletions(-) |
| |
| diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c |
| index b28089be71..8b9f8aecfe 100644 |
| --- a/src/udev/udev-event.c |
| +++ b/src/udev/udev-event.c |
| @@ -895,9 +895,6 @@ static int update_devnode(UdevEvent *event) { |
| if (r < 0) |
| return log_device_error_errno(dev, r, "Failed to get devnum: %m"); |
| |
| - /* remove/update possible left-over symlinks from old database entry */ |
| - (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); |
| if (r < 0 && r != -ENOENT) |
| @@ -921,7 +918,11 @@ static int update_devnode(UdevEvent *event) { |
| |
| bool apply_mac = device_for_action(dev, SD_DEVICE_ADD); |
| |
| - return udev_node_add(dev, apply_mac, event->mode, event->uid, event->gid, event->seclabel_list); |
| + r = udev_node_apply_permissions(dev, apply_mac, event->mode, event->uid, event->gid, event->seclabel_list); |
| + if (r < 0) |
| + return log_device_error_errno(dev, r, "Failed to apply devnode permissions: %m"); |
| + |
| + return udev_node_update(dev, event->dev_db_clone); |
| } |
| |
| static int event_execute_rules_on_remove( |
| diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c |
| index 9e52906571..7cc9ee3670 100644 |
| --- a/src/udev/udev-node.c |
| +++ b/src/udev/udev-node.c |
| @@ -356,45 +356,117 @@ static int link_update(sd_device *dev, const char *slink_in, bool add) { |
| return i < LINK_UPDATE_MAX_RETRIES ? 0 : -ELOOP; |
| } |
| |
| -int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) { |
| - const char *name; |
| +static int device_get_devpath_by_devnum(sd_device *dev, char **ret) { |
| + const char *subsystem; |
| + dev_t devnum; |
| + int r; |
| + |
| + assert(dev); |
| + assert(ret); |
| + |
| + r = sd_device_get_subsystem(dev, &subsystem); |
| + if (r < 0) |
| + return r; |
| + |
| + r = sd_device_get_devnum(dev, &devnum); |
| + if (r < 0) |
| + return r; |
| + |
| + return device_path_make_major_minor(streq(subsystem, "block") ? S_IFBLK : S_IFCHR, devnum, ret); |
| +} |
| + |
| +int udev_node_update(sd_device *dev, sd_device *dev_old) { |
| + _cleanup_free_ char *filename = NULL; |
| + const char *devnode, *devlink; |
| int r; |
| |
| assert(dev); |
| assert(dev_old); |
| |
| - /* update possible left-over symlinks */ |
| - FOREACH_DEVICE_DEVLINK(dev_old, name) { |
| - const char *name_current; |
| - bool found = false; |
| + r = sd_device_get_devname(dev, &devnode); |
| + if (r < 0) |
| + return log_device_debug_errno(dev, r, "Failed to get devnode: %m"); |
| |
| - /* check if old link name still belongs to this device */ |
| - FOREACH_DEVICE_DEVLINK(dev, name_current) |
| - if (streq(name, name_current)) { |
| - found = true; |
| - break; |
| - } |
| + if (DEBUG_LOGGING) { |
| + const char *id = NULL; |
| |
| - if (found) |
| + (void) device_get_device_id(dev, &id); |
| + log_device_debug(dev, "Handling device node '%s', devnum=%s", devnode, strna(id)); |
| + } |
| + |
| + /* update possible left-over symlinks */ |
| + FOREACH_DEVICE_DEVLINK(dev_old, devlink) { |
| + /* check if old link name still belongs to this device */ |
| + if (device_has_devlink(dev, devlink)) |
| continue; |
| |
| log_device_debug(dev, |
| - "Updating old device symlink '%s', which is no longer belonging to this device.", |
| - name); |
| + "Removing/updating old device symlink '%s', which is no longer belonging to this device.", |
| + devlink); |
| |
| - r = link_update(dev, name, false); |
| + r = link_update(dev, devlink, /* add = */ false); |
| if (r < 0) |
| log_device_warning_errno(dev, r, |
| - "Failed to update device symlink '%s', ignoring: %m", |
| - name); |
| + "Failed to remove/update device symlink '%s', ignoring: %m", |
| + devlink); |
| } |
| |
| + /* create/update symlinks, add symlinks to name index */ |
| + FOREACH_DEVICE_DEVLINK(dev, devlink) { |
| + r = link_update(dev, devlink, /* add = */ true); |
| + if (r < 0) |
| + log_device_warning_errno(dev, r, |
| + "Failed to create/update device symlink '%s', ignoring: %m", |
| + devlink); |
| + } |
| + |
| + r = device_get_devpath_by_devnum(dev, &filename); |
| + if (r < 0) |
| + return log_device_debug_errno(dev, r, "Failed to get device path: %m"); |
| + |
| + /* always add /dev/{block,char}/$major:$minor */ |
| + r = node_symlink(dev, devnode, filename); |
| + if (r < 0) |
| + return log_device_warning_errno(dev, r, "Failed to create device symlink '%s': %m", filename); |
| + |
| + return 0; |
| +} |
| + |
| +int udev_node_remove(sd_device *dev) { |
| + _cleanup_free_ char *filename = NULL; |
| + const char *devlink; |
| + int r; |
| + |
| + assert(dev); |
| + |
| + /* remove/update symlinks, remove symlinks from name index */ |
| + FOREACH_DEVICE_DEVLINK(dev, devlink) { |
| + r = link_update(dev, devlink, /* add = */ false); |
| + if (r < 0) |
| + log_device_warning_errno(dev, r, |
| + "Failed to remove/update device symlink '%s', ignoring: %m", |
| + devlink); |
| + } |
| + |
| + r = device_get_devpath_by_devnum(dev, &filename); |
| + if (r < 0) |
| + return log_device_debug_errno(dev, r, "Failed to get device path: %m"); |
| + |
| + /* remove /dev/{block,char}/$major:$minor */ |
| + if (unlink(filename) < 0 && errno != ENOENT) |
| + return log_device_debug_errno(dev, errno, "Failed to remove '%s': %m", filename); |
| + |
| return 0; |
| } |
| |
| -static int node_permissions_apply(sd_device *dev, bool apply_mac, |
| - mode_t mode, uid_t uid, gid_t gid, |
| - OrderedHashmap *seclabel_list) { |
| +int udev_node_apply_permissions( |
| + sd_device *dev, |
| + bool apply_mac, |
| + mode_t mode, |
| + uid_t uid, |
| + gid_t gid, |
| + OrderedHashmap *seclabel_list) { |
| + |
| const char *devnode, *subsystem, *id = NULL; |
| bool apply_mode, apply_uid, apply_gid; |
| _cleanup_close_ int node_fd = -1; |
| @@ -511,95 +583,5 @@ static int node_permissions_apply(sd_device *dev, bool apply_mac, |
| if (r < 0) |
| log_device_debug_errno(dev, r, "Failed to adjust timestamp of node %s: %m", devnode); |
| |
| - return r; |
| -} |
| - |
| -static int xsprintf_dev_num_path_from_sd_device(sd_device *dev, char **ret) { |
| - const char *subsystem; |
| - dev_t devnum; |
| - int r; |
| - |
| - assert(ret); |
| - |
| - r = sd_device_get_subsystem(dev, &subsystem); |
| - if (r < 0) |
| - return r; |
| - |
| - r = sd_device_get_devnum(dev, &devnum); |
| - if (r < 0) |
| - return r; |
| - |
| - return device_path_make_major_minor(streq(subsystem, "block") ? S_IFBLK : S_IFCHR, devnum, ret); |
| -} |
| - |
| -int udev_node_add(sd_device *dev, bool apply, |
| - mode_t mode, uid_t uid, gid_t gid, |
| - OrderedHashmap *seclabel_list) { |
| - const char *devnode, *devlink; |
| - _cleanup_free_ char *filename = NULL; |
| - int r; |
| - |
| - assert(dev); |
| - |
| - r = sd_device_get_devname(dev, &devnode); |
| - if (r < 0) |
| - return log_device_debug_errno(dev, r, "Failed to get devnode: %m"); |
| - |
| - if (DEBUG_LOGGING) { |
| - const char *id = NULL; |
| - |
| - (void) device_get_device_id(dev, &id); |
| - log_device_debug(dev, "Handling device node '%s', devnum=%s", devnode, strna(id)); |
| - } |
| - |
| - r = node_permissions_apply(dev, apply, mode, uid, gid, seclabel_list); |
| - if (r < 0) |
| - return r; |
| - |
| - /* create/update symlinks, add symlinks to name index */ |
| - FOREACH_DEVICE_DEVLINK(dev, devlink) { |
| - r = link_update(dev, devlink, true); |
| - if (r < 0) |
| - log_device_warning_errno(dev, r, |
| - "Failed to update device symlink '%s', ignoring: %m", |
| - devlink); |
| - } |
| - |
| - r = xsprintf_dev_num_path_from_sd_device(dev, &filename); |
| - if (r < 0) |
| - return log_device_debug_errno(dev, r, "Failed to get device path: %m"); |
| - |
| - /* always add /dev/{block,char}/$major:$minor */ |
| - r = node_symlink(dev, devnode, filename); |
| - if (r < 0) |
| - return log_device_warning_errno(dev, r, "Failed to create device symlink '%s': %m", filename); |
| - |
| - return 0; |
| -} |
| - |
| -int udev_node_remove(sd_device *dev) { |
| - _cleanup_free_ char *filename = NULL; |
| - const char *devlink; |
| - int r; |
| - |
| - assert(dev); |
| - |
| - /* remove/update symlinks, remove symlinks from name index */ |
| - FOREACH_DEVICE_DEVLINK(dev, devlink) { |
| - r = link_update(dev, devlink, false); |
| - if (r < 0) |
| - log_device_warning_errno(dev, r, |
| - "Failed to update device symlink '%s', ignoring: %m", |
| - devlink); |
| - } |
| - |
| - r = xsprintf_dev_num_path_from_sd_device(dev, &filename); |
| - if (r < 0) |
| - return log_device_debug_errno(dev, r, "Failed to get device path: %m"); |
| - |
| - /* remove /dev/{block,char}/$major:$minor */ |
| - if (unlink(filename) < 0 && errno != ENOENT) |
| - return log_device_debug_errno(dev, errno, "Failed to remove '%s': %m", filename); |
| - |
| return 0; |
| } |
| diff --git a/src/udev/udev-node.h b/src/udev/udev-node.h |
| index 2349f9c471..a34af77146 100644 |
| --- a/src/udev/udev-node.h |
| +++ b/src/udev/udev-node.h |
| @@ -8,10 +8,14 @@ |
| |
| #include "hashmap.h" |
| |
| -int udev_node_add(sd_device *dev, bool apply, |
| - mode_t mode, uid_t uid, gid_t gid, |
| - OrderedHashmap *seclabel_list); |
| +int udev_node_apply_permissions( |
| + sd_device *dev, |
| + bool apply_mac, |
| + mode_t mode, |
| + uid_t uid, |
| + gid_t gid, |
| + OrderedHashmap *seclabel_list); |
| int udev_node_remove(sd_device *dev); |
| -int udev_node_update_old_links(sd_device *dev, sd_device *dev_old); |
| +int udev_node_update(sd_device *dev, sd_device *dev_old); |
| |
| size_t udev_node_escape_path(const char *src, char *dest, size_t size); |
| -- |
| 2.36.1.124.g0e6072fb45-goog |
| |
| |
| From 7840a984836dc8f14b900794a74ad3945b48e682 Mon Sep 17 00:00:00 2001 |
| From: Yu Watanabe <watanabe.yu+github@gmail.com> |
| Date: Wed, 1 Sep 2021 04:14:42 +0900 |
| Subject: [PATCH 03/12] udev-node: stack directory must exist when adding |
| device node symlink |
| |
| --- |
| src/udev/udev-node.c | 11 ++++++----- |
| 1 file changed, 6 insertions(+), 5 deletions(-) |
| |
| diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c |
| index 7cc9ee3670..4496a2bd9b 100644 |
| --- a/src/udev/udev-node.c |
| +++ b/src/udev/udev-node.c |
| @@ -161,12 +161,13 @@ static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir, |
| |
| dir = opendir(stackdir); |
| if (!dir) { |
| - if (errno == ENOENT) { |
| - *ret = TAKE_PTR(target); |
| - return !!*ret; |
| - } |
| + if (add) /* The stack directory must exist. */ |
| + return -errno; |
| + if (errno != ENOENT) |
| + return -errno; |
| |
| - return -errno; |
| + *ret = NULL; |
| + return 0; |
| } |
| |
| r = device_get_device_id(dev, &id); |
| -- |
| 2.36.1.124.g0e6072fb45-goog |
| |
| |
| From 4a47c387c8046a6fa67ee142ec3af3006c38057e Mon Sep 17 00:00:00 2001 |
| From: Yu Watanabe <watanabe.yu+github@gmail.com> |
| Date: Wed, 1 Sep 2021 04:16:21 +0900 |
| Subject: [PATCH 04/12] udev-node: save information about device node and |
| priority in symlink |
| |
| Previously, we only store device IDs in /run/udev/links, and when |
| creating/removing device node symlink, we create sd_device object |
| corresponds to the IDs and read device node and priority from the |
| object. That requires parsing uevent and udev database files. |
| |
| This makes link_find_prioritized() get the most prioritzed device node |
| without parsing the files. |
| --- |
| src/udev/udev-node.c | 172 ++++++++++++++++++++++++++++++------------- |
| 1 file changed, 121 insertions(+), 51 deletions(-) |
| |
| diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c |
| index 4496a2bd9b..5d6aae0bd4 100644 |
| --- a/src/udev/udev-node.c |
| +++ b/src/udev/udev-node.c |
| @@ -18,6 +18,7 @@ |
| #include "fs-util.h" |
| #include "hexdecoct.h" |
| #include "mkdir.h" |
| +#include "parse-util.h" |
| #include "path-util.h" |
| #include "selinux-util.h" |
| #include "smack-util.h" |
| @@ -28,9 +29,9 @@ |
| #include "udev-node.h" |
| #include "user-util.h" |
| |
| -#define CREATE_LINK_MAX_RETRIES 128 |
| -#define LINK_UPDATE_MAX_RETRIES 128 |
| -#define TOUCH_FILE_MAX_RETRIES 128 |
| +#define CREATE_LINK_MAX_RETRIES 128 |
| +#define LINK_UPDATE_MAX_RETRIES 128 |
| +#define CREATE_STACK_LINK_MAX_RETRIES 128 |
| #define UDEV_NODE_HASH_KEY SD_ID128_MAKE(b9,6a,f1,ce,40,31,44,1a,9e,19,ec,8b,ae,f3,e3,2f) |
| |
| static int create_symlink(const char *target, const char *slink) { |
| @@ -175,39 +176,67 @@ static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir, |
| return r; |
| |
| FOREACH_DIRENT_ALL(dent, dir, break) { |
| - _cleanup_(sd_device_unrefp) sd_device *dev_db = NULL; |
| - const char *devnode; |
| - int db_prio = 0; |
| + _cleanup_free_ char *path = NULL, *buf = NULL; |
| + int tmp_prio; |
| |
| - if (dent->d_name[0] == '\0') |
| - break; |
| if (dent->d_name[0] == '.') |
| continue; |
| |
| - log_device_debug(dev, "Found '%s' claiming '%s'", dent->d_name, stackdir); |
| - |
| - /* did we find ourself? */ |
| + /* skip ourself */ |
| if (streq(dent->d_name, id)) |
| continue; |
| |
| - if (sd_device_new_from_device_id(&dev_db, dent->d_name) < 0) |
| - continue; |
| + path = path_join(stackdir, dent->d_name); |
| + if (!path) |
| + return -ENOMEM; |
| |
| - if (sd_device_get_devname(dev_db, &devnode) < 0) |
| - continue; |
| + if (readlink_malloc(path, &buf) >= 0) { |
| + char *devnode; |
| |
| - if (device_get_devlink_priority(dev_db, &db_prio) < 0) |
| - continue; |
| + /* New format. The devnode and priority can be obtained from symlink. */ |
| |
| - if (target && db_prio <= priority) |
| - continue; |
| + devnode = strchr(buf, ':'); |
| + if (!devnode || devnode == buf) |
| + continue; |
| |
| - log_device_debug(dev_db, "Device claims priority %i for '%s'", db_prio, stackdir); |
| + *(devnode++) = '\0'; |
| + if (!path_startswith(devnode, "/dev")) |
| + continue; |
| |
| - r = free_and_strdup(&target, devnode); |
| - if (r < 0) |
| - return r; |
| - priority = db_prio; |
| + if (safe_atoi(buf, &tmp_prio) < 0) |
| + continue; |
| + |
| + if (target && tmp_prio <= priority) |
| + continue; |
| + |
| + r = free_and_strdup(&target, devnode); |
| + if (r < 0) |
| + return r; |
| + } else { |
| + _cleanup_(sd_device_unrefp) sd_device *tmp_dev = NULL; |
| + const char *devnode; |
| + |
| + /* Old format. The devnode and priority must be obtained from uevent and |
| + * udev database files. */ |
| + |
| + if (sd_device_new_from_device_id(&tmp_dev, dent->d_name) < 0) |
| + continue; |
| + |
| + if (device_get_devlink_priority(tmp_dev, &tmp_prio) < 0) |
| + continue; |
| + |
| + if (target && tmp_prio <= priority) |
| + continue; |
| + |
| + if (sd_device_get_devname(tmp_dev, &devnode) < 0) |
| + continue; |
| + |
| + r = free_and_strdup(&target, devnode); |
| + if (r < 0) |
| + return r; |
| + } |
| + |
| + priority = tmp_prio; |
| } |
| |
| *ret = TAKE_PTR(target); |
| @@ -256,10 +285,72 @@ toolong: |
| return size - 1; |
| } |
| |
| +static int update_stack_directory(sd_device *dev, const char *dirname, bool add) { |
| + _cleanup_free_ char *filename = NULL, *data = NULL, *buf = NULL; |
| + const char *devname, *id; |
| + int priority, r; |
| + |
| + assert(dev); |
| + assert(dirname); |
| + |
| + r = device_get_device_id(dev, &id); |
| + if (r < 0) |
| + return log_device_debug_errno(dev, r, "Failed to get device id: %m"); |
| + |
| + filename = path_join(dirname, id); |
| + if (!filename) |
| + return log_oom_debug(); |
| + |
| + if (!add) { |
| + if (unlink(filename) < 0 && errno != ENOENT) |
| + log_device_debug_errno(dev, errno, "Failed to remove %s, ignoring: %m", filename); |
| + |
| + (void) rmdir(dirname); |
| + return 0; |
| + } |
| + |
| + r = sd_device_get_devname(dev, &devname); |
| + if (r < 0) |
| + return log_device_debug_errno(dev, r, "Failed to get device node: %m"); |
| + |
| + r = device_get_devlink_priority(dev, &priority); |
| + if (r < 0) |
| + return log_device_debug_errno(dev, r, "Failed to get priority of device node symlink: %m"); |
| + |
| + if (asprintf(&data, "%i:%s", priority, devname) < 0) |
| + return log_oom_debug(); |
| + |
| + if (readlink_malloc(filename, &buf) >= 0 && streq(buf, data)) |
| + return 0; |
| + |
| + if (unlink(filename) < 0 && errno != ENOENT) |
| + log_device_debug_errno(dev, errno, "Failed to remove %s, ignoring: %m", filename); |
| + |
| + for (unsigned j = 0; j < CREATE_STACK_LINK_MAX_RETRIES; j++) { |
| + /* This may fail with -ENOENT when the parent directory is removed during |
| + * creating the file by another udevd worker. */ |
| + r = mkdir_p(dirname, 0755); |
| + if (r == -ENOENT) |
| + continue; |
| + if (r < 0) |
| + return log_device_debug_errno(dev, r, "Failed to create directory %s: %m", dirname); |
| + |
| + if (symlink(data, filename) < 0) { |
| + if (errno == ENOENT) |
| + continue; |
| + return log_device_debug_errno(dev, errno, "Failed to create symbolic link %s: %m", filename); |
| + } |
| + |
| + return 0; |
| + } |
| + |
| + return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ELOOP), "Failed to create symbolic link %s: %m", filename); |
| +} |
| + |
| /* manage "stack of names" with possibly specified device priorities */ |
| static int link_update(sd_device *dev, const char *slink_in, bool add) { |
| - _cleanup_free_ char *slink = NULL, *filename = NULL, *dirname = NULL; |
| - const char *slink_name, *id; |
| + _cleanup_free_ char *slink = NULL, *dirname = NULL; |
| + const char *slink_name; |
| char name_enc[NAME_MAX+1]; |
| int i, r, retries; |
| |
| @@ -279,35 +370,14 @@ static int link_update(sd_device *dev, const char *slink_in, bool add) { |
| return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL), |
| "Invalid symbolic link of device node: %s", slink); |
| |
| - r = device_get_device_id(dev, &id); |
| - if (r < 0) |
| - return log_device_debug_errno(dev, r, "Failed to get device id: %m"); |
| - |
| (void) udev_node_escape_path(slink_name, name_enc, sizeof(name_enc)); |
| - dirname = path_join("/run/udev/links/", name_enc); |
| + dirname = path_join("/run/udev/links", name_enc); |
| if (!dirname) |
| return log_oom_debug(); |
| |
| - filename = path_join(dirname, id); |
| - if (!filename) |
| - return log_oom_debug(); |
| - |
| - if (!add) { |
| - if (unlink(filename) < 0 && errno != ENOENT) |
| - log_device_debug_errno(dev, errno, "Failed to remove %s, ignoring: %m", filename); |
| - |
| - (void) rmdir(dirname); |
| - } else { |
| - for (unsigned j = 0; j < TOUCH_FILE_MAX_RETRIES; j++) { |
| - /* This may fail with -ENOENT when the parent directory is removed during |
| - * creating the file by another udevd worker. */ |
| - r = touch_file(filename, /* parents= */ true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0444); |
| - if (r != -ENOENT) |
| - break; |
| - } |
| - if (r < 0) |
| - return log_device_debug_errno(dev, r, "Failed to create %s: %m", filename); |
| - } |
| + r = update_stack_directory(dev, dirname, add); |
| + if (r < 0) |
| + return r; |
| |
| /* If the database entry is not written yet we will just do one iteration and possibly wrong symlink |
| * will be fixed in the second invocation. */ |
| -- |
| 2.36.1.124.g0e6072fb45-goog |
| |
| |
| From 6990c625632d15bf24a79a61d56cf4bc88b6006f Mon Sep 17 00:00:00 2001 |
| From: Yu Watanabe <watanabe.yu+github@gmail.com> |
| Date: Wed, 1 Sep 2021 12:57:40 +0900 |
| Subject: [PATCH 05/12] udev-node: always update timestamp of stack directory |
| |
| Please see the comments in the code. |
| --- |
| src/udev/udev-node.c | 90 ++++++++++++++++++++++++++++++++++++++++++-- |
| 1 file changed, 87 insertions(+), 3 deletions(-) |
| |
| diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c |
| index 5d6aae0bd4..0de848da19 100644 |
| --- a/src/udev/udev-node.c |
| +++ b/src/udev/udev-node.c |
| @@ -32,6 +32,7 @@ |
| #define CREATE_LINK_MAX_RETRIES 128 |
| #define LINK_UPDATE_MAX_RETRIES 128 |
| #define CREATE_STACK_LINK_MAX_RETRIES 128 |
| +#define UPDATE_TIMESTAMP_MAX_RETRIES 128 |
| #define UDEV_NODE_HASH_KEY SD_ID128_MAKE(b9,6a,f1,ce,40,31,44,1a,9e,19,ec,8b,ae,f3,e3,2f) |
| |
| static int create_symlink(const char *target, const char *slink) { |
| @@ -285,9 +286,60 @@ toolong: |
| return size - 1; |
| } |
| |
| +static int update_timestamp(sd_device *dev, const char *path, struct stat *prev) { |
| + assert(path); |
| + assert(prev); |
| + |
| + /* Even if a symlink in the stack directory is created/removed, the mtime of the directory may |
| + * not be changed. Why? Let's consider the following situation. For simplicity, let's assume |
| + * there exist three udev workers (A, B, and C) and all of them calls link_update() for the |
| + * same devlink simultaneously. |
| + * |
| + * 1. B creates/removes a symlink in the stack directory. |
| + * 2. A calls the first stat() in the loop of link_update(). |
| + * 3. A calls link_find_prioritized(). |
| + * 4. C creates/removes another symlink in the stack directory, so the result of the step 3 is outdated. |
| + * 5. B and C finish link_update(). |
| + * 6. A creates/removes devlink according to the outdated result in the step 3. |
| + * 7. A calls the second stat() in the loop of link_update(). |
| + * |
| + * If these 7 steps are processed in this order within a short time period that kernel's timer |
| + * does not increase, then even if the contents in the stack directory is changed, the results |
| + * of two stat() called by A shows the same timestamp, and A cannot detect the change. |
| + * |
| + * By calling this function after creating/removing symlinks in the stack directory, the |
| + * timestamp of the stack directory is always increased at least in the above step 5, so A can |
| + * detect the update. */ |
| + |
| + if ((prev->st_mode & S_IFMT) == 0) |
| + return 0; /* Does not exist, or previous stat() failed. */ |
| + |
| + for (unsigned i = 0; i < UPDATE_TIMESTAMP_MAX_RETRIES; i++) { |
| + struct stat st; |
| + |
| + if (stat(path, &st) < 0) |
| + return -errno; |
| + |
| + if (!stat_inode_unmodified(prev, &st)) |
| + return 0; |
| + |
| + log_device_debug(dev, |
| + "%s is modified, but its timestamp is not changed, " |
| + "updating timestamp after 10ms.", |
| + path); |
| + |
| + (void) usleep(10 * USEC_PER_MSEC); |
| + if (utimensat(AT_FDCWD, path, NULL, 0) < 0) |
| + return -errno; |
| + } |
| + |
| + return -ELOOP; |
| +} |
| + |
| static int update_stack_directory(sd_device *dev, const char *dirname, bool add) { |
| _cleanup_free_ char *filename = NULL, *data = NULL, *buf = NULL; |
| const char *devname, *id; |
| + struct stat st = {}; |
| int priority, r; |
| |
| assert(dev); |
| @@ -302,10 +354,31 @@ static int update_stack_directory(sd_device *dev, const char *dirname, bool add) |
| return log_oom_debug(); |
| |
| if (!add) { |
| - if (unlink(filename) < 0 && errno != ENOENT) |
| - log_device_debug_errno(dev, errno, "Failed to remove %s, ignoring: %m", filename); |
| + bool unlink_failed = false; |
| + |
| + if (stat(dirname, &st) < 0) { |
| + if (errno == ENOENT) |
| + return 0; /* The stack directory is already removed. That's OK. */ |
| + log_device_debug_errno(dev, errno, "Failed to stat %s, ignoring: %m", dirname); |
| + } |
| + |
| + if (unlink(filename) < 0) { |
| + unlink_failed = true; |
| + if (errno != ENOENT) |
| + log_device_debug_errno(dev, errno, "Failed to remove %s, ignoring: %m", filename); |
| + } |
| + |
| + if (rmdir(dirname) >= 0 || errno == ENOENT) |
| + return 0; |
| + |
| + if (unlink_failed) |
| + return 0; /* If we failed to remove the symlink, there is almost nothing we can do. */ |
| + |
| + /* The symlink was removed. Check if the timestamp of directory is changed. */ |
| + r = update_timestamp(dev, dirname, &st); |
| + if (r < 0 && r != -ENOENT) |
| + return log_device_debug_errno(dev, r, "Failed to update timestamp of %s: %m", dirname); |
| |
| - (void) rmdir(dirname); |
| return 0; |
| } |
| |
| @@ -335,12 +408,23 @@ static int update_stack_directory(sd_device *dev, const char *dirname, bool add) |
| if (r < 0) |
| return log_device_debug_errno(dev, r, "Failed to create directory %s: %m", dirname); |
| |
| + if (stat(dirname, &st) < 0) { |
| + if (errno == ENOENT) |
| + continue; |
| + return log_device_debug_errno(dev, errno, "Failed to stat %s: %m", dirname); |
| + } |
| + |
| if (symlink(data, filename) < 0) { |
| if (errno == ENOENT) |
| continue; |
| return log_device_debug_errno(dev, errno, "Failed to create symbolic link %s: %m", filename); |
| } |
| |
| + /* The symlink was created. Check if the timestamp of directory is changed. */ |
| + r = update_timestamp(dev, dirname, &st); |
| + if (r < 0) |
| + return log_device_debug_errno(dev, r, "Failed to update timestamp of %s: %m", dirname); |
| + |
| return 0; |
| } |
| |
| -- |
| 2.36.1.124.g0e6072fb45-goog |
| |
| |
| From 10d7b9e3d9cf8516f688c80b261053d5190a9a5a Mon Sep 17 00:00:00 2001 |
| From: Yu Watanabe <watanabe.yu+github@gmail.com> |
| Date: Thu, 2 Sep 2021 06:58:59 +0900 |
| Subject: [PATCH 06/12] udev-node: assume no new claim to a symlink if |
| /run/udev/links is not updated |
| |
| During creating a symlink to a device node, if another device node which |
| requests the same symlink is added/removed, `stat_inode_unmodified()` |
| should always detects that. We do not need to continue the loop |
| unconditionally. |
| --- |
| src/udev/udev-node.c | 5 ----- |
| 1 file changed, 5 deletions(-) |
| |
| diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c |
| index 0de848da19..1a34ea8128 100644 |
| --- a/src/udev/udev-node.c |
| +++ b/src/udev/udev-node.c |
| @@ -491,11 +491,6 @@ static int link_update(sd_device *dev, const char *slink_in, bool add) { |
| r = node_symlink(dev, target, slink); |
| if (r < 0) |
| return r; |
| - if (r == 1) |
| - /* We have replaced already existing symlink, possibly there is some other device trying |
| - * to claim the same symlink. Let's do one more iteration to give us a chance to fix |
| - * the error if other device actually claims the symlink with higher priority. */ |
| - continue; |
| |
| /* Skip the second stat() if the first failed, stat_inode_unmodified() would return false regardless. */ |
| if ((st1.st_mode & S_IFMT) != 0) { |
| -- |
| 2.36.1.124.g0e6072fb45-goog |
| |
| |
| From c5f67d4bec3eebbb4226c1895dc29c5c65289b59 Mon Sep 17 00:00:00 2001 |
| From: Yu Watanabe <watanabe.yu+github@gmail.com> |
| Date: Wed, 1 Sep 2021 02:20:33 +0900 |
| Subject: [PATCH 07/12] udev-node: always atomically create symlink to device |
| node |
| |
| By the previous commit, it is not necessary to distinguish if the devlink |
| already exists. Also, I cannot find any significant advantages of the |
| previous complecated logic, that is, first try to create directly, and then |
| fallback to atomically creation. Moreover, such logic increases the chance |
| of conflicts between multiple udev workers. |
| |
| This makes devlinks always created atomically. Hopefully, this reduces the |
| conflicts between the workers. |
| --- |
| src/udev/udev-node.c | 42 +++++++++--------------------------------- |
| 1 file changed, 9 insertions(+), 33 deletions(-) |
| |
| diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c |
| index 1a34ea8128..46c04fe00b 100644 |
| --- a/src/udev/udev-node.c |
| +++ b/src/udev/udev-node.c |
| @@ -71,6 +71,13 @@ static int node_symlink(sd_device *dev, const char *node, const char *slink) { |
| assert(node); |
| assert(slink); |
| |
| + if (lstat(slink, &stats) >= 0) { |
| + if (!S_ISLNK(stats.st_mode)) |
| + return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EEXIST), |
| + "Conflicting inode '%s' found, link to '%s' will not be created.", slink, node); |
| + } else if (errno != ENOENT) |
| + return log_device_debug_errno(dev, errno, "Failed to lstat() '%s': %m", slink); |
| + |
| r = path_extract_directory(slink, &slink_dirname); |
| if (r < 0) |
| return log_device_debug_errno(dev, r, "Failed to get parent directory of '%s': %m", slink); |
| @@ -80,41 +87,11 @@ static int node_symlink(sd_device *dev, const char *node, const char *slink) { |
| if (r < 0) |
| return log_device_debug_errno(dev, r, "Failed to get relative path from '%s' to '%s': %m", slink, node); |
| |
| - if (lstat(slink, &stats) >= 0) { |
| - _cleanup_free_ char *buf = NULL; |
| - |
| - if (!S_ISLNK(stats.st_mode)) |
| - return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EEXIST), |
| - "Conflicting inode '%s' found, link to '%s' will not be created.", slink, node); |
| - |
| - if (readlink_malloc(slink, &buf) >= 0 && |
| - path_equal(target, buf)) { |
| - /* preserve link with correct target, do not replace node of other device */ |
| - log_device_debug(dev, "Preserve already existing symlink '%s' to '%s'", slink, target); |
| - |
| - (void) label_fix(slink, LABEL_IGNORE_ENOENT); |
| - (void) utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW); |
| - |
| - return 0; |
| - } |
| - } else if (errno == ENOENT) { |
| - log_device_debug(dev, "Creating symlink '%s' to '%s'", slink, target); |
| - |
| - r = create_symlink(target, slink); |
| - if (r >= 0) |
| - return 0; |
| - |
| - log_device_debug_errno(dev, r, "Failed to create symlink '%s' to '%s', trying to replace '%s': %m", slink, target, slink); |
| - } else |
| - return log_device_debug_errno(dev, errno, "Failed to lstat() '%s': %m", slink); |
| - |
| - log_device_debug(dev, "Atomically replace '%s'", slink); |
| - |
| r = device_get_device_id(dev, &id); |
| if (r < 0) |
| return log_device_debug_errno(dev, r, "Failed to get device id: %m"); |
| - slink_tmp = strjoina(slink, ".tmp-", id); |
| |
| + slink_tmp = strjoina(slink, ".tmp-", id); |
| (void) unlink(slink_tmp); |
| |
| r = create_symlink(target, slink_tmp); |
| @@ -127,8 +104,7 @@ static int node_symlink(sd_device *dev, const char *node, const char *slink) { |
| return r; |
| } |
| |
| - /* Tell caller that we replaced already existing symlink. */ |
| - return 1; |
| + return 0; |
| } |
| |
| static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir, char **ret) { |
| -- |
| 2.36.1.124.g0e6072fb45-goog |
| |
| |
| From edd2eaeaa77cbc6cc1a8a43e27caab1c06dc3689 Mon Sep 17 00:00:00 2001 |
| From: Yu Watanabe <watanabe.yu+github@gmail.com> |
| Date: Wed, 1 Sep 2021 09:44:26 +0900 |
| Subject: [PATCH 08/12] udev-node: check stack directory change even if devlink |
| is removed |
| |
| Otherwise, when multiple device additions and removals occur |
| simultaneously, symlink to unexisting devnode may be created. |
| |
| Hopefully fixes #19946. |
| --- |
| src/udev/udev-node.c | 15 ++++++--------- |
| 1 file changed, 6 insertions(+), 9 deletions(-) |
| |
| diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c |
| index 46c04fe00b..28e6e8df94 100644 |
| --- a/src/udev/udev-node.c |
| +++ b/src/udev/udev-node.c |
| @@ -468,15 +468,12 @@ static int link_update(sd_device *dev, const char *slink_in, bool add) { |
| if (r < 0) |
| return r; |
| |
| - /* Skip the second stat() if the first failed, stat_inode_unmodified() would return false regardless. */ |
| - if ((st1.st_mode & S_IFMT) != 0) { |
| - r = stat(dirname, &st2); |
| - if (r < 0 && errno != ENOENT) |
| - return log_device_debug_errno(dev, errno, "Failed to stat %s: %m", dirname); |
| - |
| - if (stat_inode_unmodified(&st1, &st2)) |
| - break; |
| - } |
| + if (stat(dirname, &st2) < 0 && errno != ENOENT) |
| + return log_device_debug_errno(dev, errno, "Failed to stat %s: %m", dirname); |
| + |
| + if (((st1.st_mode & S_IFMT) == 0 && (st2.st_mode & S_IFMT) == 0) || |
| + stat_inode_unmodified(&st1, &st2)) |
| + return 0; |
| } |
| |
| return i < LINK_UPDATE_MAX_RETRIES ? 0 : -ELOOP; |
| -- |
| 2.36.1.124.g0e6072fb45-goog |
| |
| |
| From 6bddf24f5557d2ee1711696580c33e5161a479ae Mon Sep 17 00:00:00 2001 |
| From: Yu Watanabe <watanabe.yu+github@gmail.com> |
| Date: Thu, 2 Sep 2021 08:23:35 +0900 |
| Subject: [PATCH 09/12] udev-node: shorten code a bit and update log message |
| |
| --- |
| src/udev/udev-node.c | 5 ++--- |
| 1 file changed, 2 insertions(+), 3 deletions(-) |
| |
| diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c |
| index 28e6e8df94..2e7df899e4 100644 |
| --- a/src/udev/udev-node.c |
| +++ b/src/udev/udev-node.c |
| @@ -447,13 +447,12 @@ static int link_update(sd_device *dev, const char *slink_in, bool add) { |
| _cleanup_free_ char *target = NULL; |
| struct stat st1 = {}, st2 = {}; |
| |
| - r = stat(dirname, &st1); |
| - if (r < 0 && errno != ENOENT) |
| + if (stat(dirname, &st1) < 0 && errno != ENOENT) |
| return log_device_debug_errno(dev, errno, "Failed to stat %s: %m", dirname); |
| |
| r = link_find_prioritized(dev, add, dirname, &target); |
| if (r < 0) |
| - return log_device_debug_errno(dev, r, "Failed to determine highest priority for symlink '%s': %m", slink); |
| + return log_device_debug_errno(dev, r, "Failed to determine device node with the highest priority for '%s': %m", slink); |
| if (r == 0) { |
| log_device_debug(dev, "No reference left for '%s', removing", slink); |
| |
| -- |
| 2.36.1.124.g0e6072fb45-goog |
| |
| |
| From 7830747a73f224c257cdb93287793a6ba482d550 Mon Sep 17 00:00:00 2001 |
| From: Yu Watanabe <watanabe.yu+github@gmail.com> |
| Date: Wed, 1 Sep 2021 04:34:48 +0900 |
| Subject: [PATCH 10/12] udev-node: add random delay on conflict in updating |
| device node symlink |
| |
| To make multiple workers not update the same device node symlink |
| simultaneously. |
| --- |
| src/udev/udev-node.c | 12 ++++++++++++ |
| 1 file changed, 12 insertions(+) |
| |
| diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c |
| index 2e7df899e4..675e6ce313 100644 |
| --- a/src/udev/udev-node.c |
| +++ b/src/udev/udev-node.c |
| @@ -20,12 +20,14 @@ |
| #include "mkdir.h" |
| #include "parse-util.h" |
| #include "path-util.h" |
| +#include "random-util.h" |
| #include "selinux-util.h" |
| #include "smack-util.h" |
| #include "stat-util.h" |
| #include "stdio-util.h" |
| #include "string-util.h" |
| #include "strxcpyx.h" |
| +#include "time-util.h" |
| #include "udev-node.h" |
| #include "user-util.h" |
| |
| @@ -33,6 +35,8 @@ |
| #define LINK_UPDATE_MAX_RETRIES 128 |
| #define CREATE_STACK_LINK_MAX_RETRIES 128 |
| #define UPDATE_TIMESTAMP_MAX_RETRIES 128 |
| +#define MAX_RANDOM_DELAY (250 * USEC_PER_MSEC) |
| +#define MIN_RANDOM_DELAY ( 50 * USEC_PER_MSEC) |
| #define UDEV_NODE_HASH_KEY SD_ID128_MAKE(b9,6a,f1,ce,40,31,44,1a,9e,19,ec,8b,ae,f3,e3,2f) |
| |
| static int create_symlink(const char *target, const char *slink) { |
| @@ -447,6 +451,14 @@ static int link_update(sd_device *dev, const char *slink_in, bool add) { |
| _cleanup_free_ char *target = NULL; |
| struct stat st1 = {}, st2 = {}; |
| |
| + if (i > 0) { |
| + usec_t delay = MIN_RANDOM_DELAY + random_u64_range(MAX_RANDOM_DELAY - MIN_RANDOM_DELAY); |
| + |
| + log_device_debug(dev, "Directory %s was updated, retrying to update devlink %s after %s.", |
| + dirname, slink, FORMAT_TIMESPAN(delay, USEC_PER_MSEC)); |
| + (void) usleep(delay); |
| + } |
| + |
| if (stat(dirname, &st1) < 0 && errno != ENOENT) |
| return log_device_debug_errno(dev, errno, "Failed to stat %s: %m", dirname); |
| |
| -- |
| 2.36.1.124.g0e6072fb45-goog |
| |
| |
| From 2a1956d95029b831de9c4d53bea11e90c898bdef Mon Sep 17 00:00:00 2001 |
| From: Yu Watanabe <watanabe.yu+github@gmail.com> |
| Date: Wed, 1 Sep 2021 09:29:42 +0900 |
| Subject: [PATCH 11/12] udev-node: drop redundant trial of devlink creation |
| |
| Previously, the devlink was created based on the priority saved in udev |
| database. So, we needed to reevaluate devlinks after database is saved. |
| |
| But now the priority is stored in the symlink under /run/udev/links, and |
| the loop of devlink creation is controlled with the timestamp of the |
| directory. So, the double evaluation is not necessary anymore. |
| --- |
| src/udev/udev-event.c | 5 +---- |
| src/udev/udev-node.c | 12 ++++-------- |
| 2 files changed, 5 insertions(+), 12 deletions(-) |
| |
| diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c |
| index 8b9f8aecfe..c77f55c67e 100644 |
| --- a/src/udev/udev-event.c |
| +++ b/src/udev/udev-event.c |
| @@ -1060,10 +1060,7 @@ int udev_event_execute_rules( |
| |
| device_set_is_initialized(dev); |
| |
| - /* Yes, we run update_devnode() twice, because in the first invocation, that is before update of udev database, |
| - * it could happen that two contenders are replacing each other's symlink. Hence we run it again to make sure |
| - * symlinks point to devices that claim them with the highest priority. */ |
| - return update_devnode(event); |
| + return 0; |
| } |
| |
| void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec, int timeout_signal) { |
| diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c |
| index 675e6ce313..bb551d86b0 100644 |
| --- a/src/udev/udev-node.c |
| +++ b/src/udev/udev-node.c |
| @@ -416,7 +416,7 @@ static int link_update(sd_device *dev, const char *slink_in, bool add) { |
| _cleanup_free_ char *slink = NULL, *dirname = NULL; |
| const char *slink_name; |
| char name_enc[NAME_MAX+1]; |
| - int i, r, retries; |
| + int r; |
| |
| assert(dev); |
| assert(slink_in); |
| @@ -443,11 +443,7 @@ static int link_update(sd_device *dev, const char *slink_in, bool add) { |
| if (r < 0) |
| return r; |
| |
| - /* If the database entry is not written yet we will just do one iteration and possibly wrong symlink |
| - * will be fixed in the second invocation. */ |
| - retries = sd_device_get_is_initialized(dev) > 0 ? LINK_UPDATE_MAX_RETRIES : 1; |
| - |
| - for (i = 0; i < retries; i++) { |
| + for (unsigned i = 0; i < LINK_UPDATE_MAX_RETRIES; i++) { |
| _cleanup_free_ char *target = NULL; |
| struct stat st1 = {}, st2 = {}; |
| |
| @@ -472,7 +468,7 @@ static int link_update(sd_device *dev, const char *slink_in, bool add) { |
| log_device_debug_errno(dev, errno, "Failed to remove '%s', ignoring: %m", slink); |
| |
| (void) rmdir_parents(slink, "/dev"); |
| - break; |
| + return 0; |
| } |
| |
| r = node_symlink(dev, target, slink); |
| @@ -487,7 +483,7 @@ static int link_update(sd_device *dev, const char *slink_in, bool add) { |
| return 0; |
| } |
| |
| - return i < LINK_UPDATE_MAX_RETRIES ? 0 : -ELOOP; |
| + return -ELOOP; |
| } |
| |
| static int device_get_devpath_by_devnum(sd_device *dev, char **ret) { |
| -- |
| 2.36.1.124.g0e6072fb45-goog |
| |
| |
| From 24d250ff8cb6f66c72af7e22749cd2c05d13aeaa Mon Sep 17 00:00:00 2001 |
| From: royyang <royyang@google.com> |
| Date: Mon, 23 May 2022 14:12:18 -0700 |
| Subject: [PATCH 12/12] Add Micros |
| |
| --- |
| src/basic/time-util.h | 1 + |
| 1 file changed, 1 insertion(+) |
| |
| diff --git a/src/basic/time-util.h b/src/basic/time-util.h |
| index 2bd947d6a8..7d1bb98b06 100644 |
| --- a/src/basic/time-util.h |
| +++ b/src/basic/time-util.h |
| @@ -72,6 +72,7 @@ typedef enum TimestampStyle { |
| |
| #define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) {}) |
| #define TRIPLE_TIMESTAMP_NULL ((struct triple_timestamp) {}) |
| +#define FORMAT_TIMESPAN(t, accuracy) format_timespan((char[FORMAT_TIMESPAN_MAX]){}, FORMAT_TIMESPAN_MAX, t, accuracy) |
| |
| usec_t now(clockid_t clock); |
| nsec_t now_nsec(clockid_t clock); |
| -- |
| 2.36.1.124.g0e6072fb45-goog |
| |