chiark / gitweb /
unit: use weaker dependencies between mount and device units in --user mode
[elogind.git] / src / core / swap.c
index 9f353af..f73a8e6 100644 (file)
 ***/
 
 #include <errno.h>
-#include <limits.h>
 #include <unistd.h>
-#include <fcntl.h>
 #include <sys/epoll.h>
 #include <sys/stat.h>
-#include <sys/swap.h>
 #include <libudev.h>
 
 #include "unit.h"
 #include "swap.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
 #include "unit-name.h"
 #include "dbus-swap.h"
 #include "special.h"
-#include "bus-errors.h"
 #include "exit-status.h"
-#include "def.h"
 #include "path-util.h"
 #include "virt.h"
 #include "udev-util.h"
+#include "fstab-util.h"
 
 static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = {
         [SWAP_DEAD] = UNIT_INACTIVE,
@@ -77,7 +71,7 @@ static int swap_set_devnode(Swap *s, const char *devnode) {
 
         assert(s);
 
-        r = hashmap_ensure_allocated(&UNIT(s)->manager->swaps_by_devnode, string_hash_func, string_compare_func);
+        r = hashmap_ensure_allocated(&UNIT(s)->manager->swaps_by_devnode, &string_hash_ops);
         if (r < 0)
                 return r;
 
@@ -152,6 +146,9 @@ static void swap_done(Unit *u) {
         free(s->parameters_fragment.what);
         s->parameters_fragment.what = NULL;
 
+        free(s->parameters_fragment.options);
+        s->parameters_fragment.options = NULL;
+
         s->exec_runtime = exec_runtime_unref(s->exec_runtime);
         exec_command_done_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX);
         s->control_command = NULL;
@@ -188,20 +185,16 @@ static int swap_arm_timer(Swap *s) {
 }
 
 static int swap_add_device_links(Swap *s) {
-        SwapParameters *p;
-
         assert(s);
 
         if (!s->what)
                 return 0;
 
-        if (s->from_fragment)
-                p = &s->parameters_fragment;
-        else
+        if (!s->from_fragment)
                 return 0;
 
         if (is_device_path(s->what))
-                return unit_add_node_link(UNIT(s), s->what, !p->noauto && UNIT(s)->manager->running_as == SYSTEMD_SYSTEM);
+                return unit_add_node_link(UNIT(s), s->what, UNIT(s)->manager->running_as == SYSTEMD_SYSTEM);
         else
                 /* File based swap devices need to be ordered after
                  * systemd-remount-fs.service, since they might need a
@@ -210,9 +203,6 @@ static int swap_add_device_links(Swap *s) {
 }
 
 static int swap_add_default_dependencies(Swap *s) {
-        bool nofail = false, noauto = false;
-        int r;
-
         assert(s);
 
         if (UNIT(s)->manager->running_as != SYSTEMD_SYSTEM)
@@ -221,27 +211,7 @@ static int swap_add_default_dependencies(Swap *s) {
         if (detect_container(NULL) > 0)
                 return 0;
 
-        r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
-        if (r < 0)
-                return r;
-
-        if (s->from_fragment) {
-                SwapParameters *p = &s->parameters_fragment;
-
-                nofail = p->nofail;
-                noauto = p->noauto;
-        }
-
-        if (!noauto) {
-                if (nofail)
-                        r = unit_add_dependency_by_name_inverse(UNIT(s), UNIT_WANTS, SPECIAL_SWAP_TARGET, NULL, true);
-                else
-                        r = unit_add_two_dependencies_by_name_inverse(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SWAP_TARGET, NULL, true);
-                if (r < 0)
-                        return r;
-        }
-
-        return 0;
+        return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
 }
 
 static int swap_verify(Swap *s) {
@@ -257,12 +227,12 @@ static int swap_verify(Swap *s) {
 
         b = unit_has_name(UNIT(s), e);
         if (!b) {
-                log_error_unit(UNIT(s)->id, "%s: Value of \"What\" and unit name do not match, not loading.", UNIT(s)->id);
+                log_unit_error(UNIT(s)->id, "%s: Value of \"What\" and unit name do not match, not loading.", UNIT(s)->id);
                 return -EINVAL;
         }
 
         if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) {
-                log_error_unit(UNIT(s)->id, "%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing to load.", UNIT(s)->id);
+                log_unit_error(UNIT(s)->id, "%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing to load.", UNIT(s)->id);
                 return -EINVAL;
         }
 
@@ -366,8 +336,6 @@ static int swap_add_one(
                 const char *what,
                 const char *what_proc_swaps,
                 int priority,
-                bool noauto,
-                bool nofail,
                 bool set_flags) {
 
         _cleanup_free_ char *e = NULL;
@@ -430,15 +398,13 @@ static int swap_add_one(
         SWAP(u)->from_proc_swaps = true;
 
         p->priority = priority;
-        p->noauto = noauto;
-        p->nofail = nofail;
 
         unit_add_to_dbus_queue(u);
 
         return 0;
 
 fail:
-        log_warning_unit(e, "Failed to load swap unit: %s", strerror(-r));
+        log_unit_warning_errno(e, r, "Failed to load swap unit: %m");
 
         if (delete && u)
                 unit_free(u);
@@ -455,7 +421,7 @@ static int swap_process_new_swap(Manager *m, const char *device, int prio, bool
 
         assert(m);
 
-        r = swap_add_one(m, device, device, prio, false, false, set_flags);
+        r = swap_add_one(m, device, device, prio, set_flags);
         if (r < 0)
                 return r;
 
@@ -471,7 +437,7 @@ static int swap_process_new_swap(Manager *m, const char *device, int prio, bool
         /* Add the main device node */
         dn = udev_device_get_devnode(d);
         if (dn && !streq(dn, device))
-                swap_add_one(m, dn, device, prio, false, false, set_flags);
+                swap_add_one(m, dn, device, prio, set_flags);
 
         /* Add additional units for all symlinks */
         first = udev_device_get_devlinks_list_entry(d);
@@ -492,7 +458,7 @@ static int swap_process_new_swap(Manager *m, const char *device, int prio, bool
                             st.st_rdev != udev_device_get_devnum(d))
                                 continue;
 
-                swap_add_one(m, p, device, prio, false, false, set_flags);
+                swap_add_one(m, p, device, prio, set_flags);
         }
 
         return r;
@@ -500,6 +466,7 @@ static int swap_process_new_swap(Manager *m, const char *device, int prio, bool
 
 static void swap_set_state(Swap *s, SwapState state) {
         SwapState old_state;
+        Swap *other;
 
         assert(s);
 
@@ -520,13 +487,22 @@ static void swap_set_state(Swap *s, SwapState state) {
         }
 
         if (state != old_state)
-                log_debug_unit(UNIT(s)->id,
+                log_unit_debug(UNIT(s)->id,
                                "%s changed %s -> %s",
                                UNIT(s)->id,
                                swap_state_to_string(old_state),
                                swap_state_to_string(state));
 
         unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
+
+        /* If there other units for the same device node have a job
+           queued it might be worth checking again if it is runnable
+           now. This is necessary, since swap_start() refuses
+           operation with EAGAIN if there's already another job for
+           the same device node queued. */
+        LIST_FOREACH_OTHERS(same_devnode, other, s)
+                if (UNIT(other)->job)
+                        job_add_to_run_queue(UNIT(other)->job);
 }
 
 static int swap_coldplug(Unit *u) {
@@ -601,11 +577,9 @@ static void swap_dump(Unit *u, FILE *f, const char *prefix) {
         if (p)
                 fprintf(f,
                         "%sPriority: %i\n"
-                        "%sNoAuto: %s\n"
-                        "%sNoFail: %s\n",
+                        "%sOptions: %s\n",
                         prefix, p->priority,
-                        prefix, yes_no(p->noauto),
-                        prefix, yes_no(p->nofail));
+                        prefix, strempty(p->options));
 
         if (s->control_pid > 0)
                 fprintf(f,
@@ -619,6 +593,11 @@ static void swap_dump(Unit *u, FILE *f, const char *prefix) {
 static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
         pid_t pid;
         int r;
+        ExecParameters exec_params = {
+                .apply_permissions = true,
+                .apply_chroot      = true,
+                .apply_tty_stdin   = true,
+        };
 
         assert(s);
         assert(c);
@@ -634,21 +613,17 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
         if (r < 0)
                 goto fail;
 
+        exec_params.environment = UNIT(s)->manager->environment;
+        exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn;
+        exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported;
+        exec_params.cgroup_path = UNIT(s)->cgroup_path;
+        exec_params.cgroup_delegate = s->cgroup_context.delegate;
+        exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager);
+        exec_params.unit_id = UNIT(s)->id;
+
         r = exec_spawn(c,
-                       NULL,
                        &s->exec_context,
-                       NULL, 0,
-                       UNIT(s)->manager->environment,
-                       true,
-                       true,
-                       true,
-                       UNIT(s)->manager->confirm_spawn,
-                       UNIT(s)->manager->cgroup_supported,
-                       UNIT(s)->cgroup_path,
-                       manager_get_runtime_prefix(UNIT(s)->manager),
-                       UNIT(s)->id,
-                       0,
-                       NULL,
+                       &exec_params,
                        s->exec_runtime,
                        &pid);
         if (r < 0)
@@ -665,7 +640,6 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
 
 fail:
         s->timer_event_source = sd_event_source_unref(s->timer_event_source);
-
         return r;
 }
 
@@ -703,7 +677,8 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) {
         r = unit_kill_context(
                         UNIT(s),
                         &s->kill_context,
-                        state != SWAP_ACTIVATING_SIGTERM && state != SWAP_DEACTIVATING_SIGTERM,
+                        (state != SWAP_ACTIVATING_SIGTERM && state != SWAP_DEACTIVATING_SIGTERM) ?
+                        KILL_KILL : KILL_TERMINATE,
                         -1,
                         s->control_pid,
                         false);
@@ -726,44 +701,55 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) {
         return;
 
 fail:
-        log_warning_unit(UNIT(s)->id,
-                         "%s failed to kill processes: %s", UNIT(s)->id, strerror(-r));
-
+        log_unit_warning_errno(UNIT(s)->id, r, "%s failed to kill processes: %m", UNIT(s)->id);
         swap_enter_dead(s, SWAP_FAILURE_RESOURCES);
 }
 
 static void swap_enter_activating(Swap *s) {
-        int r, priority;
+        _cleanup_free_ char *discard = NULL;
+        int r, priority = -1;
 
         assert(s);
 
         s->control_command_id = SWAP_EXEC_ACTIVATE;
         s->control_command = s->exec_command + SWAP_EXEC_ACTIVATE;
 
-        if (s->from_fragment)
+        if (s->from_fragment) {
+                fstab_filter_options(s->parameters_fragment.options, "discard\0",
+                                     NULL, &discard, NULL);
+
                 priority = s->parameters_fragment.priority;
-        else
-                priority = -1;
+                if (priority < 0)
+                        fstab_find_pri(s->parameters_fragment.options, &priority);
+        }
+
+        r = exec_command_set(s->control_command, "/sbin/swapon", NULL);
+        if (r < 0)
+                goto fail;
 
         if (priority >= 0) {
                 char p[DECIMAL_STR_MAX(int)];
 
                 sprintf(p, "%i", priority);
+                r = exec_command_append(s->control_command, "-p", p, NULL);
+                if (r < 0)
+                        goto fail;
+        }
 
-                r = exec_command_set(
-                                s->control_command,
-                                "/sbin/swapon",
-                                "-p",
-                                p,
-                                s->what,
-                                NULL);
-        } else
-                r = exec_command_set(
-                                s->control_command,
-                                "/sbin/swapon",
-                                s->what,
-                                NULL);
+        if (discard && !streq(discard, "none")) {
+                const char *discard_arg;
 
+                if (streq(discard, "all"))
+                        discard_arg = "--discard";
+                else
+                        discard_arg = strjoina("--discard=", discard);
+
+                r = exec_command_append(s->control_command, discard_arg, NULL);
+                if (r < 0)
+                        goto fail;
+        }
+
+        r = exec_command_append(s->control_command, s->what, NULL);
         if (r < 0)
                 goto fail;
 
@@ -778,9 +764,7 @@ static void swap_enter_activating(Swap *s) {
         return;
 
 fail:
-        log_warning_unit(UNIT(s)->id,
-                         "%s failed to run 'swapon' task: %s",
-                         UNIT(s)->id, strerror(-r));
+        log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'swapon' task: %m", UNIT(s)->id);
         swap_enter_dead(s, SWAP_FAILURE_RESOURCES);
 }
 
@@ -810,14 +794,12 @@ static void swap_enter_deactivating(Swap *s) {
         return;
 
 fail:
-        log_warning_unit(UNIT(s)->id,
-                         "%s failed to run 'swapoff' task: %s",
-                         UNIT(s)->id, strerror(-r));
+        log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'swapoff' task: %m", UNIT(s)->id);
         swap_enter_active(s, SWAP_FAILURE_RESOURCES);
 }
 
 static int swap_start(Unit *u) {
-        Swap *s = SWAP(u);
+        Swap *s = SWAP(u), *other;
 
         assert(s);
 
@@ -839,9 +821,16 @@ static int swap_start(Unit *u) {
         if (detect_container(NULL) > 0)
                 return -EPERM;
 
+        /* If there's a job for another swap unit for the same node
+         * running, then let's not dispatch this one for now, and wait
+         * until that other job has finished. */
+        LIST_FOREACH_OTHERS(same_devnode, other, s)
+                if (UNIT(other)->job && UNIT(other)->job->state == JOB_RUNNING)
+                        return -EAGAIN;
+
         s->result = SWAP_SUCCESS;
         swap_enter_activating(s);
-        return 0;
+        return 1;
 }
 
 static int swap_stop(Unit *u) {
@@ -864,7 +853,7 @@ static int swap_stop(Unit *u) {
                 return -EPERM;
 
         swap_enter_deactivating(s);
-        return 0;
+        return 1;
 }
 
 static int swap_serialize(Unit *u, FILE *f, FDSet *fds) {
@@ -897,7 +886,7 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD
 
                 state = swap_state_from_string(value);
                 if (state < 0)
-                        log_debug_unit(u->id, "Failed to parse state value %s", value);
+                        log_unit_debug(u->id, "Failed to parse state value %s", value);
                 else
                         s->deserialized_state = state;
         } else if (streq(key, "result")) {
@@ -905,14 +894,14 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD
 
                 f = swap_result_from_string(value);
                 if (f < 0)
-                        log_debug_unit(u->id, "Failed to parse result value %s", value);
+                        log_unit_debug(u->id, "Failed to parse result value %s", value);
                 else if (f != SWAP_SUCCESS)
                         s->result = f;
         } else if (streq(key, "control-pid")) {
                 pid_t pid;
 
                 if (parse_pid(value, &pid) < 0)
-                        log_debug_unit(u->id, "Failed to parse control-pid value %s", value);
+                        log_unit_debug(u->id, "Failed to parse control-pid value %s", value);
                 else
                         s->control_pid = pid;
 
@@ -921,13 +910,13 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD
 
                 id = swap_exec_command_from_string(value);
                 if (id < 0)
-                        log_debug_unit(u->id, "Failed to parse exec-command value %s", value);
+                        log_unit_debug(u->id, "Failed to parse exec-command value %s", value);
                 else {
                         s->control_command_id = id;
                         s->control_command = s->exec_command + id;
                 }
         } else
-                log_debug_unit(u->id, "Unknown serialization key '%s'", key);
+                log_unit_debug(u->id, "Unknown serialization key '%s'", key);
 
         return 0;
 }
@@ -985,8 +974,8 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                 s->control_command_id = _SWAP_EXEC_COMMAND_INVALID;
         }
 
-        log_full_unit(f == SWAP_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
-                      u->id,
+        log_unit_full(u->id,
+                      f == SWAP_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
                       "%s swap process exited, code=%s status=%i",
                       u->id, sigchld_code_to_string(code), status);
 
@@ -1028,38 +1017,38 @@ static int swap_dispatch_timer(sd_event_source *source, usec_t usec, void *userd
 
         case SWAP_ACTIVATING:
         case SWAP_ACTIVATING_DONE:
-                log_warning_unit(UNIT(s)->id, "%s activation timed out. Stopping.", UNIT(s)->id);
+                log_unit_warning(UNIT(s)->id, "%s activation timed out. Stopping.", UNIT(s)->id);
                 swap_enter_signal(s, SWAP_ACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT);
                 break;
 
         case SWAP_DEACTIVATING:
-                log_warning_unit(UNIT(s)->id, "%s deactivation timed out. Stopping.", UNIT(s)->id);
+                log_unit_warning(UNIT(s)->id, "%s deactivation timed out. Stopping.", UNIT(s)->id);
                 swap_enter_signal(s, SWAP_DEACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT);
                 break;
 
         case SWAP_ACTIVATING_SIGTERM:
                 if (s->kill_context.send_sigkill) {
-                        log_warning_unit(UNIT(s)->id, "%s activation timed out. Killing.", UNIT(s)->id);
+                        log_unit_warning(UNIT(s)->id, "%s activation timed out. Killing.", UNIT(s)->id);
                         swap_enter_signal(s, SWAP_ACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT);
                 } else {
-                        log_warning_unit(UNIT(s)->id, "%s activation timed out. Skipping SIGKILL. Ignoring.", UNIT(s)->id);
+                        log_unit_warning(UNIT(s)->id, "%s activation timed out. Skipping SIGKILL. Ignoring.", UNIT(s)->id);
                         swap_enter_dead(s, SWAP_FAILURE_TIMEOUT);
                 }
                 break;
 
         case SWAP_DEACTIVATING_SIGTERM:
                 if (s->kill_context.send_sigkill) {
-                        log_warning_unit(UNIT(s)->id, "%s deactivation timed out. Killing.", UNIT(s)->id);
+                        log_unit_warning(UNIT(s)->id, "%s deactivation timed out. Killing.", UNIT(s)->id);
                         swap_enter_signal(s, SWAP_DEACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT);
                 } else {
-                        log_warning_unit(UNIT(s)->id, "%s deactivation timed out. Skipping SIGKILL. Ignoring.", UNIT(s)->id);
+                        log_unit_warning(UNIT(s)->id, "%s deactivation timed out. Skipping SIGKILL. Ignoring.", UNIT(s)->id);
                         swap_enter_dead(s, SWAP_FAILURE_TIMEOUT);
                 }
                 break;
 
         case SWAP_ACTIVATING_SIGKILL:
         case SWAP_DEACTIVATING_SIGKILL:
-                log_warning_unit(UNIT(s)->id, "%s swap process still around after SIGKILL. Ignoring.", UNIT(s)->id);
+                log_unit_warning(UNIT(s)->id, "%s swap process still around after SIGKILL. Ignoring.", UNIT(s)->id);
                 swap_enter_dead(s, SWAP_FAILURE_TIMEOUT);
                 break;
 
@@ -1121,7 +1110,7 @@ static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v
 
         r = swap_load_proc_swaps(m, true);
         if (r < 0) {
-                log_error("Failed to reread /proc/swaps: %s", strerror(-r));
+                log_error_errno(r, "Failed to reread /proc/swaps: %m");
 
                 /* Reset flags, just in case, for late calls */
                 LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_SWAP]) {
@@ -1193,11 +1182,21 @@ static Unit *swap_following(Unit *u) {
 
         assert(s);
 
-        if (streq_ptr(s->what, s->devnode))
+        /* If the user configured the swap through /etc/fstab or
+         * a device unit, follow that. */
+
+        if (s->from_fragment)
                 return NULL;
 
-        /* Make everybody follow the unit that's named after the swap
-         * device in the kernel */
+        LIST_FOREACH_OTHERS(same_devnode, other, s)
+                if (other->from_fragment)
+                        return UNIT(other);
+
+        /* Otherwise make everybody follow the unit that's named after
+         * the swap device in the kernel */
+
+        if (streq_ptr(s->what, s->devnode))
+                return NULL;
 
         LIST_FOREACH_AFTER(same_devnode, other, s)
                 if (streq_ptr(other->what, other->devnode))
@@ -1210,6 +1209,7 @@ static Unit *swap_following(Unit *u) {
                 first = other;
         }
 
+        /* Fall back to the first on the list */
         return UNIT(first);
 }
 
@@ -1226,17 +1226,11 @@ static int swap_following_set(Unit *u, Set **_set) {
                 return 0;
         }
 
-        set = set_new(NULL, NULL);
+        set = set_new(NULL);
         if (!set)
                 return -ENOMEM;
 
-        LIST_FOREACH_AFTER(same_devnode, other, s) {
-                r = set_put(set, other);
-                if (r < 0)
-                        goto fail;
-        }
-
-        LIST_FOREACH_BEFORE(same_devnode, other, s) {
+        LIST_FOREACH_OTHERS(same_devnode, other, s) {
                 r = set_put(set, other);
                 if (r < 0)
                         goto fail;
@@ -1389,6 +1383,21 @@ static int swap_get_timeout(Unit *u, uint64_t *timeout) {
         return 1;
 }
 
+static bool swap_supported(Manager *m) {
+        static int supported = -1;
+
+        /* If swap support is not available in the kernel, or we are
+         * running in a container we don't support swap units, and any
+         * attempts to starting one should fail immediately. */
+
+        if (supported < 0)
+                supported =
+                        access("/proc/swaps", F_OK) >= 0 &&
+                        detect_container(NULL) <= 0;
+
+        return supported;
+}
+
 static const char* const swap_state_table[_SWAP_STATE_MAX] = {
         [SWAP_DEAD] = "dead",
         [SWAP_ACTIVATING] = "activating",
@@ -1475,6 +1484,7 @@ const UnitVTable swap_vtable = {
 
         .enumerate = swap_enumerate,
         .shutdown = swap_shutdown,
+        .supported = swap_supported,
 
         .status_message_formats = {
                 .starting_stopping = {