@@ -1558,6 +1558,7 @@ static bool strv_fnmatch_strv_or_empty(char* const* patterns, char **strv, int f
static int do_unit_files(int argc, char *argv[], void *userdata) {
_cleanup_(lookup_paths_free) LookupPaths lp = {};
_cleanup_hashmap_free_ Hashmap *unit_ids = NULL;
+ _cleanup_hashmap_free_ Hashmap *unit_withdrawals = NULL;
_cleanup_hashmap_free_ Hashmap *unit_names = NULL;
char **patterns = strv_skip(argv, 1);
Iterator i;
@@ -1569,7 +1570,7 @@ static int do_unit_files(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "lookup_paths_init() failed: %m");
- r = unit_file_build_name_map(&lp, NULL, &unit_ids, &unit_names, NULL);
+ r = unit_file_build_name_map(&lp, NULL, &unit_ids, &unit_withdrawals, &unit_names, NULL);
if (r < 0)
return log_error_errno(r, "unit_file_build_name_map() failed: %m");
@@ -1581,6 +1582,14 @@ static int do_unit_files(int argc, char *argv[], void *userdata) {
printf("ids: %s → %s\n", k, dst);
}
+ HASHMAP_FOREACH_KEY(dst, k, unit_withdrawals, i) {
+ if (!strv_fnmatch_or_empty(patterns, k, FNM_NOESCAPE) &&
+ !strv_fnmatch_or_empty(patterns, dst, FNM_NOESCAPE))
+ continue;
+
+ printf("withdrawals: %s → %s\n", k, dst);
+ }
+
HASHMAP_FOREACH_KEY(v, k, unit_names, i) {
if (!strv_fnmatch_or_empty(patterns, k, FNM_NOESCAPE) &&
!strv_fnmatch_strv_or_empty(patterns, v, FNM_NOESCAPE))
@@ -4670,6 +4670,7 @@ int unit_load_fragment(Unit *u) {
r = unit_file_build_name_map(&u->manager->lookup_paths,
&u->manager->unit_cache_mtime,
&u->manager->unit_id_map,
+ &u->manager->unit_withdrawal_map,
&u->manager->unit_name_map,
&u->manager->unit_path_cache);
if (r < 0)
@@ -4702,8 +4703,17 @@ int unit_load_fragment(Unit *u) {
return r;
if (null_or_empty(&st)) {
+ const char *wpath;
+
u->load_state = UNIT_MASKED;
u->fragment_mtime = 0;
+
+ wpath = hashmap_get(u->manager->unit_withdrawal_map, u->id);
+ if (wpath) {
+ r = free_and_strdup(&u->withdrawal_path, wpath);
+ if (r < 0)
+ return r;
+ }
} else {
u->load_state = UNIT_LOADED;
u->fragment_mtime = timespec_load(&st.st_mtim);
@@ -691,6 +691,7 @@ static int manager_setup_prefix(Manager *m) {
static void manager_free_unit_name_maps(Manager *m) {
m->unit_id_map = hashmap_free(m->unit_id_map);
+ m->unit_withdrawal_map = hashmap_free(m->unit_withdrawal_map);
m->unit_name_map = hashmap_free(m->unit_name_map);
m->unit_path_cache = set_free_free(m->unit_path_cache);
m->unit_cache_mtime = 0;
@@ -224,6 +224,7 @@ struct Manager {
UnitFileScope unit_file_scope;
LookupPaths lookup_paths;
Hashmap *unit_id_map;
+ Hashmap *unit_withdrawal_map;
Hashmap *unit_name_map;
Set *unit_path_cache;
usec_t unit_cache_mtime;
@@ -708,6 +708,7 @@ void unit_free(Unit *u) {
strv_free(u->documentation);
free(u->fragment_path);
free(u->source_path);
+ free(u->withdrawal_path);
strv_free(u->dropin_paths);
free(u->instance);
@@ -1252,6 +1253,9 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
if (u->source_path)
fprintf(f, "%s\tSource Path: %s\n", prefix, u->source_path);
+ if (u->withdrawal_path)
+ fprintf(f, "%s\tWithdrawal Path: %s\n", prefix, u->withdrawal_path);
+
STRV_FOREACH(j, u->dropin_paths)
fprintf(f, "%s\tDropIn Path: %s\n", prefix, *j);
@@ -5709,9 +5713,10 @@ const char *unit_label_path(Unit *u) {
if (!p)
return NULL;
- /* If a unit is masked, then don't read the SELinux label of /dev/null, as that really makes no sense */
+ /* If a unit is masked, then don't read the SELinux label of /dev/null, as that really makes no sense.
+ * Instead try withdrawal path (which can be NULL) */
if (path_equal(p, "/dev/null"))
- return NULL;
+ return u->withdrawal_path;
return p;
}
@@ -130,6 +130,7 @@ typedef struct Unit {
char *fragment_path; /* if loaded from a config file this is the primary path to it */
char *source_path; /* if converted, the source file */
+ char *withdrawal_path; /* if masked, path to withdrawal unit file (if existent) */
char **dropin_paths;
usec_t fragment_mtime;
@@ -195,20 +195,23 @@ int unit_file_build_name_map(
const LookupPaths *lp,
usec_t *cache_mtime,
Hashmap **ret_unit_ids_map,
+ Hashmap **ret_unit_withdrawal_map,
Hashmap **ret_unit_names_map,
Set **ret_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
- * key is included in the value iff we saw a file or symlink with that name. In other words, if we
- * 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.
+ /* Build three mappings:
+ * - any name → main unit (i.e. the end result of symlink resolution)
+ * - any name → withdrawal unit file path (in case the unit is maksed)
+ * - 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 key is included in the value iff we saw a file or symlink with that name. In other words, if we
+ * 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.
*/
- _cleanup_hashmap_free_ Hashmap *ids = NULL, *names = NULL;
- _cleanup_set_free_free_ Set *paths = NULL;
+ _cleanup_hashmap_free_ Hashmap *ids = NULL, *withdrawals = NULL, *names = NULL;
+ _cleanup_set_free_free_ Set *paths = NULL, *masked_ids = NULL;
char **dir;
int r;
usec_t mtime = 0;
@@ -224,6 +227,10 @@ int unit_file_build_name_map(
return log_oom();
}
+ masked_ids = set_new(&string_hash_ops);
+ if (!masked_ids)
+ return log_oom();
+
STRV_FOREACH(dir, (char**) lp->search_path) {
struct dirent *de;
_cleanup_closedir_ DIR *d = NULL;
@@ -248,6 +255,7 @@ int unit_file_build_name_map(
_cleanup_free_ char *_filename_free = NULL, *simplified = NULL;
const char *suffix, *dst = NULL;
bool valid_unit_name;
+ bool revisit_masked;
valid_unit_name = unit_name_is_valid(de->d_name, UNIT_NAME_ANY);
@@ -276,8 +284,8 @@ int unit_file_build_name_map(
/* search_path is ordered by priority (highest first). If the name is already mapped
* to something (incl. itself), it means that we have already seen it, and we should
- * ignore it here. */
- if (hashmap_contains(ids, de->d_name))
+ * ignore it here. Except the unit is masked, then try to get the withdrawal path */
+ if ((revisit_masked = hashmap_contains(ids, de->d_name)) && !set_contains(masked_ids, de->d_name))
continue;
dirent_ensure_type(d, de);
@@ -349,10 +357,27 @@ int unit_file_build_name_map(
log_debug("%s: normal unit file: %s", __func__, dst);
}
- r = hashmap_put_strdup(&ids, de->d_name, dst);
- if (r < 0)
- return log_warning_errno(r, "Failed to add entry to hashmap (%s→%s): %m",
- de->d_name, dst);
+ if (revisit_masked) {
+ if (!path_equal(dst, "/dev/null")) {
+ r = hashmap_put_strdup(&withdrawals, de->d_name, dst);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to add entry to hashmap (%s→%s): %m",
+ de->d_name, dst);
+
+ set_remove(masked_ids, de->d_name);
+ }
+ } else {
+ r = hashmap_put_strdup(&ids, de->d_name, dst);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to add entry to hashmap (%s→%s): %m",
+ de->d_name, dst);
+
+ if (path_equal(dst, "/dev/null")) {
+ r = set_put_strdup(masked_ids, de->d_name);
+ if (r < 0)
+ return log_oom();
+ }
+ }
}
}
@@ -383,6 +408,7 @@ int unit_file_build_name_map(
if (cache_mtime)
*cache_mtime = mtime;
*ret_unit_ids_map = TAKE_PTR(ids);
+ *ret_unit_withdrawal_map = TAKE_PTR(withdrawals);
*ret_unit_names_map = TAKE_PTR(names);
if (ret_path_cache)
*ret_path_cache = TAKE_PTR(paths);
@@ -45,6 +45,7 @@ int unit_file_build_name_map(
const LookupPaths *lp,
usec_t *ret_time,
Hashmap **ret_unit_ids_map,
+ Hashmap **ret_unit_withdrawal_map,
Hashmap **ret_unit_names_map,
Set **ret_path_cache);
@@ -2660,7 +2660,7 @@ static int unit_find_paths(
_cleanup_set_free_free_ Set *names = NULL;
if (!cached_name_map) {
- r = unit_file_build_name_map(lp, NULL, &cached_id_map, &cached_name_map, NULL);
+ r = unit_file_build_name_map(lp, NULL, &cached_id_map, NULL, &cached_name_map, NULL);
if (r < 0)
return r;
}
@@ -29,6 +29,7 @@ static void test_unit_validate_alias_symlink_and_warn(void) {
static void test_unit_file_build_name_map(char **ids) {
_cleanup_(lookup_paths_free) LookupPaths lp = {};
_cleanup_hashmap_free_ Hashmap *unit_ids = NULL;
+ _cleanup_hashmap_free_ Hashmap *unit_withdrawals = NULL;
_cleanup_hashmap_free_ Hashmap *unit_names = NULL;
Iterator i;
const char *k, *dst;
@@ -38,11 +39,14 @@ static void test_unit_file_build_name_map(char **ids) {
assert_se(lookup_paths_init(&lp, UNIT_FILE_SYSTEM, 0, NULL) >= 0);
- assert_se(unit_file_build_name_map(&lp, &mtime, &unit_ids, &unit_names, NULL) == 1);
+ assert_se(unit_file_build_name_map(&lp, &mtime, &unit_ids, &unit_withdrawals, &unit_names, NULL) == 1);
HASHMAP_FOREACH_KEY(dst, k, unit_ids, i)
log_info("ids: %s → %s", k, dst);
+ HASHMAP_FOREACH_KEY(dst, k, unit_withdrawals, i)
+ log_info("withdrawals: %s → %s", k, dst);
+
HASHMAP_FOREACH_KEY(v, k, unit_names, i) {
_cleanup_free_ char *j = strv_join(v, ", ");
log_info("aliases: %s ← %s", k, j);
@@ -51,7 +55,7 @@ static void test_unit_file_build_name_map(char **ids) {
char buf[FORMAT_TIMESTAMP_MAX];
log_debug("Last modification time: %s", format_timestamp(buf, sizeof buf, mtime));
- r = unit_file_build_name_map(&lp, &mtime, &unit_ids, &unit_names, NULL);
+ r = unit_file_build_name_map(&lp, &mtime, &unit_ids, &unit_withdrawals, &unit_names, NULL);
assert_se(IN_SET(r, 0, 1));
if (r == 0)
log_debug("Cache rebuild skipped based on mtime.");