swap.c revision 74c964d369b11bbf465c140c3f2dfcde2c7f5977
/*-*- 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 <limits.h>
#include <unistd.h>
#include <fcntl.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"
[SWAP_DEAD] = UNIT_INACTIVE,
[SWAP_ACTIVE] = UNIT_ACTIVE,
};
static void swap_unset_proc_swaps(Swap *s) {
assert(s);
if (!s->parameters_proc_swaps.what)
return;
/* Remove this unit from the chain of swaps which share the
* same kernel swap device. */
if (first)
first);
else
}
assert(s);
UNIT(s)->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);
s->control_command = NULL;
unit_unwatch_timer(u, &s->timer_watch);
}
int r;
assert(s);
assert(m);
return 0;
if (is_device_path(s->what))
return 0;
return 0;
if (r < 0)
return r;
return 0;
}
static int swap_add_mount_links(Swap *s) {
int r;
assert(s);
return r;
return 0;
}
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
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) {
int r;
assert(s);
return 0;
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;
return 0;
}
static int swap_verify(Swap *s) {
bool b;
_cleanup_free_ char *e = NULL;
return 0;
if (e == NULL)
return log_oom();
b = unit_has_name(UNIT(s), e);
if (!b) {
"%s: Value of \"What\" and unit name do not match, not loading.",
return -EINVAL;
}
"%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing to load.",
return -EINVAL;
}
return 0;
}
int r;
assert(s);
/* Load a .swap file */
if (r < 0)
return r;
if (u->load_state == UNIT_LOADED) {
r = unit_add_exec_dependencies(u, &s->exec_context);
if (r < 0)
return r;
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)
if ((r = unit_set_description(u, s->what)) < 0)
return r;
r = swap_add_device_links(s);
if (r < 0)
return r;
r = swap_add_mount_links(s);
if (r < 0)
return r;
r = unit_add_default_slice(u);
if (r < 0)
return r;
if (UNIT(s)->default_dependencies) {
r = swap_add_default_dependencies(s);
if (r < 0)
return r;
}
r = unit_exec_context_defaults(u, &s->exec_context);
if (r < 0)
return r;
}
return swap_verify(s);
}
static int swap_add_one(
Manager *m,
const char *what,
const char *what_proc_swaps,
int priority,
bool noauto,
bool nofail,
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 &&
return -EEXIST;
if (!u) {
delete = true;
if (!u)
return log_oom();
r = unit_add_name(u, e);
if (r < 0)
goto fail;
r = log_oom();
goto fail;
}
} else
delete = false;
p = &SWAP(u)->parameters_proc_swaps;
if (!p->what) {
if (!wp) {
r = log_oom();
goto fail;
}
if (!m->swaps_by_proc_swaps) {
if (!m->swaps_by_proc_swaps) {
r = log_oom();
goto fail;
}
}
if (r < 0)
goto fail;
}
if (set_flags) {
}
SWAP(u)->from_proc_swaps = true;
return 0;
fail:
if (delete && u)
unit_free(u);
return r;
}
int r = 0, k;
assert(m);
struct udev_device *d;
const char *dn;
/* So this is a proper swap device. Create swap units
* for all names this swap device is known under */
if (!d)
return log_oom();
dn = udev_device_get_devnode(d);
/* Skip dn==device, since that case will be handled below */
/* Add additional units for all symlinks */
const char *p;
p = udev_list_entry_get_name(item);
if (path_startswith(p, "/dev/block/"))
continue;
continue;
if (k < 0)
r = k;
}
}
if (k < 0)
r = k;
return r;
}
assert(s);
if (state != SWAP_ACTIVATING &&
state != SWAP_ACTIVATING_SIGTERM &&
state != SWAP_ACTIVATING_SIGKILL &&
state != SWAP_DEACTIVATING &&
s->control_command = NULL;
}
"%s changed %s -> %s",
state_translation_table[state], true);
}
static int swap_coldplug(Unit *u) {
int r;
assert(s);
if (s->deserialized_state != s->state)
new_state = s->deserialized_state;
else if (s->from_proc_swaps)
if (new_state == SWAP_ACTIVATING ||
new_state == SWAP_DEACTIVATING ||
if (s->control_pid <= 0)
return -EBADMSG;
if (r < 0)
return r;
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 (p)
fprintf(f,
"%sPriority: %i\n"
"%sNoAuto: %s\n"
"%sNoFail: %s\n",
if (s->control_pid > 0)
fprintf(f,
"%sControl PID: %lu\n",
prefix, (unsigned long) s->control_pid);
}
int r;
assert(s);
assert(c);
unit_realize_cgroup(UNIT(s));
if (r < 0)
goto fail;
r = exec_spawn(c,
NULL,
&s->exec_context,
NULL, 0,
true,
true,
true,
UNIT(s)->cgroup_mask,
UNIT(s)->cgroup_path,
NULL,
&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;
}
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) {
if (r < 0)
goto fail;
swap_set_state(s, state);
} else
return;
fail:
}
static void swap_enter_activating(Swap *s) {
int r, priority;
assert(s);
if (s->from_fragment)
else
priority = -1;
if (priority >= 0) {
char p[LINE_MAX];
char_array_0(p);
r = exec_command_set(
s->control_command,
"-p",
p,
s->what,
NULL);
} else
r = exec_command_set(
s->control_command,
s->what,
NULL);
if (r < 0)
goto fail;
if (r < 0)
goto fail;
return;
fail:
"%s failed to run 'swapon' task: %s",
}
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:
"%s failed to run 'swapoff' task: %s",
}
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;
s->result = SWAP_SUCCESS;
return 0;
}
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_ACTIVE);
if (detect_container(NULL) > 0)
return -EPERM;
return 0;
}
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;
}
char *t;
if (!t)
return log_oom();
s->exec_context.tmp_dir = t;
char *t;
if (!t)
return log_oom();
s->exec_context.var_tmp_dir = t;
} 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;
}
u->id,
"%s swap process exited, code=%s status=%i",
switch (s->state) {
case SWAP_ACTIVATING:
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:
if (f == SWAP_SUCCESS)
swap_enter_dead(s, f);
else
swap_enter_dead(s, f);
break;
default:
assert_not_reached("Uh, control process died at wrong time.");
}
/* Notify clients about changed exit status */
* can follow our state change */
u->manager->request_reload = true;
}
assert(s);
assert(w == &s->timer_watch);
switch (s->state) {
case SWAP_ACTIVATING:
break;
case SWAP_DEACTIVATING:
break;
case SWAP_ACTIVATING_SIGTERM:
if (s->kill_context.send_sigkill) {
} else {
}
break;
if (s->kill_context.send_sigkill) {
} else {
}
break;
case SWAP_ACTIVATING_SIGKILL:
break;
default:
assert_not_reached("Timeout at wrong time.");
}
}
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 -ENOMEM;
free(d);
if (k < 0)
r = k;
}
return r;
}
int swap_dispatch_reload(Manager *m) {
/* This function should go as soon as the kernel properly notifies us */
if (_likely_(!m->request_reload))
return 0;
m->request_reload = false;
return swap_fd_event(m, EPOLLPRI);
}
Unit *u;
int r;
assert(m);
r = swap_load_proc_swaps(m, true);
if (r < 0) {
/* Reset flags, just in case, for late calls */
}
return 0;
}
/* This has just been deactivated */
swap->from_proc_swaps = false;
case SWAP_ACTIVE:
break;
default:
break;
}
} else if (swap->just_activated) {
/* New swap entry */
case SWAP_DEAD:
case SWAP_FAILED:
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);
return NULL;
/* Make everybody follow the unit that's named after the swap
* device in the kernel */
}
}
int r;
assert(s);
if (LIST_JUST_US(same_proc_swaps, s)) {
return 0;
}
return -ENOMEM;
goto fail;
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_proc_swaps = NULL;
}
static int swap_enumerate(Manager *m) {
int r;
assert(m);
if (!m->proc_swaps) {
struct epoll_event ev = {
};
if (!m->proc_swaps)
return -errno;
}
r = swap_load_proc_swaps(m, false);
if (r < 0)
swap_shutdown(m);
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;
}
}
static const char* const swap_state_table[_SWAP_STATE_MAX] = {
[SWAP_DEAD] = "dead",
[SWAP_ACTIVATING] = "activating",
[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.",
},
},
};