swap.c revision 6e392c9c45643d106673c6643ac8bf4e65da13c1
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <unistd.h>
#include <libudev.h>
#include "unit.h"
#include "swap.h"
#include "unit-name.h"
#include "dbus-swap.h"
#include "special.h"
#include "exit-status.h"
#include "path-util.h"
#include "virt.h"
#include "udev-util.h"
#include "fstab-util.h"
[SWAP_DEAD] = UNIT_INACTIVE,
[SWAP_ACTIVE] = UNIT_ACTIVE,
};
static void swap_unset_proc_swaps(Swap *s) {
assert(s);
if (!s->from_proc_swaps)
return;
s->from_proc_swaps = false;
}
int r;
assert(s);
if (r < 0)
return r;
if (s->devnode) {
if (first)
else
}
if (devnode) {
if (!s->devnode)
return -ENOMEM;
}
return 0;
}
assert(s);
u->ignore_on_isolate = true;
}
static void swap_unwatch_control_pid(Swap *s) {
assert(s);
if (s->control_pid <= 0)
return;
s->control_pid = 0;
}
assert(s);
swap_set_devnode(s, NULL);
s->control_command = NULL;
}
static int swap_arm_timer(Swap *s) {
int r;
assert(s);
if (s->timeout_usec <= 0) {
return 0;
}
if (s->timer_event_source) {
if (r < 0)
return r;
}
return sd_event_add_time(
&s->timer_event_source,
swap_dispatch_timer, s);
}
static int swap_add_device_links(Swap *s) {
assert(s);
if (!s->what)
return 0;
if (!s->from_fragment)
return 0;
if (is_device_path(s->what))
else
/* File based swap devices need to be ordered after
* systemd-remount-fs.service, since they might need a
* writable file system. */
}
static int swap_add_default_dependencies(Swap *s) {
assert(s);
return 0;
if (detect_container(NULL) > 0)
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) {
bool b;
_cleanup_free_ char *e = NULL;
return 0;
if (!e)
return log_oom();
b = unit_has_name(UNIT(s), e);
if (!b) {
log_unit_error(UNIT(s)->id, "%s: Value of \"What\" and unit name do not match, not loading.", UNIT(s)->id);
return -EINVAL;
}
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;
}
return 0;
}
static int swap_load_devnode(Swap *s) {
const char *p;
assert(s);
return 0;
if (!d)
return 0;
p = udev_device_get_devnode(d);
if (!p)
return 0;
return swap_set_devnode(s, p);
}
int r;
assert(s);
/* Load a .swap file */
if (r < 0)
return r;
if (u->load_state == UNIT_LOADED) {
if (UNIT(s)->fragment_path)
s->from_fragment = true;
if (!s->what) {
if (s->parameters_fragment.what)
else if (s->parameters_proc_swaps.what)
else
if (!s->what)
return -ENOMEM;
}
path_kill_slashes(s->what);
if (!UNIT(s)->description) {
r = unit_set_description(u, s->what);
if (r < 0)
return r;
}
if (r < 0)
return r;
r = swap_add_device_links(s);
if (r < 0)
return r;
r = swap_load_devnode(s);
if (r < 0)
return r;
r = unit_patch_contexts(u);
if (r < 0)
return r;
r = unit_add_exec_dependencies(u, &s->exec_context);
if (r < 0)
return r;
r = unit_add_default_slice(u, &s->cgroup_context);
if (r < 0)
return r;
if (UNIT(s)->default_dependencies) {
r = swap_add_default_dependencies(s);
if (r < 0)
return r;
}
}
return swap_verify(s);
}
static int swap_setup_unit(
Manager *m,
const char *what,
const char *what_proc_swaps,
int priority,
bool set_flags) {
_cleanup_free_ char *e = NULL;
bool delete = false;
int r;
SwapParameters *p;
assert(m);
if (!e)
return log_oom();
u = manager_get_unit(m, e);
if (u &&
SWAP(u)->from_proc_swaps &&
log_error("Swap %s appeared twice with different device paths %s and %s", e, SWAP(u)->parameters_proc_swaps.what, what_proc_swaps);
return -EEXIST;
}
if (!u) {
delete = true;
if (!u)
return log_oom();
r = unit_add_name(u, e);
if (r < 0)
goto fail;
r = -ENOMEM;
goto fail;
}
} else
delete = false;
p = &SWAP(u)->parameters_proc_swaps;
if (!p->what) {
if (!p->what) {
r = -ENOMEM;
goto fail;
}
}
if (set_flags) {
}
SWAP(u)->from_proc_swaps = true;
return 0;
fail:
log_unit_warning_errno(e, r, "Failed to load swap unit: %m");
if (delete && u)
unit_free(u);
return r;
}
const char *dn;
int r;
assert(m);
if (r < 0)
return r;
/* If this is a block device, then let's add duplicates for
* all other names of this block device */
return 0;
if (!d)
return 0;
/* Add the main device node */
dn = udev_device_get_devnode(d);
/* Add additional units for all symlinks */
const char *p;
p = udev_list_entry_get_name(item);
continue;
if (path_startswith(p, "/dev/block/"))
continue;
continue;
}
return r;
}
assert(s);
if (state != SWAP_ACTIVATING &&
state != SWAP_ACTIVATING_SIGTERM &&
state != SWAP_ACTIVATING_SIGKILL &&
state != SWAP_ACTIVATING_DONE &&
state != SWAP_DEACTIVATING &&
s->control_command = NULL;
}
"%s changed %s -> %s",
/* 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. */
}
int r;
assert(s);
if (s->deserialized_state != s->state)
new_state = s->deserialized_state;
else if (s->from_proc_swaps)
return 0;
if (new_state == SWAP_ACTIVATING ||
new_state == SWAP_DEACTIVATING ||
if (s->control_pid <= 0)
return -EBADMSG;
if (r < 0)
return r;
r = swap_arm_timer(s);
if (r < 0)
return r;
}
swap_set_state(s, new_state);
return 0;
}
SwapParameters *p;
assert(s);
assert(f);
if (s->from_proc_swaps)
p = &s->parameters_proc_swaps;
else if (s->from_fragment)
p = &s->parameters_fragment;
else
p = NULL;
fprintf(f,
"%sSwap State: %s\n"
"%sResult: %s\n"
"%sWhat: %s\n"
"%sFrom fragment: %s\n",
if (s->devnode)
if (p)
fprintf(f,
"%sPriority: %i\n"
"%sOptions: %s\n",
if (s->control_pid > 0)
fprintf(f,
prefix, s->control_pid);
}
int r;
.apply_permissions = true,
.apply_chroot = true,
.apply_tty_stdin = true,
};
assert(s);
assert(c);
(void) unit_realize_cgroup(UNIT(s));
if (s->reset_cpu_usage) {
(void) unit_reset_cpu_usage(UNIT(s));
s->reset_cpu_usage = false;
}
r = unit_setup_exec_runtime(UNIT(s));
if (r < 0)
goto fail;
r = swap_arm_timer(s);
if (r < 0)
goto fail;
r = exec_spawn(c,
&s->exec_context,
s->exec_runtime,
&pid);
if (r < 0)
goto fail;
if (r < 0)
/* FIXME: we need to do something here */
goto fail;
return 0;
fail:
return r;
}
assert(s);
if (f != SWAP_SUCCESS)
s->result = f;
exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
}
assert(s);
if (f != SWAP_SUCCESS)
s->result = f;
}
int r;
assert(s);
if (f != SWAP_SUCCESS)
s->result = f;
r = unit_kill_context(
UNIT(s),
&s->kill_context,
-1,
s->control_pid,
false);
if (r < 0)
goto fail;
if (r > 0) {
r = swap_arm_timer(s);
if (r < 0)
goto fail;
swap_set_state(s, state);
} else if (state == SWAP_ACTIVATING_SIGTERM)
else if (state == SWAP_DEACTIVATING_SIGTERM)
else
return;
fail:
}
static void swap_enter_activating(Swap *s) {
int r, priority = -1;
assert(s);
if (s->from_fragment) {
if (priority < 0)
}
if (r < 0)
goto fail;
if (priority >= 0) {
char p[DECIMAL_STR_MAX(int)];
if (r < 0)
goto fail;
}
const char *discard_arg;
discard_arg = "--discard";
else
if (r < 0)
goto fail;
}
if (r < 0)
goto fail;
if (r < 0)
goto fail;
return;
fail:
}
static void swap_enter_deactivating(Swap *s) {
int r;
assert(s);
r = exec_command_set(s->control_command,
s->what,
NULL);
if (r < 0)
goto fail;
if (r < 0)
goto fail;
return;
fail:
}
static int swap_start(Unit *u) {
assert(s);
/* We cannot fulfill this request right now, try again later
* please! */
if (s->state == SWAP_DEACTIVATING ||
s->state == SWAP_DEACTIVATING_SIGTERM ||
s->state == SWAP_DEACTIVATING_SIGKILL ||
s->state == SWAP_ACTIVATING_SIGTERM ||
s->state == SWAP_ACTIVATING_SIGKILL)
return -EAGAIN;
if (s->state == SWAP_ACTIVATING)
return 0;
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. */
return -EAGAIN;
s->result = SWAP_SUCCESS;
s->reset_cpu_usage = true;
return 1;
}
assert(s);
if (s->state == SWAP_DEACTIVATING ||
s->state == SWAP_DEACTIVATING_SIGTERM ||
s->state == SWAP_DEACTIVATING_SIGKILL ||
s->state == SWAP_ACTIVATING_SIGTERM ||
s->state == SWAP_ACTIVATING_SIGKILL)
return 0;
s->state == SWAP_ACTIVATING_DONE ||
s->state == SWAP_ACTIVE);
if (detect_container(NULL) > 0)
return -EPERM;
return 1;
}
assert(s);
assert(f);
if (s->control_pid > 0)
if (s->control_command_id >= 0)
return 0;
}
assert(s);
if (state < 0)
else
s->deserialized_state = state;
SwapResult f;
f = swap_result_from_string(value);
if (f < 0)
else if (f != SWAP_SUCCESS)
s->result = f;
else
s->control_pid = pid;
if (id < 0)
else {
s->control_command_id = id;
}
} else
return 0;
}
assert(u);
}
assert(u);
}
assert(s);
return s->from_proc_swaps;
}
SwapResult f;
assert(s);
if (pid != s->control_pid)
return;
s->control_pid = 0;
f = SWAP_SUCCESS;
else if (code == CLD_EXITED)
else if (code == CLD_KILLED)
f = SWAP_FAILURE_SIGNAL;
else if (code == CLD_DUMPED)
else
assert_not_reached("Unknown code");
if (f != SWAP_SUCCESS)
s->result = f;
if (s->control_command) {
s->control_command = NULL;
}
log_unit_full(u->id,
"%s swap process exited, code=%s status=%i",
switch (s->state) {
case SWAP_ACTIVATING:
case SWAP_ACTIVATING_DONE:
case SWAP_ACTIVATING_SIGTERM:
case SWAP_ACTIVATING_SIGKILL:
if (f == SWAP_SUCCESS)
swap_enter_active(s, f);
else
swap_enter_dead(s, f);
break;
case SWAP_DEACTIVATING:
swap_enter_dead(s, f);
break;
default:
assert_not_reached("Uh, control process died at wrong time.");
}
/* Notify clients about changed exit status */
}
assert(s);
switch (s->state) {
case SWAP_ACTIVATING:
case SWAP_ACTIVATING_DONE:
break;
case SWAP_DEACTIVATING:
break;
case SWAP_ACTIVATING_SIGTERM:
if (s->kill_context.send_sigkill) {
} else {
}
break;
if (s->kill_context.send_sigkill) {
} else {
log_unit_warning(UNIT(s)->id, "%s deactivation timed out. Skipping SIGKILL. Ignoring.", UNIT(s)->id);
}
break;
case SWAP_ACTIVATING_SIGKILL:
log_unit_warning(UNIT(s)->id, "%s swap process still around after SIGKILL. Ignoring.", UNIT(s)->id);
break;
default:
assert_not_reached("Timeout at wrong time.");
}
return 0;
}
unsigned i;
int r = 0;
assert(m);
rewind(m->proc_swaps);
for (i = 1;; i++) {
int prio = 0, k;
k = fscanf(m->proc_swaps,
"%*s " /* type of swap */
"%*s " /* swap size */
"%*s " /* used */
"%i\n", /* priority */
if (k != 2) {
if (k == EOF)
break;
log_warning("Failed to parse /proc/swaps:%u.", i);
continue;
}
if (!d)
return log_oom();
if (k < 0)
r = k;
}
return r;
}
Unit *u;
int r;
assert(m);
r = swap_load_proc_swaps(m, true);
if (r < 0) {
log_error_errno(r, "Failed to reread /proc/swaps: %m");
/* Reset flags, just in case, for late calls */
}
return 0;
}
/* This has just been deactivated */
case SWAP_ACTIVE:
break;
default:
/* Fire again */
break;
}
} else if (swap->just_activated) {
/* New swap entry */
case SWAP_DEAD:
case SWAP_FAILED:
break;
case SWAP_ACTIVATING:
break;
default:
/* Nothing really changed, but let's
* issue an notification call
* nonetheless, in case somebody is
* waiting for this. */
break;
}
}
/* Reset the flags for later calls */
}
return 1;
}
assert(s);
* a device unit, follow that. */
if (s->from_fragment)
return NULL;
if (other->from_fragment)
/* Otherwise make everybody follow the unit that's named after
* the swap device in the kernel */
return NULL;
}
/* Fall back to the first on the list */
}
int r;
assert(s);
if (LIST_JUST_US(same_devnode, s)) {
return 0;
}
if (!set)
return -ENOMEM;
if (r < 0)
goto fail;
}
return 1;
fail:
return r;
}
static void swap_shutdown(Manager *m) {
assert(m);
if (m->proc_swaps) {
fclose(m->proc_swaps);
m->proc_swaps = NULL;
}
m->swaps_by_devnode = NULL;
}
static int swap_enumerate(Manager *m) {
int r;
assert(m);
if (!m->proc_swaps) {
if (!m->proc_swaps)
r = sd_event_add_io(m->event, &m->swap_event_source, fileno(m->proc_swaps), EPOLLPRI, swap_dispatch_io, m);
if (r < 0)
goto fail;
/* Dispatch this before we dispatch SIGCHLD, so that
if (r < 0)
goto fail;
}
r = swap_load_proc_swaps(m, false);
if (r < 0)
goto fail;
return 0;
fail:
swap_shutdown(m);
return r;
}
_cleanup_free_ char *e = NULL;
const char *dn;
Swap *s;
int r = 0;
assert(m);
if (!dn)
return 0;
if (!e)
return -ENOMEM;
s = hashmap_get(m->units, e);
if (s)
r = swap_set_devnode(s, dn);
_cleanup_free_ char *n = NULL;
if (!n)
return -ENOMEM;
s = hashmap_get(m->units, n);
if (s) {
int q;
q = swap_set_devnode(s, dn);
if (q < 0)
r = q;
}
}
return r;
}
const char *dn;
int r = 0;
Swap *s;
if (!dn)
return 0;
int q;
q = swap_set_devnode(s, NULL);
if (q < 0)
r = q;
}
return r;
}
static void swap_reset_failed(Unit *u) {
assert(s);
if (s->state == SWAP_FAILED)
swap_set_state(s, SWAP_DEAD);
s->result = SWAP_SUCCESS;
}
}
int r;
if (!s->timer_event_source)
return 0;
if (r < 0)
return r;
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)
detect_container(NULL) <= 0;
return supported;
}
static const char* const swap_state_table[_SWAP_STATE_MAX] = {
[SWAP_DEAD] = "dead",
[SWAP_ACTIVATING] = "activating",
[SWAP_ACTIVATING_DONE] = "activating-done",
[SWAP_ACTIVE] = "active",
[SWAP_DEACTIVATING] = "deactivating",
[SWAP_ACTIVATING_SIGTERM] = "activating-sigterm",
[SWAP_ACTIVATING_SIGKILL] = "activating-sigkill",
[SWAP_DEACTIVATING_SIGTERM] = "deactivating-sigterm",
[SWAP_DEACTIVATING_SIGKILL] = "deactivating-sigkill",
[SWAP_FAILED] = "failed"
};
static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = {
[SWAP_EXEC_ACTIVATE] = "ExecActivate",
[SWAP_EXEC_DEACTIVATE] = "ExecDeactivate",
};
static const char* const swap_result_table[_SWAP_RESULT_MAX] = {
[SWAP_SUCCESS] = "success",
[SWAP_FAILURE_RESOURCES] = "resources",
[SWAP_FAILURE_TIMEOUT] = "timeout",
[SWAP_FAILURE_EXIT_CODE] = "exit-code",
[SWAP_FAILURE_SIGNAL] = "signal",
[SWAP_FAILURE_CORE_DUMP] = "core-dump"
};
const UnitVTable swap_vtable = {
.object_size = sizeof(Swap),
.sections =
"Unit\0"
"Swap\0"
"Install\0",
.private_section = "Swap",
.no_alias = true,
.no_instances = true,
.start = swap_start,
.bus_interface = "org.freedesktop.systemd1.Swap",
.starting_stopping = {
[0] = "Activating swap %s...",
[1] = "Deactivating swap %s...",
},
.finished_start_job = {
[JOB_DONE] = "Activated swap %s.",
[JOB_FAILED] = "Failed to activate swap %s.",
[JOB_DEPENDENCY] = "Dependency failed for %s.",
[JOB_TIMEOUT] = "Timed out activating swap %s.",
},
.finished_stop_job = {
[JOB_DONE] = "Deactivated swap %s.",
[JOB_FAILED] = "Failed deactivating swap %s.",
[JOB_TIMEOUT] = "Timed out deactivating swap %s.",
},
},
};