From: Yu Watanabe Date: Mon, 11 Apr 2022 12:52:49 +0000 (+0900) Subject: udev: cleanup stack directory /run/udev/links when all workers exited X-Git-Tag: v252-rc1~207^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1055172804e660df2e1c498dafa998ffd65e0c88;p=thirdparty%2Fsystemd.git udev: cleanup stack directory /run/udev/links when all workers exited By the previous commit, the stack directories are not removed even if it is empty. To reduce the inode usage of /run, let's cleanup the directories. --- diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c index 42efaaa0288..4e7dca06de0 100644 --- a/src/udev/udev-node.c +++ b/src/udev/udev-node.c @@ -26,6 +26,48 @@ #define UDEV_NODE_HASH_KEY SD_ID128_MAKE(b9,6a,f1,ce,40,31,44,1a,9e,19,ec,8b,ae,f3,e3,2f) +int udev_node_cleanup(void) { + _cleanup_closedir_ DIR *dir = NULL; + + /* This must not be called when any workers exist. It would cause a race between mkdir() called + * by stack_directory_lock() and unlinkat() called by this. */ + + dir = opendir("/run/udev/links"); + if (!dir) { + if (errno == ENOENT) + return 0; + + return log_debug_errno(errno, "Failed to open directory '/run/udev/links', ignoring: %m"); + } + + FOREACH_DIRENT_ALL(de, dir, break) { + _cleanup_free_ char *lockfile = NULL; + + if (de->d_name[0] == '.') + continue; + + if (de->d_type != DT_DIR) + continue; + + /* As commented in the above, this is called when no worker exists, hence the file is not + * locked. On a later uevent, the lock file will be created if necessary. So, we can safely + * remove the file now. */ + lockfile = path_join(de->d_name, ".lock"); + if (!lockfile) + return log_oom_debug(); + + if (unlinkat(dirfd(dir), lockfile, 0) < 0 && errno != ENOENT) { + log_debug_errno(errno, "Failed to remove '/run/udev/links/%s', ignoring: %m", lockfile); + continue; + } + + if (unlinkat(dirfd(dir), de->d_name, AT_REMOVEDIR) < 0 && errno != ENOTEMPTY) + log_debug_errno(errno, "Failed to remove '/run/udev/links/%s', ignoring: %m", de->d_name); + } + + return 0; +} + static int node_symlink(sd_device *dev, const char *devnode, const char *slink) { _cleanup_free_ char *target = NULL; const char *id, *slink_tmp; diff --git a/src/udev/udev-node.h b/src/udev/udev-node.h index 86a829545a5..0c545e4a0f7 100644 --- a/src/udev/udev-node.h +++ b/src/udev/udev-node.h @@ -24,5 +24,6 @@ int static_node_apply_permissions( int udev_node_remove(sd_device *dev); int udev_node_update(sd_device *dev, sd_device *dev_old); +int udev_node_cleanup(void); size_t udev_node_escape_path(const char *src, char *dest, size_t size); diff --git a/src/udev/udevd.c b/src/udev/udevd.c index b179dfac370..c519e19897a 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -65,6 +65,7 @@ #include "udev-builtin.h" #include "udev-ctrl.h" #include "udev-event.h" +#include "udev-node.h" #include "udev-util.h" #include "udev-watch.h" #include "user-util.h" @@ -111,6 +112,7 @@ typedef struct Manager { usec_t last_usec; + bool udev_node_needs_cleanup; bool stop_exec_queue; bool exit; } Manager; @@ -976,6 +978,9 @@ static int event_queue_start(Manager *manager) { if (!manager->events || manager->exit || manager->stop_exec_queue) return 0; + /* To make the stack directory /run/udev/links cleaned up later. */ + manager->udev_node_needs_cleanup = true; + r = event_source_disable(manager->kill_workers_event); if (r < 0) log_warning_errno(r, "Failed to disable event source for cleaning up idle workers, ignoring: %m"); @@ -1607,6 +1612,11 @@ static int on_post(sd_event_source *s, void *userdata) { /* There are no idle workers. */ + if (manager->udev_node_needs_cleanup) { + (void) udev_node_cleanup(); + manager->udev_node_needs_cleanup = false; + } + if (manager->exit) return sd_event_exit(manager->event, 0);