swap.c revision 88f3e0c91f08c65a479e1aa09f171550b744d829
/*-*- 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"
[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)
hashmap_remove_and_replace(UNIT(s)->manager->swaps_by_proc_swaps, s->parameters_proc_swaps.what, first->parameters_proc_swaps.what, first);
else
}
assert(s);
s->parameters_etc_fstab.priority = s->parameters_proc_swaps.priority = s->parameters_fragment.priority = -1;
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;
return r;
return 0;
}
static int swap_add_mount_links(Swap *s) {
int r;
assert(s);
return r;
return 0;
}
static int swap_add_target_links(Swap *s) {
SwapParameters *p;
int r;
assert(s);
if (s->from_fragment)
p = &s->parameters_fragment;
else if (s->from_etc_fstab)
p = &s->parameters_etc_fstab;
else
return 0;
return r;
if (!p->noauto &&
!p->nofail &&
s->from_etc_fstab &&
return r;
}
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_etc_fstab)
p = &s->parameters_etc_fstab;
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);
if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true)) < 0)
return r;
}
return 0;
}
static int swap_verify(Swap *s) {
bool b;
char *e;
return 0;
return -ENOMEM;
b = unit_has_name(UNIT(s), e);
free(e);
if (!b) {
return -EINVAL;
}
return -EINVAL;
}
return 0;
}
int r;
assert(s);
/* Load a .swap file */
if ((r = unit_load_fragment_and_dropin_optional(u)) < 0)
return r;
if (u->load_state == UNIT_LOADED) {
if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
return r;
if (UNIT(s)->fragment_path)
s->from_fragment = true;
if (!s->what) {
if (s->parameters_fragment.what)
else if (s->parameters_etc_fstab.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;
if ((r = swap_add_device_links(s)) < 0)
return r;
if ((r = swap_add_mount_links(s)) < 0)
return r;
if ((r = swap_add_target_links(s)) < 0)
return r;
if ((r = unit_add_default_cgroups(u)) < 0)
return r;
if (UNIT(s)->default_dependencies)
if ((r = swap_add_default_dependencies(s)) < 0)
return r;
}
return swap_verify(s);
}
int swap_add_one(
Manager *m,
const char *what,
const char *what_proc_swaps,
int priority,
bool noauto,
bool nofail,
bool set_flags) {
bool delete = false;
int r;
SwapParameters *p;
assert(m);
if (!e)
return -ENOMEM;
u = manager_get_unit(m, e);
if (what_proc_swaps &&
u &&
SWAP(u)->from_proc_swaps &&
return -EEXIST;
if (!u) {
delete = true;
if (!u) {
free(e);
return -ENOMEM;
}
r = unit_add_name(u, e);
if (r < 0)
goto fail;
r = -ENOMEM;
goto fail;
}
} else
delete = false;
if (what_proc_swaps) {
p = &SWAP(u)->parameters_proc_swaps;
if (!p->what) {
r = -ENOMEM;
goto fail;
}
if (!m->swaps_by_proc_swaps)
r = -ENOMEM;
goto fail;
}
goto fail;
}
if (set_flags) {
}
SWAP(u)->from_proc_swaps = true;
} else {
p = &SWAP(u)->parameters_etc_fstab;
r = -ENOMEM;
goto fail;
}
SWAP(u)->from_etc_fstab = true;
}
free(e);
return 0;
fail:
free(e);
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 */
return -ENOMEM;
dn = udev_device_get_devnode(d);
if (dn)
/* 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;
}
log_debug("%s changed %s -> %s",
}
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;
return r;
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 = &s->parameters_etc_fstab;
fprintf(f,
"%sSwap State: %s\n"
"%sResult: %s\n"
"%sWhat: %s\n"
"%sPriority: %i\n"
"%sNoAuto: %s\n"
"%sNoFail: %s\n"
"%sFrom fragment: %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);
goto fail;
if ((r = exec_spawn(c,
NULL,
&s->exec_context,
NULL, 0,
true,
true,
true,
UNIT(s)->cgroup_bondings,
UNIT(s)->cgroup_attributes,
NULL,
NULL,
&pid)) < 0)
goto fail;
/* 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;
bool wait_for_exit = false;
assert(s);
if (f != SWAP_SUCCESS)
s->result = f;
if (s->control_pid > 0) {
else
wait_for_exit = true;
}
r = -ENOMEM;
goto fail;
}
/* Exclude the control pid from being killed via the cgroup */
if (s->control_pid > 0)
goto fail;
if (r < 0) {
} else if (r > 0)
wait_for_exit = true;
}
}
if (wait_for_exit) {
goto fail;
swap_set_state(s, state);
} else
return;
fail:
if (pid_set)
}
static void swap_enter_activating(Swap *s) {
int r, priority;
assert(s);
if (s->from_fragment)
else if (s->from_etc_fstab)
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;
goto fail;
return;
fail:
}
static void swap_enter_deactivating(Swap *s) {
int r;
assert(s);
if ((r = exec_command_set(
s->control_command,
s->what,
NULL)) < 0)
goto fail;
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;
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);
return 0;
}
assert(s);
assert(f);
if (s->control_pid > 0)
if (s->control_command_id >= 0)
return 0;
}
assert(s);
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;
else {
s->control_command_id = id;
}
} else
return 0;
}
assert(u);
}
static const char *swap_sub_state_to_string(Unit *u) {
assert(u);
}
static bool swap_check_gc(Unit *u) {
assert(s);
return s->from_etc_fstab || 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;
}
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->exec_context.send_sigkill) {
} else {
}
break;
if (s->exec_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;
if ((k = fscanf(m->proc_swaps,
"%*s " /* type of swap */
"%*s " /* swap size */
"%*s " /* used */
"%i\n", /* priority */
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);
if ((r = swap_load_proc_swaps(m, true)) < 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;
struct epoll_event ev;
assert(m);
if (!m->proc_swaps) {
return -errno;
}
if ((r = swap_load_proc_swaps(m, false)) < 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;
}
int r = 0;
assert(s);
return -ESRCH;
}
return -ESRCH;
}
if (s->control_pid > 0)
r = -errno;
int q;
return -ENOMEM;
/* Exclude the control pid from being killed via the cgroup */
if (s->control_pid > 0)
r = q;
goto finish;
}
if (q < 0)
r = q;
}
if (pid_set)
return r;
}
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 = {
.suffix = ".swap",
.object_size = sizeof(Swap),
.sections =
"Unit\0"
"Swap\0"
"Install\0",
.no_alias = true,
.no_instances = true,
.show_status = true,
.start = swap_start,
.bus_interface = "org.freedesktop.systemd1.Swap",
};