]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev: save stats of all udev rules file
authorYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 13 Jul 2022 12:58:15 +0000 (21:58 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 23 Jul 2022 06:05:22 +0000 (15:05 +0900)
The mtime of directory is not updated when an existing rule file is
changed. Hence, paths_check_timestamp() is not reliable.

src/shared/conf-parser.c
src/shared/conf-parser.h
src/udev/udev-rules.c
src/udev/udev-rules.h
src/udev/udevd.c

index 9a888eec219b86d6ffd49c5df248597c3621ebca..765ccec5016d765a2cb9c928e58ffcfc8af0a703 100644 (file)
@@ -445,7 +445,7 @@ int config_parse(
         return 1;
 }
 
-static int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const struct stat *st) {
+int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const struct stat *st) {
         _cleanup_free_ struct stat *st_copy = NULL;
         _cleanup_free_ char *path_copy = NULL;
         int r;
index 15616adf3b5e2f16e6009e105f7bd2c3c7ad4db9..bd57575af127c613efeb763a8b39870a73e8f633 100644 (file)
@@ -122,6 +122,7 @@ int config_get_stats_by_path(
                 bool check_dropins,
                 Hashmap **ret);
 
+int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const struct stat *st);
 bool stats_by_path_equal(Hashmap *a, Hashmap *b);
 
 typedef struct ConfigSection {
index b977338c385cba8dfa5878b1097042019ccace6c..eaa3578489bc2ba6e83cd52ac0e8029dc91d447f 100644 (file)
@@ -5,6 +5,7 @@
 #include "alloc-util.h"
 #include "architecture.h"
 #include "conf-files.h"
+#include "conf-parser.h"
 #include "def.h"
 #include "device-private.h"
 #include "device-util.h"
@@ -177,10 +178,10 @@ struct UdevRuleFile {
 };
 
 struct UdevRules {
-        usec_t dirs_ts_usec;
         ResolveNameTiming resolve_name_timing;
         Hashmap *known_users;
         Hashmap *known_groups;
+        Hashmap *stats_by_path;
         UdevRuleFile *current_file;
         LIST_HEAD(UdevRuleFile, rule_files);
 };
@@ -315,6 +316,7 @@ UdevRules *udev_rules_free(UdevRules *rules) {
 
         hashmap_free_free_key(rules->known_users);
         hashmap_free_free_key(rules->known_groups);
+        hashmap_free(rules->stats_by_path);
         return mfree(rules);
 }
 
@@ -1176,6 +1178,7 @@ int udev_rules_parse_file(UdevRules *rules, const char *filename) {
         UdevRuleFile *rule_file;
         bool ignore_line = false;
         unsigned line_nr = 0;
+        struct stat st;
         int r;
 
         f = fopen(filename, "re");
@@ -1183,16 +1186,23 @@ int udev_rules_parse_file(UdevRules *rules, const char *filename) {
                 if (errno == ENOENT)
                         return 0;
 
-                return -errno;
+                return log_warning_errno(errno, "Failed to open %s, ignoring: %m", filename);
         }
 
-        (void) fd_warn_permissions(filename, fileno(f));
+        if (fstat(fileno(f), &st) < 0)
+                return log_warning_errno(errno, "Failed to stat %s, ignoring: %m", filename);
 
-        if (null_or_empty_fd(fileno(f))) {
+        if (null_or_empty(&st)) {
                 log_debug("Skipping empty file: %s", filename);
                 return 0;
         }
 
+        r = hashmap_put_stats_by_path(&rules->stats_by_path, filename, &st);
+        if (r < 0)
+                return log_warning_errno(errno, "Failed to save stat for %s, ignoring: %m", filename);
+
+        (void) fd_warn_permissions(filename, fileno(f));
+
         log_debug("Reading rules file: %s", filename);
 
         name = strdup(filename);
@@ -1296,8 +1306,6 @@ int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing
         if (!rules)
                 return -ENOMEM;
 
-        (void) udev_rules_check_timestamp(rules);
-
         r = conf_files_list_strv(&files, ".rules", NULL, 0, RULES_DIRS);
         if (r < 0)
                 return log_debug_errno(r, "Failed to enumerate rules files: %m");
@@ -1312,11 +1320,25 @@ int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing
         return 0;
 }
 
-bool udev_rules_check_timestamp(UdevRules *rules) {
+bool udev_rules_should_reload(UdevRules *rules) {
+        _cleanup_hashmap_free_ Hashmap *stats_by_path = NULL;
+        int r;
+
         if (!rules)
-                return false;
+                return true;
+
+        r = config_get_stats_by_path(".rules", NULL, 0, RULES_DIRS, /* check_dropins = */ false, &stats_by_path);
+        if (r < 0) {
+                log_warning_errno(r, "Failed to get stats of udev rules, ignoring: %m");
+                return true;
+        }
+
+        if (!stats_by_path_equal(rules->stats_by_path, stats_by_path)) {
+                log_debug("Udev rules need reloading");
+                return true;
+        }
 
-        return paths_check_timestamp(RULES_DIRS, &rules->dirs_ts_usec, true);
+        return false;
 }
 
 static bool token_match_string(UdevRuleToken *token, const char *str) {
index d11297da857c73e82954e50cd85241b0d9f2bcf1..704f690b676ef11119ae269d3df8b22b69705ce1 100644 (file)
@@ -22,7 +22,7 @@ int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing
 UdevRules *udev_rules_free(UdevRules *rules);
 DEFINE_TRIVIAL_CLEANUP_FUNC(UdevRules*, udev_rules_free);
 
-bool udev_rules_check_timestamp(UdevRules *rules);
+bool udev_rules_should_reload(UdevRules *rules);
 int udev_rules_apply_to_event(UdevRules *rules, UdevEvent *event,
                               usec_t timeout_usec,
                               int timeout_signal,
index d154cf4b73f8c15506a500a42c03861e1fa89b2d..a457ebcc9a3b182e2a19a256e5f4599bc36e4c77 100644 (file)
@@ -949,7 +949,7 @@ static int event_queue_start(Manager *manager) {
         /* check for changed config, every 3 seconds at most */
         if (manager->last_usec == 0 ||
             usec > usec_add(manager->last_usec, 3 * USEC_PER_SEC)) {
-                if (udev_rules_check_timestamp(manager->rules) ||
+                if (udev_rules_should_reload(manager->rules) ||
                     udev_builtin_validate())
                         manager_reload(manager);