| From 819333d81964fd110565d35a33993b831ba60725 Mon Sep 17 00:00:00 2001 |
| From: Yu Watanabe <watanabe.yu+github@gmail.com> |
| Date: Mon, 25 Oct 2021 11:13:27 +0900 |
| Subject: [PATCH] conf-parse: make config_parse_many() optionally save 'struct |
| stat' for each file |
| |
| Fixes #21113. |
| --- |
| src/core/load-dropin.c | 18 +++--- |
| src/network/networkd-network.c | 38 +++++++++--- |
| src/network/networkd-network.h | 2 +- |
| src/shared/conf-parser.c | 103 +++++++++++++++++++++++++-------- |
| src/shared/conf-parser.h | 8 ++- |
| 5 files changed, 127 insertions(+), 42 deletions(-) |
| |
| diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c |
| index 3bb48564cc..080a63bc7e 100644 |
| --- a/src/core/load-dropin.c |
| +++ b/src/core/load-dropin.c |
| @@ -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; |
| } |
| diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c |
| index 850b4f449e..32d76e29e4 100644 |
| --- a/src/network/networkd-network.c |
| +++ b/src/network/networkd-network.c |
| @@ -480,7 +480,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; |
| |
| @@ -527,6 +527,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; |
| @@ -540,14 +562,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) |
| @@ -573,6 +596,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); |
| diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h |
| index b39063fe8a..c8d24a415f 100644 |
| --- a/src/network/networkd-network.h |
| +++ b/src/network/networkd-network.h |
| @@ -72,7 +72,7 @@ struct Network { |
| |
| char *name; |
| char *filename; |
| - usec_t timestamp; |
| + Hashmap *stats_by_path; |
| char *description; |
| |
| /* [Match] section */ |
| diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c |
| index d0ac1b2660..9a367d757f 100644 |
| --- a/src/shared/conf-parser.c |
| +++ b/src/shared/conf-parser.c |
| @@ -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) \ |
| diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h |
| index c3a138274d..f893a53aa0 100644 |
| --- a/src/shared/conf-parser.h |
| +++ b/src/shared/conf-parser.h |
| @@ -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); |
| -- |
| 2.33.0.1079.g6e70778dc9-goog |
| |