]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
conf-parse: make config_parse_many() optionally save 'struct stat' for each file
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 25 Oct 2021 02:13:27 +0000 (11:13 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 27 Oct 2021 10:49:55 +0000 (19:49 +0900)
Fixes #21113.

src/core/load-dropin.c
src/network/networkd-network.c
src/network/networkd-network.h
src/shared/conf-parser.c
src/shared/conf-parser.h

index 3bb48564ccdfd642cf0dcc6aac6c28cb8abcd349..080a63bc7e733a26426170938a5b401ddfe69411 100644 (file)
@@ -113,14 +113,16 @@ int unit_load_dropin(Unit *u) {
         }
 
         u->dropin_mtime = 0;
-        STRV_FOREACH(f, u->dropin_paths)
-                (void) config_parse(
-                                u->id, *f, NULL,
-                                UNIT_VTABLE(u)->sections,
-                                config_item_perf_lookup, load_fragment_gperf_lookup,
-                                0,
-                                u,
-                                &u->dropin_mtime);
+        STRV_FOREACH(f, u->dropin_paths) {
+                struct stat st;
+
+                r = config_parse(u->id, *f, NULL,
+                                 UNIT_VTABLE(u)->sections,
+                                 config_item_perf_lookup, load_fragment_gperf_lookup,
+                                 0, u, &st);
+                if (r > 0)
+                        u->dropin_mtime = MAX(u->dropin_mtime, timespec_load(&st.st_mtim));
+        }
 
         return 0;
 }
index 5469cb4560bb9a72169a392f262d2c1d6bb157df..32c03ce47b80bd89aa5456679788747009a779a1 100644 (file)
@@ -532,7 +532,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                         config_item_perf_lookup, network_network_gperf_lookup,
                         CONFIG_PARSE_WARN,
                         network,
-                        &network->timestamp);
+                        &network->stats_by_path);
         if (r < 0)
                 return r;
 
@@ -582,6 +582,28 @@ int network_load(Manager *manager, OrderedHashmap **networks) {
         return 0;
 }
 
+static bool stats_by_path_equal(Hashmap *a, Hashmap *b) {
+        struct stat *st_a, *st_b;
+        const char *path;
+
+        assert(a);
+        assert(b);
+
+        if (hashmap_size(a) != hashmap_size(b))
+                return false;
+
+        HASHMAP_FOREACH_KEY(st_a, path, a) {
+                st_b = hashmap_get(b, path);
+                if (!st_b)
+                        return false;
+
+                if (!stat_inode_unmodified(st_a, st_b))
+                        return false;
+        }
+
+        return true;
+}
+
 int network_reload(Manager *manager) {
         OrderedHashmap *new_networks = NULL;
         Network *n, *old;
@@ -595,14 +617,15 @@ int network_reload(Manager *manager) {
 
         ORDERED_HASHMAP_FOREACH(n, new_networks) {
                 r = network_get_by_name(manager, n->name, &old);
-                if (r < 0)
-                        continue; /* The .network file is new. */
-
-                if (n->timestamp != old->timestamp)
-                        continue; /* The .network file is modified. */
+                if (r < 0) {
+                        log_debug("Found new .network file: %s", n->filename);
+                        continue;
+                }
 
-                if (!streq(n->filename, old->filename))
+                if (!stats_by_path_equal(n->stats_by_path, old->stats_by_path)) {
+                        log_debug("Found updated .network file: %s", n->filename);
                         continue;
+                }
 
                 r = ordered_hashmap_replace(new_networks, old->name, old);
                 if (r < 0)
@@ -628,6 +651,7 @@ static Network *network_free(Network *network) {
                 return NULL;
 
         free(network->filename);
+        hashmap_free(network->stats_by_path);
 
         net_match_clear(&network->match);
         condition_free_list(network->conditions);
index d10119f58cf9c3252c24f481a771d8e5b05d47a4..91553265da2d6b051546848724fe9e8367cf1aa9 100644 (file)
@@ -72,7 +72,7 @@ struct Network {
 
         char *name;
         char *filename;
-        usec_t timestamp;
+        Hashmap *stats_by_path;
         char *description;
 
         /* [Match] section */
index d0ac1b26601eba9e0a03e014cbfe7a60ed17e9ad..9a367d757f76b5cc0b8b2bedd6fb768c5f3a6fbf 100644 (file)
@@ -264,21 +264,18 @@ int config_parse(
                 const void *table,
                 ConfigParseFlags flags,
                 void *userdata,
-                usec_t *latest_mtime) {
+                struct stat *ret_stat) {
 
         _cleanup_free_ char *section = NULL, *continuation = NULL;
         _cleanup_fclose_ FILE *ours = NULL;
         unsigned line = 0, section_line = 0;
         bool section_ignored = false, bom_seen = false;
+        struct stat st;
         int r, fd;
-        usec_t mtime;
 
         assert(filename);
         assert(lookup);
 
-        /* latest_mtime is an input-output parameter: it will be updated if the mtime of the file we're
-         * looking at is later than the current *latest_mtime value. */
-
         if (!f) {
                 f = ours = fopen(filename, "re");
                 if (!f) {
@@ -287,22 +284,28 @@ int config_parse(
                         if ((flags & CONFIG_PARSE_WARN) || errno == ENOENT)
                                 log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
                                                "Failed to open configuration file '%s': %m", filename);
-                        return errno == ENOENT ? 0 : -errno;
+
+                        if (errno == ENOENT) {
+                                if (ret_stat)
+                                        *ret_stat = (struct stat) {};
+
+                                return 0;
+                        }
+
+                        return -errno;
                 }
         }
 
         fd = fileno(f);
         if (fd >= 0) { /* stream might not have an fd, let's be careful hence */
-                struct stat st;
 
                 if (fstat(fd, &st) < 0)
                         return log_full_errno(FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_ERR : LOG_DEBUG, errno,
                                               "Failed to fstat(%s): %m", filename);
 
                 (void) stat_warn_permissions(filename, &st);
-                mtime = timespec_load(&st.st_mtim);
         } else
-                mtime = 0;
+                st = (struct stat) {};
 
         for (;;) {
                 _cleanup_free_ char *buf = NULL;
@@ -422,12 +425,43 @@ int config_parse(
                 }
         }
 
-        if (latest_mtime)
-                *latest_mtime = MAX(*latest_mtime, mtime);
+        if (ret_stat)
+                *ret_stat = st;
 
         return 1;
 }
 
+static 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;
+
+        assert(stats_by_path);
+        assert(path);
+        assert(st);
+
+        r = hashmap_ensure_allocated(stats_by_path, &path_hash_ops_free_free);
+        if (r < 0)
+                return r;
+
+        st_copy = newdup(struct stat, st, 1);
+        if (!st_copy)
+                return -ENOMEM;
+
+        path_copy = strdup(path);
+        if (!path)
+                return -ENOMEM;
+
+        r = hashmap_put(*stats_by_path, path_copy, st_copy);
+        if (r < 0)
+                return r;
+
+        assert(r > 0);
+        TAKE_PTR(path_copy);
+        TAKE_PTR(st_copy);
+        return 0;
+}
+
 static int config_parse_many_files(
                 const char* const* conf_files,
                 char **files,
@@ -436,30 +470,53 @@ static int config_parse_many_files(
                 const void *table,
                 ConfigParseFlags flags,
                 void *userdata,
-                usec_t *ret_mtime) {
+                Hashmap **ret_stats_by_path) {
 
-        usec_t mtime = 0;
+        _cleanup_hashmap_free_ Hashmap *stats_by_path = NULL;
+        struct stat st;
         char **fn;
         int r;
 
+        if (ret_stats_by_path) {
+                stats_by_path = hashmap_new(&path_hash_ops_free_free);
+                if (!stats_by_path)
+                        return -ENOMEM;
+        }
+
         /* First read the first found main config file. */
         STRV_FOREACH(fn, (char**) conf_files) {
-                r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata, &mtime);
+                r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata, &st);
                 if (r < 0)
                         return r;
-                if (r > 0)
-                        break;
+                if (r == 0)
+                        continue;
+
+                if (ret_stats_by_path) {
+                        r = hashmap_put_stats_by_path(&stats_by_path, *fn, &st);
+                        if (r < 0)
+                                return r;
+                }
+
+                break;
         }
 
         /* Then read all the drop-ins. */
         STRV_FOREACH(fn, files) {
-                r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata, &mtime);
+                r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata, &st);
                 if (r < 0)
                         return r;
+                if (r == 0)
+                        continue;
+
+                if (ret_stats_by_path) {
+                        r = hashmap_put_stats_by_path(&stats_by_path, *fn, &st);
+                        if (r < 0)
+                                return r;
+                }
         }
 
-        if (ret_mtime)
-                *ret_mtime = mtime;
+        if (ret_stats_by_path)
+                *ret_stats_by_path = TAKE_PTR(stats_by_path);
 
         return 0;
 }
@@ -473,7 +530,7 @@ int config_parse_many_nulstr(
                 const void *table,
                 ConfigParseFlags flags,
                 void *userdata,
-                usec_t *ret_mtime) {
+                Hashmap **ret_stats_by_path) {
 
         _cleanup_strv_free_ char **files = NULL;
         int r;
@@ -484,7 +541,7 @@ int config_parse_many_nulstr(
 
         return config_parse_many_files(STRV_MAKE_CONST(conf_file),
                                        files, sections, lookup, table, flags, userdata,
-                                       ret_mtime);
+                                       ret_stats_by_path);
 }
 
 /* Parse each config file in the directories specified as strv. */
@@ -497,7 +554,7 @@ int config_parse_many(
                 const void *table,
                 ConfigParseFlags flags,
                 void *userdata,
-                usec_t *ret_mtime) {
+                Hashmap **ret_stats_by_path) {
 
         _cleanup_strv_free_ char **dropin_dirs = NULL;
         _cleanup_strv_free_ char **files = NULL;
@@ -513,7 +570,7 @@ int config_parse_many(
         if (r < 0)
                 return r;
 
-        return config_parse_many_files(conf_files, files, sections, lookup, table, flags, userdata, ret_mtime);
+        return config_parse_many_files(conf_files, files, sections, lookup, table, flags, userdata, ret_stats_by_path);
 }
 
 #define DEFINE_PARSER(type, vartype, conv_func)                         \
index c3a138274db3651a7a72053f0e8fca18f6d55b14..f893a53aa01195fad853a1c07efc58eed5509edd 100644 (file)
@@ -6,8 +6,10 @@
 #include <stddef.h>
 #include <stdio.h>
 #include <syslog.h>
+#include <sys/stat.h>
 
 #include "alloc-util.h"
+#include "hashmap.h"
 #include "log.h"
 #include "macro.h"
 #include "time-util.h"
@@ -89,7 +91,7 @@ int config_parse(
                 const void *table,
                 ConfigParseFlags flags,
                 void *userdata,
-                usec_t *latest_mtime);      /* input/output, possibly NULL */
+                struct stat *ret_stat);     /* possibly NULL */
 
 int config_parse_many_nulstr(
                 const char *conf_file,      /* possibly NULL */
@@ -99,7 +101,7 @@ int config_parse_many_nulstr(
                 const void *table,
                 ConfigParseFlags flags,
                 void *userdata,
-                usec_t *ret_mtime);         /* possibly NULL */
+                Hashmap **ret_stats_by_path);   /* possibly NULL */
 
 int config_parse_many(
                 const char* const* conf_files,  /* possibly empty */
@@ -110,7 +112,7 @@ int config_parse_many(
                 const void *table,
                 ConfigParseFlags flags,
                 void *userdata,
-                usec_t *ret_mtime);         /* possibly NULL */
+                Hashmap **ret_stats_by_path);   /* possibly NULL */
 
 CONFIG_PARSER_PROTOTYPE(config_parse_int);
 CONFIG_PARSER_PROTOTYPE(config_parse_unsigned);