231 lines
8.1 KiB
Diff
231 lines
8.1 KiB
Diff
From fe5bad818a26875914f3b0c59fa3d4f5e6b3a41d Mon Sep 17 00:00:00 2001
|
|
From: Yu Watanabe <watanabe.yu+github@gmail.com>
|
|
Date: Tue, 28 Jan 2025 09:55:12 +0900
|
|
Subject: [PATCH] unit-file: introduce unit_file_remove_from_name_map()
|
|
|
|
(cherry picked from commit d8b34aaef24599917d4e7fa04c78fffac3afe7cf)
|
|
|
|
Related: RHEL-14112
|
|
|
|
[msekleta: I've backported strv_equal_ignore_order() in the same commit
|
|
in order to get this to compile.]
|
|
---
|
|
src/basic/strv.c | 20 ++++++++++
|
|
src/basic/strv.h | 1 +
|
|
src/basic/unit-file.c | 35 +++++++++++++++++
|
|
src/basic/unit-file.h | 8 ++++
|
|
src/test/test-unit-file.c | 81 +++++++++++++++++++++++++++++++++++++++
|
|
5 files changed, 145 insertions(+)
|
|
|
|
diff --git a/src/basic/strv.c b/src/basic/strv.c
|
|
index 1f5d6f058f..47cc6931c1 100644
|
|
--- a/src/basic/strv.c
|
|
+++ b/src/basic/strv.c
|
|
@@ -775,6 +775,26 @@ int strv_compare(char * const *a, char * const *b) {
|
|
return 0;
|
|
}
|
|
|
|
+bool strv_equal_ignore_order(char **a, char **b) {
|
|
+
|
|
+ /* Just like strv_equal(), but doesn't care about the order of elements or about redundant entries
|
|
+ * (i.e. it's even ok if the number of entries in the array differ, as long as the difference just
|
|
+ * consists of repititions) */
|
|
+
|
|
+ if (a == b)
|
|
+ return true;
|
|
+
|
|
+ STRV_FOREACH(i, a)
|
|
+ if (!strv_contains(b, *i))
|
|
+ return false;
|
|
+
|
|
+ STRV_FOREACH(i, b)
|
|
+ if (!strv_contains(a, *i))
|
|
+ return false;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
void strv_print(char * const *l) {
|
|
STRV_FOREACH(s, l)
|
|
puts(*s);
|
|
diff --git a/src/basic/strv.h b/src/basic/strv.h
|
|
index 9eb685fb86..1de3c98e5c 100644
|
|
--- a/src/basic/strv.h
|
|
+++ b/src/basic/strv.h
|
|
@@ -160,6 +160,7 @@ bool strv_overlap(char * const *a, char * const *b) _pure_;
|
|
_STRV_FOREACH_PAIR(x, y, l, UNIQ_T(i, UNIQ))
|
|
|
|
char** strv_sort(char **l);
|
|
+bool strv_equal_ignore_order(char **a, char **b);
|
|
void strv_print(char * const *l);
|
|
|
|
#define strv_from_stdarg_alloca(first) \
|
|
diff --git a/src/basic/unit-file.c b/src/basic/unit-file.c
|
|
index c81c69db30..d7d7fd70f6 100644
|
|
--- a/src/basic/unit-file.c
|
|
+++ b/src/basic/unit-file.c
|
|
@@ -627,6 +627,41 @@ int unit_file_build_name_map(
|
|
return 1;
|
|
}
|
|
|
|
+int unit_file_remove_from_name_map(
|
|
+ const LookupPaths *lp,
|
|
+ uint64_t *cache_timestamp_hash,
|
|
+ Hashmap **unit_ids_map,
|
|
+ Hashmap **unit_names_map,
|
|
+ Set **path_cache,
|
|
+ const char *path) {
|
|
+
|
|
+ int r;
|
|
+
|
|
+ assert(path);
|
|
+
|
|
+ /* This assumes the specified path is already removed, and drops the relevant entries from the maps. */
|
|
+
|
|
+ /* If one of the lookup paths we are monitoring is already changed, let's rebuild the map. Then, the
|
|
+ * new map should not contain entries relevant to the specified path. */
|
|
+ r = unit_file_build_name_map(lp, cache_timestamp_hash, unit_ids_map, unit_names_map, path_cache);
|
|
+ if (r != 0)
|
|
+ return r;
|
|
+
|
|
+ /* If not, drop the relevant entries. */
|
|
+
|
|
+ _cleanup_free_ char *name = NULL;
|
|
+ r = path_extract_filename(path, &name);
|
|
+ if (r < 0)
|
|
+ return log_warning_errno(r, "Failed to extract file name from '%s': %m", path);
|
|
+
|
|
+ _unused_ _cleanup_free_ char *key = NULL;
|
|
+ free(hashmap_remove2(*unit_ids_map, name, (void**) &key));
|
|
+ string_strv_hashmap_remove(*unit_names_map, name, name);
|
|
+ free(set_remove(*path_cache, path));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int add_name(
|
|
const char *unit_name,
|
|
Set **names,
|
|
diff --git a/src/basic/unit-file.h b/src/basic/unit-file.h
|
|
index 1c43861f00..78f65dbc8e 100644
|
|
--- a/src/basic/unit-file.h
|
|
+++ b/src/basic/unit-file.h
|
|
@@ -52,6 +52,14 @@ int unit_file_build_name_map(
|
|
Hashmap **unit_names_map,
|
|
Set **path_cache);
|
|
|
|
+int unit_file_remove_from_name_map(
|
|
+ const LookupPaths *lp,
|
|
+ uint64_t *cache_timestamp_hash,
|
|
+ Hashmap **unit_ids_map,
|
|
+ Hashmap **unit_names_map,
|
|
+ Set **path_cache,
|
|
+ const char *path);
|
|
+
|
|
int unit_file_find_fragment(
|
|
Hashmap *unit_ids_map,
|
|
Hashmap *unit_name_map,
|
|
diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c
|
|
index dffa2822e6..389113c336 100644
|
|
--- a/src/test/test-unit-file.c
|
|
+++ b/src/test/test-unit-file.c
|
|
@@ -1,10 +1,15 @@
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
+#include "fileio.h"
|
|
#include "path-lookup.h"
|
|
+#include "path-util.h"
|
|
+#include "random-util.h"
|
|
+#include "rm-rf.h"
|
|
#include "set.h"
|
|
#include "special.h"
|
|
#include "strv.h"
|
|
#include "tests.h"
|
|
+#include "tmpfile-util.h"
|
|
#include "unit-file.h"
|
|
|
|
TEST(unit_validate_alias_symlink_and_warn) {
|
|
@@ -85,6 +90,82 @@ TEST(unit_file_build_name_map) {
|
|
}
|
|
}
|
|
|
|
+static bool test_unit_file_remove_from_name_map_trail(const LookupPaths *lp, size_t trial) {
|
|
+ int r;
|
|
+
|
|
+ log_debug("/* %s(trial=%zu) */", __func__, trial);
|
|
+
|
|
+ _cleanup_hashmap_free_ Hashmap *unit_ids = NULL, *unit_names = NULL;
|
|
+ _cleanup_set_free_ Set *path_cache = NULL;
|
|
+ assert_se(unit_file_build_name_map(lp, NULL, &unit_ids, &unit_names, &path_cache) > 0);
|
|
+
|
|
+ _cleanup_free_ char *name = NULL;
|
|
+ for (size_t i = 0; i < 100; i++) {
|
|
+ ASSERT_OK(asprintf(&name, "test-unit-file-%"PRIx64".service", random_u64()));
|
|
+ if (!hashmap_contains(unit_ids, name))
|
|
+ break;
|
|
+ name = mfree(name);
|
|
+ }
|
|
+ ASSERT_NOT_NULL(name);
|
|
+
|
|
+ _cleanup_free_ char *path = path_join(lp->transient, name);
|
|
+ ASSERT_NOT_NULL(path);
|
|
+ ASSERT_OK(write_string_file(path, "[Unit]\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755));
|
|
+
|
|
+ uint64_t cache_timestamp_hash = 0;
|
|
+ assert_se(unit_file_build_name_map(lp, &cache_timestamp_hash, &unit_ids, &unit_names, &path_cache) > 0);
|
|
+
|
|
+ ASSERT_STREQ(hashmap_get(unit_ids, name), path);
|
|
+ ASSERT_TRUE(strv_equal(hashmap_get(unit_names, name), STRV_MAKE(name)));
|
|
+ ASSERT_TRUE(set_contains(path_cache, path));
|
|
+
|
|
+ assert_se(unlink(path) >= 0);
|
|
+
|
|
+ ASSERT_OK(r = unit_file_remove_from_name_map(lp, &cache_timestamp_hash, &unit_ids, &unit_names, &path_cache, path));
|
|
+ if (r > 0)
|
|
+ return false; /* someone touches unit files. Retrying. */
|
|
+
|
|
+ ASSERT_FALSE(hashmap_contains(unit_ids, name));
|
|
+ ASSERT_FALSE(hashmap_contains(unit_names, path));
|
|
+ ASSERT_FALSE(set_contains(path_cache, path));
|
|
+
|
|
+ _cleanup_hashmap_free_ Hashmap *unit_ids_2 = NULL, *unit_names_2 = NULL;
|
|
+ _cleanup_set_free_ Set *path_cache_2 = NULL;
|
|
+ assert_se(unit_file_build_name_map(lp, NULL, &unit_ids_2, &unit_names_2, &path_cache_2) > 0);
|
|
+
|
|
+ if (hashmap_size(unit_ids) != hashmap_size(unit_ids_2) ||
|
|
+ hashmap_size(unit_names) != hashmap_size(unit_names_2) ||
|
|
+ !set_equal(path_cache, path_cache_2))
|
|
+ return false;
|
|
+
|
|
+ const char *k, *v;
|
|
+ HASHMAP_FOREACH_KEY(v, k, unit_ids)
|
|
+ if (!streq_ptr(hashmap_get(unit_ids_2, k), v))
|
|
+ return false;
|
|
+
|
|
+ char **l;
|
|
+ HASHMAP_FOREACH_KEY(l, k, unit_names)
|
|
+ if (!strv_equal_ignore_order(hashmap_get(unit_names_2, k), l))
|
|
+ return false;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+
|
|
+TEST(unit_file_remove_from_name_map) {
|
|
+ _cleanup_(rm_rf_physical_and_freep) char *d = NULL;
|
|
+
|
|
+ _cleanup_(lookup_paths_free) LookupPaths lp = {};
|
|
+ ASSERT_OK(lookup_paths_init(&lp, LOOKUP_SCOPE_SYSTEM, LOOKUP_PATHS_TEMPORARY_GENERATED, NULL));
|
|
+ ASSERT_NOT_NULL(d = strdup(lp.temporary_dir));
|
|
+
|
|
+ for (size_t i = 0; i < 10; i++)
|
|
+ if (test_unit_file_remove_from_name_map_trail(&lp, i))
|
|
+ return;
|
|
+
|
|
+ assert_not_reached();
|
|
+}
|
|
+
|
|
TEST(runlevel_to_target) {
|
|
in_initrd_force(false);
|
|
assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
|