]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev: cleanup stack directory /run/udev/links when all workers exited 23043/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 11 Apr 2022 12:52:49 +0000 (21:52 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 2 Sep 2022 20:01:52 +0000 (05:01 +0900)
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.

src/udev/udev-node.c
src/udev/udev-node.h
src/udev/udevd.c

index 42efaaa028876abc25ea0f1458af60e7b1e4b6e2..4e7dca06de0d86819c7edf4ad9f8237ffc95e497 100644 (file)
 
 #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;
index 86a829545a533138087cb9f2f3e14d69612f6fab..0c545e4a0f7b995124f2df3580965dbca12ab6c4 100644 (file)
@@ -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);
index b179dfac3702fd6dd6ce72fcde336a676673fdad..c519e19897acfc2277f89c297b14f8f823df2679 100644 (file)
@@ -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);