]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/load-fragment.c
core: never allow perpetual units to be masked
[thirdparty/systemd.git] / src / core / load-fragment.c
index 3288b0b83885c2fb07631705e0d126e615c32cc6..0de9adb6e56af8cf8ebbdb7179efa819ca718ed4 100644 (file)
@@ -11,9 +11,7 @@
 #include <seccomp.h>
 #endif
 #include <sched.h>
-#include <string.h>
 #include <sys/resource.h>
-#include <sys/stat.h>
 
 #include "af-list.h"
 #include "alloc-util.h"
@@ -24,7 +22,7 @@
 #include "bus-util.h"
 #include "cap-list.h"
 #include "capability-util.h"
-#include "cgroup.h"
+#include "cgroup-setup.h"
 #include "conf-parser.h"
 #include "cpu-set-util.h"
 #include "env-util.h"
@@ -40,7 +38,6 @@
 #include "limits-util.h"
 #include "load-fragment.h"
 #include "log.h"
-#include "missing.h"
 #include "mountpoint-util.h"
 #include "nulstr-util.h"
 #include "parse-util.h"
@@ -51,6 +48,7 @@
 #endif
 #include "securebits-util.h"
 #include "signal-util.h"
+#include "socket-netlink.h"
 #include "stat-util.h"
 #include "string-util.h"
 #include "strv.h"
@@ -98,14 +96,12 @@ int parse_confirm_spawn(const char *value, char **console) {
         if (r == 0) {
                 *console = NULL;
                 return 0;
-        }
-
-        if (r > 0) /* on with default tty */
+        } else if (r > 0) /* on with default tty */
                 s = strdup("/dev/console");
         else if (is_path(value)) /* on with fully qualified path */
                 s = strdup(value);
         else /* on with only a tty file name, not a fully qualified path */
-                s = strjoin("/dev/", value);
+                s = path_join("/dev/", value);
         if (!s)
                 return -ENOMEM;
 
@@ -135,6 +131,7 @@ DEFINE_CONFIG_PARSE_PTR(config_parse_cg_weight, cg_weight_parse, uint64_t, "Inva
 DEFINE_CONFIG_PARSE_PTR(config_parse_cpu_shares, cg_cpu_shares_parse, uint64_t, "Invalid CPU shares");
 DEFINE_CONFIG_PARSE_PTR(config_parse_exec_mount_flags, mount_propagation_flags_from_string, unsigned long, "Failed to parse mount flag");
 DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_numa_policy, mpol, int, -1, "Invalid NUMA policy type");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_status_unit_format, status_unit_format, StatusUnitFormat, "Failed to parse status unit format");
 
 int config_parse_unit_deps(
                 const char *unit,
@@ -216,7 +213,7 @@ int config_parse_unit_string_printf(
                 void *userdata) {
 
         _cleanup_free_ char *k = NULL;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         int r;
 
         assert(filename);
@@ -245,7 +242,7 @@ int config_parse_unit_strv_printf(
                 void *data,
                 void *userdata) {
 
-        Unit *u = userdata;
+        const Unit *u = userdata;
         _cleanup_free_ char *k = NULL;
         int r;
 
@@ -276,7 +273,7 @@ int config_parse_unit_path_printf(
                 void *userdata) {
 
         _cleanup_free_ char *k = NULL;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         int r;
         bool fatal = ltype;
 
@@ -317,7 +314,7 @@ int config_parse_unit_path_strv_printf(
                 void *userdata) {
 
         char ***x = data;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         int r;
         const char *p;
 
@@ -607,7 +604,7 @@ int config_parse_exec(
                 void *userdata) {
 
         ExecCommand **e = data;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         const char *p;
         bool semicolon;
         int r;
@@ -618,7 +615,6 @@ int config_parse_exec(
         assert(e);
 
         e += ltype;
-        rvalue += strspn(rvalue, WHITESPACE);
 
         if (isempty(rvalue)) {
                 /* An empty assignment resets the list */
@@ -879,7 +875,7 @@ int config_parse_exec_input(
                 void *userdata) {
 
         ExecContext *c = data;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         const char *n;
         ExecInput ei;
         int r;
@@ -949,7 +945,7 @@ int config_parse_exec_input_text(
 
         _cleanup_free_ char *unescaped = NULL, *resolved = NULL;
         ExecContext *c = data;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         size_t sz;
         void *p;
         int r;
@@ -1062,7 +1058,7 @@ int config_parse_exec_output(
         _cleanup_free_ char *resolved = NULL;
         const char *n;
         ExecContext *c = data;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         ExecOutput eo;
         int r;
 
@@ -1406,7 +1402,7 @@ int config_parse_exec_selinux_context(
                 void *userdata) {
 
         ExecContext *c = data;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         bool ignore;
         char *k;
         int r;
@@ -1455,7 +1451,7 @@ int config_parse_exec_apparmor_profile(
                 void *userdata) {
 
         ExecContext *c = data;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         bool ignore;
         char *k;
         int r;
@@ -1504,7 +1500,7 @@ int config_parse_exec_smack_process_label(
                 void *userdata) {
 
         ExecContext *c = data;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         bool ignore;
         char *k;
         int r;
@@ -1554,7 +1550,7 @@ int config_parse_timer(
 
         _cleanup_(calendar_spec_freep) CalendarSpec *c = NULL;
         _cleanup_free_ char *k = NULL;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         Timer *t = data;
         usec_t usec = 0;
         TimerValue *v;
@@ -1872,7 +1868,7 @@ int config_parse_bus_name(
                 void *userdata) {
 
         _cleanup_free_ char *k = NULL;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         int r;
 
         assert(filename);
@@ -1935,7 +1931,7 @@ int config_parse_service_timeout(
         return 0;
 }
 
-int config_parse_service_timeout_abort(
+int config_parse_timeout_abort(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -1947,27 +1943,49 @@ int config_parse_service_timeout_abort(
                 void *data,
                 void *userdata) {
 
-        Service *s = userdata;
+        usec_t *ret = data;
         int r;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
-        assert(s);
+        assert(ret);
+
+        /* Note: apart from setting the arg, this returns an extra bit of information in the return value. */
 
-        rvalue += strspn(rvalue, WHITESPACE);
         if (isempty(rvalue)) {
-                s->timeout_abort_set = false;
-                return 0;
+                *ret = 0;
+                return 0; /* "not set" */
         }
 
-        r = parse_sec(rvalue, &s->timeout_abort_usec);
-        if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse TimeoutAbortSec= setting, ignoring: %s", rvalue);
-                return 0;
-        }
+        r = parse_sec(rvalue, ret);
+        if (r < 0)
+                return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= setting, ignoring: %s", lvalue, rvalue);
+
+        return 1; /* "set" */
+}
+
+int config_parse_service_timeout_abort(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Service *s = userdata;
+        int r;
+
+        assert(s);
 
-        s->timeout_abort_set = true;
+        r = config_parse_timeout_abort(unit, filename, line, section, section_line, lvalue, ltype, rvalue,
+                                       &s->timeout_abort_usec, s);
+        if (r >= 0)
+                s->timeout_abort_set = r;
         return 0;
 }
 
@@ -2004,7 +2022,7 @@ int config_parse_sec_fix_0(
         return 0;
 }
 
-int config_parse_user_group(
+int config_parse_user_group_compat(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -2018,7 +2036,7 @@ int config_parse_user_group(
 
         _cleanup_free_ char *k = NULL;
         char **user = data;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         int r;
 
         assert(filename);
@@ -2037,7 +2055,7 @@ int config_parse_user_group(
                 return -ENOEXEC;
         }
 
-        if (!valid_user_group_name_or_id(k)) {
+        if (!valid_user_group_name_or_id_compat(k)) {
                 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
                 return -ENOEXEC;
         }
@@ -2045,7 +2063,7 @@ int config_parse_user_group(
         return free_and_replace(*user, k);
 }
 
-int config_parse_user_group_strv(
+int config_parse_user_group_strv_compat(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -2058,7 +2076,7 @@ int config_parse_user_group_strv(
                 void *userdata) {
 
         char ***users = data;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         const char *p = rvalue;
         int r;
 
@@ -2091,7 +2109,7 @@ int config_parse_user_group_strv(
                         return -ENOEXEC;
                 }
 
-                if (!valid_user_group_name_or_id(k)) {
+                if (!valid_user_group_name_or_id_compat(k)) {
                         log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
                         return -ENOEXEC;
                 }
@@ -2119,7 +2137,7 @@ int config_parse_working_directory(
                 void *userdata) {
 
         ExecContext *c = data;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         bool missing_ok;
         int r;
 
@@ -2179,7 +2197,7 @@ int config_parse_unit_env_file(const char *unit,
                                void *userdata) {
 
         char ***env = data;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         _cleanup_free_ char *n = NULL;
         int r;
 
@@ -2225,7 +2243,7 @@ int config_parse_environ(
                 void *data,
                 void *userdata) {
 
-        Unit *u = userdata;
+        const Unit *u = userdata;
         char ***env = data;
         const char *p;
         int r;
@@ -2295,7 +2313,7 @@ int config_parse_pass_environ(
         size_t nlen = 0, nbufsize = 0;
         char*** passenv = data;
         const char *p = rvalue;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         int r;
 
         assert(filename);
@@ -2371,7 +2389,7 @@ int config_parse_unset_environ(
         size_t nlen = 0, nbufsize = 0;
         char*** unsetenv = data;
         const char *p = rvalue;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         int r;
 
         assert(filename);
@@ -2444,7 +2462,7 @@ int config_parse_log_extra_fields(
                 void *userdata) {
 
         ExecContext *c = data;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         const char *p = rvalue;
         int r;
 
@@ -2517,7 +2535,7 @@ int config_parse_unit_condition_path(
         Condition **list = data, *c;
         ConditionType t = ltype;
         bool trigger, negate;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         int r;
 
         assert(filename);
@@ -2573,7 +2591,7 @@ int config_parse_unit_condition_string(
         Condition **list = data, *c;
         ConditionType t = ltype;
         bool trigger, negate;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         int r;
 
         assert(filename);
@@ -2777,7 +2795,7 @@ int config_parse_syscall_filter(
                 void *userdata) {
 
         ExecContext *c = data;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         bool invert = false;
         const char *p;
         int r;
@@ -3087,7 +3105,7 @@ int config_parse_unit_slice(
 
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_free_ char *k = NULL;
-        Unit *u = userdata, *slice = NULL;
+        Unit *u = userdata, *slice;
         int r;
 
         assert(filename);
@@ -3150,6 +3168,44 @@ int config_parse_cpu_quota(
         return 0;
 }
 
+int config_parse_allowed_cpus(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        CGroupContext *c = data;
+
+        (void) parse_cpu_set_extend(rvalue, &c->cpuset_cpus, true, unit, filename, line, lvalue);
+
+        return 0;
+}
+
+int config_parse_allowed_mems(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        CGroupContext *c = data;
+
+        (void) parse_cpu_set_extend(rvalue, &c->cpuset_mems, true, unit, filename, line, lvalue);
+
+        return 0;
+}
+
 int config_parse_memory_limit(
                 const char *unit,
                 const char *filename,
@@ -3229,36 +3285,39 @@ int config_parse_tasks_max(
                 void *data,
                 void *userdata) {
 
-        uint64_t *tasks_max = data, v;
-        Unit *u = userdata;
+        const Unit *u = userdata;
+        TasksMax *tasks_max = data;
+        uint64_t v;
         int r;
 
         if (isempty(rvalue)) {
-                *tasks_max = u ? u->manager->default_tasks_max : UINT64_MAX;
+                *tasks_max = u ? u->manager->default_tasks_max : TASKS_MAX_UNSET;
                 return 0;
         }
 
         if (streq(rvalue, "infinity")) {
-                *tasks_max = CGROUP_LIMIT_MAX;
+                *tasks_max = TASKS_MAX_UNSET;
                 return 0;
         }
 
         r = parse_permille(rvalue);
-        if (r < 0) {
+        if (r >= 0)
+                *tasks_max = (TasksMax) { r, 1000U }; /* r‰ */
+        else {
                 r = safe_atou64(rvalue, &v);
                 if (r < 0) {
                         log_syntax(unit, LOG_ERR, filename, line, r, "Invalid maximum tasks value '%s', ignoring: %m", rvalue);
                         return 0;
                 }
-        } else
-                v = system_tasks_max_scale(r, 1000U);
 
-        if (v <= 0 || v >= UINT64_MAX) {
-                log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range, ignoring.", rvalue);
-                return 0;
+                if (v <= 0 || v >= UINT64_MAX) {
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range, ignoring.", rvalue);
+                        return 0;
+                }
+
+                *tasks_max = (TasksMax) { v };
         }
 
-        *tasks_max = v;
         return 0;
 }
 
@@ -3853,7 +3912,7 @@ int config_parse_exec_directories(
                 void *userdata) {
 
         char***rt = data;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         const char *p;
         int r;
 
@@ -3936,37 +3995,33 @@ int config_parse_set_status(
 
         FOREACH_WORD(word, l, rvalue, state) {
                 _cleanup_free_ char *temp;
-                int val;
-                Set **set;
+                Bitmap *bitmap;
 
                 temp = strndup(word, l);
                 if (!temp)
                         return log_oom();
 
-                r = safe_atoi(temp, &val);
-                if (r < 0) {
-                        val = signal_from_string(temp);
+                /* We need to call exit_status_from_string() first, because we want
+                 * to parse numbers as exit statuses, not signals. */
 
-                        if (val <= 0) {
-                                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word);
-                                continue;
-                        }
-                        set = &status_set->signal;
+                r = exit_status_from_string(temp);
+                if (r >= 0) {
+                        assert(r >= 0 && r < 256);
+                        bitmap = &status_set->status;
                 } else {
-                        if (val < 0 || val > 255) {
-                                log_syntax(unit, LOG_ERR, filename, line, 0, "Value %d is outside range 0-255, ignoring", val);
+                        r = signal_from_string(temp);
+
+                        if (r <= 0) {
+                                log_syntax(unit, LOG_ERR, filename, line, 0,
+                                           "Failed to parse value, ignoring: %s", word);
                                 continue;
                         }
-                        set = &status_set->status;
+                        bitmap = &status_set->signal;
                 }
 
-                r = set_ensure_allocated(set, NULL);
-                if (r < 0)
-                        return log_oom();
-
-                r = set_put(*set, INT_TO_PTR(val));
+                r = bitmap_set(bitmap, r);
                 if (r < 0)
-                        return log_oom();
+                        return log_error_errno(r, "Failed to set signal or status %s: %m", word);
         }
         if (!isempty(state))
                 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
@@ -3986,7 +4041,7 @@ int config_parse_namespace_path_strv(
                 void *data,
                 void *userdata) {
 
-        Unit *u = userdata;
+        const Unit *u = userdata;
         char*** sv = data;
         const char *p = rvalue;
         int r;
@@ -4063,7 +4118,7 @@ int config_parse_temporary_filesystems(
                 void *data,
                 void *userdata) {
 
-        Unit *u = userdata;
+        const Unit *u = userdata;
         ExecContext *c = data;
         const char *p = rvalue;
         int r;
@@ -4137,7 +4192,7 @@ int config_parse_bind_paths(
                 void *userdata) {
 
         ExecContext *c = data;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         const char *p;
         int r;
 
@@ -4390,7 +4445,7 @@ int config_parse_pid_file(
                 void *userdata) {
 
         _cleanup_free_ char *k = NULL, *n = NULL;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         char **s = data;
         int r;
 
@@ -4512,7 +4567,7 @@ int config_parse_ip_filter_bpf_progs(
                 void *userdata) {
 
         _cleanup_free_ char *resolved = NULL;
-        Unit *u = userdata;
+        const Unit *u = userdata;
         char ***paths = data;
         int r;
 
@@ -4559,236 +4614,114 @@ int config_parse_ip_filter_bpf_progs(
         return 0;
 }
 
-#define FOLLOW_MAX 8
-
-static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
-        char *id = NULL;
-        unsigned c = 0;
-        int fd, r;
-        FILE *f;
-
-        assert(filename);
-        assert(*filename);
-        assert(_f);
-        assert(names);
-
-        /* This will update the filename pointer if the loaded file is
-         * reached by a symlink. The old string will be freed. */
-
-        for (;;) {
-                char *target, *name;
-
-                if (c++ >= FOLLOW_MAX)
-                        return -ELOOP;
-
-                path_simplify(*filename, false);
-
-                /* Add the file name we are currently looking at to
-                 * the names of this unit, but only if it is a valid
-                 * unit name. */
-                name = basename(*filename);
-                if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
-
-                        id = set_get(names, name);
-                        if (!id) {
-                                id = strdup(name);
-                                if (!id)
-                                        return -ENOMEM;
-
-                                r = set_consume(names, id);
-                                if (r < 0)
-                                        return r;
-                        }
-                }
-
-                /* Try to open the file name, but don't if its a symlink */
-                fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
-                if (fd >= 0)
-                        break;
-
-                if (errno != ELOOP)
-                        return -errno;
-
-                /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
-                r = readlink_and_make_absolute(*filename, &target);
-                if (r < 0)
-                        return r;
-
-                free_and_replace(*filename, target);
-        }
-
-        f = fdopen(fd, "r");
-        if (!f) {
-                safe_close(fd);
-                return -errno;
-        }
-
-        *_f = f;
-        *_final = id;
-
-        return 0;
-}
-
 static int merge_by_names(Unit **u, Set *names, const char *id) {
         char *k;
         int r;
 
         assert(u);
         assert(*u);
-        assert(names);
 
-        /* Let's try to add in all symlink names we found */
+        /* Let's try to add in all names that are aliases of this unit */
         while ((k = set_steal_first(names))) {
+                _cleanup_free_ _unused_ char *free_k = k;
 
-                /* First try to merge in the other name into our
-                 * unit */
+                /* First try to merge in the other name into our unit */
                 r = unit_merge_by_name(*u, k);
                 if (r < 0) {
                         Unit *other;
 
-                        /* Hmm, we couldn't merge the other unit into
-                         * ours? Then let's try it the other way
-                         * round */
-
-                        /* If the symlink name we are looking at is unit template, then
-                           we must search for instance of this template */
-                        if (unit_name_is_valid(k, UNIT_NAME_TEMPLATE) && (*u)->instance) {
-                                _cleanup_free_ char *instance = NULL;
-
-                                r = unit_name_replace_instance(k, (*u)->instance, &instance);
-                                if (r < 0)
-                                        return r;
+                        /* Hmm, we couldn't merge the other unit into ours? Then let's try it the other way
+                         * round. */
 
-                                other = manager_get_unit((*u)->manager, instance);
-                        } else
-                                other = manager_get_unit((*u)->manager, k);
-
-                        free(k);
+                        other = manager_get_unit((*u)->manager, k);
+                        if (!other)
+                                return r; /* return previous failure */
 
-                        if (other) {
-                                r = unit_merge(other, *u);
-                                if (r >= 0) {
-                                        *u = other;
-                                        return merge_by_names(u, names, NULL);
-                                }
-                        }
+                        r = unit_merge(other, *u);
+                        if (r < 0)
+                                return r;
 
-                        return r;
+                        *u = other;
+                        return merge_by_names(u, names, NULL);
                 }
 
-                if (id == k)
+                if (streq_ptr(id, k))
                         unit_choose_id(*u, id);
-
-                free(k);
         }
 
         return 0;
 }
 
-static int load_from_path(Unit *u, const char *path) {
-        _cleanup_set_free_free_ Set *symlink_names = NULL;
-        _cleanup_fclose_ FILE *f = NULL;
-        _cleanup_free_ char *filename = NULL;
-        char *id = NULL;
-        Unit *merged;
+int unit_load_fragment(Unit *u) {
+        const char *fragment;
+        _cleanup_set_free_free_ Set *names = NULL;
         struct stat st;
         int r;
 
         assert(u);
-        assert(path);
-
-        symlink_names = set_new(&string_hash_ops);
-        if (!symlink_names)
-                return -ENOMEM;
-
-        if (path_is_absolute(path)) {
-
-                filename = strdup(path);
-                if (!filename)
-                        return -ENOMEM;
-
-                r = open_follow(&filename, &f, symlink_names, &id);
-                if (r < 0) {
-                        filename = mfree(filename);
-                        if (r != -ENOENT)
-                                return r;
-                }
-
-        } else  {
-                char **p;
-
-                STRV_FOREACH(p, u->manager->lookup_paths.search_path) {
-
-                        /* Instead of opening the path right away, we manually
-                         * follow all symlinks and add their name to our unit
-                         * name set while doing so */
-                        filename = path_make_absolute(path, *p);
-                        if (!filename)
-                                return -ENOMEM;
-
-                        if (u->manager->unit_path_cache &&
-                            !set_get(u->manager->unit_path_cache, filename))
-                                r = -ENOENT;
-                        else
-                                r = open_follow(&filename, &f, symlink_names, &id);
-                        if (r >= 0)
-                                break;
-
-                        /* ENOENT means that the file is missing or is a dangling symlink.
-                         * ENOTDIR means that one of paths we expect to be is a directory
-                         * is not a directory, we should just ignore that.
-                         * EACCES means that the directory or file permissions are wrong.
-                         */
-                        if (r == -EACCES)
-                                log_debug_errno(r, "Cannot access \"%s\": %m", filename);
-                        else if (!IN_SET(r, -ENOENT, -ENOTDIR))
-                                return r;
-
-                        filename = mfree(filename);
-                        /* Empty the symlink names for the next run */
-                        set_clear_free(symlink_names);
-                }
-        }
+        assert(u->load_state == UNIT_STUB);
+        assert(u->id);
 
-        if (!filename)
-                /* Hmm, no suitable file found? */
+        if (u->transient) {
+                u->load_state = UNIT_LOADED;
                 return 0;
-
-        if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) {
-                log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id);
-                return -ELOOP;
         }
 
-        merged = u;
-        r = merge_by_names(&merged, symlink_names, id);
+        /* Possibly rebuild the fragment map to catch new units */
+        r = unit_file_build_name_map(&u->manager->lookup_paths,
+                                     &u->manager->unit_cache_mtime,
+                                     &u->manager->unit_id_map,
+                                     &u->manager->unit_name_map,
+                                     &u->manager->unit_path_cache);
         if (r < 0)
+                log_error_errno(r, "Failed to rebuild name map: %m");
+
+        r = unit_file_find_fragment(u->manager->unit_id_map,
+                                    u->manager->unit_name_map,
+                                    u->id,
+                                    &fragment,
+                                    &names);
+        if (r < 0 && r != -ENOENT)
                 return r;
 
-        if (merged != u) {
-                u->load_state = UNIT_MERGED;
-                return 0;
-        }
+        if (fragment) {
+                /* Open the file, check if this is a mask, otherwise read. */
+                _cleanup_fclose_ FILE *f = NULL;
 
-        if (fstat(fileno(f), &st) < 0)
-                return -errno;
+                /* Try to open the file name. A symlink is OK, for example for linked files or masks. We
+                 * expect that all symlinks within the lookup paths have been already resolved, but we don't
+                 * verify this here. */
+                f = fopen(fragment, "re");
+                if (!f)
+                        return log_unit_notice_errno(u, errno, "Failed to open %s: %m", fragment);
 
-        if (null_or_empty(&st)) {
-                u->load_state = UNIT_MASKED;
-                u->fragment_mtime = 0;
-        } else {
-                u->load_state = UNIT_LOADED;
-                u->fragment_mtime = timespec_load(&st.st_mtim);
+                if (fstat(fileno(f), &st) < 0)
+                        return -errno;
 
-                /* Now, parse the file contents */
-                r = config_parse(u->id, filename, f,
-                                 UNIT_VTABLE(u)->sections,
-                                 config_item_perf_lookup, load_fragment_gperf_lookup,
-                                 CONFIG_PARSE_ALLOW_INCLUDE, u);
+                r = free_and_strdup(&u->fragment_path, fragment);
                 if (r < 0)
                         return r;
-        }
 
-        free_and_replace(u->fragment_path, filename);
+                if (null_or_empty(&st)) {
+                        /* Unit file is masked */
+
+                        u->load_state = u->perpetual ? UNIT_LOADED : UNIT_MASKED; /* don't allow perpetual units to ever be masked */
+                        u->fragment_mtime = 0;
+                } else {
+                        u->load_state = UNIT_LOADED;
+                        u->fragment_mtime = timespec_load(&st.st_mtim);
+
+                        /* Now, parse the file contents */
+                        r = config_parse(u->id, fragment, f,
+                                         UNIT_VTABLE(u)->sections,
+                                         config_item_perf_lookup, load_fragment_gperf_lookup,
+                                         CONFIG_PARSE_ALLOW_INCLUDE, u);
+                        if (r == -ENOEXEC)
+                                log_unit_notice_errno(u, r, "Unit configuration has fatal error, unit will not be started.");
+                        if (r < 0)
+                                return r;
+                }
+        }
 
         if (u->source_path) {
                 if (stat(u->source_path, &st) >= 0)
@@ -4797,95 +4730,33 @@ static int load_from_path(Unit *u, const char *path) {
                         u->source_mtime = 0;
         }
 
-        return 0;
-}
-
-int unit_load_fragment(Unit *u) {
-        int r;
-        Iterator i;
-        const char *t;
-
-        assert(u);
-        assert(u->load_state == UNIT_STUB);
-        assert(u->id);
-
-        if (u->transient) {
-                u->load_state = UNIT_LOADED;
-                return 0;
-        }
-
-        /* First, try to find the unit under its id. We always look
-         * for unit files in the default directories, to make it easy
-         * to override things by placing things in /etc/systemd/system */
-        r = load_from_path(u, u->id);
-        if (r < 0)
-                return r;
-
-        /* Try to find an alias we can load this with */
-        if (u->load_state == UNIT_STUB) {
-                SET_FOREACH(t, u->names, i) {
+        /* We do the merge dance here because for some unit types, the unit might have aliases which are not
+         * declared in the file system. In particular, this is true (and frequent) for device and swap units.
+         */
+        Unit *merged;
+        const char *id = u->id;
+        _cleanup_free_ char *free_id = NULL;
 
-                        if (t == u->id)
-                                continue;
+        if (fragment) {
+                id = basename(fragment);
+                if (unit_name_is_valid(id, UNIT_NAME_TEMPLATE)) {
+                        assert(u->instance); /* If we're not trying to use a template for non-instanced unit,
+                                              * this must be set. */
 
-                        r = load_from_path(u, t);
+                        r = unit_name_replace_instance(id, u->instance, &free_id);
                         if (r < 0)
-                                return r;
-
-                        if (u->load_state != UNIT_STUB)
-                                break;
+                                return log_debug_errno(r, "Failed to build id (%s + %s): %m", id, u->instance);
+                        id = free_id;
                 }
         }
 
-        /* And now, try looking for it under the suggested (originally linked) path */
-        if (u->load_state == UNIT_STUB && u->fragment_path) {
-
-                r = load_from_path(u, u->fragment_path);
-                if (r < 0)
-                        return r;
-
-                if (u->load_state == UNIT_STUB)
-                        /* Hmm, this didn't work? Then let's get rid
-                         * of the fragment path stored for us, so that
-                         * we don't point to an invalid location. */
-                        u->fragment_path = mfree(u->fragment_path);
-        }
-
-        /* Look for a template */
-        if (u->load_state == UNIT_STUB && u->instance) {
-                _cleanup_free_ char *k = NULL;
-
-                r = unit_name_template(u->id, &k);
-                if (r < 0)
-                        return r;
-
-                r = load_from_path(u, k);
-                if (r < 0) {
-                        if (r == -ENOEXEC)
-                                log_unit_notice(u, "Unit configuration has fatal error, unit will not be started.");
-                        return r;
-                }
-
-                if (u->load_state == UNIT_STUB) {
-                        SET_FOREACH(t, u->names, i) {
-                                _cleanup_free_ char *z = NULL;
-
-                                if (t == u->id)
-                                        continue;
-
-                                r = unit_name_template(t, &z);
-                                if (r < 0)
-                                        return r;
-
-                                r = load_from_path(u, z);
-                                if (r < 0)
-                                        return r;
+        merged = u;
+        r = merge_by_names(&merged, names, id);
+        if (r < 0)
+                return r;
 
-                                if (u->load_state != UNIT_STUB)
-                                        break;
-                        }
-                }
-        }
+        if (merged != u)
+                u->load_state = UNIT_MERGED;
 
         return 0;
 }
@@ -5135,8 +5006,8 @@ int config_parse_crash_chvt(
         return 0;
 }
 
-int config_parse_timeout_abort(
-                const charunit,
+int config_parse_swap_priority(
+                const char *unit,
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -5147,26 +5018,38 @@ int config_parse_timeout_abort(
                 void *data,
                 void *userdata) {
 
-        usec_t *timeout_usec = data;
-        int r;
+        Swap *s = userdata;
+        int r, priority;
 
+        assert(s);
         assert(filename);
         assert(lvalue);
         assert(rvalue);
-        assert(timeout_usec);
+        assert(data);
 
-        rvalue += strspn(rvalue, WHITESPACE);
         if (isempty(rvalue)) {
-                *timeout_usec = false;
+                s->parameters_fragment.priority = -1;
+                s->parameters_fragment.priority_set = false;
                 return 0;
         }
 
-        r = parse_sec(rvalue, timeout_usec);
+        r = safe_atoi(rvalue, &priority);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DefaultTimeoutAbortSec= setting, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, r, "Invalid swap pririty '%s', ignoring.", rvalue);
+                return 0;
+        }
+
+        if (priority < -1) {
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Sorry, swap priorities smaller than -1 may only be assigned by the kernel itself, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        if (priority > 32767) {
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Swap priority out of range, ignoring: %s", rvalue);
                 return 0;
         }
 
-        *timeout_usec = true;
+        s->parameters_fragment.priority = priority;
+        s->parameters_fragment.priority_set = true;
         return 0;
 }