/* SPDX-License-Identifier: LGPL-2.1+ */
+#include "sd-id128.h"
+
#include "dirent-util.h"
#include "fd-util.h"
#include "fs-util.h"
UNIT_PATH);
}
+int unit_symlink_name_compatible(const char *symlink, const char *target, bool instance_propagation) {
+ _cleanup_free_ char *template = NULL;
+ int r, un_type1, un_type2;
+
+ un_type1 = unit_name_classify(symlink);
+
+ /* The straightforward case: the symlink name matches the target and we have a valid unit */
+ if (streq(symlink, target) &&
+ (un_type1 & (UNIT_NAME_PLAIN | UNIT_NAME_INSTANCE)))
+ return 1;
+
+ r = unit_name_template(symlink, &template);
+ if (r == -EINVAL)
+ return 0; /* Not a template */
+ if (r < 0)
+ return r;
+
+ un_type2 = unit_name_classify(target);
+
+ /* An instance name points to a target that is just the template name */
+ if (un_type1 == UNIT_NAME_INSTANCE &&
+ un_type2 == UNIT_NAME_TEMPLATE &&
+ streq(template, target))
+ return 1;
+
+ /* foo@.target.requires/bar@.service: instance will be propagated */
+ if (instance_propagation &&
+ un_type1 == UNIT_NAME_TEMPLATE &&
+ un_type2 == UNIT_NAME_TEMPLATE &&
+ streq(template, target))
+ return 1;
+
+ return 0;
+}
+
int unit_validate_alias_symlink_and_warn(const char *filename, const char *target) {
const char *src, *dst;
_cleanup_free_ char *src_instance = NULL, *dst_instance = NULL;
streq_ptr(path, lp->runtime_control);
}
-static bool lookup_paths_mtime_good(const LookupPaths *lp, usec_t mtime) {
- char **dir;
+#define HASH_KEY SD_ID128_MAKE(4e,86,1b,e3,39,b3,40,46,98,5d,b8,11,34,8f,c3,c1)
+bool lookup_paths_timestamp_hash_same(const LookupPaths *lp, uint64_t timestamp_hash, uint64_t *ret_new) {
+ struct siphash state;
+
+ siphash24_init(&state, HASH_KEY.bytes);
+
+ char **dir;
STRV_FOREACH(dir, (char**) lp->search_path) {
struct stat st;
continue;
}
- if (timespec_load(&st.st_mtim) > mtime) {
- log_debug_errno(errno, "Unit dir %s has changed, need to update cache.", *dir);
- return false;
- }
+ siphash24_compress_usec_t(timespec_load(&st.st_mtim), &state);
}
- return true;
+ uint64_t updated = siphash24_finalize(&state);
+ if (ret_new)
+ *ret_new = updated;
+ if (updated != timestamp_hash)
+ log_debug("Modification times have changed, need to update cache.");
+ return updated == timestamp_hash;
}
int unit_file_build_name_map(
const LookupPaths *lp,
- usec_t *cache_mtime,
- Hashmap **ret_unit_ids_map,
- Hashmap **ret_unit_names_map,
- Set **ret_path_cache) {
+ uint64_t *cache_timestamp_hash,
+ Hashmap **unit_ids_map,
+ Hashmap **unit_names_map,
+ Set **path_cache) {
/* Build two mappings: any name → main unit (i.e. the end result of symlink resolution), unit name →
* all aliases (i.e. the entry for a given key is a a list of all names which point to this key). The
* have a key, but it is not present in the value for itself, there was an alias pointing to it, but
* the unit itself is not loadable.
*
- * At the same, build a cache of paths where to find units.
+ * At the same, build a cache of paths where to find units. The non-const parameters are for input
+ * and output. Existing contents will be freed before the new contents are stored.
*/
_cleanup_hashmap_free_ Hashmap *ids = NULL, *names = NULL;
_cleanup_set_free_free_ Set *paths = NULL;
+ uint64_t timestamp_hash;
char **dir;
int r;
- usec_t mtime = 0;
- /* Before doing anything, check if the mtime that was passed is still valid. If
- * yes, do nothing. If *cache_time == 0, always build the cache. */
- if (cache_mtime && *cache_mtime > 0 && lookup_paths_mtime_good(lp, *cache_mtime))
- return 0;
+ /* Before doing anything, check if the timestamp hash that was passed is still valid.
+ * If yes, do nothing. */
+ if (cache_timestamp_hash &&
+ lookup_paths_timestamp_hash_same(lp, *cache_timestamp_hash, ×tamp_hash))
+ return 0;
+
+ /* The timestamp hash is now set based on the mtimes from before when we start reading files.
+ * If anything is modified concurrently, we'll consider the cache outdated. */
- if (ret_path_cache) {
- paths = set_new(&path_hash_ops);
+ if (path_cache) {
+ paths = set_new(&path_hash_ops_free);
if (!paths)
return log_oom();
}
STRV_FOREACH(dir, (char**) lp->search_path) {
struct dirent *de;
_cleanup_closedir_ DIR *d = NULL;
- struct stat st;
d = opendir(*dir);
if (!d) {
continue;
}
- /* Determine the latest lookup path modification time */
- if (fstat(dirfd(d), &st) < 0)
- return log_error_errno(errno, "Failed to fstat %s: %m", *dir);
-
- if (!lookup_paths_mtime_exclude(lp, *dir))
- mtime = MAX(mtime, timespec_load(&st.st_mtim));
-
FOREACH_DIRENT_ALL(de, d, log_warning_errno(errno, "Failed to read \"%s\", ignoring: %m", *dir)) {
char *filename;
_cleanup_free_ char *_filename_free = NULL, *simplified = NULL;
if (!filename)
return log_oom();
- if (ret_path_cache) {
+ if (paths) {
r = set_consume(paths, filename);
if (r < 0)
return log_oom();
/* We don't explicitly check for alias loops here. unit_ids_map_get() which
* limits the number of hops should be used to access the map. */
- _cleanup_free_ char *target = NULL, *target_abs = NULL;
+ _cleanup_free_ char *target = NULL;
r = readlinkat_malloc(dirfd(d), de->d_name, &target);
if (r < 0) {
continue;
}
- if (!path_is_absolute(target)) {
- target_abs = path_join(*dir, target);
+ const bool is_abs = path_is_absolute(target);
+ if (lp->root_dir || !is_abs) {
+ char *target_abs = path_join(is_abs ? lp->root_dir : *dir, target);
if (!target_abs)
return log_oom();
}
/* Let's also put the names in the reverse db. */
- Iterator it;
const char *dummy, *src;
- HASHMAP_FOREACH_KEY(dummy, src, ids, it) {
+ HASHMAP_FOREACH_KEY(dummy, src, ids) {
const char *dst;
r = unit_ids_map_get(ids, src, &dst);
basename(dst), src);
}
- if (cache_mtime)
- *cache_mtime = mtime;
- *ret_unit_ids_map = TAKE_PTR(ids);
- *ret_unit_names_map = TAKE_PTR(names);
- if (ret_path_cache)
- *ret_path_cache = TAKE_PTR(paths);
+ if (cache_timestamp_hash)
+ *cache_timestamp_hash = timestamp_hash;
+
+ hashmap_free_and_replace(*unit_ids_map, ids);
+ hashmap_free_and_replace(*unit_names_map, names);
+ if (path_cache)
+ set_free_and_replace(*path_cache, paths);
return 1;
}
/* The unit always has its own name if it's not a template. */
if (IN_SET(name_type, UNIT_NAME_PLAIN, UNIT_NAME_INSTANCE)) {
- r = set_put_strdup(names, unit_name);
+ r = set_put_strdup(&names, unit_name);
if (r < 0)
return r;
}
if (!streq(unit_name, *t))
log_debug("%s: %s has alias %s", __func__, unit_name, *t);
- r = set_put_strdup(names, *t);
+ r = set_put_strdup(&names, *t);
}
if (r < 0)
return r;